* [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix
@ 2026-01-15 23:02 Lucid Duck
2026-01-15 23:02 ` [PATCH 1/3] wifi: mt76: update timer APIs for kernel 6.18 Lucid Duck
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Lucid Duck @ 2026-01-15 23:02 UTC (permalink / raw)
To: nbd, lorenzo.bianconi83; +Cc: linux-wireless, lucid_duck
This series addresses kernel 6.18 API changes and fixes TX power
REPORTING accuracy for mt7921 (and related connac devices) when
the interface is unassociated.
IMPORTANT: These patches do NOT change actual RF transmit power.
The firmware power tables and regulatory limits remain unchanged.
This only fixes how the driver REPORTS txpower to userspace via
nl80211 (e.g., `iw dev wlan0 info`).
Background
----------
In kernel 6.18, mac80211's __ieee80211_recalc_txpower() returns early
when chanctx_conf is NULL (unassociated interface), leaving
bss_conf.txpower at INT_MIN. This is intentional mac80211 behavior.
However, BSS_CHANGED_TXPOWER is still fired with txpower_type set
correctly but txpower=INT_MIN. Without driver-side handling, this
results in misleading txpower values (3 dBm) reported to userspace.
Testing
-------
Tested on Alfa AWUS036AXML (MT7921AU):
Kernel 6.18.3 (Kali Linux):
- Unassociated: Uses regulatory max for reporting
- Associated: Exact user-requested values work
- Firmware txpower_sku debugfs unchanged
Kernel 6.18.4 (Fedora 43):
- Unassociated: Uses regulatory max for reporting
- Associated (5GHz 80MHz): User values honored correctly
- Firmware txpower_sku debugfs unchanged
Lucid Duck (3):
wifi: mt76: update timer APIs for kernel 6.18
wifi: mt76: mt7921: convert to MLO callbacks and fix txpower reporting
wifi: mt76: connac: preserve txpower_cur in set_rate_txpower
mt76.h | 1 +
mt7615/main.c | 6 ++--
mt7615/pci_mac.c | 4 +--
mt7615/usb.c | 2 +-
mt76_connac_mcu.c | 13 ++++++-
mt76x0/eeprom.c | 2 +-
mt76x02_eeprom.c | 2 +-
mt76x02_usb_core.c | 4 +--
mt76x2/eeprom.c | 2 +-
mt7921/main.c | 84 +++++++++++++++++++++++++++++++++++-----------
mt7925/main.c | 4 +--
mt792x_core.c | 6 ++--
12 files changed, 93 insertions(+), 37 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] wifi: mt76: update timer APIs for kernel 6.18
2026-01-15 23:02 [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix Lucid Duck
@ 2026-01-15 23:02 ` Lucid Duck
2026-01-15 23:02 ` [PATCH 2/3] wifi: mt76: mt7921: convert to MLO callbacks and fix txpower reporting Lucid Duck
2026-01-19 9:06 ` [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix Johannes Berg
2 siblings, 0 replies; 4+ messages in thread
From: Lucid Duck @ 2026-01-15 23:02 UTC (permalink / raw)
To: nbd, lorenzo.bianconi83; +Cc: linux-wireless, lucid_duck
Kernel 6.18 introduced several timer API changes:
- hrtimer_init() replaced with hrtimer_setup()
- del_timer_sync() renamed to timer_delete_sync()
- from_timer() macro removed, use container_of() directly
- asm/unaligned.h moved to linux/unaligned.h
Update all mt76 timer usage to the new APIs for kernel 6.18+
compatibility.
Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
mt76.h | 1 +
mt7615/main.c | 6 +++---
mt7615/pci_mac.c | 4 ++--
mt7615/usb.c | 2 +-
mt76x0/eeprom.c | 2 +-
mt76x02_eeprom.c | 2 +-
mt76x02_usb_core.c | 4 ++--
mt76x2/eeprom.c | 2 +-
mt7925/main.c | 4 ++--
mt792x_core.c | 6 +++---
10 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/mt76.h b/mt76.h
index 646cc8e..4f48167 100644
--- a/mt76.h
+++ b/mt76.h
@@ -19,6 +19,7 @@
#include "airoha_offload.h"
#endif
#include <linux/soc/mediatek/mtk_wed.h>
+#include <linux/version.h>
#include <net/netlink.h>
#include <net/mac80211.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,6,0)
diff --git a/mt7615/main.c b/mt7615/main.c
index a18aa0d..2ac2ba8 100644
--- a/mt7615/main.c
+++ b/mt7615/main.c
@@ -97,7 +97,7 @@ static void mt7615_stop(struct ieee80211_hw *hw, bool suspend)
struct mt7615_phy *phy = mt7615_hw_phy(hw);
cancel_delayed_work_sync(&phy->mt76->mac_work);
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
cancel_delayed_work_sync(&dev->pm.ps_work);
@@ -1046,7 +1046,7 @@ void mt7615_roc_work(struct work_struct *work)
void mt7615_roc_timer(struct timer_list *timer)
{
- struct mt7615_phy *phy = from_timer(phy, timer, roc_timer);
+ struct mt7615_phy *phy = container_of(timer, struct mt7615_phy, roc_timer);
ieee80211_queue_work(phy->mt76->hw, &phy->roc_work);
}
@@ -1197,7 +1197,7 @@ static int mt7615_cancel_remain_on_channel(struct ieee80211_hw *hw,
if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
return 0;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
mt7615_mutex_acquire(phy->dev);
diff --git a/mt7615/pci_mac.c b/mt7615/pci_mac.c
index d83d8ec..53cb1ee 100644
--- a/mt7615/pci_mac.c
+++ b/mt7615/pci_mac.c
@@ -220,12 +220,12 @@ void mt7615_mac_reset_work(struct work_struct *work)
set_bit(MT76_MCU_RESET, &dev->mphy.state);
wake_up(&dev->mt76.mcu.wait);
cancel_delayed_work_sync(&dev->mphy.mac_work);
- del_timer_sync(&dev->phy.roc_timer);
+ timer_delete_sync(&dev->phy.roc_timer);
cancel_work_sync(&dev->phy.roc_work);
if (phy2) {
set_bit(MT76_RESET, &phy2->mt76->state);
cancel_delayed_work_sync(&phy2->mt76->mac_work);
- del_timer_sync(&phy2->roc_timer);
+ timer_delete_sync(&phy2->roc_timer);
cancel_work_sync(&phy2->roc_work);
}
diff --git a/mt7615/usb.c b/mt7615/usb.c
index 7736ae3..d91feff 100644
--- a/mt7615/usb.c
+++ b/mt7615/usb.c
@@ -85,7 +85,7 @@ static void mt7663u_stop(struct ieee80211_hw *hw, bool suspend)
struct mt7615_dev *dev = hw->priv;
clear_bit(MT76_STATE_RUNNING, &dev->mphy.state);
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
cancel_delayed_work_sync(&phy->scan_work);
cancel_delayed_work_sync(&phy->mt76->mac_work);
diff --git a/mt76x0/eeprom.c b/mt76x0/eeprom.c
index f6720a4..d4506b8 100644
--- a/mt76x0/eeprom.c
+++ b/mt76x0/eeprom.c
@@ -10,7 +10,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/etherdevice.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "mt76x0.h"
#include "eeprom.h"
#include "../mt76x02_phy.h"
diff --git a/mt76x02_eeprom.c b/mt76x02_eeprom.c
index 2a15bd2..d16be0c 100644
--- a/mt76x02_eeprom.c
+++ b/mt76x02_eeprom.c
@@ -4,7 +4,7 @@
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "mt76x02_eeprom.h"
diff --git a/mt76x02_usb_core.c b/mt76x02_usb_core.c
index c94c2f6..4ca311d 100644
--- a/mt76x02_usb_core.c
+++ b/mt76x02_usb_core.c
@@ -264,8 +264,8 @@ void mt76x02u_init_beacon_config(struct mt76x02_dev *dev)
};
dev->beacon_ops = &beacon_ops;
- hrtimer_init(&dev->pre_tbtt_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- dev->pre_tbtt_timer.function = mt76x02u_pre_tbtt_interrupt;
+ hrtimer_setup(&dev->pre_tbtt_timer, mt76x02u_pre_tbtt_interrupt,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
INIT_WORK(&dev->pre_tbtt_work, mt76x02u_pre_tbtt_work);
mt76x02_init_beacon_config(dev);
diff --git a/mt76x2/eeprom.c b/mt76x2/eeprom.c
index e1e317a..782813a 100644
--- a/mt76x2/eeprom.c
+++ b/mt76x2/eeprom.c
@@ -5,7 +5,7 @@
#include <linux/module.h>
#include <linux/of.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "mt76x2.h"
#include "eeprom.h"
diff --git a/mt7925/main.c b/mt7925/main.c
index 307850a..2d358a9 100644
--- a/mt7925/main.c
+++ b/mt7925/main.c
@@ -457,7 +457,7 @@ void mt7925_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
ieee80211_iterate_interfaces(mt76_hw(dev),
@@ -489,7 +489,7 @@ static int mt7925_abort_roc(struct mt792x_phy *phy,
{
int err = 0;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
mt792x_mutex_acquire(phy->dev);
diff --git a/mt792x_core.c b/mt792x_core.c
index cc488ee..530e489 100644
--- a/mt792x_core.c
+++ b/mt792x_core.c
@@ -305,7 +305,7 @@ EXPORT_SYMBOL_GPL(mt792x_tx_worker);
void mt792x_roc_timer(struct timer_list *timer)
{
- struct mt792x_phy *phy = from_timer(phy, timer, roc_timer);
+ struct mt792x_phy *phy = container_of(timer, struct mt792x_phy, roc_timer);
ieee80211_queue_work(phy->mt76->hw, &phy->roc_work);
}
@@ -313,7 +313,7 @@ EXPORT_SYMBOL_GPL(mt792x_roc_timer);
void mt792x_csa_timer(struct timer_list *timer)
{
- struct mt792x_vif *mvif = from_timer(mvif, timer, csa_timer);
+ struct mt792x_vif *mvif = container_of(timer, struct mt792x_vif, csa_timer);
ieee80211_queue_work(mvif->phy->mt76->hw, &mvif->csa_work);
}
@@ -362,7 +362,7 @@ void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw,
mutex_unlock(&dev->mt76.mutex);
if (vif->bss_conf.csa_active) {
- del_timer_sync(&mvif->csa_timer);
+ timer_delete_sync(&mvif->csa_timer);
cancel_work_sync(&mvif->csa_work);
}
}
--
2.52.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] wifi: mt76: mt7921: convert to MLO callbacks and fix txpower reporting
2026-01-15 23:02 [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix Lucid Duck
2026-01-15 23:02 ` [PATCH 1/3] wifi: mt76: update timer APIs for kernel 6.18 Lucid Duck
@ 2026-01-15 23:02 ` Lucid Duck
2026-01-19 9:06 ` [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix Johannes Berg
2 siblings, 0 replies; 4+ messages in thread
From: Lucid Duck @ 2026-01-15 23:02 UTC (permalink / raw)
To: nbd, lorenzo.bianconi83; +Cc: linux-wireless, lucid_duck
Kernel 6.18's mac80211 MLO support requires splitting bss_info_changed
into vif_cfg_changed (VIF-wide) and link_info_changed (per-link).
Additionally, handle the case where bss_conf.txpower is not populated
(INT_MIN) when the interface is unassociated. This is intentional
mac80211 behavior - __ieee80211_recalc_txpower() returns early when
chanctx_conf is NULL.
When bss_conf.txpower is unset, fall back to the channel's regulatory
maximum for REPORTING purposes. This does NOT change actual RF power
(controlled by firmware) - only nl80211 reporting accuracy.
Changes:
- Split bss_info_changed into vif_cfg_changed + link_info_changed
- Route VIF-wide changes (ASSOC, PS, ARP_FILTER) to vif_cfg_changed
- Route per-link changes (ERP_SLOT, BEACON, TXPOWER) to link_info_changed
- Handle unset txpower gracefully for unassociated interfaces
Tested on Alfa AWUS036AXML (MT7921AU):
- Kernel 6.18.3 (Kali): Associated and unassociated tests pass
- Kernel 6.18.4 (Fedora): Associated and unassociated tests pass
Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
mt7921/main.c | 84 +++++++++++++++++++++++++++++++++++++++------------
1 file changed, 64 insertions(+), 20 deletions(-)
diff --git a/mt7921/main.c b/mt7921/main.c
index 66051b0..391fafe 100644
--- a/mt7921/main.c
+++ b/mt7921/main.c
@@ -371,7 +371,7 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
ieee80211_iterate_interfaces(mt76_hw(dev),
@@ -402,7 +402,7 @@ static int mt7921_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif)
{
int err = 0;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
mt792x_mutex_acquire(phy->dev);
@@ -687,10 +687,49 @@ static void mt7921_configure_filter(struct ieee80211_hw *hw,
*total_flags &= (FIF_OTHER_BSS | FIF_FCSFAIL | FIF_CONTROL);
}
-static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *info,
- u64 changed)
+/*
+ * mt7921_vif_cfg_changed - handle VIF-wide configuration changes
+ *
+ * In kernel 6.18+, mac80211 splits bss_info_changed into vif_cfg_changed
+ * (VIF-wide) and link_info_changed (per-link). This handles VIF-wide changes.
+ */
+static void mt7921_vif_cfg_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u64 changed)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ mt792x_mutex_acquire(dev);
+
+ if (changed & BSS_CHANGED_PS)
+ mt7921_mcu_uni_bss_ps(dev, vif);
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ mt7921_mcu_sta_update(dev, NULL, vif, true,
+ MT76_STA_INFO_STATE_ASSOC);
+ mt7921_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
+ }
+
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+ mt76_connac_mcu_update_arp_filter(&dev->mt76, &mvif->bss_conf.mt76,
+ &vif->bss_conf);
+ }
+
+ mt792x_mutex_release(dev);
+}
+
+/*
+ * mt7921_link_info_changed - handle per-link BSS information changes
+ *
+ * In kernel 6.18+, mac80211 uses link_info_changed callback for per-link
+ * BSS changes including TX power, ERP slot, beacon, QOS, etc.
+ */
+static void mt7921_link_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u64 changed)
{
struct mt792x_phy *phy = mt792x_hw_phy(hw);
struct mt792x_dev *dev = mt792x_hw_dev(hw);
@@ -715,23 +754,27 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
mt7921_mcu_set_tx(dev, vif);
- if (changed & BSS_CHANGED_PS)
- mt7921_mcu_uni_bss_ps(dev, vif);
-
if (changed & BSS_CHANGED_CQM)
mt7921_mcu_set_rssimonitor(dev, vif);
- if (changed & BSS_CHANGED_ASSOC) {
- mt7921_mcu_sta_update(dev, NULL, vif, true,
- MT76_STA_INFO_STATE_ASSOC);
- mt7921_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
- }
+ if (changed & BSS_CHANGED_TXPOWER) {
+ int tx_power = info->txpower;
- if (changed & BSS_CHANGED_ARP_FILTER) {
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ /*
+ * 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;
- mt76_connac_mcu_update_arp_filter(&dev->mt76, &mvif->bss_conf.mt76,
- info);
+ 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;
}
mt792x_mutex_release(dev);
@@ -1494,7 +1537,7 @@ static void mt7921_abort_channel_switch(struct ieee80211_hw *hw,
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- del_timer_sync(&mvif->csa_timer);
+ timer_delete_sync(&mvif->csa_timer);
cancel_work_sync(&mvif->csa_work);
}
@@ -1535,7 +1578,8 @@ const struct ieee80211_ops mt7921_ops = {
.config = mt7921_config,
.conf_tx = mt792x_conf_tx,
.configure_filter = mt7921_configure_filter,
- .bss_info_changed = mt7921_bss_info_changed,
+ .vif_cfg_changed = mt7921_vif_cfg_changed,
+ .link_info_changed = mt7921_link_info_changed,
.start_ap = mt7921_start_ap,
.stop_ap = mt7921_stop_ap,
.sta_state = mt7921_sta_state,
--
2.52.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix
2026-01-15 23:02 [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix Lucid Duck
2026-01-15 23:02 ` [PATCH 1/3] wifi: mt76: update timer APIs for kernel 6.18 Lucid Duck
2026-01-15 23:02 ` [PATCH 2/3] wifi: mt76: mt7921: convert to MLO callbacks and fix txpower reporting Lucid Duck
@ 2026-01-19 9:06 ` Johannes Berg
2 siblings, 0 replies; 4+ messages in thread
From: Johannes Berg @ 2026-01-19 9:06 UTC (permalink / raw)
To: Lucid Duck, nbd, lorenzo.bianconi83; +Cc: linux-wireless
Hi,
I don't know who you are and if you're serious about this at all, since
this doesn't look like a very serious submission (against 6.18, etc.),
but even if you do that, please ensure that _every_ patch sent to this
list is actually addressed for the kernel and using the right paths.
>
> mt76.h | 1 +
> mt7615/main.c | 6 ++--
> mt7615/pci_mac.c | 4 +--
> mt7615/usb.c | 2 +-
> mt76_connac_mcu.c | 13 ++++++-
> mt76x0/eeprom.c | 2 +-
> mt76x02_eeprom.c | 2 +-
> mt76x02_usb_core.c | 4 +--
> mt76x2/eeprom.c | 2 +-
> mt7921/main.c | 84 +++++++++++++++++++++++++++++++++++-----------
> mt7925/main.c | 4 +--
> mt792x_core.c | 6 ++--
> 12 files changed, 93 insertions(+), 37 deletions(-)
These are clearly not patches that can be applied to the kernel, and
they're erroneously routed to me.
johannes
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-01-19 9:07 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-15 23:02 [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix Lucid Duck
2026-01-15 23:02 ` [PATCH 1/3] wifi: mt76: update timer APIs for kernel 6.18 Lucid Duck
2026-01-15 23:02 ` [PATCH 2/3] wifi: mt76: mt7921: convert to MLO callbacks and fix txpower reporting Lucid Duck
2026-01-19 9:06 ` [PATCH 0/3] wifi: mt76: kernel 6.18 compatibility and txpower reporting fix Johannes Berg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox