Linux-mediatek Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: JB Tsai <jb.tsai@mediatek.com>
To: <nbd@nbd.name>, <lorenzo@kernel.org>
Cc: <linux-wireless@vger.kernel.org>,
	<linux-mediatek@lists.infradead.org>, <Sean.Wang@mediatek.com>,
	<James-LY.Huang@mediatek.com>, <jb.tsai@mediatek.com>,
	james-ly huang <james-ly.huang@mediatek.com>
Subject: [PATCH 6/9] wifi: mt76: mt7925: request the channel before programming a GO BSS
Date: Wed, 4 Nov 2026 17:23:41 +0800	[thread overview]
Message-ID: <20261104092344.1932406-6-jb.tsai@mediatek.com> (raw)
In-Reply-To: <20261104092344.1932406-1-jb.tsai@mediatek.com>

From: james-ly huang <james-ly.huang@mediatek.com>

Mirror the STA JOIN channel-grant handshake for a P2P GO
coming up. Without an ACQUIRE, mconf->mt76.band_idx stayed
at the 0xff auto value that only a ROC grant fills in, so
the BSS_INFO_BASIC always guessed the DBDC band:

  - .start_ap runs ACQUIRE (GO_START_BSS reqtype, which CNM treats
    as the BSS-owning request) -> add_bss_info with the granted
    band_idx -> ABORT, with per-role hold times (3s P2P GO /
    200ms SAP). Falls back to a JOIN request once on FW that
    predates GO_START_BSS; a refused grant is not fatal;
  - gate .change_chanctx on p2p: the RLM-only BSS_INFO_UPDATE
    that mac80211 issues BEFORE the grant reaches CNM with a
    guessed band - skip it, both roles send a full BASIC+RLM
    update once the grant supplied the real band_idx
    (mt7925_p2p_chanctx_bss_programmed in p2p.c);
  - P2P GC join: hold the channel for 4s instead of the 1s
    mac80211 default, so the whole auth/assoc exchange fits in
    one grant (a GO only listens part of the time);
  - renumber the channel-privilege reqtypes to match the FW CNM
    interface (OFFCHNL_TX / GO_START_BSS / DFS_CAC);
  - add mt792x_phy::roc_is_mlo so the grant handler can tell an MLO
    ROC apart from a plain remain_on_channel request.

Signed-off-by: james-ly huang <james-ly.huang@mediatek.com>
---
 .../net/wireless/mediatek/mt76/mt7925/main.c  | 65 ++++++++++++++++++-
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   | 10 +--
 .../wireless/mediatek/mt76/mt7925/mt7925.h    | 16 ++++-
 .../net/wireless/mediatek/mt76/mt7925/p2p.c   | 35 ++++++++++
 .../net/wireless/mediatek/mt76/mt7925/p2p.h   |  2 +
 drivers/net/wireless/mediatek/mt76/mt792x.h   |  1 +
 6 files changed, 119 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index b82360da9791..cfdb736f53ca 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -2050,11 +2050,51 @@ mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 {
 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+	struct mt792x_bss_conf *mconf = &mvif->bss_conf;
+	bool roc_taken = false;
+	u32 hold_time;
 	int err;
 
 	mt792x_mutex_acquire(dev);
 
-	err = mt7925_mcu_add_bss_info(&dev->phy, mvif->bss_conf.mt76.ctx,
+	/* Mirror the STA JOIN channel-grant handshake for a GO/AP coming up.
+	 * mt7925_mac_link_bss_add() leaves mconf->mt76.band_idx at 0xff (auto)
+	 * and mt7925_mcu_roc_iter() - reached only from a ROC grant event - is
+	 * the ONLY writer of it. A GO had no ROC at all, so the BSS_INFO_BASIC
+	 * below always carried band_idx=0xff: the host never learned which DBDC
+	 * band the FW parked the GO on, TXD1 TGID stayed FW-auto, and the
+	 * host-built IEs could end up describing a different channel than the
+	 * one the radio actually ended up on.
+	 *
+	 * The request type is GO_START_BSS, not JOIN: CNM treats
+	 * GO_START_BSS as the BSS-owning request, so it is allowed to
+	 * create a new channel context rather than only joining an
+	 * existing one.
+	 */
+	if (mconf->mt76.ctx) {
+		hold_time = vif->p2p ? MT7925_P2P_CHNL_HOLD_TIME_MS :
+				       MT7925_SAP_CHNL_HOLD_TIME_MS;
+
+		err = mt7925_set_roc(mvif->phy, mconf, mconf->mt76.ctx->def.chan,
+				     hold_time, MT7925_ROC_REQ_GO_START_BSS);
+
+		/* FW that predates the GO_START_BSS reqtype rejects the ACQUIRE
+		 * outright. JOIN is understood by every branch and produces the
+		 * same band_idx update, so fall back to it once. -EBUSY and
+		 * -ETIMEDOUT mean the command itself WAS accepted (another ROC
+		 * holds the flag / CNM never granted), so do not burn a second
+		 * 4s wait on those.
+		 */
+		if (err < 0 && err != -EBUSY && err != -ETIMEDOUT)
+			err = mt7925_set_roc(mvif->phy, mconf,
+					     mconf->mt76.ctx->def.chan,
+					     hold_time, MT7925_ROC_REQ_JOIN);
+
+		if (!err)
+			roc_taken = true;
+	}
+
+	err = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
 				      link_conf, NULL, true);
 	if (err)
 		goto out;
@@ -2069,6 +2109,14 @@ mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 out:
 	mt792x_mutex_release(dev);
 
+	/* Release the grant now that the BSS is programmed. The GO keeps its
+	 * channel through the BSS_INFO RLM TLV, not through the ROC, and the FW
+	 * CNM arms a band-abort watchdog on GRANTED that asserts if the host
+	 * never sends the abort. Also covers the error paths above.
+	 */
+	if (roc_taken)
+		mt7925_abort_roc(mvif->phy, mconf);
+
 	return err;
 }
 
@@ -2156,8 +2204,10 @@ mt7925_change_chanctx(struct ieee80211_hw *hw,
 			struct ieee80211_bss_conf *link_conf;
 
 			link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id);
-			mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76,
-					     link_conf, ctx);
+
+			if (mt7925_p2p_chanctx_bss_programmed(vif, link_conf))
+				mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76,
+						     link_conf, ctx);
 
 			if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING)
 				mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76,
@@ -2186,6 +2236,15 @@ static void mt7925_mgd_prepare_tx(struct ieee80211_hw *hw,
 	if (info->was_assoc)
 		return;
 
+	/* P2P GC join: hold the channel for
+	 * MT7925_P2P_GC_JOIN_CH_REQ_INTERVAL_MS (4s) instead of the 1s
+	 * mac80211 default, because a GO is only listening on its operating
+	 * channel part of the time and the whole auth/assoc exchange has to
+	 * fit inside one grant.
+	 */
+	if (!info->duration && vif->p2p)
+		duration = MT7925_P2P_GC_JOIN_CH_REQ_INTERVAL_MS;
+
 	mt792x_mutex_acquire(dev);
 	mt7925_set_roc(mvif->phy, &mvif->bss_conf,
 		       mvif->bss_conf.mt76.ctx->def.chan, duration,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 056f7e2f284e..6ec74d7bfd82 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -405,7 +405,9 @@ static void mt7925_mcu_roc_handle_grant(struct mt792x_dev *dev,
 
 	if (grant->reqtype == MT7925_ROC_REQ_ROC)
 		ieee80211_ready_on_channel(hw);
-	else if (grant->reqtype == MT7925_ROC_REQ_JOIN)
+	else if (grant->reqtype == MT7925_ROC_REQ_JOIN ||
+		 grant->reqtype == MT7925_ROC_REQ_GO_START_BSS ||
+		 dev->phy.roc_is_mlo)
 		ieee80211_iterate_active_interfaces_atomic(hw,
 						IEEE80211_IFACE_ITER_RESUME_ALL,
 						mt7925_mcu_roc_iter, grant);
@@ -1598,8 +1600,8 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf
  * The CNM channel-privilege request carries bw / center_chan / center_chan2 /
  * sco, and for a BSS-bound request they must match the BSS's own channel.
  * mt7925 only has a bare channel at this point, but for a BSS-bound
- * request (JOIN) the BSS's own chanctx already describes exactly that
- * channel, so use it.
+ * request (JOIN / GO_START_BSS) the BSS's own chanctx already describes
+ * exactly that channel, so use it.
  *
  * Everything else - an off-channel dwell (ROC / OFFCHNL_TX), or a request for a
  * channel that is not the one this BSS is parked on - keeps the previous 20 MHz
@@ -1611,7 +1613,7 @@ mt7925_mcu_roc_chandef(struct mt792x_bss_conf *mconf,
 		       enum mt7925_roc_req type,
 		       struct cfg80211_chan_def *chandef)
 {
-	if (type == MT7925_ROC_REQ_JOIN &&
+	if ((type == MT7925_ROC_REQ_JOIN || type == MT7925_ROC_REQ_GO_START_BSS) &&
 	    mconf->mt76.ctx && mconf->mt76.ctx->def.chan == chan) {
 		*chandef = mconf->mt76.ctx->def;
 		return;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 36cbeaedb7b2..e3eee41b22c3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -73,15 +73,25 @@ enum mt7925_fw_chan_width {
 	MT7925_CW_320MHZ,
 };
 
+/* Channel-privilege request types. The values are part of the FW CNM
+ * interface (CH_REQ_TYPE_*), so do not renumber.
+ */
 enum mt7925_roc_req {
-	MT7925_ROC_REQ_JOIN,
-	MT7925_ROC_REQ_ROC,
-	MT7925_ROC_REQ_SUB_LINK,
+	MT7925_ROC_REQ_JOIN,			/* CH_REQ_TYPE_JOIN */
+	MT7925_ROC_REQ_ROC,			/* CH_REQ_TYPE_ROC */
+	MT7925_ROC_REQ_OFFCHNL_TX = 2,		/* CH_REQ_TYPE_OFFCHNL_TX */
+	MT7925_ROC_REQ_GO_START_BSS = 3,	/* CH_REQ_TYPE_GO_START_BSS */
+	MT7925_ROC_REQ_DFS_CAC = 4,		/* CH_REQ_TYPE_DFS_CAC */
 	MT7925_ROC_REQ_MLSR_AG = 10,
 	MT7925_ROC_REQ_MLSR_AA,
 	MT7925_ROC_REQ_NUM
 };
 
+/* CNM channel-privilege hold times, per role. */
+#define MT7925_SAP_CHNL_HOLD_TIME_MS		200	/* SAP_CHNL_HOLD_TIME_MS */
+#define MT7925_P2P_CHNL_HOLD_TIME_MS		3000	/* P2P_CHNL_HOLD_TIME_MS */
+#define MT7925_P2P_GC_JOIN_CH_REQ_INTERVAL_MS	4000	/* P2P_GC_JOIN_CH_REQUEST_INTERVAL */
+
 enum {
 	UNI_EVENT_ROC_GRANT = 0,
 	UNI_EVENT_ROC_GRANT_SUB_LINK = 4,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c
index 4e41ba9beca1..f41098b324ba 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c
@@ -427,3 +427,38 @@ void mt7925_p2p_bmc_remove(struct mt792x_dev *dev,
 	mt76_wcid_mask_clear(dev->mt76.wcid_mask, wcid->idx);
 }
 
+/* ------------------------------------------------------------------ */
+/* .change_chanctx gate                                                 */
+/* ------------------------------------------------------------------ */
+
+/* Is this link's BSS already programmed in FW on a granted channel, i.e. is an
+ * RLM-only BSS_INFO_UPDATE from .change_chanctx a genuine *channel update*?
+ *
+ * mac80211 runs ieee80211_link_use_channel() -> ieee80211_assign_link_chanctx()
+ * before .start_ap and before the auth exchange, and that path calls
+ * ieee80211_recalc_chanctx_min_def() -> drv_change_chanctx(). For a P2P link
+ * that is still coming up the resulting RLM TLV therefore reaches the FW
+ * *before* the channel privilege request: CNM creates its channel-info entry
+ * from a band it has to guess, because mconf->mt76.band_idx is still the 0xff
+ * "auto" value that only the ROC grant fills in (mt7925_mcu_roc_iter).
+ *
+ * Nothing is lost by skipping it: once the grant has supplied the real
+ * band_idx both roles send a full BASIC+RLM BSS_INFO_UPDATE anyway, and every
+ * later .change_chanctx (bandwidth change, CSA) passes the test.
+ */
+bool mt7925_p2p_chanctx_bss_programmed(struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *link_conf)
+{
+	if (!vif->p2p || !link_conf)
+		return true;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_AP:
+		return link_conf->enable_beacon;	/* P2P GO */
+	case NL80211_IFTYPE_STATION:
+		return vif->cfg.assoc;			/* P2P GC */
+	default:
+		return true;
+	}
+}
+
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h
index 25b6c57749a2..f7af45ee4c1f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h
@@ -51,5 +51,7 @@ int mt7925_p2p_bmc_add(struct mt792x_dev *dev,
 		       struct mt792x_bss_conf *mconf);
 void mt7925_p2p_bmc_remove(struct mt792x_dev *dev,
 			   struct mt792x_bss_conf *mconf);
+bool mt7925_p2p_chanctx_bss_programmed(struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *link_conf);
 
 #endif /* __MT7925_P2P_H */
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 66c8722220cc..94186e9f77c8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -242,6 +242,7 @@ struct mt792x_phy {
 	wait_queue_head_t roc_wait;
 	u8 roc_token_id;
 	bool roc_grant;
+	bool roc_is_mlo;
 };
 
 struct mt792x_irq_map {
-- 
2.45.2



  parent reply	other threads:[~2026-11-04  9:24 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-04  9:23 [PATCH 1/9] wifi: mt76: mt7925: drop P2P mgmt frames received on foreign channel JB Tsai
2026-11-04  9:23 ` [PATCH 2/9] wifi: mt76: mt7925: bound P2P off-channel mgmt TX lifetime JB Tsai
2026-11-04  9:23 ` [PATCH 3/9] wifi: mt76: mt7925: give every P2P interface a BMC wcid of its own JB Tsai
2026-11-04  9:23 ` [PATCH 4/9] wifi: mt76: mt7925: harden the ROC channel-grant handshake JB Tsai
2026-11-04  9:23 ` [PATCH 5/9] wifi: mt76: mt7925: describe the real chandef in ROC requests JB Tsai
2026-11-04  9:23 ` JB Tsai [this message]
2026-11-04  9:23 ` [PATCH 7/9] wifi: mt76: mt7925: inject the deauth for an aging-timeout SAP event JB Tsai
2026-11-06  0:34   ` kernel test robot
2026-11-04  9:23 ` [PATCH 8/9] wifi: mt76: mt7925: track BSS absence and drop stale GO probe responses JB Tsai
2026-11-04  9:23 ` [PATCH 9/9] wifi: mt76: mt7925: send the common scan IEs and size the request JB Tsai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261104092344.1932406-6-jb.tsai@mediatek.com \
    --to=jb.tsai@mediatek.com \
    --cc=James-LY.Huang@mediatek.com \
    --cc=Sean.Wang@mediatek.com \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=lorenzo@kernel.org \
    --cc=nbd@nbd.name \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox