4913 Commits
Author SHA1 Message Date
Felix Fietkau 503c643b78 wifi: mt76: account non-AQL frames per peer rather than per link
wcid->non_aql_packets is charged when a frame reaches the hardware queue
and credited back from the completion path. For an MLO peer the hardware
reports the completion against the link it actually transmitted on, which
need not be the link the frame was queued for, so the charge and the
credit can land on different link wcids.

The credited side is clamped at zero while the charged side keeps the
residue. Once it passes MT_MAX_NON_AQL_PKT, mt76_txq_schedule_list()
skips that station on every pass and its traffic stops for good, with the
frames left sitting in the mac80211 queues and nothing counted as
dropped. Only a reassociation, which allocates new wcids, recovers it.

Keep the count on the primary link's wcid, as is already done for the
other peer-wide state.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-08 14:43:09 +02:00
Felix Fietkau 2c7b3011d2 wifi: mt76: clear offchannel state when a scan leaves no channel to restore
mt76_scan_complete() only restores the operating channel when the phy has
one, and __mt76_set_channel() is the sole place that clears
phy->offchannel. A scan on a phy without an operating channel, such as a
6 GHz radio with no interface configured, therefore leaves the flag set
for good.

mt76_txq_schedule_list() skips every TXQ whose wcid points at a phy in
that state, so all traffic for stations on the band stops being dequeued.
The frames stay in the mac80211 queues, never reach the hardware and are
counted nowhere, until an unrelated channel set clears the flag.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-08 08:22:00 +02:00
Felix Fietkau 476cac3604 mt76: derive a distinct address for bands sharing one MAC
Factory data and device trees can point several bands at a single MAC
address. Publishing it once per band hands the same address to userspace
repeatedly, which then assigns it to more than one BSS.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-08 05:35:06 +02:00
Felix Fietkau 03f86e9294 wifi: mt76: mt7996: take over connection monitoring
mt76 already runs its own beacon monitor. mt76_beacon_mon_check() walks
the affiliated links of an MLD, counts a miss per link, and holds off
while the phy is off channel. mt7996 feeds it via mt76_rx_beacon() but
never claims CONNECTION_MONITOR, so mac80211 arms its legacy monitor on
top.

That one knows a single interface rather than a set of links, and it
keeps counting while the radio is away serving another link, so it
reaches its seven beacon intervals and tears down an association that is
still carrying traffic on the other links.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-07 07:21:18 +02:00
Felix Fietkau d3b243c548 wifi: mt76: check a remain-on-channel still belongs to the interface
roc_phy on the shared per-interface data names the phy of the one started last,
which may have ended or moved on to another interface since. Both users take it
at face value: an interface going away aborts whatever it names, and cancelling
one for an interface acts on it without asking whose it is.

Aborting a stranger's leaves the interface's own outstanding one behind, and its
work then runs once the interface is gone, tearing the link down through the
freed bss_conf:

  Unable to handle kernel paging request at virtual address 000043c218ce0afd
  Workqueue: phy0 mt76_roc_complete_work [mt76]
  pc : mt76_connac_mcu_sta_basic_tlv+0xdc/0x260 [mt76_connac_lib]
  Call trace:
   mt76_connac_mcu_sta_basic_tlv
   mt7996_mcu_add_sta
   mt7996_vif_link_destroy
   mt7996_vif_link_remove
   mt76_put_vif_phy_link
   mt76_roc_complete
   mt76_roc_complete_work

Check the interface before acting on it in both places.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-04 11:40:37 +02:00
Felix Fietkau 044081b202 wifi: mt76: mt7996: program a link we already hold again
The firmware wants active links removed from the BSSINFO table as the interface
is destroyed, so a link stays registered from its first chanctx assignment until
the interface goes away, and adding one the driver already holds does nothing.
mac80211 hands a non-AP MLD a fresh address whenever it sets a link up, though,
so a link coming back after a failed attempt carries an address the hardware was
never told about, on a BSS that was never revisited: it authenticates from an
address nothing answers, every time, until the interface is torn down for good.

Program the address and the BSS again, the pair the initial add sets up, which
is for a station what mt7996_mcu_mld_link_oper() does for an AP.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-04 11:40:37 +02:00
Sriram RandFelix Fietkau 4ad4e6a6dc wifi: mac80211: fetch FILS discovery template by link ID
Currently, the FILS discovery template is always fetched from the default
link of a virtual interface in both Multi-Link Operation (MLO) and
non-MLO cases. However, in the MLO case there is a need to fetch the FILS
discovery template from a specific link instead of the default link.

Hence, add support for fetching the FILS discovery template based on the
link ID from the corresponding link data.

Signed-off-by: Sriram R <[email protected]>
Co-developed-by: Raj Kumar Bhagat <[email protected]>
Signed-off-by: Raj Kumar Bhagat <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Johannes Berg <[email protected]>
2026-08-03 18:27:36 +02:00
Pablo Martin-GomezandFelix Fietkau b9e37795e7 wifi: Rename EMLSR delay constants and add EMLMR helpers and definitions
In the final version of 802.11be-2024, Transition Delay and Padding
Delay subfield are for both EMLSR and EMLMR. Depending if the mode is
EMLSR or EMLMR, the interpretation of the encoded value might change.

Define all the constants and helpers to interpret delay subfields both
in EMLSR and EMLMR mode.

Signed-off-by: Pablo Martin-Gomez <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Johannes Berg <[email protected]>
2026-08-03 18:27:10 +02:00
Sriram RandFelix Fietkau ffeb5d8e16 wifi: mac80211: fetch unsolicited probe response template by link ID
Currently, the unsolicited probe response template is always fetched from
the default link of a virtual interface in both Multi-Link Operation (MLO)
and non-MLO cases. However, in the MLO case there is a need to fetch the
unsolicited probe response template from a specific link instead of the
default link.

Hence, add support for fetching the unsolicited probe response template
based on the link ID from the corresponding link data.

Signed-off-by: Sriram R <[email protected]>
Co-developed-by: Raj Kumar Bhagat <[email protected]>
Signed-off-by: Raj Kumar Bhagat <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Johannes Berg <[email protected]>
2026-08-03 17:52:58 +02:00
Johannes BergandFelix Fietkau 6ff9f07cdd wifi: move action code from per-type frame structs
The action code actually serves to identify the type of action
frame, so it really isn't part of the per-type structure. Pull
it out and have it in the general action frame format.

In theory, whether or not the action code is present in this
way is up to each category, but all categories that are defined
right now all have that value.

While at it, and since this change requires changing all users,
remove the 'u' and make it an anonymous union in this case, so
that all code using this changes.

Change IEEE80211_MIN_ACTION_SIZE to take an argument which says
how much of the frame is needed, e.g. category, action_code or
the specific frame type that's defined in the union. Again this
also ensures that all code is updated.

In some cases, fix bugs where the SKB length was checked after
having accessed beyond the checked length, in particular in FTM
code, e.g. ieee80211_is_ftm().

Link: https://patch.msgid.link/20260226183607.67e71846b59e.I9a24328e3ffcaae179466a935f1c3345029f9961@changeid
Signed-off-by: Johannes Berg <[email protected]>
2026-08-03 17:52:28 +02:00
Felix Fietkau 2b7be52496 wifi: mt76: honour the more-data flag when releasing buffered frames
The last frame of a service period was always sent with MORE_DATA clear,
regardless of what mac80211 asked for and regardless of what is left in
the released TIDs. mac80211 sets @more_data when other TIDs still hold
buffered frames, and it expects the driver to set the bit itself when the
released TIDs hold more than the requested number of frames. Neither was
done, so a client that trusts MORE_DATA stops polling and its remaining
traffic sits in the queues until the next TIM or until the client leaves
powersave.

Pass the flag down to the last frame and add what is still queued for the
released TIDs.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 21:17:51 +02:00
Felix Fietkau 56b1108043 wifi: mt76: mt76x02: serialise wcid drop mask updates
One MT_WCID_DROP register word holds the drop bits of 32 stations, and the
read-modify-write that flips a single bit is not serialised against
anything. On MMIO devices mt76x02_sta_ps() runs it from the rx path while
mt76x02_tx_prepare_skb() runs it from the tx path, so an update for one
station can be lost, or an old value can be written back over it. A
station whose drop bit is restored that way has all of its traffic
discarded by the hardware until its next powersave transition.

Take mt76.lock around the update. USB devices can only reach this from
station setup, where register access may sleep and mt76.mutex already
serialises the only two callers.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 21:16:41 +02:00
Felix Fietkau 05fc7fb18f wifi: mt76: mt76x02: clear the wcid drop mask for all powersave responses
Only frames that the driver has buffered in an ieee80211_txq are released
through mt76_release_buffered_frames() and end up on the PSD queue. Frames
that mac80211 buffered itself are handed back through the regular tx path
and go out on the access category queue of the frame. Clearing the drop
mask for MT_TXQ_PSD alone therefore misses them, and the hardware keeps
discarding PS-Poll and U-APSD responses for a station that is still marked
as sleeping. The client polls again on the next TIM and never receives
anything, so its traffic stalls until it leaves powersave.

Key off the tx info flags instead, the same way mt7603_tx_prepare_skb()
does. This covers both release paths and any other frame that mac80211
marks as exempt from powersave buffering.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 21:15:26 +02:00
Jose Ignacio Tornos MartinezandFelix Fietkau 3ad249ed8d wifi: mt76: mt7996: remove beacon_int_min_gcd from ADHOC interface combinations
The driver fails to register with error -22 (EINVAL) due to a cfg80211
validation failure in wiphy_verify_iface_combinations().

Commit 5ef0e8e2653b ("wifi: mt76: mt7996: fix iface combination for
different chipsets") added beacon_int_min_gcd to if_comb_global and
if_comb_global_7992, but these combinations include ADHOC (IBSS)
interface type. This violates a cfg80211 rule from commit 56271da29c52
("cfg80211: disallow beacon_int_min_gcd with IBSS") that explicitly
forbids combining ADHOC with beacon_int_min_gcd.

The restriction exists because beacon_int_min_gcd requires static,
predictable beacon intervals to coordinate multiple beaconing interfaces,
but ADHOC interfaces have dynamic beacon intervals that change when
joining different networks, making the GCD constraint unenforceable.

Remove beacon_int_min_gcd from the interface combinations that include
ADHOC because they are not necessary for ADHOC operation. The if_comb
combination (AP/MESH/STA only, without ADHOC) correctly retains
beacon_int_min_gcd for multi-AP coordination.

Fixes: 5ef0e8e2653b ("wifi: mt76: mt7996: fix iface combination for different chipsets")
Signed-off-by: Jose Ignacio Tornos Martinez <[email protected]>
Tested-by: Alex Gavin <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 19:56:12 +02:00
Devin WittmayerandFelix Fietkau a9b6931687 wifi: mt76: mt792x: do not advertise active monitor
mt76_phy_init() sets NL80211_FEATURE_ACTIVE_MONITOR for every mt76 device,
but mt792x firmware does not honor it: entering active monitor mode stops
RX. Gate the feature behind a new per-phy no_active_monitor flag and set it
for mt792x.

Signed-off-by: Devin Wittmayer <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 19:56:12 +02:00
Felix Fietkau ff47d54067 wifi: mt76: mt7925: fix infinite loop in UNI event TLV parsing
The event TLV loops accept a zero-length TLV, which advances neither the
cursor nor the remaining length, so a malformed event hangs the caller.
mt7925_mcu_uni_roc_event() additionally walked past the end of the skb,
since it never checked the declared length against the remainder.

Replace the five open-coded loops with a shared iterator that rejects
lengths below the TLV header and beyond the remaining buffer, and check
the per-tag payload sizes before dereferencing them.

While here, make the RSSI monitor event read from the current TLV rather
than from the start of the list.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 20d1568e2d wifi: mt76: mt7996: do not leave state behind after a failed WED attach
mt7996_mmio_wed_init() set dev->mt76.hwrro_mode and rx_token_size while
building the WED configuration, before knowing whether the WED attach
can succeed. A failed attach left the enlarged rx_token_size behind and
reset hwrro_mode to MT76_HWRRO_OFF, clobbering the values that another
RX datapath owner may have configured earlier in probe: on Airoha
platforms with the wed_enable module parameter set, this broke the NPU
offload configuration set up by mt76_npu_init() (NPU offload requires
HW-RRO and a larger rx token space, and the attach always fails there
since no SoC has both an Airoha NPU and MTK WED).

Move both assignments after a successful attach, next to the existing
success-only dma_dev/irq assignments. This is safe for the regular WED
attach case: the first consumer of either field runs after probe
continues (mtk_wed_device_attach() only invokes the init_buf callback;
rx buffers are allocated via init_rx_buf from mtk_wed_start(), long
after mt7996_mmio_wed_init() has returned).

Within the WED configuration the HW-RRO checks were constant: the mode
was assigned unconditionally right before them, and the hif2 path is
only reachable after a successful main attach has set it. Resolve them
to their constant values and drop the dead branches.

Fixes: 377aa17d2aed ("wifi: mt76: mt7996: Add NPU offload support to MT7996 driver")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Howard HsuandFelix Fietkau f723115440 wifi: mt76: mt7996: reject iTWT setup requests from MLD stations
The firmware does not support individual TWT agreements with non-AP MLDs,
and the driver only tracks TWT flow state on the default link, which may
not be the link the agreement was negotiated on. Reject TWT setup
requests from MLD stations instead of programming an unsupported
configuration.

Signed-off-by: Howard Hsu <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Peter ChiuandFelix Fietkau c488b587a9 wifi: mt76: fix queue assignment for disassoc packets
Like deauth, a disassoc frame sent to a client in powersave mode can get
stuck in a tx queue along with other buffered frames, filling up hardware
queues with frames that are only released after the WTBL slot is reused
for another client.

Move disassoc packets to the ALTX queue, matching the existing deauth
handling.

Fixes: dedf2ec30fe4 ("wifi: mt76: fix queue assignment for deauth packets")
Signed-off-by: Peter Chiu <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 0b09c6cbf9 wifi: mt76: mt7915: fix chainmask handling for single-phy band-1 devices
Single-adie mt7986 binds its only phy to band 1. The eeprom parse
stores that phy's chainmask unshifted (chainshift is still 0 at that
point) but then sets chainshift to the mask width, so every consumer
that shifts by band_idx * chainshift disagrees with the storage:
mt7915_mcu_set_chan_info() sends rx_path 0 to the firmware with the
default mask, and mt7915_set_antenna() stores a band-shifted mask
inconsistent with the parse-time default.

Derive chainshift only from a band-0 main phy. In the single-phy
band-1 configuration the chains are numbered from 0 like a band-0 phy,
so with chainshift kept at 0 the stored mask, the set_antenna path, the
channel switch command and the RX chain report all agree on unshifted
masks. Drop the now-redundant empty-mask fallback in the RX path.
Dual-phy DBDC configurations parse the band-0 phy first and are
unaffected.

Fixes: 006b9d4ad5bf ("mt76: mt7915: introduce band_idx in mt7915_phy")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
StanleyYP WangandFelix Fietkau 95c614cc6b wifi: mt76: mt7996: remove repeater muar config
Repeater mode is not supported currently, so remove it.
get_omac_idx() only hands out HW_BSSID/EXT_BSSID indices, so the
omac_idx >= REPEATER_BSSID_START call sites are unreachable.

Signed-off-by: StanleyYP Wang <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
StanleyYP WangandFelix Fietkau f3541c1fc3 wifi: mt76: mt7996: add scan dwell time hw cap
Add NL80211_EXT_FEATURE_SET_SCAN_DWELL support in driver.
This allows user to specify channel dwell time during scanning.

Signed-off-by: StanleyYP Wang <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 5ed926a345 wifi: mt76: mt7915: fix VoW per-station DRR field layout
The per-station DRR config value packs the BSS group into bits 0-3;
the bit 4-5 AC priority and bit 6-7 WMM ID fields follow. The group
mask covered bits 0-5, overlapping the AC priority field, and the BWC
group value was written as a bare constant that ignored the station's
group. Narrow the group mask and derive the BWC group from it.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 5781c92d13 wifi: mt76: mt7915: honour the valid flag in efuse block reads
The EFUSE_ACCESS response carries a valid flag that was ignored, so
reading an unwritten efuse block copied garbage into the eeprom data.
Return -ENODATA for invalid blocks and leave the corresponding eeprom
range untouched (zero-initialised) when loading the full eeprom from
efuse.

Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau ba81b0234b wifi: mt76: mt7915: update SKU power limits after changing antennas
Changing the antenna configuration updates the stream capabilities but
left the per-path SKU power limits and path delta compensation stale
until the next channel switch. Reapply the SKU table like mt7996
already does.

Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau f364e31099 wifi: mt76: mt7915: report RX chain signal for all RX paths
status->chains was set from the antenna mask, which is derived from
the number of spatial streams, while the chain_signal array is filled
from all RCPI fields. On boards where the number of RX paths exceeds
the stream count, e.g. the 3T3R mt7916/mt7981 variant with 2 streams
on the 5 GHz band, the RSSI of the extra chains was never reported.

Derive the chain mask from the RX path chainmask instead, shifted by
the band index of the phy owning the frame. On single-adie mt7986 the
main phy is bound to band 1 but its default chainmask is stored
without the band shift, unlike the mask set by mt7915_set_antenna();
fall back to the raw mask when the band group is empty to cover both
stored forms.

Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau fa98398ec4 wifi: mt76: mt7996: do not attach hif2 WED when the main WED attach failed
If the WED attach for the primary PCIe function fails, the probe path
still attached wed_hif2 for the secondary function, leaving the device
in an inconsistent half-WED configuration that crashes later. The hif2
call also re-enabled hwrro_mode, which the failed primary attach had
just turned off.

Skip the hif2 WED setup when the primary WED device is not active.

Fixes: 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau be7e2fa433 wifi: mt76: mt7996: fix MIB TX aggregation counter registers for mt7990
The MIB_TSCR0-7 counters read by mt7996_mac_update_stats() are
hardcoded at the mt7996/mt7992 offsets 0x6b0-0x6d0, but mt7990 moved
them to 0x750-0x770, so TX AMPDU statistics were read from unrelated
registers on that chip. Move the offsets into the per-chip register
tables.

Fixes: f6c87411d15f ("wifi: mt76: mt7996: rework register mapping for mt7990")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 00107991a2 wifi: mt76: mt7996: use the default timeout for STA_REC and BSS_INFO updates
STA_REC_UPDATE and BSS_INFO_UPDATE carry variable-size payloads,
including beacon templates that grow considerably with 11v MBSS and
MLD configurations, and firmware can legitimately take longer than 2
seconds to process them. Since a message timeout now triggers a full
device reset, a false timeout on these commands is punished harshly.

Keep the shorter timeout for the small fixed-size commands and move
STA_REC_UPDATE/BSS_INFO_UPDATE back to the 5 second default.

Fixes: beb01caa570c ("wifi: mt76: mt7996: decrease timeout for commonly issued MCU commands")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
StanleyYP WangandFelix Fietkau c85317f1d4 wifi: mt76: mt7996: fix reg addr remap when addr is 0
When addr is less than the hardcoded threshold in __mt7996_reg_addr,
it indicates that remapping is unnecessary.
Currently, the flow remaps address 0x0 to MT_HIF_REMAP_BASE_L2,
which is incorrect.
To address this, modify __mt7996_reg_addr to return INVALID_REG_ADDR
if the address is not below the hardcoded value or is not present in
the mt7996_reg_map array.
Additionally, update the remap condition to check if addr is equal to
INVALID_REG_ADDR.

Fixes: 3687854d3e7e ("wifi: mt76: mt7996: add locking for accessing mapped registers")
Signed-off-by: StanleyYP Wang <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 686b509b9f wifi: mt76: mt7996: bound RRO session id before indexing addr_elem[]
seq_id from the WED RRO indication is a 12-bit field (0-4095), but only
values up to MT7996_RRO_MAX_SESSION map into wed_rro.addr_elem[], which
has MT7996_RRO_ADDR_ELEM_LEN (128) entries. A larger session id computes
an out-of-bounds element index that is then read and, on a signature
mismatch, written. Reject an out-of-range session id.

Fixes: a09d2f9d69af ("wifi: mt76: Convert mt76_wed_rro_ind to LE")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 5367804a6a wifi: mt76: mt7915: guard firmware memory dump write on disabled memdump
mt7915_coredump_build()/mt7996_coredump_build() wrote dump_mem->len
unconditionally, but the allocation only reserves room for the memory
dump header when coredump_memdump is enabled and memdump_buf_len is
non-zero. In the default configuration the allocation ends right after
the fixed header, so the write landed one struct past the buffer. Only
populate the memory-dump section when it was actually allocated.

Fixes: 4dbcb9125cc3 ("wifi: mt76: mt7915: enable coredump support")
Fixes: 878161d5d4a4 ("wifi: mt76: mt7996: enable coredump support")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau d5738b1ab8 wifi: mt76: mt7915: release hif2 reference on probe IRQ failure
The hif2 reference obtained by mt7915_pci_init_hif2() is only released on
error paths that key off dev->hif2, which is not assigned until after the
IRQ setup. If pci_alloc_irq_vectors() or the primary devm_request_irq()
fails, the reference leaks. Drop it explicitly on those paths via
mt7915_put_hif2().

Fixes: f68d67623dec ("mt76: mt7915: add Wireless Ethernet Dispatch support")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 57ad1439bf wifi: mt76: mt7915: fix ext PHY use-after-free on register error path
After mt7915_register_ext_phy() succeeded, a failure of the main PHY
mt7915_init_debugfs() or mt7915_coredump_register() unwound through
free_phy2, which called ieee80211_free_hw() on the ext PHY hw while it
was still registered with mac80211, since mt76_unregister_device() only
unregisters the main hw. Unregister the ext PHY (thermal + phy + hw)
first and skip the redundant free.

Fixes: 7b8e1ae886e4 ("mt76: mt7915: rework hardware/phy initialization")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau 220bb3c31c wifi: mt76: mt7915: unregister thermal cooling device on hwmon failure
mt7915_thermal_init() registered the thermal cooling device and its sysfs
link, then returned the hwmon registration error without undoing them.
The cooling device (not devm-managed) and the link then leaked, and its
callbacks referenced the freed phy. Unregister the cooling device before
returning the error.

Fixes: 0ae3ff568451 ("mt76: mt7915: fix hwmon temp sensor mem use-after-free")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:21 +02:00
Felix Fietkau a06dbaf0c3 wifi: mt76: mt7996: validate firmware event lengths before parsing
Two unsolicited event handlers trusted firmware-provided lengths:
mt7996_mcu_rx_all_sta_info_event() looped over res->sta_num without
checking it against skb->len, reading the flex array out of bounds; and
mt7996_mcu_rx_log_message() could compute a negative or oversized length
from tlv->len and feed it to %.*s and the debugfs log path. Bound both
against the actual skb length.

Fixes: adde3eed4a75 ("wifi: mt76: mt7996: Add mcu commands for getting sta tx statistic")
Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:20 +02:00
Felix Fietkau 041c5d4c41 wifi: mt76: mt7915: check default EEPROM firmware size before copying
mt7915_eeprom_load_default() copied mt7915_eeprom_size() bytes from the
firmware blob after only checking that it was non-NULL. A truncated
default bin caused an out-of-bounds read past the firmware buffer into
the EEPROM image. Reject a blob smaller than the EEPROM size.

Fixes: beed8bea8d74 ("mt76: mt7915: add mt7916 calibrated data support")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:20 +02:00
Felix Fietkau e308e7430d wifi: mt76: mt7915: bound EEPROM access event before copying
mt7915_mcu_get_eeprom() copied a 16-byte block out of the firmware
response skb without checking that the response was long enough to hold
it. A short or malformed response could read past the end of the skb.
Reject a response shorter than the response header.

Fixes: 888208d1ccc4 ("wifi: mt76: mt7915: set correct background radar capability")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:20 +02:00
Felix Fietkau 591568f4e6 wifi: mt76: mt7996: bound EEPROM access event before copying
mt7996_mcu_get_eeprom() copied the event->eeprom payload out of the
firmware response skb without checking that the response actually held
the bytes being read. A short or malformed response could read past the
end of the skb. Reject a response shorter than the event header and bound
each copy to the payload present in the skb.

Fixes: 0da2e410705e ("wifi: mt76: mt7996: extend flexibility of mt7996_mcu_get_eeprom()")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:20 +02:00
Felix Fietkau d170cb14b9 wifi: mt76: mt7915: fix double hif2 init on the non-WED path
mt7915_pci_init_hif2() was called unconditionally and again inside the
WED-inactive branch. The helper increments the global hif_idx, writes the
PCIe RECOG_ID register and takes a get_device() reference via
mt7915_pci_get_hif2(), while removal only drops one reference. On non-WED
dual-hif hardware this double-incremented hif_idx, wrote RECOG_ID twice and
leaked a device reference. Only the call inside the WED-inactive branch is
correct; drop the unconditional one. hif2 is already initialised to NULL.

Fixes: cacdd67812c6 ("mt76: mt7915: add mt7915_mmio_probe() as a common probing function")
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:46:20 +02:00
Felix Fietkau b2704cf5a4 wifi: mt76: mt7996: fix out-of-bounds link array access in mt7996_tx()
When mac80211 leaves the link unspecified, mt7996_tx() substitutes the
primary link id of the station or vif. That value is
IEEE80211_LINK_UNSPECIFIED (0xf) until the first link has been added,
and it is then used unchecked to index vif->link_conf[],
mvif->mt76.link[] and sta->link[], all of which hold
IEEE80211_MLD_MAX_NUM_LINKS (15) entries.

Clamp the primary link id to the default link before using it, and use
the clamped value for the link_sta fallback as well.

Fixes: 1609b014aa29 ("wifi: mt76: mt7996: Overwrite unspecified link_id in mt7996_tx()")
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:45:48 +02:00
Felix Fietkau 8a03e88fc0 wifi: mt76: reject out-of-range link ids in mt76_vif_link()
mt76_vif_link() indexes mvif->link[] without validating link_id, but
callers pass mvif->deflink_id / msta->deflink_id, which hold
IEEE80211_LINK_UNSPECIFIED (0xf) until the first link has been added.
Since IEEE80211_MLD_MAX_NUM_LINKS is 15, that reads one element past the
end of the array, aliasing mt76_vif_data.offchannel_link.

Reachable via mt7996_set_tsf()/mt7996_offset_tsf() and
mt7996_net_fill_forward_path(). Bounds check link_id and return NULL,
matching mt7996_sta_link() and mt7996_sta_link_protected().

Fixes: a9384b36a42a ("wifi: mt76: mt7996: rework set/get_tsf callabcks to support MLO")
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:45:48 +02:00
Felix Fietkau 5504bac46a wifi: mt76: mt7603: file buffered frames under the TID reported to mac80211
The PS queue filed redirected frames under the TID taken from the TXD,
while mac80211 was told about the TID taken from the QoS header, or TID 0
for anything that is not a QoS data frame. When the two disagree,
mt7603_release_buffered_frames() skips the frame because it does not
match the requested TIDs, so it stays in the PS queue until the station
wakes up. Buffered MMPDUs hit this whenever they were sent on a
non-zero TID.

Use one TID for both.

Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:45:48 +02:00
Felix Fietkau 276db249c2 wifi: mt76: mt7603: restore hardware PS buffering after a service period
Releasing buffered frames has to turn off the PSE redirect for the
station, otherwise the released frames are looped straight back into the
driver PS queue. Nothing ever turns it back on: mt7603_sta_ps() only runs
on an observed PM bit transition, and MT_WCID_FLAG_PS keeps mt76 from
reporting the same state twice. After the first service period the
hardware therefore treats a dozing station as awake and transmits at it
directly, which is where the retry storms and the packet loss reported
against U-APSD clients come from.

Re-arm hardware buffering from mt7603_mac_work() for every station that
is still known to be asleep, once the PSD queue has drained and the
released frames have passed the redirect stage. Frames that are still
queued belong to the service period that was just served, so unlike on a
sleep transition they must not be pulled back with mt7603_filter_tx().

Track the sleep state separately from the WTBL state, so a station that
wakes up while the re-arm is pending is not put back to sleep.

Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:45:48 +02:00
Felix Fietkau 56fe4e0c0a wifi: mt76: mt7603: tell mac80211 when the PS queue has run empty
mt7603_rx_loopback_skb() marks a TID as buffered when the hardware
redirects a frame into the driver PS queue, but nothing ever clears that
state again. mac80211 therefore keeps the TIM bit set for the station and
keeps routing every service period to the driver, which also prevents it
from releasing frames it has buffered itself.

Clear the buffered state for every requested TID that has no frames left
in the PS queue.

Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:45:48 +02:00
Felix Fietkau baac541c05 wifi: mt76: mt7603: fix U-APSD service period termination
Frames released from the driver PS queue were all tagged with MORE_DATA
and none of them ever carried the EOSP bit, so from the client's point of
view a U-APSD service period was started but never finished. Clients that
keep their receiver on until EOSP arrives stop sending trigger frames,
and all downlink traffic for that station stalls until they give up.
ieee80211_sta_eosp() only cleared the service period state inside
mac80211, which is why the mismatch went unnoticed.

Assign MORE_DATA per frame and set the wire EOSP bit on the last one. If
the last released frame is a bufferable MMPDU it has no QoS control field
to carry EOSP, so let mac80211 append a QoS-Null frame instead.

Also stop handing the remaining frame budget to
mt76_release_buffered_frames() once frames have been released from the PS
queue: both would signal the end of the same service period. Releasing
fewer frames than requested is allowed, and MORE_DATA tells the client to
trigger again.

Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:45:48 +02:00
Felix Fietkau cf8a369f52 wifi: mt76: set the EOSP bit in the QoS header of the last released frame
When the driver implements .release_buffered_frames, mac80211 leaves the
U-APSD signalling entirely to the driver: "In this case it is also
responsible for setting the EOSP flag in the QoS header of the frames"
(include/net/mac80211.h). Only IEEE80211_TX_STATUS_EOSP was being set,
which merely ends the service period inside mac80211, so on air the
service period was never terminated. Clients that wait for EOSP before
going back to doze keep the SP open and stop triggering, which stalls all
downlink traffic for that station.

Set the wire EOSP bit on the last frame of a U-APSD service period.
EOSP has no meaning for a PS-Poll response, so pass the release reason
down and leave those frames alone.

Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-08-01 17:45:48 +02:00
shengwei.luandFelix Fietkau 0df35270b7 wifi: mt76: mt7925: Fix EHT Beamformee SS subfields to meet 802.11be minimum
Per IEEE 802.11be, the Beamformee SS <= 80/160/320 MHz 3-bit subfields
in the EHT PHY Capabilities are encoded as (Nss - 1) and are required
to be >= 3 (i.e. at least 4 SS receive capability) whenever SU
Beamformee is advertised.

MT7925 is a 2x2 STA (sts = 2), so directly filling (sts - 1) = 1
violates the spec minimum. Clamp the encoded value to 3 when sts <= 3,
otherwise use (sts - 1). This is applied consistently to the
BEAMFORMEE_SS <= 80 MHz (split across phy_cap_info[0]/[1]), <= 160 MHz
and <= 320 MHz (6 GHz only) subfields.

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips")
Signed-off-by: shengwei.lu <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-07-31 07:26:40 +02:00
Dmitry AntipovandFelix Fietkau 540a8ed525 wifi: mt76: mt7915: simplify mt7915_sys_recovery_set()
Use convenient ' kstrtou16_from_user()'
to simplify 'mt7915_sys_recovery_set()'.

Signed-off-by: Dmitry Antipov <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-07-30 10:14:20 +02:00
Jeff HsuandFelix Fietkau cd26ff6646 wifi: mt76: mt7925: skip DMA re-init selectively
Add a flag to skip it on chips that don't need it.

Signed-off-by: Jeff Hsu <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-07-30 10:13:51 +02:00