* [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support
@ 2026-09-07 20:29 Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 01/18] net: skbuff: Add SKB extension support to wireless drivers Pooventhiran G
` (17 more replies)
0 siblings, 18 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
IEEE P802.11bn (Ultra High Reliability) introduces the Seamless Mobility
Domain (SMD), a mechanism by which a non-AP MLD can transfer its session
from one AP MLD to another within the same administrative domain without
losing its RSNA or experiencing a visible connection break. The key
properties of an SMD are:
- A shared domain identifier (SMD-ID, encoded as a 6-byte MAC address)
advertised in the beacon of participating AP MLDs.
- A Seamless Transition (ST) protocol in two phases: ST Preparation
(cryptographic handshake and resource reservation on the target AP)
and ST Execution (atomic handoff with optional DL draining).
- Session-context transfer: the current AP MLD collects the STA's
DL/UL sequence numbers, block-ack parameters, PN values, and QoS
descriptors and conveys them to the target AP MLD so the STA can
resume without re-association.
1. ST Discovery (37.16.2)
--------------------------
For a non-AP MLD: ST discovery of an SMD capable AP is via OTA signalling
in beacon/probe responses.
For AP MLDs: Discovery of partner AP MLDs within the same SMD is via
Inter-AP (IAP) communication (solicited and unsolicited) over the
backhaul.
IAP is housed entirely within hostapd and the communication happens
between hostapd of AP MLDs.
The spec defines only the type of data that may be exchanged via IAP but does
not define the format and method of the IAP communication protocol and
keeps it out of scope (subclause 37.16.9). So, those details are not
included part of this series.
SMD capabilities are indicated to the mac80211/driver via NL80211_CMD_START_AP.
2. Association to the SMD-ME (37.16.3)
---------------------------------------
Before roaming, the client must perform a single association and
authentication with the SMD-ME through any AP MLD in the SMD. This establishes
the PMKSA and PTKSA at the SMD level that will persist across all roams.
SMD capabilities of a non-AP MLD is indicated to the mac80211/driver via
NL80211_CMD_NEW_STATION.
3. PTK Derivation (37.16.4)
----------------------------
Key derivation have seen some standard update and its to be taken care in
hostapd/wpa_supplicant, updated PTKs are plumbed to the mac and driver using
existing legacy netlink commands. PTK derivation uses SMD MAC address
(SMD identifier) and also derives SMD KDK which is used in lieu of PMK for
multi-PTK roaming. This is accommodated within hostapd using legacy netlink
commands.
4. ST Preparation (37.16.6)
----------------------------
This is the key step that enables seamlessness. Before the actual roam, the
client pre-provisions the target AP MLD:
- The non-AP MLD sends an ST preparation request (a UHR Link Reconfiguration
Request) to its current AP MLD, identifying the target AP MLD and the links to
be set up.
- The driver in the current AP MLD, when forwarding this special frame
to mac80211, collects the station session context and appends it to the
frame skb using skb-extns so that mac80211 can deliver it via a new
NL80211_ATTR_SMD_CTX attribute in the existing NL80211_CMD_FRAME.
- This method saves the round-trip from driver -> hostapd -> driver to fetch
the context.
As the context is to be sent with the frame skb (as context is per-frame) and
may be big, SKB extensions help carry the related context within the same
frame skb.
- The context collection happens in the vendor driver and the same is passed on
to mac80211 for forwarding to hostapd.
- The current AP MLD transfers the collected session context (block-ack
agreements, sequence numbers, replay counters, SCS streams, MSCS, EPCS state,
starting PN values) to the target AP MLD over the backhaul IAP.
- If Per-AP MLD PTK mode is used, a DH key exchange occurs in the preparation
frames to derive the new PTK at the Target-AP.
- The target AP MLD sets up the links (NONE -> AUTH -> ASSOC + SET_KEY), and
enters the prepared state (4a), and sets the context via NL80211_CMD_SET_CTX.
- The target AP MLD builds the ST prep response with the target AP MLD's full
capability profile (Basic Multi-Link element with per-STA profiles for each
accepted link) and sends it over the IAP to the current-AP MLD.
- The current AP MLD responds to the station with the OTA ST prep response
and a preparation timeout is started at both the current and target AP MLDs
(duration for which the prepared state is valid).
- The client can prepare multiple target AP MLDs simultaneously (up to the Max
Number Of Prepared Target AP MLDs advertised by the current AP MLD).
6. ST Execution (37.16.7 and 37.16.8)
---------------------------------------
When the client is ready to roam, it triggers the actual switch via one of two
paths:
Via the current AP MLD (37.16.7):
- The non-AP MLD sends an ST execution request to its current AP MLD. The
current AP MLD moves the station to the execution in-progress state (4b)
and transfers the current context, notifies the target AP MLD, and sends
back an ST execution response with SUCCESS to station.
- Context collection mechanism for Execution is same as Preparation.
- The response includes a Nominal Maximum DL Draining Period — a grace period
during which the current AP MLD may continue forwarding buffered downlink data
to the non-AP MLD before the old link goes away. During draining, the current
AP MLD moves the station to the draining state (4c).
Via the target AP MLD directly (37.16.8):
- Used as a fallback when the link to the current AP MLD has deteriorated
(e.g., after ST preparation, the RSSI to the current AP drops).
- The non-AP MLD sends the ST execution request directly to the target AP MLD;
the target AP MLD fetches remaining context from the current AP MLD over the
IAP.
- The current AP MLD uses NL80211_CMD_GET_SMD_CTX to pull the STA session
context on behalf of the target AP MLD and forwards it to the target AP MLD
over IAP.
The Target AP in both cases plumbs the stations context to the mac80211 and
subsequently to the driver via NL80211_CMD_SET_CTX.
Upon successful execution:
- The non-AP MLD enters State 4 with the target AP MLD (fully
associated/connected) and State 1 (unauthenticated and unassociated) with the
former current AP MLD post draining.
- No reassociation is required; the client retains the same PTKSA and IP
address.
- TTLM (TID-to-link mapping) reverts to default mapping mode initially.
7. Context Transfer (37.16.9)
------------------------------
The following per-client state is transferred from the current AP MLD to the
target AP MLD during ST (preparation and execution):
- Block-ack parameters and timeout per TID
- Next DL sequence numbers per TID
- Duplicate receiver cache entries
- Replay counters
- Starting PN for DL individually addressed frames
- SCS stream descriptors
- MSCS Descriptor
- EPCS authorization info and priority access state
- WinStartO for existing DL block-ack agreements
The client may optionally request that sequence numbers not be transferred (to
reset SN to 0 at the target).
8. Downlink Draining Period (37.16.10)
---------------------------------------
After ST execution, the current AP MLD may continue transmitting buffered DL
data to the non-AP MLD for a controlled grace period:
- The period duration is signaled in the ST execution response.
- Both the current AP MLD and the non-AP MLD can signal early termination of
the draining period via a UHR Link Reconfiguration Notify frame.
- During draining, the non-AP MLD is not required to listen to Beacons of the
target AP.
This series adds the kernel infrastructure needed to support SMD in AP
mode. It is structured as below logical groups:
- SKB Extension definition: patch 1
- AP configuration: patches 2-4
- STA association: patch 5 and 6
- SMD BSS Transition state machine: patches 7 and 8
- SMD Context Programming: patches 9-18
RFC:
https://lore.kernel.org/linux-wireless/fbf4209c-4fd8-4047-96d7-7fa34d9ba44d@quicinc.com/
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
Aditya Sathish (2):
wifi: nl80211: Add kernel interfaces for Seamless Mobility Domain setup
wifi: cfg80211/mac80211: Parse SMD parameters in STA addition/modification
Pooventhiran G (13):
net: skbuff: Add SKB extension support to wireless drivers
wifi: nl80211/mac80211: Add SMD BSS Transition sub-state STA flags
wifi: mac80211: Add driver_op for SMD substate changes
wifi: mac80211: Send BlockAck policy in AMPDU action
wifi: mac80211: Define SMD BSS Transition context for transport
wifi: mac80211: Enable skb extensions along with mac80211
wifi: nl80211: Define attributes to pack SMD BSS Transition context
wifi: cfg80211/mac80211: Handle UHR Link Reconfiguration frame
wifi: nl80211: Pack SMD dynamic context along with frame
wifi: nl80211/cfg80211: Add support for SMD context programming
wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX
wifi: nl80211/cfg80211: Add support for querying SMD context for target AP MLD
wifi: mac80211: Add mac80211 support to handle NL80211_CMD_GET_SMD_CTX
Rohan Dutta (2):
wifi: cfg80211/mac80211: Configure AP with SMD capabilities
wifi: nl80211/cfg80211: Indicate STA creation via SMD BSS Transition
Sidhanta Sahu (1):
wifi: nl80211: Define Seamless Mobility Domain (SMD) device capability
include/linux/ieee80211-uhr.h | 149 +++++++
include/linux/skbuff.h | 3 +
include/linux/skbuff_wireless.h | 55 +++
include/net/cfg80211.h | 115 +++++
include/net/mac80211.h | 124 ++++++
include/uapi/linux/nl80211.h | 303 +++++++++++++
net/core/skbuff.c | 55 +++
net/mac80211/Kconfig | 1 +
net/mac80211/agg-rx.c | 1 +
net/mac80211/cfg.c | 166 +++++++
net/mac80211/debugfs_sta.c | 4 +
net/mac80211/driver-ops.c | 24 +
net/mac80211/driver-ops.h | 54 +++
net/mac80211/rx.c | 22 +
net/mac80211/sta_info.c | 146 +++++++
net/mac80211/sta_info.h | 19 +
net/mac80211/trace.h | 117 ++++-
net/wireless/core.c | 25 ++
net/wireless/mlme.c | 32 ++
net/wireless/nl80211.c | 941 +++++++++++++++++++++++++++++++++++++++-
net/wireless/nl80211.h | 9 +
net/wireless/rdev-ops.h | 28 ++
net/wireless/trace.h | 73 ++++
23 files changed, 2455 insertions(+), 11 deletions(-)
---
base-commit: 1b60ed34f712e9f606d80951f1586f4274ebadf1
change-id: 20260908-smd-16986850d725
Best regards,
--
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH wireless-next 01/18] net: skbuff: Add SKB extension support to wireless drivers
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 02/18] wifi: nl80211: Define Seamless Mobility Domain (SMD) device capability Pooventhiran G
` (16 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16, defines Seamless
Mobility Domain (SMD) BSS Transition, in which a station roams between
two AP MLDs within an SMD across two phases: Preparation and Execution.
During these phases, the receiving AP driver processes a query frame and
must carry the station's roaming context - per-TID sequence numbers in
downlink (DL) and uplink (UL) directions, packet number in DL and
per-TID packet numbers in UL, and per-TID BlockAck session parameters in
DL and UL, plus a variable-length driver context - up through mac80211
and nl80211 to userspace, which uses it to complete the transition.
This context does not fit in skb->cb (48 bytes). Carrying it separately,
outside the SKB, breaks when the SKB is cloned through the RX path because
the context lifetime is no longer tied to the frame; the driver would need
independent state correlated with the SKB at nl80211 encode time, adding
error-prone lifecycle management. SKB extensions avoid these problems: they
are reference-counted, copy-on-write safe, and freed automatically with the
SKB.
Add a new skbuff_wireless.h with a typed union structured to accommodate
future wireless extension types, carrying the SMD roaming context as its
first payload.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/linux/skbuff.h | 3 +++
include/linux/skbuff_wireless.h | 55 +++++++++++++++++++++++++++++++++++++++++
net/core/skbuff.c | 55 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 113 insertions(+)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 421f6fc45451..fb686024e97d 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -5061,6 +5061,9 @@ enum skb_ext_id {
#endif
#if IS_ENABLED(CONFIG_CAN)
SKB_EXT_CAN,
+#endif
+#if IS_ENABLED(CONFIG_WIRELESS)
+ SKB_EXT_WIRELESS,
#endif
SKB_EXT_NUM, /* must be last */
};
diff --git a/include/linux/skbuff_wireless.h b/include/linux/skbuff_wireless.h
new file mode 100644
index 000000000000..2cb04bff061c
--- /dev/null
+++ b/include/linux/skbuff_wireless.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * IEEE 802.11 WLAN skb_ext definitions
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef LINUX_SKB_WIRELESS_H
+#define LINUX_SKB_WIRELESS_H
+
+#include <linux/types.h>
+#include <linux/refcount.h>
+
+/**
+ * enum wireless_skb_ext_type - type of data in wireless_skb_ext
+ * @WIRELESS_SKB_EXT_INVALID: uninitialized state
+ * @WIRELESS_SKB_EXT_UHR_SMD: payload is @uhr_smd_ctx
+ */
+enum wireless_skb_ext_type {
+ WIRELESS_SKB_EXT_INVALID,
+ WIRELESS_SKB_EXT_UHR_SMD,
+};
+
+/**
+ * struct wireless_skb_ext_smd_ctx - UHR SMD roaming context container in
+ * SKB extensions
+ * @refcnt: reference count; one count per distinct skb_ext block that holds
+ * this pointer. Plain skb_clone() shares the same skb_ext block and
+ * does not increment this counter. Only skb_ext_maybe_cow() increments
+ * this counter, when a COW produces a second independent ext block copy.
+ * @smd_ctx: pointer to the IEEE P802.11bn UHR SMD roaming context;
+ * must be a kmalloc-ed buffer, freed together with this container.
+ */
+struct wireless_skb_ext_smd_ctx {
+ refcount_t refcnt;
+ void *smd_ctx;
+};
+
+/**
+ * struct wireless_skb_ext - SKB extension data for wireless module use
+ *
+ * Carried as an skb extension (SKB_EXT_WIRELESS). The union is reserved
+ * for future wireless extension types; only one member is active per skb.
+ *
+ * @type: type of data
+ * @uhr_smd_ctx: pointer to the UHR SMD context container
+ */
+struct wireless_skb_ext {
+ enum wireless_skb_ext_type type;
+ union {
+ struct wireless_skb_ext_smd_ctx *uhr_smd_ctx;
+ };
+};
+
+#endif /* LINUX_SKB_WIRELESS_H */
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index ab195b99c853..f28ab7c3c888 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -93,6 +93,10 @@
#include <linux/indirect_call_wrapper.h>
#include <linux/textsearch.h>
+#if IS_ENABLED(CONFIG_WIRELESS)
+#include <linux/skbuff_wireless.h>
+#endif
+
#include "dev.h"
#include "devmem.h"
#include "net-sysfs.h"
@@ -5169,6 +5173,9 @@ static const u8 skb_ext_type_len[] = {
#if IS_ENABLED(CONFIG_CAN)
[SKB_EXT_CAN] = SKB_EXT_CHUNKSIZEOF(struct can_skb_ext),
#endif
+#if IS_ENABLED(CONFIG_WIRELESS)
+ [SKB_EXT_WIRELESS] = SKB_EXT_CHUNKSIZEOF(struct wireless_skb_ext),
+#endif
};
static __always_inline __no_profile unsigned int skb_ext_total_length(void)
@@ -7152,6 +7159,16 @@ static struct skb_ext *skb_ext_maybe_cow(struct skb_ext *old,
if (flow->key)
refcount_inc(&flow->key->refs);
}
+#endif
+#if IS_ENABLED(CONFIG_WIRELESS)
+ if (old_active & (1 << SKB_EXT_WIRELESS)) {
+ struct wireless_skb_ext *wifi =
+ skb_ext_get_ptr(old, SKB_EXT_WIRELESS);
+
+ if (wifi->type == WIRELESS_SKB_EXT_UHR_SMD &&
+ wifi->uhr_smd_ctx)
+ refcount_inc(&wifi->uhr_smd_ctx->refcnt);
+ }
#endif
__skb_ext_put(old);
return new;
@@ -7256,6 +7273,36 @@ static void skb_ext_put_mctp(struct mctp_flow *flow)
}
#endif
+#if IS_ENABLED(CONFIG_WIRELESS)
+static void skb_ext_put_wireless(struct wireless_skb_ext *wifi)
+{
+ switch (wifi->type) {
+ case WIRELESS_SKB_EXT_UHR_SMD:
+ if (!wifi->uhr_smd_ctx)
+ break;
+
+ if (refcount_dec_and_test(&wifi->uhr_smd_ctx->refcnt)) {
+ kfree(wifi->uhr_smd_ctx->smd_ctx);
+ kfree(wifi->uhr_smd_ctx);
+ }
+
+ /*
+ * __skb_ext_del clears active_extensions but not ext->offset[],
+ * so __skb_ext_put will call skb_ext_put_wireless again;
+ * mark NULL regardless to avoid double free in __skb_ext_put.
+ */
+ wifi->uhr_smd_ctx = NULL;
+ break;
+ case WIRELESS_SKB_EXT_INVALID:
+ /* data not yet set */
+ break;
+ default:
+ WARN_ONCE(1, "unknown wireless skb extension type=%u\n",
+ wifi->type);
+ }
+}
+#endif
+
void __skb_ext_del(struct sk_buff *skb, enum skb_ext_id id)
{
struct skb_ext *ext = skb->extensions;
@@ -7278,6 +7325,10 @@ void __skb_ext_del(struct sk_buff *skb, enum skb_ext_id id)
if (id == SKB_EXT_MCTP)
skb_ext_put_mctp(skb_ext_get_ptr(ext, SKB_EXT_MCTP));
#endif
+#if IS_ENABLED(CONFIG_WIRELESS)
+ if (id == SKB_EXT_WIRELESS)
+ skb_ext_put_wireless(skb_ext_get_ptr(ext, SKB_EXT_WIRELESS));
+#endif
}
EXPORT_SYMBOL(__skb_ext_del);
@@ -7300,6 +7351,10 @@ void __skb_ext_put(struct skb_ext *ext)
if (__skb_ext_exist(ext, SKB_EXT_MCTP))
skb_ext_put_mctp(skb_ext_get_ptr(ext, SKB_EXT_MCTP));
#endif
+#if IS_ENABLED(CONFIG_WIRELESS)
+ if (__skb_ext_exist(ext, SKB_EXT_WIRELESS))
+ skb_ext_put_wireless(skb_ext_get_ptr(ext, SKB_EXT_WIRELESS));
+#endif
kmem_cache_free(skbuff_ext_cache, ext);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 02/18] wifi: nl80211: Define Seamless Mobility Domain (SMD) device capability
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 01/18] net: skbuff: Add SKB extension support to wireless drivers Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 03/18] wifi: nl80211: Add kernel interfaces for Seamless Mobility Domain setup Pooventhiran G
` (15 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
From: Sidhanta Sahu <sidhanta.sahu@oss.qualcomm.com>
Per IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16, an AP MLD can be
managed by a Seamless Mobility Domain Management Entity (SMD-ME) along
with partner AP MLDs to enable seamless transition of an associated
non-AP MLD from the current AP MLD to a target AP MLD. A non-AP MLD
advertising SMD supports association to an SMD-ME and SMD BSS
Transition between AP MLDs within an SMD.
Subclause 37.16.11 defines an optional capability for the current AP
MLD to forward buffered downlink (DL) data frames for the non-AP MLD to
the target AP MLD.
Userspace needs a way to be aware of the driver's SMD capabilities so that
it can enable SMD configuration and operation only on capable devices.
Define two extended feature flags
- NL80211_EXT_FEATURE_SMD for SMD management and BSS Transition
capability
- NL80211_EXT_FEATURE_SMD_DL_PKT_FORWARDING for DL data forwarding
capability
so that userspace can gate SMD setup and data forwarding configuration
on driver support.
Signed-off-by: Sidhanta Sahu <sidhanta.sahu@oss.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/uapi/linux/nl80211.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index d8fefefc11a9..d9ce185b0ef1 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -7114,6 +7114,15 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_PROBE_AP: Driver supports probing the associated AP
* in STA mode using @NL80211_CMD_PROBE_PEER.
*
+ * @NL80211_EXT_FEATURE_SMD: Driver supports Seamless Mobility Domain (SMD)
+ * and is managed by an SMD-ME to support SMD BSS Transition in AP mode
+ * and supports association to an SMD-ME and SMD BSS transition in
+ * non-AP STA mode.
+ *
+ * @NL80211_EXT_FEATURE_SMD_DL_PKT_FORWARDING: Driver supports forwarding of
+ * buffered downlink (DL) data packets in AP mode to the non-AP STA during
+ * SMD BSS Transition.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -7196,6 +7205,8 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_ROC_ADDR_FILTER,
NL80211_EXT_FEATURE_SET_KEY_LTF_SEED,
NL80211_EXT_FEATURE_PROBE_AP,
+ NL80211_EXT_FEATURE_SMD,
+ NL80211_EXT_FEATURE_SMD_DL_PKT_FORWARDING,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 03/18] wifi: nl80211: Add kernel interfaces for Seamless Mobility Domain setup
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 01/18] net: skbuff: Add SKB extension support to wireless drivers Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 02/18] wifi: nl80211: Define Seamless Mobility Domain (SMD) device capability Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 04/18] wifi: cfg80211/mac80211: Configure AP with SMD capabilities Pooventhiran G
` (14 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
From: Aditya Sathish <asathish@qti.qualcomm.com>
An AP MLD managed by an SMD-ME must advertise its SMD parameters in
Beacon and Probe Response frames so that an SMD-capable non-AP MLD can
negotiate SMD during association and may choose to later perform SMD BSS
Transition. Add nl80211 kernel interfaces for userspace to configure
these parameters in nl80211_start_ap() so that the driver and lower
layers can be configured with appropriate parameters.
Signed-off-by: Aditya Sathish <asathish@qti.qualcomm.com>
Co-developed-by: Rohan Dutta <drohan@qti.qualcomm.com>
Signed-off-by: Rohan Dutta <drohan@qti.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/linux/ieee80211-uhr.h | 4 ++
include/uapi/linux/nl80211.h | 95 +++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.c | 17 ++++++++
3 files changed, 116 insertions(+)
diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h
index 19e9260d0db8..c3f87d4c8bec 100644
--- a/include/linux/ieee80211-uhr.h
+++ b/include/linux/ieee80211-uhr.h
@@ -589,6 +589,10 @@ ieee80211_uhr_dbe_cap(const struct ieee80211_uhr_cap *cap)
#define IEEE80211_SMD_INFO_CAPA_TYPE 0x10
#define IEEE80211_SMD_INFO_CAPA_PTK_PER_AP_MLD 0x20
+/* IEEE P802.11bn/D2.0, Aug 2026, subclause 9.4.2.363 */
+#define IEEE80211_SMD_INFO_PREP_TIMEOUT_MIN 2 /* in 64 TUs */
+#define IEEE80211_SMD_INFO_NUM_PREP_AP_MAX 7
+
struct ieee80211_smd_info {
u8 id[ETH_ALEN];
u8 capa;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index d9ce185b0ef1..f5c8dbe5f26a 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3185,6 +3185,12 @@ enum nl80211_commands {
* The aggregated message always precedes the per-link messages for the
* same station within a dump sequence.
*
+ * @NL80211_ATTR_SMD_PARAMS: Nested attribute to indicate support for Seamless
+ * Mobility Domain (SMD); it contains parameters to define capabilities of
+ * an AP managed by an SMD-ME. Valid if the driver supports
+ * %NL80211_EXT_FEATURE_SMD.
+ * Used with %NL80211_CMD_START_AP. See &enum nl80211_smd_params_attrs.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3785,6 +3791,8 @@ enum nl80211_attrs {
NL80211_ATTR_STA_DUMP_LINK_STATS,
+ NL80211_ATTR_SMD_PARAMS,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -9118,4 +9126,91 @@ enum nl80211_nan_peer_map_attrs {
#define NL80211_NAN_SCHED_NOT_AVAIL_SLOT 0xff
+/**
+ * enum nl80211_smd_type - Type of the SMD
+ *
+ * @NL80211_SMD_TYPE_PER_AP_MLD_MAC_SAP: Separate MAC SAP is used per AP MLD.
+ * @NL80211_SMD_TYPE_PER_SMD_MAC_SAP: one MAC SAP is used in the SMD.
+ * @NUM_NL80211_SMD_TYPES: internal.
+ * @NL80211_SMD_TYPE_MAX: Max type of the SMD.
+ *
+ * This value represents the data path models between the non-AP MLD and DS.
+ * DS mapping for the non-AP MLD is updated if
+ * %NL80211_SMD_TYPE_PER_AP_MLD_MAC_SAP is used.
+ *
+ * All AP MLDs within an SMD shall indicate the same SMD type (refer
+ * IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16.1.2).
+ */
+enum nl80211_smd_type {
+ NL80211_SMD_TYPE_PER_AP_MLD_MAC_SAP,
+ NL80211_SMD_TYPE_PER_SMD_MAC_SAP,
+
+ NUM_NL80211_SMD_TYPES,
+ NL80211_SMD_TYPE_MAX = NUM_NL80211_SMD_TYPES - 1,
+};
+
+/**
+ * enum nl80211_smd_ptk_mode - PTK Mode used in the SMD
+ *
+ * @NL80211_SMD_PTK_MODE_PER_SMD_PTK: one PTK is used by all AP MLDs in the SMD.
+ * @NL80211_SMD_PTK_MODE_PER_AP_MLD_PTK: Separate PTK is used per AP MLD.
+ * @NUM_NL80211_SMD_PTK_MODES: internal.
+ * @NL80211_SMD_PTK_MODE_MAX: Max type of PTK mode.
+ *
+ * This value represents the security mode of the SMD that dictates the PTK used
+ * in current and target AP MLDs to protect communications with the non-AP MLD
+ * (refer IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16.1.3.2)
+ */
+enum nl80211_smd_ptk_mode {
+ NL80211_SMD_PTK_MODE_PER_SMD_PTK,
+ NL80211_SMD_PTK_MODE_PER_AP_MLD_PTK,
+
+ NUM_NL80211_SMD_PTK_MODES,
+ NL80211_SMD_PTK_MODE_MAX = NUM_NL80211_SMD_PTK_MODES - 1,
+};
+
+/**
+ * enum nl80211_smd_params_attrs - SMD Domain parameters
+ *
+ * @NL80211_SMD_PARAMS_ATTR_UNSPEC: Invalid attribute.
+ * @NL80211_SMD_PARAMS_ATTR_IDENTIFIER: Required (binary) attribute of ETH_ALEN
+ * bytes used to define the unique identifier for the SMD.
+ * @NL80211_SMD_PARAMS_ATTR_TIMEOUT: Required (u16) attribute to define the SMD
+ * Preparation timeout (in 64 TUs, values 0 and 1 are reserved) for
+ * the target AP MLD.
+ * @NL80211_SMD_PARAMS_ATTR_DL_DATA_FORWARDING: Flag attribute to indicate that
+ * the device supports forwarding of buffered DL data packets.
+ * @NL80211_SMD_PARAMS_ATTR_MAX_PEER_APMLDS: Required (u8) attribute to define
+ * the maximum number of AP MLDs a non-AP MLD can prepare with. Value is
+ * the maximum number of AP MLDs - 1. A value of 0 means that the non-AP
+ * MLD can prepare with 1 AP MLD.
+ * @NL80211_SMD_PARAMS_ATTR_TYPE: Required (u8) attribute to indicate the
+ * SMD Type (&enum nl80211_smd_type).
+ * @NL80211_SMD_PARAMS_ATTR_PTK_MODE: Required (u8) attribute to indicate the
+ * PTK mode used for protection (&enum nl80211_smd_ptk_mode).
+ * @NL80211_SMD_PARAMS_ATTR_NEIGHBOR_PROBING: Flag attribute to indicate that
+ * the device supports UHR Neighboring AP Probing.
+ * @__NL80211_SMD_PARAMS_ATTR_LAST: Last attribute, used internally.
+ * @NL80211_SMD_PARAMS_ATTR_MAX: Max attribute.
+ *
+ * Nested attributes carried in %NL80211_ATTR_SMD_PARAMS to define
+ * device capabilities that is part of an SMD (refer IEEE P802.11bn/D2.0,
+ * Aug 2026, subclause 9.4.2.363, Figure 9-aa43 and Figure 9-aa44).
+ *
+ */
+enum nl80211_smd_params_attrs {
+ NL80211_SMD_PARAMS_ATTR_UNSPEC,
+ NL80211_SMD_PARAMS_ATTR_IDENTIFIER,
+ NL80211_SMD_PARAMS_ATTR_TIMEOUT,
+ NL80211_SMD_PARAMS_ATTR_DL_DATA_FORWARDING,
+ NL80211_SMD_PARAMS_ATTR_MAX_PEER_APMLDS,
+ NL80211_SMD_PARAMS_ATTR_TYPE,
+ NL80211_SMD_PARAMS_ATTR_PTK_MODE,
+ NL80211_SMD_PARAMS_ATTR_NEIGHBOR_PROBING,
+
+ /* keep last */
+ __NL80211_SMD_PARAMS_ATTR_LAST,
+ NL80211_SMD_PARAMS_ATTR_MAX = __NL80211_SMD_PARAMS_ATTR_LAST - 1
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e585da1d79c9..22d71f32815f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -689,6 +689,21 @@ nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = {
[NL80211_NAN_CONF_NOTIFY_DW] = { .type = NLA_FLAG },
};
+static const struct nla_policy
+nl80211_smd_params_policy[NL80211_SMD_PARAMS_ATTR_MAX + 1] = {
+ [NL80211_SMD_PARAMS_ATTR_IDENTIFIER] = NLA_POLICY_ETH_ADDR,
+ [NL80211_SMD_PARAMS_ATTR_TIMEOUT] =
+ NLA_POLICY_MIN(NLA_U16, IEEE80211_SMD_INFO_PREP_TIMEOUT_MIN),
+ [NL80211_SMD_PARAMS_ATTR_DL_DATA_FORWARDING] = { .type = NLA_FLAG },
+ [NL80211_SMD_PARAMS_ATTR_MAX_PEER_APMLDS] =
+ NLA_POLICY_MAX(NLA_U8, IEEE80211_SMD_INFO_NUM_PREP_AP_MAX),
+ [NL80211_SMD_PARAMS_ATTR_TYPE] =
+ NLA_POLICY_MAX(NLA_U8, NL80211_SMD_TYPE_MAX),
+ [NL80211_SMD_PARAMS_ATTR_PTK_MODE] =
+ NLA_POLICY_MAX(NLA_U8, NL80211_SMD_PTK_MODE_MAX),
+ [NL80211_SMD_PARAMS_ATTR_NEIGHBOR_PROBING] = { .type = NLA_FLAG },
+};
+
static const struct netlink_range_validation nl80211_punct_bitmap_range = {
.min = 0,
.max = 0xffff,
@@ -1097,6 +1112,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_NPCA_PUNCT_BITMAP] =
NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },
+ [NL80211_ATTR_SMD_PARAMS] =
+ NLA_POLICY_NESTED(nl80211_smd_params_policy),
};
/* policy for the key attributes */
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 04/18] wifi: cfg80211/mac80211: Configure AP with SMD capabilities
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (2 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 03/18] wifi: nl80211: Add kernel interfaces for Seamless Mobility Domain setup Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 05/18] wifi: cfg80211/mac80211: Parse SMD parameters in STA addition/modification Pooventhiran G
` (13 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
From: Rohan Dutta <drohan@qti.qualcomm.com>
When an AP is getting started, configure the AP as SMD-enabled if
userspace defines configuration for the Seamless Mobility Domain (SMD).
The SMD parameters are:
- SMD Identifier: unique identifier of the SMD
- SMD Preparation Timeout: Valid duration for a prepared target AP MLD
- Downlink Data Forwarding: Forwarding support for buffered DL packets
- Max number of Peer AP MLDs: Number of target AP MLDs a non-AP MLD can
prepare with
- SMD Type: Type of SMD
- SMD PTK Mode: PTK mode used in the SMD
- Neighbor Probing: UHR Neighboring AP Probing
Without these parameters configured, drivers and lower layers cannot be
able to support SMD BSS Transition.
Signed-off-by: Rohan Dutta <drohan@qti.qualcomm.com>
Co-developed-by: Aditya Sathish <asathish@qti.qualcomm.com>
Signed-off-by: Aditya Sathish <asathish@qti.qualcomm.com>
Co-developed-by: Sidhanta Sahu <sidhanta.sahu@oss.qualcomm.com>
Signed-off-by: Sidhanta Sahu <sidhanta.sahu@oss.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/cfg80211.h | 25 ++++++++++++++++
include/net/mac80211.h | 2 ++
net/mac80211/cfg.c | 5 ++++
net/wireless/nl80211.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 110 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6461a7bd0169..d0a269f4c711 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1587,6 +1587,29 @@ struct cfg80211_s1g_short_beacon {
size_t short_tail_len;
};
+/**
+ * struct cfg80211_smd_params - Seamless Mobility Domain parameters
+ *
+ * @smd_enabled: SMD is enabled on this device
+ * @smd_identifier: SMD ID (ETH_ALEN bytes)
+ * @smd_timeout: timeout in 64 TUs for ST preparation
+ * @dl_data_fwd: MSDU forwarding supported
+ * @max_num_of_peer_apmlds: max prepared AP MLDs supported (0-7)
+ * @smd_type: SMD type
+ * @ptk_mode: PTK mode
+ * @neighbor_probing: UHR Neighboring AP Probing supported
+ */
+struct cfg80211_smd_params {
+ bool smd_enabled;
+ u8 smd_identifier[ETH_ALEN];
+ u16 smd_timeout;
+ bool dl_data_fwd;
+ u8 max_num_of_peer_apmlds;
+ enum nl80211_smd_type smd_type;
+ enum nl80211_smd_ptk_mode ptk_mode;
+ bool neighbor_probing;
+};
+
/**
* struct cfg80211_ap_settings - AP configuration
*
@@ -1623,6 +1646,7 @@ 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_params: Seamless Mobility Domain (SMD) parameters
*/
struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef;
@@ -1655,6 +1679,7 @@ struct cfg80211_ap_settings {
struct cfg80211_mbssid_config mbssid_config;
u8 s1g_long_beacon_period;
struct cfg80211_s1g_short_beacon s1g_short_beacon;
+ struct cfg80211_smd_params smd_params;
};
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7bb4618065b6..6b82ca07fbf0 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -797,6 +797,7 @@ struct ieee80211_bss_npca_params {
* @s1g_long_beacon_period: number of beacon intervals between each long
* beacon transmission.
* @npca: NPCA parameters
+ * @smd_params: SMD parameters
*/
struct ieee80211_bss_conf {
struct ieee80211_vif *vif;
@@ -904,6 +905,7 @@ struct ieee80211_bss_conf {
u8 s1g_long_beacon_period;
struct ieee80211_bss_npca_params npca;
+ struct cfg80211_smd_params smd_params;
};
#define IEEE80211_NAN_MAX_CHANNELS 3
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 23f4f9ec86d0..b4b42790c5d7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1796,6 +1796,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
}
}
+ if (params->smd_params.smd_enabled)
+ link_conf->smd_params = params->smd_params;
+ else
+ link_conf->smd_params.smd_enabled = false;
+
if (sdata->vif.type == NL80211_IFTYPE_AP &&
params->mbssid_config.tx_wdev) {
err = ieee80211_set_ap_mbssid_options(sdata,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 22d71f32815f..aa7aa70a5b41 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -7224,6 +7224,78 @@ static int nl80211_check_npca(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
+static int nl80211_parse_smd_params(struct genl_info *info,
+ struct cfg80211_smd_params *smd_params)
+{
+ struct nlattr *smd_params_attr = info->attrs[NL80211_ATTR_SMD_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;
+ struct nlattr *tb[NL80211_SMD_PARAMS_ATTR_MAX + 1];
+ int err;
+
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_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;
+ }
+
+ err = nla_parse_nested(tb, NL80211_SMD_PARAMS_ATTR_MAX,
+ smd_params_attr, nl80211_smd_params_policy,
+ NULL);
+ if (err) {
+ GENL_SET_ERR_MSG(info, "failed to parse SMD parameters");
+ return err;
+ }
+
+ if (!tb[NL80211_SMD_PARAMS_ATTR_IDENTIFIER] ||
+ !tb[NL80211_SMD_PARAMS_ATTR_TIMEOUT] ||
+ !tb[NL80211_SMD_PARAMS_ATTR_MAX_PEER_APMLDS] ||
+ !tb[NL80211_SMD_PARAMS_ATTR_TYPE] ||
+ !tb[NL80211_SMD_PARAMS_ATTR_PTK_MODE]) {
+ GENL_SET_ERR_MSG(info, "required SMD parameters are missing");
+ return -EINVAL;
+ }
+
+ memcpy(smd_params->smd_identifier,
+ nla_data(tb[NL80211_SMD_PARAMS_ATTR_IDENTIFIER]),
+ ETH_ALEN);
+
+ smd_params->smd_timeout =
+ nla_get_u16(tb[NL80211_SMD_PARAMS_ATTR_TIMEOUT]);
+
+ smd_params->dl_data_fwd =
+ !!tb[NL80211_SMD_PARAMS_ATTR_DL_DATA_FORWARDING];
+ if (smd_params->dl_data_fwd &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_SMD_DL_PKT_FORWARDING)) {
+ GENL_SET_ERR_MSG(info,
+ "SMD DL packet forwarding is not supported");
+ return -EOPNOTSUPP;
+ }
+
+ smd_params->max_num_of_peer_apmlds =
+ nla_get_u8(tb[NL80211_SMD_PARAMS_ATTR_MAX_PEER_APMLDS]);
+
+ smd_params->smd_type =
+ nla_get_u8(tb[NL80211_SMD_PARAMS_ATTR_TYPE]);
+
+ smd_params->ptk_mode =
+ nla_get_u8(tb[NL80211_SMD_PARAMS_ATTR_PTK_MODE]);
+
+ smd_params->neighbor_probing =
+ !!tb[NL80211_SMD_PARAMS_ATTR_NEIGHBOR_PROBING];
+
+ smd_params->smd_enabled = true;
+
+ return 0;
+}
+
static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -7505,6 +7577,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
/* FIXME: validate MLO/link-id against driver capabilities */
+ if (info->attrs[NL80211_ATTR_SMD_PARAMS]) {
+ err = nl80211_parse_smd_params(info, ¶ms->smd_params);
+ if (err)
+ goto out;
+ }
+
err = rdev_start_ap(rdev, dev, params);
if (!err) {
wdev->links[link_id].ap.beacon_interval = params->beacon_interval;
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 05/18] wifi: cfg80211/mac80211: Parse SMD parameters in STA addition/modification
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (3 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 04/18] wifi: cfg80211/mac80211: Configure AP with SMD capabilities Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 06/18] wifi: nl80211/cfg80211: Indicate STA creation via SMD BSS Transition Pooventhiran G
` (12 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
From: Aditya Sathish <asathish@qti.qualcomm.com>
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.
Signed-off-by: Aditya Sathish <asathish@qti.qualcomm.com>
Co-developed-by: Sidhanta Sahu <sidhanta.sahu@oss.qualcomm.com>
Signed-off-by: Sidhanta Sahu <sidhanta.sahu@oss.qualcomm.com>
Co-developed-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/cfg80211.h | 15 +++++++++++++
include/net/mac80211.h | 3 +++
include/uapi/linux/nl80211.h | 17 ++++++++++++++
net/mac80211/cfg.c | 10 +++++++++
net/mac80211/debugfs_sta.c | 1 +
net/mac80211/sta_info.h | 2 ++
net/wireless/nl80211.c | 53 ++++++++++++++++++++++++++++++++++++++------
7 files changed, 94 insertions(+), 7 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index d0a269f4c711..bf33cf5366ef 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1897,6 +1897,19 @@ struct cfg80211_ttlm_params {
u16 ulink[8];
};
+/**
+ * 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
+ */
+struct cfg80211_sta_smd_params {
+ bool smd_sta;
+ u8 smd_identifier[ETH_ALEN];
+ bool dl_data_fwd;
+};
+
/**
* struct station_parameters - station parameters
*
@@ -1937,6 +1950,7 @@ struct cfg80211_ttlm_params {
* @link_sta_params: link related params.
* @epp_peer: EPP peer indication
* @nmi_mac: MAC address of the NMI station of the NAN peer
+ * @smd_params: SMD params for this station
*/
struct station_parameters {
struct net_device *vlan;
@@ -1965,6 +1979,7 @@ struct station_parameters {
struct link_station_parameters link_sta_params;
bool epp_peer;
const u8 *nmi_mac;
+ struct cfg80211_sta_smd_params smd_params;
};
/**
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 6b82ca07fbf0..d8f5e5d8a021 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2727,6 +2727,7 @@ 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
*/
struct ieee80211_sta {
u8 addr[ETH_ALEN] __aligned(2);
@@ -2761,6 +2762,8 @@ 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 *));
};
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index f5c8dbe5f26a..f1d8db31f17e 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3191,6 +3191,17 @@ 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.
+ *
+ * @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.
+ *
+ * @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.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3793,6 +3804,10 @@ enum nl80211_attrs {
NL80211_ATTR_SMD_PARAMS,
+ 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,
@@ -3942,6 +3957,7 @@ 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_SMD: station has negotiated SMD.
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
@@ -3955,6 +3971,7 @@ enum nl80211_sta_flags {
NL80211_STA_FLAG_TDLS_PEER,
NL80211_STA_FLAG_ASSOCIATED,
NL80211_STA_FLAG_SPP_AMSDU,
+ NL80211_STA_FLAG_SMD,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b4b42790c5d7..138b67f4bda4 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2528,6 +2528,16 @@ 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);
+ 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);
+ }
+
+ if (params->smd_params.smd_sta)
+ sta->sta.smd_params = params->smd_params;
+
/* 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 &&
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index ef75255d47d5..d7e7c9c578e4 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -78,6 +78,7 @@ static const char * const sta_flag_names[] = {
FLAG(PS_DELIVER),
FLAG(USES_ENCRYPTION),
FLAG(DECAP_OFFLOAD),
+ FLAG(SMD),
#undef FLAG
};
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index ee0d32877c5b..506aeb241a6d 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -72,6 +72,7 @@
* @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
*/
@@ -104,6 +105,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_PS_DELIVER,
WLAN_STA_USES_ENCRYPTION,
WLAN_STA_DECAP_OFFLOAD,
+ WLAN_STA_SMD,
NUM_WLAN_STA_FLAGS,
};
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index aa7aa70a5b41..0d20c2ab3d5e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1114,6 +1114,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .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 },
};
/* policy for the key attributes */
@@ -7702,6 +7705,7 @@ 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 },
[NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_SMD] = { .type = NLA_FLAG },
};
static int parse_station_flags(struct genl_info *info,
@@ -7768,11 +7772,13 @@ 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);
+ fallthrough;
case NL80211_IFTYPE_P2P_GO:
- params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
- BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
- BIT(NL80211_STA_FLAG_WME) |
- BIT(NL80211_STA_FLAG_MFP);
+ params->sta_flags_mask |= BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+ BIT(NL80211_STA_FLAG_WME) |
+ BIT(NL80211_STA_FLAG_MFP);
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
@@ -8913,7 +8919,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 != 8);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9);
switch (statype) {
case CFG80211_STA_MESH_PEER_KERNEL:
@@ -9007,7 +9013,8 @@ 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_SMD)))
return -EINVAL;
/* but authenticated/associated only if driver handles it */
@@ -9261,6 +9268,30 @@ static int nl80211_parse_sta_txpower_setting(struct genl_info *info,
return 0;
}
+static int nl80211_set_station_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];
+
+ 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)
+ 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];
+
+ return 0;
+}
+
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9399,6 +9430,10 @@ 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)
@@ -9627,6 +9662,10 @@ 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);
+ if (err)
+ return err;
+
/* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT
* as userspace might just pass through the capabilities from the IEs
* directly, rather than enforcing this restriction and returning an
@@ -9660,7 +9699,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 != 8);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9);
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 06/18] wifi: nl80211/cfg80211: Indicate STA creation via SMD BSS Transition
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (4 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 05/18] wifi: cfg80211/mac80211: Parse SMD parameters in STA addition/modification Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 07/18] wifi: nl80211/mac80211: Add SMD BSS Transition sub-state STA flags Pooventhiran G
` (11 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
From: Rohan Dutta <drohan@qti.qualcomm.com>
When the peer STA is created via SMD BSS Transition Preparation on the
target AP MLD, indicate that the STA is created as part of SMD procedure
unlike STA creation using typical association. This indication helps the
drivers and lower layers to prepare the STA for handling ST Execution
and datapath switch once the ST procedure is completed.
Signed-off-by: Rohan Dutta <drohan@qti.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/cfg80211.h | 2 ++
include/uapi/linux/nl80211.h | 7 +++++++
net/wireless/nl80211.c | 3 +++
3 files changed, 12 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index bf33cf5366ef..75d0123fbb5d 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1903,11 +1903,13 @@ struct cfg80211_ttlm_params {
* @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;
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index f1d8db31f17e..5e474fda2a49 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3202,6 +3202,11 @@ enum nl80211_commands {
* supports DL data forwarding within the SMD domain.
* Used with %NL80211_CMD_NEW_STATION.
*
+ * @NL80211_ATTR_SMD_ROAM_STA: Flag attribute indicating this station was
+ * created via SMD BSS Transition Preparation at the Target AP MLD (refer
+ * IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16.6).
+ * This flag is carried in %NL80211_CMD_NEW_STATION.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3808,6 +3813,8 @@ enum nl80211_attrs {
NL80211_ATTR_PEER_SMD_MAC_ADDR,
NL80211_ATTR_PEER_SMD_DL_DATA_FORWARDING,
+ NL80211_ATTR_SMD_ROAM_STA,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0d20c2ab3d5e..7577eb31ff65 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1117,6 +1117,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[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 },
+ [NL80211_ATTR_SMD_ROAM_STA] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -9289,6 +9290,8 @@ static int nl80211_set_station_smd(struct genl_info *info,
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;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 07/18] wifi: nl80211/mac80211: Add SMD BSS Transition sub-state STA flags
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (5 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 06/18] wifi: nl80211/cfg80211: Indicate STA creation via SMD BSS Transition Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 08/18] wifi: mac80211: Add driver_op for SMD substate changes Pooventhiran G
` (10 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
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:
NL80211_STA_FLAG_SMD_PREP_TARGET -- State 4a: target AP MLD prepared;
Class 3 frames blocked except ST execution request/response.
NL80211_STA_FLAG_SMD_EXEC_CURRENT -- State 4b: ST execution active on
current AP MLD; UL Class 3 data blocked.
NL80211_STA_FLAG_SMD_DL_DRAIN -- State 4c: DL draining period; only
control frames and UHR Reconfiguration Notify permitted on current
AP MLD.
Add three internal station flags to track the 802.11bn SMD BSS
Transition sub-states (4a/4b/4c) in mac80211:
WLAN_STA_SMD_PREP_TARGET - State 4a
WLAN_STA_SMD_EXEC_CURRENT - State 4b
WLAN_STA_SMD_DL_DRAIN - State 4c
These are transient sub-states of WLAN_STA_AUTHORIZED with the SMD-ME.
The station remains authenticated, associated, and RSNA-established
throughout; only the frame-filtering rules change. At most one of the
three may be set at a time; mutual exclusion is enforced in
sta_apply_smd_state_flags().
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/uapi/linux/nl80211.h | 10 ++++++++
net/mac80211/cfg.c | 60 ++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/debugfs_sta.c | 3 +++
net/mac80211/sta_info.h | 8 ++++++
net/wireless/nl80211.c | 18 ++++++++++---
5 files changed, 95 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 5e474fda2a49..65c7eefe9db5 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3965,6 +3965,13 @@ 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). Class 3 frames blocked except ST execution req/resp.
+ * @NL80211_STA_FLAG_SMD_EXEC_CURRENT: current AP MLD is executing SMD BSS
+ * Transition (State 4b). Uplink Class 3 data blocked after exec-req Tx.
+ * @NL80211_STA_FLAG_SMD_DL_DRAIN: current AP MLD is in DL draining period
+ * (State 4c). Both directions restricted; only control and UHR Reconf
+ * Notify frames are permitted.
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
@@ -3979,6 +3986,9 @@ 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,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 138b67f4bda4..69b3ac0b48f3 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2452,6 +2452,55 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
return 0;
}
+static int sta_apply_smd_state_flags(struct sta_info *sta,
+ u32 mask, u32 set)
+{
+ bool smd_prep_target, smd_exec_current, smd_dl_drain;
+ u32 expected_mask = BIT(NL80211_STA_FLAG_SMD_PREP_TARGET) |
+ BIT(NL80211_STA_FLAG_SMD_EXEC_CURRENT) |
+ BIT(NL80211_STA_FLAG_SMD_DL_DRAIN);
+ u32 smd_flags;
+
+ if (!(mask & expected_mask))
+ return 0;
+
+ smd_flags = mask & expected_mask & set;
+ if (WARN_ON(hweight32(smd_flags) > 1))
+ return -EINVAL;
+
+ smd_prep_target =
+ !!(smd_flags & BIT(NL80211_STA_FLAG_SMD_PREP_TARGET));
+ smd_exec_current =
+ !!(smd_flags & BIT(NL80211_STA_FLAG_SMD_EXEC_CURRENT));
+ smd_dl_drain =
+ !!(smd_flags & BIT(NL80211_STA_FLAG_SMD_DL_DRAIN));
+
+ if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
+ (smd_exec_current || smd_dl_drain))
+ return -EINVAL;
+
+ if (smd_prep_target) {
+ clear_sta_flag(sta, WLAN_STA_SMD_EXEC_CURRENT);
+ clear_sta_flag(sta, WLAN_STA_SMD_DL_DRAIN);
+ set_sta_flag(sta, WLAN_STA_SMD_PREP_TARGET);
+ } else if (smd_exec_current) {
+ clear_sta_flag(sta, WLAN_STA_SMD_PREP_TARGET);
+ clear_sta_flag(sta, WLAN_STA_SMD_DL_DRAIN);
+ set_sta_flag(sta, WLAN_STA_SMD_EXEC_CURRENT);
+ } else if (smd_dl_drain) {
+ clear_sta_flag(sta, WLAN_STA_SMD_PREP_TARGET);
+ clear_sta_flag(sta, WLAN_STA_SMD_EXEC_CURRENT);
+ set_sta_flag(sta, WLAN_STA_SMD_DL_DRAIN);
+ } else {
+ /* all flags revert back to none being set */
+ clear_sta_flag(sta, WLAN_STA_SMD_PREP_TARGET);
+ clear_sta_flag(sta, WLAN_STA_SMD_EXEC_CURRENT);
+ clear_sta_flag(sta, WLAN_STA_SMD_DL_DRAIN);
+ }
+
+ return 0;
+}
+
static int sta_apply_parameters(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
@@ -2538,6 +2587,17 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (params->smd_params.smd_sta)
sta->sta.smd_params = params->smd_params;
+ /*
+ * SMD BSS Transition sub-states (IEEE P802.11bn/D2.0, Aug 2026,
+ * subclause 11.3.1).
+ * Only meaningful for SMD-capable stations (WLAN_STA_SMD set).
+ */
+ if (test_sta_flag(sta, WLAN_STA_SMD)) {
+ ret = sta_apply_smd_state_flags(sta, mask, set);
+ if (ret)
+ return ret;
+ }
+
/* 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 &&
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index d7e7c9c578e4..24c985687bd2 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -79,6 +79,9 @@ static const char * const sta_flag_names[] = {
FLAG(USES_ENCRYPTION),
FLAG(DECAP_OFFLOAD),
FLAG(SMD),
+ FLAG(SMD_PREP_TARGET),
+ FLAG(SMD_EXEC_CURRENT),
+ FLAG(SMD_DL_DRAIN),
#undef FLAG
};
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 506aeb241a6d..89137b18a862 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -73,6 +73,11 @@
* 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.
+ * @WLAN_STA_SMD_PREP_TARGET: this SMD station is prepared at the target AP MLD.
+ * @WLAN_STA_SMD_EXEC_CURRENT: this SMD station has started execution at
+ * the current AP MLD.
+ * @WLAN_STA_SMD_DL_DRAIN: this SMD station has started draining at
+ * the current AP MLD.
*
* @NUM_WLAN_STA_FLAGS: number of defined flags
*/
@@ -106,6 +111,9 @@ enum ieee80211_sta_info_flags {
WLAN_STA_USES_ENCRYPTION,
WLAN_STA_DECAP_OFFLOAD,
WLAN_STA_SMD,
+ WLAN_STA_SMD_PREP_TARGET,
+ WLAN_STA_SMD_EXEC_CURRENT,
+ WLAN_STA_SMD_DL_DRAIN,
NUM_WLAN_STA_FLAGS,
};
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7577eb31ff65..282d3c82dce8 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -7707,6 +7707,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,
@@ -7773,7 +7776,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) |
@@ -8920,7 +8927,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 != 9);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 12);
switch (statype) {
case CFG80211_STA_MESH_PEER_KERNEL:
@@ -9015,7 +9022,10 @@ 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 */
@@ -9702,7 +9712,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 != 9);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 12);
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 08/18] wifi: mac80211: Add driver_op for SMD substate changes
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (6 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 07/18] wifi: nl80211/mac80211: Add SMD BSS Transition sub-state STA flags Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 09/18] wifi: mac80211: Send BlockAck policy in AMPDU action Pooventhiran G
` (9 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
During SMD BSS Transition, a non-AP MLD cycles through sub-states 4a,
4b, and 4c that impose different frame-filtering rules on the AP MLD.
mac80211 tracks these sub-states in STA flags but has no mechanism to
propagate the transitions to the driver, so hardware-level queue
gating and frame-filtering cannot follow the SMD state machine.
Add a new driver op that mac80211 calls on each sub-state transition.
This lets drivers perform the necessary hardware queue and filtering
adjustments at each phase of the SMD BSS Transition.
Valid transition order:
NONE -> PREP (ST preparation completed at the target AP MLD)
NONE -> EXEC (ST execution in progress at the current AP MLD)
EXEC -> DRAIN (DL drain period started at the current AP MLD)
Suggested-by: Rohan Dutta <drohan@qti.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/mac80211.h | 26 +++++++++
net/mac80211/cfg.c | 59 ++++++++++++++-----
net/mac80211/driver-ops.c | 24 ++++++++
net/mac80211/driver-ops.h | 6 ++
net/mac80211/sta_info.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/sta_info.h | 9 +++
net/mac80211/trace.h | 33 +++++++++++
7 files changed, 284 insertions(+), 13 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index d8f5e5d8a021..9b5fa6d6281b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -4077,6 +4077,24 @@ struct ieee80211_prep_tx_info {
int link_id;
};
+/**
+ * enum ieee80211_sta_smd_state - SMD BSS Transition sub-state
+ *
+ * Tracks the 802.11bn SMD BSS Transition sub-state (IEEE P802.11bn/D2.0,
+ * Aug 2026, subclause 11.3.1) for a specific (non-AP MLD, AP MLD) association.
+ *
+ * @IEEE80211_STA_SMD_NONE: Plain State 4. No sub-state active.
+ * @IEEE80211_STA_SMD_PREP: State 4a. STA prepared at the target AP MLD
+ * @IEEE80211_STA_SMD_EXEC: State 4b. ST execution active at the current AP MLD
+ * @IEEE80211_STA_SMD_DRAIN: State 4c. DL draining period at the current AP MLD
+ */
+enum ieee80211_sta_smd_state {
+ IEEE80211_STA_SMD_NONE,
+ IEEE80211_STA_SMD_PREP,
+ IEEE80211_STA_SMD_EXEC,
+ IEEE80211_STA_SMD_DRAIN,
+};
+
/**
* struct ieee80211_ops - callbacks from mac80211 to the driver
*
@@ -4743,6 +4761,9 @@ struct ieee80211_prep_tx_info {
* @set_eml_op_mode: Configure eMLSR/eMLMR operation mode in the underlay
* driver according to the parameter received in the EML Operating mode
* notification frame.
+ * @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.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -5144,6 +5165,11 @@ struct ieee80211_ops {
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_eml_params *eml_params);
+ int (*sta_smd_state)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_smd_state old_state,
+ enum ieee80211_sta_smd_state new_state);
};
/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 69b3ac0b48f3..0ea8b610a771 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2459,7 +2459,9 @@ static int sta_apply_smd_state_flags(struct sta_info *sta,
u32 expected_mask = BIT(NL80211_STA_FLAG_SMD_PREP_TARGET) |
BIT(NL80211_STA_FLAG_SMD_EXEC_CURRENT) |
BIT(NL80211_STA_FLAG_SMD_DL_DRAIN);
+ enum ieee80211_sta_smd_state new_ss;
u32 smd_flags;
+ int err;
if (!(mask & expected_mask))
return 0;
@@ -2479,6 +2481,22 @@ static int sta_apply_smd_state_flags(struct sta_info *sta,
(smd_exec_current || smd_dl_drain))
return -EINVAL;
+ if (smd_prep_target)
+ new_ss = IEEE80211_STA_SMD_PREP;
+ else if (smd_exec_current)
+ new_ss = IEEE80211_STA_SMD_EXEC;
+ else if (smd_dl_drain)
+ new_ss = IEEE80211_STA_SMD_DRAIN;
+ else
+ new_ss = IEEE80211_STA_SMD_NONE;
+
+ if (new_ss == sta->smd_state)
+ return 0;
+
+ err = sta_info_move_smd_state(sta, new_ss, true);
+ if (err)
+ return err;
+
if (smd_prep_target) {
clear_sta_flag(sta, WLAN_STA_SMD_EXEC_CURRENT);
clear_sta_flag(sta, WLAN_STA_SMD_DL_DRAIN);
@@ -2578,26 +2596,17 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta->sta.spp_amsdu = set & BIT(NL80211_STA_FLAG_SPP_AMSDU);
if (mask & BIT(NL80211_STA_FLAG_SMD)) {
- if (set & 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))
+ } else if (test_sta_flag(sta, WLAN_STA_SMD)) {
+ sta_info_reset_smd_state(sta, true);
clear_sta_flag(sta, WLAN_STA_SMD);
+ }
}
if (params->smd_params.smd_sta)
sta->sta.smd_params = params->smd_params;
- /*
- * SMD BSS Transition sub-states (IEEE P802.11bn/D2.0, Aug 2026,
- * subclause 11.3.1).
- * Only meaningful for SMD-capable stations (WLAN_STA_SMD set).
- */
- if (test_sta_flag(sta, WLAN_STA_SMD)) {
- ret = sta_apply_smd_state_flags(sta, mask, set);
- if (ret)
- return ret;
- }
-
/* 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 &&
@@ -2699,6 +2708,17 @@ static int sta_apply_parameters(struct ieee80211_local *local,
return ret;
}
+ /*
+ * SMD BSS Transition sub-states (IEEE P802.11bn/D2.0, Aug 2026,
+ * subclause 11.3.1).
+ * Only meaningful for SMD-capable stations (WLAN_STA_SMD set).
+ */
+ if (test_sta_flag(sta, WLAN_STA_SMD)) {
+ ret = sta_apply_smd_state_flags(sta, mask, set);
+ if (ret)
+ return ret;
+ }
+
/* Mark the STA as MLO if MLD MAC address is available */
if (params->link_sta_params.mld_mac)
sta->sta.mlo = true;
@@ -2783,6 +2803,19 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct wireless_dev *wdev,
*/
if (!err && sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
ieee80211_nan_update_ndi_carrier(sta->sdata);
+
+ /*
+ * SMD sub-state was set in sta_apply_parameters() before the station
+ * was inserted so drv_sta_smd_state() was skipped (WLAN_STA_INSERTED
+ * was not yet set). Walk the driver step-by-step to the target state.
+ */
+ if (!err && test_sta_flag(sta, WLAN_STA_SMD) &&
+ sta->smd_state != IEEE80211_STA_SMD_NONE) {
+ err = sta_info_move_drv_smd_state(sta);
+ if (err)
+ WARN_ON(__sta_info_destroy(sta));
+ }
+
return err;
}
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index 49753b73aba2..d77a5a80f06e 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -633,3 +633,27 @@ int drv_change_sta_links(struct ieee80211_local *local,
return 0;
}
+
+int drv_sta_smd_state(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_smd_state old_state,
+ enum ieee80211_sta_smd_state new_state)
+{
+ int ret = 0;
+
+ might_sleep();
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ sdata = get_bss_sdata(sdata);
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ trace_drv_sta_smd_state(local, sdata, sta, old_state, new_state);
+ if (local->ops->sta_smd_state)
+ ret = local->ops->sta_smd_state(&local->hw, &sdata->vif,
+ sta, old_state, new_state);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index f1c0b87fddd5..ce11a723ec36 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1814,4 +1814,10 @@ drv_nan_peer_sched_changed(struct ieee80211_local *local,
return ret;
}
+int drv_sta_smd_state(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_smd_state old_state,
+ enum ieee80211_sta_smd_state new_state);
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index fdf00cbf49d8..5aa6f5b481e3 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -701,6 +701,7 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata,
ieee80211_init_frag_cache(&sta->frags);
sta->sta_state = IEEE80211_STA_NONE;
+ sta->smd_state = IEEE80211_STA_SMD_NONE;
if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
sta->amsdu_mesh_control = -1;
@@ -1527,6 +1528,20 @@ static int _sta_info_move_state(struct sta_info *sta,
WARN_ONCE(err,
"Driver is not allowed to fail if the sta_state is transitioning down the list: %d\n",
err);
+
+ /* revert SMD states if STA has transitioned down */
+ if (new_state == IEEE80211_STA_AUTH &&
+ sta->sta_state == IEEE80211_STA_ASSOC) {
+ if (test_sta_flag(sta, WLAN_STA_SMD) &&
+ sta->smd_state == IEEE80211_STA_SMD_PREP)
+ sta_info_reset_smd_state(sta, true);
+ } else if (new_state == IEEE80211_STA_ASSOC &&
+ sta->sta_state == IEEE80211_STA_AUTHORIZED) {
+ if (test_sta_flag(sta, WLAN_STA_SMD) &&
+ sta->smd_state != IEEE80211_STA_SMD_NONE &&
+ sta->smd_state != IEEE80211_STA_SMD_PREP)
+ sta_info_reset_smd_state(sta, true);
+ }
}
sta->sta_state = new_state;
@@ -3883,3 +3898,128 @@ bool ieee80211_link_sta_update_rc_bw(struct ieee80211_link_data *link,
return true;
}
+
+int sta_info_move_smd_state(struct sta_info *sta,
+ enum ieee80211_sta_smd_state new_ss,
+ bool notify)
+{
+ enum ieee80211_sta_smd_state old_ss = sta->smd_state;
+ struct ieee80211_local *local = sta->local;
+ bool notify_driver;
+ int err;
+
+ might_sleep();
+
+ if (old_ss == new_ss)
+ return 0;
+
+ switch (new_ss) {
+ case IEEE80211_STA_SMD_NONE:
+ if (old_ss != IEEE80211_STA_SMD_PREP &&
+ old_ss != IEEE80211_STA_SMD_EXEC &&
+ old_ss != IEEE80211_STA_SMD_DRAIN)
+ return -EINVAL;
+ break;
+ case IEEE80211_STA_SMD_EXEC:
+ if (old_ss != IEEE80211_STA_SMD_NONE)
+ return -EINVAL;
+ break;
+ case IEEE80211_STA_SMD_DRAIN:
+ if (old_ss != IEEE80211_STA_SMD_EXEC)
+ return -EINVAL;
+ break;
+ case IEEE80211_STA_SMD_PREP:
+ if (old_ss != IEEE80211_STA_SMD_NONE)
+ return -EINVAL;
+ break;
+ default:
+ sdata_err(sta->sdata, "invalid smd state %d", new_ss);
+ return -EINVAL;
+ }
+
+ sta_dbg(sta->sdata, "moving STA %pM SMD state %u -> %u\n",
+ sta->sta.addr, old_ss, new_ss);
+
+ notify_driver = notify && test_sta_flag(sta, WLAN_STA_INSERTED);
+
+ if (new_ss > old_ss && notify_driver) {
+ err = drv_sta_smd_state(local, sta->sdata, &sta->sta, old_ss,
+ new_ss);
+ if (err)
+ return err;
+ }
+
+ if (new_ss < old_ss && notify_driver) {
+ err = drv_sta_smd_state(local, sta->sdata, &sta->sta, old_ss,
+ new_ss);
+ /* driver should not fail moving down SMD states */
+ WARN_ONCE(err,
+ "Driver should not fail moving down SMD states: %d\n",
+ err);
+ }
+
+ sta->smd_state = new_ss;
+
+ return 0;
+}
+
+void sta_info_reset_smd_state(struct sta_info *sta, bool notify)
+{
+ if (sta->smd_state == IEEE80211_STA_SMD_NONE)
+ return;
+
+ /* DRAIN -> NONE, EXEC -> NONE or PREP -> NONE */
+ WARN_ON_ONCE(sta_info_move_smd_state(sta, IEEE80211_STA_SMD_NONE,
+ notify));
+
+ clear_sta_flag(sta, WLAN_STA_SMD_PREP_TARGET);
+ clear_sta_flag(sta, WLAN_STA_SMD_EXEC_CURRENT);
+ clear_sta_flag(sta, WLAN_STA_SMD_DL_DRAIN);
+}
+
+int sta_info_move_drv_smd_state(struct sta_info *sta)
+{
+ enum ieee80211_sta_smd_state new_ss = sta->smd_state;
+ struct ieee80211_local *local = sta->local;
+ int err = 0;
+
+ if (!test_sta_flag(sta, WLAN_STA_INSERTED))
+ return 0;
+
+ switch (new_ss) {
+ case IEEE80211_STA_SMD_PREP:
+ err = drv_sta_smd_state(local, sta->sdata, &sta->sta,
+ IEEE80211_STA_SMD_NONE,
+ IEEE80211_STA_SMD_PREP);
+ if (err)
+ goto rollback;
+ break;
+ case IEEE80211_STA_SMD_EXEC:
+ case IEEE80211_STA_SMD_DRAIN:
+ err = drv_sta_smd_state(local, sta->sdata, &sta->sta,
+ IEEE80211_STA_SMD_NONE,
+ IEEE80211_STA_SMD_EXEC);
+ if (err)
+ goto rollback;
+
+ if (new_ss == IEEE80211_STA_SMD_DRAIN)
+ err = drv_sta_smd_state(local, sta->sdata, &sta->sta,
+ IEEE80211_STA_SMD_EXEC,
+ IEEE80211_STA_SMD_DRAIN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!err)
+ return 0;
+
+ /* if EXEC -> DRAIN failed in the driver, notify EXEC -> NONE */
+ WARN_ON_ONCE(drv_sta_smd_state(local, sta->sdata, &sta->sta,
+ IEEE80211_STA_SMD_EXEC,
+ IEEE80211_STA_SMD_NONE));
+
+rollback:
+ sta_info_reset_smd_state(sta, false);
+ return err;
+}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 89137b18a862..bdc021675727 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -721,6 +721,7 @@ struct ieee80211_sta_removed_link_stats {
* would be assigned to link[link_id] where link_id is the id assigned
* by the AP.
* @rem_link_stats: accumulated removed link stats
+ * @smd_state: Sub-state for STAs in SMD BSS Transition
*/
struct sta_info {
/* General information, mostly static */
@@ -797,6 +798,8 @@ struct sta_info {
struct link_sta_info __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
struct ieee80211_sta_removed_link_stats rem_link_stats;
+ enum ieee80211_sta_smd_state smd_state;
+
/* keep last! */
struct ieee80211_sta sta;
};
@@ -1169,4 +1172,10 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
return r;
}
+int sta_info_move_smd_state(struct sta_info *sta,
+ enum ieee80211_sta_smd_state new_ss,
+ bool notify);
+void sta_info_reset_smd_state(struct sta_info *sta, bool notify);
+int sta_info_move_drv_smd_state(struct sta_info *sta);
+
#endif /* STA_INFO_H */
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 562a4964afa3..294a8a44a125 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -3403,6 +3403,39 @@ TRACE_EVENT(drv_nan_peer_sched_changed,
)
);
+TRACE_EVENT(drv_sta_smd_state,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_smd_state old_state,
+ enum ieee80211_sta_smd_state new_state),
+
+ TP_ARGS(local, sdata, sta, old_state, new_state),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u32, old_state)
+ __field(u32, new_state)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->old_state = old_state;
+ __entry->new_state = new_state;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT
+ " smd_state: %d->%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG,
+ __entry->old_state, __entry->new_state
+ )
+);
+
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 09/18] wifi: mac80211: Send BlockAck policy in AMPDU action
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (7 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 08/18] wifi: mac80211: Add driver_op for SMD substate changes Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 10/18] wifi: mac80211: Define SMD BSS Transition context for transport Pooventhiran G
` (8 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
While sending AMPDU action hints to wireless drivers, mac80211 does not
currently send the BlockAck policy extracted from ADDBA frames. Send the
BlockAck policy as well so that drivers have complete information about
the negotiation. Policy is valid only for IEEE80211_AMPDU_RX_START.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/mac80211.h | 15 +++++++++++++++
net/mac80211/agg-rx.c | 1 +
net/mac80211/trace.h | 8 +++++---
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9b5fa6d6281b..32b9b7cd1685 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3964,6 +3964,19 @@ enum ieee80211_ampdu_mlme_action {
#define IEEE80211_AMPDU_TX_START_IMMEDIATE 1
#define IEEE80211_AMPDU_TX_START_DELAY_ADDBA 2
+/**
+ * enum ieee80211_ba_policy - BlockAck Policy
+ *
+ * @IEEE80211_BLOCKACK_DELAYED: Block ACK sent to the BAR sender in next TXOP
+ * @IEEE80211_BLOCKACK_IMMEDIATE: Block ACK sent to the BAR sender immediately
+ *
+ * This field is valid only for %IEEE80211_AMPDU_RX_START.
+ */
+enum ieee80211_ba_policy {
+ IEEE80211_BLOCKACK_DELAYED,
+ IEEE80211_BLOCKACK_IMMEDIATE,
+};
+
/**
* struct ieee80211_ampdu_params - AMPDU action parameters
*
@@ -3976,6 +3989,7 @@ enum ieee80211_ampdu_mlme_action {
* @buf_size: reorder buffer size (number of subframes). Valid only when the
* action is set to %IEEE80211_AMPDU_RX_START or
* %IEEE80211_AMPDU_TX_OPERATIONAL
+ * @policy: BlockAck policy of type &enum ieee80211_ba_policy
* @amsdu: indicates the peer's ability to receive A-MSDU within A-MPDU.
* valid when the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL
* @timeout: BA session timeout. Valid only when the action is set to
@@ -3987,6 +4001,7 @@ struct ieee80211_ampdu_params {
u16 tid;
u16 ssn;
u16 buf_size;
+ enum ieee80211_ba_policy policy;
bool amsdu;
u16 timeout;
};
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 9629e00069a1..2d67fd883146 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -289,6 +289,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
.amsdu = false,
.timeout = timeout,
.ssn = start_seq_num,
+ .policy = ba_policy,
};
int i, ret = -EOPNOTSUPP;
u16 status = WLAN_STATUS_REQUEST_DECLINED;
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 294a8a44a125..63838a6d5d07 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -116,6 +116,7 @@
__field(u16, ssn) \
__field(u16, buf_size) \
__field(bool, amsdu) \
+ __field(u8, policy) \
__field(u16, timeout) \
__field(u16, action)
#define AMPDU_ACTION_ASSIGN STA_NAMED_ASSIGN(params->sta); \
@@ -123,12 +124,13 @@
__entry->ssn = params->ssn; \
__entry->buf_size = params->buf_size; \
__entry->amsdu = params->amsdu; \
+ __entry->policy = params->policy; \
__entry->timeout = params->timeout; \
__entry->action = params->action;
-#define AMPDU_ACTION_PR_FMT STA_PR_FMT " tid %d, ssn %d, buf_size %u, amsdu %d, timeout %d action %d"
+#define AMPDU_ACTION_PR_FMT STA_PR_FMT " tid %d, ssn %d, buf_size %u, amsdu %d, policy=%u, timeout %d action %d"
#define AMPDU_ACTION_PR_ARG STA_PR_ARG, __entry->tid, __entry->ssn, \
- __entry->buf_size, __entry->amsdu, __entry->timeout, \
- __entry->action
+ __entry->buf_size, __entry->amsdu, __entry->policy, \
+ __entry->timeout, __entry->action
/*
* Tracing for driver callbacks.
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 10/18] wifi: mac80211: Define SMD BSS Transition context for transport
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (8 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 09/18] wifi: mac80211: Send BlockAck policy in AMPDU action Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 11/18] wifi: mac80211: Enable skb extensions along with mac80211 Pooventhiran G
` (7 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
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
subclause 37.16. The station's context data is attached to the ST
Preparation or Execution management frame using SKB extensions so that
userspace receives the context along with the relevant frame.
Define the context data - per-TID sequence numbers in downlink (DL) and
uplink (UL) directions, packet number in DL, per-TID packet numbers in
UL, and per-TID BlockAck session parameters in DL and UL, plus a
variable-length driver context - to be used by the driver to attach the
context to the frame SKB. Additionally, define a helper that attaches
the SMD context to the SKB extension so that the initialization
operations (container allocation, reference tracking, and context
linking) are properly handled.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/linux/ieee80211-uhr.h | 86 +++++++++++++++++++++++++++++++++++++++++++
include/net/mac80211.h | 50 +++++++++++++++++++++++++
2 files changed, 136 insertions(+)
diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h
index c3f87d4c8bec..f904b1e65f10 100644
--- a/include/linux/ieee80211-uhr.h
+++ b/include/linux/ieee80211-uhr.h
@@ -649,6 +649,92 @@ struct ieee80211_uhr_mode_change_tuple {
u8 variable[];
} __packed;
+/*
+ * 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,
+ * subclause 9.4.2.364.
+ */
+#define IEEE80211_SMD_CTX_NUM_TIDS 8
+
+#define IEEE80211_SMD_CTX_MAX_PN_LEN 16
+
+/**
+ * struct ieee80211_smd_ctx_ba - BlockAck parameters for DL and UL
+ *
+ * @amsdu_supported: Peer's capability to support A-MSDU within A-MPDU.
+ * @ba_policy: BlockAck policy (0 = delayed BlockAck, 1 = immediate BlockAck)
+ * @buffer_size: Reorder buffer size from ADDBA Request (10-bit, max 1023)
+ * @timeout: BlockAck session timeout
+ * @ext_no_frag: ADDBA Extension fragmentation support
+ * @extfrag_level: ADDBA Extension HE fragmentation level
+ * @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
+ */
+struct ieee80211_smd_ctx_ba {
+ bool amsdu_supported;
+ u8 ba_policy;
+ u16 buffer_size;
+ u16 timeout;
+ bool ext_no_frag;
+ u8 extfrag_level;
+ u16 ext_buffer_size;
+};
+
+/**
+ * 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
+ * @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 the drivers.
+ */
+struct ieee80211_smd_ctx {
+ DECLARE_BITMAP(valid_ctx_bmap, IEEE80211_SMD_CTX_NUM_VALID_CTX);
+ u8 pn_len;
+
+ struct {
+ DECLARE_BITMAP(valid_tid_bmap, IEEE80211_SMD_CTX_NUM_TIDS);
+ 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];
+ } dl;
+ struct {
+ DECLARE_BITMAP(valid_tid_bmap, IEEE80211_SMD_CTX_NUM_TIDS);
+ 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];
+ } 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)
{
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 32b9b7cd1685..b4b1d3db9b19 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
+#include <linux/skbuff_wireless.h>
#include <linux/ieee80211.h>
#include <linux/lockdep.h>
#include <net/cfg80211.h>
@@ -8246,4 +8247,53 @@ bool ieee80211_vif_nan_started(struct ieee80211_vif *vif);
* Return: 0 if success and non-zero on error
*/
int ieee80211_encrypt_tx_skb(struct sk_buff *skb);
+
+/**
+ * ieee80211_skb_ext_add_uhr - attach or replace a UHR SMD context on
+ * SKB_EXT_WIRELESS
+ * @skb: the SKB to attach the extension to
+ * @ctx: the SMD context; must be a kmalloc-ed buffer by the caller
+ *
+ * Allocates a &struct wireless_skb_ext_smd_ctx container, sets @ctx to it with
+ * @refcnt initialized to 1, and attaches it to @skb on SKB_EXT_WIRELESS.
+ * If SKB_EXT_WIRELESS is already active, the existing @container is released
+ * before the new one is attached.
+ *
+ * Ownership of @ctx transfers to @container on success - the core will
+ * kfree() both when the last reference is released.
+ * Upon error, @ctx ownership is not transferred from the caller; old @container
+ * may get freed.
+ *
+ * Drivers must use this helper to attach SKB_EXT_WIRELESS extension to
+ * properly handle clone and COW copies.
+ *
+ * Returns 0 on success; -ENOMEM on allocation failure.
+ */
+static inline int ieee80211_skb_ext_add_uhr(struct sk_buff *skb,
+ struct ieee80211_smd_ctx *ctx)
+{
+ struct wireless_skb_ext_smd_ctx *container;
+ struct wireless_skb_ext *ext;
+
+ container = kzalloc_obj(*container, GFP_ATOMIC);
+ if (!container)
+ return -ENOMEM;
+
+ refcount_set(&container->refcnt, 1);
+ container->smd_ctx = ctx;
+
+ skb_ext_del(skb, SKB_EXT_WIRELESS);
+
+ ext = skb_ext_add(skb, SKB_EXT_WIRELESS);
+ if (!ext) {
+ kfree(container);
+ return -ENOMEM;
+ }
+
+ ext->uhr_smd_ctx = container;
+ ext->type = WIRELESS_SKB_EXT_UHR_SMD;
+
+ return 0;
+}
+
#endif /* MAC80211_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 11/18] wifi: mac80211: Enable skb extensions along with mac80211
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (9 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 10/18] wifi: mac80211: Define SMD BSS Transition context for transport Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 12/18] wifi: nl80211: Define attributes to pack SMD BSS Transition context Pooventhiran G
` (6 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
When mac80211 module is selected in build configs, enable SKB_EXTENSIONS
as a dependency since mac80211 has started using SKB extensions with SMD
as its first user.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
net/mac80211/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index d6bc295e23a1..10d28985a1ce 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -9,6 +9,7 @@ config MAC80211
select CRYPTO_CCM
select CRYPTO_GCM
select CRC32
+ select SKB_EXTENSIONS
help
This option enables the hardware independent IEEE 802.11
networking stack.
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 12/18] wifi: nl80211: Define attributes to pack SMD BSS Transition context
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (10 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 11/18] wifi: mac80211: Enable skb extensions along with mac80211 Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 13/18] wifi: cfg80211/mac80211: Handle UHR Link Reconfiguration frame Pooventhiran G
` (5 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
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
and get the context during roaming via current AP MLD and roaming via
target AP MLD. Without these, userspace will not be able to transport the
context to the target AP MLD, program the context on the target AP MLD TX
and RX queues, nor fetch the context on behalf of the target AP MLD if
the ST Execution frame is sent directly to the target.
Suggested-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/uapi/linux/nl80211.h | 163 +++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.c | 44 ++++++++++++
2 files changed, 207 insertions(+)
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 65c7eefe9db5..f41266181bf1 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1427,6 +1427,18 @@
* @NL80211_CMD_STOP_PD: Stop the PD operation, identified by
* its %NL80211_ATTR_WDEV interface.
*
+ * @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_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.
+ * @NL80211_CMD_SMD_CTX_EVENT: Event reporting the collected SMD context
+ * (requested via %NL80211_CMD_GET_SMD_CTX) to userspace. It carries
+ * %NL80211_ATTR_MLD_ADDR, %NL80211_ATTR_SMD_CTX_TYPE, and
+ * %NL80211_ATTR_SMD_CTX.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1705,6 +1717,10 @@ enum nl80211_commands {
NL80211_CMD_START_PD,
NL80211_CMD_STOP_PD,
+ NL80211_CMD_SET_SMD_CTX,
+ NL80211_CMD_GET_SMD_CTX,
+ NL80211_CMD_SMD_CTX_EVENT,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -3207,6 +3223,13 @@ 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_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.
+ * @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.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3815,6 +3838,9 @@ enum nl80211_attrs {
NL80211_ATTR_SMD_ROAM_STA,
+ NL80211_ATTR_SMD_CTX,
+ NL80211_ATTR_SMD_CTX_TYPE,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -9247,4 +9273,141 @@ enum nl80211_smd_params_attrs {
NL80211_SMD_PARAMS_ATTR_MAX = __NL80211_SMD_PARAMS_ATTR_LAST - 1
};
+/**
+ * enum nl80211_smd_ctx_type - SMD Request Type values
+ *
+ * Used with %NL80211_ATTR_SMD_CTX_TYPE.
+ *
+ * @NL80211_SMD_CTX_TYPE_PREP: SMD BSS Transition (ST) Preparation.
+ * @NL80211_SMD_CTX_TYPE_EXEC: ST Execution.
+ * @NUM_SMD_CTX_TYPES: internal.
+ * @NL80211_SMD_CTX_TYPE_MAX: Max type of ST context.
+ */
+enum nl80211_smd_ctx_type {
+ NL80211_SMD_CTX_TYPE_PREP,
+ NL80211_SMD_CTX_TYPE_EXEC,
+
+ NUM_SMD_CTX_TYPES,
+ NL80211_SMD_CTX_TYPE_MAX = NUM_SMD_CTX_TYPES - 1,
+};
+
+/**
+ * enum nl80211_smd_ctx_ba_param - SMD Block Ack Context attributes
+ *
+ * @__NL80211_SMD_CTX_BA_ATTR_INVALID: Invalid attribute.
+ * @NL80211_SMD_CTX_BA_ATTR_BUFF_SIZE: Required (u16) BA buffer size.
+ * @NL80211_SMD_CTX_BA_ATTR_POLICY: Required (u8) BA policy.
+ * @NL80211_SMD_CTX_BA_ATTR_AMSDU_SUPPORT: Flag attribute to indicate
+ * AMSDU support.
+ * @NL80211_SMD_CTX_BA_ATTR_TIMEOUT: Required (u16) Block Ack timeout.
+ * @NL80211_SMD_CTX_BA_ATTR_EXT_NO_FRAG: Flag attribute to indicate
+ * fragmentation support.
+ * @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_LAST: Last attribute, used internally.
+ * @NL80211_SMD_CTX_BA_ATTR_MAX: Max attribute.
+ *
+ * Used with %NL80211_SMD_CTX_DL_ATTR_BA_PARAMS and
+ * %NL80211_SMD_CTX_UL_ATTR_BA_PARAMS.
+ */
+enum nl80211_smd_ctx_ba_param {
+ __NL80211_SMD_CTX_BA_ATTR_INVALID,
+ NL80211_SMD_CTX_BA_ATTR_BUFF_SIZE,
+ NL80211_SMD_CTX_BA_ATTR_POLICY,
+ NL80211_SMD_CTX_BA_ATTR_AMSDU_SUPPORT,
+ NL80211_SMD_CTX_BA_ATTR_TIMEOUT,
+ 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_LAST,
+ NL80211_SMD_CTX_BA_ATTR_MAX = __NL80211_SMD_CTX_BA_ATTR_LAST - 1
+};
+
+/**
+ * enum nl80211_smd_ctx_dl - SMD Dynamic Context - DL direction
+ *
+ * @__NL80211_SMD_CTX_DL_ATTR_INVALID: Invalid attribute.
+ * @NL80211_SMD_CTX_DL_ATTR_VALID_TID_BITMAP: Optional (u8) TIDs included.
+ * @NL80211_SMD_CTX_DL_ATTR_SN: Optional (nested) next SN (u16) per TID to
+ * be assigned to DL individually addressed frames.
+ * @NL80211_SMD_CTX_DL_ATTR_PN: Optional (binary) starting PN (max 16 bytes) for
+ * 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_LAST: Last attribute, used internally.
+ * @NL80211_SMD_CTX_DL_ATTR_MAX: Max attribute.
+ *
+ * Used with %NL80211_SMD_CTX_ATTR_DL.
+ */
+enum nl80211_smd_ctx_dl {
+ __NL80211_SMD_CTX_DL_ATTR_INVALID,
+ NL80211_SMD_CTX_DL_ATTR_VALID_TID_BITMAP,
+ NL80211_SMD_CTX_DL_ATTR_SN,
+ NL80211_SMD_CTX_DL_ATTR_PN,
+ NL80211_SMD_CTX_DL_ATTR_BA_PARAMS,
+
+ __NL80211_SMD_CTX_DL_ATTR_LAST,
+ NL80211_SMD_CTX_DL_ATTR_MAX = __NL80211_SMD_CTX_DL_ATTR_LAST - 1
+};
+
+/**
+ * enum nl80211_smd_ctx_ul - SMD Dynamic Context - UL direction
+ *
+ * @__NL80211_SMD_CTX_UL_ATTR_INVALID: Invalid attribute.
+ * @NL80211_SMD_CTX_UL_ATTR_VALID_TID_BITMAP: Optional (u8) TIDs included.
+ * @NL80211_SMD_CTX_UL_ATTR_SN: Optional (nested) last SN received per TID
+ * for UL individually addressed frames.
+ * @NL80211_SMD_CTX_UL_ATTR_PN: Optional (nested) last PN received per TID
+ * 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_LAST: Last attribute, used internally.
+ * @NL80211_SMD_CTX_UL_ATTR_MAX: Max attribute.
+ *
+ * Used with %NL80211_SMD_CTX_ATTR_UL.
+ */
+enum nl80211_smd_ctx_ul {
+ __NL80211_SMD_CTX_UL_ATTR_INVALID,
+ NL80211_SMD_CTX_UL_ATTR_VALID_TID_BITMAP,
+ NL80211_SMD_CTX_UL_ATTR_SN,
+ NL80211_SMD_CTX_UL_ATTR_PN,
+ NL80211_SMD_CTX_UL_ATTR_BA_PARAMS,
+
+ __NL80211_SMD_CTX_UL_ATTR_LAST,
+ NL80211_SMD_CTX_UL_ATTR_MAX = __NL80211_SMD_CTX_UL_ATTR_LAST - 1
+};
+
+/**
+ * enum nl80211_smd_ctx - SMD Context information
+ *
+ * Top-level container for SMD BSS Transition dynamic context. Carried in
+ * %NL80211_ATTR_SMD_CTX with %NL80211_CMD_SET_SMD_CTX (target AP MLD) or
+ * with %NL80211_CMD_FRAME (current AP MLD reporting context to userspace)
+ * and %NL80211_CMD_SMD_CTX_EVENT (current AP MLD reporting the requested
+ * context on behalf of the target AP MLD).
+ *
+ * @__NL80211_SMD_CTX_ATTR_INVALID: Invalid attribute.
+ * @NL80211_SMD_CTX_ATTR_DL: Optional (nested) DL parameters.
+ * 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.
+ */
+enum nl80211_smd_ctx {
+ __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
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 282d3c82dce8..d59f0eb40493 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -704,6 +704,47 @@ nl80211_smd_params_policy[NL80211_SMD_PARAMS_ATTR_MAX + 1] = {
[NL80211_SMD_PARAMS_ATTR_NEIGHBOR_PROBING] = { .type = NLA_FLAG },
};
+static const struct nla_policy
+nl80211_smd_ctx_ba_policy[NL80211_SMD_CTX_BA_ATTR_MAX + 1] = {
+ [NL80211_SMD_CTX_BA_ATTR_BUFF_SIZE] = { .type = NLA_U16 },
+ [NL80211_SMD_CTX_BA_ATTR_POLICY] = { .type = NLA_U8 },
+ [NL80211_SMD_CTX_BA_ATTR_AMSDU_SUPPORT] = { .type = NLA_FLAG },
+ [NL80211_SMD_CTX_BA_ATTR_TIMEOUT] = { .type = NLA_U16 },
+ [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 },
+};
+
+static const struct nla_policy
+nl80211_smd_ctx_dl_policy[NL80211_SMD_CTX_DL_ATTR_MAX + 1] = {
+ [NL80211_SMD_CTX_DL_ATTR_VALID_TID_BITMAP] = { .type = NLA_U8 },
+ [NL80211_SMD_CTX_DL_ATTR_SN] = { .type = NLA_NESTED },
+ [NL80211_SMD_CTX_DL_ATTR_PN] = { .type = NLA_BINARY,
+ .len = IEEE80211_SMD_CTX_MAX_PN_LEN },
+ [NL80211_SMD_CTX_DL_ATTR_BA_PARAMS] =
+ NLA_POLICY_NESTED_ARRAY(nl80211_smd_ctx_ba_policy),
+};
+
+static const struct nla_policy
+nl80211_smd_ctx_ul_policy[NL80211_SMD_CTX_UL_ATTR_MAX + 1] = {
+ [NL80211_SMD_CTX_UL_ATTR_VALID_TID_BITMAP] = { .type = NLA_U8 },
+ [NL80211_SMD_CTX_UL_ATTR_SN] = { .type = NLA_NESTED },
+ [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),
+};
+
+static const struct nla_policy
+nl80211_smd_ctx_policy[NL80211_SMD_CTX_ATTR_MAX + 1] = {
+ [NL80211_SMD_CTX_ATTR_DL] =
+ 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,
@@ -1118,6 +1159,9 @@ 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_SMD_ROAM_STA] = { .type = NLA_FLAG },
+ [NL80211_ATTR_SMD_CTX] = NLA_POLICY_NESTED(nl80211_smd_ctx_policy),
+ [NL80211_ATTR_SMD_CTX_TYPE] =
+ NLA_POLICY_MAX(NLA_U8, NL80211_SMD_CTX_TYPE_MAX),
};
/* policy for the key attributes */
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 13/18] wifi: cfg80211/mac80211: Handle UHR Link Reconfiguration frame
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (11 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 12/18] wifi: nl80211: Define attributes to pack SMD BSS Transition context Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 14/18] wifi: nl80211: Pack SMD dynamic context along with frame Pooventhiran G
` (4 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
The UHR Link Reconfiguration Request frame triggers SMD BSS Transition
on the current AP MLD. At the point where mac80211 forwards this
frame to userspace via nl80211_send_mgmt(), the driver has already
collected the STA's dynamic context (SN, PN, BlockAck state) and
attached it to the SKB via the SKB_EXT_WIRELESS extension. But this
information is not known by nl80211 to pack the context with the frame
sent to userspace.
Add handling for the UHR ST Preparation and Execution request frames
in ieee80211_rx_h_userspace_mgmt() to pull the ieee80211_smd_ctx
pointer from the SKB extension and store it in cfg80211_rx_info, so
that nl80211_send_mgmt() can include it in the NL80211_CMD_FRAME event.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/linux/ieee80211-uhr.h | 59 +++++++++++++++++++++++++++++++++++++++++++
include/net/cfg80211.h | 13 ++++++++++
net/mac80211/rx.c | 22 ++++++++++++++++
3 files changed, 94 insertions(+)
diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h
index f904b1e65f10..cda8251a1a9a 100644
--- a/include/linux/ieee80211-uhr.h
+++ b/include/linux/ieee80211-uhr.h
@@ -743,6 +743,65 @@ 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) && \
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 75d0123fbb5d..7b0f328fec79 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4887,6 +4887,17 @@ 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
*
@@ -9543,6 +9554,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
* @flags: flags, as defined in &enum nl80211_rxmgmt_flags
* @rx_tstamp: Hardware timestamp of frame RX in nanoseconds
* @ack_tstamp: Hardware timestamp of ack TX in nanoseconds
+ * @st_info: SMD BSS Transition data in skb extension
*/
struct cfg80211_rx_info {
int freq;
@@ -9554,6 +9566,7 @@ struct cfg80211_rx_info {
u32 flags;
u64 rx_tstamp;
u64 ack_tstamp;
+ struct cfg80211_smd_transition_info st_info;
};
/**
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 40ed03a775ba..156a4c0d55ac 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3993,6 +3993,24 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
+static void
+ieee80211_rx_h_userspace_mgmt_st_frame(struct cfg80211_rx_info *info,
+ struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *)info->buf;
+ struct wireless_skb_ext *ext;
+ u8 type;
+
+ type = mgmt->u.action.uhr_link_reconf_req.type;
+
+ ext = skb_ext_find(skb, SKB_EXT_WIRELESS);
+ if (!ext || ext->type != WIRELESS_SKB_EXT_UHR_SMD)
+ return;
+
+ info->st_info.type = type;
+ info->st_info.ctx = ext->uhr_smd_ctx->smd_ctx;
+}
+
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
{
@@ -4003,6 +4021,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
.len = rx->skb->len,
.link_id = rx->link_id,
.have_link_id = rx->link_id >= 0,
+ .st_info.ctx = NULL,
};
/* skip known-bad action frames and return them in the next handler */
@@ -4024,6 +4043,9 @@ 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_frame(&info, rx->skb);
}
if (cfg80211_rx_mgmt_ext(&rx->sdata->wdev, &info)) {
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 14/18] wifi: nl80211: Pack SMD dynamic context along with frame
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (12 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 13/18] wifi: cfg80211/mac80211: Handle UHR Link Reconfiguration frame Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 15/18] wifi: nl80211/cfg80211: Add support for SMD context programming Pooventhiran G
` (3 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
While packing the received management frame in nl80211_send_mgmt() to
send to userspace, nest the SMD dynamic context along with the frame
message. This shall be used by userspace to transport the context to the
target AP MLD for programming the dynamic state of the STA session for
seamless roaming as part of SMD BSS Transition.
Suggested-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Co-developed-by: Aditya Sathish <asathish@qti.qualcomm.com>
Signed-off-by: Aditya Sathish <asathish@qti.qualcomm.com>
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
net/wireless/nl80211.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 268 insertions(+), 1 deletion(-)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d59f0eb40493..5065dc75b0bc 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -19568,6 +19568,265 @@ 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(void)
+{
+ size_t n = 0;
+
+ n += nla_total_size(0); /* ba_params nest header */
+ n += n_dl_tids * (nla_total_size(0) + /* tid nest header */
+ nla_total_size(sizeof(u16)) + /* buff_size */
+ nla_total_size(sizeof(u8)) + /* policy */
+ nla_total_size(0) + /* amsdu_support */
+ nla_total_size(sizeof(u16)) + /* timeout */
+ nla_total_size(0) + /* ext_no_frag */
+ nla_total_size(sizeof(u8)) + /* ext_frag_lvl */
+ nla_total_size(sizeof(u16))); /* ext_buff_size */
+
+ return n;
+}
+
+static size_t
+nl80211_smd_ctx_nl_size(struct cfg80211_smd_transition_info *st_info)
+{
+ const struct ieee80211_smd_ctx *ctx = st_info->ctx;
+ int n_dl_tids = 0, n_ul_tids = 0;
+ u8 pn_len = ctx->pn_len;
+ 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 += nla_total_size(sizeof(u8)); /* ST type */
+
+ /* outer NL80211_ATTR_SMD_CTX nest */
+ n += nla_total_size(0); /* nest header */
+
+ /* DL nest */
+ n += nla_total_size(0); /* DL nest header */
+
+ n += nla_total_size(sizeof(u8)); /* valid_tid_bitmap */
+
+ if (test_bit(IEEE80211_SMD_CTX_VALID_DL_SN, ctx->valid_ctx_bmap)) {
+ n += nla_total_size(0); /* DL SN nest header */
+ n += n_dl_tids * nla_total_size(sizeof(u16)); /* DL SN */
+ }
+
+ if (test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap))
+ 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(); /* DL BA params */
+
+ /* 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)) {
+ n += nla_total_size(0); /* UL SN nest header */
+ n += n_ul_tids * nla_total_size(sizeof(u16)); /* UL SN */
+ }
+
+ if (test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap)) {
+ n += nla_total_size(0); /* UL PN nest header */
+ n += n_ul_tids * nla_total_size(pn_len); /* UL PN */
+ }
+
+ if (test_bit(IEEE80211_SMD_CTX_VALID_BA_PARAMS, ctx->valid_ctx_bmap))
+ n += _nl80211_smd_ctx_nl_ba_param_size(); /* UL BA params */
+
+ if (ctx->drv_ctx_size)
+ n += nla_total_size(ctx->drv_ctx_size); /* Driver data */
+
+ return n;
+}
+
+static int
+nl80211_put_smd_ctx_ba_params(struct sk_buff *msg,
+ const unsigned long *tid_bmap,
+ const struct ieee80211_smd_ctx_ba *ba_tids,
+ bool dl)
+{
+ u32 ba_params_attr = NL80211_SMD_CTX_DL_ATTR_BA_PARAMS;
+ struct nlattr *dl_ul_ba = NULL, *tid_ba = NULL;
+ u8 tid;
+
+ if (!dl)
+ ba_params_attr = NL80211_SMD_CTX_UL_ATTR_BA_PARAMS;
+
+ dl_ul_ba = nla_nest_start(msg, ba_params_attr);
+ if (!dl_ul_ba)
+ return -ENOBUFS;
+
+ for_each_set_bit(tid, tid_bmap, IEEE80211_SMD_CTX_NUM_TIDS) {
+ const struct ieee80211_smd_ctx_ba *ba = &ba_tids[tid];
+
+ tid_ba = nla_nest_start(msg, tid + 1);
+ if (!tid_ba)
+ goto nla_fail;
+
+ if (nla_put_u16(msg, NL80211_SMD_CTX_BA_ATTR_BUFF_SIZE,
+ ba->buffer_size) ||
+ nla_put_u8(msg, NL80211_SMD_CTX_BA_ATTR_POLICY,
+ ba->ba_policy) ||
+ (ba->amsdu_supported &&
+ nla_put_flag(msg,
+ NL80211_SMD_CTX_BA_ATTR_AMSDU_SUPPORT)) ||
+ nla_put_u16(msg, NL80211_SMD_CTX_BA_ATTR_TIMEOUT,
+ ba->timeout) ||
+ (ba->ext_no_frag &&
+ nla_put_flag(msg, NL80211_SMD_CTX_BA_ATTR_EXT_NO_FRAG)) ||
+ 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)) {
+ nla_nest_cancel(msg, tid_ba);
+ goto nla_fail;
+ }
+
+ nla_nest_end(msg, tid_ba);
+ }
+
+ nla_nest_end(msg, dl_ul_ba);
+ return 0;
+
+nla_fail:
+ nla_nest_cancel(msg, dl_ul_ba);
+ return -ENOBUFS;
+}
+
+static int nl80211_put_smd_ctx(struct sk_buff *msg,
+ struct cfg80211_smd_transition_info *st_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;
+ u8 tid;
+
+ /* ST Type */
+ if (nla_put_u8(msg, NL80211_ATTR_SMD_CTX_TYPE, st_info->type))
+ return -ENOBUFS;
+
+ smd_ctx = nla_nest_start(msg, NL80211_ATTR_SMD_CTX);
+ if (!smd_ctx)
+ return -ENOBUFS;
+
+ ctx = st_info->ctx;
+
+ /* Build DL context */
+ dl_tid_bmap = bitmap_read(ctx->dl.valid_tid_bmap, 0,
+ IEEE80211_SMD_CTX_NUM_TIDS);
+
+ 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))
+ goto nla_fail_dl;
+
+ if (test_bit(IEEE80211_SMD_CTX_VALID_DL_SN, ctx->valid_ctx_bmap)) {
+ 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,
+ IEEE80211_SMD_CTX_NUM_TIDS) {
+ if (nla_put_u16(msg, tid + 1, ctx->dl.sn[tid])) {
+ nla_nest_cancel(msg, dl_sn);
+ goto nla_fail_dl;
+ }
+ }
+ nla_nest_end(msg, dl_sn);
+ }
+
+ if (test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap) &&
+ 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,
+ ctx->dl.ba, true))
+ 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 = 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))
+ goto nla_fail_ul;
+
+ if (test_bit(IEEE80211_SMD_CTX_VALID_UL_SN, ctx->valid_ctx_bmap)) {
+ 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,
+ IEEE80211_SMD_CTX_NUM_TIDS) {
+ if (nla_put_u16(msg, tid + 1, ctx->ul.sn[tid])) {
+ nla_nest_cancel(msg, ul_sn);
+ goto nla_fail_ul;
+ }
+ }
+ nla_nest_end(msg, ul_sn);
+ }
+
+ if (ctx->pn_len &&
+ test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap)) {
+ 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,
+ IEEE80211_SMD_CTX_NUM_TIDS) {
+ if (nla_put(msg, tid + 1, ctx->pn_len,
+ ctx->ul.pn[tid])) {
+ nla_nest_cancel(msg, ul_pn);
+ goto nla_fail_ul;
+ }
+ }
+ 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,
+ ctx->ul.ba, false))
+ goto nla_fail_ul;
+
+ nla_nest_end(msg, 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;
+
+ nla_nest_end(msg, smd_ctx);
+
+ return 0;
+
+nla_fail_ul:
+ nla_nest_cancel(msg, ul);
+
+nla_fail_dl:
+ nla_nest_cancel(msg, dl);
+
+nla_fail:
+ nla_nest_cancel(msg, smd_ctx);
+ return -EINVAL;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -22193,10 +22452,14 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct cfg80211_rx_info *info, gfp_t gfp)
{
struct net_device *netdev = wdev->netdev;
+ int st_roaming_data_len = 0;
struct sk_buff *msg;
void *hdr;
- msg = nlmsg_new(100 + info->len, gfp);
+ if (info->st_info.ctx)
+ st_roaming_data_len = nl80211_smd_ctx_nl_size(&info->st_info);
+
+ msg = nlmsg_new(100 + info->len + st_roaming_data_len, gfp);
if (!msg)
return -ENOMEM;
@@ -22230,6 +22493,10 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
NL80211_ATTR_PAD)))
goto nla_put_failure;
+ if (st_roaming_data_len &&
+ nl80211_put_smd_ctx(msg, &info->st_info))
+ goto nla_put_failure;
+
genlmsg_end(msg, hdr);
return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 15/18] wifi: nl80211/cfg80211: Add support for SMD context programming
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (13 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 14/18] wifi: nl80211: Pack SMD dynamic context along with frame Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 16/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX Pooventhiran G
` (2 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
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
on the TX and RX queues to enable seamless transition and reception.
Such data include Sequence Number (SN), Packet Number (PN), BlockAck
sessions, and any driver-specific data.
Add nl80211 support to handle NL80211_CMD_SET_SMD_CTX, parse the SMD
context attributes, and invoke the cfg80211 operation on the target AP
MLD.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/cfg80211.h | 7 +
net/wireless/nl80211.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/rdev-ops.h | 14 ++
net/wireless/trace.h | 47 +++++++
4 files changed, 400 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7b0f328fec79..b68f5f6837be 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5329,6 +5329,10 @@ struct cfg80211_smd_transition_info {
*
* @start_pd: Start the PD interface.
* @stop_pd: Stop the PD interface.
+ *
+ * @set_smd_ctx: Set UHR SMD context data for the non-AP MLD. @st_info->ctx is
+ * freed immediately after this call returns; drivers must copy
+ * @st_info->ctx if asynchronous processing is required.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -5707,6 +5711,9 @@ struct cfg80211_ops {
bool val);
int (*start_pd)(struct wiphy *wiphy, struct wireless_dev *wdev);
void (*stop_pd)(struct wiphy *wiphy, struct wireless_dev *wdev);
+ int (*set_smd_ctx)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *addr,
+ struct cfg80211_smd_transition_info *st_info);
};
/*
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5065dc75b0bc..70b8697e5dac 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -19827,6 +19827,293 @@ static int nl80211_put_smd_ctx(struct sk_buff *msg,
return -EINVAL;
}
+static int
+nl80211_parse_smd_ctx_ba_params(struct nlattr *tb,
+ struct ieee80211_smd_ctx_ba *ba_tids,
+ unsigned long *valid_ctx_bmap)
+{
+ struct nlattr *ba_tb[NL80211_SMD_CTX_BA_ATTR_MAX + 1];
+ struct ieee80211_smd_ctx_ba *ba;
+ struct nlattr *ba_attr;
+ 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)
+ return -EINVAL;
+
+ if (nla_parse_nested(ba_tb, NL80211_SMD_CTX_BA_ATTR_MAX,
+ ba_attr, nl80211_smd_ctx_ba_policy,
+ NULL))
+ return -EINVAL;
+
+ ba = &ba_tids[tid];
+
+ if (ba_tb[NL80211_SMD_CTX_BA_ATTR_BUFF_SIZE])
+ ba->buffer_size =
+ nla_get_u16(ba_tb[NL80211_SMD_CTX_BA_ATTR_BUFF_SIZE]);
+
+ if (ba_tb[NL80211_SMD_CTX_BA_ATTR_POLICY])
+ ba->ba_policy =
+ nla_get_u8(ba_tb[NL80211_SMD_CTX_BA_ATTR_POLICY]);
+
+ ba->amsdu_supported =
+ !!ba_tb[NL80211_SMD_CTX_BA_ATTR_AMSDU_SUPPORT];
+
+ if (ba_tb[NL80211_SMD_CTX_BA_ATTR_TIMEOUT])
+ ba->timeout =
+ nla_get_u16(ba_tb[NL80211_SMD_CTX_BA_ATTR_TIMEOUT]);
+
+ ba->ext_no_frag = !!ba_tb[NL80211_SMD_CTX_BA_ATTR_EXT_NO_FRAG];
+
+ if (ba_tb[NL80211_SMD_CTX_BA_ATTR_EXT_FRAG_LEVEL])
+ ba->extfrag_level =
+ nla_get_u8(ba_tb[NL80211_SMD_CTX_BA_ATTR_EXT_FRAG_LEVEL]);
+
+ if (ba_tb[NL80211_SMD_CTX_BA_ATTR_EXT_BUFF_SIZE])
+ ba->ext_buffer_size =
+ nla_get_u16(ba_tb[NL80211_SMD_CTX_BA_ATTR_EXT_BUFF_SIZE]);
+
+ n_tids++;
+ }
+
+ if (!n_tids)
+ return -EINVAL;
+
+ set_bit(IEEE80211_SMD_CTX_VALID_BA_PARAMS, valid_ctx_bmap);
+
+ return 0;
+}
+
+static int nl80211_parse_smd_ctx(struct genl_info *info,
+ struct cfg80211_smd_transition_info *st_info)
+{
+ struct nlattr *dl_tb[NL80211_SMD_CTX_DL_ATTR_MAX + 1];
+ struct nlattr *ul_tb[NL80211_SMD_CTX_UL_ATTR_MAX + 1];
+ 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]);
+
+ if (nla_parse_nested(tb, NL80211_SMD_CTX_ATTR_MAX,
+ info->attrs[NL80211_ATTR_SMD_CTX],
+ nl80211_smd_ctx_policy, NULL)) {
+ GENL_SET_ERR_MSG(info, "failed to parse SMD context");
+ 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);
+ if (!ctx)
+ return -ENOMEM;
+
+ st_info->ctx = ctx;
+
+ /* DL Context */
+ if (tb[NL80211_SMD_CTX_ATTR_DL]) {
+ struct nlattr *ba_attr;
+
+ if (nla_parse_nested(dl_tb, NL80211_SMD_CTX_DL_ATTR_MAX,
+ tb[NL80211_SMD_CTX_ATTR_DL],
+ nl80211_smd_ctx_dl_policy, NULL)) {
+ GENL_SET_ERR_MSG(info, "failed to parse DL context");
+ goto err_free_ctx;
+ }
+
+ 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);
+
+ if (dl_tb[NL80211_SMD_CTX_DL_ATTR_SN]) {
+ struct nlattr *sn_attr;
+ int n_tids = 0;
+ int rem, tid;
+
+ nla_for_each_nested(sn_attr,
+ dl_tb[NL80211_SMD_CTX_DL_ATTR_SN],
+ rem) {
+ tid = nla_type(sn_attr) - 1;
+ if (tid < 0 ||
+ tid >= IEEE80211_SMD_CTX_NUM_TIDS) {
+ GENL_SET_ERR_MSG(info,
+ "invalid DL SN tid");
+ goto err_free_ctx;
+ }
+
+ if (nla_len(sn_attr) != sizeof(u16)) {
+ GENL_SET_ERR_MSG(info,
+ "invalid DL SN len");
+ goto err_free_ctx;
+ }
+
+ ctx->dl.sn[tid] = nla_get_u16(sn_attr);
+ n_tids++;
+ }
+
+ if (!n_tids) {
+ GENL_SET_ERR_MSG(info, "empty DL SN nesting");
+ goto err_free_ctx;
+ }
+
+ set_bit(IEEE80211_SMD_CTX_VALID_DL_SN,
+ ctx->valid_ctx_bmap);
+ }
+
+ if (dl_tb[NL80211_SMD_CTX_DL_ATTR_PN]) {
+ const u8 *pn_data =
+ nla_data(dl_tb[NL80211_SMD_CTX_DL_ATTR_PN]);
+
+ ctx->pn_len =
+ nla_len(dl_tb[NL80211_SMD_CTX_DL_ATTR_PN]);
+
+ memcpy(ctx->dl.pn, pn_data, ctx->pn_len);
+ set_bit(IEEE80211_SMD_CTX_VALID_PN,
+ ctx->valid_ctx_bmap);
+ }
+
+ 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;
+ }
+ }
+
+ /* UL Context */
+ if (tb[NL80211_SMD_CTX_ATTR_UL]) {
+ struct nlattr *ba_attr;
+
+ if (nla_parse_nested(ul_tb, NL80211_SMD_CTX_UL_ATTR_MAX,
+ tb[NL80211_SMD_CTX_ATTR_UL],
+ nl80211_smd_ctx_ul_policy, NULL)) {
+ GENL_SET_ERR_MSG(info, "failed to parse UL context");
+ goto err_free_ctx;
+ }
+
+ 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);
+
+ if (ul_tb[NL80211_SMD_CTX_UL_ATTR_SN]) {
+ struct nlattr *sn_attr;
+ int n_tids = 0;
+ int rem, tid;
+
+ nla_for_each_nested(sn_attr,
+ ul_tb[NL80211_SMD_CTX_UL_ATTR_SN],
+ rem) {
+ tid = nla_type(sn_attr) - 1;
+ if (tid < 0 ||
+ tid >= IEEE80211_SMD_CTX_NUM_TIDS) {
+ GENL_SET_ERR_MSG(info,
+ "invalid UL SN tid");
+ goto err_free_ctx;
+ }
+
+ if (nla_len(sn_attr) != sizeof(u16)) {
+ GENL_SET_ERR_MSG(info,
+ "invalid UL SN len");
+ goto err_free_ctx;
+ }
+
+ ctx->ul.sn[tid] = nla_get_u16(sn_attr);
+ n_tids++;
+ }
+
+ if (!n_tids) {
+ GENL_SET_ERR_MSG(info, "empty UL SN nesting");
+ goto err_free_ctx;
+ }
+
+ set_bit(IEEE80211_SMD_CTX_VALID_UL_SN,
+ ctx->valid_ctx_bmap);
+ }
+
+ if (ul_tb[NL80211_SMD_CTX_UL_ATTR_PN]) {
+ struct nlattr *pn_attr;
+ int n_tids = 0;
+ int rem, tid;
+
+ nla_for_each_nested(pn_attr,
+ ul_tb[NL80211_SMD_CTX_UL_ATTR_PN],
+ rem) {
+ u8 pn_len;
+
+ tid = nla_type(pn_attr) - 1;
+ if (tid < 0 ||
+ tid >= IEEE80211_SMD_CTX_NUM_TIDS) {
+ GENL_SET_ERR_MSG(info,
+ "invalid UL PN tid");
+ goto err_free_ctx;
+ }
+
+ /*
+ * PN length must be the same for all DL and
+ * UL TIDs.
+ */
+ pn_len = nla_len(pn_attr);
+ if (pn_len > IEEE80211_SMD_CTX_MAX_PN_LEN ||
+ (ctx->pn_len && pn_len != ctx->pn_len)) {
+ GENL_SET_ERR_MSG(info,
+ "invalid PN length");
+ goto err_free_ctx;
+ }
+ ctx->pn_len = pn_len;
+
+ memcpy(ctx->ul.pn[tid], nla_data(pn_attr),
+ ctx->pn_len);
+ n_tids++;
+ }
+
+ if (!n_tids) {
+ GENL_SET_ERR_MSG(info, "empty UL PN nesting");
+ goto err_free_ctx;
+ }
+
+ set_bit(IEEE80211_SMD_CTX_VALID_PN,
+ ctx->valid_ctx_bmap);
+ }
+
+ 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;
+ }
+ }
+
+ /* 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);
+ }
+
+ return 0;
+
+err_free_ctx:
+ kfree(ctx);
+ st_info->ctx = NULL;
+ return -EINVAL;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -20168,6 +20455,44 @@ static int nl80211_set_sar_specs(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_set_smd_ctx(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_smd_transition_info st_info = {};
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *addr;
+ int err;
+
+ if (!rdev->ops->set_smd_ctx)
+ return -EOPNOTSUPP;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_AP_VLAN)
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MLD_ADDR] ||
+ !info->attrs[NL80211_ATTR_SMD_CTX] ||
+ !info->attrs[NL80211_ATTR_SMD_CTX_TYPE]) {
+ GENL_SET_ERR_MSG(info, "required attributes are missing");
+ return -EINVAL;
+ }
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]);
+
+ err = nl80211_parse_smd_ctx(info, &st_info);
+ if (err)
+ return err;
+
+ /*
+ * Drivers must copy @ctx if processing is deferred after
+ * returning from this function.
+ */
+ err = rdev_set_smd_ctx(rdev, wdev, addr, &st_info);
+ kfree(st_info.ctx);
+ return err;
+}
+
#define SELECTOR(__sel, name, value) \
((__sel) == (value)) ? NL80211_IFL_SEL_##name :
int __missing_selector(void);
@@ -21069,6 +21394,13 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.flags = GENL_ADMIN_PERM,
.internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
},
+ {
+ .cmd = NL80211_CMD_SET_SMD_CTX,
+ .doit = nl80211_set_smd_ctx,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_CLEAR_SKB),
+ },
};
static struct genl_family nl80211_fam __ro_after_init = {
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 46849fe8d0b3..d33a969606d7 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1636,4 +1636,18 @@ rdev_set_epcs(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int rdev_set_smd_ctx(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, const u8 *addr,
+ struct cfg80211_smd_transition_info *st_info)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_rdev_set_smd_ctx(&rdev->wiphy, wdev, addr, st_info);
+ if (rdev->ops->set_smd_ctx)
+ ret = rdev->ops->set_smd_ctx(&rdev->wiphy, wdev, addr, st_info);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+
+ return ret;
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 8c2a91b85c39..6234ceb9ebb1 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -4405,6 +4405,53 @@ TRACE_EVENT(cfg80211_nan_channel_evac,
TP_printk(WDEV_PR_FMT ", " WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
WDEV_PR_ARG, WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
);
+
+TRACE_EVENT(rdev_set_smd_ctx,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, const u8 *addr,
+ struct cfg80211_smd_transition_info *st_info),
+
+ TP_ARGS(wiphy, wdev, addr, st_info),
+
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ 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))
+ ),
+
+ 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);
+ ),
+
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT
+ ", sta=%pM, ST type=%u DL TIDs: %*pb, UL TIDs: %*pb",
+ 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))
+);
+
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 16/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (14 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 15/18] wifi: nl80211/cfg80211: Add support for SMD context programming Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 17/18] wifi: nl80211/cfg80211: Add support for querying SMD context for target AP MLD Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 18/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_GET_SMD_CTX Pooventhiran G
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
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@oss.qualcomm.com>
---
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
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 17/18] wifi: nl80211/cfg80211: Add support for querying SMD context for target AP MLD
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (15 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 16/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 18/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_GET_SMD_CTX Pooventhiran G
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
During SMD BSS Transition (ST), the non-AP MLD that has been prepared
with a target AP MLD may send the ST Execution frame directly to the
target AP MLD. In such scenarios, the target AP MLD should pull the
context from the current AP MLD to program TX and RX queues.
Add nl80211 support to handle NL80211_CMD_GET_SMD_CTX, which is sent by
userspace of the current AP MLD on behalf of the target AP MLD. This
command carries the context type to indicate to the drivers which context
needs to be collected. Once the collection is complete, the context is
sent back to userspace as an asynchronous multicast event using
NL80211_CMD_SMD_CTX_EVENT with full context data carried in
NL80211_ATTR_SMD_CTX.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/cfg80211.h | 53 ++++++++++++++++++
net/wireless/core.c | 25 +++++++++
net/wireless/mlme.c | 32 +++++++++++
net/wireless/nl80211.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++--
net/wireless/nl80211.h | 9 +++
net/wireless/rdev-ops.h | 14 +++++
net/wireless/trace.h | 26 +++++++++
7 files changed, 298 insertions(+), 4 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index b68f5f6837be..d72d1feb0ae3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4898,6 +4898,33 @@ struct cfg80211_smd_transition_info {
enum nl80211_smd_ctx_type type;
};
+/**
+ * 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
+ */
+struct cfg80211_smd_get_ctx_pending {
+ struct list_head list;
+ u8 sta_addr[ETH_ALEN];
+};
+
+/**
+ * cfg80211_get_smd_ctx_done - deliver async GET_SMD_CTX result to nl80211
+ * @wdev: wireless device that handled the request
+ * @sta_addr: non-AP MLD address the context was collected for
+ * @st_info: SMD BSS Transition Info (@st_info or @st_info->ctx may be NULL to
+ * indicate failure)
+ *
+ * Called via ieee80211_get_smd_ctx_done after async driver context collection
+ * completes. Finds the matching pending request and sends a multicast
+ * %NL80211_CMD_SMD_CTX_EVENT reply to userspace (MLME group).
+ *
+ * Return: None
+ */
+void cfg80211_get_smd_ctx_done(struct wireless_dev *wdev,
+ const u8 *sta_addr,
+ struct cfg80211_smd_transition_info *st_info);
+
/**
* struct cfg80211_ops - backend description for wireless configuration
*
@@ -5333,6 +5360,8 @@ struct cfg80211_smd_transition_info {
* @set_smd_ctx: Set UHR SMD context data for the non-AP MLD. @st_info->ctx is
* freed immediately after this call returns; drivers must copy
* @st_info->ctx if asynchronous processing is required.
+ *
+ * @get_smd_ctx: Get UHR SMD context data for the non-AP MLD.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -5714,6 +5743,9 @@ struct cfg80211_ops {
int (*set_smd_ctx)(struct wiphy *wiphy, struct wireless_dev *wdev,
const u8 *addr,
struct cfg80211_smd_transition_info *st_info);
+ int (*get_smd_ctx)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *addr,
+ enum nl80211_smd_ctx_type type);
};
/*
@@ -7286,6 +7318,8 @@ enum ieee80211_ap_reg_power {
* @links.cac_time_ms: CAC time in ms
* @valid_links: bitmap describing what elements of @links are valid
* @radio_mask: Bitmask of radios that this interface is allowed to operate on.
+ * @smd_get_ctx_lock: lock to protect pending get_smd_ctx requests.
+ * @smd_get_ctx_pending_list: list to track pending get_smd_ctx requests.
*/
struct wireless_dev {
struct wiphy *wiphy;
@@ -7405,6 +7439,10 @@ struct wireless_dev {
} links[IEEE80211_MLD_MAX_NUM_LINKS];
u16 valid_links;
+ /* protects @smd_get_ctx_pending_list */
+ spinlock_t smd_get_ctx_lock;
+ struct list_head smd_get_ctx_pending_list;
+
u32 radio_mask;
};
@@ -11081,4 +11119,19 @@ void cfg80211_incumbent_signal_notify(struct wiphy *wiphy,
u32 signal_interference_bitmap,
gfp_t gfp);
+/**
+ * cfg80211_free_pending_smd_ctx_req - Free remaining entries in the
+ * cfg80211_smd_get_ctx_pending list.
+ * @wdev: wdev that maintains the list
+ * @addr: if @addr is NULL, drain the entire list; otherwise, drain only the
+ * matching entries
+ *
+ * When the current AP MLD requests the station's context, those requests are
+ * maintained in a pending list until drivers notify completion. In case the
+ * device is brought down or the station is removed prematurely, free up the
+ * remaining entries in the list.
+ */
+void cfg80211_free_pending_smd_ctx_req(struct wireless_dev *wdev,
+ const u8 *addr);
+
#endif /* __NET_CFG80211_H */
diff --git a/net/wireless/core.c b/net/wireless/core.c
index d13310fef691..129af6ba5fda 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1405,6 +1405,25 @@ void wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked,
}
EXPORT_SYMBOL(wiphy_rfkill_set_hw_state_reason);
+void cfg80211_free_pending_smd_ctx_req(struct wireless_dev *wdev,
+ const u8 *addr)
+{
+ struct cfg80211_smd_get_ctx_pending *pending, *tmp;
+
+ spin_lock_bh(&wdev->smd_get_ctx_lock);
+ list_for_each_entry_safe(pending, tmp,
+ &wdev->smd_get_ctx_pending_list,
+ list) {
+ if (!addr || ether_addr_equal(pending->sta_addr, addr)) {
+ list_del(&pending->list);
+ kfree(pending);
+ dev_put(wdev->netdev);
+ }
+ }
+ spin_unlock_bh(&wdev->smd_get_ctx_lock);
+}
+EXPORT_SYMBOL(cfg80211_free_pending_smd_ctx_req);
+
static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
bool unregister_netdev)
{
@@ -1477,6 +1496,10 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
}
}
+ if (wdev->iftype == NL80211_IFTYPE_AP ||
+ wdev->iftype == NL80211_IFTYPE_AP_VLAN)
+ cfg80211_free_pending_smd_ctx_req(wdev, NULL);
+
wdev->connected = false;
}
@@ -1619,6 +1642,8 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
INIT_LIST_HEAD(&wdev->pmsr_list);
spin_lock_init(&wdev->pmsr_lock);
wiphy_work_init(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
+ spin_lock_init(&wdev->smd_get_ctx_lock);
+ INIT_LIST_HEAD(&wdev->smd_get_ctx_pending_list);
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index a0d1cde26f0c..fa60e391f9bc 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -1475,3 +1475,35 @@ void cfg80211_mlo_reconf_add_done(struct net_device *dev,
nl80211_mlo_reconf_add_done(dev, data);
}
EXPORT_SYMBOL(cfg80211_mlo_reconf_add_done);
+
+void cfg80211_get_smd_ctx_done(struct wireless_dev *wdev,
+ const u8 *sta_addr,
+ struct cfg80211_smd_transition_info *st_info)
+{
+ struct cfg80211_smd_get_ctx_pending *pending = NULL, *iter;
+
+ spin_lock_bh(&wdev->smd_get_ctx_lock);
+ list_for_each_entry(iter, &wdev->smd_get_ctx_pending_list, list) {
+ if (ether_addr_equal(iter->sta_addr, sta_addr)) {
+ pending = iter;
+ list_del(&pending->list);
+ break;
+ }
+ }
+ spin_unlock_bh(&wdev->smd_get_ctx_lock);
+
+ if (!pending) {
+ wiphy_err(wdev->wiphy,
+ "No pending SMD ctx req for %pM found in %s\n",
+ sta_addr, __func__);
+ return;
+ }
+
+ nl80211_notify_get_smd_ctx_done(wdev, sta_addr,
+ (st_info && st_info->ctx) ? st_info :
+ NULL);
+
+ dev_put(wdev->netdev);
+ kfree(pending);
+}
+EXPORT_SYMBOL(cfg80211_get_smd_ctx_done);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 70b8697e5dac..47454ea2983b 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -19585,8 +19585,7 @@ static size_t _nl80211_smd_ctx_nl_ba_param_size(void)
return n;
}
-static size_t
-nl80211_smd_ctx_nl_size(struct cfg80211_smd_transition_info *st_info)
+size_t nl80211_smd_ctx_nl_size(struct cfg80211_smd_transition_info *st_info)
{
const struct ieee80211_smd_ctx *ctx = st_info->ctx;
int n_dl_tids = 0, n_ul_tids = 0;
@@ -19698,8 +19697,8 @@ nl80211_put_smd_ctx_ba_params(struct sk_buff *msg,
return -ENOBUFS;
}
-static int nl80211_put_smd_ctx(struct sk_buff *msg,
- struct cfg80211_smd_transition_info *st_info)
+int nl80211_put_smd_ctx(struct sk_buff *msg,
+ struct cfg80211_smd_transition_info *st_info)
{
struct nlattr *dl_sn = NULL, *ul_sn = NULL, *ul_pn = NULL;
struct nlattr *smd_ctx = NULL, *dl = NULL, *ul = NULL;
@@ -20493,6 +20492,95 @@ static int nl80211_set_smd_ctx(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static struct cfg80211_smd_get_ctx_pending *
+__cfg80211_get_smd_sta_pending_ctx(struct wireless_dev *wdev, const u8 *addr)
+{
+ struct cfg80211_smd_get_ctx_pending *pending = NULL, *tmp;
+
+ lockdep_assert_held(&wdev->smd_get_ctx_lock);
+
+ list_for_each_entry(tmp, &wdev->smd_get_ctx_pending_list, list) {
+ if (ether_addr_equal(tmp->sta_addr, addr)) {
+ pending = tmp;
+ break;
+ }
+ }
+
+ return pending;
+}
+
+static int nl80211_get_smd_ctx(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_smd_get_ctx_pending *pending;
+ enum nl80211_smd_ctx_type type;
+ const u8 *addr = NULL;
+ int err;
+
+ if (!rdev->ops->get_smd_ctx)
+ return -EOPNOTSUPP;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_AP_VLAN)
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MLD_ADDR] ||
+ !info->attrs[NL80211_ATTR_SMD_CTX_TYPE]) {
+ GENL_SET_ERR_MSG(info, "required attributes are missing");
+ return -EINVAL;
+ }
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]);
+ type = nla_get_u8(info->attrs[NL80211_ATTR_SMD_CTX_TYPE]);
+
+ spin_lock_bh(&wdev->smd_get_ctx_lock);
+ if (__cfg80211_get_smd_sta_pending_ctx(wdev, addr)) {
+ spin_unlock_bh(&wdev->smd_get_ctx_lock);
+ return -EBUSY;
+ }
+ /*
+ * Concurrent NL80211_CMD_GET_SMD_CTX commands are serialized by
+ * wiphy_lock in nl80211_pre_doit, so safe to unlock here.
+ */
+ spin_unlock_bh(&wdev->smd_get_ctx_lock);
+
+ pending = kzalloc_obj(*pending);
+ if (!pending)
+ return -ENOMEM;
+
+ memcpy(pending->sta_addr, addr, ETH_ALEN);
+
+ /*
+ * Take reference and add to the list before calling the driver to avoid
+ * race. Upon success, the reference is released in
+ * cfg80211_get_smd_ctx_done() or the list removal and drain path.
+ */
+ dev_hold(wdev->netdev);
+
+ spin_lock_bh(&wdev->smd_get_ctx_lock);
+ list_add_tail(&pending->list, &wdev->smd_get_ctx_pending_list);
+ spin_unlock_bh(&wdev->smd_get_ctx_lock);
+
+ err = rdev_get_smd_ctx(rdev, wdev, addr, type);
+ if (err) {
+ /* revert upon failure */
+ spin_lock_bh(&wdev->smd_get_ctx_lock);
+ pending = __cfg80211_get_smd_sta_pending_ctx(wdev, addr);
+ if (pending) {
+ list_del(&pending->list);
+ kfree(pending);
+ dev_put(wdev->netdev);
+ }
+ spin_unlock_bh(&wdev->smd_get_ctx_lock);
+ return err;
+ }
+
+ /* Reply will arrive via cfg80211_get_smd_ctx_done() */
+ return 0;
+}
+
#define SELECTOR(__sel, name, value) \
((__sel) == (value)) ? NL80211_IFL_SEL_##name :
int __missing_selector(void);
@@ -21401,6 +21489,12 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_CLEAR_SKB),
},
+ {
+ .cmd = NL80211_CMD_GET_SMD_CTX,
+ .doit = nl80211_get_smd_ctx,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
+ },
};
static struct genl_family nl80211_fam __ro_after_init = {
@@ -24426,6 +24520,47 @@ void cfg80211_nan_channel_evac(struct wireless_dev *wdev,
}
EXPORT_SYMBOL(cfg80211_nan_channel_evac);
+void
+nl80211_notify_get_smd_ctx_done(struct wireless_dev *wdev,
+ const u8 *sta_addr,
+ struct cfg80211_smd_transition_info *st_info)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct net_device *dev = wdev->netdev;
+ size_t msg_len = NLMSG_DEFAULT_SIZE;
+ struct sk_buff *msg;
+ void *hdr;
+
+ if (st_info && st_info->ctx)
+ msg_len = 100 + nl80211_smd_ctx_nl_size(st_info) +
+ nla_total_size(ETH_ALEN); /* MLD address */
+
+ msg = nlmsg_new(msg_len, GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SMD_CTX_EVENT);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN, sta_addr))
+ goto nla_put_failure;
+
+ if (st_info && st_info->ctx && nl80211_put_smd_ctx(msg, st_info))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, GFP_ATOMIC);
+ return;
+
+nla_put_failure:
+ nlmsg_free(msg);
+}
+
/* initialisation/exit functions */
int __init nl80211_init(void)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index bdb065d14054..26e647dff866 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -129,4 +129,13 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info);
void nl80211_mlo_reconf_add_done(struct net_device *dev,
struct cfg80211_mlo_reconf_done_data *data);
+void
+nl80211_notify_get_smd_ctx_done(struct wireless_dev *wdev,
+ const u8 *sta_addr,
+ struct cfg80211_smd_transition_info *st_info);
+
+size_t nl80211_smd_ctx_nl_size(struct cfg80211_smd_transition_info *st_info);
+int nl80211_put_smd_ctx(struct sk_buff *msg,
+ struct cfg80211_smd_transition_info *st_info);
+
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index d33a969606d7..ab520560f604 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1650,4 +1650,18 @@ static inline int rdev_set_smd_ctx(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int rdev_get_smd_ctx(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, const u8 *addr,
+ enum nl80211_smd_ctx_type type)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_rdev_get_smd_ctx(&rdev->wiphy, wdev, addr, type);
+ if (rdev->ops->get_smd_ctx)
+ ret = rdev->ops->get_smd_ctx(&rdev->wiphy, wdev, addr, type);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+
+ return ret;
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 6234ceb9ebb1..a2662919b8f0 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -4452,6 +4452,32 @@ TRACE_EVENT(rdev_set_smd_ctx,
(unsigned long *)__get_dynamic_array(rx_tid_bitmap))
);
+TRACE_EVENT(rdev_get_smd_ctx,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, const u8 *addr,
+ enum nl80211_smd_ctx_type type),
+
+ TP_ARGS(wiphy, wdev, addr, type),
+
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ MAC_ENTRY(sta_addr)
+ __field(u8, st_type)
+ ),
+
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ MAC_ASSIGN(sta_addr, addr);
+ __entry->st_type = type;
+ ),
+
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT
+ ", sta=%pM, ST type=%u",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->sta_addr,
+ __entry->st_type)
+);
+
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH wireless-next 18/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_GET_SMD_CTX
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
` (16 preceding siblings ...)
2026-09-07 20:29 ` [PATCH wireless-next 17/18] wifi: nl80211/cfg80211: Add support for querying SMD context for target AP MLD Pooventhiran G
@ 2026-09-07 20:29 ` Pooventhiran G
17 siblings, 0 replies; 19+ messages in thread
From: Pooventhiran G @ 2026-09-07 20:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Johannes Berg
Cc: linux-kernel, netdev, linux-wireless, Pooventhiran G
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;
this context is used by the target AP MLD to program its TX and RX
queues to support seamless roaming of the non-AP MLD.
Implement the get_smd_ctx cfg80211_op in mac80211 to invoke the driver
to collect the requested station context. Because context collection is
asynchronous, the driver signals completion later via
ieee80211_get_smd_ctx_done(), which packs the collected context into
NL80211_CMD_SMD_CTX_EVENT and delivers it to userspace as a multicast
event.
Signed-off-by: Pooventhiran G <pooventhiran.g@oss.qualcomm.com>
---
include/net/mac80211.h | 23 +++++++++++++++++++++++
net/mac80211/cfg.c | 34 ++++++++++++++++++++++++++++++++++
net/mac80211/driver-ops.h | 24 ++++++++++++++++++++++++
net/mac80211/sta_info.c | 6 ++++++
net/mac80211/trace.h | 27 +++++++++++++++++++++++++++
5 files changed, 114 insertions(+)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e0e13f0ed31f..f16112e41594 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2528,6 +2528,21 @@ enum ieee80211_sta_state {
IEEE80211_STA_AUTHORIZED,
};
+/**
+ * ieee80211_get_smd_ctx_done - Deliver async get_smd_ctx result to nl80211
+ * @vif: virtual interface
+ * @sta_addr: non-AP MLD address the context was collected for
+ * @st_info: SMD BSS Transition Info (@st_info or @st_info->ctx may be NULL to
+ * indicate failure). @st_info is owned by the driver.
+ *
+ * Called by the driver after the %NL80211_CMD_GET_SMD_CTX context collection
+ * is complete. mac80211 forwards the result to cfg80211 which finds the pending
+ * nl80211 request and sends reply to userspace.
+ */
+void ieee80211_get_smd_ctx_done(struct ieee80211_vif *vif,
+ const u8 *sta_addr,
+ struct cfg80211_smd_transition_info *st_info);
+
/**
* enum ieee80211_sta_rx_bandwidth - station RX bandwidth
* @IEEE80211_STA_RX_BW_20: station can only receive 20 MHz
@@ -4782,6 +4797,11 @@ enum ieee80211_sta_smd_state {
* 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.
+ * @get_smd_ctx: Get the UHR SMD context for the non-AP MLD. This is used in
+ * the current AP MLD side on behalf of the target AP MLD to collect
+ * dynamic context, when the non-AP MLD sends ST Execute frame directly
+ * to the target. Since the response is asynchronous, the drivers that
+ * implement this should not store reference to @sta.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -5191,6 +5211,9 @@ struct ieee80211_ops {
int (*set_smd_ctx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct cfg80211_smd_transition_info *st_info);
+ int (*get_smd_ctx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum nl80211_smd_ctx_type type);
};
/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 268d5f42293a..a33ce96a4752 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -6104,6 +6104,39 @@ static int ieee80211_set_smd_ctx(struct wiphy *wiphy, struct wireless_dev *wdev,
return drv_set_smd_ctx(local, sdata, &sta->sta, st_info);
}
+static int ieee80211_get_smd_ctx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *addr,
+ enum nl80211_smd_ctx_type type)
+{
+ 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_get_smd_ctx(local, sdata, &sta->sta, type);
+}
+
+void ieee80211_get_smd_ctx_done(struct ieee80211_vif *vif,
+ const u8 *sta_addr,
+ struct cfg80211_smd_transition_info *st_info)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ cfg80211_get_smd_ctx_done(&sdata->wdev, sta_addr, st_info);
+}
+EXPORT_SYMBOL(ieee80211_get_smd_ctx_done);
+
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -6223,4 +6256,5 @@ 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,
.set_smd_ctx = ieee80211_set_smd_ctx,
+ .get_smd_ctx = ieee80211_get_smd_ctx,
};
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index e764dd394de2..0339ff71cd4a 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1844,4 +1844,28 @@ static inline int drv_set_smd_ctx(struct ieee80211_local *local,
return ret;
}
+static inline int drv_get_smd_ctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ enum nl80211_smd_ctx_type type)
+{
+ int ret;
+
+ if (!sdata || !local->ops->get_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_get_smd_ctx(local, sdata, sta, type);
+ ret = local->ops->get_smd_ctx(&local->hw, &sdata->vif, sta, type);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 5aa6f5b481e3..9ea0a8791289 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1312,6 +1312,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
{
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
+ struct wireless_dev *wdev;
int ret, i;
might_sleep();
@@ -1321,6 +1322,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
local = sta->local;
sdata = sta->sdata;
+ wdev = &sdata->wdev;
lockdep_assert_wiphy(local->hw.wiphy);
@@ -1390,6 +1392,10 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
rcu_access_pointer(sdata->u.vlan.sta) == sta)
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ cfg80211_free_pending_smd_ctx_req(wdev, sta->addr);
+
return 0;
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 7aba79e441cf..38ad5076232d 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -3487,6 +3487,33 @@ TRACE_EVENT(drv_set_smd_ctx,
)
);
+TRACE_EVENT(drv_get_smd_ctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ enum nl80211_smd_ctx_type type),
+
+ TP_ARGS(local, sdata, sta, type),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u8, st_type)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->st_type = type;
+ ),
+
+ TP_printk(LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT ", ST type=%u",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->st_type
+ )
+);
+
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
--
2.34.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-09-07 20:34 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-07 20:29 [PATCH wireless-next 00/18] wifi: Add Seamless Mobility Domain (SMD) AP support Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 01/18] net: skbuff: Add SKB extension support to wireless drivers Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 02/18] wifi: nl80211: Define Seamless Mobility Domain (SMD) device capability Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 03/18] wifi: nl80211: Add kernel interfaces for Seamless Mobility Domain setup Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 04/18] wifi: cfg80211/mac80211: Configure AP with SMD capabilities Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 05/18] wifi: cfg80211/mac80211: Parse SMD parameters in STA addition/modification Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 06/18] wifi: nl80211/cfg80211: Indicate STA creation via SMD BSS Transition Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 07/18] wifi: nl80211/mac80211: Add SMD BSS Transition sub-state STA flags Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 08/18] wifi: mac80211: Add driver_op for SMD substate changes Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 09/18] wifi: mac80211: Send BlockAck policy in AMPDU action Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 10/18] wifi: mac80211: Define SMD BSS Transition context for transport Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 11/18] wifi: mac80211: Enable skb extensions along with mac80211 Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 12/18] wifi: nl80211: Define attributes to pack SMD BSS Transition context Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 13/18] wifi: cfg80211/mac80211: Handle UHR Link Reconfiguration frame Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 14/18] wifi: nl80211: Pack SMD dynamic context along with frame Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 15/18] wifi: nl80211/cfg80211: Add support for SMD context programming Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 16/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 17/18] wifi: nl80211/cfg80211: Add support for querying SMD context for target AP MLD Pooventhiran G
2026-09-07 20:29 ` [PATCH wireless-next 18/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_GET_SMD_CTX Pooventhiran G
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox