* [PATCH 2/7] wifi: mt76: mt7925: guard against NULL chandef in BSS RLM TLV
2026-03-04 23:50 [PATCH 1/7] wifi: mt76: mt792x: advertise multicast management frame registration support Sean Wang
@ 2026-03-04 23:50 ` Sean Wang
2026-03-04 23:50 ` [PATCH 3/7] wifi: mt76: mt7925: guard HE 6 GHz capa lookup by HE iftype caps Sean Wang
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sean Wang @ 2026-03-04 23:50 UTC (permalink / raw)
To: nbd, lorenzo.bianconi
Cc: linux-wireless, linux-mediatek, yu-ching.liu, yuchi.wang,
jenhao.yang, posh.sun, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
mt7925_mcu_bss_rlm_tlv() dereferences chandef->chan without verifying
that chandef and chandef->chan are present.
While current callers normally provide a valid chandef, future call
paths or partially configured interfaces may result in missing channel
information and lead to a NULL pointer dereference.
Add a defensive check and bail out early when channel information is
not available. This does not change behaviour for normal operation.
Co-developed-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 1379bf6a26b5..e050c2795cb4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -2291,11 +2291,18 @@ void mt7925_mcu_bss_rlm_tlv(struct sk_buff *skb, struct mt76_phy *phy,
{
struct cfg80211_chan_def *chandef = ctx ? &ctx->def :
&link_conf->chanreq.oper;
- int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2;
- enum nl80211_band band = chandef->chan->band;
struct bss_rlm_tlv *req;
+ enum nl80211_band band;
+ int freq1, freq2;
struct tlv *tlv;
+ if (WARN_ON_ONCE(!chandef || !chandef->chan))
+ return;
+
+ freq1 = chandef->center_freq1;
+ freq2 = chandef->center_freq2;
+ band = chandef->chan->band;
+
tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*req));
req = (struct bss_rlm_tlv *)tlv;
req->control_channel = chandef->chan->hw_value;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 3/7] wifi: mt76: mt7925: guard HE 6 GHz capa lookup by HE iftype caps
2026-03-04 23:50 [PATCH 1/7] wifi: mt76: mt792x: advertise multicast management frame registration support Sean Wang
2026-03-04 23:50 ` [PATCH 2/7] wifi: mt76: mt7925: guard against NULL chandef in BSS RLM TLV Sean Wang
@ 2026-03-04 23:50 ` Sean Wang
2026-03-04 23:50 ` [PATCH 4/7] wifi: mt76: connac: add NAN connection type Sean Wang
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sean Wang @ 2026-03-04 23:50 UTC (permalink / raw)
To: nbd, lorenzo.bianconi
Cc: linux-wireless, linux-mediatek, yu-ching.liu, yuchi.wang,
jenhao.yang, posh.sun, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
ieee80211_get_he_6ghz_capa() WARNs if the iftype has no sband iftype_data
or lacks HE. Probe HE support first with ieee80211_get_he_iftype_cap()
and only then query the 6 GHz capa. And initialize he_6ghz_capa/eht_cap
to avoid uninitialized use.
Co-developed-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index e050c2795cb4..261ed6528808 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -2436,8 +2436,8 @@ mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
enum nl80211_band band,
struct ieee80211_link_sta *link_sta)
{
- struct ieee80211_he_6ghz_capa *he_6ghz_capa;
- const struct ieee80211_sta_eht_cap *eht_cap;
+ struct ieee80211_he_6ghz_capa *he_6ghz_capa = NULL;
+ const struct ieee80211_sta_eht_cap *eht_cap = NULL;
__le16 capa = 0;
u8 mode = 0;
@@ -2445,11 +2445,18 @@ mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
he_6ghz_capa = &link_sta->he_6ghz_capa;
eht_cap = &link_sta->eht_cap;
} else {
+ const struct ieee80211_sta_he_cap *he_cap;
struct ieee80211_supported_band *sband;
sband = phy->hw->wiphy->bands[band];
- capa = ieee80211_get_he_6ghz_capa(sband, vif->type);
- he_6ghz_capa = (struct ieee80211_he_6ghz_capa *)&capa;
+
+ he_cap = (band == NL80211_BAND_6GHZ) ?
+ ieee80211_get_he_iftype_cap(sband, vif->type) : NULL;
+
+ if (he_cap) {
+ capa = ieee80211_get_he_6ghz_capa(sband, vif->type);
+ he_6ghz_capa = (struct ieee80211_he_6ghz_capa *)&capa;
+ }
eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 4/7] wifi: mt76: connac: add NAN connection type
2026-03-04 23:50 [PATCH 1/7] wifi: mt76: mt792x: advertise multicast management frame registration support Sean Wang
2026-03-04 23:50 ` [PATCH 2/7] wifi: mt76: mt7925: guard against NULL chandef in BSS RLM TLV Sean Wang
2026-03-04 23:50 ` [PATCH 3/7] wifi: mt76: mt7925: guard HE 6 GHz capa lookup by HE iftype caps Sean Wang
@ 2026-03-04 23:50 ` Sean Wang
2026-03-04 23:50 ` [PATCH 5/7] wifi: mt76: mt7925: add NAN MCU ABI and basic cmd/event support Sean Wang
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sean Wang @ 2026-03-04 23:50 UTC (permalink / raw)
To: nbd, lorenzo.bianconi
Cc: linux-wireless, linux-mediatek, yu-ching.liu, yuchi.wang,
jenhao.yang, posh.sun, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
Introduce a dedicated NAN connection type for connac-based firmware.
This prepares driver support for NAN interfaces by allowing MCU
device/BSS configuration to represent NAN roles using a distinct
connection type.
No functional behaviour change yet as NAN is not exposed to userspace.
Co-developed-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 11 +++++++++++
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2 ++
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 4 ++++
3 files changed, 17 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 89bd52ea8bf7..09efcab7d783 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -422,6 +422,9 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
basic->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC);
basic->aid = cpu_to_le16(link_sta->sta->aid);
break;
+ case NL80211_IFTYPE_NAN:
+ basic->conn_type = cpu_to_le32(CONNECTION_NAN);
+ break;
default:
WARN_ON(1);
break;
@@ -1212,6 +1215,10 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy,
case NL80211_IFTYPE_ADHOC:
basic_req.basic.conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC);
break;
+ case NL80211_IFTYPE_NAN:
+ basic_req.basic.conn_type = cpu_to_le32(CONNECTION_NAN);
+ basic_req.basic.conn_state = !enable;
+ break;
default:
WARN_ON(1);
break;
@@ -1613,6 +1620,10 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
case NL80211_IFTYPE_ADHOC:
basic_req.basic.conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC);
break;
+ case NL80211_IFTYPE_NAN:
+ basic_req.basic.conn_type = cpu_to_le32(CONNECTION_NAN);
+ basic_req.basic.active = enable;
+ break;
default:
WARN_ON(1);
break;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index fd9cf9c0c32f..11986e164a38 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -876,6 +876,7 @@ enum {
#define NETWORK_P2P BIT(17)
#define NETWORK_IBSS BIT(18)
#define NETWORK_WDS BIT(21)
+#define NETWORK_NAN BIT(22)
#define SCAN_FUNC_RANDOM_MAC BIT(0)
#define SCAN_FUNC_RNR_SCAN BIT(3)
@@ -888,6 +889,7 @@ enum {
#define CONNECTION_IBSS_ADHOC (STA_TYPE_ADHOC | NETWORK_IBSS)
#define CONNECTION_WDS (STA_TYPE_WDS | NETWORK_WDS)
#define CONNECTION_INFRA_BC (STA_TYPE_BC | NETWORK_INFRA)
+#define CONNECTION_NAN (NETWORK_NAN)
#define CONN_STATE_DISCONNECT 0
#define CONN_STATE_CONNECT 1
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 261ed6528808..a41e0d2fb3f9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -2563,6 +2563,10 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb,
basic_req->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC);
basic_req->active = true;
break;
+ case NL80211_IFTYPE_NAN:
+ basic_req->conn_type = cpu_to_le32(CONNECTION_NAN);
+ basic_req->active = enable;
+ break;
default:
WARN_ON(1);
break;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 5/7] wifi: mt76: mt7925: add NAN MCU ABI and basic cmd/event support
2026-03-04 23:50 [PATCH 1/7] wifi: mt76: mt792x: advertise multicast management frame registration support Sean Wang
` (2 preceding siblings ...)
2026-03-04 23:50 ` [PATCH 4/7] wifi: mt76: connac: add NAN connection type Sean Wang
@ 2026-03-04 23:50 ` Sean Wang
2026-03-04 23:50 ` [PATCH 6/7] wifi: mt76: mt7925: add mac80211 NAN start/stop/change_conf ops Sean Wang
2026-03-04 23:50 ` [PATCH 7/7] wifi: mt76: mt792x: build iface combinations dynamically for optional NAN Sean Wang
5 siblings, 0 replies; 7+ messages in thread
From: Sean Wang @ 2026-03-04 23:50 UTC (permalink / raw)
To: nbd, lorenzo.bianconi
Cc: linux-wireless, linux-mediatek, yu-ching.liu, yuchi.wang,
jenhao.yang, posh.sun, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
Add initial Neighbor Awareness Networking (NAN) MCU ABI definitions for
mt7925 and implement basic NAN control commands.
This introduces NAN command TLVs for enable/disable/configure, plus
event parsing for NAN discovery engine indications. The new code also
adds helper logic for configuring DW interval, cluster ID, RSSI
thresholds and social channel scan parameters.
This change only adds the firmware-facing ABI and command/event
plumbing; it does not yet adapt or expose NAN functionality to upper
layers (cfg80211/mac80211) in mt76. Upper-layer integration will follow
in subsequent patches.
Co-developed-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 2 +
.../wireless/mediatek/mt76/mt7925/Makefile | 2 +-
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 6 +-
.../net/wireless/mediatek/mt76/mt7925/nan.c | 386 ++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/nan.h | 200 +++++++++
.../net/wireless/mediatek/mt76/mt7925/regd.c | 30 ++
.../net/wireless/mediatek/mt76/mt7925/regd.h | 3 +
7 files changed, 627 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/nan.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/nan.h
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 11986e164a38..ca29e015d1ab 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1075,6 +1075,7 @@ enum {
MCU_UNI_EVENT_THERMAL = 0x35,
MCU_UNI_EVENT_RSSI_MONITOR = 0x41,
MCU_UNI_EVENT_NIC_CAPAB = 0x43,
+ MCU_UNI_EVENT_NAN = 0x56,
MCU_UNI_EVENT_WED_RRO = 0x57,
MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
@@ -1314,6 +1315,7 @@ enum {
MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
MCU_UNI_CMD_RSSI_MONITOR = 0x41,
MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ MCU_UNI_CMD_NAN = 0x56,
MCU_UNI_CMD_RRO = 0x57,
MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
MCU_UNI_CMD_PER_STA_INFO = 0x6d,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index 8f1078ce3231..f9dcc0bba393 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
-mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o
+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index a41e0d2fb3f9..ecf086aee930 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -7,6 +7,7 @@
#include "regd.h"
#include "mcu.h"
#include "mac.h"
+#include "nan.h"
#define MT_STA_BFER BIT(0)
#define MT_STA_BFEE BIT(1)
@@ -37,7 +38,8 @@ int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) ||
cmd == MCU_UNI_CMD(STA_REC_UPDATE) ||
cmd == MCU_UNI_CMD(OFFLOAD) ||
- cmd == MCU_UNI_CMD(SUSPEND)) {
+ cmd == MCU_UNI_CMD(SUSPEND) ||
+ cmd == MCU_UNI_CMD(NAN)) {
struct mt7925_mcu_uni_event *event;
skb_pull(skb, sizeof(*rxd));
@@ -600,6 +602,8 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev,
dev->fw_assert = true;
mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump);
return;
+ case MCU_UNI_EVENT_NAN:
+ mt7925_nan_mcu_event(dev, skb);
default:
break;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
new file mode 100644
index 000000000000..9483ee94c05e
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2025-2026 MediaTek Inc. */
+
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+
+#include "mt7925.h"
+#include "mcu.h"
+#include "nan.h"
+#include "regd.h"
+
+static void mt7925_nan_set_5g_channel(struct mt792x_dev *dev,
+ struct mt7925_nan_enable_req_tlv *req,
+ struct cfg80211_nan_conf *conf)
+{
+ struct ieee80211_channel *chan;
+ u32 ch5g = 0;
+
+ chan = conf->band_cfgs[NL80211_BAND_5GHZ].chan;
+
+ if (chan)
+ dev_info(dev->mt76.dev, "MP = %u, 5g ch: %u\n",
+ conf->master_pref, chan->hw_value);
+ else
+ dev_info(dev->mt76.dev, "MP = %u, 5g ch is null\n",
+ conf->master_pref);
+
+ if (!chan)
+ return;
+
+ if (!mt7925_regd_is_valid_channel(dev, NL80211_BAND_5GHZ, chan))
+ return;
+
+ req->config_5g_channel = 1;
+
+ if (chan->hw_value == NAN_5G_LOW_DISC_CHANNEL)
+ ch5g |= BIT(0);
+ else if (chan->hw_value == NAN_5G_HIGH_DISC_CHANNEL)
+ ch5g |= BIT(1);
+
+ req->channel_5g_val = cpu_to_le32(ch5g);
+}
+
+static void mt7925_nan_set_cluster_id(struct mt7925_nan_enable_req_tlv *req,
+ const u8 *cluster_id)
+{
+ if (!cluster_id)
+ return;
+
+ req->cluster_high = cpu_to_le16(*(const u16 *)(cluster_id + 4));
+ req->cluster_low = cpu_to_le16((u16)cluster_id[3]);
+}
+
+static void mt7925_nan_set_dw_interval(struct mt7925_nan_enable_req_tlv *req,
+ struct cfg80211_nan_conf *conf)
+{
+ if (conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval > 0) {
+ req->config_dw.config_2dot4g_dw_band = 1;
+ req->config_dw.dw_2dot4g_interval_val =
+ cpu_to_le32(conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval);
+ }
+
+ if (conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval > 0) {
+ req->config_dw.config_5g_dw_band = 1;
+ req->config_dw.dw_5g_interval_val =
+ cpu_to_le32(conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval);
+ }
+}
+
+static void mt7925_nan_set_disc_beacon(struct mt7925_nan_enable_req_tlv *req,
+ struct cfg80211_nan_conf *conf)
+{
+ if (conf->discovery_beacon_interval > 0) {
+ req->config_2dot4g_beacons = true;
+ req->beacon_2dot4g_val = conf->discovery_beacon_interval;
+ }
+}
+
+static void mt7925_nan_set_rssi_thresholds(struct mt7925_nan_enable_req_tlv *req,
+ struct cfg80211_nan_conf *conf)
+{
+ if (conf->band_cfgs[NL80211_BAND_2GHZ].chan) {
+ req->config_2dot4g_rssi_close = 1;
+ req->rssi_close_2dot4g_val =
+ abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close);
+ req->config_2dot4g_rssi_middle = 1;
+ req->rssi_middle_2dot4g_val =
+ abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle);
+ }
+
+ if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) {
+ req->config_5g_rssi_close = 1;
+ req->rssi_close_5g_val =
+ abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close);
+ req->config_5g_rssi_middle = 1;
+ req->rssi_middle_5g_val =
+ abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle);
+ }
+}
+
+static void mt7925_nan_set_scan_params(struct mt7925_nan_enable_req_tlv *req,
+ struct cfg80211_nan_conf *conf)
+{
+ req->scan_params_val.scan_period[0] =
+ conf->scan_period < 255 ? conf->scan_period : 255;
+ req->scan_params_val.dwell_time[0] =
+ conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255;
+}
+
+int mt7925_nan_enable(struct ieee80211_vif *vif,
+ struct mt792x_dev *dev,
+ struct cfg80211_nan_conf *conf)
+{
+ struct mt76_dev *mdev = &dev->mt76;
+ struct {
+ u8 rsv[4];
+ struct mt7925_nan_enable_req_tlv nan_req_tlv;
+ } nan_cmd = {
+ .rsv = { 0 },
+ .nan_req_tlv = {
+ .tag = cpu_to_le16(NAN_UNI_CMD_ENABLE_REQUEST),
+ .len = cpu_to_le16(sizeof(struct mt7925_nan_enable_req_tlv)),
+ .config_random_factor_force = 0,
+ .random_factor_force_val = 0,
+ .config_hop_count_force = 0,
+ .hop_count_force_val = 0,
+ },
+ };
+ struct mt7925_nan_enable_req_tlv *p_nan_req_tlv = &nan_cmd.nan_req_tlv;
+
+ if (!dev || !conf)
+ return -EINVAL;
+
+ p_nan_req_tlv->master_pref = conf->master_pref;
+
+ mt7925_nan_set_5g_channel(dev, p_nan_req_tlv, conf);
+ mt7925_nan_set_cluster_id(p_nan_req_tlv, conf->cluster_id);
+ mt7925_nan_set_dw_interval(p_nan_req_tlv, conf);
+ mt7925_nan_set_disc_beacon(p_nan_req_tlv, conf);
+ mt7925_nan_set_rssi_thresholds(p_nan_req_tlv, conf);
+ mt7925_nan_set_scan_params(p_nan_req_tlv, conf);
+
+ return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true);
+}
+
+int mt7925_nan_disable(struct ieee80211_vif *vif, struct mt792x_dev *dev)
+{
+ struct mt76_dev *mdev = &dev->mt76;
+ struct {
+ u8 rsv[4];
+ struct tlv nan_dis_tlv;
+ } nan_cmd = {
+ .rsv = { 0 },
+ .nan_dis_tlv = {
+ .tag = cpu_to_le16(NAN_UNI_CMD_DISABLE_REQUEST),
+ .len = cpu_to_le16(sizeof(struct tlv)),
+ },
+ };
+
+ if (!dev)
+ return -EINVAL;
+
+ return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true);
+}
+
+static void
+mt7925_nan_mp_tlv(struct sk_buff *skb, u8 master_pref)
+{
+ struct mt7925_nan_master_preference_tlv *mp_tlv = NULL;
+ struct tlv *tlv = NULL;
+
+ if (!skb)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_MASTER_PREFERENCE,
+ sizeof(struct mt7925_nan_master_preference_tlv));
+ if (!tlv)
+ return;
+
+ mp_tlv = (struct mt7925_nan_master_preference_tlv *)tlv;
+
+ if (master_pref > NAN_MAX_MASTER_PREFERENCE)
+ return;
+
+ mp_tlv->master_preference = master_pref;
+}
+
+static void
+mt7925_nan_dw_tlv(struct sk_buff *skb, struct cfg80211_nan_conf *conf)
+{
+ struct mt7925_nan_dw_interval_tlv *dw_tlv = NULL;
+ struct tlv *tlv = NULL;
+ u16 interval;
+
+ if (!skb || !conf)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_DW_INTERVAL,
+ sizeof(struct mt7925_nan_dw_interval_tlv));
+
+ if (!tlv)
+ return;
+
+ dw_tlv = (struct mt7925_nan_dw_interval_tlv *)tlv;
+
+ /* Set DW interval for 2.4GHz and 5GHz bands if available */
+ if (conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval > 0) {
+ dw_tlv->dw_interval = conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval;
+ } else if (conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval > 0) {
+ dw_tlv->dw_interval = conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval;
+ } else {
+ /* Fallback to a default value or log a warning */
+ dw_tlv->dw_interval = NAN_DEFAULT_DW_INTERVAL;
+ }
+
+ /* Validate and set NAN Discovery Beacon Interval */
+ interval = conf->discovery_beacon_interval > 0 ?
+ conf->discovery_beacon_interval :
+ NAN_DEFAULT_DISC_BCN_INTERVAL;
+
+ dw_tlv->disc_bcn_interval = cpu_to_le16(interval);
+}
+
+static void
+mt7925_nan_cluster_id_tlv(struct sk_buff *skb, const u8 *cluster_id)
+{
+ struct mt7925_nan_cluster_id_tlv *cluster_tlv = NULL;
+ struct tlv *tlv = NULL;
+
+ if (!skb || !cluster_id)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_CLUSTER_ID,
+ sizeof(struct mt7925_nan_cluster_id_tlv));
+
+ if (!tlv)
+ return;
+
+ cluster_tlv = (struct mt7925_nan_cluster_id_tlv *)tlv;
+
+ memcpy(cluster_tlv->cluster_id, cluster_id, ETH_ALEN);
+}
+
+static void
+mt7925_nan_sync_rssi_tlv(struct sk_buff *skb, struct cfg80211_nan_conf *conf)
+{
+ struct mt7925_nan_sync_rssi_tlv *rssi_tlv = NULL;
+ struct tlv *tlv = NULL;
+
+ if (!skb || !conf)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_SYNC_RSSI,
+ sizeof(struct mt7925_nan_sync_rssi_tlv));
+
+ if (!tlv)
+ return;
+
+ rssi_tlv = (struct mt7925_nan_sync_rssi_tlv *)tlv;
+
+ if (conf->band_cfgs[NL80211_BAND_2GHZ].chan) {
+ rssi_tlv->rssi_close_2g =
+ conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close;
+ rssi_tlv->rssi_middle_2g =
+ conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle;
+ }
+
+ if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) {
+ rssi_tlv->rssi_close_5g =
+ conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close;
+ rssi_tlv->rssi_middle_5g =
+ conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle;
+ }
+}
+
+int mt7925_nan_change_configure(struct ieee80211_vif *vif,
+ struct mt792x_dev *dev,
+ struct cfg80211_nan_conf *conf)
+{
+ struct mt76_dev *mdev = &dev->mt76;
+ struct mt7925_nan_common_hdr *hdr = NULL;
+ struct sk_buff *skb = NULL;
+
+ if (!dev || !conf)
+ return -EINVAL;
+
+ skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_CONF_MAX_SIZE);
+ if (!skb)
+ return -ENOMEM;
+
+ hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr));
+ memset(hdr, 0, sizeof(*hdr));
+
+ mt7925_nan_mp_tlv(skb, conf->master_pref);
+ mt7925_nan_dw_tlv(skb, conf);
+ mt7925_nan_cluster_id_tlv(skb, conf->cluster_id);
+ mt7925_nan_sync_rssi_tlv(skb, conf);
+
+ return mt76_mcu_skb_send_msg(mdev, skb,
+ MCU_UNI_CMD(NAN), true);
+}
+
+static void
+mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)
+{
+ struct mt7925_nan_de_event *de_evt = NULL;
+ u8 cluster_id[ETH_ALEN] __aligned(2) = {0x50, 0x6f, 0x9a, 0x01, 0x00, 0x00};
+ u16 len;
+
+ if (!dev || !tlv) {
+ if (dev)
+ dev_warn(dev->mt76.dev, "nan: failed to parse TLV\n");
+ return;
+ }
+
+ len = le16_to_cpu(tlv->len);
+ if (len < sizeof(*tlv) + sizeof(*de_evt)) {
+ dev_warn(dev->mt76.dev,
+ "nan: short de_event tlv len=%u\n", len);
+ return;
+ }
+
+ de_evt = (struct mt7925_nan_de_event *)tlv->data;
+ if (!de_evt) {
+ dev_warn(dev->mt76.dev, "nan: missing DE event payload\n");
+ return;
+ }
+
+ if (de_evt->event_type == NAN_EVENT_ID_DISC_MAC_ADDR)
+ return;
+
+ memcpy(cluster_id, de_evt->cluster_id, ETH_ALEN);
+
+ dev_dbg(dev->mt76.dev, "nan: evt=%u cluster=%pM\n",
+ de_evt->event_type, de_evt->cluster_id);
+
+ if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER)
+ return;
+
+ dev_dbg(dev->mt76.dev, "nan: anchor_master_rank=%*phN\n",
+ NAN_ANCHOR_MASTER_RANK_NUM, de_evt->anchor_master_rank);
+
+ dev_dbg(dev->mt76.dev, "nan: own_nmi=%pM master_nmi=%pM\n",
+ de_evt->own_nmi, de_evt->master_nmi);
+}
+
+void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+ struct tlv *tlv;
+ u32 tlv_len;
+
+ if (!dev || !skb)
+ return;
+
+ if (skb->len < sizeof(struct mt7925_mcu_rxd) + 4)
+ return;
+
+ skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
+ tlv = (struct tlv *)skb->data;
+ tlv_len = skb->len;
+
+ while (tlv_len >= sizeof(*tlv)) {
+ u16 len = le16_to_cpu(tlv->len);
+
+ if (len < sizeof(*tlv) || len > tlv_len)
+ break;
+
+ switch (le16_to_cpu(tlv->tag)) {
+ case NAN_UNI_EVENT_ID_DE_EVENT_IND:
+ mt7925_nan_mcu_handle_de_event(dev, tlv);
+ break;
+ default:
+ break;
+ }
+
+ tlv_len -= len;
+ tlv = (struct tlv *)((u8 *)tlv + len);
+ }
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
new file mode 100644
index 000000000000..8c145e4b0b6f
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2025-2026 MediaTek Inc. */
+
+#ifndef __MT7925_NAN_H
+#define __MT7925_NAN_H
+
+#include <linux/if_ether.h>
+#include <linux/types.h>
+
+#include "../mt76_connac_mcu.h"
+
+#define NAN_MAX_SOCIAL_CHANNELS 3
+#define NAN_ANCHOR_MASTER_RANK_NUM 8
+#define NAN_5G_LOW_DISC_CHANNEL 44
+#define NAN_5G_HIGH_DISC_CHANNEL 149
+#define NAN_MAX_MASTER_PREFERENCE 255
+#define NAN_DEFAULT_DW_INTERVAL 1
+#define NAN_DEFAULT_DISC_BCN_INTERVAL 100
+
+#define MT7925_NAN_CONF_MAX_SIZE \
+ (sizeof(struct mt7925_nan_common_hdr) + \
+ sizeof(struct mt7925_nan_master_preference_tlv) + \
+ sizeof(struct mt7925_nan_dw_interval_tlv) + \
+ sizeof(struct mt7925_nan_cluster_id_tlv) + \
+ sizeof(struct mt7925_nan_sync_rssi_tlv))
+
+enum nan_uni_cmd_tag {
+ NAN_UNI_CMD_SET_MASTER_PREFERENCE = 0,
+ NAN_UNI_CMD_ENABLE_REQUEST = 7,
+ NAN_UNI_CMD_DISABLE_REQUEST = 8,
+ NAN_UNI_CMD_SET_DW_INTERVAL = 26,
+ NAN_UNI_CMD_SET_SYNC_RSSI = 39,
+ NAN_UNI_CMD_SET_CLUSTER_ID = 40,
+};
+
+enum nan_uni_event_tag {
+ NAN_UNI_EVENT_ID_DE_EVENT_IND = 19,
+};
+
+enum nan_disc_event_type {
+ NAN_EVENT_ID_DISC_MAC_ADDR = 0,
+ NAN_EVENT_ID_JOINED_CLUSTER = 2,
+};
+
+struct mt7925_nan_social_ch_scan_params {
+ u8 dwell_time[NAN_MAX_SOCIAL_CHANNELS];
+ __le16 scan_period[NAN_MAX_SOCIAL_CHANNELS];
+} __packed;
+
+struct mt7925_nan_conf_dw {
+ u8 config_2dot4g_dw_band;
+ __le32 dw_2dot4g_interval_val;
+
+ u8 config_5g_dw_band;
+ __le32 dw_5g_interval_val;
+} __packed;
+
+struct mt7925_nan_enable_req_tlv {
+ __le16 tag;
+ __le16 len;
+
+ u8 master_pref;
+ __le16 cluster_low;
+ __le16 cluster_high;
+
+ u8 config_support_5g;
+ u8 support_5g_val;
+
+ u8 config_sid_beacon;
+ u8 sid_beacon_val;
+
+ u8 config_2dot4g_rssi_close;
+ u8 rssi_close_2dot4g_val;
+ u8 config_2dot4g_rssi_middle;
+ u8 rssi_middle_2dot4g_val;
+
+ u8 config_2dot4g_rssi_proximity;
+ u8 rssi_proximity_2dot4g_val;
+ u8 config_hop_count_limit;
+ u8 hop_count_limit_val;
+
+ u8 config_2dot4g_support;
+ u8 support_2dot4g_val;
+
+ u8 config_2dot4g_beacons;
+ u8 beacon_2dot4g_val;
+
+ u8 config_2dot4g_sdf;
+ u8 sdf_2dot4g_val;
+
+ u8 config_5g_beacons;
+ u8 beacon_5g_val;
+
+ u8 config_5g_sdf;
+ u8 sdf_5g_val;
+
+ u8 config_5g_rssi_close;
+ u8 rssi_close_5g_val;
+
+ u8 config_5g_rssi_middle;
+ u8 rssi_middle_5g_val;
+
+ u8 config_5g_rssi_close_proximity;
+ u8 rssi_close_proximity_5g_val;
+
+ u8 config_rssi_window_size;
+ u8 rssi_window_size_val;
+
+ u8 config_oui;
+ __le32 oui_val;
+
+ u8 config_intf_addr;
+ u8 intf_addr_val[ETH_ALEN];
+
+ u8 config_cluster_attribute_val;
+
+ u8 config_scan_params;
+ struct mt7925_nan_social_ch_scan_params scan_params_val;
+
+ u8 config_random_factor_force;
+ u8 random_factor_force_val;
+
+ u8 config_hop_count_force;
+ u8 hop_count_force_val;
+
+ u8 config_24g_channel;
+ __le32 channel_24g_val;
+
+ u8 config_5g_channel;
+ __le32 channel_5g_val;
+
+ struct mt7925_nan_conf_dw config_dw;
+
+ u8 config_disc_mac_addr_randomization;
+ __le32 disc_mac_addr_rand_interval_sec;
+
+ u8 discovery_indication_cfg;
+
+ u8 config_subscribe_sid_beacon;
+ __le32 subscribe_sid_beacon_val;
+
+ u8 enable_log_slot_statistics;
+} __packed __aligned(4);
+
+struct mt7925_nan_common_hdr {
+ u8 reserved[4];
+};
+
+struct mt7925_nan_master_preference_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 master_preference;
+ u8 reserved[3];
+} __packed __aligned(4);
+
+struct mt7925_nan_dw_interval_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 dw_interval;
+ u8 vendor_ioctl;
+ __le16 disc_bcn_interval;
+} __packed __aligned(4);
+
+struct mt7925_nan_cluster_id_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 cluster_id[ETH_ALEN];
+ u8 reserved[2];
+} __packed __aligned(4);
+
+struct mt7925_nan_sync_rssi_tlv {
+ __le16 tag;
+ __le16 len;
+ s8 rssi_close_2g;
+ s8 rssi_middle_2g;
+ s8 rssi_close_5g;
+ s8 rssi_middle_5g;
+} __packed __aligned(4);
+
+struct mt7925_nan_de_event {
+ u8 event_type;
+ u8 cluster_id[ETH_ALEN];
+ u8 anchor_master_rank[NAN_ANCHOR_MASTER_RANK_NUM];
+ u8 own_nmi[ETH_ALEN];
+ u8 master_nmi[ETH_ALEN];
+};
+
+int mt7925_nan_enable(struct ieee80211_vif *vif,
+ struct mt792x_dev *dev,
+ struct cfg80211_nan_conf *conf);
+
+int mt7925_nan_disable(struct ieee80211_vif *vif,
+ struct mt792x_dev *dev);
+
+int mt7925_nan_change_configure(struct ieee80211_vif *vif,
+ struct mt792x_dev *dev,
+ struct cfg80211_nan_conf *conf);
+
+void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb);
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
index 16f56ee879d4..0235437d11d5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
@@ -217,6 +217,36 @@ mt7925_regd_is_valid_alpha2(const char *alpha2)
return false;
}
+bool
+mt7925_regd_is_valid_channel(struct mt792x_dev *dev,
+ enum nl80211_band band,
+ struct ieee80211_channel *chan)
+{
+ struct ieee80211_hw *hw = mt76_hw(dev);
+ struct wiphy *wiphy = hw->wiphy;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ int i;
+
+ if (!chan)
+ return false;
+
+ sband = wiphy->bands[band];
+ if (!sband)
+ return false;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ if (ch->hw_value == chan->hw_value &&
+ ((ch->flags & IEEE80211_CHAN_DISABLED) == 0))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(mt7925_regd_is_valid_channel);
+
int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2)
{
struct wiphy *wiphy = phy->mt76->hw->wiphy;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
index 0767f078862e..0b0754cf8ae7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
@@ -13,6 +13,9 @@ void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2);
void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req);
bool mt7925_regd_clc_supported(struct mt792x_dev *dev);
int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2);
+bool mt7925_regd_is_valid_channel(struct mt792x_dev *dev,
+ enum nl80211_band band,
+ struct ieee80211_channel *chan);
int mt7925_regd_init(struct mt792x_phy *phy);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 6/7] wifi: mt76: mt7925: add mac80211 NAN start/stop/change_conf ops
2026-03-04 23:50 [PATCH 1/7] wifi: mt76: mt792x: advertise multicast management frame registration support Sean Wang
` (3 preceding siblings ...)
2026-03-04 23:50 ` [PATCH 5/7] wifi: mt76: mt7925: add NAN MCU ABI and basic cmd/event support Sean Wang
@ 2026-03-04 23:50 ` Sean Wang
2026-03-04 23:50 ` [PATCH 7/7] wifi: mt76: mt792x: build iface combinations dynamically for optional NAN Sean Wang
5 siblings, 0 replies; 7+ messages in thread
From: Sean Wang @ 2026-03-04 23:50 UTC (permalink / raw)
To: nbd, lorenzo.bianconi
Cc: linux-wireless, linux-mediatek, yu-ching.liu, yuchi.wang,
jenhao.yang, posh.sun, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
Wire mac80211 NAN start/stop/change_conf callbacks to mt7925 NAN MCU
enable/disable/config. Track the active NAN vif and notify mac80211 on
cluster join events.
Co-developed-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 69 +++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/nan.c | 7 ++
drivers/net/wireless/mediatek/mt76/mt792x.h | 2 +
3 files changed, 78 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index afcc0fa4aa35..f215984495c7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -11,6 +11,7 @@
#include "regd.h"
#include "mcu.h"
#include "mac.h"
+#include "nan.h"
static void
mt7925_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band,
@@ -442,6 +443,8 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
INIT_WORK(&mvif->csa_work, mt7925_csa_work);
timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0);
+ if (vif->type == NL80211_IFTYPE_NAN)
+ dev->nan_vif = vif;
out:
mt792x_mutex_release(dev);
@@ -2297,6 +2300,69 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw,
}
}
+static int mt7925_start_nan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct ieee80211_bss_conf *link_conf = &vif->bss_conf;
+ int err = 0;
+
+ mt792x_mutex_acquire(dev);
+
+ link_conf->chanreq.oper.chan = conf->band_cfgs[NL80211_BAND_2GHZ].chan;
+
+ err = mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf,
+ NULL, true);
+ if (err < 0)
+ goto out;
+
+ err = mt7925_nan_enable(vif, dev, conf);
+
+out:
+ mt792x_mutex_release(dev);
+
+ return err;
+}
+
+static int mt7925_stop_nan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_bss_conf *link_conf = &vif->bss_conf;
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ int err = 0;
+
+ mt792x_mutex_acquire(dev);
+
+ err = mt7925_nan_disable(vif, dev);
+ if (err < 0)
+ goto out;
+
+ err = mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf,
+ NULL, false);
+out:
+ mt792x_mutex_release(dev);
+
+ return err;
+}
+
+static int mt7925_nan_change_conf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf,
+ u32 changes)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ int err = 0;
+
+ mt792x_mutex_acquire(dev);
+
+ err = mt7925_nan_change_configure(vif, dev, conf);
+
+ mt792x_mutex_release(dev);
+
+ return err;
+}
+
const struct ieee80211_ops mt7925_ops = {
.tx = mt792x_tx,
.start = mt7925_start,
@@ -2366,6 +2432,9 @@ const struct ieee80211_ops mt7925_ops = {
.channel_switch = mt7925_channel_switch,
.abort_channel_switch = mt7925_abort_channel_switch,
.channel_switch_rx_beacon = mt7925_channel_switch_rx_beacon,
+ .start_nan = mt7925_start_nan,
+ .stop_nan = mt7925_stop_nan,
+ .nan_change_conf = mt7925_nan_change_conf,
};
EXPORT_SYMBOL_GPL(mt7925_ops);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
index 9483ee94c05e..bb67374c9764 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
@@ -344,11 +344,18 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)
if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER)
return;
+ if (!ieee80211_vif_nan_started(dev->nan_vif)) {
+ dev_warn(dev->mt76.dev, "nan: joined-cluster event but NAN not started\n");
+ return;
+ }
+
dev_dbg(dev->mt76.dev, "nan: anchor_master_rank=%*phN\n",
NAN_ANCHOR_MASTER_RANK_NUM, de_evt->anchor_master_rank);
dev_dbg(dev->mt76.dev, "nan: own_nmi=%pM master_nmi=%pM\n",
de_evt->own_nmi, de_evt->master_nmi);
+
+ ieee80211_nan_cluster_joined(dev->nan_vif, cluster_id, true, GFP_KERNEL);
}
void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb)
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 1f381ab356bc..d3988e5b38c0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -261,6 +261,8 @@ struct mt792x_dev {
u32 backup_l2;
struct ieee80211_chanctx_conf *new_ctx;
+
+ struct ieee80211_vif *nan_vif;
};
static inline struct mt792x_bss_conf *
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 7/7] wifi: mt76: mt792x: build iface combinations dynamically for optional NAN
2026-03-04 23:50 [PATCH 1/7] wifi: mt76: mt792x: advertise multicast management frame registration support Sean Wang
` (4 preceding siblings ...)
2026-03-04 23:50 ` [PATCH 6/7] wifi: mt76: mt7925: add mac80211 NAN start/stop/change_conf ops Sean Wang
@ 2026-03-04 23:50 ` Sean Wang
5 siblings, 0 replies; 7+ messages in thread
From: Sean Wang @ 2026-03-04 23:50 UTC (permalink / raw)
To: nbd, lorenzo.bianconi
Cc: linux-wireless, linux-mediatek, yu-ching.liu, yuchi.wang,
jenhao.yang, posh.sun, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
Expose NAN capability only when supported by firmware. Split chanctx
interface combinations into base and NAN sets and dynamically append NAN
combinations when available.
Co-developed-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt792x.h | 4 +
.../net/wireless/mediatek/mt76/mt792x_core.c | 113 ++++++++++++++++--
2 files changed, 109 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index d3988e5b38c0..57c9a0437c5e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -23,6 +23,7 @@
#define MT792x_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
#define MT792x_FW_TAG_FEATURE 4
+#define MT792x_FW_CAP_NAN BIT(5)
#define MT792x_FW_CAP_CNM BIT(7)
#define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0)
@@ -263,6 +264,9 @@ struct mt792x_dev {
struct ieee80211_chanctx_conf *new_ctx;
struct ieee80211_vif *nan_vif;
+
+ const struct ieee80211_iface_combination *iface_combinations;
+ int n_iface_combinations;
};
static inline struct mt792x_bss_conf *
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index f12cbfb35f9b..e0850998cb4e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -3,6 +3,7 @@
#include <linux/module.h>
#include <linux/firmware.h>
+#include <linux/slab.h>
#include "mt792x.h"
#include "dma.h"
@@ -60,7 +61,33 @@ static const struct ieee80211_iface_limit if_limits_chanctx_scc[] = {
}
};
-static const struct ieee80211_iface_combination if_comb_chanctx[] = {
+static const struct ieee80211_iface_limit if_limits_nan_mcc[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_NAN),
+ },
+};
+
+static const struct ieee80211_iface_limit if_limits_nan_scc[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_NAN),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP),
+ },
+};
+
+static const struct ieee80211_iface_combination if_comb_chanctx_base[] = {
{
.limits = if_limits_chanctx_mcc,
.n_limits = ARRAY_SIZE(if_limits_chanctx_mcc),
@@ -77,6 +104,60 @@ static const struct ieee80211_iface_combination if_comb_chanctx[] = {
}
};
+static const struct ieee80211_iface_combination if_comb_chanctx_nan[] = {
+ {
+ .limits = if_limits_nan_mcc,
+ .n_limits = ARRAY_SIZE(if_limits_nan_mcc),
+ .max_interfaces = 3,
+ .num_different_channels = 2,
+ .beacon_int_infra_match = false,
+ },
+ {
+ .limits = if_limits_nan_scc,
+ .n_limits = ARRAY_SIZE(if_limits_nan_scc),
+ .max_interfaces = 4,
+ .num_different_channels = 1,
+ .beacon_int_infra_match = false,
+ },
+};
+
+static int mt792x_setup_iface_combinations(struct mt792x_dev *dev,
+ struct wiphy *wiphy)
+{
+ const bool cnm = !!(dev->fw_features & MT792x_FW_CAP_CNM);
+ const bool nan = !!(dev->fw_features & MT792x_FW_CAP_NAN);
+ const int n_base = ARRAY_SIZE(if_comb_chanctx_base);
+ const int n_nan = ARRAY_SIZE(if_comb_chanctx_nan);
+ struct ieee80211_iface_combination *comb;
+
+ if (!cnm) {
+ dev->iface_combinations = if_comb;
+ dev->n_iface_combinations = ARRAY_SIZE(if_comb);
+ return 0;
+ }
+
+ /* CNM enabled, NAN optional */
+ if (!nan) {
+ dev->iface_combinations = if_comb_chanctx_base;
+ dev->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx_base);
+ return 0;
+ }
+
+ /* CNM + NAN: dynamically build base + nan list */
+ comb = devm_kcalloc(&wiphy->dev, n_base + n_nan, sizeof(*comb),
+ GFP_KERNEL);
+ if (!comb)
+ return -ENOMEM;
+
+ memcpy(comb, if_comb_chanctx_base, sizeof(if_comb_chanctx_base));
+ memcpy(comb + n_base, if_comb_chanctx_nan, sizeof(if_comb_chanctx_nan));
+
+ dev->iface_combinations = comb;
+ dev->n_iface_combinations = n_base + n_nan;
+
+ return 0;
+}
+
void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
struct sk_buff *skb)
{
@@ -621,6 +702,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
struct mt792x_phy *phy = mt792x_hw_phy(hw);
struct mt792x_dev *dev = phy->dev;
struct wiphy *wiphy = hw->wiphy;
+ int err;
hw->queues = 4;
if (dev->has_eht) {
@@ -641,15 +723,17 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
hw->vif_data_size = sizeof(struct mt792x_vif);
hw->chanctx_data_size = sizeof(struct mt792x_chanctx);
- if (dev->fw_features & MT792x_FW_CAP_CNM) {
+ if (dev->fw_features & MT792x_FW_CAP_CNM)
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
- wiphy->iface_combinations = if_comb_chanctx;
- wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx);
- } else {
+ else
wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
- wiphy->iface_combinations = if_comb;
- wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
- }
+
+ err = mt792x_setup_iface_combinations(dev, wiphy);
+ if (err)
+ return err;
+
+ wiphy->iface_combinations = dev->iface_combinations;
+ wiphy->n_iface_combinations = dev->n_iface_combinations;
wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP |
WIPHY_FLAG_4ADDR_STATION);
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
@@ -657,6 +741,19 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
+ if ((dev->fw_features & MT792x_FW_CAP_CNM) &&
+ (dev->fw_features & MT792x_FW_CAP_NAN)) {
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN);
+ wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ);
+ wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC |
+ WIPHY_NAN_FLAGS_USERSPACE_DE;
+ wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_MASK;
+ wiphy->nan_capa.n_antennas = 0x22;
+ wiphy->nan_capa.max_channel_switch_time = 12;
+ wiphy->nan_capa.dev_capabilities = NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED |
+ NAN_DEV_CAPA_NDPE_SUPPORTED;
+ }
+
wiphy->max_remain_on_channel_duration = 5000;
wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN;
wiphy->max_scan_ssids = 4;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread