* [PATCH v2 wireless-next 15/15] wifi: mac80211_hwsim: switch to use TXQs
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
Simply immediately TX all available packets on the corresponding queue.
This removes the HWSIM_TM_CMD_STOP_QUEUES/HWSIM_TM_CMD_RESUME_QUEUES
feature for now. It should be reasonably simple to add it back if it is
needed.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/virtual/mac80211_hwsim_main.c | 20 ++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index c6d1f841c04d..e510357c1411 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -2198,6 +2198,17 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
ieee80211_tx_status_irqsafe(hw, skb);
}
+static void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ieee80211_tx_control control = {
+ .sta = txq->sta,
+ };
+ struct sk_buff *skb;
+
+ while ((skb = ieee80211_tx_dequeue(hw, txq)))
+ mac80211_hwsim_tx(hw, &control, skb);
+}
static int mac80211_hwsim_start(struct ieee80211_hw *hw)
{
@@ -2943,11 +2954,7 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw,
goto nla_put_failure;
return cfg80211_testmode_reply(skb);
case HWSIM_TM_CMD_STOP_QUEUES:
- ieee80211_stop_queues(hw);
- return 0;
case HWSIM_TM_CMD_WAKE_QUEUES:
- ieee80211_wake_queues(hw);
- return 0;
default:
return -EOPNOTSUPP;
}
@@ -4051,7 +4058,7 @@ static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw,
#define HWSIM_COMMON_OPS \
.tx = mac80211_hwsim_tx, \
- .wake_tx_queue = ieee80211_handle_wake_tx_queue, \
+ .wake_tx_queue = ieee80211_hwsim_wake_tx_queue, \
.start = mac80211_hwsim_start, \
.stop = mac80211_hwsim_stop, \
.add_interface = mac80211_hwsim_add_interface, \
@@ -5530,6 +5537,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
ieee80211_hw_set(hw, TDLS_WIDER_BW);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
ieee80211_hw_set(hw, STRICT);
+ ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);
+ ieee80211_hw_set(hw, STA_MMPDU_TXQ);
if (param->mlo) {
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
@@ -5588,6 +5597,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
hw->sta_data_size = sizeof(struct hwsim_sta_priv);
hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv);
+ hw->txq_data_size = 0;
memcpy(data->channels_2ghz, hwsim_channels_2ghz,
sizeof(hwsim_channels_2ghz));
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 14/15] wifi: mac80211_hwsim: refactor NAN timer handling
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
Refactor the NAN timer to more closely track the where in the NAN
schedule the device currently is. Do this by having an hrtimer that
fires at the start of every slot.
For now continue to update the current channel at the start of the DW.
In the future, the correct channel according to the schedule should be
used everywhere.
This is in preparation to more accurately simulate more of the NAN
logic.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/virtual/mac80211_hwsim_i.h | 5 +
.../wireless/virtual/mac80211_hwsim_main.c | 22 +--
.../net/wireless/virtual/mac80211_hwsim_nan.c | 160 ++++++++++++------
.../net/wireless/virtual/mac80211_hwsim_nan.h | 8 +-
4 files changed, 133 insertions(+), 62 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
index d345595ca588..6b2a5dccb106 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -128,6 +128,11 @@ struct mac80211_hwsim_data {
extern spinlock_t hwsim_radio_lock;
extern struct list_head hwsim_radios;
+ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data,
+ u64 tsf);
+u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data,
+ ktime_t ts);
+
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index ba2aa09b37cb..c6d1f841c04d 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1216,12 +1216,18 @@ static inline u64 mac80211_hwsim_get_sim_tsf(void)
return ktime_to_us(ktime_get_boottime());
}
-static ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data,
- u64 tsf)
+ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data,
+ u64 tsf)
{
return us_to_ktime(tsf - data->tsf_offset);
}
+u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data,
+ ktime_t ts)
+{
+ return ktime_to_us(ts + data->tsf_offset);
+}
+
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -2045,12 +2051,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
* on channel 6 or channel 149, unless a ROC is in progress (for
* USD use cases).
*/
- if (data->nan.curr_dw_band == NL80211_BAND_2GHZ)
- channel = ieee80211_get_channel(hw->wiphy, 2437);
- else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
- channel = ieee80211_get_channel(hw->wiphy, 5745);
- else
- channel = NULL;
+ channel = data->nan.channel;
if (WARN_ON(!channel)) {
ieee80211_free_txskb(hw, skb);
@@ -5451,8 +5452,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED |
NAN_DEV_CAPA_NDPE_SUPPORTED;
- hrtimer_setup(&data->nan.timer, mac80211_hwsim_nan_dw_start,
- CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT);
+ hrtimer_setup(&data->nan.slot_timer,
+ mac80211_hwsim_nan_slot_timer,
+ CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
}
data->if_combination.radar_detect_widths =
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
index 5d7c736d7972..f4191954ca9d 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
@@ -6,63 +6,132 @@
#include "mac80211_hwsim_i.h"
+/* Defined as the lower 23 bits being zero */
+#define DW0_TSF_MASK GENMASK(22, 0)
+
+/* DWs are repeated every 512 TUs */
+#define DWST_TU 512
+#define DWST_TSF_MASK (ieee80211_tu_to_usec(DWST_TU) - 1)
+
+#define SLOT_TU 16
+#define SLOT_TSF_MASK (ieee80211_tu_to_usec(DWST_TU) - 1)
+
+/* The 2.4 GHz DW is at the start, the 5 GHz is in slot 8 (after 128 TUs) */
+#define DW_5G_OFFSET_TU 128
+
+#define SLOT_24GHZ_DW 0
+#define SLOT_5GHZ_DW (DW_5G_OFFSET_TU / SLOT_TU)
+
+/* The special DW0 happens every 16 DWSTs (8192 TUs) */
+static_assert(16 * DWST_TU * 1024 == 8192 * 1024);
+static_assert(DW0_TSF_MASK + 1 == 8192 * 1024);
+
static u8 hwsim_nan_cluster_id[ETH_ALEN];
+static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data)
+{
+ ktime_t expires = hrtimer_get_expires(&data->nan.slot_timer);
+
+ return mac80211_hwsim_boottime_to_tsf(data, expires);
+}
+
+static u8 hwsim_nan_slot_from_tsf(u64 tsf)
+{
+ return (tsf & DWST_TSF_MASK) / ieee80211_tu_to_usec(SLOT_TU);
+}
+
+static void
+mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot)
+{
+ u64 tsf = hwsim_nan_get_timer_tsf(data);
+
+ /* Only called by mac80211_hwsim_nan_dw_timer from softirq context */
+ lockdep_assert_in_softirq();
+
+ tsf &= ~DWST_TSF_MASK;
+ tsf += ieee80211_tu_to_usec(slot * SLOT_TU);
+
+ hrtimer_set_expires(&data->nan.slot_timer,
+ mac80211_hwsim_tsf_to_boottime(data, tsf));
+}
+
+static void
+mac80211_hwsim_nan_exec_state_transitions(struct mac80211_hwsim_data *data)
+{
+ /*
+ * Handle NAN role and state transitions at the end of the DW period
+ * in accordance to Wi-Fi Aware version 4.0 section 3.3.7 point 2, i.e.
+ * end of 5 GHz DW if enabled else at the end of the 2.4 GHz DW.
+ *
+ * TODO: Implement
+ */
+}
+
enum hrtimer_restart
-mac80211_hwsim_nan_dw_start(struct hrtimer *timer)
+mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
{
struct mac80211_hwsim_data *data =
container_of(timer, struct mac80211_hwsim_data,
- nan.timer);
+ nan.slot_timer);
struct ieee80211_hw *hw = data->hw;
- u64 orig_tsf = mac80211_hwsim_get_tsf(hw, NULL), tsf = orig_tsf;
- u32 dw_int = 512 * 1024;
- u64 until_dw;
+ struct ieee80211_channel *notify_dw_chan = NULL;
+ u64 tsf = hwsim_nan_get_timer_tsf(data);
+ u8 slot = hwsim_nan_slot_from_tsf(tsf);
+ bool dwst_of_dw0 = false;
+ bool dw_end = false;
if (!data->nan.device_vif)
return HRTIMER_NORESTART;
- if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
- if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) {
- dw_int = 128 * 1024;
- data->nan.curr_dw_band = NL80211_BAND_5GHZ;
- } else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) {
- data->nan.curr_dw_band = NL80211_BAND_2GHZ;
- }
- }
+ if ((tsf & DW0_TSF_MASK & ~DWST_TSF_MASK) == 0)
+ dwst_of_dw0 = true;
- until_dw = dw_int - do_div(tsf, dw_int);
- /* The timer might fire just before the actual DW, in which case
- * update the timeout to the actual next DW
- */
- if (until_dw < dw_int / 2)
- until_dw += dw_int;
+ switch (slot) {
+ case SLOT_24GHZ_DW:
+ wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n",
+ dwst_of_dw0);
+ data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437);
+ break;
- /* The above do_div() call directly modifies the 'tsf' variable, thus,
- * use a copy so that the print below would show the original TSF.
- */
- wiphy_debug(hw->wiphy,
- "%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n",
- __func__, orig_tsf, data->nan.curr_dw_band,
- until_dw);
+ case SLOT_24GHZ_DW + 1:
+ if (!(data->nan.bands & BIT(NL80211_BAND_5GHZ))) {
+ notify_dw_chan = ieee80211_get_channel(hw->wiphy, 2437);
+ dw_end = true;
+ } else {
+ notify_dw_chan = ieee80211_get_channel(hw->wiphy, 5745);
+ }
+ break;
- hrtimer_forward_now(&data->nan.timer,
- ns_to_ktime(until_dw * NSEC_PER_USEC));
+ case SLOT_5GHZ_DW:
+ if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
+ wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n");
+ data->nan.channel =
+ ieee80211_get_channel(hw->wiphy, 5745);
+ }
+ break;
+
+ case SLOT_5GHZ_DW + 1:
+ if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
+ notify_dw_chan =
+ ieee80211_get_channel(hw->wiphy, 2437);
+ dw_end = true;
+ }
+ break;
+ }
+
+ if (dw_end)
+ mac80211_hwsim_nan_exec_state_transitions(data);
- if (data->nan.notify_dw) {
- struct ieee80211_channel *ch;
+ if (data->nan.notify_dw && notify_dw_chan) {
struct wireless_dev *wdev =
ieee80211_vif_to_wdev(data->nan.device_vif);
- if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
- ch = ieee80211_get_channel(hw->wiphy, 5745);
- else
- ch = ieee80211_get_channel(hw->wiphy, 2437);
-
- cfg80211_next_nan_dw_notif(wdev, ch, GFP_ATOMIC);
+ cfg80211_next_nan_dw_notif(wdev, notify_dw_chan, GFP_ATOMIC);
}
+ mac80211_hwsim_nan_schedule_slot(data, slot + 1);
+
return HRTIMER_RESTART;
}
@@ -71,9 +140,6 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
struct cfg80211_nan_conf *conf)
{
struct mac80211_hwsim_data *data = hw->priv;
- u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
- u32 dw_int = 512 * 1000;
- u64 until_dw = dw_int - do_div(tsf, dw_int);
struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
if (vif->type != NL80211_IFTYPE_NAN)
@@ -85,13 +151,11 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
/* set this before starting the timer, as preemption might occur */
data->nan.device_vif = vif;
data->nan.bands = conf->bands;
- data->nan.curr_dw_band = NL80211_BAND_2GHZ;
+ data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437);
- wiphy_debug(hw->wiphy, "nan_started, next_dw=%llu\n",
- until_dw);
-
- hrtimer_start(&data->nan.timer,
- ns_to_ktime(until_dw * NSEC_PER_USEC),
+ /* Just run this "soon" and start in a random schedule position */
+ hrtimer_start(&data->nan.slot_timer,
+ ns_to_ktime(10 * NSEC_PER_USEC),
HRTIMER_MODE_REL_SOFT);
if (conf->cluster_id && !is_zero_ether_addr(conf->cluster_id) &&
@@ -125,7 +189,7 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
data->nan.device_vif != vif)
return -EINVAL;
- hrtimer_cancel(&data->nan.timer);
+ hrtimer_cancel(&data->nan.slot_timer);
data->nan.device_vif = NULL;
spin_lock_bh(&hwsim_radio_lock);
@@ -159,10 +223,8 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes);
/* Handle only the changes we care about for simulation purposes */
- if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) {
+ if (changes & CFG80211_NAN_CONF_CHANGED_BANDS)
data->nan.bands = conf->bands;
- data->nan.curr_dw_band = NL80211_BAND_2GHZ;
- }
if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
data->nan.notify_dw = conf->enable_dw_notification;
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
index eac64ac37589..e86e7f9e9a3c 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
@@ -11,13 +11,15 @@ struct mac80211_hwsim_nan_data {
struct ieee80211_vif *device_vif;
u8 bands;
- enum nl80211_band curr_dw_band;
- struct hrtimer timer;
+ /* Current channel of the NAN device */
+ struct ieee80211_channel *channel;
+
+ struct hrtimer slot_timer;
bool notify_dw;
};
enum hrtimer_restart
-mac80211_hwsim_nan_dw_start(struct hrtimer *timer);
+mac80211_hwsim_nan_slot_timer(struct hrtimer *timer);
int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 13/15] wifi: mac80211_hwsim: register beacon timer by calculating TBTT
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
It is easy to calculate the next target beacon transmission time (TBTT)
based on the current TSF and the beacon interval. Use this method to
calculate the time to the next beacon.
With this, the bcn_delta variable can be removed and drift over time due
to the timer firing late is fully avoided.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/virtual/mac80211_hwsim_i.h | 1 -
.../wireless/virtual/mac80211_hwsim_main.c | 39 ++++++++++++-------
2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
index b4d0a3869619..d345595ca588 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -103,7 +103,6 @@ struct mac80211_hwsim_data {
/* difference between this hw's clock and the real clock, in usecs */
s64 tsf_offset;
- s64 bcn_delta;
/* Stats */
u64 tx_pkts;
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 5bf6541498ec..ba2aa09b37cb 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1216,6 +1216,12 @@ static inline u64 mac80211_hwsim_get_sim_tsf(void)
return ktime_to_us(ktime_get_boottime());
}
+static ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data,
+ u64 tsf)
+{
+ return us_to_ktime(tsf - data->tsf_offset);
+}
+
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -1237,8 +1243,6 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
{
struct mac80211_hwsim_data *data = hw->priv;
u64 now = mac80211_hwsim_get_tsf(hw, vif);
- /* MLD not supported here */
- u32 bcn_int = data->link_data[0].beacon_int;
u64 delta = abs(tsf - now);
struct ieee80211_bss_conf *conf;
@@ -1247,13 +1251,10 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
return;
/* adjust after beaconing with new timestamp at old TBTT */
- if (tsf > now) {
+ if (tsf > now)
data->tsf_offset += delta;
- data->bcn_delta = do_div(delta, bcn_int);
- } else {
+ else
data->tsf_offset -= delta;
- data->bcn_delta = -(s64)do_div(delta, bcn_int);
- }
}
static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
@@ -2410,7 +2411,9 @@ mac80211_hwsim_beacon(struct hrtimer *timer)
container_of(link_data, struct mac80211_hwsim_data,
link_data[link_data->link_id]);
struct ieee80211_hw *hw = data->hw;
- u64 bcn_int = link_data->beacon_int;
+ u32 remainder;
+ u64 tsf_now;
+ u64 tbtt;
if (!data->started)
return HRTIMER_NORESTART;
@@ -2419,13 +2422,19 @@ mac80211_hwsim_beacon(struct hrtimer *timer)
hw, IEEE80211_IFACE_ITER_NORMAL,
mac80211_hwsim_beacon_tx, link_data);
- /* beacon at new TBTT + beacon interval */
- if (data->bcn_delta) {
- bcn_int -= data->bcn_delta;
- data->bcn_delta = 0;
- }
- hrtimer_forward_now(&link_data->beacon_timer,
- ns_to_ktime(bcn_int * NSEC_PER_USEC));
+ /* TSF is the same for all VIFs, parameter is unused */
+ tsf_now = mac80211_hwsim_get_tsf(hw, NULL);
+
+ /* Wrap value to be after the next TBTT */
+ tbtt = tsf_now + link_data->beacon_int;
+
+ /* Round TBTT down to the correct time */
+ div_u64_rem(tbtt, link_data->beacon_int, &remainder);
+ tbtt = tbtt - remainder;
+
+ hrtimer_set_expires(&link_data->beacon_timer,
+ mac80211_hwsim_tsf_to_boottime(data, tbtt));
+
return HRTIMER_RESTART;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 12/15] wifi: mac80211_hwsim: move timestamp writing later in the datapath
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
By delegating writing the timestamp into beacons and probe responses, we
can remove the abs_bcn_ts from the global data and still avoid any time
offset issues. This also seems conceptually closer to "real" hardware
where the timestamp will be written late in the TX path.
Move sending the SKB to the monitor interface to happen later, so that
the frame timestamp has the value filled in by mac80211_hwsim.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/virtual/mac80211_hwsim_i.h | 2 -
.../wireless/virtual/mac80211_hwsim_main.c | 110 ++++++++----------
2 files changed, 49 insertions(+), 63 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
index 741eb08f8a85..b4d0a3869619 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -104,8 +104,6 @@ struct mac80211_hwsim_data {
/* difference between this hw's clock and the real clock, in usecs */
s64 tsf_offset;
s64 bcn_delta;
- /* absolute beacon transmission time. Used to cover up "tx" delay. */
- u64 abs_bcn_ts;
/* Stats */
u64 tx_pkts;
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 3bda5532ab62..5bf6541498ec 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1523,6 +1523,43 @@ static inline u16 trans_tx_rate_flags_ieee2hwsim(struct ieee80211_tx_rate *rate)
return result;
}
+static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data,
+ struct sk_buff *skb, u64 sim_time)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct ieee80211_rate *txrate;
+ /* TODO: get MCS */
+ int bitrate = 100;
+
+ txrate = ieee80211_get_tx_rate(data->hw, info);
+ if (txrate)
+ bitrate = txrate->bitrate;
+
+ if (skb->len >= offsetofend(typeof(*mgmt), u.probe_resp.timestamp) &&
+ ieee80211_is_probe_resp(hdr->frame_control)) {
+ mgmt->u.probe_resp.timestamp =
+ cpu_to_le64(sim_time + data->tsf_offset +
+ 24 * 8 * 10 / bitrate);
+ } else if (skb->len >= offsetofend(typeof(*mgmt), u.beacon.timestamp) &&
+ ieee80211_is_beacon(mgmt->frame_control)) {
+ mgmt->u.beacon.timestamp = cpu_to_le64(sim_time +
+ data->tsf_offset +
+ 24 * 8 * 10 /
+ bitrate);
+ } else if (skb->len >= offsetofend(struct ieee80211_ext,
+ u.s1g_beacon.timestamp) &&
+ ieee80211_is_s1g_beacon(mgmt->frame_control)) {
+ struct ieee80211_ext *ext = (void *)mgmt;
+
+ ext->u.s1g_beacon.timestamp = cpu_to_le32(sim_time +
+ data->tsf_offset +
+ 10 * 8 * 10 /
+ bitrate);
+ }
+}
+
static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
struct sk_buff *my_skb,
int dst_portid,
@@ -1538,6 +1575,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
struct hwsim_tx_rate_flag tx_attempts_flags[IEEE80211_TX_MAX_RATES];
uintptr_t cookie;
+ u64 sim_tsf;
if (data->ps != PS_DISABLED)
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
@@ -1550,6 +1588,9 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
}
}
+ sim_tsf = mac80211_hwsim_get_sim_tsf();
+ mac80211_hwsim_write_tsf(data, my_skb, sim_tsf);
+
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (skb == NULL)
goto nla_put_failure;
@@ -1781,7 +1822,11 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rx_status rx_status;
- u64 sim_tsf;
+ u64 sim_tsf = mac80211_hwsim_get_sim_tsf();
+
+ mac80211_hwsim_write_tsf(data, skb, sim_tsf);
+
+ mac80211_hwsim_monitor_rx(hw, skb, chan);
memset(&rx_status, 0, sizeof(rx_status));
rx_status.flag |= RX_FLAG_MACTIME_START;
@@ -1824,20 +1869,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
skb_ext_reset(skb);
nf_reset_ct(skb);
- /*
- * Get absolute mactime here so all HWs RX at the "same time", and
- * absolute TX time for beacon mactime so the timestamp matches.
- * Giving beacons a different mactime than non-beacons looks messy, but
- * it helps the Toffset be exact and a ~10us mactime discrepancy
- * probably doesn't really matter.
- */
if (ieee80211_is_beacon(hdr->frame_control) ||
- ieee80211_is_probe_resp(hdr->frame_control)) {
+ ieee80211_is_probe_resp(hdr->frame_control))
rx_status.boottime_ns = ktime_get_boottime_ns();
- sim_tsf = data->abs_bcn_ts;
- } else {
- sim_tsf = mac80211_hwsim_get_sim_tsf();
- }
/* Copy skb to all enabled radios that are on the current frequency */
spin_lock(&hwsim_radio_lock);
@@ -2137,27 +2171,6 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
return;
}
- if (skb->len >= 24 + 8 &&
- ieee80211_is_probe_resp(hdr->frame_control)) {
- /* fake header transmission time */
- struct ieee80211_mgmt *mgmt;
- struct ieee80211_rate *txrate;
- /* TODO: get MCS */
- int bitrate = 100;
- u64 ts;
-
- mgmt = (struct ieee80211_mgmt *)skb->data;
- txrate = ieee80211_get_tx_rate(hw, txi);
- if (txrate)
- bitrate = txrate->bitrate;
- ts = mac80211_hwsim_get_sim_tsf();
- mgmt->u.probe_resp.timestamp =
- cpu_to_le64(ts + data->tsf_offset +
- 24 * 8 * 10 / bitrate);
- }
-
- mac80211_hwsim_monitor_rx(hw, skb, channel);
-
/* wmediumd mode check */
_portid = READ_ONCE(data->wmediumd);
@@ -2291,8 +2304,6 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
ARRAY_SIZE(txi->control.rates));
}
- mac80211_hwsim_monitor_rx(hw, skb, chan);
-
if (_portid || hwsim_virtio_enabled)
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, chan);
@@ -2310,10 +2321,6 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf,
{
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct ieee80211_tx_info *info;
- struct ieee80211_rate *txrate;
- struct ieee80211_mgmt *mgmt;
- /* TODO: get MCS */
- int bitrate = 100;
if (vp->skip_beacons[link_conf->link_id]) {
vp->skip_beacons[link_conf->link_id]--;
@@ -2327,27 +2334,6 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf,
info->control.rates,
ARRAY_SIZE(info->control.rates));
- txrate = ieee80211_get_tx_rate(hw, info);
- if (txrate)
- bitrate = txrate->bitrate;
-
- mgmt = (struct ieee80211_mgmt *) skb->data;
- /* fake header transmission time */
- data->abs_bcn_ts = mac80211_hwsim_get_sim_tsf();
- if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
- struct ieee80211_ext *ext = (void *) mgmt;
-
- ext->u.s1g_beacon.timestamp = cpu_to_le32(data->abs_bcn_ts +
- data->tsf_offset +
- 10 * 8 * 10 /
- bitrate);
- } else {
- mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts +
- data->tsf_offset +
- 24 * 8 * 10 /
- bitrate);
- }
-
mac80211_hwsim_tx_frame(hw, skb,
rcu_dereference(link_conf->chanctx_conf)->def.chan);
}
@@ -6033,6 +6019,8 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
if (!found)
goto out;
+ mac80211_hwsim_monitor_rx(data2->hw, skb, data2->channel);
+
/* Tx info received because the frame was broadcasted on user space,
so we get all the necessary info: tx attempts and skb control buff */
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 11/15] wifi: mac80211_hwsim: rename and switch simulation time to boottime
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
The mac80211_hwsim base time for the simulation of the TSF was based on
the real time of the system. This clock is subject to unexpected
changes. Switch it to use boottime which is always monotonic and also
continues to run through times where the system is suspended.
Also change the function name from tsf_raw to sim_tsf to better
differentiate between the TSF of the mac and the TSF base of the
simulation.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/virtual/mac80211_hwsim_main.c | 33 ++++++++++---------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 4ad2c6f38663..3bda5532ab62 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1211,22 +1211,25 @@ static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
-static inline u64 mac80211_hwsim_get_tsf_raw(void)
+static inline u64 mac80211_hwsim_get_sim_tsf(void)
{
- return ktime_to_us(ktime_get_real());
-}
-
-static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data)
-{
- u64 now = mac80211_hwsim_get_tsf_raw();
- return cpu_to_le64(now + data->tsf_offset);
+ return ktime_to_us(ktime_get_boottime());
}
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mac80211_hwsim_data *data = hw->priv;
- return le64_to_cpu(__mac80211_hwsim_get_tsf(data));
+ u64 sim_time = mac80211_hwsim_get_sim_tsf();
+
+ return sim_time + data->tsf_offset;
+}
+
+static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data)
+{
+ u64 sim_time = mac80211_hwsim_get_sim_tsf();
+
+ return cpu_to_le64(sim_time + data->tsf_offset);
}
static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
@@ -1778,7 +1781,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rx_status rx_status;
- u64 now;
+ u64 sim_tsf;
memset(&rx_status, 0, sizeof(rx_status));
rx_status.flag |= RX_FLAG_MACTIME_START;
@@ -1831,9 +1834,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
if (ieee80211_is_beacon(hdr->frame_control) ||
ieee80211_is_probe_resp(hdr->frame_control)) {
rx_status.boottime_ns = ktime_get_boottime_ns();
- now = data->abs_bcn_ts;
+ sim_tsf = data->abs_bcn_ts;
} else {
- now = mac80211_hwsim_get_tsf_raw();
+ sim_tsf = mac80211_hwsim_get_sim_tsf();
}
/* Copy skb to all enabled radios that are on the current frequency */
@@ -1894,7 +1897,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
if (mac80211_hwsim_addr_match(data2, hdr->addr1))
ack = true;
- rx_status.mactime = now + data2->tsf_offset;
+ rx_status.mactime = sim_tsf + data2->tsf_offset;
mac80211_hwsim_rx(data2, &rx_status, nskb);
}
@@ -2147,7 +2150,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
txrate = ieee80211_get_tx_rate(hw, txi);
if (txrate)
bitrate = txrate->bitrate;
- ts = mac80211_hwsim_get_tsf_raw();
+ ts = mac80211_hwsim_get_sim_tsf();
mgmt->u.probe_resp.timestamp =
cpu_to_le64(ts + data->tsf_offset +
24 * 8 * 10 / bitrate);
@@ -2330,7 +2333,7 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf,
mgmt = (struct ieee80211_mgmt *) skb->data;
/* fake header transmission time */
- data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw();
+ data->abs_bcn_ts = mac80211_hwsim_get_sim_tsf();
if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
struct ieee80211_ext *ext = (void *) mgmt;
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 10/15] wifi: mac80211_hwsim: split NAN handling into separate file
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
Having everything in one file for mac80211_hwsim is starting to get a
lot and it will be even worse if we implement more parts of NAN. Split
the NAN implementation into separate files to improve the code
structuring.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
MAINTAINERS | 2 +-
drivers/net/wireless/virtual/Makefile | 2 +
.../net/wireless/virtual/mac80211_hwsim_i.h | 137 ++++++++
...mac80211_hwsim.c => mac80211_hwsim_main.c} | 307 ++----------------
.../net/wireless/virtual/mac80211_hwsim_nan.c | 171 ++++++++++
.../net/wireless/virtual/mac80211_hwsim_nan.h | 34 ++
6 files changed, 365 insertions(+), 288 deletions(-)
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_i.h
rename drivers/net/wireless/virtual/{mac80211_hwsim.c => mac80211_hwsim_main.c} (96%)
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.c
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..ac5c0314c870 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15344,7 +15344,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git
F: Documentation/networking/mac80211-injection.rst
F: Documentation/networking/mac80211_hwsim/mac80211_hwsim.rst
-F: drivers/net/wireless/virtual/mac80211_hwsim.[ch]
+F: drivers/net/wireless/virtual/mac80211_hwsim*.[ch]
F: include/net/mac80211.h
F: net/mac80211/
diff --git a/drivers/net/wireless/virtual/Makefile b/drivers/net/wireless/virtual/Makefile
index 5773cc6d643e..6ad860dd7643 100644
--- a/drivers/net/wireless/virtual/Makefile
+++ b/drivers/net/wireless/virtual/Makefile
@@ -1,3 +1,5 @@
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
+mac80211_hwsim-objs += mac80211_hwsim_main.o
+mac80211_hwsim-objs += mac80211_hwsim_nan.o
obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
new file mode 100644
index 000000000000..741eb08f8a85
--- /dev/null
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
+ * Copyright (c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 - 2025 Intel Corporation
+ */
+
+#ifndef __MAC80211_HWSIM_I_H
+#define __MAC80211_HWSIM_I_H
+
+#include <net/mac80211.h>
+#include "mac80211_hwsim.h"
+#include "mac80211_hwsim_nan.h"
+
+struct mac80211_hwsim_link_data {
+ u32 link_id;
+ u64 beacon_int /* beacon interval in us */;
+ struct hrtimer beacon_timer;
+};
+
+#define HWSIM_NUM_CHANNELS_2GHZ 14
+#define HWSIM_NUM_CHANNELS_5GHZ 40
+#define HWSIM_NUM_CHANNELS_6GHZ 59
+#define HWSIM_NUM_S1G_CHANNELS_US 51
+#define HWSIM_NUM_RATES 12
+#define HWSIM_NUM_CIPHERS 11
+
+struct mac80211_hwsim_data {
+ struct list_head list;
+ struct rhash_head rht;
+ struct ieee80211_hw *hw;
+ struct device *dev;
+ struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
+ struct ieee80211_channel channels_2ghz[HWSIM_NUM_CHANNELS_2GHZ];
+ struct ieee80211_channel channels_5ghz[HWSIM_NUM_CHANNELS_5GHZ];
+ struct ieee80211_channel channels_6ghz[HWSIM_NUM_CHANNELS_6GHZ];
+ struct ieee80211_channel channels_s1g[HWSIM_NUM_S1G_CHANNELS_US];
+ struct ieee80211_rate rates[HWSIM_NUM_RATES];
+ struct ieee80211_iface_combination if_combination;
+ struct ieee80211_iface_limit if_limits[4];
+ int n_if_limits;
+ /* Storage space for channels, etc. */
+ struct mac80211_hwsim_phy_data *phy_data;
+
+ struct ieee80211_iface_combination if_combination_radio;
+ struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS];
+ struct wiphy_radio radio[NUM_NL80211_BANDS];
+
+ u32 ciphers[HWSIM_NUM_CIPHERS];
+
+ struct mac_address addresses[3];
+ int channels, idx;
+ bool use_chanctx;
+ bool destroy_on_close;
+ u32 portid;
+ char alpha2[2];
+ const struct ieee80211_regdomain *regd;
+
+ struct ieee80211_channel *tmp_chan;
+ struct ieee80211_channel *roc_chan;
+ u32 roc_duration;
+ struct delayed_work roc_start;
+ struct delayed_work roc_done;
+ struct delayed_work hw_scan;
+ struct cfg80211_scan_request *hw_scan_request;
+ struct ieee80211_vif *hw_scan_vif;
+ int scan_chan_idx;
+ u8 scan_addr[ETH_ALEN];
+ struct {
+ struct ieee80211_channel *channel;
+ unsigned long next_start, start, end;
+ } survey_data[HWSIM_NUM_CHANNELS_2GHZ +
+ HWSIM_NUM_CHANNELS_5GHZ +
+ HWSIM_NUM_CHANNELS_6GHZ];
+
+ struct ieee80211_channel *channel;
+ enum nl80211_chan_width bw;
+ unsigned int rx_filter;
+ bool started, idle, scanning;
+ struct mutex mutex;
+ enum ps_mode {
+ PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
+ } ps;
+ bool ps_poll_pending;
+ struct dentry *debugfs;
+ struct cfg80211_chan_def radar_background_chandef;
+
+ atomic_t pending_cookie;
+ struct sk_buff_head pending; /* packets pending */
+ /*
+ * Only radios in the same group can communicate together (the
+ * channel has to match too). Each bit represents a group. A
+ * radio can be in more than one group.
+ */
+ u64 group;
+
+ /* group shared by radios created in the same netns */
+ int netgroup;
+ /* wmediumd portid responsible for netgroup of this radio */
+ u32 wmediumd;
+
+ /* difference between this hw's clock and the real clock, in usecs */
+ s64 tsf_offset;
+ s64 bcn_delta;
+ /* absolute beacon transmission time. Used to cover up "tx" delay. */
+ u64 abs_bcn_ts;
+
+ /* Stats */
+ u64 tx_pkts;
+ u64 rx_pkts;
+ u64 tx_bytes;
+ u64 rx_bytes;
+ u64 tx_dropped;
+ u64 tx_failed;
+
+ /* RSSI in rx status of the receiver */
+ int rx_rssi;
+
+ /* only used when pmsr capability is supplied */
+ struct cfg80211_pmsr_capabilities pmsr_capa;
+ struct cfg80211_pmsr_request *pmsr_request;
+ struct wireless_dev *pmsr_request_wdev;
+
+ struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS];
+
+ struct mac80211_hwsim_nan_data nan;
+};
+
+extern spinlock_t hwsim_radio_lock;
+extern struct list_head hwsim_radios;
+
+u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+#endif /* __MAC80211_HWSIM_I_H */
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
similarity index 96%
rename from drivers/net/wireless/virtual/mac80211_hwsim.c
rename to drivers/net/wireless/virtual/mac80211_hwsim_main.c
index d5b9170f690c..4ad2c6f38663 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -39,6 +39,7 @@
#include <linux/uaccess.h>
#include <linux/string.h>
#include "mac80211_hwsim.h"
+#include "mac80211_hwsim_i.h"
#define WARN_QUEUE 100
#define MAX_QUEUE 200
@@ -379,6 +380,8 @@ static const struct ieee80211_channel hwsim_channels_2ghz[] = {
CHAN2G(2472), /* Channel 13 */
CHAN2G(2484), /* Channel 14 */
};
+static_assert(HWSIM_NUM_CHANNELS_2GHZ == ARRAY_SIZE(hwsim_channels_2ghz),
+ "Inconsistent 2 GHz channel count");
static const struct ieee80211_channel hwsim_channels_5ghz[] = {
CHAN5G(5180), /* Channel 36 */
@@ -428,6 +431,8 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = {
CHAN5G(5920), /* Channel 184 */
CHAN5G(5925), /* Channel 185 */
};
+static_assert(HWSIM_NUM_CHANNELS_5GHZ == ARRAY_SIZE(hwsim_channels_5ghz),
+ "Inconsistent 5 GHz channel count");
static const struct ieee80211_channel hwsim_channels_6ghz[] = {
CHAN6G(5955), /* Channel 1 */
@@ -490,9 +495,10 @@ static const struct ieee80211_channel hwsim_channels_6ghz[] = {
CHAN6G(7095), /* Channel 229 */
CHAN6G(7115), /* Channel 233 */
};
+static_assert(HWSIM_NUM_CHANNELS_6GHZ == ARRAY_SIZE(hwsim_channels_6ghz),
+ "Inconsistent 6 GHz channel count");
-#define NUM_S1G_CHANS_US 51
-static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US];
+static struct ieee80211_channel hwsim_channels_s1g[HWSIM_NUM_S1G_CHANNELS_US];
static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = {
.s1g = true,
@@ -525,7 +531,7 @@ static void hwsim_init_s1g_channels(struct ieee80211_channel *chans)
{
int ch, freq;
- for (ch = 0; ch < NUM_S1G_CHANS_US; ch++) {
+ for (ch = 0; ch < ARRAY_SIZE(hwsim_channels_s1g); ch++) {
freq = 902000 + (ch + 1) * 500;
chans[ch].band = NL80211_BAND_S1GHZ;
chans[ch].center_freq = KHZ_TO_MHZ(freq);
@@ -548,6 +554,8 @@ static const struct ieee80211_rate hwsim_rates[] = {
{ .bitrate = 480 },
{ .bitrate = 540 }
};
+static_assert(HWSIM_NUM_RATES == ARRAY_SIZE(hwsim_rates),
+ "Inconsistent rates count");
#define DEFAULT_RX_RSSI -50
@@ -564,6 +572,8 @@ static const u32 hwsim_ciphers[] = {
WLAN_CIPHER_SUITE_BIP_GMAC_128,
WLAN_CIPHER_SUITE_BIP_GMAC_256,
};
+static_assert(HWSIM_NUM_CIPHERS == ARRAY_SIZE(hwsim_ciphers),
+ "Inconsistent cipher count");
#define OUI_QCA 0x001374
#define QCA_NL80211_SUBCMD_TEST 1
@@ -644,12 +654,11 @@ static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = {
{ .vendor_id = OUI_QCA, .subcmd = 1 },
};
-static DEFINE_SPINLOCK(hwsim_radio_lock);
-static LIST_HEAD(hwsim_radios);
+DEFINE_SPINLOCK(hwsim_radio_lock);
+LIST_HEAD(hwsim_radios);
static struct rhashtable hwsim_radios_rht;
static int hwsim_radio_idx;
static int hwsim_radios_generation = 1;
-static u8 hwsim_nan_cluster_id[ETH_ALEN];
static struct platform_driver mac80211_hwsim_driver = {
.driver = {
@@ -657,120 +666,6 @@ static struct platform_driver mac80211_hwsim_driver = {
},
};
-struct mac80211_hwsim_link_data {
- u32 link_id;
- u64 beacon_int /* beacon interval in us */;
- struct hrtimer beacon_timer;
-};
-
-struct mac80211_hwsim_nan_data {
- struct ieee80211_vif *device_vif;
- u8 bands;
-
- enum nl80211_band curr_dw_band;
- struct hrtimer timer;
- bool notify_dw;
-};
-
-struct mac80211_hwsim_data {
- struct list_head list;
- struct rhash_head rht;
- struct ieee80211_hw *hw;
- struct device *dev;
- struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
- struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)];
- struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
- struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)];
- struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)];
- struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
- struct ieee80211_iface_combination if_combination;
- struct ieee80211_iface_limit if_limits[4];
- int n_if_limits;
-
- struct ieee80211_iface_combination if_combination_radio;
- struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS];
- struct wiphy_radio radio[NUM_NL80211_BANDS];
-
- u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
-
- struct mac_address addresses[3];
- int channels, idx;
- bool use_chanctx;
- bool destroy_on_close;
- u32 portid;
- char alpha2[2];
- const struct ieee80211_regdomain *regd;
-
- struct ieee80211_channel *tmp_chan;
- struct ieee80211_channel *roc_chan;
- u32 roc_duration;
- struct delayed_work roc_start;
- struct delayed_work roc_done;
- struct delayed_work hw_scan;
- struct cfg80211_scan_request *hw_scan_request;
- struct ieee80211_vif *hw_scan_vif;
- int scan_chan_idx;
- u8 scan_addr[ETH_ALEN];
- struct {
- struct ieee80211_channel *channel;
- unsigned long next_start, start, end;
- } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) +
- ARRAY_SIZE(hwsim_channels_5ghz) +
- ARRAY_SIZE(hwsim_channels_6ghz)];
-
- struct ieee80211_channel *channel;
- enum nl80211_chan_width bw;
- unsigned int rx_filter;
- bool started, idle, scanning;
- struct mutex mutex;
- enum ps_mode {
- PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
- } ps;
- bool ps_poll_pending;
- struct dentry *debugfs;
- struct cfg80211_chan_def radar_background_chandef;
-
- atomic_t pending_cookie;
- struct sk_buff_head pending; /* packets pending */
- /*
- * Only radios in the same group can communicate together (the
- * channel has to match too). Each bit represents a group. A
- * radio can be in more than one group.
- */
- u64 group;
-
- /* group shared by radios created in the same netns */
- int netgroup;
- /* wmediumd portid responsible for netgroup of this radio */
- u32 wmediumd;
-
- /* difference between this hw's clock and the real clock, in usecs */
- s64 tsf_offset;
- s64 bcn_delta;
- /* absolute beacon transmission time. Used to cover up "tx" delay. */
- u64 abs_bcn_ts;
-
- /* Stats */
- u64 tx_pkts;
- u64 rx_pkts;
- u64 tx_bytes;
- u64 rx_bytes;
- u64 tx_dropped;
- u64 tx_failed;
-
- /* RSSI in rx status of the receiver */
- int rx_rssi;
-
- /* only used when pmsr capability is supplied */
- struct cfg80211_pmsr_capabilities pmsr_capa;
- struct cfg80211_pmsr_request *pmsr_request;
- struct wireless_dev *pmsr_request_wdev;
-
- struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS];
-
- struct mac80211_hwsim_nan_data nan;
-};
-
static const struct rhashtable_params hwsim_rht_params = {
.nelem_hint = 2,
.automatic_shrinking = true,
@@ -1327,8 +1222,8 @@ static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data)
return cpu_to_le64(now + data->tsf_offset);
}
-static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct mac80211_hwsim_data *data = hw->priv;
return le64_to_cpu(__mac80211_hwsim_get_tsf(data));
@@ -4130,168 +4025,6 @@ static int hwsim_pmsr_report_nl(struct sk_buff *msg, struct genl_info *info)
return err;
}
-static enum hrtimer_restart
-mac80211_hwsim_nan_dw_start(struct hrtimer *timer)
-{
- struct mac80211_hwsim_data *data =
- container_of(timer, struct mac80211_hwsim_data,
- nan.timer);
- struct ieee80211_hw *hw = data->hw;
- u64 orig_tsf = mac80211_hwsim_get_tsf(hw, NULL), tsf = orig_tsf;
- u32 dw_int = 512 * 1024;
- u64 until_dw;
-
- if (!data->nan.device_vif)
- return HRTIMER_NORESTART;
-
- if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
- if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) {
- dw_int = 128 * 1024;
- data->nan.curr_dw_band = NL80211_BAND_5GHZ;
- } else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) {
- data->nan.curr_dw_band = NL80211_BAND_2GHZ;
- }
- }
-
- until_dw = dw_int - do_div(tsf, dw_int);
-
- /* The timer might fire just before the actual DW, in which case
- * update the timeout to the actual next DW
- */
- if (until_dw < dw_int / 2)
- until_dw += dw_int;
-
- /* The above do_div() call directly modifies the 'tsf' variable, thus,
- * use a copy so that the print below would show the original TSF.
- */
- wiphy_debug(hw->wiphy,
- "%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n",
- __func__, orig_tsf, data->nan.curr_dw_band,
- until_dw);
-
- hrtimer_forward_now(&data->nan.timer,
- ns_to_ktime(until_dw * NSEC_PER_USEC));
-
- if (data->nan.notify_dw) {
- struct ieee80211_channel *ch;
- struct wireless_dev *wdev =
- ieee80211_vif_to_wdev(data->nan.device_vif);
-
- if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
- ch = ieee80211_get_channel(hw->wiphy, 5745);
- else
- ch = ieee80211_get_channel(hw->wiphy, 2437);
-
- cfg80211_next_nan_dw_notif(wdev, ch, GFP_ATOMIC);
- }
-
- return HRTIMER_RESTART;
-}
-
-static int mac80211_hwsim_start_nan(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_nan_conf *conf)
-{
- struct mac80211_hwsim_data *data = hw->priv;
- u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
- u32 dw_int = 512 * 1000;
- u64 until_dw = dw_int - do_div(tsf, dw_int);
- struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
-
- if (vif->type != NL80211_IFTYPE_NAN)
- return -EINVAL;
-
- if (data->nan.device_vif)
- return -EALREADY;
-
- /* set this before starting the timer, as preemption might occur */
- data->nan.device_vif = vif;
- data->nan.bands = conf->bands;
- data->nan.curr_dw_band = NL80211_BAND_2GHZ;
-
- wiphy_debug(hw->wiphy, "nan_started, next_dw=%llu\n",
- until_dw);
-
- hrtimer_start(&data->nan.timer,
- ns_to_ktime(until_dw * NSEC_PER_USEC),
- HRTIMER_MODE_REL_SOFT);
-
- if (!is_zero_ether_addr(conf->cluster_id) &&
- is_zero_ether_addr(hwsim_nan_cluster_id)) {
- memcpy(hwsim_nan_cluster_id, conf->cluster_id, ETH_ALEN);
- } else if (is_zero_ether_addr(hwsim_nan_cluster_id)) {
- hwsim_nan_cluster_id[0] = 0x50;
- hwsim_nan_cluster_id[1] = 0x6f;
- hwsim_nan_cluster_id[2] = 0x9a;
- hwsim_nan_cluster_id[3] = 0x01;
- hwsim_nan_cluster_id[4] = get_random_u8();
- hwsim_nan_cluster_id[5] = get_random_u8();
- }
-
- data->nan.notify_dw = conf->enable_dw_notification;
-
- cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true,
- GFP_KERNEL);
-
- return 0;
-}
-
-static int mac80211_hwsim_stop_nan(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct mac80211_hwsim_data *data = hw->priv;
- struct mac80211_hwsim_data *data2;
- bool nan_cluster_running = false;
-
- if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif ||
- data->nan.device_vif != vif)
- return -EINVAL;
-
- hrtimer_cancel(&data->nan.timer);
- data->nan.device_vif = NULL;
-
- spin_lock_bh(&hwsim_radio_lock);
- list_for_each_entry(data2, &hwsim_radios, list) {
- if (data2->nan.device_vif) {
- nan_cluster_running = true;
- break;
- }
- }
- spin_unlock_bh(&hwsim_radio_lock);
-
- if (!nan_cluster_running)
- memset(hwsim_nan_cluster_id, 0, ETH_ALEN);
-
- return 0;
-}
-
-static int mac80211_hwsim_change_nan_config(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_nan_conf *conf,
- u32 changes)
-{
- struct mac80211_hwsim_data *data = hw->priv;
-
- if (vif->type != NL80211_IFTYPE_NAN)
- return -EINVAL;
-
- if (!data->nan.device_vif)
- return -EINVAL;
-
- wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes);
-
- /* Handle only the changes we care about for simulation purposes */
- if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) {
- data->nan.bands = conf->bands;
- data->nan.curr_dw_band = NL80211_BAND_2GHZ;
- }
-
- if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
- data->nan.notify_dw = conf->enable_dw_notification;
-
- return 0;
-}
-
static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw,
struct cfg80211_chan_def *chan)
{
@@ -4342,11 +4075,11 @@ static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw,
.get_et_strings = mac80211_hwsim_get_et_strings, \
.start_pmsr = mac80211_hwsim_start_pmsr, \
.abort_pmsr = mac80211_hwsim_abort_pmsr, \
- .start_nan = mac80211_hwsim_start_nan, \
- .stop_nan = mac80211_hwsim_stop_nan, \
- .nan_change_conf = mac80211_hwsim_change_nan_config, \
.set_radar_background = mac80211_hwsim_set_radar_background, \
.set_key = mac80211_hwsim_set_key, \
+ .start_nan = mac80211_hwsim_nan_start, \
+ .stop_nan = mac80211_hwsim_nan_stop, \
+ .nan_change_conf = mac80211_hwsim_nan_change_config, \
HWSIM_DEBUGFS_OPS
#define HWSIM_NON_MLO_OPS \
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
new file mode 100644
index 000000000000..5d7c736d7972
--- /dev/null
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim
+ * Copyright (C) 2025 Intel Corporation
+ */
+
+#include "mac80211_hwsim_i.h"
+
+static u8 hwsim_nan_cluster_id[ETH_ALEN];
+
+enum hrtimer_restart
+mac80211_hwsim_nan_dw_start(struct hrtimer *timer)
+{
+ struct mac80211_hwsim_data *data =
+ container_of(timer, struct mac80211_hwsim_data,
+ nan.timer);
+ struct ieee80211_hw *hw = data->hw;
+ u64 orig_tsf = mac80211_hwsim_get_tsf(hw, NULL), tsf = orig_tsf;
+ u32 dw_int = 512 * 1024;
+ u64 until_dw;
+
+ if (!data->nan.device_vif)
+ return HRTIMER_NORESTART;
+
+ if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
+ if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) {
+ dw_int = 128 * 1024;
+ data->nan.curr_dw_band = NL80211_BAND_5GHZ;
+ } else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) {
+ data->nan.curr_dw_band = NL80211_BAND_2GHZ;
+ }
+ }
+
+ until_dw = dw_int - do_div(tsf, dw_int);
+
+ /* The timer might fire just before the actual DW, in which case
+ * update the timeout to the actual next DW
+ */
+ if (until_dw < dw_int / 2)
+ until_dw += dw_int;
+
+ /* The above do_div() call directly modifies the 'tsf' variable, thus,
+ * use a copy so that the print below would show the original TSF.
+ */
+ wiphy_debug(hw->wiphy,
+ "%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n",
+ __func__, orig_tsf, data->nan.curr_dw_band,
+ until_dw);
+
+ hrtimer_forward_now(&data->nan.timer,
+ ns_to_ktime(until_dw * NSEC_PER_USEC));
+
+ if (data->nan.notify_dw) {
+ struct ieee80211_channel *ch;
+ struct wireless_dev *wdev =
+ ieee80211_vif_to_wdev(data->nan.device_vif);
+
+ if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
+ ch = ieee80211_get_channel(hw->wiphy, 5745);
+ else
+ ch = ieee80211_get_channel(hw->wiphy, 2437);
+
+ cfg80211_next_nan_dw_notif(wdev, ch, GFP_ATOMIC);
+ }
+
+ return HRTIMER_RESTART;
+}
+
+int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+ u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
+ u32 dw_int = 512 * 1000;
+ u64 until_dw = dw_int - do_div(tsf, dw_int);
+ struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
+
+ if (vif->type != NL80211_IFTYPE_NAN)
+ return -EINVAL;
+
+ if (data->nan.device_vif)
+ return -EALREADY;
+
+ /* set this before starting the timer, as preemption might occur */
+ data->nan.device_vif = vif;
+ data->nan.bands = conf->bands;
+ data->nan.curr_dw_band = NL80211_BAND_2GHZ;
+
+ wiphy_debug(hw->wiphy, "nan_started, next_dw=%llu\n",
+ until_dw);
+
+ hrtimer_start(&data->nan.timer,
+ ns_to_ktime(until_dw * NSEC_PER_USEC),
+ HRTIMER_MODE_REL_SOFT);
+
+ if (conf->cluster_id && !is_zero_ether_addr(conf->cluster_id) &&
+ is_zero_ether_addr(hwsim_nan_cluster_id)) {
+ memcpy(hwsim_nan_cluster_id, conf->cluster_id, ETH_ALEN);
+ } else if (is_zero_ether_addr(hwsim_nan_cluster_id)) {
+ hwsim_nan_cluster_id[0] = 0x50;
+ hwsim_nan_cluster_id[1] = 0x6f;
+ hwsim_nan_cluster_id[2] = 0x9a;
+ hwsim_nan_cluster_id[3] = 0x01;
+ hwsim_nan_cluster_id[4] = get_random_u8();
+ hwsim_nan_cluster_id[5] = get_random_u8();
+ }
+
+ data->nan.notify_dw = conf->enable_dw_notification;
+
+ cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true,
+ GFP_KERNEL);
+
+ return 0;
+}
+
+int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct mac80211_hwsim_data *data2;
+ bool nan_cluster_running = false;
+
+ if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif ||
+ data->nan.device_vif != vif)
+ return -EINVAL;
+
+ hrtimer_cancel(&data->nan.timer);
+ data->nan.device_vif = NULL;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data2, &hwsim_radios, list) {
+ if (data2->nan.device_vif) {
+ nan_cluster_running = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!nan_cluster_running)
+ memset(hwsim_nan_cluster_id, 0, ETH_ALEN);
+
+ return 0;
+}
+
+int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf,
+ u32 changes)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+
+ if (vif->type != NL80211_IFTYPE_NAN)
+ return -EINVAL;
+
+ if (!data->nan.device_vif)
+ return -EINVAL;
+
+ wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes);
+
+ /* Handle only the changes we care about for simulation purposes */
+ if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) {
+ data->nan.bands = conf->bands;
+ data->nan.curr_dw_band = NL80211_BAND_2GHZ;
+ }
+
+ if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
+ data->nan.notify_dw = conf->enable_dw_notification;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
new file mode 100644
index 000000000000..eac64ac37589
--- /dev/null
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim
+ * Copyright (C) 2025 Intel Corporation
+ */
+
+#ifndef __MAC80211_HWSIM_NAN_H
+#define __MAC80211_HWSIM_NAN_H
+
+struct mac80211_hwsim_nan_data {
+ struct ieee80211_vif *device_vif;
+ u8 bands;
+
+ enum nl80211_band curr_dw_band;
+ struct hrtimer timer;
+ bool notify_dw;
+};
+
+enum hrtimer_restart
+mac80211_hwsim_nan_dw_start(struct hrtimer *timer);
+
+int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf);
+
+int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf,
+ u32 changes);
+
+#endif /* __MAC80211_HWSIM_NAN_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 09/15] wifi: mac80211_hwsim: move NAN related variables into a struct
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
Move it all into a common struct to better segment the code.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/virtual/mac80211_hwsim.c | 76 ++++++++++---------
1 file changed, 40 insertions(+), 36 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 7ab0765cb482..d5b9170f690c 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -663,6 +663,15 @@ struct mac80211_hwsim_link_data {
struct hrtimer beacon_timer;
};
+struct mac80211_hwsim_nan_data {
+ struct ieee80211_vif *device_vif;
+ u8 bands;
+
+ enum nl80211_band curr_dw_band;
+ struct hrtimer timer;
+ bool notify_dw;
+};
+
struct mac80211_hwsim_data {
struct list_head list;
struct rhash_head rht;
@@ -759,12 +768,7 @@ struct mac80211_hwsim_data {
struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS];
- struct ieee80211_vif *nan_device_vif;
- u8 nan_bands;
-
- enum nl80211_band nan_curr_dw_band;
- struct hrtimer nan_timer;
- bool notify_dw;
+ struct mac80211_hwsim_nan_data nan;
};
static const struct rhashtable_params hwsim_rht_params = {
@@ -2108,9 +2112,9 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
* on channel 6 or channel 149, unless a ROC is in progress (for
* USD use cases).
*/
- if (data->nan_curr_dw_band == NL80211_BAND_2GHZ)
+ if (data->nan.curr_dw_band == NL80211_BAND_2GHZ)
channel = ieee80211_get_channel(hw->wiphy, 2437);
- else if (data->nan_curr_dw_band == NL80211_BAND_5GHZ)
+ else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
channel = ieee80211_get_channel(hw->wiphy, 5745);
else
channel = NULL;
@@ -4131,21 +4135,21 @@ mac80211_hwsim_nan_dw_start(struct hrtimer *timer)
{
struct mac80211_hwsim_data *data =
container_of(timer, struct mac80211_hwsim_data,
- nan_timer);
+ nan.timer);
struct ieee80211_hw *hw = data->hw;
u64 orig_tsf = mac80211_hwsim_get_tsf(hw, NULL), tsf = orig_tsf;
u32 dw_int = 512 * 1024;
u64 until_dw;
- if (!data->nan_device_vif)
+ if (!data->nan.device_vif)
return HRTIMER_NORESTART;
- if (data->nan_bands & BIT(NL80211_BAND_5GHZ)) {
- if (data->nan_curr_dw_band == NL80211_BAND_2GHZ) {
+ if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
+ if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) {
dw_int = 128 * 1024;
- data->nan_curr_dw_band = NL80211_BAND_5GHZ;
- } else if (data->nan_curr_dw_band == NL80211_BAND_5GHZ) {
- data->nan_curr_dw_band = NL80211_BAND_2GHZ;
+ data->nan.curr_dw_band = NL80211_BAND_5GHZ;
+ } else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) {
+ data->nan.curr_dw_band = NL80211_BAND_2GHZ;
}
}
@@ -4162,18 +4166,18 @@ mac80211_hwsim_nan_dw_start(struct hrtimer *timer)
*/
wiphy_debug(hw->wiphy,
"%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n",
- __func__, orig_tsf, data->nan_curr_dw_band,
+ __func__, orig_tsf, data->nan.curr_dw_band,
until_dw);
- hrtimer_forward_now(&data->nan_timer,
+ hrtimer_forward_now(&data->nan.timer,
ns_to_ktime(until_dw * NSEC_PER_USEC));
- if (data->notify_dw) {
+ if (data->nan.notify_dw) {
struct ieee80211_channel *ch;
struct wireless_dev *wdev =
- ieee80211_vif_to_wdev(data->nan_device_vif);
+ ieee80211_vif_to_wdev(data->nan.device_vif);
- if (data->nan_curr_dw_band == NL80211_BAND_5GHZ)
+ if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
ch = ieee80211_get_channel(hw->wiphy, 5745);
else
ch = ieee80211_get_channel(hw->wiphy, 2437);
@@ -4197,18 +4201,18 @@ static int mac80211_hwsim_start_nan(struct ieee80211_hw *hw,
if (vif->type != NL80211_IFTYPE_NAN)
return -EINVAL;
- if (data->nan_device_vif)
+ if (data->nan.device_vif)
return -EALREADY;
/* set this before starting the timer, as preemption might occur */
- data->nan_device_vif = vif;
- data->nan_bands = conf->bands;
- data->nan_curr_dw_band = NL80211_BAND_2GHZ;
+ data->nan.device_vif = vif;
+ data->nan.bands = conf->bands;
+ data->nan.curr_dw_band = NL80211_BAND_2GHZ;
wiphy_debug(hw->wiphy, "nan_started, next_dw=%llu\n",
until_dw);
- hrtimer_start(&data->nan_timer,
+ hrtimer_start(&data->nan.timer,
ns_to_ktime(until_dw * NSEC_PER_USEC),
HRTIMER_MODE_REL_SOFT);
@@ -4224,7 +4228,7 @@ static int mac80211_hwsim_start_nan(struct ieee80211_hw *hw,
hwsim_nan_cluster_id[5] = get_random_u8();
}
- data->notify_dw = conf->enable_dw_notification;
+ data->nan.notify_dw = conf->enable_dw_notification;
cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true,
GFP_KERNEL);
@@ -4239,16 +4243,16 @@ static int mac80211_hwsim_stop_nan(struct ieee80211_hw *hw,
struct mac80211_hwsim_data *data2;
bool nan_cluster_running = false;
- if (vif->type != NL80211_IFTYPE_NAN || !data->nan_device_vif ||
- data->nan_device_vif != vif)
+ if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif ||
+ data->nan.device_vif != vif)
return -EINVAL;
- hrtimer_cancel(&data->nan_timer);
- data->nan_device_vif = NULL;
+ hrtimer_cancel(&data->nan.timer);
+ data->nan.device_vif = NULL;
spin_lock_bh(&hwsim_radio_lock);
list_for_each_entry(data2, &hwsim_radios, list) {
- if (data2->nan_device_vif) {
+ if (data2->nan.device_vif) {
nan_cluster_running = true;
break;
}
@@ -4271,19 +4275,19 @@ static int mac80211_hwsim_change_nan_config(struct ieee80211_hw *hw,
if (vif->type != NL80211_IFTYPE_NAN)
return -EINVAL;
- if (!data->nan_device_vif)
+ if (!data->nan.device_vif)
return -EINVAL;
wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes);
/* Handle only the changes we care about for simulation purposes */
if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) {
- data->nan_bands = conf->bands;
- data->nan_curr_dw_band = NL80211_BAND_2GHZ;
+ data->nan.bands = conf->bands;
+ data->nan.curr_dw_band = NL80211_BAND_2GHZ;
}
if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
- data->notify_dw = conf->enable_dw_notification;
+ data->nan.notify_dw = conf->enable_dw_notification;
return 0;
}
@@ -5716,7 +5720,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED |
NAN_DEV_CAPA_NDPE_SUPPORTED;
- hrtimer_setup(&data->nan_timer, mac80211_hwsim_nan_dw_start,
+ hrtimer_setup(&data->nan.timer, mac80211_hwsim_nan_dw_start,
CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT);
}
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 08/15] wifi: mac80211_hwsim: remove unused nan_vif struct member
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
The struct also contains nan_device_vif and that is the member that is
being used.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/virtual/mac80211_hwsim.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 1fcf5d0d2e13..7ab0765cb482 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -765,7 +765,6 @@ struct mac80211_hwsim_data {
enum nl80211_band nan_curr_dw_band;
struct hrtimer nan_timer;
bool notify_dw;
- struct ieee80211_vif *nan_vif;
};
static const struct rhashtable_params hwsim_rht_params = {
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 07/15] wifi: mac80211: add NAN channel evacuation support
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
A NAN channel can be evacuated, i.e. detached from its chanctx, if all
chanctxs are used by NAN and a chanctx is needed for something else.
For example if the STA interface needs to perform a channel switch.
Implement the evacuation: detach the NAN channel from its chanctx, remove
all the peer NAN channels that were using this chanctx, and update the
driver.
Internally, the NAN channel evacuation will be triggered in the scenario
described above, and API is provided for the driver to also trigger it.
The driver/device is assumed to publish a ULW to notify the peers about
the fact that we won't be present on this NAN channel anymore.
Also export this as an API for the drivers: if a driver has other
resources per channel, it might want to trigger channel evacuation in
order to free up such internal resources for other usages.
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
include/net/mac80211.h | 15 +++++
net/mac80211/chan.c | 28 ++++++---
net/mac80211/nan.c | 126 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 162 insertions(+), 7 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 0d1b1d726b9c..d909bc1b29ff 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7905,6 +7905,21 @@ void ieee80211_nan_cluster_joined(struct ieee80211_vif *vif,
const u8 *cluster_id, bool new_cluster,
gfp_t gfp);
+/**
+ * ieee80211_nan_try_evacuate - try to evacuate a NAN channel
+ *
+ * This function tries to evacuate a NAN channel that is using the given
+ * channel context, to free up channel context resources.
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @conf: the channel context configuration to try to evacuate. If %NULL,
+ * the NAN channel that has the fewest slots scheduled will be evacuated.
+ *
+ * Return: %true if a channel was evacuated, %false otherwise
+ */
+bool ieee80211_nan_try_evacuate(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf);
+
/**
* ieee80211_calc_rx_airtime - calculate estimated transmission airtime for RX.
*
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 248531051a4e..9683d3e6e1d2 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -1861,16 +1861,21 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
}
if (n_assigned != n_reserved) {
- if (n_ready == n_reserved) {
- wiphy_info(local->hw.wiphy,
- "channel context reservation cannot be finalized because some interfaces aren't switching\n");
- err = -EBUSY;
- goto err;
- }
+ if (n_ready != n_reserved)
+ return -EAGAIN;
- return -EAGAIN;
+ if (n_assigned == n_reserved + 1 &&
+ ieee80211_nan_try_evacuate(&local->hw,
+ &ctx->replace_ctx->conf))
+ goto use_reserved;
+
+ wiphy_info(local->hw.wiphy,
+ "channel context reservation cannot be finalized because some interfaces aren't switching\n");
+ err = -EBUSY;
+ goto err;
}
+use_reserved:
ctx->conf.radar_enabled = false;
for_each_chanctx_user_reserved(local, ctx, &iter) {
if (ieee80211_link_has_in_place_reservation(iter.link) &&
@@ -2178,6 +2183,15 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
ctx = ieee80211_find_or_create_chanctx(sdata, chanreq, mode,
assign_on_failure, &reused_ctx);
+ if (IS_ERR(ctx)) {
+ /* Try to evacuate a NAN channel to free up a chanctx */
+ if (ieee80211_nan_try_evacuate(&local->hw, NULL))
+ ctx = ieee80211_find_or_create_chanctx(sdata, chanreq,
+ mode,
+ assign_on_failure,
+ &reused_ctx);
+ }
+
if (IS_ERR(ctx)) {
ret = PTR_ERR(ctx);
goto out;
diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c
index 4e262b624521..cea620aaee6a 100644
--- a/net/mac80211/nan.c
+++ b/net/mac80211/nan.c
@@ -334,7 +334,10 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
sched_idx_to_chan[i] = chan;
ieee80211_nan_init_channel(chan,
&sched->nan_channels[i]);
+ }
+ /* Also a pre-existing channel might have been ULWed, so no chanctx */
+ if (!chan->chanctx_conf) {
ret = ieee80211_nan_use_chanctx(sdata, chan, false);
if (ret) {
memset(chan, 0, sizeof(*chan));
@@ -708,3 +711,126 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
ieee80211_nan_free_peer_sched(to_free);
return ret;
}
+
+static void
+ieee80211_nan_evacuate_channel(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_nan_channel *nan_channel)
+{
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+
+ lockdep_assert_wiphy(sdata->local->hw.wiphy);
+
+ if (WARN_ON(!nan_channel || !nan_channel->chanreq.oper.chan))
+ return;
+
+ conf = nan_channel->chanctx_conf;
+ if (WARN_ON(!conf))
+ return;
+
+ nan_channel->chanctx_conf = NULL;
+
+ /* Update all peer channels that reference this chanctx */
+ ieee80211_nan_update_peer_channels(sdata, conf);
+
+ drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED);
+
+ cfg80211_nan_channel_evac(&sdata->wdev, &nan_channel->chanreq.oper,
+ GFP_KERNEL);
+
+ /* Update NDI carrier states */
+ ieee80211_nan_update_all_ndi_carriers(sdata->local);
+
+ /* Clean up the channel context if no longer used */
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+ if (ieee80211_chanctx_num_assigned(sdata->local, ctx) > 0) {
+ ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
+ ieee80211_recalc_smps_chanctx(sdata->local, ctx);
+ ieee80211_recalc_chanctx_min_def(sdata->local, ctx);
+ }
+
+ if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
+ ieee80211_free_chanctx(sdata->local, ctx, false);
+}
+
+bool ieee80211_nan_try_evacuate(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ieee80211_sub_if_data *sdata = NULL, *tmp;
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_nan_channel *evac_chan = NULL;
+ struct ieee80211_nan_sched_cfg *sched_cfg;
+ struct ieee80211_chanctx *ctx = NULL;
+ int min_slot_count = INT_MAX;
+ int usable_channels = 0;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ if (conf)
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+ /* Find the NAN interface - there can only be one */
+ list_for_each_entry(tmp, &local->interfaces, list) {
+ if (ieee80211_sdata_running(tmp) &&
+ tmp->vif.type == NL80211_IFTYPE_NAN) {
+ sdata = tmp;
+ break;
+ }
+ }
+
+ if (!sdata)
+ return false;
+
+ sched_cfg = &sdata->vif.cfg.nan_sched;
+
+ /* Find the channel to evacuate and count usable channels */
+ for (int i = 0; i < IEEE80211_NAN_MAX_CHANNELS; i++) {
+ struct ieee80211_nan_channel *chan =
+ &sched_cfg->channels[i];
+ struct ieee80211_chanctx *chan_ctx;
+ int slot_count = 0;
+
+ if (!chan->chanreq.oper.chan || !chan->chanctx_conf)
+ continue;
+
+ usable_channels++;
+
+ chan_ctx = container_of(chan->chanctx_conf,
+ struct ieee80211_chanctx, conf);
+
+ /* If ctx specified, only consider that specific chanctx */
+ if (ctx) {
+ if (chan_ctx == ctx)
+ evac_chan = chan;
+ continue;
+ }
+
+ /* Can only evacuate channels whose chanctx is NAN-only */
+ if (ieee80211_chanctx_refcount(local, chan_ctx) > 1)
+ continue;
+
+ /* Count how many time slots use this channel */
+ for (int s = 0; s < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; s++)
+ if (sched_cfg->schedule[s] == chan)
+ slot_count++;
+
+ if (slot_count < min_slot_count) {
+ min_slot_count = slot_count;
+ evac_chan = chan;
+ }
+ }
+
+ /* No suitable NAN channel found */
+ if (!evac_chan)
+ return false;
+
+ /* NAN needs at least one remaining usable channel after evacuation */
+ if (usable_channels < 2)
+ return false;
+
+ ieee80211_nan_evacuate_channel(sdata, evac_chan);
+
+ return true;
+}
+EXPORT_SYMBOL(ieee80211_nan_try_evacuate);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 06/15] wifi: mac80211: avoid out-of-bounds access in monitor
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Ilan Peer, Johannes Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
In NAN, we don't know on what band the frame will be sent. Therefore we
set info->band to NUM_NL80211_BANDS. However, this leads to out-of-bound
access in ieee80211_add_tx_radiotap_header when we try to access the
sbands array.
Fix it by not accessing the array if the band is NUM_NL80211_BANDS.
This means that we will not report rate info for legacy rate in NAN.
But nobody really cares about it.
Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
net/mac80211/status.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 4b38aa0e902a..8716eda8317d 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -5,7 +5,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2008-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
- * Copyright 2021-2025 Intel Corporation
+ * Copyright 2021-2026 Intel Corporation
*/
#include <linux/export.h>
@@ -295,9 +295,10 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
RATE_INFO_FLAGS_VHT_MCS |
RATE_INFO_FLAGS_HE_MCS)))
legacy_rate = status_rate->rate_idx.legacy;
- } else if (info->status.rates[0].idx >= 0 &&
- !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
- IEEE80211_TX_RC_VHT_MCS))) {
+ } else if (info->band < NUM_NL80211_BANDS &&
+ info->status.rates[0].idx >= 0 &&
+ !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+ IEEE80211_TX_RC_VHT_MCS))) {
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[info->band];
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 05/15] wifi: mac80211: Fix a kernel panic in ieee80211_encrypt_tx_skb()
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Andrei Otcheretianski
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
skb->dev may be NULL for frames on non-netdev devices. For example, NAN
device frames after pairing. Fix it.
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
net/mac80211/tx.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c18de2cb3769..933c86ca21c3 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -5355,7 +5355,7 @@ static int ieee80211_beacon_protect(struct sk_buff *skb,
int ieee80211_encrypt_tx_skb(struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = NULL;
struct sk_buff *check_skb;
struct ieee80211_tx_data tx;
ieee80211_tx_result res;
@@ -5370,7 +5370,14 @@ int ieee80211_encrypt_tx_skb(struct sk_buff *skb)
__skb_queue_head_init(&tx.skbs);
__skb_queue_tail(&tx.skbs, skb);
- sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
+ if (skb->dev)
+ sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
+ else if (info->control.vif)
+ sdata = vif_to_sdata(info->control.vif);
+
+ if (WARN_ON(!sdata))
+ return -EINVAL;
+
tx.sdata = sdata;
tx.local = sdata->local;
res = ieee80211_tx_h_encrypt(&tx);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 04/15] wifi: mac80211: Allow setting MAC address on interface creation
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Ilan Peer, Johannes Berg
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Ilan Peer <ilan.peer@intel.com>
Allow setting the interface MAC address for NAN Device interfaces
and P2P Device interfaces on interface creation.
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
net/mac80211/iface.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 95b779c4d627..683d8db4da14 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -2280,7 +2280,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sdata->dev = NULL;
strscpy(sdata->name, name, IFNAMSIZ);
- ieee80211_assign_perm_addr(local, wdev->address, type);
+
+ if (is_valid_ether_addr(params->macaddr))
+ memcpy(wdev->address, params->macaddr, ETH_ALEN);
+ else
+ ieee80211_assign_perm_addr(local, wdev->address, type);
+
memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 03/15] wifi: mac80211: accept protected frames for NAN device
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Avraham Stern
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Avraham Stern <avraham.stern@intel.com>
Some frames sent to the NAN device may be protected, such as
protected action frames (in particular protected dual of
public action).
Accept robust management frames except disassoc on the NAN
device, and clean up the code a little bit.
Signed-off-by: Avraham Stern <avraham.stern@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
net/mac80211/rx.c | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 82ea7404f3da..e1f376e0620c 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4624,16 +4624,24 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
ieee80211_has_fromds(hdr->frame_control))
return false;
- /* Accept only frames that are addressed to the NAN cluster
+ /*
+ * Accept only frames that are addressed to the NAN cluster
* (based on the Cluster ID). From these frames, accept only
- * action frames or authentication frames that are addressed to
- * the local NAN interface.
+ * - public action frames,
+ * - authentication frames to the local address, and
+ * - robust management frames except disassoc.
*/
- return memcmp(sdata->u.nan.conf.cluster_id,
- hdr->addr3, ETH_ALEN) == 0 &&
- (ieee80211_is_public_action(hdr, skb->len) ||
- (ieee80211_is_auth(hdr->frame_control) &&
- ether_addr_equal(sdata->vif.addr, hdr->addr1)));
+ if (!ether_addr_equal(sdata->u.nan.conf.cluster_id, hdr->addr3))
+ return false;
+ if (ieee80211_is_public_action(hdr, skb->len))
+ return true;
+ if (ieee80211_is_auth(hdr->frame_control) &&
+ ether_addr_equal(sdata->vif.addr, hdr->addr1))
+ return true;
+ if (!ieee80211_is_disassoc(hdr->frame_control) &&
+ ieee80211_is_robust_mgmt_frame(skb))
+ return true;
+ return false;
case NL80211_IFTYPE_NAN_DATA:
if (ieee80211_has_tods(hdr->frame_control) ||
ieee80211_has_fromds(hdr->frame_control))
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 02/15] wifi: mac80211: allow userspace TX/RX over NAN Data interfaces
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Ilan Peer
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
From: Ilan Peer <ilan.peer@intel.com>
Allow TX/RX of action frames (for NAN action frames) over
NAN Data interfaces to support cases where there's a secure
NDP and NAFs may be exchanged over that.
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
net/mac80211/main.c | 4 ++++
net/mac80211/offchannel.c | 9 +++++++--
net/mac80211/tx.c | 5 +++--
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index f47dd58770ad..8400792d67e2 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -750,6 +750,10 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4),
},
+ [NL80211_IFTYPE_NAN_DATA] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4),
+ },
};
static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index f60f6a58948b..10c962d28037 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -8,7 +8,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2019, 2022-2025 Intel Corporation
+ * Copyright (C) 2019, 2022-2026 Intel Corporation
*/
#include <linux/export.h>
#include <net/mac80211.h>
@@ -898,6 +898,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
break;
case NL80211_IFTYPE_NAN:
break;
+ case NL80211_IFTYPE_NAN_DATA:
+ if (is_multicast_ether_addr(mgmt->da))
+ return -EOPNOTSUPP;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -911,7 +915,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
/* Check if the operating channel is the requested channel */
if (!params->chan && mlo_sta) {
need_offchan = false;
- } else if (sdata->vif.type == NL80211_IFTYPE_NAN) {
+ } else if (sdata->vif.type == NL80211_IFTYPE_NAN ||
+ sdata->vif.type == NL80211_IFTYPE_NAN_DATA) {
/* Frames can be sent during NAN schedule */
} else if (!need_offchan) {
struct ieee80211_chanctx_conf *chanctx_conf = NULL;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1702f816419b..c18de2cb3769 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -5,7 +5,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*
* Transmit and frame generation functions.
*/
@@ -6377,7 +6377,8 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
enum nl80211_band band;
rcu_read_lock();
- if (sdata->vif.type == NL80211_IFTYPE_NAN) {
+ if (sdata->vif.type == NL80211_IFTYPE_NAN ||
+ sdata->vif.type == NL80211_IFTYPE_NAN_DATA) {
band = NUM_NL80211_BANDS;
} else if (!ieee80211_vif_is_mld(&sdata->vif)) {
WARN_ON(link_id >= 0);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 01/15] wifi: mac80211: track the id of the NAN cluster we joined
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20260429114049.4167111-1-miriam.rachel.korenblit@intel.com>
Currently, we store in nan.conf the cluster id that was configured from
upper layer to be used when the device opens a cluster.
But after we joined a cluster, the configured cluster id is no longer
relevant. Particularly, in reconfig we will give the driver the
(possibly) wrong cluster id.
Add an API to be called by the driver when joined a cluster
in which the cluster id will be updated.
Use the locally stored cluster id instead of cfg80211's copy.
Ignore cluster id updates from cfg80211 if we already have one
configured.
Adjust the drivers that use the cfg80211 API
(cfg80211_nan_cluster_joined) directly, otherwise we break functionality
(i.e. accept frame check won't evaluate to true).
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
include/net/mac80211.h | 14 ++++++++++++++
net/mac80211/cfg.c | 19 +++++++++++++++++++
net/mac80211/rx.c | 4 ++--
net/mac80211/tx.c | 2 +-
4 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 02318a4be0e1..0d1b1d726b9c 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7891,6 +7891,20 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
*/
void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif);
+/**
+ * ieee80211_nan_cluster_joined - notify about NAN cluster join.
+ *
+ * This function is used to notify mac80211 about NAN cluster join.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @cluster_id: the cluster ID that was joined
+ * @new_cluster: true if this is a new cluster
+ * @gfp: allocation flags
+ */
+void ieee80211_nan_cluster_joined(struct ieee80211_vif *vif,
+ const u8 *cluster_id, bool new_cluster,
+ gfp_t gfp);
+
/**
* ieee80211_calc_rx_airtime - calculate estimated transmission airtime for RX.
*
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 00261bd6674b..c71af8878562 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -5215,6 +5215,25 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
}
EXPORT_SYMBOL(ieee80211_nan_func_match);
+void ieee80211_nan_cluster_joined(struct ieee80211_vif *vif,
+ const u8 *cluster_id, bool new_cluster,
+ gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
+ return;
+
+ if (WARN_ON(!sdata->u.nan.started))
+ return;
+
+ ether_addr_copy(sdata->u.nan.conf.cluster_id, cluster_id);
+
+ cfg80211_nan_cluster_joined(ieee80211_vif_to_wdev(vif), cluster_id,
+ new_cluster, gfp);
+}
+EXPORT_SYMBOL(ieee80211_nan_cluster_joined);
+
static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
struct net_device *dev,
const bool enabled)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3e5d1c47a5b0..82ea7404f3da 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4629,7 +4629,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
* action frames or authentication frames that are addressed to
* the local NAN interface.
*/
- return memcmp(sdata->wdev.u.nan.cluster_id,
+ return memcmp(sdata->u.nan.conf.cluster_id,
hdr->addr3, ETH_ALEN) == 0 &&
(ieee80211_is_public_action(hdr, skb->len) ||
(ieee80211_is_auth(hdr->frame_control) &&
@@ -4646,7 +4646,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
if (!nmi)
return false;
- if (!ether_addr_equal(nmi->wdev.u.nan.cluster_id,
+ if (!ether_addr_equal(nmi->u.nan.conf.cluster_id,
hdr->addr3))
return false;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 8e4876d1c544..1702f816419b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2854,7 +2854,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
ret = -ENOTCONN;
goto free;
}
- memcpy(hdr.addr3, nmi->wdev.u.nan.cluster_id, ETH_ALEN);
+ memcpy(hdr.addr3, nmi->u.nan.conf.cluster_id, ETH_ALEN);
hdrlen = 24;
break;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 00/15] wifi: mac80211: more NAN patches
From: Miri Korenblit @ 2026-04-29 11:40 UTC (permalink / raw)
To: linux-wireless
Hi,
This series adds a few more NAN features to mac80211 and mainly hwsim
support.
Thanks,
Miri
---
Andrei Otcheretianski (1):
wifi: mac80211: Fix a kernel panic in ieee80211_encrypt_tx_skb()
Avraham Stern (1):
wifi: mac80211: accept protected frames for NAN device
Benjamin Berg (8):
wifi: mac80211_hwsim: remove unused nan_vif struct member
wifi: mac80211_hwsim: move NAN related variables into a struct
wifi: mac80211_hwsim: split NAN handling into separate file
wifi: mac80211_hwsim: rename and switch simulation time to boottime
wifi: mac80211_hwsim: move timestamp writing later in the datapath
wifi: mac80211_hwsim: register beacon timer by calculating TBTT
wifi: mac80211_hwsim: refactor NAN timer handling
wifi: mac80211_hwsim: switch to use TXQs
Ilan Peer (2):
wifi: mac80211: allow userspace TX/RX over NAN Data interfaces
wifi: mac80211: Allow setting MAC address on interface creation
Miri Korenblit (3):
wifi: mac80211: track the id of the NAN cluster we joined
wifi: mac80211: avoid out-of-bounds access in monitor
wifi: mac80211: add NAN channel evacuation support
MAINTAINERS | 2 +-
drivers/net/wireless/virtual/Makefile | 2 +
.../net/wireless/virtual/mac80211_hwsim_i.h | 139 +++++
...mac80211_hwsim.c => mac80211_hwsim_main.c} | 508 +++++-------------
.../net/wireless/virtual/mac80211_hwsim_nan.c | 233 ++++++++
.../net/wireless/virtual/mac80211_hwsim_nan.h | 36 ++
include/net/mac80211.h | 29 +
net/mac80211/cfg.c | 19 +
net/mac80211/chan.c | 28 +-
net/mac80211/iface.c | 7 +-
net/mac80211/main.c | 4 +
net/mac80211/nan.c | 126 +++++
net/mac80211/offchannel.c | 9 +-
net/mac80211/rx.c | 26 +-
net/mac80211/status.c | 9 +-
net/mac80211/tx.c | 18 +-
16 files changed, 786 insertions(+), 409 deletions(-)
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_i.h
rename drivers/net/wireless/virtual/{mac80211_hwsim.c => mac80211_hwsim_main.c} (94%)
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.c
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.h
--
2.34.1
---
v2: fix MAINTAINERS file to include the newly added files
^ permalink raw reply
* Re: [RFC PATCH v1 8/9] arm64: Add unsafe_copy_from_user()
From: Will Deacon @ 2026-04-29 11:26 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
Thomas Gleixner, linux-alpha, linux-kernel, linux-snps-arc,
linux-arm-kernel, linux-mips, linuxppc-dev, kvm, linux-riscv,
linux-s390, sparclinux, linux-um, dmaengine, linux-efi, linux-fsi,
amd-gfx, dri-devel, intel-gfx, linux-wpan, netdev, linux-wireless,
linux-spi, linux-media, linux-staging, linux-serial, linux-usb,
xen-devel, linux-fsdevel, ocfs2-devel, bpf, kasan-dev, linux-mm,
linux-x25, rust-for-linux, linux-sound, sound-open-firmware,
linux-csky, linux-hexagon, loongarch, linux-m68k, linux-openrisc,
linux-parisc, linux-sh, linux-arch, catalin.marinas
In-Reply-To: <5b09e58a84c9edcfe5724db5cd57e45d96a96bfa.1777306795.git.chleroy@kernel.org>
[+Catalin]
On Mon, Apr 27, 2026 at 07:13:49PM +0200, Christophe Leroy (CS GROUP) wrote:
> At the time being, x86 and arm64 are missing unsafe_copy_from_user().
>
> Add it.
>
> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> ---
> arch/arm64/include/asm/uaccess.h | 29 ++++++++++++++++++++++++-----
> 1 file changed, 24 insertions(+), 5 deletions(-)
Why?
And please cc the arm64 maintainers on arm64 patches next time. You've
managed to cc most of the world apart from us.
Will
^ permalink raw reply
* Re: [DONOTAPPLY RFC PATCH v2 0/4] WiFi support for samsung,coreprimevelte
From: Francesco Dolcini @ 2026-04-29 11:22 UTC (permalink / raw)
To: Karel Balej
Cc: Brian Norris, Francesco Dolcini, Johannes Berg, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Duje Mihanović,
Andrew Lunn, Gregory Clement, Sebastian Hesselbarth, Ulf Hansson,
Frank Li, linux-wireless, devicetree, linux-kernel,
linux-arm-kernel, linux-mmc, ~postmarketos/upstreaming,
phone-devel, Jeff Chen, Peng Fan, david
In-Reply-To: <DI5L100Q1RKO.1A68EJIPWYSRC@matfyz.cz>
Hello Karel,
On Wed, Apr 29, 2026 at 12:55:23PM +0200, Karel Balej wrote:
> without the firmware being in linux-firmware?
What's the license of this firmware? Am I wrong saying that if
the license allows it, you could just send a patch to have it integrated
to the linux-firmware repository? Is there any history or documentation
on the topic (please apologize if this is a well known topic, just
answer RTFM if this is the case).
I read a couple of days ago that for example the firmwares for the
various Apple arm64 laptop cannot be redistributed and every single
individual that wants to use those needs to extract those from their
system.
All of that seems just a legal matter, and IANAL ...
Francesco
^ permalink raw reply
* Re: [DONOTAPPLY RFC PATCH v2 0/4] WiFi support for samsung,coreprimevelte
From: Karel Balej @ 2026-04-29 10:55 UTC (permalink / raw)
To: Brian Norris, Francesco Dolcini
Cc: Johannes Berg, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Duje Mihanović, Andrew Lunn, Gregory Clement,
Sebastian Hesselbarth, Ulf Hansson, Frank Li, linux-wireless,
devicetree, linux-kernel, linux-arm-kernel, linux-mmc,
~postmarketos/upstreaming, phone-devel, Jeff Chen, Peng Fan,
david
In-Reply-To: <aSnWYS2g5slVFaSk@gaggiata.pivistrello.it>
Hello,
Francesco Dolcini, 2025-11-28T18:05:37+01:00:
> Hello Karel,
>
> On Thu, Nov 27, 2025 at 04:29:12PM +0100, Karel Balej wrote:
>> To reiterate, the firmware is generally available but is not part of
>> linux-firmware and the entire process of upstreaming the chipset support is
>> stuck on that.
>
> I'll try to see if any of my contact in NXP Wi-Fi group is able to help. Give
> me a few days.
so I was in a long conversation over the past months with Jeff from NXP
who was very helpful and tried to arrange for the upstreaming of the
firmware. Unfortunately however, his efforts were ultimately rejected by
the internal management.
We were directed to try to reach out to NXP via the customer support
page which yielded nothing. The current situation is thus the same as a
few months ago, summarized in my quote above.
Brian, what are the options here now? Would it be possible to make an
exception and accept the patches without the firmware being in
linux-firmware? This is an old device with no mainstream audience so I
expect everyone who will want to use it will be able to supply the
firmware themselves and it would be great to not have to keep the
patches in a fork, especially when trying to build on top of them
further (such as to fix the driver-firmware incompatibilities discussed
in one of the patches of this series).
Francesco, would you perhaps still be able to help in any way?
Thank you, kind regards,
K. B.
^ permalink raw reply
* Re: [PATCH v4 03/15] firmware: qcom_scm: Migrate to generic PAS service
From: Mukesh Ojha @ 2026-04-29 10:48 UTC (permalink / raw)
To: Sumit Garg
Cc: andersson, konradybcio, linux-arm-msm, devicetree, dri-devel,
freedreno, linux-media, netdev, linux-wireless, ath12k,
linux-remoteproc, robh, krzk+dt, conor+dt, robin.clark, sean,
akhilpo, lumag, abhinav.kumar, jesszhan0024, marijn.suijten,
airlied, simona, vikash.garodia, dikshita.agarwal, bod, mchehab,
elder, andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
mathieu.poirier, trilokkumar.soni, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260427095603.1157963-4-sumit.garg@kernel.org>
On Mon, Apr 27, 2026 at 03:25:51PM +0530, Sumit Garg wrote:
> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>
> With the availability of generic PAS service, let's add SCM calls as
> a backend to keep supporting legacy QTEE interfaces. The exported
> qcom_scm* wrappers will get dropped once all the client drivers get
> migrated as part of future patches.
>
> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> ---
> drivers/firmware/qcom/Kconfig | 1 +
> drivers/firmware/qcom/qcom_scm.c | 335 ++++++++++++++-----------------
> 2 files changed, 155 insertions(+), 181 deletions(-)
>
> diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
> index 8653639d06db..9a12ae2b639d 100644
> --- a/drivers/firmware/qcom/Kconfig
> +++ b/drivers/firmware/qcom/Kconfig
> @@ -15,6 +15,7 @@ config QCOM_PAS
> TEE bus based PAS service implementation.
>
> config QCOM_SCM
> + select QCOM_PAS
> select QCOM_TZMEM
> tristate
>
> diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
> index 9b06a69d3a6d..d87a962e93da 100644
> --- a/drivers/firmware/qcom/qcom_scm.c
> +++ b/drivers/firmware/qcom/qcom_scm.c
> @@ -13,6 +13,7 @@
> #include <linux/dma-mapping.h>
> #include <linux/err.h>
> #include <linux/export.h>
> +#include <linux/firmware/qcom/qcom_pas.h>
> #include <linux/firmware/qcom/qcom_scm.h>
> #include <linux/firmware/qcom/qcom_tzmem.h>
> #include <linux/init.h>
> @@ -33,6 +34,7 @@
>
> #include <dt-bindings/interrupt-controller/arm-gic.h>
>
> +#include "qcom_pas.h"
> #include "qcom_scm.h"
> #include "qcom_tzmem.h"
>
> @@ -479,25 +481,6 @@ void qcom_scm_cpu_power_down(u32 flags)
> }
> EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down);
>
> -int qcom_scm_set_remote_state(u32 state, u32 id)
> -{
> - struct qcom_scm_desc desc = {
> - .svc = QCOM_SCM_SVC_BOOT,
> - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
> - .arginfo = QCOM_SCM_ARGS(2),
> - .args[0] = state,
> - .args[1] = id,
> - .owner = ARM_SMCCC_OWNER_SIP,
> - };
> - struct qcom_scm_res res;
> - int ret;
> -
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> -
> - return ret ? : res.result[0];
> -}
> -EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
> -
> static int qcom_scm_disable_sdi(void)
> {
> int ret;
> @@ -570,26 +553,12 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
> dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
> }
>
> -/**
> - * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service
> - * context for a given peripheral
> - *
> - * PAS context is device-resource managed, so the caller does not need
> - * to worry about freeing the context memory.
> - *
> - * @dev: PAS firmware device
> - * @pas_id: peripheral authentication service id
> - * @mem_phys: Subsystem reserve memory start address
> - * @mem_size: Subsystem reserve memory size
> - *
> - * Returns: The new PAS context, or ERR_PTR() on failure.
> - */
> struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
> u32 pas_id,
> phys_addr_t mem_phys,
> size_t mem_size)
> {
> - struct qcom_scm_pas_context *ctx;
> + struct qcom_pas_context *ctx;
>
> ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> if (!ctx)
> @@ -600,11 +569,12 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
> ctx->mem_phys = mem_phys;
> ctx->mem_size = mem_size;
>
> - return ctx;
> + return (struct qcom_scm_pas_context *)ctx;
> }
> EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc);
>
> -static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
> +static int __qcom_scm_pas_init_image(struct device *dev, u32 pas_id,
> + dma_addr_t mdata_phys,
> struct qcom_scm_res *res)
> {
> struct qcom_scm_desc desc = {
> @@ -626,7 +596,7 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
>
> desc.args[1] = mdata_phys;
>
> - ret = qcom_scm_call(__scm->dev, &desc, res);
> + ret = qcom_scm_call(dev, &desc, res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -635,7 +605,8 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
> return ret;
> }
>
> -static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
> +static int qcom_scm_pas_prep_and_init_image(struct device *dev,
> + struct qcom_pas_context *ctx,
> const void *metadata, size_t size)
> {
> struct qcom_scm_res res;
> @@ -650,7 +621,7 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
> memcpy(mdata_buf, metadata, size);
> mdata_phys = qcom_tzmem_to_phys(mdata_buf);
>
> - ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res);
> + ret = __qcom_scm_pas_init_image(dev, ctx->pas_id, mdata_phys, &res);
> if (ret < 0)
> qcom_tzmem_free(mdata_buf);
> else
> @@ -659,25 +630,9 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
> return ret ? : res.result[0];
> }
>
> -/**
> - * qcom_scm_pas_init_image() - Initialize peripheral authentication service
> - * state machine for a given peripheral, using the
> - * metadata
> - * @pas_id: peripheral authentication service id
> - * @metadata: pointer to memory containing ELF header, program header table
> - * and optional blob of data used for authenticating the metadata
> - * and the rest of the firmware
> - * @size: size of the metadata
> - * @ctx: optional pas context
> - *
> - * Return: 0 on success.
> - *
> - * Upon successful return, the PAS metadata context (@ctx) will be used to
> - * track the metadata allocation, this needs to be released by invoking
> - * qcom_scm_pas_metadata_release() by the caller.
> - */
> -int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> - struct qcom_scm_pas_context *ctx)
> +static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id,
> + const void *metadata, size_t size,
> + struct qcom_pas_context *ctx)
> {
> struct qcom_scm_res res;
> dma_addr_t mdata_phys;
> @@ -685,7 +640,7 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> int ret;
>
> if (ctx && ctx->use_tzmem)
> - return qcom_scm_pas_prep_and_init_image(ctx, metadata, size);
> + return qcom_scm_pas_prep_and_init_image(dev, ctx, metadata, size);
>
> /*
> * During the scm call memory protection will be enabled for the meta
> @@ -699,16 +654,15 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> * If we pass a buffer that is already part of an SHM Bridge to this
> * call, it will fail.
> */
> - mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
> - GFP_KERNEL);
> + mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL);
> if (!mdata_buf)
> return -ENOMEM;
>
> memcpy(mdata_buf, metadata, size);
>
> - ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res);
> + ret = __qcom_scm_pas_init_image(dev, pas_id, mdata_phys, &res);
> if (ret < 0 || !ctx) {
> - dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
> + dma_free_coherent(dev, size, mdata_buf, mdata_phys);
> } else if (ctx) {
> ctx->ptr = mdata_buf;
> ctx->phys = mdata_phys;
> @@ -717,36 +671,35 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
>
> return ret ? : res.result[0];
> }
> -EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
>
> -/**
> - * qcom_scm_pas_metadata_release() - release metadata context
> - * @ctx: pas context
> - */
> -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
> +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> + struct qcom_scm_pas_context *ctx)
> {
> - if (!ctx->ptr)
> - return;
> + return __qcom_scm_pas_init_image2(__scm->dev, pas_id, metadata, size,
> + (struct qcom_pas_context *)ctx);
> +}
> +EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
>
> +static void __qcom_scm_pas_metadata_release(struct device *dev,
> + struct qcom_pas_context *ctx)
> +{
> if (ctx->use_tzmem)
> qcom_tzmem_free(ctx->ptr);
> else
> - dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
> + dma_free_coherent(dev, ctx->size, ctx->ptr, ctx->phys);
>
> ctx->ptr = NULL;
> }
> +
> +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
> +{
> + __qcom_scm_pas_metadata_release(__scm->dev,
> + (struct qcom_pas_context *)ctx);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
>
> -/**
> - * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
> - * for firmware loading
> - * @pas_id: peripheral authentication service id
> - * @addr: start address of memory area to prepare
> - * @size: size of the memory area to prepare
> - *
> - * Returns 0 on success.
> - */
> -int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
> +static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id,
> + phys_addr_t addr, phys_addr_t size)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -768,7 +721,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
> if (ret)
> goto disable_clk;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -776,9 +729,15 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
>
> return ret ? : res.result[0];
> }
> +
> +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
> +{
> + return __qcom_scm_pas_mem_setup(__scm->dev, pas_id, addr, size);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
>
> -static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
> +static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id,
> + void *input_rt_tzm,
> size_t input_rt_size,
> size_t *output_rt_size)
> {
> @@ -813,7 +772,7 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
> * with output_rt_tzm buffer with res.result[2] size however, It should not
> * be of unresonable size.
> */
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> if (!ret && res.result[2] > SZ_1G) {
> ret = -E2BIG;
> goto free_output_rt;
> @@ -830,51 +789,11 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
> return ret ? ERR_PTR(ret) : output_rt_tzm;
> }
>
> -/**
> - * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
> - * for a given peripheral.
> - *
> - * Qualcomm remote processor may rely on both static and dynamic resources for
> - * its functionality. Static resources typically refer to memory-mapped addresses
> - * required by the subsystem and are often embedded within the firmware binary
> - * and dynamic resources, such as shared memory in DDR etc., are determined at
> - * runtime during the boot process.
> - *
> - * On Qualcomm Technologies devices, it's possible that static resources are not
> - * embedded in the firmware binary and instead are provided by TrustZone However,
> - * dynamic resources are always expected to come from TrustZone. This indicates
> - * that for Qualcomm devices, all resources (static and dynamic) will be provided
> - * by TrustZone via the SMC call.
> - *
> - * If the remote processor firmware binary does contain static resources, they
> - * should be passed in input_rt. These will be forwarded to TrustZone for
> - * authentication. TrustZone will then append the dynamic resources and return
> - * the complete resource table in output_rt_tzm.
> - *
> - * If the remote processor firmware binary does not include a resource table,
> - * the caller of this function should set input_rt as NULL and input_rt_size
> - * as zero respectively.
> - *
> - * More about documentation on resource table data structures can be found in
> - * include/linux/remoteproc.h
> - *
> - * @ctx: PAS context
> - * @pas_id: peripheral authentication service id
> - * @input_rt: resource table buffer which is present in firmware binary
> - * @input_rt_size: size of the resource table present in firmware binary
> - * @output_rt_size: TrustZone expects caller should pass worst case size for
> - * the output_rt_tzm.
> - *
> - * Return:
> - * On success, returns a pointer to the allocated buffer containing the final
> - * resource table and output_rt_size will have actual resource table size from
> - * TrustZone. The caller is responsible for freeing the buffer. On failure,
> - * returns ERR_PTR(-errno).
> - */
> -struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
> - void *input_rt,
> - size_t input_rt_size,
> - size_t *output_rt_size)
> +static void *__qcom_scm_pas_get_rsc_table2(struct device *dev,
> + struct qcom_pas_context *ctx,
> + void *input_rt,
> + size_t input_rt_size,
> + size_t *output_rt_size)
> {
> struct resource_table empty_rsc = {};
> size_t size = SZ_16K;
> @@ -909,11 +828,12 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
>
> memcpy(input_rt_tzm, input_rt, input_rt_size);
>
> - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm,
> + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id,
> + input_rt_tzm,
> input_rt_size, &size);
> if (PTR_ERR(output_rt_tzm) == -EOVERFLOW)
> /* Try again with the size requested by the TZ */
> - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id,
> + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id,
> input_rt_tzm,
> input_rt_size,
> &size);
> @@ -943,16 +863,20 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
>
> return ret ? ERR_PTR(ret) : tbl_ptr;
> }
> +
> +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
> + void *input_rt,
> + size_t input_rt_size,
> + size_t *output_rt_size)
> +{
> + return __qcom_scm_pas_get_rsc_table2(__scm->dev,
> + (struct qcom_pas_context *)ctx,
> + input_rt, input_rt_size,
> + output_rt_size);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
>
> -/**
> - * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
> - * and reset the remote processor
> - * @pas_id: peripheral authentication service id
> - *
> - * Return 0 on success.
> - */
> -int qcom_scm_pas_auth_and_reset(u32 pas_id)
> +static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -972,7 +896,7 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id)
> if (ret)
> goto disable_clk;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -980,28 +904,15 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id)
>
> return ret ? : res.result[0];
> }
> +
> +int qcom_scm_pas_auth_and_reset(u32 pas_id)
> +{
> + return __qcom_scm_pas_auth_and_reset(__scm->dev, pas_id);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
>
> -/**
> - * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
> - * remote processor
> - *
> - * @ctx: Context saved during call to qcom_scm_pas_context_init()
> - *
> - * This function performs the necessary steps to prepare a PAS subsystem,
> - * authenticate it using the provided metadata, and initiate a reset sequence.
> - *
> - * It should be used when Linux is in control setting up the IOMMU hardware
> - * for remote subsystem during secure firmware loading processes. The preparation
> - * step sets up a shmbridge over the firmware memory before TrustZone accesses the
> - * firmware memory region for authentication. The authentication step verifies
> - * the integrity and authenticity of the firmware or configuration using secure
> - * metadata. Finally, the reset step ensures the subsystem starts in a clean and
> - * sane state.
> - *
> - * Return: 0 on success, negative errno on failure.
> - */
> -int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> +static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev,
> + struct qcom_pas_context *ctx)
> {
> u64 handle;
> int ret;
> @@ -1012,7 +923,7 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> * memory region and then invokes a call to TrustZone to authenticate.
> */
> if (!ctx->use_tzmem)
> - return qcom_scm_pas_auth_and_reset(ctx->pas_id);
> + return __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
>
> /*
> * When Linux runs @ EL2 Linux must create the shmbridge itself and then
> @@ -1022,20 +933,45 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> if (ret)
> return ret;
>
> - ret = qcom_scm_pas_auth_and_reset(ctx->pas_id);
> + ret = __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
> qcom_tzmem_shm_bridge_delete(handle);
>
> return ret;
> }
> +
> +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> +{
> + return __qcom_scm_pas_prepare_and_auth_reset(__scm->dev,
> + (struct qcom_pas_context *)ctx);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset);
>
> -/**
> - * qcom_scm_pas_shutdown() - Shut down the remote processor
> - * @pas_id: peripheral authentication service id
> - *
> - * Returns 0 on success.
> - */
> -int qcom_scm_pas_shutdown(u32 pas_id)
> +static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state,
> + u32 pas_id)
> +{
> + struct qcom_scm_desc desc = {
> + .svc = QCOM_SCM_SVC_BOOT,
> + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
> + .arginfo = QCOM_SCM_ARGS(2),
> + .args[0] = state,
> + .args[1] = pas_id,
> + .owner = ARM_SMCCC_OWNER_SIP,
> + };
> + struct qcom_scm_res res;
> + int ret;
> +
> + ret = qcom_scm_call(dev, &desc, &res);
> +
> + return ret ? : res.result[0];
> +}
> +
> +int qcom_scm_set_remote_state(u32 state, u32 id)
s/id/pas_id
> +{
> + return __qcom_scm_pas_set_remote_state(__scm->dev, state, id);
> +}
> +EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
> +
> +static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -1055,7 +991,7 @@ int qcom_scm_pas_shutdown(u32 pas_id)
> if (ret)
> goto disable_clk;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -1063,16 +999,14 @@ int qcom_scm_pas_shutdown(u32 pas_id)
>
> return ret ? : res.result[0];
> }
> +
> +int qcom_scm_pas_shutdown(u32 pas_id)
> +{
> + return __qcom_scm_pas_shutdown(__scm->dev, pas_id);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
>
> -/**
> - * qcom_scm_pas_supported() - Check if the peripheral authentication service is
> - * available for the given peripherial
> - * @pas_id: peripheral authentication service id
> - *
> - * Returns true if PAS is supported for this peripheral, otherwise false.
> - */
> -bool qcom_scm_pas_supported(u32 pas_id)
> +static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -1084,16 +1018,49 @@ bool qcom_scm_pas_supported(u32 pas_id)
> };
> struct qcom_scm_res res;
>
> - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
> + if (!__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_PIL,
> QCOM_SCM_PIL_PAS_IS_SUPPORTED))
> return false;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
>
> return ret ? false : !!res.result[0];
> }
> +
> +bool qcom_scm_pas_supported(u32 pas_id)
> +{
> + return __qcom_scm_pas_supported(__scm->dev, pas_id);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
>
> +static struct qcom_pas_ops qcom_pas_ops_scm = {
> + .drv_name = "qcom_scm",
> + .supported = __qcom_scm_pas_supported,
> + .init_image = __qcom_scm_pas_init_image2,
> + .mem_setup = __qcom_scm_pas_mem_setup,
> + .get_rsc_table = __qcom_scm_pas_get_rsc_table2,
> + .auth_and_reset = __qcom_scm_pas_auth_and_reset,
> + .prepare_and_auth_reset = __qcom_scm_pas_prepare_and_auth_reset,
> + .set_remote_state = __qcom_scm_pas_set_remote_state,
> + .shutdown = __qcom_scm_pas_shutdown,
> + .metadata_release = __qcom_scm_pas_metadata_release,
> +};
> +
> +/**
> + * qcom_scm_is_pas_available() - Check if the peripheral authentication service
> + * is available via SCM or not
> + *
> + * Returns true if PAS is available, otherwise false.
> + */
> +static bool qcom_scm_is_pas_available(void)
> +{
> + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
> + QCOM_SCM_PIL_PAS_AUTH_AND_RESET))
QCOM_SCM_PIL_PAS_IS_SUPPORTED ?
> + return false;
> +
> + return true;
> +}
> +
> static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
> {
> struct qcom_scm_desc desc = {
> @@ -2836,6 +2803,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
>
> __get_convention();
>
> + if (qcom_scm_is_pas_available()) {
Use qcom_scm_pas_supported() and remove qcom_scm_is_pas_available()..
> + qcom_pas_ops_scm.dev = scm->dev;
> + qcom_pas_ops_register(&qcom_pas_ops_scm);
> + }
> +
> /*
> * If "download mode" is requested, from this point on warmboot
> * will cause the boot stages to enter download mode, unless
> @@ -2875,6 +2847,7 @@ static void qcom_scm_shutdown(struct platform_device *pdev)
> {
> /* Clean shutdown, disable download mode to allow normal restart */
> qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP);
> + qcom_pas_ops_unregister();
> }
>
> static const struct of_device_id qcom_scm_dt_match[] = {
> --
> 2.51.0
>
With above changes,
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
--
-Mukesh Ojha
^ permalink raw reply
* Re: [PATCH v4] wifi: ath11k: fix peer resolution on rx path when peer_id=0
From: Praneesh P @ 2026-04-29 10:47 UTC (permalink / raw)
To: Baochen Qiang, Matthew Leach, Jeff Johnson
Cc: Rameshkumar Sundaram, linux-wireless, ath11k, linux-kernel,
kernel
In-Reply-To: <530a9e0b-ae5e-48ae-a2c8-73152e2dd6df@oss.qualcomm.com>
On 4/29/2026 1:06 PM, Baochen Qiang wrote:
>
> On 4/24/2026 5:50 PM, Matthew Leach wrote:
>> It has been observed that on certain chipsets a peer can be assigned
>> peer_id=0. For reception of non-aggregated MPDUs this is fine as
>> ath11k_dp_rx_h_find_peer() has a fallback case where it locates the peer
>> based upon the source MAC address. On an aggregated link, the mpdu_start
>> header is only populated by hardware on the first sub-MSDU. This causes
>> the peer resolution to be skipped for the subsequent MSDUs and the
>> encryption type of these frames to be set to an incorrect value,
>> resulting in these MSDUs being dropped by ieee80211.
>>
>> ath11k_pci 0000:03:00.0: data rx skb 000000002f4b704d len 1534 peer xx:xx:xx:xx:xx:xx 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 1 last_msdu 0
>> ath11k_pci 0000:03:00.0: data rx skb 0000000038acd580 len 1534 peer (null) 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d00 fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 0 last_msdu 1
>>
>> Remove the null peer_id checks in ath11k_dp_rx_h_find_peer() and
>> ath11k_hal_rx_parse_mon_status_tlv(), allowing peers with an assigned ID
>> of 0 to be resolved.
>>
>> Fixes: 2167fa606c0f ("ath11k: Add support for RX decapsulation offload")
>> Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9
> Tested-on: is not an official tag but is ath driver specific, so generally it is put above
> all official tags:
>
> Tested-on: ...
> <blank line>
> Fixes: ...
> s-o-b: ...
>
> But since this is trivial I think Jeff can make the change when he merges it, so no need
> to respin just for it.
For QCN9074, IPQ8074, IPQ5018 and IPQ6018, Peer_id 0 is not a expected
value. Like ath12k, it is better to handle it through hw_params.
>> Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
>> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
>> ---
>> Changes in v4:
>> - Prefix subsystem with 'wifi:'.
>> - Added the 'Tested-by' and 'Fixes' trailers.
>> - Link to v3: https://patch.msgid.link/20260417-ath11k-null-peerid-workaround-v3-1-d04302284486@collabora.com
>>
>> Changes in v3:
>> - Clarified that the mpdu_start header isn't populated by the h/w for
>> all sub-MSDUs in the commit message.
>> - Fix second null-peer check in ath11k_hal_rx_parse_mon_status_tlv().
>> - Link to v2: https://patch.msgid.link/20260415-ath11k-null-peerid-workaround-v2-1-2abae3bbac16@collabora.com
>>
>> Changes in v2:
>>
>> - Since peer_id=0 is a valid condition on some chips, remove the guard
>> that prevented the peer lookup.
>> - Link to v1: https://patch.msgid.link/20260326-ath11k-null-peerid-workaround-v1-1-0c2fd53202f8@collabora.com
>>
>> To: Jeff Johnson <jjohnson@kernel.org>
>> Cc: linux-wireless@vger.kernel.org
>> Cc: ath11k@lists.infradead.org
>> Cc: linux-kernel@vger.kernel.org
>> ---
>> drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +--
>> drivers/net/wireless/ath/ath11k/hal_rx.c | 5 +----
>> 2 files changed, 2 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
>> index 49d959b2e148..ff2c78a4e5f3 100644
>> --- a/drivers/net/wireless/ath/ath11k/dp_rx.c
>> +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
>> @@ -2215,8 +2215,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu)
>>
>> lockdep_assert_held(&ab->base_lock);
>>
>> - if (rxcb->peer_id)
>> - peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
>> + peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
>>
>> if (peer)
>> return peer;
>> diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c
>> index 753bd93f0212..51e0840bc0d1 100644
>> --- a/drivers/net/wireless/ath/ath11k/hal_rx.c
>> +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c
>> @@ -1467,11 +1467,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
>> case HAL_RX_MPDU_START: {
>> struct hal_rx_mpdu_info *mpdu_info =
>> (struct hal_rx_mpdu_info *)tlv_data;
>> - u16 peer_id;
>>
>> - peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info);
>> - if (peer_id)
>> - ppdu_info->peer_id = peer_id;
>> + ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info);
>> break;
>> }
>> case HAL_RXPCU_PPDU_END_INFO: {
>>
>> ---
>> base-commit: f338e77383789c0cae23ca3d48adcc5e9e137e3c
>> change-id: 20260326-ath11k-null-peerid-workaround-625a129781b1
>>
>> Best regards,
>> --
>> Matt
>>
>
^ permalink raw reply
* Re: [PATCH net-next] net: Consistently define pci_device_ids using named initializers
From: Uwe Kleine-König (The Capable Hub) @ 2026-04-29 10:30 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: Michael Grzeschik, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Vincent Mailhol, Krzysztof Halasa,
Johannes Berg, Markus Schneider-Pargmann, Steffen Klassert,
David Dillow, Ion Badulescu, Mark Einon, Rasesh Mody,
GR-Linux-NIC-Dev, Manish Chopra, Potnuri Bharat Teja,
Denis Kirjanov, Jijie Shao, Jian Shen, Cai Huoqing, Fan Gong,
Tony Nguyen, Przemek Kitszel, Tariq Toukan, Saeed Mahameed,
Leon Romanovsky, Mark Bloch, Ido Schimmel, Petr Machata,
Yibo Dong, Simon Horman, Heiner Kallweit, nic_swsd, Jiri Pirko,
Francois Romieu, Daniele Venzano, Samuel Chessman, Jiawen Wu,
Mengyuan Lou, Kevin Curtis, Arend van Spriel, Stanislav Yakovlev,
Richard Cochran, Kees Cook, Thomas Gleixner, Thomas Fourier,
Ingo Molnar, Kory Maincent, Zilin Guan, Marco Crivellari,
Vadim Fedorenko, Jacob Keller, Philipp Stanner, Bjorn Helgaas,
Yeounsu Moon, Denis Benato, Yonglong Liu, Andy Shevchenko,
Yicong Hui, Randy Dunlap, MD Danish Anwar, Nathan Chancellor,
Sai Krishna, Ethan Nelson-Moore, Larysa Zaremba, Joe Damato,
Double Lo, Colin Ian King, netdev, linux-kernel, linux-can,
linux-parisc, intel-wired-lan, linux-rdma, oss-drivers,
linux-wireless, brcm80211, brcm80211-dev-list.pdl
In-Reply-To: <20260429-responsible-clever-coyote-6b79f1-mkl@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 1350 bytes --]
Hello Marc,
On Wed, Apr 29, 2026 at 11:10:21AM +0200, Marc Kleine-Budde wrote:
> On 28.04.2026 19:18:44, Uwe Kleine-König (The Capable Hub) wrote:
> > },
> > - { 0,}
> > + { }
>
> Nitpick: can you convert the terminating entry to follow the same style
> as the rest of the driver:
>
> diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c
> index a03553b80a5d..d69ff0ccfd94 100644
> --- a/drivers/net/can/sja1000/plx_pci.c
> +++ b/drivers/net/can/sja1000/plx_pci.c
> @@ -353,8 +353,8 @@ static const struct pci_device_id plx_pci_tbl[] = {
> PCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
> ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS),
> .driver_data = (kernel_ulong_t)&plx_pci_card_info_asem_dual_can,
> - },
> - { }
> + }, {
> + }
> };
> MODULE_DEVICE_TABLE(pci, plx_pci_tbl);
That might be subjective. I also see some value to have the terminating
entry stand out a bit in the formatting and so I usually kept the entry
as it was.
If you prefer I can rework the can drivers at least to match your taste.
As you didn't object to have the can drivers converted as part of the
drivers/net patch, I assume that part is OK for you?!
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH net-next] net: Consistently define pci_device_ids using named initializers
From: Uwe Kleine-König (The Capable Hub) @ 2026-04-29 10:26 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Michael Grzeschik, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Marc Kleine-Budde, Vincent Mailhol,
Krzysztof Halasa, Johannes Berg, Markus Schneider-Pargmann,
Steffen Klassert, David Dillow, Ion Badulescu, Mark Einon,
Rasesh Mody, GR-Linux-NIC-Dev, Manish Chopra, Potnuri Bharat Teja,
Denis Kirjanov, Jijie Shao, Jian Shen, Cai Huoqing, Fan Gong,
Tony Nguyen, Przemek Kitszel, Tariq Toukan, Saeed Mahameed,
Leon Romanovsky, Mark Bloch, Ido Schimmel, Petr Machata,
Yibo Dong, Simon Horman, Heiner Kallweit, nic_swsd, Jiri Pirko,
Francois Romieu, Daniele Venzano, Samuel Chessman, Jiawen Wu,
Mengyuan Lou, Kevin Curtis, Arend van Spriel, Stanislav Yakovlev,
Richard Cochran, Kees Cook, Thomas Gleixner, Thomas Fourier,
Ingo Molnar, Kory Maincent, Zilin Guan, Marco Crivellari,
Vadim Fedorenko, Jacob Keller, Philipp Stanner, Bjorn Helgaas,
Yeounsu Moon, Denis Benato, Yonglong Liu, Yicong Hui,
Randy Dunlap, MD Danish Anwar, Nathan Chancellor, Sai Krishna,
Ethan Nelson-Moore, Larysa Zaremba, Joe Damato, Double Lo,
Colin Ian King, netdev, linux-kernel, linux-can, linux-parisc,
intel-wired-lan, linux-rdma, oss-drivers, linux-wireless,
brcm80211, brcm80211-dev-list.pdl
In-Reply-To: <afGrPvUeZ-DjWbC8@ashevche-desk.local>
[-- Attachment #1: Type: text/plain, Size: 3039 bytes --]
[I dropped a few addresses from Cc: that bounced for me before.]
Hello Andy,
On Wed, Apr 29, 2026 at 09:54:54AM +0300, Andy Shevchenko wrote:
> On Tue, Apr 28, 2026 at 07:18:44PM +0200, Uwe Kleine-König (The Capable Hub) wrote:
> > ... and PCI device helpers.
> >
> > The various struct pci_device_id arrays were initialized mostly by one
> > the PCI_DEVICE macros and then list expressions. The latter isn't easily
> > readable if you're not into PCI. Using named initializers is more
> > explicit and thus easier to parse.
> >
> > Also use PCI_DEVICE* helper macros to assign .vendor, .device,
> > .subvendor and .subdevice where appropriate and skip explicit
> > assignments of 0 (which the compiler takes care of).
> >
> > The secret plan is to make struct pci_device_id::driver_data an
> > anonymous union (similar to
> > https://lore.kernel.org/all/cover.1776579304.git.u.kleine-koenig@baylibre.com/)
> > and that requires named initializers. But it's also a nice cleanup on
> > its own.
> >
> > This change doesn't introduce changes to the compiled pci_device_id
> > arrays. Tested on x86 and arm64.
>
> ...
>
> > - {0,} /* 0 terminated list. */
> > + { } /* 0 terminated list. */
>
> The comments like these are just noises.
Agreed, but I'd consider it out of scope for this patch to drop these
comments. That might also be subjective.
> The rule of thumb is to play with a
> trailing comma:
> - always drop it in the terminator entry
> - always keep it in the normal initialisers when semantically it's not a
> terminator
That was my intention. Will rework.
> > static const struct pci_device_id liquidio_pci_tbl[] = {
> > { /* 68xx */
> > - PCI_VENDOR_ID_CAVIUM, 0x91, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
> > + PCI_VDEVICE(CAVIUM, 0x91)
>
> Use full fixed-width device id value(s). 0x0091 here and so on...
Sounds fair.
> > },
>
> Also seems that you may decrease number of LoC here putting it as
>
> { PCI_VDEVICE(CAVIUM, 0x0091) }, /* 68xx */
>
> and so on...
Agreed if all lines of an array can be compressed like that.
> > { /* 66xx */
> > - PCI_VENDOR_ID_CAVIUM, 0x92, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
> > + PCI_VDEVICE(CAVIUM, 0x92)
> > },
> > { /* 23xx pf */
> > - PCI_VENDOR_ID_CAVIUM, 0x9702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
> > + PCI_VDEVICE(CAVIUM, 0x9702)
> > },
> > - {
> > - 0, 0, 0, 0, 0, 0, 0
> > - }
> > + { }
> > };
>
> ...
>
> > #define CH_PCI_DEVICE_ID_TABLE_DEFINE_END \
> > - { 0, } \
> > + { } \
> > }
>
> Why do we have this macro at all?
Over engineering? Reworking that also seems to be out of scope for this
patch to me.
> Also I somehow managed to remove, but I remember you had an inner comma in some
> cases after the .driver_data, when the full ID entry is located on a single
> line. I.o.w. do
>
> { PCI_...(), .driver_data = ... // no trailing comma here! },
That was also my intention. Will rework.
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
From: Usama Arif @ 2026-04-29 10:25 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Usama Arif, Yury Norov, Andrew Morton, Linus Torvalds,
David Laight, Thomas Gleixner, linux-alpha, linux-kernel,
linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
netdev, linux-wireless, linux-spi, linux-media, linux-staging,
linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
sound-open-firmware, linux-csky, linux-hexagon, loongarch,
linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <0ee46bb228d97163fbdc14f2a7c52b93d8bc34ce.1777306795.git.chleroy@kernel.org>
On Mon, 27 Apr 2026 19:13:48 +0200 "Christophe Leroy (CS GROUP)" <chleroy@kernel.org> wrote:
> At the time being, x86 and arm64 are missing unsafe_copy_from_user().
>
> Add it.
>
> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> ---
> arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++-----
> 1 file changed, 24 insertions(+), 5 deletions(-)
>
> diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
> index 3a0dd3c2b233..10c458ffa399 100644
> --- a/arch/x86/include/asm/uaccess.h
> +++ b/arch/x86/include/asm/uaccess.h
> @@ -598,7 +598,7 @@ _label: \
> * We want the unsafe accessors to always be inlined and use
> * the error labels - thus the macro games.
> */
> -#define unsafe_copy_loop(dst, src, len, type, label) \
> +#define unsafe_put_loop(dst, src, len, type, label) \
> while (len >= sizeof(type)) { \
> unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \
> dst += sizeof(type); \
> @@ -611,10 +611,29 @@ do { \
> char __user *__ucu_dst = (_dst); \
> const char *__ucu_src = (_src); \
> size_t __ucu_len = (_len); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
> +} while (0)
> +
> +#define unsafe_get_loop(dst, src, len, type, label) \
> + while (len >= sizeof(type)) { \
> + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \
Hi,
Just wanted to check if src and dst need to be swapped? Same for arm64 patch.
> + dst += sizeof(type); \
> + src += sizeof(type); \
> + len -= sizeof(type); \
> + }
> +
> +#define unsafe_copy_from_user(_dst,_src,_len,label) \
> +do { \
> + char *__ucu_dst = (_dst); \
> + const char __user *__ucu_src = (_src); \
> + size_t __ucu_len = (_len); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
> } while (0)
>
> #ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
> --
> 2.49.0
>
>
^ permalink raw reply
* Re: [PATCH] wifi: ath10k: drop gpio_led reference
From: Loic Poulain @ 2026-04-29 10:21 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Jeff Johnson, Arnd Bergmann, Kang Yang, Baochen Qiang,
Sumanth Gavini, linux-wireless, ath10k, linux-kernel
In-Reply-To: <20260428162955.614739-1-arnd@kernel.org>
On Tue, Apr 28, 2026 at 6:30 PM Arnd Bergmann <arnd@kernel.org> wrote:
>
> From: Arnd Bergmann <arnd@arndb.de>
>
> The driver uses a 'struct gpio_led' internally, but does not actually interact
> with the gpio_led driver, in particular it does not actually use gpiolib here.
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath10k/core.h | 1 -
> drivers/net/wireless/ath/ath10k/leds.c | 8 +-------
> 2 files changed, 1 insertion(+), 8 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
> index 73a9db302245..dfee432615eb 100644
> --- a/drivers/net/wireless/ath/ath10k/core.h
> +++ b/drivers/net/wireless/ath/ath10k/core.h
> @@ -1269,7 +1269,6 @@ struct ath10k {
> } testmode;
>
> struct {
> - struct gpio_led wifi_led;
> struct led_classdev cdev;
> char label[48];
> u32 gpio_state_pin;
> diff --git a/drivers/net/wireless/ath/ath10k/leds.c b/drivers/net/wireless/ath/ath10k/leds.c
> index 3a6c8111e7c6..a3961e7760a5 100644
> --- a/drivers/net/wireless/ath/ath10k/leds.c
> +++ b/drivers/net/wireless/ath/ath10k/leds.c
> @@ -19,15 +19,13 @@ static int ath10k_leds_set_brightness_blocking(struct led_classdev *led_cdev,
> {
> struct ath10k *ar = container_of(led_cdev, struct ath10k,
> leds.cdev);
> - struct gpio_led *led = &ar->leds.wifi_led;
>
> mutex_lock(&ar->conf_mutex);
>
> if (ar->state != ATH10K_STATE_ON)
> goto out;
>
> - ar->leds.gpio_state_pin = (brightness != LED_OFF) ^ led->active_low;
> - ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, ar->leds.gpio_state_pin);
> + ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, brightness == LED_OFF);
>
> out:
> mutex_unlock(&ar->conf_mutex);
> @@ -63,13 +61,9 @@ int ath10k_leds_register(struct ath10k *ar)
>
> snprintf(ar->leds.label, sizeof(ar->leds.label), "ath10k-%s",
> wiphy_name(ar->hw->wiphy));
> - ar->leds.wifi_led.active_low = 1;
> - ar->leds.wifi_led.name = ar->leds.label;
> - ar->leds.wifi_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
>
> ar->leds.cdev.name = ar->leds.label;
> ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking;
> - ar->leds.cdev.default_trigger = ar->leds.wifi_led.default_trigger;
>
> ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev);
> if (ret)
> --
> 2.39.5
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox