public inbox for linux-wireless@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] wifi: mt76: connac: fix txpower_cur not being updated
@ 2026-01-25 22:22 Lucid Duck
  2026-01-27  1:59 ` Nick
                   ` (3 more replies)
  0 siblings, 4 replies; 19+ messages in thread
From: Lucid Duck @ 2026-01-25 22:22 UTC (permalink / raw)
  To: linux-wireless; +Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Lucid Duck

The mt76_connac_mcu_set_rate_txpower() function sends TX power settings
to the firmware but never updates phy->txpower_cur. This causes
mt76_get_txpower() to return stale or incorrect values (typically
showing 3 dBm regardless of actual transmit power) when userspace
queries TX power via nl80211.

This affects MT7921 and other connac-based devices. Users observe:
  $ iw dev wlan0 info
  ...
  txpower 3.00 dBm

The firmware receives and applies the correct power level, but the
reported value is wrong because txpower_cur is never set.

Fix by updating phy->txpower_cur after successfully configuring TX
power, matching the behavior of other mt76 drivers like mt7915.

Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
 drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 045771228..7cd357419 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -2251,7 +2251,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
 
 int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
 {
-	int err;
+	int err, tx_power;
 
 	if (phy->cap.has_2ghz) {
 		err = mt76_connac_mcu_rate_txpower_band(phy,
@@ -2272,6 +2272,12 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
 			return err;
 	}
 
+	/* Update txpower_cur for accurate reporting via nl80211 */
+	tx_power = 2 * phy->hw->conf.power_level;
+	if (!tx_power)
+		tx_power = 127;
+	phy->txpower_cur = tx_power;
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_rate_txpower);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH] wifi: mt76: connac: fix txpower_cur not being updated
  2026-01-25 22:22 [PATCH] wifi: mt76: connac: fix txpower_cur not being updated Lucid Duck
@ 2026-01-27  1:59 ` Nick
  2026-01-27 12:21 ` Felix Fietkau
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 19+ messages in thread
From: Nick @ 2026-01-27  1:59 UTC (permalink / raw)
  To: Lucid Duck; +Cc: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee

> The mt76_connac_mcu_set_rate_txpower() function sends TX power settings
> to the firmware but never updates phy->txpower_cur. This causes
> mt76_get_txpower() to return stale or incorrect values (typically
> showing 3 dBm regardless of actual transmit power) when userspace
> queries TX power via nl80211.
>
> This affects MT7921 and other connac-based devices. Users observe:
>   $ iw dev wlan0 info
>   ...
>   txpower 3.00 dBm
>
> The firmware receives and applies the correct power level, but the
> reported value is wrong because txpower_cur is never set.
>
> Fix by updating phy->txpower_cur after successfully configuring TX
> power, matching the behavior of other mt76 drivers like mt7915.
>
> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
> ---
>  drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> index 045771228..7cd357419 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> @@ -2251,7 +2251,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>
>  int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>  {
> -       int err;
> +       int err, tx_power;
>
>         if (phy->cap.has_2ghz) {
>                 err = mt76_connac_mcu_rate_txpower_band(phy,
> @@ -2272,6 +2272,12 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>                         return err;
>         }
>
> +       /* Update txpower_cur for accurate reporting via nl80211 */
> +       tx_power = 2 * phy->hw->conf.power_level;
> +       if (!tx_power)
> +               tx_power = 127;
> +       phy->txpower_cur = tx_power;
> +
>         return 0;
>  }
>  EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_rate_txpower);
> --
> 2.52.0
>

Tested on kernel 6.18.7

 $ iw dev
phy#0
Unnamed/non-netdev interface
wdev 0x2
addr 76:19:f8:16:a4:48
type P2P-device
txpower 67.00 dBm
Interface wlan0
ifindex 3
wdev 0x1
addr 74:19:f8:16:a4:48
type managed
txpower 67.00 dBm
...

Device tested is a USB WiFi adapter using the mt7921u driver. The
txpower output is not correct.

This is a problem that really needs to be fixed. My hope is that those
that know how to help, do so.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] wifi: mt76: connac: fix txpower_cur not being updated
  2026-01-25 22:22 [PATCH] wifi: mt76: connac: fix txpower_cur not being updated Lucid Duck
  2026-01-27  1:59 ` Nick
@ 2026-01-27 12:21 ` Felix Fietkau
  2026-01-27 16:17   ` Bitterblue Smith
  2026-01-30 21:54 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck
  2026-01-30 21:58 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck
  3 siblings, 1 reply; 19+ messages in thread
From: Felix Fietkau @ 2026-01-27 12:21 UTC (permalink / raw)
  To: Lucid Duck, linux-wireless; +Cc: Lorenzo Bianconi, Ryder Lee

On 25.01.26 23:22, Lucid Duck wrote:
> The mt76_connac_mcu_set_rate_txpower() function sends TX power settings
> to the firmware but never updates phy->txpower_cur. This causes
> mt76_get_txpower() to return stale or incorrect values (typically
> showing 3 dBm regardless of actual transmit power) when userspace
> queries TX power via nl80211.
> 
> This affects MT7921 and other connac-based devices. Users observe:
>    $ iw dev wlan0 info
>    ...
>    txpower 3.00 dBm
> 
> The firmware receives and applies the correct power level, but the
> reported value is wrong because txpower_cur is never set.
> 
> Fix by updating phy->txpower_cur after successfully configuring TX
> power, matching the behavior of other mt76 drivers like mt7915.
> 
> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
> ---
>   drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 8 +++++++-
>   1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> index 045771228..7cd357419 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> @@ -2251,7 +2251,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>   
>   int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>   {
> -	int err;
> +	int err, tx_power;
>   
>   	if (phy->cap.has_2ghz) {
>   		err = mt76_connac_mcu_rate_txpower_band(phy,
> @@ -2272,6 +2272,12 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>   			return err;
>   	}
>   
> +	/* Update txpower_cur for accurate reporting via nl80211 */
> +	tx_power = 2 * phy->hw->conf.power_level;
> +	if (!tx_power)
> +		tx_power = 127;
> +	phy->txpower_cur = tx_power;

phy->hw->conf.power_level is the user configured power level, not what 
the hardware is capable of transmitting.

To fix it properly, I think you should determine the maximum rate power 
used in the loop within mt76_connac_mcu_rate_txpower_band (updated with 
each call).

- Felix

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] wifi: mt76: connac: fix txpower_cur not being updated
  2026-01-27 12:21 ` Felix Fietkau
@ 2026-01-27 16:17   ` Bitterblue Smith
  2026-01-27 17:00     ` Felix Fietkau
  0 siblings, 1 reply; 19+ messages in thread
From: Bitterblue Smith @ 2026-01-27 16:17 UTC (permalink / raw)
  To: Felix Fietkau, Lucid Duck, linux-wireless; +Cc: Lorenzo Bianconi, Ryder Lee

On 27/01/2026 14:21, Felix Fietkau wrote:
> On 25.01.26 23:22, Lucid Duck wrote:
>> The mt76_connac_mcu_set_rate_txpower() function sends TX power settings
>> to the firmware but never updates phy->txpower_cur. This causes
>> mt76_get_txpower() to return stale or incorrect values (typically
>> showing 3 dBm regardless of actual transmit power) when userspace
>> queries TX power via nl80211.
>>
>> This affects MT7921 and other connac-based devices. Users observe:
>>    $ iw dev wlan0 info
>>    ...
>>    txpower 3.00 dBm
>>
>> The firmware receives and applies the correct power level, but the
>> reported value is wrong because txpower_cur is never set.
>>
>> Fix by updating phy->txpower_cur after successfully configuring TX
>> power, matching the behavior of other mt76 drivers like mt7915.
>>
>> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
>> ---
>>   drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 8 +++++++-
>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>> index 045771228..7cd357419 100644
>> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>> @@ -2251,7 +2251,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>>     int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>   {
>> -    int err;
>> +    int err, tx_power;
>>         if (phy->cap.has_2ghz) {
>>           err = mt76_connac_mcu_rate_txpower_band(phy,
>> @@ -2272,6 +2272,12 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>               return err;
>>       }
>>   +    /* Update txpower_cur for accurate reporting via nl80211 */
>> +    tx_power = 2 * phy->hw->conf.power_level;
>> +    if (!tx_power)
>> +        tx_power = 127;
>> +    phy->txpower_cur = tx_power;
> 
> phy->hw->conf.power_level is the user configured power level, not what the hardware is capable of transmitting.
> 
> To fix it properly, I think you should determine the maximum rate power used in the loop within mt76_connac_mcu_rate_txpower_band (updated with each call).
> 
> - Felix
> 

What about these older patches?

https://patchwork.kernel.org/project/linux-wireless/list/?series=932665&submitter=&state=*&q=&archive=&delegate=

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] wifi: mt76: connac: fix txpower_cur not being updated
  2026-01-27 16:17   ` Bitterblue Smith
@ 2026-01-27 17:00     ` Felix Fietkau
  0 siblings, 0 replies; 19+ messages in thread
From: Felix Fietkau @ 2026-01-27 17:00 UTC (permalink / raw)
  To: Bitterblue Smith, Lucid Duck, linux-wireless; +Cc: Lorenzo Bianconi, Ryder Lee

On 27.01.26 17:17, Bitterblue Smith wrote:
> On 27/01/2026 14:21, Felix Fietkau wrote:
>> On 25.01.26 23:22, Lucid Duck wrote:
>>> The mt76_connac_mcu_set_rate_txpower() function sends TX power settings
>>> to the firmware but never updates phy->txpower_cur. This causes
>>> mt76_get_txpower() to return stale or incorrect values (typically
>>> showing 3 dBm regardless of actual transmit power) when userspace
>>> queries TX power via nl80211.
>>>
>>> This affects MT7921 and other connac-based devices. Users observe:
>>>    $ iw dev wlan0 info
>>>    ...
>>>    txpower 3.00 dBm
>>>
>>> The firmware receives and applies the correct power level, but the
>>> reported value is wrong because txpower_cur is never set.
>>>
>>> Fix by updating phy->txpower_cur after successfully configuring TX
>>> power, matching the behavior of other mt76 drivers like mt7915.
>>>
>>> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
>>> ---
>>>   drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 8 +++++++-
>>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>>> index 045771228..7cd357419 100644
>>> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>>> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>>> @@ -2251,7 +2251,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>>>     int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>>   {
>>> -    int err;
>>> +    int err, tx_power;
>>>         if (phy->cap.has_2ghz) {
>>>           err = mt76_connac_mcu_rate_txpower_band(phy,
>>> @@ -2272,6 +2272,12 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>>               return err;
>>>       }
>>>   +    /* Update txpower_cur for accurate reporting via nl80211 */
>>> +    tx_power = 2 * phy->hw->conf.power_level;
>>> +    if (!tx_power)
>>> +        tx_power = 127;
>>> +    phy->txpower_cur = tx_power;
>> 
>> phy->hw->conf.power_level is the user configured power level, not what the hardware is capable of transmitting.
>> 
>> To fix it properly, I think you should determine the maximum rate power used in the loop within mt76_connac_mcu_rate_txpower_band (updated with each call).
>> 
>> - Felix
>> 
> 
> What about these older patches?
> 
> https://patchwork.kernel.org/project/linux-wireless/list/?series=932665&submitter=&state=*&q=&archive=&delegate=

If I remember correctly, I found some issues in those patches and asked 
the Author about them and he mentioned that he only tested them on some 
old 5.15 vendor tree and didn't actually use anything recent.
So I dropped them from my tree again.

- Felix

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2] wifi: mt76: mt7921: fix txpower reporting
  2026-01-25 22:22 [PATCH] wifi: mt76: connac: fix txpower_cur not being updated Lucid Duck
  2026-01-27  1:59 ` Nick
  2026-01-27 12:21 ` Felix Fietkau
@ 2026-01-30 21:54 ` Lucid Duck
  2026-02-10  3:02   ` Nick
  2026-03-09 21:50   ` Lucid Duck
  2026-01-30 21:58 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck
  3 siblings, 2 replies; 19+ messages in thread
From: Lucid Duck @ 2026-01-30 21:54 UTC (permalink / raw)
  To: nbd; +Cc: lorenzo, linux-wireless, Lucid Duck

The mt7921 driver never updates phy->txpower_cur, causing
mt76_get_txpower() to report incorrect values via nl80211.
Users see bogus txpower readings (typically 3 dBm or 67 dBm)
regardless of actual regulatory limits.

Fix this by updating txpower_cur in mt7921_bss_info_changed()
when BSS_CHANGED_TXPOWER is set. Use bss_conf.txpower as the
primary source, with a fallback to the channel's max_reg_power
for cases where bss_conf.txpower is not populated (INT_MIN).

Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.18.6:
  - 2.4GHz ch1:  33 dBm (30 dBm limit + 3 dBm path delta) - PASS
  - 5GHz ch100:  27 dBm (24 dBm limit + 3 dBm path delta) - PASS
  - 6GHz ch5:    15 dBm (12 dBm limit + 3 dBm path delta) - PASS

Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
 drivers/net/wireless/mediatek/mt76/mt7921/main.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index f37a35b..99fe46d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -725,6 +725,25 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
 	if (changed & BSS_CHANGED_CQM)
 		mt7921_mcu_set_rssimonitor(dev, vif);

+	if (changed & BSS_CHANGED_TXPOWER) {
+		int tx_power = info->txpower;
+
+		/*
+		 * Workaround for kernel 6.18+: bss_conf.txpower may not be
+		 * populated (INT_MIN) even when BSS_CHANGED_TXPOWER is set.
+		 * In this case, use the channel's max regulatory power.
+		 */
+		if (tx_power == INT_MIN || tx_power <= 0) {
+			struct ieee80211_channel *chan = phy->mt76->chandef.chan;
+			if (chan)
+				tx_power = chan->max_reg_power;
+		}
+
+		/* txpower is in dBm, txpower_cur is in 0.5dBm units */
+		if (tx_power > 0 && tx_power < 127)
+			phy->mt76->txpower_cur = tx_power * 2;
+	}
+
 	if (changed & BSS_CHANGED_ASSOC) {
 		mt7921_mcu_sta_update(dev, NULL, vif, true,
 				      MT76_STA_INFO_STATE_ASSOC);
--
2.43.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2] wifi: mt76: mt7921: fix txpower reporting
  2026-01-25 22:22 [PATCH] wifi: mt76: connac: fix txpower_cur not being updated Lucid Duck
                   ` (2 preceding siblings ...)
  2026-01-30 21:54 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck
@ 2026-01-30 21:58 ` Lucid Duck
  3 siblings, 0 replies; 19+ messages in thread
From: Lucid Duck @ 2026-01-30 21:58 UTC (permalink / raw)
  To: nbd; +Cc: lorenzo, linux-wireless, Lucid Duck

On Tue, 27 Jan 2026, Felix Fietkau wrote:
> phy->hw->conf.power_level is the user configured power level, not what
> the hardware is capable of transmitting.
>
> To fix it properly, I think you should determine the maximum rate power
> used in the loop within mt76_connac_mcu_rate_txpower_band (updated with
> each call).

Thanks for the feedback, Felix.

I looked into tracking the max rate power in mt76_connac_mcu_rate_txpower_band()
as you suggested. However, I found that this function is only called from
mt7921_set_sar_specs() during SAR configuration changes - it's not in the
normal connection code path when a user associates with an AP.

As an alternative, this patch updates txpower_cur in mt7921_bss_info_changed()
when BSS_CHANGED_TXPOWER is set. It uses info->txpower from bss_conf as the
primary source, with a fallback to chan->max_reg_power for cases where
bss_conf.txpower isn't populated (which can happen on newer kernels).

If I've misunderstood your suggestion or there's a better approach, I'd be
happy to revise.

Background:

I originally started debugging this driver because hcxdumptool was
crashing repeatedly on my new Alfa AWUS036AXML adapter. The crashes
appeared related to the bogus txpower values being reported (3 dBm or
67 dBm). After applying this patch, hcxdumptool runs without crashing
and txpower values are reported correctly.

Test Results:

Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.18.6:

  Test            Expected    Actual      Status
  --------------  ----------  ----------  ------
  2.4GHz ch1      ~33 dBm     33.00 dBm   PASS
  5GHz ch100      ~27 dBm     27.00 dBm   PASS
  6GHz ch5        ~15 dBm     15.00 dBm   PASS
  Band switching  Updates     Correct     PASS
  Consistency     Stable      5/5         PASS
  hcxdumptool     No crash    8927 pkts   PASS

Regulatory baseline (Canada):
- 2.4GHz: 30 dBm limit + 3 dBm path delta = 33 dBm reported
- 5GHz DFS: 24 dBm limit + 3 dBm path delta = 27 dBm reported
- 6GHz: 12 dBm limit + 3 dBm path delta = 15 dBm reported

Before patch:
  $ iw dev wlan0 info | grep txpower
          txpower 3.00 dBm    # bogus value

After patch:
  $ iw dev wlan0 info | grep txpower
          txpower 33.00 dBm   # matches regulatory limit

Lucid

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2] wifi: mt76: mt7921: fix txpower reporting
  2026-01-30 21:54 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck
@ 2026-02-10  3:02   ` Nick
  2026-02-12  2:46     ` Lucid Duck
  2026-03-09 21:50   ` Lucid Duck
  1 sibling, 1 reply; 19+ messages in thread
From: Nick @ 2026-02-10  3:02 UTC (permalink / raw)
  To: Lucid Duck; +Cc: nbd, lorenzo, linux-wireless

> The mt7921 driver never updates phy->txpower_cur, causing
> mt76_get_txpower() to report incorrect values via nl80211.
> Users see bogus txpower readings (typically 3 dBm or 67 dBm)
> regardless of actual regulatory limits.
>
> Fix this by updating txpower_cur in mt7921_bss_info_changed()
> when BSS_CHANGED_TXPOWER is set. Use bss_conf.txpower as the
> primary source, with a fallback to the channel's max_reg_power
> for cases where bss_conf.txpower is not populated (INT_MIN).
>
> Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.18.6:
>   - 2.4GHz ch1:  33 dBm (30 dBm limit + 3 dBm path delta) - PASS
>   - 5GHz ch100:  27 dBm (24 dBm limit + 3 dBm path delta) - PASS
>   - 6GHz ch5:    15 dBm (12 dBm limit + 3 dBm path delta) - PASS

I just tested this patch with a fresh download of kernel 6.18.8. All I
see is 3 dBm
when running "iw dev". My USB WiFi adapter also uses the mt7921u driver.

Also, something just doesn't seem right with 33, 27 and 15 dBm. You are in CA,
right?

> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
> ---
>  drivers/net/wireless/mediatek/mt76/mt7921/main.c | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index f37a35b..99fe46d 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -725,6 +725,25 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
>         if (changed & BSS_CHANGED_CQM)
>                 mt7921_mcu_set_rssimonitor(dev, vif);
>
> +       if (changed & BSS_CHANGED_TXPOWER) {
> +               int tx_power = info->txpower;
> +
> +               /*
> +                * Workaround for kernel 6.18+: bss_conf.txpower may not be
> +                * populated (INT_MIN) even when BSS_CHANGED_TXPOWER is set.
> +                * In this case, use the channel's max regulatory power.
> +                */
> +               if (tx_power == INT_MIN || tx_power <= 0) {
> +                       struct ieee80211_channel *chan = phy->mt76->chandef.chan;
> +                       if (chan)
> +                               tx_power = chan->max_reg_power;
> +               }
> +
> +               /* txpower is in dBm, txpower_cur is in 0.5dBm units */
> +               if (tx_power > 0 && tx_power < 127)
> +                       phy->mt76->txpower_cur = tx_power * 2;
> +       }
> +
>         if (changed & BSS_CHANGED_ASSOC) {
>                 mt7921_mcu_sta_update(dev, NULL, vif, true,
>                                       MT76_STA_INFO_STATE_ASSOC);
> --
> 2.43.0
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2] wifi: mt76: mt7921: fix txpower reporting
  2026-02-10  3:02   ` Nick
@ 2026-02-12  2:46     ` Lucid Duck
  0 siblings, 0 replies; 19+ messages in thread
From: Lucid Duck @ 2026-02-12  2:46 UTC (permalink / raw)
  To: morrownr; +Cc: nbd, lorenzo, linux-wireless, Lucid Duck

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1973 bytes --]

On Mon, 9 Feb 2026, Nick wrote:
> I just tested this patch with a fresh download of kernel 6.18.8. All I
> see is 3 dBm when running "iw dev". My USB WiFi adapter also uses the
> mt7921u driver.

Hi Nick,

Thanks for testing.

First thing to check: were you associated to an AP when you ran the
command?

This v2 updates txpower_cur from mt7921_bss_info_changed() on
BSS_CHANGED_TXPOWER, which only fires when there is an active BSS
context. If the interface is not connected, that callback may never
run and txpower_cur can remain at its default (which often shows up
as 3 dBm via iw).

Could you share the output of these while testing?

  iw dev wlan0 link
  iw dev wlan0 info
  iw reg get

If you can reproduce the 3 dBm while clearly associated (the
iw ... link output shows a connected state), then we are looking at
a different path not triggering the update on your setup, and I can
adjust where the driver sets txpower_cur (likely earlier around
channel context setup rather than only in BSS change handling).

> Also, something just doesn't seem right with 33, 27 and 15 dBm. You
> are in CA, right?

I am in Canada (not California). Those numbers are not a measurement
of actual RF output, they are derived from the regulatory information
(the chan->max_reg_power fallback path) when bss_conf.txpower is not
populated. They will vary based on regulatory domain and the
channel/band selected. For reference:

                Canada (ISED)    US (FCC)
  2.4 GHz       30 dBm           30 dBm
  5 GHz DFS     24 dBm           24 dBm
  6 GHz LPI     12 dBm           30 dBm

The 6 GHz band is where the difference is most visible — ISED limits
low-power indoor to 12 dBm while FCC allows 30 dBm. The values you
see will depend on what iw reg get reports for your system.

Thanks again, your results are helpful for tightening up the next
revision.

Lucid Duck

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2] wifi: mt76: mt7921: fix txpower reporting
  2026-01-30 21:54 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck
  2026-02-10  3:02   ` Nick
@ 2026-03-09 21:50   ` Lucid Duck
  2026-03-12  6:38     ` Sean Wang
  1 sibling, 1 reply; 19+ messages in thread
From: Lucid Duck @ 2026-03-09 21:50 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-wireless, Lucid Duck

Hi Felix,

Friendly ping on this v2 from January 30. Life got in the way of
following up sooner -- apologies for the delay.

Since submitting, Nick (morrownr, USB-WiFi maintainer) has tested and
confirmed the fix works on his MT7921U adapter -- 33 dBm on 2.4 GHz
and 24 dBm on 5 GHz, both matching regulatory limits as expected.

I noticed Bryam Vargas recently submitted a competing fix that updates
txpower_cur in mt76_connac_mcu_set_rate_txpower(). That function is
only called from mt7921_set_sar_specs(), so it wouldn't fire during
normal AP association or channel changes. My v2 hooks
bss_info_changed() on BSS_CHANGED_TXPOWER, which covers the common
case.

Happy to rework if you'd prefer a different approach -- just wanted to
make sure this wasn't lost in the shuffle.

Thanks,
Lucid Duck

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2] wifi: mt76: mt7921: fix txpower reporting
  2026-03-09 21:50   ` Lucid Duck
@ 2026-03-12  6:38     ` Sean Wang
  2026-03-17 17:30       ` [PATCH v3 0/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration Lucid Duck
  0 siblings, 1 reply; 19+ messages in thread
From: Sean Wang @ 2026-03-12  6:38 UTC (permalink / raw)
  To: Lucid Duck; +Cc: Felix Fietkau, linux-wireless

Hi Lucid,

On Mon, Mar 9, 2026 at 4:55 PM Lucid Duck <lucid_duck@justthetip.ca> wrote:
>
> Hi Felix,
>
> Friendly ping on this v2 from January 30. Life got in the way of
> following up sooner -- apologies for the delay.
>
> Since submitting, Nick (morrownr, USB-WiFi maintainer) has tested and
> confirmed the fix works on his MT7921U adapter -- 33 dBm on 2.4 GHz
> and 24 dBm on 5 GHz, both matching regulatory limits as expected.
>
> I noticed Bryam Vargas recently submitted a competing fix that updates
> txpower_cur in mt76_connac_mcu_set_rate_txpower(). That function is
> only called from mt7921_set_sar_specs(), so it wouldn't fire during
> normal AP association or channel changes. My v2 hooks
> bss_info_changed() on BSS_CHANGED_TXPOWER, which covers the common
> case.
>

The maximum value tracked in the loop inside
mt76_connac_mcu_rate_txpower_band() is close to the actual maximum power
that users generally expect to see reported.

If the value is not derived from that path, the reported txpower may not
reflect the SAR limits that are actually applied to the hardware.

mt7921_set_sar_specs() is mainly the userspace entry point for SAR
configuration. The actual SAR power update path goes through
mt7921_set_tx_sar_pwr().

If you look closely, mt7921_set_tx_sar_pwr() is invoked in several
situations, including device start and regulatory updates. Therefore it
is still part of the configuration flow that determines the effective
transmit power.
> Happy to rework if you'd prefer a different approach -- just wanted to
> make sure this wasn't lost in the shuffle.
>
> Thanks,
> Lucid Duck
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v3 0/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-12  6:38     ` Sean Wang
@ 2026-03-17 17:30       ` Lucid Duck
  2026-03-17 17:30         ` [PATCH v3 1/1] " Lucid Duck
  0 siblings, 1 reply; 19+ messages in thread
From: Lucid Duck @ 2026-03-17 17:30 UTC (permalink / raw)
  To: linux-wireless
  Cc: nbd, sean.wang, lorenzo, linux-mediatek, morrownr, Lucid Duck

Sean, Felix,

Here's v3 of the txpower fix, reworked based on both of your feedback.

Felix pointed out in v1 review that the fix should determine the maximum
rate power from the loop in mt76_connac_mcu_rate_txpower_band(). Sean
confirmed this direction in v2 review and clarified that the value should
come from the same computation that configures the firmware's per-rate
power tables, not from bss_conf.txpower.

v3 does exactly that. The loop already computes the right value via
mt76_get_rate_power_limits() but discards the return. This patch
captures it and stores it to phy->txpower_cur for the current channel,
matching how mt7915 handles this in mt7915_mcu_set_txpower_sku().
Nick independently confirmed v2 produced correct values on his
MT7921AU -- v3 preserves those results while addressing the
source-of-truth concern.

Two additional issues came up during implementation:

- mt7921's chanctx callbacks don't update phy->chandef (the common
  mt76_add_chanctx does, but mt7921 overrides it with a minimal
  version). Fixed by syncing chandef from ctx->def and re-triggering
  the rate power path in add_chanctx and change_chanctx.

- For chanctx drivers, mac80211 routes iw set txpower through
  BSS_CHANGED_TXPOWER, not IEEE80211_CONF_CHANGE_POWER, and never
  updates hw->conf.power_level. Added a BSS_CHANGED_TXPOWER handler
  to bridge this into the rate power path.

Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.17.1, Canada:

  Band       Auto       User limit (15dBm)   Stock (before)
  2.4GHz     33 dBm     18 dBm               3 dBm
  5GHz       26 dBm     18 dBm               3 dBm
  6GHz       15 dBm      8 dBm (5dBm limit)  3 dBm

  Values match regulatory limits + 3 dBm 2x2 path delta.

Test suite (973 tests, 3.5 hours):

  Regulatory accuracy:     correct on all 3 bands (2.4/5/6 GHz)

  User txpower sweep:      every 1 dBm from 1 to reg_max, all 3 bands

  Band switching:          100 cycles between 2.4 and 5 GHz, 0 failures

  Module reload:           50 rmmod/insmod/connect/verify cycles, 0 failures

  2-hour soak:             480 samples at 15s intervals, zero drift

  Band rotation soak:      30 rotations across all 3 bands over 1 hour,
                           240 samples, 0 failures

  Regdomain switching:     10 countries (CA/US/JP/DE/GB/AU/NZ/BR/KR/TW)
                           mid-session without reconnecting, all correct
                           (JP correctly dropped to 23 dBm on 5GHz UNII-1)

  Monitor mode:            correct on ch1/6/11 (2.4GHz) and ch36/44/149
                           (5GHz), txpower set works in monitor mode

  Edge cases:              min power (1dBm), over-max clamping, rapid
                           power changes (10 in 10s), all correct

  Firmware cross-check:    debugfs txpower_sku confirms per-rate tables
                           match regulatory limits on all bands

Lucid Duck (1):
  wifi: mt76: mt7921: fix txpower reporting from rate power
    configuration

 .../wireless/mediatek/mt76/mt76_connac_mcu.c  | 12 +++++++---
 .../net/wireless/mediatek/mt76/mt7921/main.c  | 22 ++++++++++++++++++-
 2 files changed, 30 insertions(+), 4 deletions(-)

--
2.51.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v3 1/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-17 17:30       ` [PATCH v3 0/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration Lucid Duck
@ 2026-03-17 17:30         ` Lucid Duck
  2026-03-17 21:51           ` Lucid Duck
  2026-03-19  6:00           ` Sean Wang
  0 siblings, 2 replies; 19+ messages in thread
From: Lucid Duck @ 2026-03-17 17:30 UTC (permalink / raw)
  To: linux-wireless
  Cc: nbd, sean.wang, lorenzo, linux-mediatek, morrownr, Lucid Duck

The mt7921 driver never updates phy->txpower_cur
when TX power rate configuration is sent to firmware. This causes
mt76_get_txpower() to report bogus values to userspace (typically
3 dBm) regardless of actual regulatory or SAR limits. User-set
txpower limits via iw are also not reflected.

Three root causes are addressed:

1. The rate power loop in mt76_connac_mcu_rate_txpower_band() computes
   the correct bounded TX power for each channel but discards the return
   value of mt76_get_rate_power_limits(). Fix: capture the return value
   and store it to phy->txpower_cur when processing the current channel.

2. mt7921 uses the chanctx model but its add_chanctx callback bypasses
   the common mt76_phy_update_channel(), leaving phy->chandef stale.
   Fix: update phy->chandef from ctx->def in both add_chanctx and
   change_chanctx, and trigger the rate power path to refresh
   txpower_cur. Also trigger on IEEE80211_CONF_CHANGE_CHANNEL in
   config(), matching mt7915.

3. For chanctx drivers, mac80211 routes user txpower changes through
   BSS_CHANGED_TXPOWER in bss_info_changed() -- not through
   IEEE80211_CONF_CHANGE_POWER in config(). hw->conf.power_level is
   never updated. Fix: handle BSS_CHANGED_TXPOWER in
   mt7921_bss_info_changed(), bridge bss_conf.txpower to
   hw->conf.power_level, and re-trigger the rate power path.

Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.17.1-300.fc43:

  Before: iw dev wlan0 info shows "txpower 3.00 dBm" (wrong)
  After:  correct per-band values, user limits reflected

Test results (regulatory domain: Canada/CA):
  - 2.4GHz ch6:  33 dBm (30 dBm limit + 3 dBm 2x2 path delta)
  - 5GHz ch36:   26 dBm (23 dBm limit + 3 dBm path delta)
  - 6GHz ch5:    15 dBm (12 dBm limit + 3 dBm path delta)
  - Band switch: 100 cycles, 0 failures
  - Module reload: 50 cycles, 0 failures
  - 2-hour soak: 480 samples, zero drift
  - Regdomain switching: 10 countries, all correct
  - User txpower limits: reflected on all bands
  - Monitor mode: correct on all tested channels

Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
 .../wireless/mediatek/mt76/mt76_connac_mcu.c  | 12 +++++++---
 .../net/wireless/mediatek/mt76/mt7921/main.c  | 22 ++++++++++++++++++-
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 16db0f208..5856924a9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -2193,14 +2193,20 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
 				.hw_value = ch_list[idx],
 				.band = band,
 			};
-			s8 reg_power, sar_power;
+			s8 reg_power, sar_power, max_power;
 
 			reg_power = mt76_connac_get_ch_power(phy, &chan,
 							     tx_power);
 			sar_power = mt76_get_sar_power(phy, &chan, reg_power);
 
-			mt76_get_rate_power_limits(phy, &chan, limits,
-						   sar_power);
+			max_power = mt76_get_rate_power_limits(phy, &chan,
+							       limits,
+							       sar_power);
+
+			if (phy->chandef.chan &&
+			    phy->chandef.chan->hw_value == ch_list[idx] &&
+			    phy->chandef.chan->band == band)
+				phy->txpower_cur = max_power;
 
 			tx_power_tlv.last_msg = ch_list[idx] == last_ch;
 			sku_tlbv.channel = ch_list[idx];
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 5881040ac..38a59c6f2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -638,7 +638,8 @@ static int mt7921_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
 
 	mt792x_mutex_acquire(dev);
 
-	if (changed & IEEE80211_CONF_CHANGE_POWER) {
+	if (changed & (IEEE80211_CONF_CHANGE_POWER |
+		       IEEE80211_CONF_CHANGE_CHANNEL)) {
 		ret = mt7921_set_tx_sar_pwr(hw, NULL);
 		if (ret)
 			goto out;
@@ -719,6 +720,14 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
 	if (changed & BSS_CHANGED_CQM)
 		mt7921_mcu_set_rssimonitor(dev, vif);
 
+	if (changed & BSS_CHANGED_TXPOWER) {
+		int tx_power = info->txpower;
+
+		if (tx_power != INT_MIN && tx_power > 0)
+			hw->conf.power_level = tx_power;
+		mt7921_set_tx_sar_pwr(hw, NULL);
+	}
+
 	if (changed & BSS_CHANGED_ASSOC) {
 		mt7921_mcu_sta_update(dev, NULL, vif, true,
 				      MT76_STA_INFO_STATE_ASSOC);
@@ -1360,8 +1369,15 @@ mt7921_add_chanctx(struct ieee80211_hw *hw,
 		   struct ieee80211_chanctx_conf *ctx)
 {
 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+	struct mt76_phy *mphy = hw->priv;
 
 	dev->new_ctx = ctx;
+	mphy->chandef = ctx->def;
+
+	mt792x_mutex_acquire(dev);
+	mt7921_set_tx_sar_pwr(hw, NULL);
+	mt792x_mutex_release(dev);
+
 	return 0;
 }
 
@@ -1396,6 +1412,10 @@ mt7921_change_chanctx(struct ieee80211_hw *hw,
 		mt7921_mcu_config_sniffer(mvif, ctx);
 	else
 		mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx);
+
+	phy->mt76->chandef = ctx->def;
+	mt7921_set_tx_sar_pwr(hw, NULL);
+
 	mt792x_mutex_release(phy->dev);
 }
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 1/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-17 17:30         ` [PATCH v3 1/1] " Lucid Duck
@ 2026-03-17 21:51           ` Lucid Duck
  2026-03-19  6:00           ` Sean Wang
  1 sibling, 0 replies; 19+ messages in thread
From: Lucid Duck @ 2026-03-17 21:51 UTC (permalink / raw)
  To: linux-wireless; +Cc: nbd, sean.wang, lorenzo, linux-mediatek, stable

I missed adding the stable tag. This bug has been present since the
mt7921 driver was introduced, so it should be backported to maintained
stable kernels:

Cc: stable@vger.kernel.org
Fixes: 1c099ab44727 ("mt76: mt7921: add MAC support")

Sorry for the oversight.

Lucid Duck

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 1/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-17 17:30         ` [PATCH v3 1/1] " Lucid Duck
  2026-03-17 21:51           ` Lucid Duck
@ 2026-03-19  6:00           ` Sean Wang
  2026-03-19 20:38             ` [PATCH v4] " Lucid Duck
  1 sibling, 1 reply; 19+ messages in thread
From: Sean Wang @ 2026-03-19  6:00 UTC (permalink / raw)
  To: Lucid Duck; +Cc: linux-wireless, nbd, lorenzo, linux-mediatek, morrownr

Hi,

On Tue, Mar 17, 2026 at 12:30 PM Lucid Duck <lucid_duck@justthetip.ca> wrote:
>
> The mt7921 driver never updates phy->txpower_cur
> when TX power rate configuration is sent to firmware. This causes
> mt76_get_txpower() to report bogus values to userspace (typically
> 3 dBm) regardless of actual regulatory or SAR limits. User-set
> txpower limits via iw are also not reflected.
>
> Three root causes are addressed:
>
> 1. The rate power loop in mt76_connac_mcu_rate_txpower_band() computes
>    the correct bounded TX power for each channel but discards the return
>    value of mt76_get_rate_power_limits(). Fix: capture the return value
>    and store it to phy->txpower_cur when processing the current channel.
>
> 2. mt7921 uses the chanctx model but its add_chanctx callback bypasses
>    the common mt76_phy_update_channel(), leaving phy->chandef stale.
>    Fix: update phy->chandef from ctx->def in both add_chanctx and
>    change_chanctx, and trigger the rate power path to refresh
>    txpower_cur. Also trigger on IEEE80211_CONF_CHANGE_CHANNEL in
>    config(), matching mt7915.
>
> 3. For chanctx drivers, mac80211 routes user txpower changes through
>    BSS_CHANGED_TXPOWER in bss_info_changed() -- not through
>    IEEE80211_CONF_CHANGE_POWER in config(). hw->conf.power_level is
>    never updated. Fix: handle BSS_CHANGED_TXPOWER in
>    mt7921_bss_info_changed(), bridge bss_conf.txpower to
>    hw->conf.power_level, and re-trigger the rate power path.
>
> Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.17.1-300.fc43:
>
>   Before: iw dev wlan0 info shows "txpower 3.00 dBm" (wrong)
>   After:  correct per-band values, user limits reflected
>
> Test results (regulatory domain: Canada/CA):
>   - 2.4GHz ch6:  33 dBm (30 dBm limit + 3 dBm 2x2 path delta)
>   - 5GHz ch36:   26 dBm (23 dBm limit + 3 dBm path delta)
>   - 6GHz ch5:    15 dBm (12 dBm limit + 3 dBm path delta)
>   - Band switch: 100 cycles, 0 failures
>   - Module reload: 50 cycles, 0 failures
>   - 2-hour soak: 480 samples, zero drift
>   - Regdomain switching: 10 countries, all correct
>   - User txpower limits: reflected on all bands
>   - Monitor mode: correct on all tested channels
>
> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
> ---
>  .../wireless/mediatek/mt76/mt76_connac_mcu.c  | 12 +++++++---
>  .../net/wireless/mediatek/mt76/mt7921/main.c  | 22 ++++++++++++++++++-
>  2 files changed, 30 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> index 16db0f208..5856924a9 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> @@ -2193,14 +2193,20 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>                                 .hw_value = ch_list[idx],
>                                 .band = band,
>                         };
> -                       s8 reg_power, sar_power;
> +                       s8 reg_power, sar_power, max_power;
>
>                         reg_power = mt76_connac_get_ch_power(phy, &chan,
>                                                              tx_power);
>                         sar_power = mt76_get_sar_power(phy, &chan, reg_power);
>
> -                       mt76_get_rate_power_limits(phy, &chan, limits,
> -                                                  sar_power);
> +                       max_power = mt76_get_rate_power_limits(phy, &chan,
> +                                                              limits,
> +                                                              sar_power);
> +
> +                       if (phy->chandef.chan &&
> +                           phy->chandef.chan->hw_value == ch_list[idx] &&
> +                           phy->chandef.chan->band == band)
> +                               phy->txpower_cur = max_power;
>
>                         tx_power_tlv.last_msg = ch_list[idx] == last_ch;
>                         sku_tlbv.channel = ch_list[idx];
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index 5881040ac..38a59c6f2 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -638,7 +638,8 @@ static int mt7921_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
>
>         mt792x_mutex_acquire(dev);
>
> -       if (changed & IEEE80211_CONF_CHANGE_POWER) {
> +       if (changed & (IEEE80211_CONF_CHANGE_POWER |
> +                      IEEE80211_CONF_CHANGE_CHANNEL)) {
>                 ret = mt7921_set_tx_sar_pwr(hw, NULL);
>                 if (ret)
>                         goto out;
> @@ -719,6 +720,14 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
>         if (changed & BSS_CHANGED_CQM)
>                 mt7921_mcu_set_rssimonitor(dev, vif);
>
> +       if (changed & BSS_CHANGED_TXPOWER) {
> +               int tx_power = info->txpower;
> +
> +               if (tx_power != INT_MIN && tx_power > 0)
> +                       hw->conf.power_level = tx_power;
> +               mt7921_set_tx_sar_pwr(hw, NULL);
> +       }
> +
>         if (changed & BSS_CHANGED_ASSOC) {
>                 mt7921_mcu_sta_update(dev, NULL, vif, true,
>                                       MT76_STA_INFO_STATE_ASSOC);
> @@ -1360,8 +1369,15 @@ mt7921_add_chanctx(struct ieee80211_hw *hw,
>                    struct ieee80211_chanctx_conf *ctx)
>  {
>         struct mt792x_dev *dev = mt792x_hw_dev(hw);
> +       struct mt76_phy *mphy = hw->priv;
>
>         dev->new_ctx = ctx;
> +       mphy->chandef = ctx->def;
> +
> +       mt792x_mutex_acquire(dev);
> +       mt7921_set_tx_sar_pwr(hw, NULL);
> +       mt792x_mutex_release(dev);
> +
>         return 0;
>  }
>
> @@ -1396,6 +1412,10 @@ mt7921_change_chanctx(struct ieee80211_hw *hw,
>                 mt7921_mcu_config_sniffer(mvif, ctx);
>         else
>                 mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx);
> +
> +       phy->mt76->chandef = ctx->def;
> +       mt7921_set_tx_sar_pwr(hw, NULL);
> +

I do not think the additional mt7921_set_tx_sar_pwr() calls are justified.

mt7921_set_tx_sar_pwr() is not a lightweight per-channel refresh. It
rebuilds and pushes the rate txpower table for all channels in the band
through the MCU path. This is appropriate when the underlying power
constraints change (e.g. SAR, regulatory limits, or user-configured
txpower), but not for pure channel/chanctx transitions.

add_chanctx(), change_chanctx(), and IEEE80211_CONF_CHANGE_CHANNEL only
reflect channel/context changes and do not imply that the firmware
txpower table needs to be recomputed. Using mt7921_set_tx_sar_pwr() here
effectively turns it into a catch-all sync path.

If the issue is stale txpower reporting after a channel switch, it should
be fixed by updating phy->txpower_cur from the already computed bounded
max power for the current channel, rather than re-triggering full table
programming on every chanctx/channel event.

BSS_CHANGED_TXPOWER is also problematic. It is a per-BSS (per-vif) event,
while hw->conf.power_level is shared per-HW state. Writing
info->txpower into hw->conf.power_level allows one interface to affect
the effective txpower of others sharing the same PHY, which breaks
multi-vif semantics.

>         mt792x_mutex_release(phy->dev);
>  }
>
> --
> 2.51.0
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-19  6:00           ` Sean Wang
@ 2026-03-19 20:38             ` Lucid Duck
  2026-03-20  1:17               ` Lucid Duck
  2026-03-21  8:58               ` Sean Wang
  0 siblings, 2 replies; 19+ messages in thread
From: Lucid Duck @ 2026-03-19 20:38 UTC (permalink / raw)
  To: Sean Wang
  Cc: Felix Fietkau, Lorenzo Bianconi, linux-wireless, linux-mediatek,
	morrownr, Lucid Duck, stable

The mt7921 driver never updates phy->txpower_cur from the rate power
configuration sent to firmware, causing mt76_get_txpower() to report
bogus values to userspace (typically 3 dBm) regardless of actual
regulatory or SAR limits.

Two issues are addressed:

1. The rate power loop in mt76_connac_mcu_rate_txpower_band() computes
   the correct bounded TX power per channel via
   mt76_get_rate_power_limits() but discards the return value. Capture
   it and store to phy->txpower_cur when processing the current
   channel, matching how mt7915 handles this in
   mt7915_mcu_set_txpower_sku(). Subtract the multi-chain path delta
   before storing, since mt76_get_txpower() adds it back when
   reporting -- consistent with mt7915's use of mt76_get_power_bound()
   which performs the same subtraction.

2. mt7921 uses the chanctx model but its add_chanctx callback does not
   update phy->chandef, leaving it stale after association. The rate
   power loop's channel comparison then fails silently. Sync
   phy->chandef from ctx->def in add_chanctx and change_chanctx, and
   recompute txpower_cur via a lightweight helper that performs the
   same bounded power calculation for the current channel without
   reprogramming firmware rate tables.

Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.19.8, Canada:

  Before: iw dev wlan0 info shows "txpower 3.00 dBm" (wrong)
  After:  2.4GHz 36 dBm, 5GHz 23 dBm, 6GHz 12 dBm (match regulatory)

Cc: stable@vger.kernel.org
Fixes: 1c099ab44727 ("mt76: mt7921: add MAC support")
Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
Changes since v3:
- Removed mt7921_set_tx_sar_pwr() from add_chanctx and change_chanctx.
  Channel transitions don't change underlying power constraints, so
  reprogramming the full rate table is unnecessary. Replaced with a
  lightweight helper that recomputes txpower_cur locally.
- Removed IEEE80211_CONF_CHANGE_CHANNEL trigger from config().
- Removed BSS_CHANGED_TXPOWER handler from bss_info_changed(). Writing
  per-vif txpower into per-HW hw->conf.power_level breaks multi-vif
  semantics. User txpower limits need a different approach (follow-up).
- Subtracted path delta before storing txpower_cur. The connac rate
  loop stores total bounded power, but mt76_get_txpower() adds the
  multi-chain path delta when reporting. mt7915 accounts for this via
  mt76_get_power_bound(), which subtracts the delta before storing.
  Without the same subtraction, reported values were inflated by 3 dBm
  on 2x2 devices.

 .../wireless/mediatek/mt76/mt76_connac_mcu.c  | 14 +++++++--
 .../net/wireless/mediatek/mt76/mt7921/main.c  | 30 +++++++++++++++++++
 2 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 16db0f208..e26a2cb39 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -2193,14 +2193,22 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
 				.hw_value = ch_list[idx],
 				.band = band,
 			};
-			s8 reg_power, sar_power;
+			s8 reg_power, sar_power, max_power;
 
 			reg_power = mt76_connac_get_ch_power(phy, &chan,
 							     tx_power);
 			sar_power = mt76_get_sar_power(phy, &chan, reg_power);
 
-			mt76_get_rate_power_limits(phy, &chan, limits,
-						   sar_power);
+			max_power = mt76_get_rate_power_limits(phy, &chan,
+							       limits,
+							       sar_power);
+
+			if (phy->chandef.chan &&
+			    phy->chandef.chan->hw_value == ch_list[idx] &&
+			    phy->chandef.chan->band == band)
+				phy->txpower_cur = max_power -
+					mt76_tx_power_path_delta(
+						hweight16(phy->chainmask));
 
 			tx_power_tlv.last_msg = ch_list[idx] == last_ch;
 			sku_tlbv.channel = ch_list[idx];
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 5881040ac..a77ae5791 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -1355,13 +1355,39 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	mt792x_mutex_release(dev);
 }
 
+static void mt7921_update_txpower_cur(struct mt76_phy *phy)
+{
+	struct mt76_power_limits limits;
+	struct ieee80211_channel *chan = phy->chandef.chan;
+	int n_chains = hweight16(phy->chainmask);
+	s8 reg_power, sar_power, max_power;
+	int tx_power;
+
+	if (!chan)
+		return;
+
+	tx_power = 2 * phy->hw->conf.power_level;
+	if (!tx_power)
+		tx_power = 127;
+
+	reg_power = mt76_connac_get_ch_power(phy, chan, tx_power);
+	sar_power = mt76_get_sar_power(phy, chan, reg_power);
+	max_power = mt76_get_rate_power_limits(phy, chan, &limits, sar_power);
+
+	phy->txpower_cur = max_power - mt76_tx_power_path_delta(n_chains);
+}
+
 static int
 mt7921_add_chanctx(struct ieee80211_hw *hw,
 		   struct ieee80211_chanctx_conf *ctx)
 {
 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+	struct mt76_phy *mphy = hw->priv;
 
 	dev->new_ctx = ctx;
+	mphy->chandef = ctx->def;
+	mt7921_update_txpower_cur(mphy);
+
 	return 0;
 }
 
@@ -1396,6 +1422,10 @@ mt7921_change_chanctx(struct ieee80211_hw *hw,
 		mt7921_mcu_config_sniffer(mvif, ctx);
 	else
 		mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx);
+
+	phy->mt76->chandef = ctx->def;
+	mt7921_update_txpower_cur(phy->mt76);
+
 	mt792x_mutex_release(phy->dev);
 }
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-19 20:38             ` [PATCH v4] " Lucid Duck
@ 2026-03-20  1:17               ` Lucid Duck
  2026-03-20  6:34                 ` Greg KH
  2026-03-21  8:58               ` Sean Wang
  1 sibling, 1 reply; 19+ messages in thread
From: Lucid Duck @ 2026-03-20  1:17 UTC (permalink / raw)
  To: sean.wang; +Cc: nbd, lorenzo, linux-wireless, linux-mediatek, morrownr, stable

Note for stable backport: this patch uses mt76_tx_power_path_delta()
which was introduced in commit 56e38675c5bd ("mt76: support power delta
calculation for 5 TX paths"). That commit renamed the older
mt76_tx_power_nss_delta() and is needed as a prerequisite for the
backport to compile.

For stable kernels that don't have 56e3867, a trivial substitution of
mt76_tx_power_nss_delta for mt76_tx_power_path_delta produces identical
results for 1-4 chain devices (the lookup tables match exactly).

Reported-by: sam8641 (compile failure on 6.12.74)

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-20  1:17               ` Lucid Duck
@ 2026-03-20  6:34                 ` Greg KH
  0 siblings, 0 replies; 19+ messages in thread
From: Greg KH @ 2026-03-20  6:34 UTC (permalink / raw)
  To: Lucid Duck
  Cc: sean.wang, nbd, lorenzo, linux-wireless, linux-mediatek, morrownr,
	stable

On Thu, Mar 19, 2026 at 06:17:27PM -0700, Lucid Duck wrote:
> Note for stable backport: this patch uses mt76_tx_power_path_delta()
> which was introduced in commit 56e38675c5bd ("mt76: support power delta
> calculation for 5 TX paths"). That commit renamed the older
> mt76_tx_power_nss_delta() and is needed as a prerequisite for the
> backport to compile.
> 
> For stable kernels that don't have 56e3867, a trivial substitution of
> mt76_tx_power_nss_delta for mt76_tx_power_path_delta produces identical
> results for 1-4 chain devices (the lookup tables match exactly).
> 
> Reported-by: sam8641 (compile failure on 6.12.74)

This "note for stable backport" will get lost, sorry.  Can you provide
working backports to stable@vger.kernel.org when this lands in Linus's
tree?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
  2026-03-19 20:38             ` [PATCH v4] " Lucid Duck
  2026-03-20  1:17               ` Lucid Duck
@ 2026-03-21  8:58               ` Sean Wang
  1 sibling, 0 replies; 19+ messages in thread
From: Sean Wang @ 2026-03-21  8:58 UTC (permalink / raw)
  To: Lucid Duck
  Cc: Felix Fietkau, Lorenzo Bianconi, linux-wireless, linux-mediatek,
	morrownr, stable

Hi,

On Thu, Mar 19, 2026 at 3:39 PM Lucid Duck <lucid_duck@justthetip.ca> wrote:
>
> The mt7921 driver never updates phy->txpower_cur from the rate power
> configuration sent to firmware, causing mt76_get_txpower() to report
> bogus values to userspace (typically 3 dBm) regardless of actual
> regulatory or SAR limits.
>
> Two issues are addressed:
>
> 1. The rate power loop in mt76_connac_mcu_rate_txpower_band() computes
>    the correct bounded TX power per channel via
>    mt76_get_rate_power_limits() but discards the return value. Capture
>    it and store to phy->txpower_cur when processing the current
>    channel, matching how mt7915 handles this in
>    mt7915_mcu_set_txpower_sku(). Subtract the multi-chain path delta
>    before storing, since mt76_get_txpower() adds it back when
>    reporting -- consistent with mt7915's use of mt76_get_power_bound()
>    which performs the same subtraction.
>
> 2. mt7921 uses the chanctx model but its add_chanctx callback does not
>    update phy->chandef, leaving it stale after association. The rate
>    power loop's channel comparison then fails silently. Sync
>    phy->chandef from ctx->def in add_chanctx and change_chanctx, and
>    recompute txpower_cur via a lightweight helper that performs the
>    same bounded power calculation for the current channel without
>    reprogramming firmware rate tables.
>
> Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.19.8, Canada:
>
>   Before: iw dev wlan0 info shows "txpower 3.00 dBm" (wrong)
>   After:  2.4GHz 36 dBm, 5GHz 23 dBm, 6GHz 12 dBm (match regulatory)
>
> Cc: stable@vger.kernel.org
> Fixes: 1c099ab44727 ("mt76: mt7921: add MAC support")
> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
> ---
> Changes since v3:
> - Removed mt7921_set_tx_sar_pwr() from add_chanctx and change_chanctx.
>   Channel transitions don't change underlying power constraints, so
>   reprogramming the full rate table is unnecessary. Replaced with a
>   lightweight helper that recomputes txpower_cur locally.
> - Removed IEEE80211_CONF_CHANGE_CHANNEL trigger from config().
> - Removed BSS_CHANGED_TXPOWER handler from bss_info_changed(). Writing
>   per-vif txpower into per-HW hw->conf.power_level breaks multi-vif
>   semantics. User txpower limits need a different approach (follow-up).
> - Subtracted path delta before storing txpower_cur. The connac rate
>   loop stores total bounded power, but mt76_get_txpower() adds the
>   multi-chain path delta when reporting. mt7915 accounts for this via
>   mt76_get_power_bound(), which subtracts the delta before storing.
>   Without the same subtraction, reported values were inflated by 3 dBm
>   on 2x2 devices.
>
>  .../wireless/mediatek/mt76/mt76_connac_mcu.c  | 14 +++++++--
>  .../net/wireless/mediatek/mt76/mt7921/main.c  | 30 +++++++++++++++++++
>  2 files changed, 41 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> index 16db0f208..e26a2cb39 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> @@ -2193,14 +2193,22 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>                                 .hw_value = ch_list[idx],
>                                 .band = band,
>                         };
> -                       s8 reg_power, sar_power;
> +                       s8 reg_power, sar_power, max_power;
>
>                         reg_power = mt76_connac_get_ch_power(phy, &chan,
>                                                              tx_power);
>                         sar_power = mt76_get_sar_power(phy, &chan, reg_power);
>
> -                       mt76_get_rate_power_limits(phy, &chan, limits,
> -                                                  sar_power);
> +                       max_power = mt76_get_rate_power_limits(phy, &chan,
> +                                                              limits,
> +                                                              sar_power);
> +
> +                       if (phy->chandef.chan &&
> +                           phy->chandef.chan->hw_value == ch_list[idx] &&
> +                           phy->chandef.chan->band == band)
> +                               phy->txpower_cur = max_power -
> +                                       mt76_tx_power_path_delta(
> +                                               hweight16(phy->chainmask));
>
>                         tx_power_tlv.last_msg = ch_list[idx] == last_ch;
>                         sku_tlbv.channel = ch_list[idx];
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index 5881040ac..a77ae5791 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -1355,13 +1355,39 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
>         mt792x_mutex_release(dev);
>  }
>
> +static void mt7921_update_txpower_cur(struct mt76_phy *phy)
> +{
> +       struct mt76_power_limits limits;
> +       struct ieee80211_channel *chan = phy->chandef.chan;
> +       int n_chains = hweight16(phy->chainmask);
> +       s8 reg_power, sar_power, max_power;
> +       int tx_power;
> +
> +       if (!chan)
> +               return;
> +
> +       tx_power = 2 * phy->hw->conf.power_level;
> +       if (!tx_power)
> +               tx_power = 127;
> +
> +       reg_power = mt76_connac_get_ch_power(phy, chan, tx_power);
> +       sar_power = mt76_get_sar_power(phy, chan, reg_power);
> +       max_power = mt76_get_rate_power_limits(phy, chan, &limits, sar_power);
> +
> +       phy->txpower_cur = max_power - mt76_tx_power_path_delta(n_chains);
> +}
> +
>  static int
>  mt7921_add_chanctx(struct ieee80211_hw *hw,
>                    struct ieee80211_chanctx_conf *ctx)
>  {
>         struct mt792x_dev *dev = mt792x_hw_dev(hw);
> +       struct mt76_phy *mphy = hw->priv;
>
>         dev->new_ctx = ctx;
> +       mphy->chandef = ctx->def;
> +       mt7921_update_txpower_cur(mphy);
> +

I don't think this is the right fix.

This mixes multiple things in one patch and duplicates the same power
limit calculation in shared connac code and mt7921-specific code.

The issue is in txpower reporting. A channel context is just a view of
the current PHY configuration and should not be used to overwrite PHY
state.

I think this needs to be reworked in a cleaner way, with the shared
logic kept in shared code and the reporting side fixed in the common
mt792x path so it can apply cleanly to both mt7921 and mt7925.

Given the points above, I'd prefer not to keep iterating on the current
approach for v5. I'll try to rework this along the lines above and send
an updated version.

>         return 0;
>  }
>
> @@ -1396,6 +1422,10 @@ mt7921_change_chanctx(struct ieee80211_hw *hw,
>                 mt7921_mcu_config_sniffer(mvif, ctx);
>         else
>                 mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx);
> +
> +       phy->mt76->chandef = ctx->def;
> +       mt7921_update_txpower_cur(phy->mt76);
> +
>         mt792x_mutex_release(phy->dev);
>  }
>
> --
> 2.53.0
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-03-21  8:58 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-25 22:22 [PATCH] wifi: mt76: connac: fix txpower_cur not being updated Lucid Duck
2026-01-27  1:59 ` Nick
2026-01-27 12:21 ` Felix Fietkau
2026-01-27 16:17   ` Bitterblue Smith
2026-01-27 17:00     ` Felix Fietkau
2026-01-30 21:54 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck
2026-02-10  3:02   ` Nick
2026-02-12  2:46     ` Lucid Duck
2026-03-09 21:50   ` Lucid Duck
2026-03-12  6:38     ` Sean Wang
2026-03-17 17:30       ` [PATCH v3 0/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration Lucid Duck
2026-03-17 17:30         ` [PATCH v3 1/1] " Lucid Duck
2026-03-17 21:51           ` Lucid Duck
2026-03-19  6:00           ` Sean Wang
2026-03-19 20:38             ` [PATCH v4] " Lucid Duck
2026-03-20  1:17               ` Lucid Duck
2026-03-20  6:34                 ` Greg KH
2026-03-21  8:58               ` Sean Wang
2026-01-30 21:58 ` [PATCH v2] wifi: mt76: mt7921: fix txpower reporting Lucid Duck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox