* Re: [PATCH net-next v3 03/13] net: introduce ndo_set_rx_mode_async and dev_rx_mode_work
From: Stanislav Fomichev @ 2026-03-24 18:13 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Stanislav Fomichev, netdev, davem, edumazet, pabeni, horms,
corbet, skhan, andrew+netdev, michael.chan, pavan.chebbi,
anthony.l.nguyen, przemyslaw.kitszel, saeedm, tariqt, mbloch,
alexanderduyck, kernel-team, johannes, sd, jianbol, dtatulea,
mohsin.bashr, jacob.e.keller, willemb, skhawaja, bestswngs,
aleksandr.loktionov, kees, linux-doc, linux-kernel,
intel-wired-lan, linux-rdma, linux-wireless, linux-kselftest,
leon
In-Reply-To: <20260323162003.0d155055@kernel.org>
On 03/23, Jakub Kicinski wrote:
> On Thu, 19 Mar 2026 18:24:51 -0700 Stanislav Fomichev wrote:
> > diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst
> > index 35704d115312..dc83d78d3b27 100644
> > --- a/Documentation/networking/netdevices.rst
> > +++ b/Documentation/networking/netdevices.rst
> > @@ -289,6 +289,14 @@ struct net_device synchronization rules
> > ndo_set_rx_mode:
> > Synchronization: netif_addr_lock spinlock.
> > Context: BHs disabled
> > + Notes: Deprecated in favor of sleepable ndo_set_rx_mode_async.
> >
> > +ndo_set_rx_mode_async:
> > + Synchronization: rtnl_lock() semaphore. In addition, netdev instance
> > + lock if the driver implements queue management or shaper API.
> > + Context: process (from a work queue)
> > + Notes: Sleepable version of ndo_set_rx_mode. Receives snapshots
>
> It's probably just my weirdness but I find creating adjectives out
> of random nouns by adding "-able" to be in poor taste. "sleepable"
> took root in certain three letter subsystems but I hope it won't
> in netdev.
>
> Please use your words:
>
> Notes: Async version of ndo_set_rx_mode which runs in process
> context. Receives snapshots f the unicast and multicast address lists.
SG, will do!
> > + of the unicast and multicast address lists.
> >
> > ndo_setup_tc:
> > ``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` are running under NFT locks
> > diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> > index 469b7cdb3237..b05bdd67b807 100644
> > --- a/include/linux/netdevice.h
> > +++ b/include/linux/netdevice.h
> > @@ -1117,6 +1117,16 @@ struct netdev_net_notifier {
> > * This function is called device changes address list filtering.
> > * If driver handles unicast address filtering, it should set
> > * IFF_UNICAST_FLT in its priv_flags.
> > + * Cannot sleep, called with netif_addr_lock_bh held.
> > + * Deprecated in favor of sleepable ndo_set_rx_mode_async.
> > + *
> > + * void (*ndo_set_rx_mode_async)(struct net_device *dev,
> > + * struct netdev_hw_addr_list *uc,
> > + * struct netdev_hw_addr_list *mc);
> > + * Sleepable version of ndo_set_rx_mode. Called from a work queue
> > + * with rtnl_lock and netdev_lock_ops(dev) held. The uc/mc parameters
> > + * are snapshots of the address lists - iterate with
> > + * netdev_hw_addr_list_for_each(ha, uc).
> > *
> > * int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
> > * This function is called when the Media Access Control address
> > @@ -1437,6 +1447,9 @@ struct net_device_ops {
> > void (*ndo_change_rx_flags)(struct net_device *dev,
> > int flags);
> > void (*ndo_set_rx_mode)(struct net_device *dev);
> > + void (*ndo_set_rx_mode_async)(struct net_device *dev,
> > + struct netdev_hw_addr_list *uc,
> > + struct netdev_hw_addr_list *mc);
> > int (*ndo_set_mac_address)(struct net_device *dev,
> > void *addr);
> > int (*ndo_validate_addr)(struct net_device *dev);
> > @@ -1903,6 +1916,7 @@ enum netdev_reg_state {
> > * has been enabled due to the need to listen to
> > * additional unicast addresses in a device that
> > * does not implement ndo_set_rx_mode()
> > + * @rx_mode_work: Work queue entry for ndo_set_rx_mode_async()
> > * @uc: unicast mac addresses
> > * @mc: multicast mac addresses
> > * @dev_addrs: list of device hw addresses
> > @@ -2293,6 +2307,7 @@ struct net_device {
> > unsigned int promiscuity;
> > unsigned int allmulti;
> > bool uc_promisc;
> > + struct work_struct rx_mode_work;
> > #ifdef CONFIG_LOCKDEP
> > unsigned char nested_level;
> > #endif
> > @@ -4661,6 +4676,11 @@ static inline bool netif_device_present(const struct net_device *dev)
> > return test_bit(__LINK_STATE_PRESENT, &dev->state);
> > }
> >
> > +static inline bool netif_up_and_present(const struct net_device *dev)
> > +{
> > + return (dev->flags & IFF_UP) && netif_device_present(dev);
>
> Is this really worth a dedicated helper? What are you trying to express
> here semantically?
I mostly added it to avoid repeating the same present & UP check. Will
undo.
> > +
> > void netif_device_detach(struct net_device *dev);
> >
> > void netif_device_attach(struct net_device *dev);
> > diff --git a/net/core/dev.c b/net/core/dev.c
> > index 200d44883fc1..fedc423306fc 100644
> > --- a/net/core/dev.c
> > +++ b/net/core/dev.c
> > @@ -2381,6 +2381,8 @@ static void netstamp_clear(struct work_struct *work)
> > static DECLARE_WORK(netstamp_work, netstamp_clear);
> > #endif
> >
> > +static struct workqueue_struct *rx_mode_wq;
> > +
> > void net_enable_timestamp(void)
> > {
> > #ifdef CONFIG_JUMP_LABEL
> > @@ -9669,22 +9671,84 @@ int netif_set_allmulti(struct net_device *dev, int inc, bool notify)
> > return 0;
> > }
> >
> > -/*
> > - * Upload unicast and multicast address lists to device and
> > - * configure RX filtering. When the device doesn't support unicast
> > - * filtering it is put in promiscuous mode while unicast addresses
> > - * are present.
> > +static void dev_rx_mode_work(struct work_struct *work)
> > +{
> > + struct net_device *dev = container_of(work, struct net_device,
> > + rx_mode_work);
> > + struct netdev_hw_addr_list uc_snap, mc_snap, uc_ref, mc_ref;
> > + const struct net_device_ops *ops = dev->netdev_ops;
> > + int err;
> > +
> > + __hw_addr_init(&uc_snap);
> > + __hw_addr_init(&mc_snap);
> > + __hw_addr_init(&uc_ref);
> > + __hw_addr_init(&mc_ref);
> > +
> > + rtnl_lock();
> > + netdev_lock_ops(dev);
> > +
> > + if (!netif_up_and_present(dev))
> > + goto out;
> > +
> > + if (ops->ndo_set_rx_mode_async) {
>
> How did we get here if we don't have this op?
> Are you planning to plumb more code thru this work in the future?
> If yes the whole rx_mode handling should be in a dedicated helper
> rather than indenting most of the function.
I do expand this, yes, in the subsequent patches. With promisc
handling (ndo_change_rx_flags) and ndo_set_rx_mode fallback. Let me try
to see if I can add some helper+struct to manage a set of snapshots
to make it a bit more clear.
> > + netif_addr_lock_bh(dev);
> > +
> > + err = __hw_addr_list_snapshot(&uc_snap, &dev->uc,
> > + dev->addr_len);
> > + if (!err)
> > + err = __hw_addr_list_snapshot(&uc_ref, &dev->uc,
> > + dev->addr_len);
> > + if (!err)
> > + err = __hw_addr_list_snapshot(&mc_snap, &dev->mc,
> > + dev->addr_len);
> > + if (!err)
> > + err = __hw_addr_list_snapshot(&mc_ref, &dev->mc,
> > + dev->addr_len);
>
> This doesn't get slow with a few thousands of addresses?
I can add kunit benchmark and attach the output? Although not sure where
to go from that. The alternative to this is allocating an array of entries.
I started with that initially but __hw_addr_sync_dev wants to kfree the
individual entries and I decided not to have a separate helpers to
manage the snapshots.
> > + netif_addr_unlock_bh(dev);
> > +
> > + if (err) {
> > + netdev_WARN(dev, "failed to sync uc/mc addresses\n");
> > + __hw_addr_flush(&uc_snap);
> > + __hw_addr_flush(&uc_ref);
> > + __hw_addr_flush(&mc_snap);
> > + goto out;
> > + }
> > +
> > + ops->ndo_set_rx_mode_async(dev, &uc_snap, &mc_snap);
> > +
> > + netif_addr_lock_bh(dev);
> > + __hw_addr_list_reconcile(&dev->uc, &uc_snap,
> > + &uc_ref, dev->addr_len);
> > + __hw_addr_list_reconcile(&dev->mc, &mc_snap,
> > + &mc_ref, dev->addr_len);
> > + netif_addr_unlock_bh(dev);
> > + }
> > +
> > +out:
> > + netdev_unlock_ops(dev);
> > + rtnl_unlock();
> > +}
> > +
> > +/**
> > + * __dev_set_rx_mode() - upload unicast and multicast address lists to device
> > + * and configure RX filtering.
> > + * @dev: device
> > + *
> > + * When the device doesn't support unicast filtering it is put in promiscuous
> > + * mode while unicast addresses are present.
> > */
> > void __dev_set_rx_mode(struct net_device *dev)
> > {
> > const struct net_device_ops *ops = dev->netdev_ops;
> >
> > /* dev_open will call this function so the list will stay sane. */
> > - if (!(dev->flags&IFF_UP))
> > + if (!netif_up_and_present(dev))
> > return;
> >
> > - if (!netif_device_present(dev))
> > + if (ops->ndo_set_rx_mode_async) {
> > + queue_work(rx_mode_wq, &dev->rx_mode_work);
> > return;
> > + }
> >
> > if (!(dev->priv_flags & IFF_UNICAST_FLT)) {
> > /* Unicast addresses changes may only happen under the rtnl,
> > @@ -11708,6 +11772,16 @@ void netdev_run_todo(void)
> >
> > __rtnl_unlock();
> >
> > + /* Make sure all pending rx_mode work completes before returning.
> > + *
> > + * rx_mode_wq may be NULL during early boot:
> > + * core_initcall(netlink_proto_init) vs subsys_initcall(net_dev_init).
> > + *
> > + * Check current_work() to avoid flushing from the wq.
> > + */
> > + if (rx_mode_wq && !current_work())
> > + flush_workqueue(rx_mode_wq);
>
> Can we give the work a reference on the netdev (at init time) and
> cancel + release it here instead of flushing / waiting?
Not sure why cancel+release, maybe you're thinking about the unregister
path? This is rtnl_unlock -> netdev_run_todo -> __rtnl_unlock + some
extras.
And the flush is here to plumb the addresses to the real devices
before we return to the callers. Mostly because of the following
things we have in the tests:
# TEST: team cleanup mode lacp [FAIL]
# macvlan unicast address not found on a slave
Can you explain a bit more on the suggestion?
> > /* Wait for rcu callbacks to finish before next phase */
> > if (!list_empty(&list))
> > rcu_barrier();
> > @@ -12099,6 +12173,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
> > #endif
> >
> > mutex_init(&dev->lock);
> > + INIT_WORK(&dev->rx_mode_work, dev_rx_mode_work);
> >
> > dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
> > setup(dev);
> > @@ -12203,6 +12278,8 @@ void free_netdev(struct net_device *dev)
> >
> > kfree(rcu_dereference_protected(dev->ingress_queue, 1));
> >
> > + cancel_work_sync(&dev->rx_mode_work);
>
> Should never happen so maybe wrap it in a WARN ?
Or maybe just flush_workqueue here as well? To signal the intent that we
are mostly waiting for the wq entry to be unused to be able to kfree it?
^ permalink raw reply
* Re: [PATCH ath-next v4] wifi: ath12k: avoid dynamic alloc when parsing wmi tb
From: Jeff Johnson @ 2026-03-24 16:55 UTC (permalink / raw)
To: Rameshkumar Sundaram, Nicolas Escande, ath12k; +Cc: linux-wireless
In-Reply-To: <ec266bec-c371-4ec8-a60f-21ebb810d38e@oss.qualcomm.com>
On 3/23/2026 5:40 AM, Rameshkumar Sundaram wrote:
> On 3/19/2026 9:29 PM, Jeff Johnson wrote:
>> On 3/19/2026 7:35 AM, Nicolas Escande wrote:
>>> On Thu Mar 19, 2026 at 12:08 PM CET, Rameshkumar Sundaram wrote:
>>>> Or may be have this allocated on first device probe and free it on last
>>>> device deinit ?
>>>
>>> That seems even more involved. It would be easier to go back to the previous
>>> version and simply, alloc it once per ath12k_base
>>>
>>> What do you guys think ?
>>>
>>
>> Going back to that may be the better solution. It isn't nice that this current
>> solution may allocate memory when the driver isn't actually used. But I'll let
>> others on the team weigh in as well.
>>
>
> Yeah, allocating once per ath12k_base is definitely the simpler
> ownership model.
> I was only wondering whether sharing it across devices might be worth a
> look, since this is per-CPU scratch space and the table itself is fairly
> large.
The other alternative is to still have a single global allocation, but also
keep a reference count that starts at 0. when each ar starts it calls a single
api to alloc the memory and when it stops it calls another api to dealloc the
memory
when the first ar starts and calls the alloc api, the refcount will be 0 so it
will allocate the memory and increment the refcount to 1. when any subsequent
ars start and call the alloc api, it will just increment the ref count. on
deinit each ar will call the dealloc api. this api will just decrement the
refcount until it reaches 0 at which time the memory is freed.
^ permalink raw reply
* pull request: mt76-next 2026-03-23 v2
From: Felix Fietkau @ 2026-03-24 15:52 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
Hi,
Here's an updated version of my mt76 pull request for 7.1.
It drops the dt-bindings patches that are missing an ACK from DT
maintainers.
- Felix
The following changes since commit 9ac76f3d0bb2940db3a9684d596b9c8f301ef315:
Merge tag 'wireless-next-2026-03-19' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next (2026-03-19 15:30:20 +0100)
are available in the Git repository at:
https://github.com/nbd168/wireless tags/mt76-next-2026-03-23
for you to fetch changes up to e8c819df02436f2c2379766946735e1f06a7c923:
wifi: mt76: mt7996: Destroy active sta links in mt7996_mac_sta_remove() (2026-03-24 15:49:32 +0000)
----------------------------------------------------------------
mt76 patches for 7.1
- fixes
- mt7996/mt7925 MLO fixes/improvements
- mt7996 NPU support
- mt7996 external EEPROM support
----------------------------------------------------------------
Allen Ye (1):
wifi: mt76: fix backoff fields and max_power calculation
Alok Tiwari (1):
wifi: mt76: mt7996: fix FCS error flag check in RX descriptor
Chad Monroe (5):
wifi: mt76: fix deadlock in remain-on-channel
wifi: mt76: mt7996: reset device after MCU message timeout
wifi: mt76: mt7996: increase txq memory limit to 32 MiB
wifi: mt76: fix multi-radio on-channel scanning
wifi: mt76: support upgrading passive scans to active
Christian Hewitt (1):
wifi: mt7601u: check multiple firmware paths
Colin Ian King (1):
wifi: mt76: mt7996: Fix spelling mistake "retriving" -> "retrieving"
David Bauer (2):
wifi: mt76: mt76x02: wake queues after reconfig
wifi: mt76: don't return TXQ when exceeding max non-AQL packets
Duoming Zhou (2):
wifi: mt76: mt7915: fix use-after-free bugs in mt7915_mac_dump_work()
wifi: mt76: mt7996: fix use-after-free bugs in mt7996_mac_dump_work()
Felix Fietkau (9):
wifi: mt76: add offchannel check to mt76_roc_complete
wifi: mt76: check chanctx before restoring channel after ROC
wifi: mt76: abort ROC on chanctx changes
wifi: mt76: optimize ROC for same-channel case
wifi: mt76: send nullfunc PS frames on offchannel transitions
wifi: mt76: flush pending TX before channel switch
wifi: mt76: route nullfunc frames to PSD/ALTX queue
wifi: mt76: wait for firmware TX completion of mgmt frames before channel switch
wifi: mt76: add per-link beacon monitoring for MLO
Howard Hsu (1):
wifi: mt76: mt7996: support critical packet mode for MT7990 chipsets
Leon Yen (5):
wifi: mt76: mt7925: introduce CSA support in non-MLO mode
wifi: mt76: mt7925: Fix incorrect MLO mode in firmware control
wifi: mt76: mt792x: Fix a potential deadlock in high-load situations
wifi: mt76: mt7921: fix a potential clc buffer length underflow
wifi: mt76: mt7925: fix tx power setting failure after chip reset
Lorenzo Bianconi (30):
wifi: mt76: mt7996: Set mtxq->wcid just for primary link
wifi: mt76: mt7996: Reset mtxq->idx if primary link is removed in mt7996_vif_link_remove()
wifi: mt76: mt7996: Switch to the secondary link if the default one is removed
wifi: mt76: mt7996: Clear wcid pointer in mt7996_mac_sta_deinit_link()
wifi: mt76: mt7996: Reset ampdu_state state in case of failure in mt7996_tx_check_aggr()
wifi: mt76: Fix memory leak destroying device
wifi: mt76: mt7996: Fix NPU stop procedure
wifi: mt76: npu: Add missing rx_token_size initialization
wifi: mt76: always enable RRO queues for non-MT7992 chipset
wifi: mt76: mt7996: Fix BAND2 tx queues initialization when NPU is enabled
wifi: mt76: mt7996: Fix wdma_idx for MT7996 device if NPU is enabled
wifi: mt76: mt7996: Add mt7992_npu_txrx_offload_init routine
wifi: mt76: mt7996: Rename mt7996_npu_rxd_init() in mt7992_npu_rxd_init()
wifi: mt76: mt7996: Add NPU support for MT7990 chipset
wifi: mt76: mt7996: Integrate NPU in RRO session management
wifi: mt76: mt7996: Integrate MT7990 init configuration for NPU
wifi: mt76: mt7996: Integrate MT7990 dma configuration for NPU
wifi: mt76: mt7996: Add __mt7996_npu_hw_init routine
wifi: mt76: mt7996: Move RRO dma start in a dedicated routine
wifi: mt76: Do not reset idx for NPU tx queues during reset
wifi: mt76: mt7996: Do not schedule RRO and TxFree queues during reset for NPU
wifi: mt76: mt7996: Store DMA mapped buffer addresses in mt7996_npu_hw_init()
wifi: mt76: Enable NPU support for MT7996 devices
wifi: mt76: mt7996: Add missing CHANCTX_STA_CSA property
wifi: mt76: mt7996: Remove link pointer dependency in mt7996_mac_sta_remove_links()
wifi: mt76: mt7996: Remove unnecessary phy filed in mt7996_vif_link struct
wifi: mt76: mt7996: Decrement sta counter removing the link in mt7996_mac_reset_sta_iter()
wifi: mt76: mt7996: Rely on msta_link link_id in mt7996_vif_link_remove()
wifi: mt76: mt7996: Destroy vif active links in mt7996_remove_interface()
wifi: mt76: mt7996: Destroy active sta links in mt7996_mac_sta_remove()
Madhur Kumar (1):
wifi: mt76: mt7921: Replace deprecated PCI function
MeiChia Chiu (1):
wifi: mt76: mt7996: Add eMLSR support
Michael Lo (2):
wifi: mt76: mt7925: Skip scan process during suspend.
wifi: mt76: mt7921: fix 6GHz regulatory update on connection
Ming Yen Hsieh (3):
wifi: mt76: mt7925: fix incorrect length field in txpower command
wifi: mt76: mt7925: prevent NULL pointer dereference in mt7925_tx_check_aggr()
wifi: mt76: mt7925: prevent NULL vif dereference in mt7925_mac_write_txwi
Peter Chiu (3):
wifi: mt76: mt7996: fix RRO EMU configuration
wifi: mt76: mt7996: update WFSYS reset flow for MT7990 chipsets
wifi: mt76: mt7996: fix frequency separation for station STR mode
Quan Zhou (3):
wifi: mt76: mt7925: fix AMPDU state handling in mt7925_tx_check_aggr
wifi: mt76: mt7921: fix ROC abort flow interruption in mt7921_roc_work
wifi: mt76: mt7925: fix incorrect TLV length in CLC command
Rex Lu (1):
wifi: mt76: mt7996: adjust timeout value for boot-up calibration commands
Rory Little (1):
wifi: mt76: mt7921: Place upper limit on station AID
Ryder Lee (4):
wifi: mt76: mt7615: fix use_cts_prot support
wifi: mt76: mt7915: fix use_cts_prot support
wifi: mt76: mt7996: add support for ERP CTS & HT protection
wifi: mt76: mt7996: Disable Rx hdr_trans in monitor mode
Sean Wang (36):
wifi: mt76: mt7921: Reset ampdu_state state in case of failure in mt76_connac2_tx_check_aggr()
wifi: mt76: mt7925: drop puncturing handling from BSS change path
wifi: mt76: mt7925: fix potential deadlock in mt7925_roc_abort_sync
wifi: mt76: mt7921: fix potential deadlock in mt7921_roc_abort_sync
wifi: mt76: connac: use is_connac2() to replace is_mt7921() checks
wifi: mt76: mt7921: use mt76_for_each_q_rx() in reset path
wifi: mt76: mt7921: handle MT7902 irq_map quirk with mutable copy
wifi: mt76: mt7921: add MT7902e DMA layout support
wifi: mt76: connac: mark MT7902 as hw txp devices
wifi: mt76: mt792x: add PSE handling barrier for the large MCU cmd
wifi: mt76: mt792x: ensure MCU ready before ROM patch download
wifi: mt76: mt7921: add MT7902 MCU support
wifi: mt76: mt792x: add MT7902 WFDMA prefetch configuration
wifi: mt76: mt7921: add MT7902 PCIe device support
wifi: mt76: mt7921: add MT7902 SDIO device support
wifi: mt76: mt792x: describe USB WFSYS reset with a descriptor
wifi: mt76: mt792x: fix mt7925u USB WFSYS reset handling
wifi: mt76: mt7925: pass mlink to sta_amsdu_tlv()
wifi: mt76: mt7925: pass WCID indices to bss_basic_tlv()
wifi: mt76: mt7925: pass mlink and mconf to sta_mld_tlv()
wifi: mt76: mt7925: pass mlink to mcu_sta_update()
wifi: mt76: mt7925: resolve primary mlink via def_wcid
wifi: mt76: mt7925: pass mlink to mac_link_sta_remove()
wifi: mt76: mt7925: pass mlink to sta_hdr_trans_tlv()
wifi: mt76: mt7925: validate mlink in sta_hdr_trans_tlv()
wifi: mt76: mt7925: pass mlink to wtbl_update_hdr_trans()
wifi: mt76: mt7925: pass mlink to set_link_key()
wifi: mt76: mt7925: resolve link after acquiring mt76 mutex
wifi: mt76: mt7925: pass mconf and mlink to wtbl_update_hdr_trans()
wifi: mt76: mt7925: make WCID cleanup unconditional in sta_remove_links()
wifi: mt76: mt7925: unwind WCID setup on link STA add failure
wifi: mt76: mt7925: drop WCID reinit after publish
wifi: mt76: mt7925: move WCID teardown into link_sta_remove()
wifi: mt76: mt7925: switch link STA allocation to RCU lifetime
wifi: mt76: mt7925: publish msta->link after successful link add
wifi: mt76: mt7925: host-only unwind published links on add failure
Shayne Chen (8):
wifi: mt76: mt7996: extend CSA and CCA support for MLO
wifi: mt76: mt7996: add duplicated WTBL command
wifi: mt76: mt7996: fix iface combination for different chipsets
wifi: mt76: mt7996: add variant for MT7992 chipsets
wifi: mt76: mt7996: fix wrong DMAD length when using MAC TXP
wifi: mt76: mt7996: Account active links in valid_links fields
wifi: mt76: mt7996: Move mlink deallocation in mt7996_vif_link_remove()
wifi: mt76: mt7996: Add mcu APIs to enable/disable vif links.
StanleyYP Wang (10):
wifi: mt76: mt7996: fix the behavior of radar detection
wifi: mt76: mt7996: set specific BSSINFO and STAREC commands after channel switch
wifi: mt76: mt7996: abort CCA when CSA is starting
wifi: mt76: mt7996: offload radar threshold initialization
wifi: mt76: add external EEPROM support for mt799x chipsets
wifi: mt76: mt7996: apply calibration-free data from OTP
wifi: mt76: mt7996: fix struct mt7996_mcu_uni_event
wifi: mt76: avoid to set ACK for MCU command if wait_resp is not set
wifi: mt76: mt7996: fix queue pause after scan due to wrong channel switch reason
wifi: mt76: mt7996: fix issues with manually triggered radar detection
Zac Bowling (1):
wifi: mt76: fix list corruption in mt76_wcid_cleanup
Zilin Guan (1):
wifi: mt76: Fix memory leak after mt76_connac_mcu_alloc_sta_req()
Ziyi Guo (1):
wifi: mt76: add missing lock protection in mt76_sta_state for sta_event callback
drivers/net/wireless/mediatek/mt76/channel.c | 39 ++-
drivers/net/wireless/mediatek/mt76/dma.c | 33 ++-
drivers/net/wireless/mediatek/mt76/dma.h | 4 +-
drivers/net/wireless/mediatek/mt76/eeprom.c | 154 +++++++----
drivers/net/wireless/mediatek/mt76/mac80211.c | 230 ++++++++++++++++-
drivers/net/wireless/mediatek/mt76/mcu.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt76.h | 47 +++-
drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 15 --
drivers/net/wireless/mediatek/mt76/mt7615/main.c | 7 +-
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c | 47 ++++
drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h | 5 +-
drivers/net/wireless/mediatek/mt76/mt7615/regs.h | 2 -
drivers/net/wireless/mediatek/mt76/mt76_connac.h | 11 +-
drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 28 +-
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 46 ++--
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 15 +-
drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c | 1 +
drivers/net/wireless/mediatek/mt76/mt7915/init.c | 1 +
drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 13 -
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 9 +-
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 66 ++++-
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 11 +
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 4 +
drivers/net/wireless/mediatek/mt76/mt7921/init.c | 4 +-
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 29 ++-
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 3 +
drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 16 ++
drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 70 ++++-
drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c | 6 +-
drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 4 +
drivers/net/wireless/mediatek/mt76/mt7925/init.c | 2 +
drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 18 +-
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 394 +++++++++++++++++++++++-----
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 194 +++++++-------
drivers/net/wireless/mediatek/mt76/mt7925/mcu.h | 7 +
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 13 +-
drivers/net/wireless/mediatek/mt76/mt7925/regd.c | 3 +-
drivers/net/wireless/mediatek/mt76/mt792x.h | 7 +
drivers/net/wireless/mediatek/mt76/mt792x_core.c | 14 +-
drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 18 +-
drivers/net/wireless/mediatek/mt76/mt792x_mac.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 6 +
drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 51 +++-
drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c | 36 ++-
drivers/net/wireless/mediatek/mt76/mt7996/dma.c | 208 +++++++++------
drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c | 64 +++--
drivers/net/wireless/mediatek/mt76/mt7996/init.c | 110 ++++++--
drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 161 ++++--------
drivers/net/wireless/mediatek/mt76/mt7996/mac.h | 5 -
drivers/net/wireless/mediatek/mt76/mt7996/main.c | 439 +++++++++++++++++++++++--------
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 823 +++++++++++++++++++++++++++++++++++++++++++++++++----------
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 112 +++++++-
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 70 ++++-
drivers/net/wireless/mediatek/mt76/mt7996/npu.c | 469 +++++++++++++++++++++++++++-------
drivers/net/wireless/mediatek/mt76/mt7996/regs.h | 11 +
drivers/net/wireless/mediatek/mt76/npu.c | 37 ++-
drivers/net/wireless/mediatek/mt76/scan.c | 70 ++++-
drivers/net/wireless/mediatek/mt76/tx.c | 34 ++-
drivers/net/wireless/mediatek/mt7601u/mcu.c | 15 +-
drivers/net/wireless/mediatek/mt7601u/usb.h | 1 +
60 files changed, 3347 insertions(+), 969 deletions(-)
^ permalink raw reply
* [PATCH 1/2] wifi: mt76: mt7996: fix out-of-bounds array access during hardware restart
From: Felix Fietkau @ 2026-03-24 15:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Chad Monroe
During hardware restart, link_id can be IEEE80211_LINK_UNSPECIFIED,
causing an out-of-bounds array access on msta->link[].
Add mt7996_sta_link() and mt7996_sta_link_protected() helper functions
for accessing sta links with proper RCU handling and bounds checking.
Use them for any sta link RCU access.
Reported-by: Chad Monroe <chad@monroe.io>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
.../wireless/mediatek/mt76/mt7996/debugfs.c | 4 ++--
.../net/wireless/mediatek/mt76/mt7996/mac.c | 6 ++---
.../net/wireless/mediatek/mt76/mt7996/main.c | 17 +++++++-------
.../net/wireless/mediatek/mt76/mt7996/mcu.c | 22 +++++++++----------
.../wireless/mediatek/mt76/mt7996/mt7996.h | 19 ++++++++++++++++
5 files changed, 43 insertions(+), 25 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
index 34af800964d1..ef9a9204adf5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
@@ -664,7 +664,7 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
if (!mlink)
continue;
- msta_link = rcu_dereference(msta->link[link_id]);
+ msta_link = mt7996_sta_link(msta, link_id);
if (!msta_link)
continue;
@@ -1042,7 +1042,7 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file,
mutex_lock(&dev->mt76.mutex);
- msta_link = mt76_dereference(msta->link[link_sta->link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_sta->link_id);
if (!msta_link) {
ret = -EINVAL;
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index e2a83da3a09c..c98446057282 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -48,7 +48,7 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
if (mlink->band_idx != band_idx)
continue;
- msta_link = rcu_dereference(msta->link[i]);
+ msta_link = mt7996_sta_link(msta, i);
break;
}
@@ -1038,7 +1038,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
if (link_id != wcid->link_id && link_id != IEEE80211_LINK_UNSPECIFIED) {
if (msta) {
struct mt7996_sta_link *msta_link =
- rcu_dereference(msta->link[link_id]);
+ mt7996_sta_link(msta, link_id);
if (msta_link)
wcid = &msta_link->wcid;
@@ -1346,7 +1346,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
IEEE80211_MLD_MAX_NUM_LINKS) {
struct mt7996_sta_link *msta_link;
- msta_link = rcu_dereference(msta->link[id]);
+ msta_link = mt7996_sta_link(msta, id);
if (!msta_link)
continue;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index a8a6552d49f6..796a8af565cb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -207,8 +207,7 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct mt7996_sta *msta;
msta = (struct mt7996_sta *)sta->drv_priv;
- msta_link = mt76_dereference(msta->link[link_id],
- &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
return 0;
@@ -1381,7 +1380,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
if (!link)
continue;
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
continue;
@@ -1573,7 +1572,7 @@ static void mt7996_tx(struct ieee80211_hw *hw,
if (msta) {
struct mt7996_sta_link *msta_link;
- msta_link = rcu_dereference(msta->link[link_id]);
+ msta_link = mt7996_sta_link(msta, link_id);
if (msta_link)
wcid = &msta_link->wcid;
}
@@ -1944,7 +1943,7 @@ static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw,
rcu_read_lock();
- msta_link = rcu_dereference(msta->link[link_sta->link_id]);
+ msta_link = mt7996_sta_link(msta, link_sta->link_id);
if (msta_link) {
struct mt7996_dev *dev = mt7996_hw_dev(hw);
@@ -1961,7 +1960,7 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta)
struct mt7996_sta_link *msta_link;
u32 *changed = data;
- msta_link = rcu_dereference(msta->link[msta->deflink_id]);
+ msta_link = mt7996_sta_link(msta, msta->deflink_id);
if (msta_link)
mt7996_link_rate_ctrl_update(&changed, msta_link);
}
@@ -2011,7 +2010,7 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
if (!link)
continue;
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
continue;
@@ -2049,7 +2048,7 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
if (!link)
continue;
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
continue;
@@ -2389,7 +2388,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
if (!link)
return -EIO;
- msta_link = rcu_dereference(msta->link[msta->deflink_id]);
+ msta_link = mt7996_sta_link(msta, msta->deflink_id);
if (!msta_link)
return -EIO;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 16420375112d..9aef28e66665 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -1136,7 +1136,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
struct mt7996_sta_link *msta_link;
int link_id = link_conf->link_id;
- msta_link = rcu_dereference(msta->link[link_id]);
+ msta_link = mt7996_sta_link(msta, link_id);
if (msta_link)
sta_wlan_idx = msta_link->wcid.idx;
}
@@ -1429,7 +1429,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
continue;
@@ -1463,7 +1463,7 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
continue;
@@ -2200,7 +2200,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta,
if (!mlink)
goto error_unlock;
- msta_link = rcu_dereference(msta->link[link_id]);
+ msta_link = mt7996_sta_link(msta, link_id);
if (!msta_link)
goto error_unlock;
@@ -2290,7 +2290,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta,
if (!link)
goto error_unlock;
- msta_link = rcu_dereference(msta->link[link_id]);
+ msta_link = mt7996_sta_link(msta, link_id);
if (!msta_link)
goto error_unlock;
@@ -2508,7 +2508,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta,
if (!link)
goto error_unlock;
- msta_link = rcu_dereference(msta->link[link_id]);
+ msta_link = mt7996_sta_link(msta, link_id);
if (!msta_link)
goto error_unlock;
@@ -2663,7 +2663,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
unsigned int link_id;
struct tlv *tlv;
- msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, msta->deflink_id);
if (!msta_link)
return;
@@ -2677,8 +2677,8 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx);
if (nlinks > 1) {
- msta_link = mt76_dereference(msta->link[msta->seclink_id],
- &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta,
+ msta->seclink_id);
if (!msta_link)
return;
}
@@ -2689,7 +2689,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
for_each_sta_active_link(vif, sta, link_sta, link_id) {
struct mt7996_vif_link *link;
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
continue;
@@ -2837,7 +2837,7 @@ void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta)
if (!link_sta)
return;
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
if (!msta_link)
return;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
index bdcf72457954..0dc4198fcf8b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
@@ -642,6 +642,25 @@ mt7996_vif_conf_link(struct mt7996_dev *dev, struct ieee80211_vif *vif,
link_conf);
}
+static inline struct mt7996_sta_link *
+mt7996_sta_link(struct mt7996_sta *msta, u8 link_id)
+{
+ if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
+ return NULL;
+
+ return rcu_dereference(msta->link[link_id]);
+}
+
+static inline struct mt7996_sta_link *
+mt7996_sta_link_protected(struct mt7996_dev *dev, struct mt7996_sta *msta,
+ u8 link_id)
+{
+ if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
+ return NULL;
+
+ return mt76_dereference(msta->link[link_id], &dev->mt76);
+}
+
#define mt7996_for_each_phy(dev, phy) \
for (int __i = 0; __i < ARRAY_SIZE((dev)->radio_phy); __i++) \
if (((phy) = (dev)->radio_phy[__i]) != NULL)
--
2.51.0
^ permalink raw reply related
* [PATCH 2/2] wifi: mt76: mt7996: add missing max_remain_on_channel_duration
From: Felix Fietkau @ 2026-03-24 15:49 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20260324154904.2555603-1-nbd@nbd.name>
Having this unset breaks remain-on-channel and mgmt TX.
Move setting it to mt76 core to keep it in one place.
Fixes: 69d54ce7491d0 ("wifi: mt76: mt7996: switch to single multi-radio wiphy")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mac80211.c | 2 ++
drivers/net/wireless/mediatek/mt76/mt7615/init.c | 1 -
drivers/net/wireless/mediatek/mt76/mt792x_core.c | 1 -
3 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 4ae5e4715a9c..dd68776ada28 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -449,6 +449,8 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL);
+ if (!wiphy->max_remain_on_channel_duration)
+ wiphy->max_remain_on_channel_duration = 5000;
if (!wiphy->available_antennas_tx)
wiphy->available_antennas_tx = phy->antenna_mask;
if (!wiphy->available_antennas_rx)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
index 42e11ba1206e..e437e088b2e9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
@@ -195,7 +195,6 @@ mt7615_check_offload_capability(struct mt7615_dev *dev)
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
wiphy->flags &= ~WIPHY_FLAG_4ADDR_STATION;
- wiphy->max_remain_on_channel_duration = 5000;
wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index 152cfcca2f90..5a5d7534830b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -657,7 +657,6 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
- wiphy->max_remain_on_channel_duration = 5000;
wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN;
wiphy->max_scan_ssids = 4;
wiphy->max_sched_scan_plan_interval =
--
2.51.0
^ permalink raw reply related
* Re: pull request: mt76-next 2026-03-23
From: Johannes Berg @ 2026-03-24 15:24 UTC (permalink / raw)
To: Felix Fietkau, linux-wireless
Cc: Ryder Lee, devicetree, Krzysztof Kozlowski, Conor Dooley
In-Reply-To: <bf6858fc-98f0-4523-bca4-8da7ecf3738c@nbd.name>
On Mon, 2026-03-23 at 10:33 +0100, Felix Fietkau wrote:
> The following changes since commit 9ac76f3d0bb2940db3a9684d596b9c8f301ef315:
>
> Merge tag 'wireless-next-2026-03-19' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next (2026-03-19 15:30:20 +0100)
>
> are available in the Git repository at:
>
> https://github.com/nbd168/wireless tags/mt76-next-2026-03-23
[...]
> dt-bindings: net: wireless: mt76: add mt79 PCI devices
> dt-bindings: net: wireless: mt76: clarify backoff limit usage
OK I have no idea what happened here, apparently Ryder has a super
messed up email setup that ended up sending different versions with the
same Message-Id, so the lore archive is a big mess ... whatever is going
on there, please fix that.
But given what I _can_ see about these patches in the archive, I don't
want to pull this now unless I get a clear ACK from DT maintainers.
johannes
^ permalink raw reply
* Re: [PATCH] wifi: wilc1000: fix u8 overflow in SSID scan buffer size calculation
From: Yasuaki Torimaru @ 2026-03-24 15:07 UTC (permalink / raw)
To: Jeff Johnson
Cc: linux-wireless, ajay.kathat, claudiu.beznea, kees, linux-kernel,
stable
In-Reply-To: <accee45c-7cae-48fc-b868-b7404b8c061c@oss.qualcomm.com>
On Tue, 25 Mar 2026, Jeff Johnson wrote:
> Reviewed-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
>
> Another thing to note is it is very strange that the struct wid that defines
> the TLV format uses a signed type for both the TLV length and
payload pointer:
> s32 size;
> s8 *val;
>
> I don't think I've ever seen this in a TLV representation!
Thank you for the review.
Good point — signed types for TLV length and payload are indeed unusual
and could mask subtle sign-extension bugs. I'll look into a follow-up
cleanup patch for struct wid once this fix lands.
Thanks,
Yasuaki
2026年3月24日(火) 23:50 Jeff Johnson <jeff.johnson@oss.qualcomm.com>:
>
> On 3/24/2026 3:06 AM, Yasuaki Torimaru wrote:
> > The variable valuesize is declared as u8 but accumulates the total
> > length of all SSIDs to scan. Each SSID contributes up to 33 bytes
> > (IEEE80211_MAX_SSID_LEN + 1), and with WILC_MAX_NUM_PROBED_SSID (10)
> > SSIDs the total can reach 330, which wraps around to 74 when stored
> > in a u8.
> >
> > This causes kmalloc to allocate only 75 bytes while the subsequent
> > memcpy writes up to 331 bytes into the buffer, resulting in a 256-byte
> > heap buffer overflow.
> >
> > Widen valuesize from u8 to u32 to accommodate the full range.
> >
> > Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Yasuaki Torimaru <yasuakitorimaru@gmail.com>
>
> Reviewed-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
>
> Another thing to note is it is very strange that the struct wid that defines
> the TLV format uses a signed type for both the TLV length and payload pointer:
> s32 size;
> s8 *val;
>
> I don't think I've ever seen this in a TLV representation!
>
> > ---
> > drivers/net/wireless/microchip/wilc1000/hif.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c
> > index f354b11cb919..944b2a812b63 100644
> > --- a/drivers/net/wireless/microchip/wilc1000/hif.c
> > +++ b/drivers/net/wireless/microchip/wilc1000/hif.c
> > @@ -163,7 +163,7 @@ int wilc_scan(struct wilc_vif *vif, u8 scan_source,
> > u32 index = 0;
> > u32 i, scan_timeout;
> > u8 *buffer;
> > - u8 valuesize = 0;
> > + u32 valuesize = 0;
> > u8 *search_ssid_vals = NULL;
> > const u8 ch_list_len = request->n_channels;
> > struct host_if_drv *hif_drv = vif->hif_drv;
>
^ permalink raw reply
* Re: [PATCH v3 2/9] dt-bindings: mmc: amlogic: Add compatible for T7 mmc
From: Ulf Hansson @ 2026-03-24 15:03 UTC (permalink / raw)
To: Ronald Claveau
Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Johannes Berg,
van Spriel, linux-arm-kernel, linux-amlogic, devicetree,
linux-kernel, linux-mmc, linux-wireless
In-Reply-To: <20260323-add-emmc-t7-vim4-v3-2-5159d90a984c@aliel.fr>
On Mon, 23 Mar 2026 at 10:58, Ronald Claveau <linux-kernel-dev@aliel.fr> wrote:
>
> Add amlogic,t7-mmc compatible string, falling back to amlogic,meson-axg-mmc
> as the T7 MMC controller is compatible with the AXG implementation.
>
> Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
Applied for next, thanks!
Kind regards
Uffe
> ---
> Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
> index 57646575a13f8..40dccf9715781 100644
> --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
> +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
> @@ -19,7 +19,10 @@ allOf:
> properties:
> compatible:
> oneOf:
> - - const: amlogic,meson-axg-mmc
> + - items:
> + - enum:
> + - amlogic,t7-mmc
> + - const: amlogic,meson-axg-mmc
> - items:
> - const: amlogic,meson-gx-mmc
> - const: amlogic,meson-gxbb-mmc
>
> --
> 2.49.0
>
^ permalink raw reply
* Re: [PATCH] wifi: wilc1000: fix u8 overflow in SSID scan buffer size calculation
From: Jeff Johnson @ 2026-03-24 14:49 UTC (permalink / raw)
To: Yasuaki Torimaru, linux-wireless
Cc: ajay.kathat, claudiu.beznea, kees, linux-kernel, stable
In-Reply-To: <20260324100624.983458-1-yasuakitorimaru@gmail.com>
On 3/24/2026 3:06 AM, Yasuaki Torimaru wrote:
> The variable valuesize is declared as u8 but accumulates the total
> length of all SSIDs to scan. Each SSID contributes up to 33 bytes
> (IEEE80211_MAX_SSID_LEN + 1), and with WILC_MAX_NUM_PROBED_SSID (10)
> SSIDs the total can reach 330, which wraps around to 74 when stored
> in a u8.
>
> This causes kmalloc to allocate only 75 bytes while the subsequent
> memcpy writes up to 331 bytes into the buffer, resulting in a 256-byte
> heap buffer overflow.
>
> Widen valuesize from u8 to u32 to accommodate the full range.
>
> Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Yasuaki Torimaru <yasuakitorimaru@gmail.com>
Reviewed-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Another thing to note is it is very strange that the struct wid that defines
the TLV format uses a signed type for both the TLV length and payload pointer:
s32 size;
s8 *val;
I don't think I've ever seen this in a TLV representation!
> ---
> drivers/net/wireless/microchip/wilc1000/hif.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c
> index f354b11cb919..944b2a812b63 100644
> --- a/drivers/net/wireless/microchip/wilc1000/hif.c
> +++ b/drivers/net/wireless/microchip/wilc1000/hif.c
> @@ -163,7 +163,7 @@ int wilc_scan(struct wilc_vif *vif, u8 scan_source,
> u32 index = 0;
> u32 i, scan_timeout;
> u8 *buffer;
> - u8 valuesize = 0;
> + u32 valuesize = 0;
> u8 *search_ssid_vals = NULL;
> const u8 ch_list_len = request->n_channels;
> struct host_if_drv *hif_drv = vif->hif_drv;
^ permalink raw reply
* pull-request: ath-next-20260324
From: Jeff Johnson @ 2026-03-24 14:26 UTC (permalink / raw)
To: linux-wireless, Johannes Berg; +Cc: ath10k, ath11k, ath12k, jjohnson
The following changes since commit 44d93cf1abb6a85d65c3b4b027c82d44263de6a5:
wifi: UHR: define DPS/DBE/P-EDCA elements and fix size parsing (2026-03-04 11:50:03 +0100)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git tags/ath-next-20260324
for you to fetch changes up to 7bbb578fc43e7dcb8690cfc98844bd67bc311e8a:
wifi: ath12k: Remove unused DFS Unit Test definitions (2026-03-16 08:34:23 -0700)
----------------------------------------------------------------
ath.git patches for v7.1 (PR #2)
A few small cleanups and bug fixes across ath drivers.
----------------------------------------------------------------
Aaradhana Sahu (1):
wifi: ath12k: Use .mbn firmware for AHB devices
Gustavo A. R. Silva (1):
wifi: ath6kl: wmi: Avoid -Wflex-array-member-not-at-end warning
Jeff Johnson (2):
wifi: ath12k: Clean up the WMI Unit Test command interface
wifi: ath12k: Remove unused DFS Unit Test definitions
Johan Hovold (5):
wifi: ath6kl: drop redundant device reference
wifi: ath6kl: rename disconnect callback
wifi: ath9k: drop redundant device reference
wifi: ath10k: drop redundant device reference
wifi: ath10k: rename disconnect callback
Manish Dharanenthiran (1):
wifi: ath12k: Fix the assignment of logical link index
P Praneesh (1):
wifi: ath12k: Fix legacy rate mapping for monitor mode capture
Sarika Sharma (1):
wifi: ath12k: account TX stats only when ACK/BA status is present
drivers/net/wireless/ath/ath10k/usb.c | 8 +--
drivers/net/wireless/ath/ath12k/ahb.h | 4 +-
drivers/net/wireless/ath/ath12k/core.h | 2 +-
drivers/net/wireless/ath/ath12k/dp_htt.c | 24 ++++----
drivers/net/wireless/ath/ath12k/hal.h | 31 +++++++----
drivers/net/wireless/ath/ath12k/mac.c | 67 +++++++++++++++--------
drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 76 ++++++++++++++++++++------
drivers/net/wireless/ath/ath12k/wmi.c | 58 +++++++++-----------
drivers/net/wireless/ath/ath12k/wmi.h | 14 +++--
drivers/net/wireless/ath/ath6kl/usb.c | 16 ++----
drivers/net/wireless/ath/ath6kl/wmi.h | 11 ----
drivers/net/wireless/ath/ath9k/hif_usb.c | 4 --
12 files changed, 180 insertions(+), 135 deletions(-)
^ permalink raw reply
* pull-request: ath-current-20260324
From: Jeff Johnson @ 2026-03-24 14:19 UTC (permalink / raw)
To: linux-wireless, Johannes Berg; +Cc: ath10k, ath11k, ath12k, jjohnson
The following changes since commit a1d9d8e833781c44ab688708804ce35f20f3cbbd:
Merge tag 'net-7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-03-19 11:25:40 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git tags/ath-current-20260324
for you to fetch changes up to 4242625f272974dd1947f73b10d884eab3b277cd:
wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session (2026-03-24 06:44:00 -0700)
----------------------------------------------------------------
ath.git update for v7.0-rc6
For both ath11k and ath12k use the correct TID when stopping an AMPDU
session.
----------------------------------------------------------------
Reshma Immaculate Rajkumar (2):
wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session
wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session
drivers/net/wireless/ath/ath11k/dp_rx.c | 15 +++++++--------
drivers/net/wireless/ath/ath12k/dp_rx.c | 4 +++-
2 files changed, 10 insertions(+), 9 deletions(-)
^ permalink raw reply
* Re: [PATCH ath-next v2] wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session
From: Jeff Johnson @ 2026-03-24 13:46 UTC (permalink / raw)
To: ath12k, Reshma Immaculate Rajkumar; +Cc: linux-wireless
In-Reply-To: <20260227110123.3726354-1-reshma.rajkumar@oss.qualcomm.com>
On Fri, 27 Feb 2026 16:31:23 +0530, Reshma Immaculate Rajkumar wrote:
> With traffic ongoing for data TID [TID 0], an DELBA request to
> stop AMPDU for the BA session was received on management TID [TID 4].
> The corresponding TID number was incorrectly passed to stop the BA session,
> resulting in the BA session for data TIDs being stopped and the BA size
> being reduced to 1, causing an overall dip in TCP throughput.
>
> Fix this issue by passing the correct argument from
> ath12k_dp_rx_ampdu_stop() to ath12k_dp_arch_peer_rx_tid_reo_update()
> during an AMPDU stop session. Instead of passing peer->dp_peer->rx_tid,
> which is the base address of the array, corresponding to TID 0, pass
> the value of &peer->dp_peer->rx_tid[params->tid]. With this, the
> different TID numbers are accounted for.
>
> [...]
Applied, thanks!
[1/1] wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session
commit: 4242625f272974dd1947f73b10d884eab3b277cd
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH ath-next v3] wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session
From: Jeff Johnson @ 2026-03-24 13:46 UTC (permalink / raw)
To: ath11k, Reshma Immaculate Rajkumar; +Cc: linux-wireless
In-Reply-To: <20260319065608.2408179-1-reshma.rajkumar@oss.qualcomm.com>
On Thu, 19 Mar 2026 12:26:08 +0530, Reshma Immaculate Rajkumar wrote:
> During ongoing traffic, a request to stop an AMPDU session
> for one TID could incorrectly affect other active sessions.
> This can happen because an incorrect TID reference would be
> passed when updating the BA session state, causing the wrong
> session to be stopped. As a result, the affected session would
> be reduced to a minimal BA size, leading to a noticeable
> throughput degradation.
>
> [...]
Applied, thanks!
[1/1] wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session
commit: e225b36f83d7926c1f2035923bb0359d851fdb73
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* pull-request: iwlwifi-fixes-2026-03-24
From: Korenblit, Miriam Rachel @ 2026-03-24 12:10 UTC (permalink / raw)
To: Linux Wireless
The following changes since commit a1d9d8e833781c44ab688708804ce35f20f3cbbd:
Merge tag 'net-7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-03-19 11:25:40 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next.git/ tags/iwlwifi-fixes-2026-03-24
for you to fetch changes up to 687a95d204e72e52f2e6bc7a994cc82f76b2678f:
wifi: iwlwifi: mld: correctly set wifi generation data (2026-03-24 13:55:53 +0200)
----------------------------------------------------------------
wifi: iwlwifi: fixes - 2026-03-24
- Fix MLO scan timing (record the scan start in FW)
- don't send a 6E related command when not supported
- correctly set wifi generation data
----------------------------------------------------------------
Emmanuel Grumbach (1):
wifi: iwlwifi: mvm: don't send a 6E related command when not supported
Johannes Berg (1):
wifi: iwlwifi: mld: correctly set wifi generation data
Pagadala Yesu Anjaneyulu (1):
wifi: iwlwifi: mld: Fix MLO scan timing
.../net/wireless/intel/iwlwifi/fw/api/commands.h | 5 +
drivers/net/wireless/intel/iwlwifi/fw/api/scan.h | 10 ++
drivers/net/wireless/intel/iwlwifi/mld/iface.c | 101 ++++++++++++++-------
drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 19 ++++
drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 +
drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 4 +-
drivers/net/wireless/intel/iwlwifi/mld/notif.c | 5 +
drivers/net/wireless/intel/iwlwifi/mld/scan.c | 30 +++++-
drivers/net/wireless/intel/iwlwifi/mld/scan.h | 9 +-
drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 +-
10 files changed, 146 insertions(+), 41 deletions(-)
^ permalink raw reply
* [PATCH AUTOSEL 6.19-6.12] wifi: mac80211: check tdls flag in ieee80211_tdls_oper
From: Sasha Levin @ 2026-03-24 11:19 UTC (permalink / raw)
To: patches, stable
Cc: Deepanshu Kartikey, syzbot+56b6a844a4ea74487b7b, Johannes Berg,
Johannes Berg, Sasha Levin, linux-wireless, linux-kernel
In-Reply-To: <20260324111931.3257972-1-sashal@kernel.org>
From: Deepanshu Kartikey <kartikey406@gmail.com>
[ Upstream commit 7d73872d949c488a1d7c308031d6a9d89b5e0a8b ]
When NL80211_TDLS_ENABLE_LINK is called, the code only checks if the
station exists but not whether it is actually a TDLS station. This
allows the operation to proceed for non-TDLS stations, causing
unintended side effects like modifying channel context and HT
protection before failing.
Add a check for sta->sta.tdls early in the ENABLE_LINK case, before
any side effects occur, to ensure the operation is only allowed for
actual TDLS peers.
Reported-by: syzbot+56b6a844a4ea74487b7b@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=56b6a844a4ea74487b7b
Tested-by: syzbot+56b6a844a4ea74487b7b@syzkaller.appspotmail.com
Suggested-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
Link: https://patch.msgid.link/20260313092417.520807-1-kartikey406@gmail.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Before commit `076fc8775dafe` (2023), the check was `if (!sta) { ret =
-ENOLINK; break; }`. In older stable trees (5.15, 5.10, 5.4), the fix
would need to be adapted to this older pattern, but it's still trivial:
change `if (!sta)` to `if (!sta || !sta->sta.tdls)`. The logic is
identical.
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
### Step 7.1: Subsystem Criticality
- **Subsystem**: wifi/mac80211 — core wireless networking stack
- **Criticality**: IMPORTANT — used by all Linux systems with WiFi
hardware
- TDLS (Tunneled Direct Link Setup) is a standard WiFi feature used for
direct device-to-device communication
### Step 7.2: Subsystem Activity
mac80211/tdls.c has moderate activity, with both bug fixes and ongoing
development.
## PHASE 8: IMPACT AND RISK ASSESSMENT
### Step 8.1: Affected Users
All Linux users with WiFi hardware that supports TDLS (most modern WiFi
devices). Reachable from userspace via netlink.
### Step 8.2: Trigger Conditions
- Triggered via `NL80211_CMD_TDLS_OPER` with `NL80211_TDLS_ENABLE_LINK`
for a non-TDLS station
- Can be triggered by an **unprivileged local user** with appropriate
netlink access (or by a local attacker)
- syzbot found and reproduced this reliably
### Step 8.3: Failure Mode Severity
- **WARN_ON_ONCE** triggered at tdls.c:1460 — kernel warning
- **Unintended side effects**: channel context and HT protection
modified for non-TDLS station — this could corrupt WiFi connection
state
- Severity: **MEDIUM-HIGH** (WARN + potential state corruption via
userspace-reachable path)
### Step 8.4: Risk-Benefit
- **Benefit**: HIGH — prevents userspace-triggerable WARN and state
corruption in WiFi subsystem; affects all stable trees
- **Risk**: VERY LOW — single additional boolean check, obviously
correct, maintainer-suggested
- **Ratio**: Excellent — minimal risk, clear benefit
## PHASE 9: FINAL SYNTHESIS
### Step 9.1: Evidence Compilation
**FOR backporting:**
- Fixes a syzbot-reported, reproducible bug reachable from userspace
- Bug exists in ALL active stable trees (since 2014)
- Single-line, obviously correct fix
- Suggested by subsystem maintainer (Johannes Berg)
- Tested by syzbot (confirmed fix)
- Prevents WARN_ON_ONCE and unintended WiFi state modification
- Zero regression risk
- Clean apply expected (trivial adaptation for older trees)
**AGAINST backporting:**
- (None significant)
### Step 9.2: Stable Rules Checklist
1. **Obviously correct and tested?** YES — one additional condition,
tested by syzbot
2. **Fixes a real bug?** YES — syzbot-reported, WARN + state corruption
3. **Important issue?** YES — userspace-reachable, affects WiFi state
4. **Small and contained?** YES — 1 line changed
5. **No new features/APIs?** Correct — pure validation fix
6. **Can apply to stable?** YES — trivial adaptation needed for pre-6.6
trees
### Step 9.3: Exception Categories
Not an exception category — this is a straightforward bug fix that meets
standard stable criteria.
### Step 9.4: Decision
Clear YES. This is a textbook stable backport candidate.
## Verification
- **[Phase 1]** Parsed tags: Reported-by syzbot, Tested-by syzbot,
Suggested-by Johannes Berg (maintainer), Signed-off-by Johannes Berg
- **[Phase 2]** Diff analysis: 1 line modified — added `||
!sta->sta.tdls` to existing NULL check in `ieee80211_tdls_oper()`
ENABLE_LINK case
- **[Phase 3]** git blame: buggy code introduced in commit
`95224fe83e5e78` (2014, v3.16 era), present in all stable trees
- **[Phase 3]** git show `076fc8775dafe`: confirmed older trees have
slightly different code structure (`if (!sta) { ret = -ENOLINK; break;
}`) but fix is trivially adaptable
- **[Phase 3]** Related commit `16ecdab5446f1`: another syzbot-reported
TDLS validation fix, independent of this one
- **[Phase 4]** Syzbot bug report: confirmed affects Linux 5.4, 5.10,
5.15, 6.1, 6.6; crash is WARN_ON_ONCE in ieee80211_tdls_oper
- **[Phase 4]** Lore: v2 patch, approach suggested by Johannes Berg; no
NAKs or concerns found
- **[Phase 5]** `ieee80211_tdls_oper` registered via `.tdls_oper` in
cfg.c:5598, reachable from userspace via NL80211_CMD_TDLS_OPER netlink
- **[Phase 6]** Code exists in all active stable trees (bug from 2014)
- **[Phase 6]** Backport: clean apply on 6.x trees; trivial context
adaptation needed for 5.x trees
- **[Phase 8]** Failure mode: WARN_ON_ONCE + unintended channel/HT
protection state modification; severity MEDIUM-HIGH; userspace-
triggerable
**YES**
net/mac80211/tdls.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index dbbfe2d6842fb..1dca2fae05a52 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -1449,7 +1449,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
}
sta = sta_info_get(sdata, peer);
- if (!sta)
+ if (!sta || !sta->sta.tdls)
return -ENOLINK;
iee80211_tdls_recalc_chanctx(sdata, sta);
--
2.51.0
^ permalink raw reply related
* [PATCH] wifi: wilc1000: fix u8 overflow in SSID scan buffer size calculation
From: Yasuaki Torimaru @ 2026-03-24 10:06 UTC (permalink / raw)
To: linux-wireless
Cc: ajay.kathat, claudiu.beznea, kees, linux-kernel, stable,
Yasuaki Torimaru
The variable valuesize is declared as u8 but accumulates the total
length of all SSIDs to scan. Each SSID contributes up to 33 bytes
(IEEE80211_MAX_SSID_LEN + 1), and with WILC_MAX_NUM_PROBED_SSID (10)
SSIDs the total can reach 330, which wraps around to 74 when stored
in a u8.
This causes kmalloc to allocate only 75 bytes while the subsequent
memcpy writes up to 331 bytes into the buffer, resulting in a 256-byte
heap buffer overflow.
Widen valuesize from u8 to u32 to accommodate the full range.
Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver")
Cc: stable@vger.kernel.org
Signed-off-by: Yasuaki Torimaru <yasuakitorimaru@gmail.com>
---
drivers/net/wireless/microchip/wilc1000/hif.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c
index f354b11cb919..944b2a812b63 100644
--- a/drivers/net/wireless/microchip/wilc1000/hif.c
+++ b/drivers/net/wireless/microchip/wilc1000/hif.c
@@ -163,7 +163,7 @@ int wilc_scan(struct wilc_vif *vif, u8 scan_source,
u32 index = 0;
u32 i, scan_timeout;
u8 *buffer;
- u8 valuesize = 0;
+ u32 valuesize = 0;
u8 *search_ssid_vals = NULL;
const u8 ch_list_len = request->n_channels;
struct host_if_drv *hif_drv = vif->hif_drv;
--
2.50.1
^ permalink raw reply related
* [PATCH iwlwifi-fixes 3/3] wifi: iwlwifi: mld: correctly set wifi generation data
From: Miri Korenblit @ 2026-03-24 9:33 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260324113316.4c56b8bac533.I6e656d8cc30bb82c96aabadedd62bd67f4c46bf9@changeid>
From: Johannes Berg <johannes.berg@intel.com>
In each MAC context, the firmware expects the wifi generation
data, i.e. whether or not HE/EHT (and in the future UHR) is
enabled on that MAC.
However, this is currently handled wrong in two ways:
- EHT is only enabled when the interface is also an MLD, but
we currently allow (despite the spec) connecting with EHT
but without MLO.
- when HE or EHT are used by TDLS peers, the firmware needs
to have them enabled regardless of the AP
Fix this by iterating setting up the data depending on the
interface type:
- for AP, just set it according to the BSS configuration
- for monitor, set it according to HW capabilities
- otherwise, particularly for client, iterate all stations
and then their links on the interface in question and set
according to their capabilities, this handles the AP and
TDLS peers. Re-calculate this whenever a TDLS station is
marked associated or removed so that it's kept updated,
for the AP it's already updated on assoc/disassoc.
Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260319110722.404713b22177.Ic972b5e557d011a5438f8f97c1e793cc829e2ea9@changeid
---
.../net/wireless/intel/iwlwifi/mld/iface.c | 101 ++++++++++++------
.../net/wireless/intel/iwlwifi/mld/mac80211.c | 19 ++++
2 files changed, 88 insertions(+), 32 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 29df747c8938..9215fc7e2eca 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -111,14 +111,75 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,
IEEE80211_HE_MAC_CAP2_ACK_EN);
}
-static void iwl_mld_set_he_support(struct iwl_mld *mld,
- struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd)
+struct iwl_mld_mac_wifi_gen_sta_iter_data {
+ struct ieee80211_vif *vif;
+ struct iwl_mac_wifi_gen_support *support;
+};
+
+static void iwl_mld_mac_wifi_gen_sta_iter(void *_data,
+ struct ieee80211_sta *sta)
{
- if (vif->type == NL80211_IFTYPE_AP)
- cmd->wifi_gen.he_ap_support = 1;
- else
- cmd->wifi_gen.he_support = 1;
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_mac_wifi_gen_sta_iter_data *data = _data;
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ if (mld_sta->vif != data->vif)
+ return;
+
+ for_each_sta_active_link(data->vif, sta, link_sta, link_id) {
+ if (link_sta->he_cap.has_he)
+ data->support->he_support = 1;
+ if (link_sta->eht_cap.has_eht)
+ data->support->eht_support = 1;
+ }
+}
+
+static void iwl_mld_set_wifi_gen(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_wifi_gen_support *support)
+{
+ struct iwl_mld_mac_wifi_gen_sta_iter_data sta_iter_data = {
+ .vif = vif,
+ .support = support,
+ };
+ struct ieee80211_bss_conf *link_conf;
+ unsigned int link_id;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_MONITOR:
+ /* for sniffer, set to HW capabilities */
+ support->he_support = 1;
+ support->eht_support = mld->trans->cfg->eht_supported;
+ break;
+ case NL80211_IFTYPE_AP:
+ /* for AP set according to the link configs */
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ support->he_ap_support |= link_conf->he_support;
+ support->eht_support |= link_conf->eht_support;
+ }
+ break;
+ default:
+ /*
+ * If we have MLO enabled, then the firmware needs to enable
+ * address translation for the station(s) we add. That depends
+ * on having EHT enabled in firmware, which in turn depends on
+ * mac80211 in the iteration below.
+ * However, mac80211 doesn't enable capabilities on the AP STA
+ * until it has parsed the association response successfully,
+ * so set EHT (and HE as a pre-requisite for EHT) when the vif
+ * is an MLD.
+ */
+ if (ieee80211_vif_is_mld(vif)) {
+ support->he_support = 1;
+ support->eht_support = 1;
+ }
+
+ ieee80211_iterate_stations_mtx(mld->hw,
+ iwl_mld_mac_wifi_gen_sta_iter,
+ &sta_iter_data);
+ break;
+ }
}
/* fill the common part for all interface types */
@@ -128,8 +189,6 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
u32 action)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
- struct ieee80211_bss_conf *link_conf;
- unsigned int link_id;
lockdep_assert_wiphy(mld->wiphy);
@@ -147,29 +206,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
cmd->nic_not_ack_enabled =
cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif));
- /* If we have MLO enabled, then the firmware needs to enable
- * address translation for the station(s) we add. That depends
- * on having EHT enabled in firmware, which in turn depends on
- * mac80211 in the code below.
- * However, mac80211 doesn't enable HE/EHT until it has parsed
- * the association response successfully, so just skip all that
- * and enable both when we have MLO.
- */
- if (ieee80211_vif_is_mld(vif)) {
- iwl_mld_set_he_support(mld, vif, cmd);
- cmd->wifi_gen.eht_support = 1;
- return;
- }
-
- for_each_vif_active_link(vif, link_conf, link_id) {
- if (!link_conf->he_support)
- continue;
-
- iwl_mld_set_he_support(mld, vif, cmd);
-
- /* EHT, if supported, was already set above */
- break;
- }
+ iwl_mld_set_wifi_gen(mld, vif, &cmd->wifi_gen);
}
static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 0c53d6bd9651..71a9a72c9ac0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1761,6 +1761,16 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
if (vif->type == NL80211_IFTYPE_STATION)
iwl_mld_link_set_2mhz_block(mld, vif, sta);
+
+ if (sta->tdls) {
+ /*
+ * update MAC since wifi generation flags may change,
+ * we also update MAC on association to the AP via the
+ * vif assoc change
+ */
+ iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+ }
+
/* Now the link_sta's capabilities are set, update the FW */
iwl_mld_config_tlc(mld, vif, sta);
@@ -1873,6 +1883,15 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
/* just removed last TDLS STA, so enable PM */
iwl_mld_update_mac_power(mld, vif, false);
}
+
+ if (sta->tdls) {
+ /*
+ * update MAC since wifi generation flags may change,
+ * we also update MAC on disassociation to the AP via
+ * the vif assoc change
+ */
+ iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+ }
} else {
return -EINVAL;
}
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-fixes 2/3] wifi: iwlwifi: mvm: don't send a 6E related command when not supported
From: Miri Korenblit @ 2026-03-24 9:33 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach, Johannes Berg
In-Reply-To: <20260324113316.4c56b8bac533.I6e656d8cc30bb82c96aabadedd62bd67f4c46bf9@changeid>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
MCC_ALLOWED_AP_TYPE_CMD is related to 6E support. Do not send it if the
device doesn't support 6E.
Apparently, the firmware is mistakenly advertising support for this
command even on AX201 which does not support 6E and then the firmware
crashes.
Fixes: 0d2fc8821a7d ("wifi: iwlwifi: nvm: parse the VLP/AFC bit from regulatory")
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220804
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 43cf94c9a36b..6cc78661116e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -470,7 +470,8 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
};
- if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210 ||
+ !mvm->trans->cfg->uhb_supported) {
IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
return;
}
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-fixes 1/3] wifi: iwlwifi: mld: Fix MLO scan timing
From: Miri Korenblit @ 2026-03-24 9:33 UTC (permalink / raw)
To: linux-wireless; +Cc: Pagadala Yesu Anjaneyulu
From: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Calculate MLO scan start time based on actual
scan start notification from firmware instead of recording
time when scan command is sent.
Currently, MLO scan start time was captured immediately
after sending the scan command to firmware. However, the
actual scan start time may differ due to the FW being busy
with a previous scan.
In that case, the link selection code will think that the MLO
scan is too old, and will warn.
To fix it, Implement start scan notification handling to
capture the precise moment when firmware begins the scan
operation.
Fixes: 9324731b9985 ("wifi: iwlwifi: mld: avoid selecting bad links")
Signed-off-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/fw/api/commands.h | 5 ++++
.../net/wireless/intel/iwlwifi/fw/api/scan.h | 10 +++++++
drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 +
drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 4 +--
.../net/wireless/intel/iwlwifi/mld/notif.c | 5 ++++
drivers/net/wireless/intel/iwlwifi/mld/scan.c | 30 +++++++++++++++++--
drivers/net/wireless/intel/iwlwifi/mld/scan.h | 9 ++++--
7 files changed, 56 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
index 8d64a271bb94..36159a769916 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
@@ -296,6 +296,11 @@ enum iwl_legacy_cmds {
*/
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
+ /**
+ * @SCAN_START_NOTIFICATION_UMAC: uses &struct iwl_umac_scan_start
+ */
+ SCAN_START_NOTIFICATION_UMAC = 0xb2,
+
/**
* @MATCH_FOUND_NOTIFICATION: scan match found
*/
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index 60f0a4924ddf..46fcc32608e3 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
@@ -1156,6 +1156,16 @@ enum iwl_umac_scan_abort_status {
IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND,
};
+/**
+ * struct iwl_umac_scan_start - scan start notification
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @reserved: for future use
+ */
+struct iwl_umac_scan_start {
+ __le32 uid;
+ __le32 reserved;
+} __packed; /* SCAN_START_UMAC_API_S_VER_1 */
+
/**
* struct iwl_umac_scan_complete - scan complete notification
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
index 495e9d8f3af6..9af79297c3b6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -171,6 +171,7 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
HCMD_NAME(MAC_PM_POWER_TABLE),
HCMD_NAME(MFUART_LOAD_NOTIFICATION),
+ HCMD_NAME(SCAN_START_NOTIFICATION_UMAC),
HCMD_NAME(RSS_CONFIG_CMD),
HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),
HCMD_NAME(REPLY_RX_MPDU_CMD),
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index f842f5183223..fbff5915f7fd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -739,7 +739,7 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld,
/* Ignore any BSS that was not seen in the last MLO scan */
if (ktime_before(link_conf->bss->ts_boottime,
- mld->scan.last_mlo_scan_time))
+ mld->scan.last_mlo_scan_start_time))
continue;
data[n_data].link_id = link_id;
@@ -945,7 +945,7 @@ static void _iwl_mld_select_links(struct iwl_mld *mld,
if (!mld_vif->authorized || hweight16(usable_links) <= 1)
return;
- if (WARN(ktime_before(mld->scan.last_mlo_scan_time,
+ if (WARN(ktime_before(mld->scan.last_mlo_scan_start_time,
ktime_sub_ns(ktime_get_boottime_ns(),
5ULL * NSEC_PER_SEC)),
"Last MLO scan was too long ago, can't select links\n"))
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
index 240526d8b632..9c88a8579a75 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -287,6 +287,8 @@ static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld,
* at least enough bytes to cover the structure listed in the CMD_VER_ENTRY.
*/
+CMD_VERSIONS(scan_start_notif,
+ CMD_VER_ENTRY(1, iwl_umac_scan_start))
CMD_VERSIONS(scan_complete_notif,
CMD_VER_ENTRY(1, iwl_umac_scan_complete))
CMD_VERSIONS(scan_iter_complete_notif,
@@ -360,6 +362,7 @@ DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif,
link_id)
DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity)
DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
+DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid)
DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
mac_id)
DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
@@ -402,6 +405,8 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
RX_HANDLER_SYNC)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif,
RX_HANDLER_SYNC)
+ RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_START_NOTIFICATION_UMAC,
+ scan_start_notif)
RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC,
scan_complete_notif)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
index a1a4cf3ab3d3..abd4281b4b0e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -473,6 +473,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld,
params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN;
+ if (scan_status == IWL_MLD_SCAN_INT_MLO)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTF_START;
+
if (params->enable_6ghz_passive)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN;
@@ -1817,9 +1820,6 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld,
ret = _iwl_mld_single_scan_start(mld, vif, req, &ies,
IWL_MLD_SCAN_INT_MLO);
- if (!ret)
- mld->scan.last_mlo_scan_time = ktime_get_boottime_ns();
-
IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret);
}
@@ -1904,6 +1904,30 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
ieee80211_sched_scan_results(mld->hw);
}
+void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+ u32 uid = le32_to_cpu(notif->uid);
+
+ if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
+ "FW reports out-of-range scan UID %d\n", uid))
+ return;
+
+ if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status),
+ "FW reports scan UID %d we didn't trigger\n", uid))
+ return;
+
+ IWL_DEBUG_SCAN(mld, "Scan started: uid=%u type=%u\n", uid,
+ mld->scan.uid_status[uid]);
+ if (IWL_FW_CHECK(mld, mld->scan.uid_status[uid] != IWL_MLD_SCAN_INT_MLO,
+ "FW reports scan start notification %d we didn't trigger\n",
+ mld->scan.uid_status[uid]))
+ return;
+
+ mld->scan.last_mlo_scan_start_time = ktime_get_boottime_ns();
+}
+
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
index 69110f0cfc8e..de5620e7f463 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
@@ -27,6 +27,9 @@ int iwl_mld_sched_scan_start(struct iwl_mld *mld,
void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
+void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
@@ -114,8 +117,8 @@ enum iwl_mld_traffic_load {
* in jiffies.
* @last_start_time_jiffies: stores the last start time in jiffies
* (interface up/reset/resume).
- * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since
- * boot.
+ * @last_mlo_scan_start_time: start time of the last MLO scan in nanoseconds
+ * since boot.
*/
struct iwl_mld_scan {
/* Add here fields that need clean up on restart */
@@ -136,7 +139,7 @@ struct iwl_mld_scan {
void *cmd;
unsigned long last_6ghz_passive_jiffies;
unsigned long last_start_time_jiffies;
- u64 last_mlo_scan_time;
+ u64 last_mlo_scan_start_time;
};
/**
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2 00/18] wifi: drop redundant USB device references
From: Johan Hovold @ 2026-03-24 8:45 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless@vger.kernel.org, Jeff Johnson,
Toke Høiland-Jørgensen, Brian Norris, Francesco Dolcini,
Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Jakub Kicinski, Stanislaw Gruszka, Hin-Tak Leung,
Jes Sorensen, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
Matthias Brugger, AngeloGioacchino Del Regno, Greg Kroah-Hartman,
libertas-dev@lists.infradead.org, linux-kernel@vger.kernel.org
In-Reply-To: <923278a6deae44f4bec8bc2ede1022a1@realtek.com>
On Tue, Mar 24, 2026 at 08:40:10AM +0000, Ping-Ke Shih wrote:
> Johan Hovold <johan@kernel.org> wrote:
> > On Fri, Mar 06, 2026 at 09:51:26AM +0100, Johan Hovold wrote:
> > > Driver core holds a reference to the USB interface and its parent USB
> > > device while the interface is bound to a driver and there is no need to
> > > take additional references unless the structures are needed after
> > > disconnect.
> > >
> > > Drop redundant device references to reduce cargo culting, make it easier
> > > to spot drivers where an extra reference is needed, and reduce the risk
> > > of memory leaks when drivers fail to release them.
> >
> > > Johan Hovold (18):
> >
> > > wifi: mt76: drop redundant device reference
> > > wifi: mt76x0u: drop redundant device reference
> > > wifi: mt76x2u: drop redundant device reference
> > > wifi: mt76: mt792xu: drop redundant device reference
> > > wifi: mt7601u: drop redundant device reference
> >
> > All of these are now in linux-next except for the five Mediatek fixes.
> >
> > Could someone pick them up as well? Not sure which tree they'd usually
> > go through.
>
> wifi: rtl818x: drop redundant device reference
> wifi: rtl8xxxu: drop redundant device reference
> wifi: rtw88: fix device leak on probe failure
>
> Realtek WiFi got merged into my tree (not in linux-next yet), and I plan to
> send pull-request to wireless tree before 7.1 merge window.
Yes, sorry, I forgot to mention that. I got a message about these having
been picked up so I didn't include them in the list above. Thanks.
Johan
^ permalink raw reply
* RE: [PATCH v2 00/18] wifi: drop redundant USB device references
From: Ping-Ke Shih @ 2026-03-24 8:40 UTC (permalink / raw)
To: Johan Hovold, linux-wireless@vger.kernel.org
Cc: Jeff Johnson, Toke Høiland-Jørgensen, Brian Norris,
Francesco Dolcini, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Jakub Kicinski, Stanislaw Gruszka,
Hin-Tak Leung, Jes Sorensen, Nicolas Ferre, Alexandre Belloni,
Claudiu Beznea, Matthias Brugger, AngeloGioacchino Del Regno,
Greg Kroah-Hartman, libertas-dev@lists.infradead.org,
linux-kernel@vger.kernel.org
In-Reply-To: <acJL3c5J6sT59sjx@hovoldconsulting.com>
Hi Johan,
Johan Hovold <johan@kernel.org> wrote:
> On Fri, Mar 06, 2026 at 09:51:26AM +0100, Johan Hovold wrote:
> > Driver core holds a reference to the USB interface and its parent USB
> > device while the interface is bound to a driver and there is no need to
> > take additional references unless the structures are needed after
> > disconnect.
> >
> > Drop redundant device references to reduce cargo culting, make it easier
> > to spot drivers where an extra reference is needed, and reduce the risk
> > of memory leaks when drivers fail to release them.
>
> > Johan Hovold (18):
>
> > wifi: mt76: drop redundant device reference
> > wifi: mt76x0u: drop redundant device reference
> > wifi: mt76x2u: drop redundant device reference
> > wifi: mt76: mt792xu: drop redundant device reference
> > wifi: mt7601u: drop redundant device reference
>
> All of these are now in linux-next except for the five Mediatek fixes.
>
> Could someone pick them up as well? Not sure which tree they'd usually
> go through.
wifi: rtl818x: drop redundant device reference
wifi: rtl8xxxu: drop redundant device reference
wifi: rtw88: fix device leak on probe failure
Realtek WiFi got merged into my tree (not in linux-next yet), and I plan to
send pull-request to wireless tree before 7.1 merge window.
Ping-Ke
^ permalink raw reply
* Re: [PATCH v2 00/18] wifi: drop redundant USB device references
From: Johan Hovold @ 2026-03-24 8:31 UTC (permalink / raw)
To: linux-wireless
Cc: Jeff Johnson, Toke Høiland-Jørgensen, Brian Norris,
Francesco Dolcini, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Jakub Kicinski, Stanislaw Gruszka,
Hin-Tak Leung, Jes Sorensen, Ping-Ke Shih, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Matthias Brugger,
AngeloGioacchino Del Regno, Greg Kroah-Hartman, libertas-dev,
linux-kernel
In-Reply-To: <20260306085144.12064-1-johan@kernel.org>
On Fri, Mar 06, 2026 at 09:51:26AM +0100, Johan Hovold wrote:
> Driver core holds a reference to the USB interface and its parent USB
> device while the interface is bound to a driver and there is no need to
> take additional references unless the structures are needed after
> disconnect.
>
> Drop redundant device references to reduce cargo culting, make it easier
> to spot drivers where an extra reference is needed, and reduce the risk
> of memory leaks when drivers fail to release them.
> Johan Hovold (18):
> wifi: mt76: drop redundant device reference
> wifi: mt76x0u: drop redundant device reference
> wifi: mt76x2u: drop redundant device reference
> wifi: mt76: mt792xu: drop redundant device reference
> wifi: mt7601u: drop redundant device reference
All of these are now in linux-next except for the five Mediatek fixes.
Could someone pick them up as well? Not sure which tree they'd usually
go through.
Johan
^ permalink raw reply
* Re: [PATCH wireless-next] wifi: mac80211: fix AEAD functions not handling errors
From: Johannes Berg @ 2026-03-24 6:36 UTC (permalink / raw)
To: Miri Korenblit, linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260323231539.aa9f1db285a8.Icf2051ed1acac174b85b99158cc2b0757310765a@changeid>
On Mon, 2026-03-23 at 23:15 +0200, Miri Korenblit wrote:
> From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
>
> This was done for CMAC but not for AEAD.
> This was spotted by Coverity.
I'm not going to apply this, given that this is all refactored (and the
bug removed) in -next:
https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/commit/?h=libcrypto-next&id=5e07ce466356d9833757b2d6c0380b6ee7b11a77
johannes
^ permalink raw reply
* [PATCH v2 rtw-next 7/7] wifi: rtw89: 8922d: add set channel of RF part
From: Ping-Ke Shih @ 2026-03-24 6:20 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20260324062049.52266-1-pkshih@realtek.com>
The set channel of RF part is to configure channel and bandwidth on a
register. The function to encode channel and bandwidth into register
value will be implemented by coming patch.
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
v2: no change
---
drivers/net/wireless/realtek/rtw89/rtw8922d.c | 1 +
.../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 33 +++++++++++++++++++
.../net/wireless/realtek/rtw89/rtw8922d_rfk.h | 14 ++++++++
3 files changed, 48 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index cae92e2abd85..1b5fc6c9ea85 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -8,6 +8,7 @@
#include "phy.h"
#include "reg.h"
#include "rtw8922d.h"
+#include "rtw8922d_rfk.h"
#include "util.h"
#define RTW8922D_FW_FORMAT_MAX 0
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
new file mode 100644
index 000000000000..6b35d196cb81
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2026 Realtek Corporation
+ */
+
+#include "phy.h"
+#include "reg.h"
+#include "rtw8922d.h"
+#include "rtw8922d_rfk.h"
+
+static
+void rtw8922d_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
+ const struct rtw89_chan *chan)
+{
+ u8 synpath;
+ u32 rf18;
+
+ synpath = rtw89_phy_get_syn_sel(rtwdev, phy);
+ rf18 = rtw89_chip_chan_to_rf18_val(rtwdev, chan);
+
+ rtw89_write_rf(rtwdev, synpath, RR_RSV1, RFREG_MASK, 0x0);
+ rtw89_write_rf(rtwdev, synpath, RR_MOD, RFREG_MASK, 0x30000);
+ rtw89_write_rf(rtwdev, synpath, RR_CFGCH, RFREG_MASK, rf18);
+ fsleep(400);
+ rtw89_write_rf(rtwdev, synpath, RR_RSV1, RFREG_MASK, 0x1);
+ rtw89_write_rf(rtwdev, synpath, RR_CFGCH_V1, RFREG_MASK, rf18);
+}
+
+void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ rtw8922d_ctl_band_ch_bw(rtwdev, phy_idx, chan);
+}
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h
new file mode 100644
index 000000000000..03af1f0497ac
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2026 Realtek Corporation
+ */
+
+#ifndef __RTW89_8922D_RFK_H__
+#define __RTW89_8922D_RFK_H__
+
+#include "core.h"
+
+void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx);
+
+#endif
--
2.25.1
^ permalink raw reply related
* [PATCH v2 rtw-next 6/7] wifi: rtw89: 8922d: add set channel of BB part
From: Ping-Ke Shih @ 2026-03-24 6:20 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20260324062049.52266-1-pkshih@realtek.com>
The set channel of BB part is the main part, which according to channel
and bandwidth to configure CCK SCO, RX gain of LNA and TIA programmed
in efuse for CCK and OFDM rate, and spur elimination of CSI and NBI tones.
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
v2: correct NBI register
---
drivers/net/wireless/realtek/rtw89/core.h | 2 +
drivers/net/wireless/realtek/rtw89/reg.h | 64 +-
drivers/net/wireless/realtek/rtw89/rtw8922d.c | 820 ++++++++++++++++++
3 files changed, 885 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index cde46ed21d32..696a1d92d62b 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5933,6 +5933,8 @@ struct rtw89_phy_efuse_gain {
s8 offset2[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */
s8 offset_base[RTW89_PHY_NUM]; /* S(8, 4) */
s8 rssi_base[RTW89_PHY_NUM]; /* S(8, 4) */
+ s8 ref_gain_base[RTW89_PHY_NUM]; /* S(8, 2) */
+ s8 cck_rpl_base[RTW89_PHY_NUM]; /* S(8, 0) */
s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */
};
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 2195f576facc..5d284f310069 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -8775,6 +8775,7 @@
#define B_P0_HW_ANTSW_DIS_BY_GNT_BT BIT(12)
#define B_P0_TRSW_TX_EXTEND GENMASK(3, 0)
#define R_MAC_PIN_SEL 0x0734
+#define R_MAC_PIN_SEL_BE4 0x20734
#define B_CH_IDX_SEG0 GENMASK(23, 16)
#define R_PLCP_HISTOGRAM 0x0738
#define R_PLCP_HISTOGRAM_BE_V1 0x20738
@@ -10461,6 +10462,13 @@
#define B_SW_SI_DATA_DAT_BE4 GENMASK(19, 0)
#define R_SW_SI_READ_ADDR_BE4 0x20378
#define B_SW_SI_READ_ADDR_BE4 GENMASK(10, 0)
+#define R_RXBW67_BE4 0x2040C
+#define B_RXBW6_BE4 GENMASK(22, 20)
+#define B_RXBW7_BE4 GENMASK(25, 23)
+#define R_RXBW_BE4 0x20410
+#define B_RXBW_BE4 GENMASK(29, 27)
+#define R_ENABLE_CCK0_BE4 0x20700
+#define B_ENABLE_CCK0_BE4 BIT(5)
#define R_EDCCA_RPT_SEL_BE4 0x20780
#define R_EDCCA_RPT_SEL_BE4_C1 0x21780
#define B_EDCCA_RPT_SEL_BE4_MSK 0xE0000
@@ -10486,21 +10494,75 @@
#define B_IFS_T3_HIS_BE4 GENMASK(15, 0)
#define B_IFS_T4_HIS_BE4 GENMASK(31, 16)
+#define R_TXPWR_RSTB0_BE4 0x2250C
+#define B_TXPWR_RSTB0_BE4 BIT(16)
+#define R_TXPWR_RSTB1_BE4 0x2260C
+#define B_TXPWR_RSTB1_BE4 BIT(16)
+
+#define R_OFDM_OFST_P0_BE4 0x240C8
+#define B_OFDM_OFST_P0_BE4 GENMASK(31, 24)
#define R_PATH0_RXIDX_INIT_BE4 0x24108
#define B_PATH0_RXIDX_INIT_BE4 GENMASK(29, 25)
#define R_PATH0_LNA_INIT_BE4 0x24158
#define B_PATH0_LNA_INIT_IDX_BE4 GENMASK(14, 12)
+#define R_BAND_SEL0_BE4 0x24160
+#define B_BAND_SEL0_BE4 BIT(26)
#define R_PATH0_TIA_INIT_BE4 0x24168
#define B_PATH0_TIA_INIT_IDX_BE4 BIT(18)
+#define R_OFDM_RPL_BIAS_P0_BE4 0x2420C
+#define B_OFDM_RPL_BIAS_P0_BE4 GENMASK(11, 2)
+#define R_OFDM_OFST_P1_BE4 0x244C8
+#define B_OFDM_OFST_P1_BE4 GENMASK(31, 24)
#define R_PATH1_RXIDX_INIT_BE4 0x24508
#define B_PATH1_RXIDX_INIT_BE4 GENMASK(29, 25)
#define R_PATH1_LNA_INIT_BE4 0x24558
#define B_PATH1_LNA_INIT_IDX_BE4 GENMASK(14, 12)
+#define R_BAND_SEL1_BE4 0x24560
+#define B_BAND_SEL1_BE4 BIT(26)
#define R_PATH1_TIA_INIT_BE4 0x24568
#define B_PATH1_TIA_INIT_IDX_BE4 BIT(18)
+#define R_OFDM_RPL_BIAS_P1_BE4 0x2460C
+#define B_OFDM_RPL_BIAS_P1_BE4 GENMASK(11, 2)
#define R_TX_CFR_MANUAL_EN_BE4 0x2483C
#define B_TX_CFR_MANUAL_EN_BE4_M BIT(30)
-
+#define R_PCOEFF0_BE4 0x24880
+#define B_PCOEFF01_BE4 GENMASK(23, 0)
+#define R_PCOEFF2_BE4 0x24884
+#define B_PCOEFF23_BE4 GENMASK(23, 0)
+#define R_PCOEFF4_BE4 0x24888
+#define B_PCOEFF45_BE4 GENMASK(23, 0)
+#define R_PCOEFF6_BE4 0x2488C
+#define B_PCOEFF67_BE4 GENMASK(23, 0)
+#define R_PCOEFF8_BE4 0x24890
+#define B_PCOEFF89_BE4 GENMASK(23, 0)
+#define R_PCOEFF10_BE4 0x24894
+#define B_PCOEFF10_BE4 GENMASK(23, 0)
+#define R_PCOEFF12_BE4 0x24898
+#define B_PCOEFF12_BE4 GENMASK(23, 0)
+#define R_PCOEFF14_BE4 0x2489C
+#define B_PCOEFF14_BE4 GENMASK(23, 0)
+#define R_BW_BE4 0x24EE4
+#define B_BW_BE4 GENMASK(6, 4)
+#define B_PRISB_BE4 GENMASK(3, 0)
+#define R_FC0_BE4 0x24EE8
+#define B_FC0_BE4 GENMASK(12, 0)
+#define R_FC0_INV_BE4 0x24EF4
+#define B_FC0_INV_BE4 GENMASK(15, 0)
+
+#define R_CCK_RPL_OFST_BE4 0x26084
+#define B_CCK_RPL_OFST_BE4 GENMASK(7, 0)
+#define R_BK_FC0_INV_BE4 0x2608C
+#define B_BK_FC0_INV_BE4 GENMASK(18, 0)
+#define R_CCK_FC0_INV_BE4 0x26090
+#define B_CCK_FC0_INV_BE4 GENMASK(18, 0)
+#define R_GAIN_BIAS_BE4 0x260A0
+#define B_GAIN_BIAS_BW20_BE4 GENMASK(11, 6)
+#define B_GAIN_BIAS_BW40_BE4 GENMASK(17, 12)
+#define R_AWGN_DET_BE4 0x2668C
+#define B_AWGN_DET_BE4 GENMASK(17, 9)
+#define R_CSI_WGT_BE4 0x26770
+#define B_CSI_WGT_EN_BE4 BIT(0)
+#define B_CSI_WGT_IDX_BE4 GENMASK(31, 20)
#define R_CHINFO_OPT_BE4 0x267C8
#define B_CHINFO_OPT_BE4 GENMASK(14, 13)
#define R_CHINFO_NX_BE4 0x267D0
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index a341734ef54d..cae92e2abd85 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -8,6 +8,7 @@
#include "phy.h"
#include "reg.h"
#include "rtw8922d.h"
+#include "util.h"
#define RTW8922D_FW_FORMAT_MAX 0
#define RTW8922D_FW_BASENAME "rtw89/rtw8922d_fw"
@@ -927,6 +928,825 @@ static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev,
rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_TB_T1_DOT05US_MASK, sifs);
}
+static const u32 rtw8922d_sco_barker_threshold[14] = {
+ 0x1fe4f, 0x1ff5e, 0x2006c, 0x2017b, 0x2028a, 0x20399, 0x204a8, 0x205b6,
+ 0x206c5, 0x207d4, 0x208e3, 0x209f2, 0x20b00, 0x20d8a
+};
+
+static const u32 rtw8922d_sco_cck_threshold[14] = {
+ 0x2bdac, 0x2bf21, 0x2c095, 0x2c209, 0x2c37e, 0x2c4f2, 0x2c666, 0x2c7db,
+ 0x2c94f, 0x2cac3, 0x2cc38, 0x2cdac, 0x2cf21, 0x2d29e
+};
+
+static int rtw8922d_ctrl_sco_cck(struct rtw89_dev *rtwdev,
+ u8 primary_ch, enum rtw89_bandwidth bw,
+ enum rtw89_phy_idx phy_idx)
+{
+ u8 ch_element;
+
+ if (primary_ch >= 14)
+ return -EINVAL;
+
+ ch_element = primary_ch - 1;
+
+ rtw89_phy_write32_idx(rtwdev, R_BK_FC0_INV_BE4, B_BK_FC0_INV_BE4,
+ rtw8922d_sco_barker_threshold[ch_element],
+ phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_CCK_FC0_INV_BE4, B_CCK_FC0_INV_BE4,
+ rtw8922d_sco_cck_threshold[ch_element],
+ phy_idx);
+
+ return 0;
+}
+
+static void rtw8922d_ctrl_ch_core(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ u16 central_freq = chan->freq;
+ u16 sco;
+
+ if (chan->band_type == RTW89_BAND_2G) {
+ rtw89_phy_write32_idx(rtwdev, R_BAND_SEL0_BE4, B_BAND_SEL0_BE4,
+ 1, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_BAND_SEL1_BE4, B_BAND_SEL1_BE4,
+ 1, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_ENABLE_CCK0_BE4, B_ENABLE_CCK0_BE4,
+ 1, phy_idx);
+ } else {
+ rtw89_phy_write32_idx(rtwdev, R_BAND_SEL0_BE4, B_BAND_SEL0_BE4,
+ 0, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_BAND_SEL1_BE4, B_BAND_SEL1_BE4,
+ 0, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_ENABLE_CCK0_BE4, B_ENABLE_CCK0_BE4,
+ 0, phy_idx);
+ }
+
+ rtw89_phy_write32_idx(rtwdev, R_FC0_BE4, B_FC0_BE4, central_freq, phy_idx);
+
+ sco = phy_div((BIT(0) << 27) + (central_freq / 2), central_freq);
+ rtw89_phy_write32_idx(rtwdev, R_FC0_INV_BE4, B_FC0_INV_BE4, sco, phy_idx);
+}
+
+struct rtw8922d_bb_gain {
+ u32 gain_g[BB_PATH_NUM_8922D];
+ u32 gain_a[BB_PATH_NUM_8922D];
+ u32 gain_g_mask;
+ u32 gain_a_mask;
+};
+
+static const struct rtw89_reg_def rpl_comp_bw160[RTW89_BW20_SC_160M] = {
+ { .addr = 0x241E8, .mask = 0xFF00},
+ { .addr = 0x241E8, .mask = 0xFF0000},
+ { .addr = 0x241E8, .mask = 0xFF000000},
+ { .addr = 0x241EC, .mask = 0xFF},
+ { .addr = 0x241EC, .mask = 0xFF00},
+ { .addr = 0x241EC, .mask = 0xFF0000},
+ { .addr = 0x241EC, .mask = 0xFF000000},
+ { .addr = 0x241F0, .mask = 0xFF}
+};
+
+static const struct rtw89_reg_def rpl_comp_bw80[RTW89_BW20_SC_80M] = {
+ { .addr = 0x241F4, .mask = 0xFF},
+ { .addr = 0x241F4, .mask = 0xFF00},
+ { .addr = 0x241F4, .mask = 0xFF0000},
+ { .addr = 0x241F4, .mask = 0xFF000000}
+};
+
+static const struct rtw89_reg_def rpl_comp_bw40[RTW89_BW20_SC_40M] = {
+ { .addr = 0x241F0, .mask = 0xFF0000},
+ { .addr = 0x241F0, .mask = 0xFF000000}
+};
+
+static const struct rtw89_reg_def rpl_comp_bw20[RTW89_BW20_SC_20M] = {
+ { .addr = 0x241F0, .mask = 0xFF00}
+};
+
+static const struct rtw8922d_bb_gain bb_gain_lna[LNA_GAIN_NUM] = {
+ { .gain_g = {0x2409C, 0x2449C}, .gain_a = {0x2406C, 0x2446C},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF},
+ { .gain_g = {0x2409C, 0x2449C}, .gain_a = {0x2406C, 0x2446C},
+ .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF0000},
+ { .gain_g = {0x240A0, 0x244A0}, .gain_a = {0x24070, 0x24470},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF},
+ { .gain_g = {0x240A0, 0x244A0}, .gain_a = {0x24070, 0x24470},
+ .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF0000},
+ { .gain_g = {0x240A4, 0x244A4}, .gain_a = {0x24074, 0x24474},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF},
+ { .gain_g = {0x240A4, 0x244A4}, .gain_a = {0x24074, 0x24474},
+ .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF0000},
+ { .gain_g = {0x240A8, 0x244A8}, .gain_a = {0x24078, 0x24478},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF},
+};
+
+static const struct rtw8922d_bb_gain bb_gain_tia[TIA_GAIN_NUM] = {
+ { .gain_g = {0x24054, 0x24454}, .gain_a = {0x24054, 0x24454},
+ .gain_g_mask = 0x7FC0000, .gain_a_mask = 0x1FF},
+ { .gain_g = {0x24058, 0x24458}, .gain_a = {0x24054, 0x24454},
+ .gain_g_mask = 0x1FF, .gain_a_mask = 0x3FE00 },
+};
+
+static const struct rtw8922d_bb_gain bb_op1db_lna[LNA_GAIN_NUM] = {
+ { .gain_g = {0x240AC, 0x244AC}, .gain_a = {0x24078, 0x24478},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF000000},
+ { .gain_g = {0x240AC, 0x244AC}, .gain_a = {0x2407C, 0x2447C},
+ .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF},
+ { .gain_g = {0x240AC, 0x244AC}, .gain_a = {0x2407C, 0x2447C},
+ .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF00},
+ { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x2407C, 0x2447C},
+ .gain_g_mask = 0xFF, .gain_a_mask = 0xFF0000},
+ { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x2407C, 0x2447C},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF000000},
+ { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x24080, 0x24480},
+ .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF},
+ { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x24080, 0x24480},
+ .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF00},
+};
+
+static const struct rtw8922d_bb_gain bb_op1db_tia_lna[TIA_LNA_OP1DB_NUM] = {
+ { .gain_g = {0x240B4, 0x244B4}, .gain_a = {0x24080, 0x24480},
+ .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF000000},
+ { .gain_g = {0x240B4, 0x244B4}, .gain_a = {0x24084, 0x24484},
+ .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF},
+ { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24084, 0x24484},
+ .gain_g_mask = 0xFF, .gain_a_mask = 0xFF00},
+ { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24084, 0x24484},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF0000},
+ { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24084, 0x24484},
+ .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF000000},
+ { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24088, 0x24488},
+ .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF},
+ { .gain_g = {0x240BC, 0x244BC}, .gain_a = {0x24088, 0x24488},
+ .gain_g_mask = 0xFF, .gain_a_mask = 0xFF00},
+ { .gain_g = {0x240BC, 0x244BC}, .gain_a = {0x24088, 0x24488},
+ .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF0000},
+};
+
+static void rtw8922d_set_rpl_gain(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be;
+ u8 gain_band = rtw89_subband_to_gain_band_be(chan->subband_type);
+ u32 reg_path_ofst = 0;
+ u32 mask;
+ s32 val;
+ u32 reg;
+ int i;
+
+ if (path == RF_PATH_B)
+ reg_path_ofst = 0x400;
+
+ for (i = 0; i < RTW89_BW20_SC_160M; i++) {
+ reg = rpl_comp_bw160[i].addr | reg_path_ofst;
+ mask = rpl_comp_bw160[i].mask;
+ val = gain->rpl_ofst_160[gain_band][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+
+ for (i = 0; i < RTW89_BW20_SC_80M; i++) {
+ reg = rpl_comp_bw80[i].addr | reg_path_ofst;
+ mask = rpl_comp_bw80[i].mask;
+ val = gain->rpl_ofst_80[gain_band][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+
+ for (i = 0; i < RTW89_BW20_SC_40M; i++) {
+ reg = rpl_comp_bw40[i].addr | reg_path_ofst;
+ mask = rpl_comp_bw40[i].mask;
+ val = gain->rpl_ofst_40[gain_band][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+
+ for (i = 0; i < RTW89_BW20_SC_20M; i++) {
+ reg = rpl_comp_bw20[i].addr | reg_path_ofst;
+ mask = rpl_comp_bw20[i].mask;
+ val = gain->rpl_ofst_20[gain_band][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+}
+
+static void rtw8922d_set_lna_tia_gain(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be;
+ u8 gain_band = rtw89_subband_to_gain_band_be(chan->subband_type);
+ enum rtw89_phy_bb_bw_be bw_type;
+ u32 mask;
+ s32 val;
+ u32 reg;
+ int i;
+
+ bw_type = chan->band_width <= RTW89_CHANNEL_WIDTH_40 ?
+ RTW89_BB_BW_20_40 : RTW89_BB_BW_80_160_320;
+
+ for (i = 0; i < LNA_GAIN_NUM; i++) {
+ if (chan->band_type == RTW89_BAND_2G) {
+ reg = bb_gain_lna[i].gain_g[path];
+ mask = bb_gain_lna[i].gain_g_mask;
+ } else {
+ reg = bb_gain_lna[i].gain_a[path];
+ mask = bb_gain_lna[i].gain_a_mask;
+ }
+ val = gain->lna_gain[gain_band][bw_type][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+
+ for (i = 0; i < TIA_GAIN_NUM; i++) {
+ if (chan->band_type == RTW89_BAND_2G) {
+ reg = bb_gain_tia[i].gain_g[path];
+ mask = bb_gain_tia[i].gain_g_mask;
+ } else {
+ reg = bb_gain_tia[i].gain_a[path];
+ mask = bb_gain_tia[i].gain_a_mask;
+ }
+ val = gain->tia_gain[gain_band][bw_type][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+}
+
+static void rtw8922d_set_op1db(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be;
+ u8 gain_band = rtw89_subband_to_gain_band_be(chan->subband_type);
+ enum rtw89_phy_bb_bw_be bw_type;
+ u32 mask;
+ s32 val;
+ u32 reg;
+ int i;
+
+ bw_type = chan->band_width <= RTW89_CHANNEL_WIDTH_40 ?
+ RTW89_BB_BW_20_40 : RTW89_BB_BW_80_160_320;
+
+ for (i = 0; i < LNA_GAIN_NUM; i++) {
+ if (chan->band_type == RTW89_BAND_2G) {
+ reg = bb_op1db_lna[i].gain_g[path];
+ mask = bb_op1db_lna[i].gain_g_mask;
+ } else {
+ reg = bb_op1db_lna[i].gain_a[path];
+ mask = bb_op1db_lna[i].gain_a_mask;
+ }
+ val = gain->lna_op1db[gain_band][bw_type][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+
+ for (i = 0; i < TIA_LNA_OP1DB_NUM; i++) {
+ if (chan->band_type == RTW89_BAND_2G) {
+ reg = bb_op1db_tia_lna[i].gain_g[path];
+ mask = bb_op1db_tia_lna[i].gain_g_mask;
+ } else {
+ reg = bb_op1db_tia_lna[i].gain_a[path];
+ mask = bb_op1db_tia_lna[i].gain_a_mask;
+ }
+ val = gain->tia_lna_op1db[gain_band][bw_type][path][i];
+ rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx);
+ }
+}
+
+static void rtw8922d_set_gain(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ rtw8922d_set_rpl_gain(rtwdev, chan, path, phy_idx);
+ rtw8922d_set_lna_tia_gain(rtwdev, chan, path, phy_idx);
+ rtw8922d_set_op1db(rtwdev, chan, path, phy_idx);
+}
+
+static s8 rtw8922d_get_rx_gain_by_chan(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path, bool is_cck)
+{
+ struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain;
+ enum rtw89_gain_offset band;
+ u8 fc_ch = chan->channel;
+ s8 normal_efuse = 0;
+
+ if (path > RF_PATH_B)
+ return 0;
+
+ if (is_cck) {
+ if (fc_ch >= 1 && fc_ch <= 7)
+ return gain->offset[path][RTW89_GAIN_OFFSET_2G_CCK];
+ else if (fc_ch >= 8 && fc_ch <= 14)
+ return gain->offset2[path][RTW89_GAIN_OFFSET_2G_CCK];
+
+ return 0;
+ }
+
+ band = rtw89_subband_to_gain_offset_band_of_ofdm(chan->subband_type);
+
+ if (band == RTW89_GAIN_OFFSET_2G_OFDM) {
+ if (fc_ch >= 1 && fc_ch <= 7)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 8 && fc_ch <= 14)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_5G_LOW) {
+ if (fc_ch == 50)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 36 && fc_ch <= 48)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 52 && fc_ch <= 64)
+ normal_efuse = gain->offset2[path][band];
+
+ } else if (band == RTW89_GAIN_OFFSET_5G_MID) {
+ if (fc_ch == 122)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 100 && fc_ch <= 120)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 124 && fc_ch <= 144)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_5G_HIGH) {
+ if (fc_ch == 163)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 149 && fc_ch <= 161)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 165 && fc_ch <= 177)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_L0) {
+ if (fc_ch == 15)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 1 && fc_ch <= 13)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 17 && fc_ch <= 29)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_L1) {
+ if (fc_ch == 47)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 33 && fc_ch <= 45)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 49 && fc_ch <= 61)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_M0) {
+ if (fc_ch == 79)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 65 && fc_ch <= 77)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 81 && fc_ch <= 93)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_M1) {
+ if (fc_ch == 111)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 97 && fc_ch <= 109)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 113 && fc_ch <= 125)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_H0) {
+ if (fc_ch == 143)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 129 && fc_ch <= 141)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 145 && fc_ch <= 157)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_H1) {
+ if (fc_ch == 175)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 161 && fc_ch <= 173)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 177 && fc_ch <= 189)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_UH0) {
+ if (fc_ch == 207)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 193 && fc_ch <= 205)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 209 && fc_ch <= 221)
+ normal_efuse = gain->offset2[path][band];
+ } else if (band == RTW89_GAIN_OFFSET_6G_UH1) {
+ if (fc_ch == 239)
+ normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1;
+ else if (fc_ch >= 225 && fc_ch <= 237)
+ normal_efuse = gain->offset[path][band];
+ else if (fc_ch >= 241 && fc_ch <= 253)
+ normal_efuse = gain->offset2[path][band];
+ } else {
+ normal_efuse = gain->offset[path][band];
+ }
+
+ return normal_efuse;
+}
+
+static void rtw8922d_calc_rx_gain_normal_cck(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx,
+ struct rtw89_phy_calc_efuse_gain *calc)
+{
+ struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain;
+ s8 rx_gain_offset;
+
+ rx_gain_offset = -rtw8922d_get_rx_gain_by_chan(rtwdev, chan, path, true);
+
+ if (chan->band_width == RTW89_CHANNEL_WIDTH_40)
+ rx_gain_offset += (3 << 2); /* compensate RPL loss of 3dB */
+
+ calc->cck_mean_gain_bias = (rx_gain_offset & 0x3) << 1;
+ calc->cck_rpl_ofst = (rx_gain_offset >> 2) + gain->cck_rpl_base[phy_idx];
+}
+
+static void rtw8922d_set_rx_gain_normal_cck(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ struct rtw89_phy_calc_efuse_gain calc = {};
+
+ rtw8922d_calc_rx_gain_normal_cck(rtwdev, chan, path, phy_idx, &calc);
+
+ rtw89_phy_write32_idx(rtwdev, R_GAIN_BIAS_BE4, B_GAIN_BIAS_BW20_BE4,
+ calc.cck_mean_gain_bias, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_GAIN_BIAS_BE4, B_GAIN_BIAS_BW40_BE4,
+ calc.cck_mean_gain_bias, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_CCK_RPL_OFST_BE4, B_CCK_RPL_OFST_BE4,
+ calc.cck_rpl_ofst, phy_idx);
+}
+
+static void rtw8922d_calc_rx_gain_normal_ofdm(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx,
+ struct rtw89_phy_calc_efuse_gain *calc)
+{
+ struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain;
+ s8 rx_gain_offset;
+
+ rx_gain_offset = rtw8922d_get_rx_gain_by_chan(rtwdev, chan, path, false);
+ calc->rssi_ofst = (rx_gain_offset + gain->ref_gain_base[phy_idx]) & 0xff;
+}
+
+static void rtw8922d_set_rx_gain_normal_ofdm(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ static const u32 rssi_ofst_addr[2] = {R_OFDM_OFST_P0_BE4, R_OFDM_OFST_P1_BE4};
+ static const u32 rssi_ofst_addr_m[2] = {B_OFDM_OFST_P0_BE4, B_OFDM_OFST_P1_BE4};
+ static const u32 rpl_bias_comp[2] = {R_OFDM_RPL_BIAS_P0_BE4, R_OFDM_RPL_BIAS_P1_BE4};
+ static const u32 rpl_bias_comp_m[2] = {B_OFDM_RPL_BIAS_P0_BE4, B_OFDM_RPL_BIAS_P1_BE4};
+ struct rtw89_phy_calc_efuse_gain calc = {};
+
+ rtw8922d_calc_rx_gain_normal_ofdm(rtwdev, chan, path, phy_idx, &calc);
+
+ rtw89_phy_write32_idx(rtwdev, rssi_ofst_addr[path], rssi_ofst_addr_m[path],
+ calc.rssi_ofst, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, rpl_bias_comp[path], rpl_bias_comp_m[path], 0, phy_idx);
+}
+
+static void rtw8922d_set_rx_gain_normal(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain;
+
+ if (!gain->offset_valid)
+ return;
+
+ if (chan->band_type == RTW89_BAND_2G)
+ rtw8922d_set_rx_gain_normal_cck(rtwdev, chan, path, phy_idx);
+
+ rtw8922d_set_rx_gain_normal_ofdm(rtwdev, chan, path, phy_idx);
+}
+
+static void rtw8922d_set_cck_parameters(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ u8 regd = rtw89_regd_get(rtwdev, chan->band_type);
+ u8 central_ch = chan->channel;
+
+ if (central_ch == 14) {
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF0_BE4, B_PCOEFF01_BE4, 0x3b13ff, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF2_BE4, B_PCOEFF23_BE4, 0x1c42de, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF4_BE4, B_PCOEFF45_BE4, 0xfdb0ad, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF6_BE4, B_PCOEFF67_BE4, 0xf60f6e, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF8_BE4, B_PCOEFF89_BE4, 0xfd8f92, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF10_BE4, B_PCOEFF10_BE4, 0x2d011, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF12_BE4, B_PCOEFF12_BE4, 0x1c02c, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF14_BE4, B_PCOEFF14_BE4, 0xfff00a, phy_idx);
+
+ return;
+ }
+
+ if (regd == RTW89_FCC) {
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF0_BE4, B_PCOEFF01_BE4, 0x39A3BC, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF2_BE4, B_PCOEFF23_BE4, 0x2AA339, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF4_BE4, B_PCOEFF45_BE4, 0x15B202, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF6_BE4, B_PCOEFF67_BE4, 0x0550C7, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF8_BE4, B_PCOEFF89_BE4, 0xfe0009, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF10_BE4, B_PCOEFF10_BE4, 0xfd7fd3, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF12_BE4, B_PCOEFF12_BE4, 0xfeffe2, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF14_BE4, B_PCOEFF14_BE4, 0xffeff8, phy_idx);
+ } else {
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF0_BE4, B_PCOEFF01_BE4, 0x3d23ff, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF2_BE4, B_PCOEFF23_BE4, 0x29b354, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF4_BE4, B_PCOEFF45_BE4, 0xfc1c8, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF6_BE4, B_PCOEFF67_BE4, 0xfdb053, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF8_BE4, B_PCOEFF89_BE4, 0xf86f9a, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF10_BE4, B_PCOEFF10_BE4, 0xfaef92, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF12_BE4, B_PCOEFF12_BE4, 0xfe5fcc, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_PCOEFF14_BE4, B_PCOEFF14_BE4, 0xffdff5, phy_idx);
+ }
+}
+
+static void rtw8922d_ctrl_ch(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ u16 central_freq = chan->freq;
+ u8 band = chan->band_type;
+ u8 chan_idx;
+
+ if (!central_freq) {
+ rtw89_warn(rtwdev, "Invalid central_freq\n");
+ return;
+ }
+
+ rtw8922d_ctrl_ch_core(rtwdev, chan, phy_idx);
+
+ chan_idx = rtw89_encode_chan_idx(rtwdev, chan->primary_channel, band);
+ rtw89_phy_write32_idx(rtwdev, R_MAC_PIN_SEL_BE4, B_CH_IDX_SEG0, chan_idx, phy_idx);
+
+ rtw8922d_set_gain(rtwdev, chan, RF_PATH_A, phy_idx);
+ rtw8922d_set_gain(rtwdev, chan, RF_PATH_B, phy_idx);
+
+ rtw8922d_set_rx_gain_normal(rtwdev, chan, RF_PATH_A, phy_idx);
+ rtw8922d_set_rx_gain_normal(rtwdev, chan, RF_PATH_B, phy_idx);
+
+ if (band == RTW89_BAND_2G)
+ rtw8922d_set_cck_parameters(rtwdev, chan, phy_idx);
+}
+
+static void rtw8922d_ctrl_bw(struct rtw89_dev *rtwdev, u8 pri_sb, u8 bw,
+ enum rtw89_phy_idx phy_idx)
+{
+ switch (bw) {
+ default:
+ case RTW89_CHANNEL_WIDTH_20:
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x0, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, 0x0, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x2, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x2, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x2, phy_idx);
+ break;
+ case RTW89_CHANNEL_WIDTH_40:
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x1, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, pri_sb, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x3, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x3, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x3, phy_idx);
+ break;
+ case RTW89_CHANNEL_WIDTH_80:
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x2, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, pri_sb, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x4, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x4, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x4, phy_idx);
+ break;
+ case RTW89_CHANNEL_WIDTH_160:
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x3, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, pri_sb, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x5, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x5, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x5, phy_idx);
+ break;
+ }
+}
+
+static const u16 spur_nbi_a[] = {6400};
+static const u16 spur_csi[] = {6400};
+
+static u32 rtw8922d_spur_freq(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan,
+ bool nbi_or_csi, enum rtw89_rf_path path)
+{
+ static const u16 cbw[RTW89_CHANNEL_WIDTH_ORDINARY_NUM] = {
+ 20, 40, 80, 160, 320,
+ };
+ u16 freq_lower, freq_upper, freq;
+ const u16 *spur_freq;
+ int spur_freq_nr, i;
+
+ if (rtwdev->hal.aid != RTL8922D_AID7060)
+ return 0;
+
+ if (nbi_or_csi && path == RF_PATH_A) {
+ spur_freq = spur_nbi_a;
+ spur_freq_nr = ARRAY_SIZE(spur_nbi_a);
+ } else if (!nbi_or_csi) {
+ spur_freq = spur_csi;
+ spur_freq_nr = ARRAY_SIZE(spur_csi);
+ } else {
+ return 0;
+ }
+
+ if (chan->band_width >= RTW89_CHANNEL_WIDTH_ORDINARY_NUM)
+ return 0;
+
+ freq_lower = chan->freq - cbw[chan->band_width] / 2;
+ freq_upper = chan->freq + cbw[chan->band_width] / 2;
+
+ for (i = 0; i < spur_freq_nr; i++) {
+ freq = spur_freq[i];
+
+ if (freq >= freq_lower && freq <= freq_upper)
+ return freq;
+ }
+
+ return 0;
+}
+
+#define CARRIER_SPACING_312_5 312500 /* 312.5 kHz */
+#define CARRIER_SPACING_78_125 78125 /* 78.125 kHz */
+#define MAX_TONE_NUM 2048
+
+static void rtw8922d_set_csi_tone_idx(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ s32 freq_diff, csi_idx, csi_tone_idx;
+ u32 spur_freq;
+
+ spur_freq = rtw8922d_spur_freq(rtwdev, chan, false, RF_PATH_AB);
+ if (spur_freq == 0) {
+ rtw89_phy_write32_idx(rtwdev, R_CSI_WGT_BE4, B_CSI_WGT_EN_BE4,
+ 0, phy_idx);
+ return;
+ }
+
+ freq_diff = (spur_freq - chan->freq) * 1000000;
+ csi_idx = s32_div_u32_round_closest(freq_diff, CARRIER_SPACING_78_125);
+ s32_div_u32_round_down(csi_idx, MAX_TONE_NUM, &csi_tone_idx);
+
+ rtw89_phy_write32_idx(rtwdev, R_CSI_WGT_BE4, B_CSI_WGT_IDX_BE4,
+ csi_tone_idx, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, R_CSI_WGT_BE4, B_CSI_WGT_EN_BE4, 1, phy_idx);
+}
+
+static const struct rtw89_nbi_reg_def rtw8922d_nbi_reg_def[] = {
+ [RF_PATH_A] = {
+ .notch1_idx = {0x241A0, 0xFF},
+ .notch1_frac_idx = {0x241A0, 0xC00},
+ .notch1_en = {0x241A0, 0x1000},
+ .notch2_idx = {0x241AC, 0xFF},
+ .notch2_frac_idx = {0x241AC, 0xC00},
+ .notch2_en = {0x241AC, 0x1000},
+ },
+ [RF_PATH_B] = {
+ .notch1_idx = {0x245A0, 0xFF},
+ .notch1_frac_idx = {0x245A0, 0xC00},
+ .notch1_en = {0x245A0, 0x1000},
+ .notch2_idx = {0x245AC, 0xFF},
+ .notch2_frac_idx = {0x245AC, 0xC00},
+ .notch2_en = {0x245AC, 0x1000},
+ },
+};
+
+static void rtw8922d_set_nbi_tone_idx(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ const struct rtw89_nbi_reg_def *nbi = &rtw8922d_nbi_reg_def[path];
+ s32 nbi_frac_idx, nbi_frac_tone_idx;
+ s32 nbi_idx, nbi_tone_idx;
+ bool notch2_chk = false;
+ u32 spur_freq, fc;
+ s32 freq_diff;
+
+ spur_freq = rtw8922d_spur_freq(rtwdev, chan, true, path);
+ if (spur_freq == 0) {
+ rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr,
+ nbi->notch1_en.mask, 0, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr,
+ nbi->notch2_en.mask, 0, phy_idx);
+ return;
+ }
+
+ fc = chan->freq;
+ if (chan->band_width == RTW89_CHANNEL_WIDTH_160) {
+ fc = (spur_freq > fc) ? fc + 40 : fc - 40;
+ if ((fc > spur_freq &&
+ chan->channel < chan->primary_channel) ||
+ (fc < spur_freq &&
+ chan->channel > chan->primary_channel))
+ notch2_chk = true;
+ }
+
+ freq_diff = (spur_freq - fc) * 1000000;
+ nbi_idx = s32_div_u32_round_down(freq_diff, CARRIER_SPACING_312_5,
+ &nbi_frac_idx);
+
+ if (chan->band_width == RTW89_CHANNEL_WIDTH_20) {
+ s32_div_u32_round_down(nbi_idx + 32, 64, &nbi_tone_idx);
+ } else {
+ u16 tone_para = (chan->band_width == RTW89_CHANNEL_WIDTH_40) ?
+ 128 : 256;
+
+ s32_div_u32_round_down(nbi_idx, tone_para, &nbi_tone_idx);
+ }
+ nbi_frac_tone_idx =
+ s32_div_u32_round_closest(nbi_frac_idx, CARRIER_SPACING_78_125);
+
+ if (chan->band_width == RTW89_CHANNEL_WIDTH_160 && notch2_chk) {
+ rtw89_phy_write32_idx(rtwdev, nbi->notch2_idx.addr,
+ nbi->notch2_idx.mask, nbi_tone_idx, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch2_frac_idx.addr,
+ nbi->notch2_frac_idx.mask, nbi_frac_tone_idx,
+ phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr,
+ nbi->notch2_en.mask, 0, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr,
+ nbi->notch2_en.mask, 1, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr,
+ nbi->notch1_en.mask, 0, phy_idx);
+ } else {
+ rtw89_phy_write32_idx(rtwdev, nbi->notch1_idx.addr,
+ nbi->notch1_idx.mask, nbi_tone_idx, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch1_frac_idx.addr,
+ nbi->notch1_frac_idx.mask, nbi_frac_tone_idx,
+ phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr,
+ nbi->notch1_en.mask, 0, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr,
+ nbi->notch1_en.mask, 1, phy_idx);
+ rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr,
+ nbi->notch2_en.mask, 0, phy_idx);
+ }
+}
+
+static void rtw8922d_spur_elimination(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ rtw8922d_set_csi_tone_idx(rtwdev, chan, phy_idx);
+ rtw8922d_set_nbi_tone_idx(rtwdev, chan, RF_PATH_A, phy_idx);
+ rtw8922d_set_nbi_tone_idx(rtwdev, chan, RF_PATH_B, phy_idx);
+}
+
+static void rtw8922d_tssi_reset(struct rtw89_dev *rtwdev,
+ enum rtw89_rf_path path,
+ enum rtw89_phy_idx phy_idx)
+{
+ if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) {
+ if (phy_idx == RTW89_PHY_0) {
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4,
+ B_TXPWR_RSTB0_BE4, 0x0);
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4,
+ B_TXPWR_RSTB0_BE4, 0x1);
+ } else {
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4,
+ B_TXPWR_RSTB1_BE4, 0x0);
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4,
+ B_TXPWR_RSTB1_BE4, 0x1);
+ }
+ } else {
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4, B_TXPWR_RSTB0_BE4, 0x0);
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4, B_TXPWR_RSTB0_BE4, 0x1);
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4, B_TXPWR_RSTB1_BE4, 0x0);
+ rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4, B_TXPWR_RSTB1_BE4, 0x1);
+ }
+}
+
+static void rtw8922d_set_channel_bb(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ bool cck_en = chan->band_type == RTW89_BAND_2G;
+ u8 pri_sb = chan->pri_sb_idx;
+ u32 val;
+
+ rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx);
+ rtw8922d_ctrl_ch(rtwdev, chan, phy_idx);
+ rtw8922d_ctrl_bw(rtwdev, pri_sb, chan->band_width, phy_idx);
+ rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx);
+
+ if (cck_en)
+ rtw8922d_ctrl_sco_cck(rtwdev, chan->primary_channel,
+ chan->band_width, phy_idx);
+
+ rtw8922d_spur_elimination(rtwdev, chan, phy_idx);
+
+ if (hal->cid == RTL8922D_CID7025) {
+ if (chan->band_width == RTW89_CHANNEL_WIDTH_160)
+ val = 0x1f9;
+ else if (chan->band_width == RTW89_CHANNEL_WIDTH_80)
+ val = 0x1f5;
+ else
+ val = 0x1e2;
+
+ rtw89_phy_write32_idx(rtwdev, R_AWGN_DET_BE4, B_AWGN_DET_BE4, val, phy_idx);
+ }
+
+ rtw8922d_tssi_reset(rtwdev, RF_PATH_AB, phy_idx);
+}
+
MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE);
MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE);
MODULE_AUTHOR("Realtek Corporation");
--
2.25.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox