* Re: [PATCH v3] wifi: iwlwifi: prevent NAPI processing after firmware error
From: Ben Greear @ 2026-02-26 19:37 UTC (permalink / raw)
To: Cole Leavitt, johannes.berg, miriam.rachel.korenblit
Cc: linux-wireless, stable
In-Reply-To: <20260214184352.11512-1-cole@unwrap.rs>
On 2/14/26 10:43, Cole Leavitt wrote:
> After a firmware error is detected and STATUS_FW_ERROR is set, NAPI can
> still be actively polling or get scheduled from a prior interrupt. The
> NAPI poll functions (both legacy and MSIX variants) have no check for
> STATUS_FW_ERROR and will continue processing stale RX ring entries from
> dying firmware. This can dispatch TX completion notifications containing
> corrupt SSN values to iwl_mld_handle_tx_resp_notif(), which passes them
> to iwl_trans_reclaim(). If the corrupt SSN causes reclaim to walk TX
> queue entries that were already freed by a prior correct reclaim, the
> result is an skb use-after-free or double-free.
Hello Cole,
We've been testing with this patch, and today managed to see its logic trigger.
The system had a cascade of other errors leading to use-after-free that does
not appear to be related to the skb use-after free, but I can at least confirm your
patch can be triggered. Here are logs around this. I believe in this case, firmware
didn't actually bubble up a crash notification/interrupt, but probably the driver detected timeout
and faked a crash.
Feb 26 11:09:35 ct523c-de7c kernel: workqueue: blk_mq_requeue_work hogged CPU for >10000us 4 times, consider switching to WQ_UNBOUND
Feb 26 11:09:36 ct523c-de7c kernel: workqueue: gc_worker [nf_conntrack] hogged CPU for >10000us 515 times, consider switching to WQ_UNBOUND
Feb 26 11:09:39 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Error sending SYSTEM_STATISTICS_CMD: time out after 2000ms.
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Current CMD queue read_ptr 92 write_ptr 93
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Start IWL Error Log Dump:
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Transport status: 0x0000004A, valid: 6
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Loaded firmware version: 101.6ef20b19.0 gl-c0-fm-c0-101.ucode
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000084 | NMI_INTERRUPT_UNKNOWN
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000002F0 | trm_hw_status0
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | trm_hw_status1
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x002C438C | branchlink2
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00009C04 | interruptlink1
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00009C04 | interruptlink2
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00009C3C | data1
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x01000000 | data2
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | data3
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xD040FE38 | beacon time
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x3B6411CD | tsf low
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000026B | tsf hi
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | time gp1
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0616DB18 | time gp2
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000001 | uCode revision type
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000065 | uCode version major
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x6EF20B19 | uCode version minor
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000472 | hw version
Feb 26 11:09:40 ct523c-de7c kernel: ------------[ cut here ]------------
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi: NAPI MSIX poll[0] invoked after FW error
Feb 26 11:09:40 ct523c-de7c kernel: WARNING: CPU: 3 PID: 36 at drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1058 iwl_pcie_napi_poll_msix+0x29e/0x310
[iwlwifi]
Feb 26 11:09:40 ct523c-de7c kernel: Modules linked in: vrf nf_conntrack_netlink nf_conntrack nfnetlink nf_defrag_ipv6 nf_defrag_ipv4 8021q garp mrp stp llc
macvlan pktgen rfcomm rpcrdma rdma_cm iw_cm ib_cm ib_core qrtr bnep intel_rapl_msr iTCO_wdt intel_pmc_bxt ee1004 iTCO_vendor_support iwlmld coretemp
intel_rapl_common intel_uncore_frequency intel_uncore_frequency_common snd_hda_codec_intelhdmi snd_hda_codec_hdmi intel_tcc_cooling x86_pkg_temp_thermal
snd_hda_codec_alc662 snd_hda_codec_realtek_lib intel_powerclamp intel_wmi_thunderbolt snd_hda_codec_generic snd_hda_intel snd_intel_dspcfg snd_hda_codec
mac80211 snd_hda_core snd_hwdep snd_seq btusb btbcm snd_seq_device btmtk btrtl btintel snd_pcm iwlwifi bluetooth snd_timer cfg80211 i2c_i801 snd i2c_smbus
soundcore i2c_mux mei_pxp mei_hdcp intel_pch_thermal intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec acpi_pad bfq nfsd
sch_fq_codel auth_rpcgss nfs_acl lockd grace nfs_localio fuse sunrpc raid1 dm_raid raid456 async_raid6_recov async_memcpy async_pq
Feb 26 11:09:40 ct523c-de7c kernel: async_xor xor async_tx raid6_pq i915 drm_buddy intel_gtt drm_client_lib drm_display_helper drm_kms_helper cec rc_core ttm
agpgart ixgbe mdio igb libie_fwlog i2c_algo_bit dca drm hwmon mei_wdt i2c_core intel_oc_wdt video wmi scsi_dh_rdac scsi_dh_emc scsi_dh_alua dm_multipath [last
unloaded: nfnetlink]
Feb 26 11:09:40 ct523c-de7c kernel: CPU: 3 UID: 0 PID: 36 Comm: ksoftirqd/3 Kdump: loaded Not tainted 6.18.9+ #18 PREEMPT(full)
Feb 26 11:09:40 ct523c-de7c kernel: Hardware name: Default string Default string/SKYBAY, BIOS 5.12 02/21/2023
Feb 26 11:09:40 ct523c-de7c kernel: RIP: 0010:iwl_pcie_napi_poll_msix+0x29e/0x310 [iwlwifi]
Feb 26 11:09:40 ct523c-de7c kernel: Code: 00 00 fc ff df 48 89 fa 48 c1 ea 03 0f b6 04 02 84 c0 74 04 3c 03 7e 74 8b b3 48 ff ff ff 48 c7 c7 a0 d2 19 a2 e8 62
00 0f df <0f> 0b eb 96 4c 89 e7 e8 d6 92 9f df e9 0b fe ff ff e8 cc 92 9f df
Feb 26 11:09:40 ct523c-de7c kernel: RSP: 0018:ffff888120fe7b70 EFLAGS: 00010286
Feb 26 11:09:40 ct523c-de7c kernel: RAX: 0000000000000000 RBX: ffff888182e100b8 RCX: 0000000000000001
Feb 26 11:09:40 ct523c-de7c kernel: RDX: 0000000000000027 RSI: 0000000000000004 RDI: ffff88841dba4e48
Feb 26 11:09:40 ct523c-de7c kernel: RBP: ffff888132504028 R08: 0000000000000001 R09: ffffed1083b749c9
Feb 26 11:09:40 ct523c-de7c kernel: R10: ffff88841dba4e4b R11: 0000000000072ee8 R12: ffff888132504090
Feb 26 11:09:40 ct523c-de7c kernel: R13: ffff888132504d90 R14: 0000000000000040 R15: ffff888134b480b8
Feb 26 11:09:40 ct523c-de7c kernel: FS: 0000000000000000(0000) GS:ffff8884974c7000(0000) knlGS:0000000000000000
Feb 26 11:09:40 ct523c-de7c kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Feb 26 11:09:40 ct523c-de7c kernel: CR2: 00007fd5beb80028 CR3: 000000013e2d4001 CR4: 00000000003706f0
Feb 26 11:09:40 ct523c-de7c kernel: Call Trace:
Feb 26 11:09:40 ct523c-de7c kernel: <TASK>
Feb 26 11:09:40 ct523c-de7c kernel: __napi_poll.constprop.0+0xa0/0x580
Feb 26 11:09:40 ct523c-de7c kernel: net_rx_action+0x84d/0xe40
Feb 26 11:09:40 ct523c-de7c kernel: ? __napi_poll.constprop.0+0x580/0x580
Feb 26 11:09:40 ct523c-de7c kernel: ? do_raw_spin_lock+0x12c/0x270
Feb 26 11:09:40 ct523c-de7c kernel: ? run_timer_softirq+0xf2/0x1b0
Feb 26 11:09:40 ct523c-de7c kernel: ? lock_release+0xce/0x290
Feb 26 11:09:40 ct523c-de7c kernel: ? trace_irq_enable.constprop.0+0xbe/0x100
Feb 26 11:09:40 ct523c-de7c kernel: handle_softirqs+0x1c6/0x810
Feb 26 11:09:40 ct523c-de7c kernel: run_ksoftirqd+0x2d/0x50
Feb 26 11:09:40 ct523c-de7c kernel: smpboot_thread_fn+0x338/0x8c0
Feb 26 11:09:40 ct523c-de7c kernel: ? sort_range+0x20/0x20
Feb 26 11:09:40 ct523c-de7c kernel: kthread+0x3b7/0x770
Feb 26 11:09:40 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:40 ct523c-de7c kernel: ? ret_from_fork+0x17/0x3a0
Feb 26 11:09:40 ct523c-de7c kernel: ? lock_release+0xce/0x290
Feb 26 11:09:40 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:40 ct523c-de7c kernel: ret_from_fork+0x28b/0x3a0
Feb 26 11:09:40 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:40 ct523c-de7c kernel: ret_from_fork_asm+0x11/0x20
Feb 26 11:09:40 ct523c-de7c kernel: </TASK>
Feb 26 11:09:40 ct523c-de7c kernel: irq event stamp: 36429934
Feb 26 11:09:40 ct523c-de7c kernel: hardirqs last enabled at (36429940): [<ffffffff816116ee>] __up_console_sem+0x5e/0x70
Feb 26 11:09:40 ct523c-de7c kernel: hardirqs last disabled at (36429945): [<ffffffff816116d3>] __up_console_sem+0x43/0x70
Feb 26 11:09:40 ct523c-de7c kernel: softirqs last enabled at (36428910): [<ffffffff81471d3d>] run_ksoftirqd+0x2d/0x50
Feb 26 11:09:40 ct523c-de7c kernel: softirqs last disabled at (36428915): [<ffffffff81471d3d>] run_ksoftirqd+0x2d/0x50
Feb 26 11:09:40 ct523c-de7c kernel: ---[ end trace 0000000000000000 ]---
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00C80002 | board version
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x036D001C | hcmd
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xF6F38000 | isr0
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x01400000 | isr1
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x48F00002 | isr2
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00C00008 | isr3
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x18200000 | isr4
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x036D001C | last cmd Id
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00009C3C | wait_event
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x10000010 | l2p_control
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | l2p_duration
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | l2p_mhvalid
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | l2p_addr_match
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000009 | lmpm_pmg_sel
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | timestamp
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00009018 | flow_handler
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Start IWL Error Log Dump:
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Transport status: 0x0000004A, valid: 6
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Loaded firmware version: 101.6ef20b19.0 gl-c0-fm-c0-101.ucode
Feb 26 11:09:40 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000084 | NMI_INTERRUPT_UNKNOWN
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000002F0 | trm_hw_status0
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | trm_hw_status1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x002C438C | branchlink2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x002B8AD0 | interruptlink1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x002B8AD0 | interruptlink2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x002A5822 | data1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x01000000 | data2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | data3
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xD040FE37 | beacon time
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x3B6411CE | tsf low
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000026B | tsf hi
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | time gp1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0616DB19 | time gp2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000001 | uCode revision type
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000065 | uCode version major
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x6EF20B19 | uCode version minor
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000472 | hw version
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00C80002 | board version
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x804AFC12 | hcmd
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00020000 | isr0
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | isr1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x48F00002 | isr2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00C0001C | isr3
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | isr4
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | last cmd Id
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x002A5822 | wait_event
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x10000010 | l2p_control
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | l2p_duration
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | l2p_mhvalid
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | l2p_addr_match
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000028 | lmpm_pmg_sel
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | timestamp
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00009018 | flow_handler
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Start IWL Error Log Dump:
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Transport status: 0x0000004A, valid: 7
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x20000066 | NMI_INTERRUPT_HOST
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | umac branchlink1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xC00808AA | umac branchlink2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x80287BFE | umac interruptlink1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0102163E | umac interruptlink2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x01000000 | umac data1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0102163E | umac data2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | umac data3
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000065 | umac major
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x6EF20B19 | umac minor
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0616DB0F | frame pointer
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xD00D6258 | stack pointer
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x005C020F | last host cmd
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000400 | isr status reg
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: TCM1 status:
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000070 | error ID
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001D2E | tcm branchlink2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000211C | tcm interruptlink1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000211C | tcm interruptlink2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x40000000 | tcm data1
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | tcm data2
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | tcm data3
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001DAC | tcm log PC
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803FF0 | tcm frame pointer
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803F5C | tcm stack pointer
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm msg ID
Feb 26 11:09:41 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x4000001F | tcm ISR status
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000002F0 | tcm HW status[0]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm HW status[1]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm HW status[2]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x40008300 | tcm HW status[3]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm HW status[4]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm SW status[0]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: RCM1 status:
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000070 | error ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001E2E | rcm branchlink2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000027A0 | rcm interruptlink1
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000027A0 | rcm interruptlink2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x20000000 | rcm data1
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | rcm data2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | rcm data3
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001E98 | rcm log PC
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803FF0 | rcm frame pointer
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803F5C | rcm stack pointer
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | rcm msg ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x2006F000 | rcm ISR status
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00020400 | frame HW status
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | LMAC-to-RCM request mbox
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | RCM-to-LMAC request mbox
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | MAC header control
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | MAC header addr1 low
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x003C0000 | MAC header info
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | MAC header error
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: TCM2 status:
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000070 | error ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001D2E | tcm branchlink2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000211C | tcm interruptlink1
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000211C | tcm interruptlink2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x40000000 | tcm data1
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | tcm data2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | tcm data3
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001DAC | tcm log PC
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803FF0 | tcm frame pointer
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803F5C | tcm stack pointer
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm msg ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x60000000 | tcm ISR status
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000002F0 | tcm HW status[0]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm HW status[1]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm HW status[2]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00008000 | tcm HW status[3]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm HW status[4]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | tcm SW status[0]
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: RCM2 status:
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000070 | error ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001E2E | rcm branchlink2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000027A0 | rcm interruptlink1
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000027A0 | rcm interruptlink2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x20000000 | rcm data1
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | rcm data2
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0xDEADBEEF | rcm data3
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00001E98 | rcm log PC
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803FF0 | rcm frame pointer
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00803F5C | rcm stack pointer
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | rcm msg ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x20000000 | rcm ISR status
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00020400 | frame HW status
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | LMAC-to-RCM request mbox
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | RCM-to-LMAC request mbox
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | MAC header control
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | MAC header addr1 low
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x003C0000 | MAC header info
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | MAC header error
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: IML/ROM dump:
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000B03 | IML/ROM error/state
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000EEE3 | IML/ROM data1
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000080 | IML/ROM WFPM_AUTH_KEY_0
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Fseq Registers:
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x60000000 | FSEQ_ERROR_CODE
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x80B10006 | FSEQ_TOP_INIT_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00570000 | FSEQ_CNVIO_INIT_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000AA14 | FSEQ_OTP_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x0000000F | FSEQ_TOP_CONTENT_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x4552414E | FSEQ_ALIVE_TOKEN
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x02001910 | FSEQ_CNVI_ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x02001910 | FSEQ_CNVR_ID
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x02001910 | CNVI_AUX_MISC_CHIP
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x02001910 | CNVR_AUX_MISC_CHIP
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x8F0F1BEF | CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00000000 | CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00570000 | FSEQ_PREV_CNVIO_INIT_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00B10006 | FSEQ_WIFI_FSEQ_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x00B10005 | FSEQ_BT_FSEQ_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x000000DC | FSEQ_CLASS_TP_VERSION
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: UMAC CURRENT PC: 0x8028b720
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: LMAC1 CURRENT PC: 0xd0
Feb 26 11:09:42 ct523c-de7c kernel: iwlwifi 0000:28:00.0: LMAC2 CURRENT PC: 0xd0
Feb 26 11:09:43 ct523c-de7c kernel: iwlwifi 0000:28:00.0: UMAC CURRENT PC 1: 0x8028b722
Feb 26 11:09:43 ct523c-de7c kernel: iwlwifi 0000:28:00.0: TCM1 CURRENT PC: 0xd0
Feb 26 11:09:43 ct523c-de7c kernel: iwlwifi 0000:28:00.0: RCM1 CURRENT PC: 0xd0
Feb 26 11:09:43 ct523c-de7c kernel: iwlwifi 0000:28:00.0: RCM2 CURRENT PC: 0xd0
Feb 26 11:09:43 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Function Scratch status:
Feb 26 11:09:43 ct523c-de7c kernel: iwlwifi 0000:28:00.0: 0x01010100 | Func Scratch
Feb 26 11:09:43 ct523c-de7c kernel: iwlwifi 0000:28:00.0: WRT: Collecting data: ini trigger 4 fired (delay=0ms).
Thanks,
Ben
>
> The race window opens when the MSIX IRQ handler schedules NAPI (lines
> 2319-2321 in rx.c) before processing the error bit (lines 2382-2396),
> or when NAPI is already running on another CPU from a previous interrupt
> when STATUS_FW_ERROR gets set on the current CPU.
>
> Add STATUS_FW_ERROR checks to both NAPI poll functions to prevent
> processing stale RX data after firmware error, and add early-return
> guards in the TX response and compressed BA notification handlers as
> defense-in-depth. Each check uses WARN_ONCE to log if the race is
> actually hit, which aids diagnosis of the hard-to-reproduce skb
> use-after-free reported on Intel BE200.
>
> Note that _iwl_trans_pcie_gen2_stop_device() already calls
> iwl_pcie_rx_napi_sync() to quiesce NAPI during device teardown, but that
> runs much later in the restart sequence. These checks close the window
> between error detection and device stop.
>
> Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Cole Leavitt <cole@unwrap.rs>
> ---
> Changes since v1:
> - Added Fixes: tag and Cc: stable@vger.kernel.org
>
> Tested on Intel BE200 (FW 101.6e695a70.0) by forcing NMI via debugfs.
> The WARN_ONCE fires reliably:
>
> iwlwifi: NAPI MSIX poll[0] invoked after FW error
> WARNING: drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1058
> at iwl_pcie_napi_poll_msix+0xff/0x130 [iwlwifi], CPU#22
>
> Confirming NAPI poll is invoked after STATUS_FW_ERROR is set. Without
> this patch, that poll processes stale RX ring data from dead firmware.
>
> drivers/net/wireless/intel/iwlwifi/mld/tx.c | 19 ++++++++++++++++++
> .../wireless/intel/iwlwifi/pcie/gen1_2/rx.c | 20 +++++++++++++++++++
> 2 files changed, 39 insertions(+)
>
> diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
> index 3b4b575aadaa..3e99f3ded9bc 100644
> --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c
> +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
> @@ -1071,6 +1071,18 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld,
> bool mgmt = false;
> bool tx_failure = (status & TX_STATUS_MSK) != TX_STATUS_SUCCESS;
>
> + /* Firmware is dead — the TX response may contain corrupt SSN values
> + * from a dying firmware DMA. Processing it could cause
> + * iwl_trans_reclaim() to free the wrong TX queue entries, leading to
> + * skb use-after-free or double-free.
> + */
> + if (unlikely(test_bit(STATUS_FW_ERROR, &mld->trans->status))) {
> + WARN_ONCE(1,
> + "iwlwifi: TX resp notif (sta=%d txq=%d) after FW error\n",
> + sta_id, txq_id);
> + return;
> + }
> +
> if (IWL_FW_CHECK(mld, tx_resp->frame_count != 1,
> "Invalid tx_resp notif frame_count (%d)\n",
> tx_resp->frame_count))
> @@ -1349,6 +1361,13 @@ void iwl_mld_handle_compressed_ba_notif(struct iwl_mld *mld,
> u8 sta_id = ba_res->sta_id;
> struct ieee80211_link_sta *link_sta;
>
> + if (unlikely(test_bit(STATUS_FW_ERROR, &mld->trans->status))) {
> + WARN_ONCE(1,
> + "iwlwifi: BA notif (sta=%d) after FW error\n",
> + sta_id);
> + return;
> + }
> +
> if (!tfd_cnt)
> return;
>
> diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c
> index 619a9505e6d9..ba18d35fa55d 100644
> --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c
> +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c
> @@ -1015,6 +1015,18 @@ static int iwl_pcie_napi_poll(struct napi_struct *napi, int budget)
> trans_pcie = iwl_netdev_to_trans_pcie(napi->dev);
> trans = trans_pcie->trans;
>
> + /* Stop processing RX if firmware has crashed. Stale notifications
> + * from dying firmware (e.g. TX completions with corrupt SSN values)
> + * can cause use-after-free in reclaim paths.
> + */
> + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) {
> + WARN_ONCE(1,
> + "iwlwifi: NAPI poll[%d] invoked after FW error\n",
> + rxq->id);
> + napi_complete_done(napi, 0);
> + return 0;
> + }
> +
> ret = iwl_pcie_rx_handle(trans, rxq->id, budget);
>
> IWL_DEBUG_ISR(trans, "[%d] handled %d, budget %d\n",
> @@ -1042,6 +1054,14 @@ static int iwl_pcie_napi_poll_msix(struct napi_struct *napi, int budget)
> trans_pcie = iwl_netdev_to_trans_pcie(napi->dev);
> trans = trans_pcie->trans;
>
> + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) {
> + WARN_ONCE(1,
> + "iwlwifi: NAPI MSIX poll[%d] invoked after FW error\n",
> + rxq->id);
> + napi_complete_done(napi, 0);
> + return 0;
> + }
> +
> ret = iwl_pcie_rx_handle(trans, rxq->id, budget);
> IWL_DEBUG_ISR(trans, "[%d] handled %d, budget %d\n", rxq->id, ret,
> budget);
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* iwlwifi + mac80211: Force success in case of EIO in dis-assoc path?
From: Ben Greear @ 2026-02-26 19:51 UTC (permalink / raw)
To: linux-wireless; +Cc: Korenblit, Miriam Rachel
Hello,
It looks like the radios in my system had some serious problem for one reason or another
and stopped responding, and it hit a warning of interest up in mac80211, in this code:
if (new_state < sta->sta_state &&
test_sta_flag(sta, WLAN_STA_INSERTED)) {
int err = drv_sta_state(sta->local, sta->sdata, sta,
sta->sta_state, new_state);
WARN_ONCE(err,
"Driver is not allowed to fail if the sta_state is transitioning down the list: %d\n",
err);
}
So in case the driver is trying to do drv_sta_state, and it runs into -EIO. If we are going 'down the list',
should we force driver to ignore the EIO error and return success?
I think this is the pertinent set of logs related to this failure (several radios were failing
concurrently I believe, so possibly some logs are jumbled).
Feb 26 11:09:56 ct523c-de7c kernel: ------------[ cut here ]------------
Feb 26 11:09:56 ct523c-de7c kernel: bad state = 0
Feb 26 11:09:56 ct523c-de7c kernel: WARNING: CPU: 1 PID: 111503 at drivers/net/wireless/intel/iwlwifi/iwl-trans.c:312 iwl_trans_send_cmd+0x2ad/0x330 [iwlwifi]
Feb 26 11:09:56 ct523c-de7c kernel: Modules linked in: vrf nf_conntrack_netlink nf_conntrack nfnetlink nf_defrag_ipv6 nf_defrag_ipv4 8021q garp mrp stp llc
macvlan pktgen rfcomm rpcrdma rdma_cm iw_cm ib_cm ib_core qrtr bnep intel_rapl_msr iTCO_wdt intel_pmc_bxt ee1004 iTCO_vendor_support iwlmld coretemp
intel_rapl_common intel_uncore_frequency intel_uncore_frequency_common snd_hda_codec_intelhdmi snd_hda_codec_hdmi intel_tcc_cooling x86_pkg_temp_thermal
snd_hda_codec_alc662 snd_hda_codec_realtek_lib intel_powerclamp intel_wmi_thunderbolt snd_hda_codec_generic snd_hda_intel snd_intel_dspcfg snd_hda_codec
mac80211 snd_hda_core snd_hwdep snd_seq btusb btbcm snd_seq_device btmtk btrtl btintel snd_pcm iwlwifi bluetooth snd_timer cfg80211 i2c_i801 snd i2c_smbus
soundcore i2c_mux mei_pxp mei_hdcp intel_pch_thermal intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec acpi_pad bfq nfsd
sch_fq_codel auth_rpcgss nfs_acl lockd grace nfs_localio fuse sunrpc raid1 dm_raid raid456 async_raid6_recov async_memcpy async_pq
Feb 26 11:09:56 ct523c-de7c kernel: async_xor xor async_tx raid6_pq i915 drm_buddy intel_gtt drm_client_lib drm_display_helper drm_kms_helper cec rc_core ttm
agpgart ixgbe mdio igb libie_fwlog i2c_algo_bit dca drm hwmon mei_wdt i2c_core intel_oc_wdt video wmi scsi_dh_rdac scsi_dh_emc scsi_dh_alua dm_multipath [last
unloaded: nfnetlink]
Feb 26 11:09:56 ct523c-de7c kernel: CPU: 1 UID: 0 PID: 111503 Comm: kworker/1:0 Kdump: loaded Tainted: G W 6.18.9+ #18 PREEMPT(full)
Feb 26 11:09:56 ct523c-de7c kernel: Tainted: [W]=WARN
Feb 26 11:09:56 ct523c-de7c kernel: Hardware name: Default string Default string/SKYBAY, BIOS 5.12 02/21/2023
Feb 26 11:09:56 ct523c-de7c kernel: Workqueue: events_freezable ieee80211_restart_work [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: RIP: 0010:iwl_trans_send_cmd+0x2ad/0x330 [iwlwifi]
Feb 26 11:09:56 ct523c-de7c kernel: Code: bd 7c ff ff ff e9 08 ff ff ff 80 3d 8c ba dc ff 00 0f 85 30 ff ff ff 48 c7 c7 00 8c 1a a2 c6 05 78 ba dc ff 01 e8 b3
bd 0a df <0f> 0b e9 16 ff ff ff 4c 89 e7 e8 14 50 9b df e9 7e fd ff ff e8 0a
Feb 26 11:09:56 ct523c-de7c kernel: RSP: 0018:ffff8881f7e96ea8 EFLAGS: 00010282
Feb 26 11:09:56 ct523c-de7c kernel: RAX: 0000000000000000 RBX: ffff888132504028 RCX: 0000000000000001
Feb 26 11:09:56 ct523c-de7c kernel: RDX: 0000000000000027 RSI: 0000000000000004 RDI: ffff88841daa4e48
Feb 26 11:09:56 ct523c-de7c kernel: RBP: ffff8881f7e96fb0 R08: 0000000000000001 R09: ffffed1083b549c9
Feb 26 11:09:56 ct523c-de7c kernel: R10: ffff88841daa4e4b R11: 000000000007bfe8 R12: ffff8881f7e96fd4
Feb 26 11:09:56 ct523c-de7c kernel: R13: ffff888132504090 R14: ffff88815aec7a68 R15: ffff88815aec5508
Feb 26 11:09:56 ct523c-de7c kernel: FS: 0000000000000000(0000) GS:ffff8884973c7000(0000) knlGS:0000000000000000
Feb 26 11:09:56 ct523c-de7c kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Feb 26 11:09:56 ct523c-de7c kernel: CR2: 0000557cd9657138 CR3: 0000000128903001 CR4: 00000000003706f0
Feb 26 11:09:56 ct523c-de7c kernel: Call Trace:
Feb 26 11:09:56 ct523c-de7c kernel: <TASK>
Feb 26 11:09:56 ct523c-de7c kernel: iwl_mld_sync_rx_queues+0x297/0x6e0 [iwlmld]
Feb 26 11:09:56 ct523c-de7c kernel: ? mark_held_locks+0x49/0x70
Feb 26 11:09:56 ct523c-de7c kernel: ? iwl_mld_rx_mpdu+0x4100/0x4100 [iwlmld]
Feb 26 11:09:56 ct523c-de7c kernel: ? do_raw_spin_lock+0x12c/0x270
Feb 26 11:09:56 ct523c-de7c kernel: ? spin_bug+0x1d0/0x1d0
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: __sta_info_destroy_part1+0x252/0x12c0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: __sta_info_flush+0x2e1/0x610 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? __sta_info_destroy+0x30/0x30 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? ieee80211_bss_info_change_notify+0x53/0x650 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ieee80211_set_disassoc+0x14fb/0x1c10 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? ieee80211_prep_channel+0x12b0/0x12b0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ieee80211_mgd_deauth+0x526/0x1780 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? check_irq_usage+0xb5/0xa20
Feb 26 11:09:56 ct523c-de7c kernel: ? ieee80211_mgd_assoc+0x5be0/0x5be0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? check_path.constprop.0+0x24/0x50
Feb 26 11:09:56 ct523c-de7c kernel: ? check_noncircular+0x7e/0x140
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_mlme_deauth+0x333/0x7a0 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? prb_final_commit+0x40/0x40
Feb 26 11:09:56 ct523c-de7c kernel: ? cfg80211_mlme_assoc+0x960/0x960 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_mlme_down+0x16f/0x230 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? cfg80211_mlme_disassoc+0x450/0x450 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? kfree+0x2ae/0x580
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_disconnect+0x4d2/0x700 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_netdev_notifier_call+0x661/0x10a0 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? __lock_acquire+0x432/0x23b0
Feb 26 11:09:56 ct523c-de7c kernel: ? cfg80211_register_netdevice+0x2a0/0x2a0 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? __lock_acquire+0x432/0x23b0
Feb 26 11:09:56 ct523c-de7c kernel: ? __bfs+0x12e/0x220
Feb 26 11:09:56 ct523c-de7c kernel: ? check_irq_usage+0xb5/0xa20
Feb 26 11:09:56 ct523c-de7c kernel: ? __bfs+0x12e/0x220
Feb 26 11:09:56 ct523c-de7c kernel: ? check_irq_usage+0xb5/0xa20
Feb 26 11:09:56 ct523c-de7c kernel: ? check_path.constprop.0+0x24/0x50
Feb 26 11:09:56 ct523c-de7c kernel: ? __bfs+0x12e/0x220
Feb 26 11:09:56 ct523c-de7c kernel: ? __lock_acquire+0x432/0x23b0
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_acquire+0x155/0x2e0
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_notifier+0xd3/0xa70
Feb 26 11:09:56 ct523c-de7c kernel: ? find_held_lock+0x2b/0x80
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_notifier+0x1f0/0xa70
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_release+0xce/0x290
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_notifier+0x1fa/0xa70
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_getname_spkt+0x290/0x290
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: notifier_call_chain+0x97/0x280
Feb 26 11:09:56 ct523c-de7c kernel: __dev_close_many+0xe8/0x670
Feb 26 11:09:56 ct523c-de7c kernel: ? netdev_notify_peers+0x20/0x20
Feb 26 11:09:56 ct523c-de7c kernel: netif_close_many+0x1e6/0x5c0
Feb 26 11:09:56 ct523c-de7c kernel: ? preempt_schedule+0x4c/0x70
Feb 26 11:09:56 ct523c-de7c kernel: ? __dev_close_many+0x670/0x670
Feb 26 11:09:56 ct523c-de7c kernel: ? wake_up_q+0xaf/0x130
Feb 26 11:09:56 ct523c-de7c kernel: netif_close+0x141/0x1d0
Feb 26 11:09:56 ct523c-de7c kernel: ? netif_close_many+0x5c0/0x5c0
Feb 26 11:09:56 ct523c-de7c kernel: dev_close+0x89/0x1f0
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_shutdown_all_interfaces+0x76/0x190 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ieee80211_restart_work+0x2c8/0x3c0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: process_one_work+0x83f/0x17b0
Feb 26 11:09:56 ct523c-de7c kernel: ? pwq_dec_nr_in_flight+0xe00/0xe00
Feb 26 11:09:56 ct523c-de7c kernel: ? assign_work+0x168/0x240
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: worker_thread+0x58c/0xfb0
Feb 26 11:09:56 ct523c-de7c kernel: ? __kthread_parkme+0xc6/0x1f0
Feb 26 11:09:56 ct523c-de7c kernel: ? rescuer_thread+0xca0/0xca0
Feb 26 11:09:56 ct523c-de7c kernel: kthread+0x3b7/0x770
Feb 26 11:09:56 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:56 ct523c-de7c kernel: ? ret_from_fork+0x17/0x3a0
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_release+0xce/0x290
Feb 26 11:09:56 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:56 ct523c-de7c kernel: ret_from_fork+0x28b/0x3a0
Feb 26 11:09:56 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:56 ct523c-de7c kernel: ret_from_fork_asm+0x11/0x20
Feb 26 11:09:56 ct523c-de7c kernel: </TASK>
Feb 26 11:09:56 ct523c-de7c kernel: irq event stamp: 1825193
Feb 26 11:09:56 ct523c-de7c kernel: hardirqs last enabled at (1825199): [<ffffffff816116ee>] __up_console_sem+0x5e/0x70
Feb 26 11:09:56 ct523c-de7c kernel: hardirqs last disabled at (1825204): [<ffffffff816116d3>] __up_console_sem+0x43/0x70
Feb 26 11:09:56 ct523c-de7c kernel: softirqs last enabled at (1823870): [<ffffffffa2661752>] __ieee80211_stop_tx_ba_session+0x6b2/0x780 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: softirqs last disabled at (1823868): [<ffffffffa26611cd>] __ieee80211_stop_tx_ba_session+0x12d/0x780 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ---[ end trace 0000000000000000 ]---
Feb 26 11:09:56 ct523c-de7c kernel: iwlwifi 0000:28:00.0: Failed to trigger RX queues sync (-5)
Feb 26 11:09:56 ct523c-de7c kernel: ------------[ cut here ]------------
Feb 26 11:09:56 ct523c-de7c kernel: Driver is not allowed to fail if the sta_state is transitioning down the list: -5
Feb 26 11:09:56 ct523c-de7c kernel: WARNING: CPU: 1 PID: 111503 at net/mac80211/sta_info.c:1564 _sta_info_move_state+0x2a5/0x1420 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: Modules linked in: vrf nf_conntrack_netlink nf_conntrack nfnetlink nf_defrag_ipv6 nf_defrag_ipv4 8021q garp mrp stp llc
macvlan pktgen rfcomm rpcrdma rdma_cm iw_cm ib_cm ib_core qrtr bnep intel_rapl_msr iTCO_wdt intel_pmc_bxt ee1004 iTCO_vendor_support iwlmld coretemp
intel_rapl_common intel_uncore_frequency intel_uncore_frequency_common snd_hda_codec_intelhdmi snd_hda_codec_hdmi intel_tcc_cooling x86_pkg_temp_thermal
snd_hda_codec_alc662 snd_hda_codec_realtek_lib intel_powerclamp intel_wmi_thunderbolt snd_hda_codec_generic snd_hda_intel snd_intel_dspcfg snd_hda_codec
mac80211 snd_hda_core snd_hwdep snd_seq btusb btbcm snd_seq_device btmtk btrtl btintel snd_pcm iwlwifi bluetooth snd_timer cfg80211 i2c_i801 snd i2c_smbus
soundcore i2c_mux mei_pxp mei_hdcp intel_pch_thermal intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec acpi_pad bfq nfsd
sch_fq_codel auth_rpcgss nfs_acl lockd grace nfs_localio fuse sunrpc raid1 dm_raid raid456 async_raid6_recov async_memcpy async_pq
Feb 26 11:09:56 ct523c-de7c kernel: async_xor xor async_tx raid6_pq i915 drm_buddy intel_gtt drm_client_lib drm_display_helper drm_kms_helper cec rc_core ttm
agpgart ixgbe mdio igb libie_fwlog i2c_algo_bit dca drm hwmon mei_wdt i2c_core intel_oc_wdt video wmi scsi_dh_rdac scsi_dh_emc scsi_dh_alua dm_multipath [last
unloaded: nfnetlink]
Feb 26 11:09:56 ct523c-de7c kernel: CPU: 1 UID: 0 PID: 111503 Comm: kworker/1:0 Kdump: loaded Tainted: G W 6.18.9+ #18 PREEMPT(full)
Feb 26 11:09:56 ct523c-de7c kernel: Tainted: [W]=WARN
Feb 26 11:09:56 ct523c-de7c kernel: Hardware name: Default string Default string/SKYBAY, BIOS 5.12 02/21/2023
Feb 26 11:09:56 ct523c-de7c kernel: Workqueue: events_freezable ieee80211_restart_work [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: RIP: 0010:_sta_info_move_state+0x2a5/0x1420 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: Code: 85 c0 0f 84 cf 01 00 00 80 3d 0f 7c f1 ff 00 0f 85 c2 01 00 00 89 c6 48 c7 c7 80 bd a0 a2 c6 05 f9 7b f1 ff 01 e8 7b
59 e3 de <0f> 0b e9 a6 01 00 00 41 83 fd 04 0f 85 ce 02 00 00 83 fa 03 0f 85
Feb 26 11:09:56 ct523c-de7c kernel: RSP: 0018:ffff8881f7e97030 EFLAGS: 00010282
Feb 26 11:09:56 ct523c-de7c kernel: RAX: 0000000000000000 RBX: ffff8881377e0188 RCX: 0000000000000001
Feb 26 11:09:56 ct523c-de7c kernel: RDX: 0000000000000027 RSI: 0000000000000004 RDI: ffff88841daa4e48
Feb 26 11:09:56 ct523c-de7c kernel: RBP: ffff8881377e0058 R08: 0000000000000001 R09: ffffed1083b549c9
Feb 26 11:09:56 ct523c-de7c kernel: R10: ffff88841daa4e4b R11: 000000000007dc78 R12: ffff8881377e0050
Feb 26 11:09:56 ct523c-de7c kernel: R13: 0000000000000003 R14: ffff8881377e0180 R15: ffff8881377e0000
Feb 26 11:09:56 ct523c-de7c kernel: FS: 0000000000000000(0000) GS:ffff8884973c7000(0000) knlGS:0000000000000000
Feb 26 11:09:56 ct523c-de7c kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Feb 26 11:09:56 ct523c-de7c kernel: CR2: 0000557cd9657138 CR3: 000000000509a002 CR4: 00000000003706f0
Feb 26 11:09:56 ct523c-de7c kernel: Call Trace:
Feb 26 11:09:56 ct523c-de7c kernel: <TASK>
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: __sta_info_destroy_part2+0x3e2/0x4a0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: __sta_info_flush+0x409/0x610 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? __sta_info_destroy+0x30/0x30 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? ieee80211_bss_info_change_notify+0x53/0x650 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ieee80211_set_disassoc+0x14fb/0x1c10 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? ieee80211_prep_channel+0x12b0/0x12b0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ieee80211_mgd_deauth+0x526/0x1780 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? check_irq_usage+0xb5/0xa20
Feb 26 11:09:56 ct523c-de7c kernel: ? ieee80211_mgd_assoc+0x5be0/0x5be0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? check_path.constprop.0+0x24/0x50
Feb 26 11:09:56 ct523c-de7c kernel: ? check_noncircular+0x7e/0x140
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_mlme_deauth+0x333/0x7a0 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? prb_final_commit+0x40/0x40
Feb 26 11:09:56 ct523c-de7c kernel: ? cfg80211_mlme_assoc+0x960/0x960 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_mlme_down+0x16f/0x230 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? cfg80211_mlme_disassoc+0x450/0x450 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? kfree+0x2ae/0x580
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_disconnect+0x4d2/0x700 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_netdev_notifier_call+0x661/0x10a0 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? __lock_acquire+0x432/0x23b0
Feb 26 11:09:56 ct523c-de7c kernel: ? cfg80211_register_netdevice+0x2a0/0x2a0 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ? __lock_acquire+0x432/0x23b0
Feb 26 11:09:56 ct523c-de7c kernel: ? __bfs+0x12e/0x220
Feb 26 11:09:56 ct523c-de7c kernel: ? check_irq_usage+0xb5/0xa20
Feb 26 11:09:56 ct523c-de7c kernel: ? __bfs+0x12e/0x220
Feb 26 11:09:56 ct523c-de7c kernel: ? check_irq_usage+0xb5/0xa20
Feb 26 11:09:56 ct523c-de7c kernel: ? check_path.constprop.0+0x24/0x50
Feb 26 11:09:56 ct523c-de7c kernel: ? __bfs+0x12e/0x220
Feb 26 11:09:56 ct523c-de7c kernel: ? __lock_acquire+0x432/0x23b0
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_acquire+0x155/0x2e0
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_notifier+0xd3/0xa70
Feb 26 11:09:56 ct523c-de7c kernel: ? find_held_lock+0x2b/0x80
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_notifier+0x1f0/0xa70
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_release+0xce/0x290
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_notifier+0x1fa/0xa70
Feb 26 11:09:56 ct523c-de7c kernel: ? packet_getname_spkt+0x290/0x290
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: notifier_call_chain+0x97/0x280
Feb 26 11:09:56 ct523c-de7c kernel: __dev_close_many+0xe8/0x670
Feb 26 11:09:56 ct523c-de7c kernel: ? netdev_notify_peers+0x20/0x20
Feb 26 11:09:56 ct523c-de7c kernel: netif_close_many+0x1e6/0x5c0
Feb 26 11:09:56 ct523c-de7c kernel: ? preempt_schedule+0x4c/0x70
Feb 26 11:09:56 ct523c-de7c kernel: ? __dev_close_many+0x670/0x670
Feb 26 11:09:56 ct523c-de7c kernel: ? wake_up_q+0xaf/0x130
Feb 26 11:09:56 ct523c-de7c kernel: netif_close+0x141/0x1d0
Feb 26 11:09:56 ct523c-de7c kernel: ? netif_close_many+0x5c0/0x5c0
Feb 26 11:09:56 ct523c-de7c kernel: dev_close+0x89/0x1f0
Feb 26 11:09:56 ct523c-de7c kernel: cfg80211_shutdown_all_interfaces+0x76/0x190 [cfg80211]
Feb 26 11:09:56 ct523c-de7c kernel: ieee80211_restart_work+0x2c8/0x3c0 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: process_one_work+0x83f/0x17b0
Feb 26 11:09:56 ct523c-de7c kernel: ? pwq_dec_nr_in_flight+0xe00/0xe00
Feb 26 11:09:56 ct523c-de7c kernel: ? assign_work+0x168/0x240
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_is_held_type+0xa5/0x110
Feb 26 11:09:56 ct523c-de7c kernel: worker_thread+0x58c/0xfb0
Feb 26 11:09:56 ct523c-de7c kernel: ? __kthread_parkme+0xc6/0x1f0
Feb 26 11:09:56 ct523c-de7c kernel: ? rescuer_thread+0xca0/0xca0
Feb 26 11:09:56 ct523c-de7c kernel: kthread+0x3b7/0x770
Feb 26 11:09:56 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:56 ct523c-de7c kernel: ? ret_from_fork+0x17/0x3a0
Feb 26 11:09:56 ct523c-de7c kernel: ? lock_release+0xce/0x290
Feb 26 11:09:56 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:56 ct523c-de7c kernel: ret_from_fork+0x28b/0x3a0
Feb 26 11:09:56 ct523c-de7c kernel: ? kthread_is_per_cpu+0xb0/0xb0
Feb 26 11:09:56 ct523c-de7c kernel: ret_from_fork_asm+0x11/0x20
Feb 26 11:09:56 ct523c-de7c kernel: </TASK>
Feb 26 11:09:56 ct523c-de7c kernel: irq event stamp: 1826775
Feb 26 11:09:56 ct523c-de7c kernel: hardirqs last enabled at (1826781): [<ffffffff816116ee>] __up_console_sem+0x5e/0x70
Feb 26 11:09:56 ct523c-de7c kernel: hardirqs last disabled at (1826786): [<ffffffff816116d3>] __up_console_sem+0x43/0x70
Feb 26 11:09:56 ct523c-de7c kernel: softirqs last enabled at (1825488): [<ffffffffa26fa978>] ieee80211_clear_fast_rx+0x58/0x80 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: softirqs last disabled at (1825486): [<ffffffffa26fa93b>] ieee80211_clear_fast_rx+0x1b/0x80 [mac80211]
Feb 26 11:09:56 ct523c-de7c kernel: ---[ end trace 0000000000000000 ]---
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: [PATCH wireless 0/3] wifi: mt76: Fix possible out-of-bound accesses in mt76 driver
From: Lorenzo Bianconi @ 2026-02-26 21:08 UTC (permalink / raw)
To: Felix Fietkau, Ryder Lee, Shayne Chen, Sean Wang,
Matthias Brugger, AngeloGioacchino Del Regno, StanleyYP Wang,
Peter Chiu, MeiChia Chiu, Leon Yen, Deren Wu, Quan Zhou,
Mingyen Hsieh
Cc: Johannes Berg, Howard Hsu, Bo Jiao, linux-wireless,
linux-arm-kernel, linux-mediatek, Hao Zhang, Nelson Yu
In-Reply-To: <20260226-mt76-addba-req-oob-access-v1-0-b0f6d1ad4850@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 994 bytes --]
> Check mgmt frame length before accessing header fields in order to avoid
> a possible oob access for injected frames.
Hi Felix,
do you think these patches can go directly into the wireless tree or do you
prefer to get them into your tree?
Regards,
Lorenzo
>
> ---
> Lorenzo Bianconi (3):
> wifi: mt76: mt7996: Fix possible oob access in mt7996_mac_write_txwi_80211()
> wifi: mt76: mt7925: Fix possible oob access in mt7925_mac_write_txwi_80211()
> wifi: mt76: Fix possible oob access in mt76_connac2_mac_write_txwi_80211()
>
> drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 1 +
> drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 1 +
> drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 1 +
> 3 files changed, 3 insertions(+)
> ---
> base-commit: 8bf22c33e7a172fbc72464f4cc484d23a6b412ba
> change-id: 20260226-mt76-addba-req-oob-access-848280920814
>
> Best regards,
> --
> Lorenzo Bianconi <lorenzo@kernel.org>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v1] wifi: ath9k: Fix typo
From: Jeff Johnson @ 2026-02-26 21:29 UTC (permalink / raw)
To: linux-wireless, Alejandro Colomar
Cc: Rajkumar Manoharan, John W. Linville,
Toke Høiland-Jørgensen
In-Reply-To: <6ab107cf786f9d05dc4d84ea4e2d1b219ce108c0.1766355822.git.alx@kernel.org>
On Sun, 21 Dec 2025 23:25:54 +0100, Alejandro Colomar wrote:
> This only worked by chance, because all callers of this macro used the
> same identifiers that were expected by the macro.
>
> $ grep -rn ath_for_each_chanctx
> drivers/net/wireless/ath/ath9k/main.c:1576: ath_for_each_chanctx(sc, ctx)
> drivers/net/wireless/ath/ath9k/main.c:2554: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/channel.c:165: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/channel.c:291: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/channel.c:861: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/debug.c:717: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/ath9k.h:446:#define ath_for_each_chanctx(_sc, _ctx) \
>
> [...]
Applied, thanks!
[1/1] wifi: ath9k: Fix typo
commit: a8911fbeff8bee4fe3376c5044b64fbf3cceb78e
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH ath-next] wifi: ath12k: fix indentation in ath12k_qmi_aux_uc_load()
From: Jeff Johnson @ 2026-02-26 21:29 UTC (permalink / raw)
To: Jeff Johnson, Baochen Qiang
Cc: linux-wireless, ath12k, linux-kernel, kernel test robot
In-Reply-To: <20260209-ath12k-fix-indention-v1-1-6b87ae99745e@oss.qualcomm.com>
On Mon, 09 Feb 2026 10:52:19 +0800, Baochen Qiang wrote:
> Smatch complains:
>
> drivers/net/wireless/ath/ath12k/qmi.c:3342 ath12k_qmi_aux_uc_load() warn: inconsistent indenting
>
> Fix it.
>
> Compile tested only.
>
> [...]
Applied, thanks!
[1/1] wifi: ath12k: fix indentation in ath12k_qmi_aux_uc_load()
commit: 0ef4738f0d38f103e525ffb12c112a935c2cf011
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH ath-next v4] wifi: ath12k: add basic hwmon temperature reporting
From: Jeff Johnson @ 2026-02-26 21:29 UTC (permalink / raw)
To: ath12k, Maharaja Kennadyrajan; +Cc: linux-wireless, Aishwarya R
In-Reply-To: <20260223132622.43464-1-maharaja.kennadyrajan@oss.qualcomm.com>
On Mon, 23 Feb 2026 18:56:22 +0530, Maharaja Kennadyrajan wrote:
> Add initial thermal support by wiring up a per-radio (pdev) hwmon temperature
> sensor backed by the existing WMI pdev temperature command and event.
> When userspace reads the sysfs file temp1_input, the driver sends
> WMI_PDEV_GET_TEMPERATURE_CMDID (tag WMI_TAG_PDEV_GET_TEMPERATURE_CMD) and waits
> for the corresponding WMI_PDEV_TEMPERATURE_EVENTID
> (tag WMI_TAG_PDEV_TEMPERATURE_EVENT) to get the temperature and pdev_id.
>
> [...]
Applied, thanks!
[1/1] wifi: ath12k: add basic hwmon temperature reporting
commit: 151322bccdbdb132f5a73cc8ad5d3ab89b90ed52
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v3] wifi: ath11k: fix memory leaks in beacon template setup
From: Jeff Johnson @ 2026-02-26 21:29 UTC (permalink / raw)
To: Zilin Guan
Cc: ath11k, baochen.qiang, jianhao.xu, jjohnson, linux-kernel,
linux-wireless
In-Reply-To: <20260130084451.110768-1-zilin@seu.edu.cn>
On Fri, 30 Jan 2026 08:44:51 +0000, Zilin Guan wrote:
> The functions ath11k_mac_setup_bcn_tmpl_ema() and
> ath11k_mac_setup_bcn_tmpl_mbssid() allocate memory for beacon templates
> but fail to free it when parameter setup returns an error.
>
> Since beacon templates must be released during normal execution, they
> must also be released in the error handling paths to prevent memory
> leaks.
>
> [...]
Applied, thanks!
[1/1] wifi: ath11k: fix memory leaks in beacon template setup
commit: ff49eba595df500e4ddccc593088c8a4ab5f2c27
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* [PATCH] wifi: mt76: mt7915: add debug query from WA firmware
From: David Bauer @ 2026-02-26 21:45 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek
Add a new file to the mt76 debugfs to allow querying internal state
information from the WA firmware.
This allows to extract more information from the internal queue
structures and configured limits.
Signed-off-by: David Bauer <mail@david-bauer.net>
---
.../wireless/mediatek/mt76/mt7915/debugfs.c | 81 +++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
index 26ed3745af43e..018b1c70b524b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
@@ -603,6 +603,86 @@ mt7915_fw_debug_wa_get(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7915_fw_debug_wa_get,
mt7915_fw_debug_wa_set, "%lld\n");
+struct {
+ u8 idx;
+ u8 query_cmd;
+ char *description;
+} mt7915_fw_wa_debug_query[] = {
+ { 0, 0x16, "MSDU drop info" },
+ { 1, 0x17, "AC queue tail drop" },
+ { 2, 0x19, "TX Free info" },
+ { 3, 0x20, "BSS Table" },
+ { 4, 0x21, "STA records" },
+};
+
+static ssize_t
+mt7915_fw_query_wa_set(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mt7915_phy *phy = file->private_data;
+ struct mt7915_dev *dev = phy->dev;
+ char buf[16];
+ u16 val;
+
+ if (count >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if (count && buf[count - 1] == '\n')
+ buf[count - 1] = '\0';
+ else
+ buf[count] = '\0';
+
+ if (kstrtou16(buf, 0, &val))
+ return -EINVAL;
+
+ for (int i = 0; i < ARRAY_SIZE(mt7915_fw_wa_debug_query); i++) {
+ if (val != mt7915_fw_wa_debug_query[i].idx)
+ continue;
+
+ mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY),
+ mt7915_fw_wa_debug_query[i].query_cmd,
+ 0, 0);
+ return count;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t
+mt7915_fw_query_wa_get(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ static const size_t bufsz = 1024;
+ ssize_t ret;
+ char *buff;
+ int desc = 0;
+ int i;
+
+ buff = kmalloc(bufsz, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(mt7915_fw_wa_debug_query); i++) {
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "%d: %s\n", mt7915_fw_wa_debug_query[i].idx,
+ mt7915_fw_wa_debug_query[i].description);
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+ kfree(buff);
+ return ret;
+}
+
+static const struct file_operations fw_query_wa_fops = {
+ .write = mt7915_fw_query_wa_set,
+ .read = mt7915_fw_query_wa_get,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
static struct dentry *
create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
struct rchan_buf *buf, int *is_global)
@@ -1310,6 +1390,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
&mt7915_sys_recovery_ops);
debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
+ debugfs_create_file("fw_query_wa", 0600, dir, dev, &fw_query_wa_fops);
debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
debugfs_create_file("fw_util_wm", 0400, dir, dev,
&mt7915_fw_util_wm_fops);
--
2.51.0
^ permalink raw reply related
* Re: [PATCH ath-next] wifi: ath5k: ahb: use devm for ioremap
From: Rosen Penev @ 2026-02-26 23:58 UTC (permalink / raw)
To: Jiri Slaby; +Cc: linux-wireless, Nick Kossifidis, Luis Chamberlain, open list
In-Reply-To: <cf50211c-4f0c-45e3-860d-220b49f4cc11@kernel.org>
On Tue, Feb 24, 2026 at 10:23 AM Jiri Slaby <jirislaby@kernel.org> wrote:
>
> On 23. 02. 26, 4:08, Rosen Penev wrote:
> > Simplifies the code by quite a bit in probe.
> >
> > Also allows removing a goto and returning directly.
>
> Looks good except:
>
> > --- a/drivers/net/wireless/ath/ath5k/ahb.c
> > +++ b/drivers/net/wireless/ath/ath5k/ahb.c
> ...
> > @@ -95,35 +94,21 @@ static int ath_ahb_probe(struct platform_device *pdev)
> >
> > if (!dev_get_platdata(&pdev->dev)) {
> > dev_err(&pdev->dev, "no platform data specified\n");
> > - ret = -EINVAL;
> > - goto err_out;
> > + return -EINVAL;
> > }
> >
> > - res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > - if (res == NULL) {
> > - dev_err(&pdev->dev, "no memory resource found\n");
> > - ret = -ENXIO;
> > - goto err_out;
> > - }
> > -
> > - mem = ioremap(res->start, resource_size(res));
> > - if (mem == NULL) {
> > - dev_err(&pdev->dev, "ioremap failed\n");
> > - ret = -ENOMEM;
> > - goto err_out;
> > - }
> > + mem = devm_platform_ioremap_resources(pdev, 0);
>
> Is this a typo or devm_platform_ioremap_resources() is new?
typo. Looks like COMPILE_TEST is needed.
>
> --
> js
> suse labs
^ permalink raw reply
* [syzbot] [wireless?] WARNING in ieee80211_set_disassoc (2)
From: syzbot @ 2026-02-27 1:43 UTC (permalink / raw)
To: johannes, linux-kernel, linux-wireless, netdev, syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: 189f164e573e Convert remaining multi-line kmalloc_obj/flex..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=1750855a580000
kernel config: https://syzkaller.appspot.com/x/.config?x=2208a171312e89b7
dashboard link: https://syzkaller.appspot.com/bug?extid=4b737df95e84787fec21
compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
Unfortunately, I don't have any reproducer for this issue yet.
Downloadable assets:
disk image (non-bootable): https://storage.googleapis.com/syzbot-assets/d900f083ada3/non_bootable_disk-189f164e.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/669b52dbab53/vmlinux-189f164e.xz
kernel image: https://storage.googleapis.com/syzbot-assets/51b76feb584c/bzImage-189f164e.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+4b737df95e84787fec21@syzkaller.appspotmail.com
mac80211_hwsim: wmediumd released netlink socket, switching to perfect channel medium
mac80211_hwsim: wmediumd released netlink socket, switching to perfect channel medium
mac80211_hwsim: wmediumd released netlink socket, switching to perfect channel medium
wlan1: deauthenticating from 08:02:11:00:00:00 by local choice (Reason: 3=DEAUTH_LEAVING)
------------[ cut here ]------------
!ap_sta
WARNING: net/mac80211/mlme.c:4137 at ieee80211_set_disassoc+0x3c8/0x1a90 net/mac80211/mlme.c:4137, CPU#0: syz.0.0/5315
Modules linked in:
CPU: 0 UID: 0 PID: 5315 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:ieee80211_set_disassoc+0x3c8/0x1a90 net/mac80211/mlme.c:4137
Code: c4 8c f6 45 85 ff 0f 84 c9 11 00 00 e8 81 c0 8c f6 eb 2b e8 7a c0 8c f6 48 83 7c 24 48 00 0f 85 38 fe ff ff e8 69 c0 8c f6 90 <0f> 0b 90 e9 aa 10 00 00 e8 5b c0 8c f6 eb 05 e8 54 c0 8c f6 48 8d
RSP: 0018:ffffc9000dd4efa0 EFLAGS: 00010287
RAX: ffffffff8b38dab7 RBX: ffff888038b28dc0 RCX: 0000000000100000
RDX: ffffc9000ec12000 RSI: 000000000001762f RDI: 0000000000017630
RBP: ffffc9000dd4f0b0 R08: 0000000000000001 R09: 0000000000000000
R10: ffffc9000dd4f1e0 R11: fffff52001ba9e3f R12: ffff888038b297f0
R13: dffffc0000000000 R14: 1ffff92001ba9e04 R15: ffffc9000dd4f1e0
FS: 00007f5691c2d6c0(0000) GS:ffff88808ca5b000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffc7ffad0c8 CR3: 0000000035dd8000 CR4: 0000000000352ef0
Call Trace:
<TASK>
ieee80211_mgd_deauth+0x9fa/0xe60 net/mac80211/mlme.c:10198
rdev_deauth net/wireless/rdev-ops.h:509 [inline]
cfg80211_mlme_deauth+0x51b/0x8f0 net/wireless/mlme.c:536
cfg80211_sme_disconnect net/wireless/sme.c:666 [inline]
cfg80211_disconnect+0x3b2/0x7c0 net/wireless/sme.c:1559
cfg80211_netdev_notifier_call+0x1ba/0x1470 net/wireless/core.c:1599
notifier_call_chain+0x1be/0x400 kernel/notifier.c:85
call_netdevice_notifiers_extack net/core/dev.c:2287 [inline]
call_netdevice_notifiers net/core/dev.c:2301 [inline]
__dev_close_many+0x119/0x6d0 net/core/dev.c:1746
netif_close_many+0x225/0x420 net/core/dev.c:1800
netif_close+0x160/0x220 net/core/dev.c:1817
dev_close+0x10a/0x220 net/core/dev_api.c:220
cfg80211_shutdown_all_interfaces+0xc4/0x200 net/wireless/core.c:282
cfg80211_rfkill_set_block+0x2d/0x50 net/wireless/core.c:312
rfkill_set_block+0x1d2/0x440 net/rfkill/core.c:346
rfkill_fop_write+0x461/0x5a0 net/rfkill/core.c:1301
vfs_write+0x29a/0xb90 fs/read_write.c:686
ksys_write+0x150/0x270 fs/read_write.c:740
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5690d9c629
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f5691c2d028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007f5691015fa0 RCX: 00007f5690d9c629
RDX: 0000000000000008 RSI: 0000200000000080 RDI: 0000000000000006
RBP: 00007f5690e32b39 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f5691016038 R14: 00007f5691015fa0 R15: 00007fff6e9cd5a8
</TASK>
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply
* [PATCH ath-next] wifi: ath12k: Enable monitor mode support on IPQ5332
From: Aaradhana Sahu @ 2026-02-27 3:33 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Aaradhana Sahu
Currently, rxdma1_enable and supports_monitor are set to false in
IPQ5332 hardware parameters, which skips monitor ring configuration
and removes NL80211_IFTYPE_MONITOR from the supported interface modes.
Set rxdma1_enable and supports_monitor to true so that monitor rings are
configured and monitor mode is enabled on IPQ5332.
Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.7-00587-QCAHKSWPL_SILICONZ-1
Signed-off-by: Aaradhana Sahu <aaradhana.sahu@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/wifi7/hw.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
index df045ddf42da..27acdfc35459 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
@@ -617,7 +617,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
ath12k_wifi7_target_service_to_ce_map_wlan_ipq5332,
.svc_to_ce_map_len = 18,
- .rxdma1_enable = false,
+ .rxdma1_enable = true,
.num_rxdma_per_pdev = 1,
.num_rxdma_dst_ring = 0,
.rx_mac_buf_ring = false,
@@ -626,7 +626,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MESH_POINT),
- .supports_monitor = false,
+ .supports_monitor = true,
.idle_ps = false,
.download_calib = true,
base-commit: 20ad0d58517073b3b683ff786c65dd3142321707
--
2.34.1
^ permalink raw reply related
* [PATCH wireless-next 00/35] wifi: mm81x: add mm81x driver
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes
Cc: arien.judge, dan.callaghan, ayman.grais, linux-wireless,
Lachlan Hodges
This series adds the first Wi-Fi HaLow driver to support the Morse
Micro mm81x chip family via USB and SDIO.
S1G support in the kernel is only new, and as a result this driver has
been scoped to be simple and only support station and AP interface.
The Wi-Fi specific features only cover the minimum required for basic
use such as powersave, aggregation, rate control and so on. The driver
will be extended into the future as S1G support for operations such as
ACS, channel switching and so on are added into the wireless stack.
The driver contains a single checkpatch CHECK for a static rate array
using the same ignore list as the wireless checkpatch NIPA bot with
the exception of OPEN_ENDED_LINE which has been added.
The driver has been build tested on a long list of architectures and
compilers via Intels LKP.
The driver currently supports IEEE80211-2024 US channels only, with
AU 2020 also available. In order for this to be expanded additional
non-trivial kernel work is required which will begin once the
driver is upstream.
Some items of importance:
* Our firmware cannot be loaded with the 00 regdom. Due to the
disparate nature of S1G channels, it's not feasible to store the
entire regdom on the chip in EEPROM or similar. This means the chip
will fail to boot when the world regdom is selected. The nature of
the reg notifier means there is no clean way to propagate this error
to usermode besides through kernel logs (obviously interface
addition will also fail).
* When reacting to dynamic regulatory changes from usermode, we must
perform a full chip restart. Unfortunately this requires a blocking
reset in the regulatory notifier to prevent subsequent command
failures. Feedback on this would be appreciated, but we note that
this is due to the limitation of our current chip/firmware as above.
* There is currently no upstream support for S1G within
hostpad / wpa_supplicant / iwd. This makes testing of the driver
challenging. We intend to post upstream patches for these utilities
shortly following the submission of this driver.
* We were going to push the driver to staging, but we believe the driver
is in a good enough state for the regular tree (pending review of
course :-)). However, while it is not the normal process, staging may
be something to consider so that the upstream ecosystem can mature over
~6 months.
The firmware will be posted in the next version.
The driver has had many authors who are listed below in
alphabetical order:
Signed-off-by: Andrew Pope andrew.pope@morsemicro.com
Signed-off-by: Arien Judge arien.judge@morsemicro.com
Signed-off-by: Ayman Grais ayman.grais@morsemicro.com
Signed-off-by: Bassem Dawood bassem@morsemicro.com
Signed-off-by: Chetan Mistry chetan.mistry@morsemicro.com
Signed-off-by: Dan Callaghan dan.callaghan@morsemicro.com
Signed-off-by: James Herbert james.herbert@morsemicro.com
Signed-off-by: Sahand Maleki sahand.maleki@morsemicro.com
Signed-off-by: Simon Wadsworth simon@morsemicro.com
Signed-off-by: Lachlan Hodges lachlan.hodges@morsemicro.com
Lachlan Hodges (35):
wifi: mm81x: add bus.h
wifi: mm81x: add command.c
wifi: mm81x: add command_defs.h
wifi: mm81x: add command.h
wifi: mm81x: add core.c
wifi: mm81x: add core.h
wifi: mm81x: add debug.c
wifi: mm81x: add debug.h
wifi: mm81x: add fw.c
wifi: mm81x: add fw.h
wifi: mm81x: add hif.h
wifi: mm81x: add hw.c
wifi: mm81x: add hw.h
wifi: mm81x: add mac.c
wifi: mm81x: add mac.h
wifi: mm81x: add mmrc.c
wifi: mm81x: add mmrc.h
wifi: mm81x: add ps.c
wifi: mm81x: add ps.h
wifi: mm81x: add rate_code.h
wifi: mm81x: add rc.c
wifi: mm81x: add rc.h
wifi: mm81x: add sdio.c
wifi: mm81x: add skbq.c
wifi: mm81x: add skbq.h
wifi: mm81x: add usb.c
wifi: mm81x: add yaps.c
wifi: mm81x: add yaps.h
wifi: mm81x: add yaps_hw.c
wifi: mm81x: add yaps_hw.h
dt-bindings: vendor-prefixes: add Morse Micro
dt-bindings: net: wireless: morsemicro: add mm81x family
mmc: sdio: add Morse Micro vendor ids
wifi: mm81x: add Kconfig and Makefile
wifi: mm81x: add MAINTAINERS entry
.../net/wireless/morsemicro,mm81x.yaml | 74 +
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 9 +
drivers/net/wireless/Kconfig | 1 +
drivers/net/wireless/Makefile | 1 +
drivers/net/wireless/morsemicro/Kconfig | 15 +
drivers/net/wireless/morsemicro/Makefile | 2 +
drivers/net/wireless/morsemicro/mm81x/Kconfig | 34 +
.../net/wireless/morsemicro/mm81x/Makefile | 19 +
drivers/net/wireless/morsemicro/mm81x/bus.h | 90 +
.../net/wireless/morsemicro/mm81x/command.c | 619 ++++
.../net/wireless/morsemicro/mm81x/command.h | 84 +
.../wireless/morsemicro/mm81x/command_defs.h | 1668 +++++++++++
drivers/net/wireless/morsemicro/mm81x/core.c | 157 +
drivers/net/wireless/morsemicro/mm81x/core.h | 499 ++++
drivers/net/wireless/morsemicro/mm81x/debug.c | 87 +
drivers/net/wireless/morsemicro/mm81x/debug.h | 58 +
drivers/net/wireless/morsemicro/mm81x/fw.c | 743 +++++
drivers/net/wireless/morsemicro/mm81x/fw.h | 107 +
drivers/net/wireless/morsemicro/mm81x/hif.h | 116 +
drivers/net/wireless/morsemicro/mm81x/hw.c | 372 +++
drivers/net/wireless/morsemicro/mm81x/hw.h | 175 ++
drivers/net/wireless/morsemicro/mm81x/mac.c | 2642 +++++++++++++++++
drivers/net/wireless/morsemicro/mm81x/mac.h | 69 +
drivers/net/wireless/morsemicro/mm81x/mmrc.c | 1353 +++++++++
drivers/net/wireless/morsemicro/mm81x/mmrc.h | 198 ++
drivers/net/wireless/morsemicro/mm81x/ps.c | 239 ++
drivers/net/wireless/morsemicro/mm81x/ps.h | 22 +
.../net/wireless/morsemicro/mm81x/rate_code.h | 177 ++
drivers/net/wireless/morsemicro/mm81x/rc.c | 559 ++++
drivers/net/wireless/morsemicro/mm81x/rc.h | 62 +
drivers/net/wireless/morsemicro/mm81x/sdio.c | 803 +++++
drivers/net/wireless/morsemicro/mm81x/skbq.c | 1056 +++++++
drivers/net/wireless/morsemicro/mm81x/skbq.h | 218 ++
drivers/net/wireless/morsemicro/mm81x/usb.c | 971 ++++++
drivers/net/wireless/morsemicro/mm81x/yaps.c | 704 +++++
drivers/net/wireless/morsemicro/mm81x/yaps.h | 77 +
.../net/wireless/morsemicro/mm81x/yaps_hw.c | 683 +++++
.../net/wireless/morsemicro/mm81x/yaps_hw.h | 52 +
include/linux/mmc/sdio_ids.h | 4 +
40 files changed, 14821 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/wireless/morsemicro,mm81x.yaml
create mode 100644 drivers/net/wireless/morsemicro/Kconfig
create mode 100644 drivers/net/wireless/morsemicro/Makefile
create mode 100644 drivers/net/wireless/morsemicro/mm81x/Kconfig
create mode 100644 drivers/net/wireless/morsemicro/mm81x/Makefile
create mode 100644 drivers/net/wireless/morsemicro/mm81x/bus.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/command.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/command.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/command_defs.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/core.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/core.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/debug.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/debug.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/fw.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/fw.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/hif.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/hw.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/hw.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/mac.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/mac.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/mmrc.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/mmrc.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/ps.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/ps.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/rate_code.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/rc.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/rc.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/sdio.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/skbq.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/skbq.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/usb.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/yaps.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/yaps.h
create mode 100644 drivers/net/wireless/morsemicro/mm81x/yaps_hw.c
create mode 100644 drivers/net/wireless/morsemicro/mm81x/yaps_hw.h
--
2.43.0
^ permalink raw reply
* [PATCH ath-next] wifi: ath12k: Set up MLO after SSR
From: Roopni Devanathan @ 2026-02-27 4:11 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Ramya Gnanasekar, Roopni Devanathan
From: Ramya Gnanasekar <ramya.gnanasekar@oss.qualcomm.com>
During recovery of an MLO setup from a core reset,
ATH12K_GROUP_FLAG_REGISTERED is set because ath12k_mac_unregister is not
called during core reset. So, when an MLO setup is recovering from a core
reset, ath12k_core_mlo_setup() is skipped. Hence, the firmware will not
have information about partner links. This makes MLO association fail
after recovery.
To resolve this, call ath12k_core_mlo_setup() during recovery, to set up
MLO. Also, if MLO setup fails during recovery, call
ath12k_mac_unregister() and ath12k_mac_destroy() to unregister mac and
then tear down the mac structures.
Also, initiate MLO teardown in the hardware group stop sequence to align
with the hardware group start sequence.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01181-QCAHKSWPL_SILICONZ-1
Signed-off-by: Ramya Gnanasekar <ramya.gnanasekar@oss.qualcomm.com>
Signed-off-by: Roopni Devanathan <roopni.devanathan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/core.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 9dca1a0af73e..218c0a0c9699 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1017,6 +1017,8 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
ath12k_mac_unregister(ag);
+ ath12k_mac_mlo_teardown(ag);
+
for (i = ag->num_devices - 1; i >= 0; i--) {
ab = ag->ab[i];
if (!ab)
@@ -1134,8 +1136,14 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag)
lockdep_assert_held(&ag->mutex);
- if (test_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags))
+ if (test_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags)) {
+ ret = ath12k_core_mlo_setup(ag);
+ if (WARN_ON(ret)) {
+ ath12k_mac_unregister(ag);
+ goto err_mac_destroy;
+ }
goto core_pdev_create;
+ }
ret = ath12k_mac_allocate(ag);
if (WARN_ON(ret))
base-commit: 20ad0d58517073b3b683ff786c65dd3142321707
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 01/35] wifi: mm81x: add bus.h
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/bus.h | 90 +++++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/bus.h
diff --git a/drivers/net/wireless/morsemicro/mm81x/bus.h b/drivers/net/wireless/morsemicro/mm81x/bus.h
new file mode 100644
index 000000000000..3c0b8bb1d8c3
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/bus.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+
+#ifndef _MM81X_BUS_H_
+#define _MM81X_BUS_H_
+
+#include <linux/skbuff.h>
+#include "core.h"
+
+enum mm81x_bus_type {
+ MM81X_BUS_TYPE_USB,
+ MM81X_BUS_TYPE_SDIO,
+};
+
+struct mm81x_bus_ops {
+ int (*dm_read)(struct mm81x *mm, u32 addr, u8 *data, int len);
+ int (*dm_write)(struct mm81x *mm, u32 addr, const u8 *data, int len);
+ int (*reg32_read)(struct mm81x *mm, u32 addr, u32 *data);
+ int (*reg32_write)(struct mm81x *mm, u32 addr, u32 data);
+ void (*set_bus_enable)(struct mm81x *mm, bool enable);
+ void (*config_burst_mode)(struct mm81x *mm, bool enable_burst);
+ void (*claim)(struct mm81x *mm);
+ void (*set_irq)(struct mm81x *mm, bool enable);
+ void (*release)(struct mm81x *mm);
+ unsigned int bulk_alignment;
+};
+
+/*
+ * Default TX alignment for buses which don't care. mac80211 will give us
+ * SKBs aligned to the 2 byte boundary, so 2 is effectively a noop.
+ */
+#define MM81X_BUS_DEFAULT_BULK_ALIGNMENT (2)
+
+/* mm81x_dm_read - len must be rounded up to the nearest 4-byte boundary */
+static inline int mm81x_dm_read(struct mm81x *mm, u32 addr, u8 *data, int len)
+{
+ return mm->bus_ops->dm_read(mm, addr, data, len);
+}
+
+static inline int mm81x_dm_write(struct mm81x *mm, u32 addr, const u8 *data,
+ int len)
+{
+ return mm->bus_ops->dm_write(mm, addr, data, len);
+}
+
+static inline int mm81x_reg32_read(struct mm81x *mm, u32 addr, u32 *data)
+{
+ return mm->bus_ops->reg32_read(mm, addr, data);
+}
+
+static inline int mm81x_reg32_write(struct mm81x *mm, u32 addr, u32 data)
+{
+ return mm->bus_ops->reg32_write(mm, addr, data);
+}
+
+static inline void mm81x_set_bus_enable(struct mm81x *mm, bool enable)
+{
+ mm->bus_ops->set_bus_enable(mm, enable);
+}
+
+static inline void mm81x_bus_config_burst_mode(struct mm81x *mm,
+ bool enable_burst)
+{
+ if (mm->bus_ops->config_burst_mode)
+ mm->bus_ops->config_burst_mode(mm, enable_burst);
+}
+
+static inline void mm81x_claim_bus(struct mm81x *mm)
+{
+ mm->bus_ops->claim(mm);
+}
+
+static inline void mm81x_bus_set_irq(struct mm81x *mm, bool enable)
+{
+ mm->bus_ops->set_irq(mm, enable);
+}
+
+static inline void mm81x_release_bus(struct mm81x *mm)
+{
+ mm->bus_ops->release(mm);
+}
+
+static inline unsigned int mm81x_bus_get_alignment(struct mm81x *mm)
+{
+ return mm->bus_ops->bulk_alignment;
+}
+
+#endif /* !_MM81X_BUS_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 02/35] wifi: mm81x: add command.c
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
.../net/wireless/morsemicro/mm81x/command.c | 619 ++++++++++++++++++
1 file changed, 619 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/command.c
diff --git a/drivers/net/wireless/morsemicro/mm81x/command.c b/drivers/net/wireless/morsemicro/mm81x/command.c
new file mode 100644
index 000000000000..d756bcf5a318
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/command.c
@@ -0,0 +1,619 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/workqueue.h>
+
+#include "debug.h"
+#include "command.h"
+#include "mac.h"
+#include "ps.h"
+#include "hif.h"
+
+#define MM_MAX_COMMAND_RETRY 2
+#define HOST_CMD_DEFAULT_TIMEOUT_MS 600
+#define HOST_CMD_POWERSAVE_TIMEOUT_MS 2000
+
+struct host_cmd_resp_cb {
+ int ret;
+ u32 length;
+ struct host_cmd_resp *dest_resp;
+};
+
+static void mm81x_cmd_init(struct mm81x *mm, struct host_cmd_header *hdr,
+ enum host_cmd_id cmd, u16 vif_id, u16 len)
+{
+ if (len < sizeof(*hdr)) {
+ mm81x_err(mm, "Invalid cmd len %d\n", len);
+ return;
+ }
+
+ hdr->message_id = cpu_to_le16(cmd);
+ hdr->len = cpu_to_le16(len - sizeof(*hdr));
+ hdr->vif_id = cpu_to_le16(vif_id);
+}
+
+static int mm81x_cmd_tx(struct mm81x *mm, struct host_cmd_resp *resp,
+ struct host_cmd_req *req, u32 length, u32 timeout)
+{
+ int cmd_len;
+ int ret = 0;
+ u16 host_id;
+ int retry = 0;
+ unsigned long wait_ret = 0;
+ struct sk_buff *skb;
+ struct mm81x_skbq *cmd_q = mm81x_hif_get_tx_cmd_queue(mm);
+ struct host_cmd_resp_cb *resp_cb;
+ DECLARE_COMPLETION_ONSTACK(cmd_comp);
+
+ BUILD_BUG_ON(sizeof(struct host_cmd_resp_cb) >
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+
+ if (!cmd_q)
+ /* No control pageset, not supported by FW */
+ return -ENODEV;
+
+ cmd_len = sizeof(*req) + le16_to_cpu(req->hdr.len);
+ req->hdr.flags = cpu_to_le16(HOST_CMD_TYPE_REQ);
+
+ mutex_lock(&mm->cmd_wait);
+ mm->cmd_seq++;
+ if (mm->cmd_seq > HOST_CMD_HOST_ID_SEQ_MAX)
+ mm->cmd_seq = 1;
+ host_id = mm->cmd_seq << HOST_CMD_HOST_ID_SEQ_SHIFT;
+
+ mm81x_ps_disable(mm);
+
+ do {
+ req->hdr.host_id = cpu_to_le16(host_id | retry);
+
+ skb = mm81x_skbq_alloc_skb(cmd_q, cmd_len);
+ if (!skb) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ memcpy(skb->data, req, cmd_len);
+ resp_cb = (struct host_cmd_resp_cb *)IEEE80211_SKB_CB(skb)
+ ->driver_data;
+ resp_cb->length = length;
+ resp_cb->dest_resp = resp;
+
+ mm81x_dbg(mm, MM81X_DBG_ANY, "CMD 0x%04x:%04x",
+ le16_to_cpu(req->hdr.message_id),
+ le16_to_cpu(req->hdr.host_id));
+
+ mutex_lock(&mm->cmd_lock);
+ mm->cmd_comp = &cmd_comp;
+ if (retry > 0)
+ reinit_completion(&cmd_comp);
+ timeout = timeout ? timeout : HOST_CMD_DEFAULT_TIMEOUT_MS;
+ ret = mm81x_skbq_skb_tx(cmd_q, &skb, NULL,
+ MM81X_SKB_CHAN_COMMAND);
+ mutex_unlock(&mm->cmd_lock);
+
+ if (ret) {
+ mm81x_err(mm, "mm81x_skbq_tx fail: %d", ret);
+ break;
+ }
+
+ wait_ret = wait_for_completion_timeout(
+ &cmd_comp, msecs_to_jiffies(timeout));
+ mutex_lock(&mm->cmd_lock);
+ mm->cmd_comp = NULL;
+
+ if (!wait_ret) {
+ mm81x_err(
+ mm,
+ "Try:%d Command %04x:%04x timeout after %u ms",
+ retry, le16_to_cpu(req->hdr.message_id),
+ le16_to_cpu(req->hdr.host_id), timeout);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = (length && resp) ? le32_to_cpu(resp->status) :
+ resp_cb->ret;
+
+ mm81x_dbg(mm, MM81X_DBG_ANY,
+ "Command 0x%04x:%04x status 0x%08x",
+ le16_to_cpu(req->hdr.message_id),
+ le16_to_cpu(req->hdr.host_id), ret);
+ if (ret) {
+ mm81x_err(mm, "Command 0x%04x:%04x error %d",
+ le16_to_cpu(req->hdr.message_id),
+ le16_to_cpu(req->hdr.host_id), ret);
+ }
+ }
+ /* Free the command request */
+ spin_lock_bh(&cmd_q->lock);
+ mm81x_skbq_skb_finish(cmd_q, skb, NULL);
+ spin_unlock_bh(&cmd_q->lock);
+ mutex_unlock(&mm->cmd_lock);
+
+ retry++;
+ } while ((ret == -ETIMEDOUT) && retry < MM_MAX_COMMAND_RETRY);
+
+ mm81x_ps_enable(mm);
+ mutex_unlock(&mm->cmd_wait);
+
+ if (ret == -ETIMEDOUT) {
+ mm81x_err(mm, "Command %02x:%02x timed out",
+ le16_to_cpu(req->hdr.message_id),
+ le16_to_cpu(req->hdr.host_id));
+ } else if (ret != 0) {
+ mm81x_err(mm, "Command %02x:%02x failed with rc %d (0x%x)\n",
+ le16_to_cpu(req->hdr.message_id),
+ le16_to_cpu(req->hdr.host_id), ret, ret);
+ }
+
+ return ret;
+}
+
+int mm81x_cmd_sta_state(struct mm81x *mm, struct mm81x_vif *mm_vif, u16 aid,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state state)
+{
+ struct host_cmd_req_set_sta_state req;
+ struct host_cmd_resp_set_sta_state resp;
+
+ memset(&req, 0, sizeof(req));
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_SET_STA_STATE, mm_vif->id,
+ sizeof(req));
+
+ memcpy(req.sta_addr, sta->addr, sizeof(req.sta_addr));
+ req.aid = cpu_to_le16(aid);
+ req.state = cpu_to_le16(state);
+ req.uapsd_queues = sta->uapsd_queues;
+
+ return mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, sizeof(resp), 0);
+}
+
+int mm81x_cmd_resp_process(struct mm81x *mm, struct sk_buff *skb)
+{
+ int length, ret = -ESRCH; /* No such process */
+ struct mm81x_skbq *cmd_q = mm81x_hif_get_tx_cmd_queue(mm);
+ struct host_cmd_resp *src_resp = (struct host_cmd_resp *)(skb->data);
+ struct sk_buff *cmd_skb = NULL;
+ struct host_cmd_resp_cb *resp_cb;
+ struct host_cmd_resp *dest_resp;
+ struct host_cmd_req *req;
+ u16 message_id = 0;
+ u16 host_id = 0;
+ u16 resp_message_id = le16_to_cpu(src_resp->hdr.message_id);
+ u16 resp_host_id = le16_to_cpu(src_resp->hdr.host_id);
+ bool is_late_response = false;
+
+ mm81x_dbg(mm, MM81X_DBG_ANY, "EVT 0x%04x:0x%04x", resp_message_id,
+ resp_host_id);
+
+ if (!HOST_CMD_IS_RESP(src_resp)) {
+ ret = mm81x_mac_event_recv(mm, skb);
+ goto exit_free;
+ }
+
+ mutex_lock(&mm->cmd_lock);
+
+ cmd_skb = mm81x_skbq_tx_pending(cmd_q);
+ if (cmd_skb) {
+ mm81x_skbq_pull_hdr_post_tx(cmd_skb);
+ req = (struct host_cmd_req *)cmd_skb->data;
+ message_id = le16_to_cpu(req->hdr.message_id);
+ host_id = le16_to_cpu(req->hdr.host_id);
+ }
+
+ /*
+ * If there is no pending command or the sequence ID does not match,
+ * this is a late response for a timed out command which has been
+ * cleaned up, so just free up the response. If a command was retried,
+ * the response may be from the retry or from the original command
+ * (late response) but not from both because the firmware will silently
+ * drop a retry if it received the initial request. So a mismatched
+ * retry counter is treated as a matched command and response.
+ */
+ if (!cmd_skb || message_id != resp_message_id ||
+ (host_id & HOST_CMD_HOST_ID_SEQ_MASK) !=
+ (resp_host_id & HOST_CMD_HOST_ID_SEQ_MASK)) {
+ mm81x_err(
+ mm,
+ "Late response for timed out req 0x%04x:%04x have 0x%04x:%04x 0x%04x",
+ resp_message_id, resp_host_id, message_id, host_id,
+ mm->cmd_seq);
+ is_late_response = true;
+ goto exit;
+ }
+ if ((host_id & HOST_CMD_HOST_ID_RETRY_MASK) !=
+ (resp_host_id & HOST_CMD_HOST_ID_RETRY_MASK))
+ mm81x_dbg(mm, MM81X_DBG_ANY,
+ "Command retry mismatch 0x%04x:%04x 0x%04x:%04x",
+ message_id, host_id, resp_message_id, resp_host_id);
+
+ resp_cb = (struct host_cmd_resp_cb *)IEEE80211_SKB_CB(cmd_skb)
+ ->driver_data;
+ length = resp_cb->length;
+ dest_resp = resp_cb->dest_resp;
+ if (length >= sizeof(struct host_cmd_resp) && dest_resp) {
+ ret = 0;
+ length = min_t(int, length,
+ le16_to_cpu(src_resp->hdr.len) +
+ sizeof(struct host_cmd_header));
+ memcpy(dest_resp, src_resp, length);
+ } else {
+ ret = le32_to_cpu(src_resp->status);
+ }
+
+ resp_cb->ret = ret;
+
+exit:
+ if (cmd_skb && !is_late_response) {
+ /* Complete if not already timed out */
+ if (mm->cmd_comp)
+ complete(mm->cmd_comp);
+ }
+
+ mutex_unlock(&mm->cmd_lock);
+exit_free:
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+int mm81x_cmd_add_if(struct mm81x *mm, u16 *vif_id, const u8 *addr,
+ enum nl80211_iftype type)
+{
+ int ret;
+ struct host_cmd_req_add_interface req;
+ struct host_cmd_resp_add_interface resp;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_ADD_INTERFACE, 0, sizeof(req));
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ req.interface_type = cpu_to_le32(HOST_CMD_INTERFACE_TYPE_STA);
+ break;
+ case NL80211_IFTYPE_AP:
+ req.interface_type = cpu_to_le32(HOST_CMD_INTERFACE_TYPE_AP);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ memcpy(req.addr.octet, addr, sizeof(req.addr.octet));
+
+ ret = mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, sizeof(resp), 0);
+ if (ret == 0)
+ *vif_id = le16_to_cpu(resp.hdr.vif_id);
+
+ return ret;
+}
+
+int mm81x_cmd_get_capabilities(struct mm81x *mm, u16 vif_id,
+ struct mm81x_fw_caps *capabilities)
+{
+ int ret = 0;
+ int i;
+ struct host_cmd_req_get_capabilities req;
+ struct host_cmd_resp_get_capabilities rsp;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_GET_CAPABILITIES, vif_id,
+ sizeof(req));
+
+ ret = mm81x_cmd_tx(mm, (struct host_cmd_resp *)&rsp,
+ (struct host_cmd_req *)&req, sizeof(rsp), 0);
+ if (ret)
+ return ret;
+
+ capabilities->ampdu_mss = rsp.capabilities.ampdu_mss;
+ capabilities->mm81x_mmss_offset = rsp.morse_mmss_offset;
+ capabilities->beamformee_sts_capability =
+ rsp.capabilities.beamformee_sts_capability;
+ capabilities->maximum_ampdu_length_exponent =
+ rsp.capabilities.maximum_ampdu_length_exponent;
+ capabilities->number_sounding_dimensions =
+ rsp.capabilities.number_sounding_dimensions;
+ for (i = 0; i < FW_CAPABILITIES_FLAGS_WIDTH; i++)
+ capabilities->flags[i] = le32_to_cpu(rsp.capabilities.flags[i]);
+
+ return ret;
+}
+
+int mm81x_cmd_get_max_txpower(struct mm81x *mm, s32 *out_power_mbm)
+{
+ int ret;
+ struct host_cmd_req_get_max_txpower req;
+ struct host_cmd_resp_get_max_txpower resp;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_GET_MAX_TXPOWER, 0,
+ sizeof(req));
+
+ ret = mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, sizeof(resp), 0);
+ if (ret == 0)
+ *out_power_mbm = QDBM_TO_MBM(le32_to_cpu(resp.power_qdbm));
+
+ return ret;
+}
+
+int mm81x_cmd_hw_scan(struct mm81x *mm, struct mm81x_hw_scan_params *params,
+ bool store)
+{
+ int ret;
+ struct host_cmd_req_hw_scan *req;
+ size_t cmd_size;
+ u8 *buf;
+ u32 flags = 0;
+
+ cmd_size = mm81x_hw_scan_h_get_cmd_size(params);
+ cmd_size = ROUND_BYTES_TO_WORD(cmd_size);
+
+ req = kzalloc(cmd_size, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ buf = req->variable;
+
+ if (store)
+ flags = HOST_CMD_HW_SCAN_FLAGS_STORE;
+ else if (params->operation == MM81X_HW_SCAN_OP_START)
+ flags |= HOST_CMD_HW_SCAN_FLAGS_START;
+ else if (params->operation == MM81X_HW_SCAN_OP_STOP)
+ flags |= HOST_CMD_HW_SCAN_FLAGS_ABORT;
+
+ if (params->use_1mhz_probes)
+ flags |= HOST_CMD_HW_SCAN_FLAGS_1MHZ_PROBES;
+
+ if (params->operation == MM81X_HW_SCAN_OP_START) {
+ req->dwell_time_ms = cpu_to_le32(params->dwell_time_ms);
+ buf = mm81x_hw_scan_h_insert_tlvs(params, buf);
+ }
+
+ req->flags = cpu_to_le32(flags);
+ mm81x_cmd_init(mm, &req->hdr, HOST_CMD_ID_HW_SCAN, 0, buf - (u8 *)req);
+ ret = mm81x_cmd_tx(mm, NULL, (struct host_cmd_req *)req, 0, 0);
+ kfree(req);
+
+ return ret;
+}
+
+int mm81x_cmd_set_txpower(struct mm81x *mm, s32 *out_power_mbm, int txpower_mbm)
+{
+ int ret;
+ struct host_cmd_req_set_txpower req;
+ struct host_cmd_resp_set_txpower resp;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_SET_TXPOWER, 0, sizeof(req));
+
+ req.power_qdbm = cpu_to_le32(MBM_TO_QDBM(txpower_mbm));
+
+ ret = mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, sizeof(resp), 0);
+ if (ret == 0)
+ *out_power_mbm = QDBM_TO_MBM(le32_to_cpu(resp.power_qdbm));
+
+ return ret;
+}
+
+int mm81x_cmd_set_channel(struct mm81x *mm, u32 op_chan_freq_hz,
+ u8 pri_1mhz_chan_idx, u8 op_bw_mhz, u8 pri_bw_mhz,
+ s32 *power_mbm)
+{
+ int ret;
+ struct host_cmd_req_set_channel req;
+ struct host_cmd_resp_set_channel resp;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_SET_CHANNEL, 0, sizeof(req));
+
+ req.op_chan_freq_hz = cpu_to_le32(op_chan_freq_hz);
+ req.op_bw_mhz = op_bw_mhz;
+ req.pri_bw_mhz = pri_bw_mhz;
+ req.pri_1mhz_chan_idx = pri_1mhz_chan_idx;
+ req.dot11_mode = HOST_CMD_DOT11_PROTO_MODE_AH;
+
+ ret = mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, sizeof(resp), 0);
+ if (!ret)
+ *power_mbm = QDBM_TO_MBM(le32_to_cpu(resp.power_qdbm));
+
+ return ret;
+}
+
+int mm81x_cmd_disable_key(struct mm81x *mm, struct mm81x_vif *mm_vif, u16 aid,
+ struct ieee80211_key_conf *key)
+{
+ struct host_cmd_req_disable_key req;
+
+ mm81x_dbg(mm, MM81X_DBG_ANY,
+ "%s Disabling key for vif (%d):\n"
+ "\tkey->hw_key_idx: %d\n"
+ "\taid (optional): %d\n",
+ __func__, mm_vif->id, key->hw_key_idx, aid);
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_DISABLE_KEY, mm_vif->id,
+ sizeof(req));
+
+ req.aid = cpu_to_le32(aid);
+ req.key_idx = key->hw_key_idx;
+ req.key_type = cpu_to_le32((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ HOST_CMD_TEMPORAL_KEY_TYPE_PTK :
+ HOST_CMD_TEMPORAL_KEY_TYPE_GTK);
+
+ return mm81x_cmd_tx(mm, NULL, (struct host_cmd_req *)&req, 0, 0);
+}
+
+int mm81x_cmd_install_key(struct mm81x *mm, struct mm81x_vif *mm_vif, u16 aid,
+ struct ieee80211_key_conf *key,
+ enum host_cmd_key_cipher cipher,
+ enum host_cmd_aes_key_len length)
+{
+ int ret;
+ struct host_cmd_req_install_key req;
+ struct host_cmd_resp_install_key resp;
+
+ mm81x_dbg(mm, MM81X_DBG_ANY,
+ "%s Installing key for vif (%d):\n"
+ "\tkey->idx: %d\n"
+ "\tkey->cipher: 0x%08x\n"
+ "\tkey->pn: %lld\n"
+ "\tkey->len: %d\n"
+ "\tkey->flags: 0x%08x\n"
+ "\taid (optional): %d\n",
+ __func__, mm_vif->id, key->keyidx, key->cipher,
+ (u64)atomic64_read(&key->tx_pn), key->keylen, key->flags,
+ aid);
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_INSTALL_KEY, mm_vif->id,
+ sizeof(req));
+
+ req.pn = cpu_to_le64(atomic64_read(&key->tx_pn));
+ req.aid = cpu_to_le32(aid);
+ req.cipher = cipher;
+ req.key_length = length;
+ req.key_idx = key->keyidx;
+ req.key_type = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ HOST_CMD_TEMPORAL_KEY_TYPE_PTK :
+ HOST_CMD_TEMPORAL_KEY_TYPE_GTK;
+
+ if (key->keylen > sizeof(req.key))
+ return -EINVAL;
+
+ memcpy(req.key, key->key, key->keylen);
+
+ ret = mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, sizeof(resp), 0);
+
+ if (ret == 0) {
+ key->hw_key_idx = resp.key_idx;
+ mm81x_dbg(mm, MM81X_DBG_ANY, "Installed key @ hw index: %d",
+ resp.key_idx);
+ }
+
+ return ret;
+}
+
+int mm81x_cmd_cfg_multicast_filter(struct mm81x *mm, struct mm81x_vif *mm_vif)
+{
+ struct host_cmd_req_mcast_filter *req;
+ struct mcast_filter *filter = mm->mcast_filter;
+ u16 filter_list_len = sizeof(filter->addr_list[0]) * filter->count;
+ u16 alloc_len = filter_list_len + sizeof(*req);
+ int ret = 0;
+
+ req = kmalloc(alloc_len, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ mm81x_cmd_init(mm, &req->hdr, HOST_CMD_ID_MCAST_FILTER, mm_vif->id,
+ alloc_len);
+
+ req->count = filter->count;
+ memcpy(req->hw_addr, filter->addr_list, filter_list_len);
+
+ ret = mm81x_cmd_tx(mm, NULL, (struct host_cmd_req *)req, 0, 0);
+ kfree(req);
+ return ret;
+}
+
+int mm81x_cmd_cfg_bss(struct mm81x *mm, u16 vif_id, u16 beacon_int,
+ u16 dtim_period, u32 cssid)
+{
+ struct host_cmd_req_bss_config req;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_BSS_CONFIG, vif_id,
+ sizeof(req));
+
+ req.beacon_interval_tu = cpu_to_le16(beacon_int);
+ req.cssid = cpu_to_le32(cssid);
+ req.dtim_period = cpu_to_le16(dtim_period);
+
+ return mm81x_cmd_tx(mm, NULL, (struct host_cmd_req *)&req, 0, 0);
+}
+
+int mm81x_cmd_config_beacon_timer(struct mm81x *mm, void *mm81x_vif,
+ bool enabled)
+{
+ struct host_cmd_req_bss_beacon_config req;
+ struct host_cmd_resp_bss_beacon_config resp;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_BSS_BEACON_CONFIG,
+ ((struct mm81x_vif *)mm81x_vif)->id, sizeof(req));
+ req.enable = enabled;
+
+ return mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, 0, 0);
+}
+
+int mm81x_cmd_set_ps(struct mm81x *mm, bool enabled)
+{
+ struct host_cmd_req_config_ps req;
+
+ if (!mm->ps.enable)
+ return 0;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_CONFIG_PS, 0, sizeof(req));
+
+ req.enabled = (u8)enabled;
+ req.dynamic_ps_offload = false;
+
+ return mm81x_cmd_tx(mm, NULL, (struct host_cmd_req *)&req, 0,
+ HOST_CMD_POWERSAVE_TIMEOUT_MS);
+}
+
+int mm81x_cmd_cfg_qos(struct mm81x *mm, struct mm81x_queue_params *params)
+{
+ struct host_cmd_req_set_qos_params req;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_SET_QOS_PARAMS, 0,
+ sizeof(req));
+
+ req.uapsd = params->uapsd;
+ req.queue_idx = params->aci;
+ req.aifs_slot_count = params->aifs;
+ req.contention_window_min = cpu_to_le16(params->cw_min);
+ req.contention_window_max = cpu_to_le16(params->cw_max);
+ req.max_txop_usec = cpu_to_le32(params->txop);
+
+ return mm81x_cmd_tx(mm, NULL, (struct host_cmd_req *)&req, 0, 0);
+}
+
+int mm81x_cmd_rm_if(struct mm81x *mm, u16 vif_id)
+{
+ struct host_cmd_req_remove_interface req;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_REMOVE_INTERFACE, vif_id,
+ sizeof(req));
+
+ return mm81x_cmd_tx(mm, NULL, (struct host_cmd_req *)&req, 0, 0);
+}
+
+int mm81x_cmd_set_frag_threshold(struct mm81x *mm, u32 frag_threshold)
+{
+ struct host_cmd_req_get_set_generic_param req = {
+ .param_id = cpu_to_le32(HOST_CMD_PARAM_ID_FRAGMENT_THRESHOLD),
+ .action = cpu_to_le32(HOST_CMD_PARAM_ACTION_SET),
+ .value = cpu_to_le32(frag_threshold),
+ };
+ struct host_cmd_resp_get_set_generic_param resp;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_GET_SET_GENERIC_PARAM, 0,
+ sizeof(req));
+
+ return mm81x_cmd_tx(mm, (struct host_cmd_resp *)&resp,
+ (struct host_cmd_req *)&req, sizeof(resp), 0);
+}
+
+int mm81x_cmd_get_disabled_channels(
+ struct mm81x *mm, struct host_cmd_resp_get_disabled_channels *resp,
+ uint resp_len)
+{
+ struct host_cmd_req req;
+
+ mm81x_cmd_init(mm, &req.hdr, HOST_CMD_ID_GET_DISABLED_CHANNELS, 0,
+ sizeof(req));
+
+ return mm81x_cmd_tx(mm, (struct host_cmd_resp *)resp, &req, resp_len,
+ 0);
+}
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 03/35] wifi: mm81x: add command_defs.h
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
.../wireless/morsemicro/mm81x/command_defs.h | 1668 +++++++++++++++++
1 file changed, 1668 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/command_defs.h
diff --git a/drivers/net/wireless/morsemicro/mm81x/command_defs.h b/drivers/net/wireless/morsemicro/mm81x/command_defs.h
new file mode 100644
index 000000000000..71e6f21bf658
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/command_defs.h
@@ -0,0 +1,1668 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+#ifndef _MM81X_COMMAND_DEFS_H_
+#define _MM81X_COMMAND_DEFS_H_
+
+#include <linux/types.h>
+
+#define __sle16 __le16
+#define __sle32 __le32
+#define __sle64 __le64
+
+#define HOST_CMD_SEMVER_MAJOR 56
+#define HOST_CMD_SEMVER_MINOR 24
+#define HOST_CMD_SEMVER_PATCH 0
+
+#define HOST_CMD_TYPE_REQ BIT(0)
+#define HOST_CMD_TYPE_RESP BIT(1)
+#define HOST_CMD_TYPE_EVT BIT(2)
+
+#define HOST_CMD_SSID_MAX_LEN 32
+#define HOST_CMD_MAC_ADDR_LEN 6
+
+enum host_cmd_id {
+ HOST_CMD_ID_SET_CHANNEL = 0x0001,
+ HOST_CMD_ID_GET_CHANNEL = 0x001D,
+ HOST_CMD_ID_GET_CHANNEL_FULL = 0x0013,
+ HOST_CMD_ID_GET_CHANNEL_DTIM = 0x001C,
+ HOST_CMD_ID_GET_VERSION = 0x0002,
+ HOST_CMD_ID_SET_TXPOWER = 0x0003,
+ HOST_CMD_ID_GET_MAX_TXPOWER = 0x0024,
+ HOST_CMD_ID_ADD_INTERFACE = 0x0004,
+ HOST_CMD_ID_REMOVE_INTERFACE = 0x0005,
+ HOST_CMD_ID_BSS_CONFIG = 0x0006,
+ HOST_CMD_ID_SCAN_CONFIG = 0x0010,
+ HOST_CMD_ID_SET_QOS_PARAMS = 0x0011,
+ HOST_CMD_ID_GET_QOS_PARAMS = 0x0012,
+ HOST_CMD_ID_SET_STA_STATE = 0x0014,
+ HOST_CMD_ID_SET_BSS_COLOR = 0x0015,
+ HOST_CMD_ID_CONFIG_PS = 0x0016,
+ HOST_CMD_ID_HEALTH_CHECK = 0x0019,
+ HOST_CMD_ID_CTS_SELF_PS = 0x001A,
+ HOST_CMD_ID_DTIM_CHANNEL_ENABLE = 0x001B,
+ HOST_CMD_ID_ARP_OFFLOAD = 0x0020,
+ HOST_CMD_ID_SET_LONG_SLEEP_CONFIG = 0x0021,
+ HOST_CMD_ID_SET_DUTY_CYCLE = 0x0022,
+ HOST_CMD_ID_GET_DUTY_CYCLE = 0x0023,
+ HOST_CMD_ID_GET_CAPABILITIES = 0x0025,
+ HOST_CMD_ID_TWT_AGREEMENT_INSTALL = 0x0026,
+ HOST_CMD_ID_TWT_AGREEMENT_VALIDATE = 0x0036,
+ HOST_CMD_ID_TWT_AGREEMENT_REMOVE = 0x0027,
+ HOST_CMD_ID_GET_TSF = 0x0028,
+ HOST_CMD_ID_MAC_ADDR = 0x0029,
+ HOST_CMD_ID_MPSW_CONFIG = 0x0030,
+ HOST_CMD_ID_INSTALL_KEY = 0x000A,
+ HOST_CMD_ID_DISABLE_KEY = 0x000B,
+ HOST_CMD_ID_DHCP_OFFLOAD = 0x0032,
+ HOST_CMD_ID_SET_KEEP_ALIVE_OFFLOAD = 0x0033,
+ HOST_CMD_ID_UPDATE_OUI_FILTER = 0x0034,
+ HOST_CMD_ID_IBSS_CONFIG = 0x0035,
+ HOST_CMD_ID_OCS = 0x0038,
+ HOST_CMD_ID_MESH_CONFIG = 0x0039,
+ HOST_CMD_ID_SET_OFFSET_TSF = 0x003A,
+ HOST_CMD_ID_GET_CHANNEL_USAGE = 0x003B,
+ HOST_CMD_ID_MCAST_FILTER = 0x003C,
+ HOST_CMD_ID_BSS_BEACON_CONFIG = 0x003D,
+ HOST_CMD_ID_UAPSD_CONFIG = 0x0040,
+ HOST_CMD_ID_PAGE_SLICING_CONFIG = 0x0043,
+ HOST_CMD_ID_HW_SCAN = 0x0044,
+ HOST_CMD_ID_SET_WHITELIST = 0x0045,
+ HOST_CMD_ID_ARP_PERIODIC_REFRESH = 0x0046,
+ HOST_CMD_ID_SET_TCP_KEEPALIVE = 0x0047,
+ HOST_CMD_ID_FORCE_POWER_MODE = 0x0048,
+ HOST_CMD_ID_LI_SLEEP = 0x0049,
+ HOST_CMD_ID_GET_DISABLED_CHANNELS = 0x004A,
+ HOST_CMD_ID_SET_CQM_RSSI = 0x004F,
+ HOST_CMD_ID_GET_APF_CAPABILITIES = 0x0050,
+ HOST_CMD_ID_READ_WRITE_APF = 0x0051,
+ HOST_CMD_ID_BSSID_SET = 0x0052,
+ HOST_CMD_ID_BEACON_OFFLOAD = 0x0053,
+ HOST_CMD_ID_PROBE_RESPONSE_OFFLOAD = 0x0054,
+ HOST_CMD_ID_HOST_STATS_LOG = 0x2007,
+ HOST_CMD_ID_HOST_STATS_RESET = 0x2008,
+ HOST_CMD_ID_MAC_STATS_LOG = 0x200C,
+ HOST_CMD_ID_MAC_STATS_RESET = 0x200D,
+ HOST_CMD_ID_UPHY_STATS_LOG = 0x200E,
+ HOST_CMD_ID_UPHY_STATS_RESET = 0x200F,
+ HOST_CMD_ID_SET_STA_TYPE = 0xA000,
+ HOST_CMD_ID_SET_ENC_MODE = 0xA001,
+ HOST_CMD_ID_TEST_BA = 0xA002,
+ HOST_CMD_ID_SET_LISTEN_INTERVAL = 0xA003,
+ HOST_CMD_ID_SET_AMPDU = 0xA004,
+ HOST_CMD_ID_COREDUMP = 0xA006,
+ HOST_CMD_ID_SET_S1G_OP_CLASS = 0xA007,
+ HOST_CMD_ID_SEND_WAKE_ACTION_FRAME = 0xA008,
+ HOST_CMD_ID_VENDOR_IE_CONFIG = 0xA009,
+ HOST_CMD_ID_SET_TWT_CONF = 0xA010,
+ HOST_CMD_ID_GET_AVAILABLE_CHANNELS = 0xA011,
+ HOST_CMD_ID_SET_ECSA_S1G_INFO = 0xA012,
+ HOST_CMD_ID_GET_HW_VERSION = 0xA013,
+ HOST_CMD_ID_CAC = 0xA014,
+ HOST_CMD_ID_DRIVER_SET_DUTY_CYCLE = 0xA015,
+ HOST_CMD_ID_OCS_DRIVER = 0xA017,
+ HOST_CMD_ID_MBSSID = 0xA016,
+ HOST_CMD_ID_SET_MESH_CONFIG = 0xA018,
+ HOST_CMD_ID_SET_MCBA_CONF = 0xA019,
+ HOST_CMD_ID_DYNAMIC_PEERING_CONFIG = 0xA020,
+ HOST_CMD_ID_CONFIG_RAW = 0xA021,
+ HOST_CMD_ID_CONFIG_BSS_STATS = 0xA022,
+ HOST_CMD_ID_GET_RSSI = 0x1002,
+ HOST_CMD_ID_SET_IFS = 0x1003,
+ HOST_CMD_ID_SET_FEM_SETTINGS = 0x1005,
+ HOST_CMD_ID_SET_TXOP = 0x1008,
+ HOST_CMD_ID_SET_CONTROL_RESPONSE = 0x1009,
+ HOST_CMD_ID_SET_PERIODIC_CAL = 0x100A,
+ HOST_CMD_ID_SET_BCN_RSSI_THRESHOLD = 0x100B,
+ HOST_CMD_ID_SET_TX_PKT_LIFETIME_USECS = 0x100C,
+ HOST_CMD_ID_SET_PHYSM_WATCHDOG = 0x100D,
+ HOST_CMD_ID_TX_POLAR = 0x100E,
+ HOST_CMD_ID_EVT_STA_STATE = 0x4001,
+ HOST_CMD_ID_EVT_BEACON_LOSS = 0x4002,
+ HOST_CMD_ID_EVT_SIG_FIELD_ERROR = 0x4003,
+ HOST_CMD_ID_EVT_UMAC_TRAFFIC_CONTROL = 0x4004,
+ HOST_CMD_ID_EVT_DHCP_LEASE_UPDATE = 0x4005,
+ HOST_CMD_ID_EVT_OCS_DONE = 0x4006,
+ HOST_CMD_ID_EVT_HW_SCAN_DONE = 0x4011,
+ HOST_CMD_ID_EVT_CHANNEL_USAGE = 0x4012,
+ HOST_CMD_ID_EVT_CONNECTION_LOSS = 0x4013,
+ HOST_CMD_ID_EVT_SCHED_SCAN_RESULTS = 0x4014,
+ HOST_CMD_ID_EVT_CQM_RSSI_NOTIFY = 0x4015,
+ HOST_CMD_ID_EVT_NDP_PROBE_REQUEST_RECEIVED = 0x4017,
+ HOST_CMD_ID_EVT_SCAN_DONE = 0x4007,
+ HOST_CMD_ID_EVT_SCAN_RESULT = 0x4008,
+ HOST_CMD_ID_EVT_CONNECTED = 0x4009,
+ HOST_CMD_ID_EVT_DISCONNECTED = 0x4010,
+ HOST_CMD_ID_EVT_BEACON_FILTER_MATCH = 0x4016,
+ HOST_CMD_ID_SET_CAPABILITIES = 0x8118,
+ HOST_CMD_ID_SET_TRANSMISSION_RATE = 0x8009,
+ HOST_CMD_ID_FORCE_ASSERT = 0x800E,
+ HOST_CMD_ID_GET_SET_GENERIC_PARAM = 0x003E,
+};
+
+struct host_cmd_mac_addr {
+ u8 octet[HOST_CMD_MAC_ADDR_LEN];
+};
+
+enum host_cmd_ocs_subcmd {
+ HOST_CMD_OCS_SUBCMD_CONFIG = 1,
+ HOST_CMD_OCS_SUBCMD_STATUS = 2,
+};
+
+enum host_cmd_headless_cfg_option {
+ HOST_CMD_HEADLESS_CFG_OPTION_KEEP_IFACES = BIT(0),
+ HOST_CMD_HEADLESS_CFG_OPTION_BUFFER_RX = BIT(1),
+ HOST_CMD_HEADLESS_CFG_OPTION_NOTIFY_ON_ANY_RX = BIT(2),
+};
+
+struct host_cmd_header {
+ __le16 flags;
+ __le16 message_id;
+ __le16 len;
+ __le16 host_id;
+ __le16 vif_id;
+ __le16 pad;
+};
+
+#define HOST_CMD_CHANNEL_BW_NOT_SET 0xFF
+#define HOST_CMD_CHANNEL_IDX_NOT_SET 0xFF
+#define HOST_CMD_CHANNEL_FREQ_NOT_SET 0xFFFFFFFF
+
+enum host_cmd_dot11_proto_mode {
+ HOST_CMD_DOT11_PROTO_MODE_AH = 0,
+};
+
+struct host_cmd_req_set_channel {
+ struct host_cmd_header hdr;
+ __le32 op_chan_freq_hz;
+ u8 op_bw_mhz;
+ u8 pri_bw_mhz;
+ u8 pri_1mhz_chan_idx;
+ u8 dot11_mode;
+ u8 __deprecated_reg_tx_power_set;
+ u8 is_off_channel;
+} __packed;
+
+struct host_cmd_resp_set_channel {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __sle32 power_qdbm;
+} __packed;
+
+struct host_cmd_req_get_channel {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_channel {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 op_chan_freq_hz;
+ u8 op_chan_bw_mhz;
+ u8 pri_chan_bw_mhz;
+ u8 pri_1mhz_chan_idx;
+} __packed;
+
+#define HOST_CMD_MAX_VERSION_LEN 128
+
+struct host_cmd_req_get_version {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_version {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __sle32 length;
+ u8 version[];
+} __packed;
+
+struct host_cmd_req_set_txpower {
+ struct host_cmd_header hdr;
+ __sle32 power_qdbm;
+} __packed;
+
+struct host_cmd_resp_set_txpower {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __sle32 power_qdbm;
+} __packed;
+
+struct host_cmd_req_get_max_txpower {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_max_txpower {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __sle32 power_qdbm;
+} __packed;
+
+enum host_cmd_interface_type {
+ HOST_CMD_INTERFACE_TYPE_INVALID = 0,
+ HOST_CMD_INTERFACE_TYPE_STA = 1,
+ HOST_CMD_INTERFACE_TYPE_AP = 2,
+ HOST_CMD_INTERFACE_TYPE_MON = 3,
+ HOST_CMD_INTERFACE_TYPE_ADHOC = 4,
+ HOST_CMD_INTERFACE_TYPE_MESH = 5,
+ HOST_CMD_INTERFACE_TYPE_LAST = HOST_CMD_INTERFACE_TYPE_MESH,
+};
+
+struct host_cmd_req_add_interface {
+ struct host_cmd_header hdr;
+ struct host_cmd_mac_addr addr;
+ __le32 interface_type;
+} __packed;
+
+struct host_cmd_resp_add_interface {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_remove_interface {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_remove_interface {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_bss_config {
+ struct host_cmd_header hdr;
+ __le16 beacon_interval_tu;
+ __le16 dtim_period;
+ u8 __padding[2];
+ __le32 cssid;
+} __packed;
+
+struct host_cmd_resp_bss_config {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_scan_config {
+ struct host_cmd_header hdr;
+ u8 enabled;
+ u8 is_survey;
+} __packed;
+
+struct host_cmd_resp_scan_config {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_qos_params {
+ struct host_cmd_header hdr;
+ u8 uapsd;
+ u8 queue_idx;
+ u8 aifs_slot_count;
+ __le16 contention_window_min;
+ __le16 contention_window_max;
+ __le32 max_txop_usec;
+} __packed;
+
+struct host_cmd_resp_set_qos_params {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_get_qos_params {
+ struct host_cmd_header hdr;
+ u8 queue_idx;
+} __packed;
+
+struct host_cmd_resp_get_qos_params {
+ struct host_cmd_header hdr;
+ __le32 status;
+ u8 aifs_slot_count;
+ __le16 contention_window_min;
+ __le16 contention_window_max;
+ __le32 max_txop_usec;
+} __packed;
+
+struct host_cmd_req_set_sta_state {
+ struct host_cmd_header hdr;
+ u8 sta_addr[HOST_CMD_MAC_ADDR_LEN];
+ __le16 aid;
+ __le16 state;
+ u8 uapsd_queues;
+ __le32 flags;
+} __packed;
+
+struct host_cmd_resp_set_sta_state {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_bss_color {
+ struct host_cmd_header hdr;
+ u8 bss_color;
+} __packed;
+
+struct host_cmd_resp_set_bss_color {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_config_ps {
+ struct host_cmd_header hdr;
+ u8 enabled;
+ u8 dynamic_ps_offload;
+} __packed;
+
+struct host_cmd_resp_config_ps {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_health_check {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_health_check {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_cts_self_ps {
+ struct host_cmd_header hdr;
+ u8 enable;
+} __packed;
+
+struct host_cmd_resp_cts_self_ps {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_dtim_channel_enable {
+ struct host_cmd_header hdr;
+ u8 enable;
+} __packed;
+
+struct host_cmd_resp_dtim_channel_enable {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+#define HOST_CMD_ARP_OFFLOAD_MAX_IP_ADDRESSES 4
+
+struct host_cmd_req_arp_offload {
+ struct host_cmd_header hdr;
+ __be32 ip_table[HOST_CMD_ARP_OFFLOAD_MAX_IP_ADDRESSES];
+} __packed;
+
+struct host_cmd_resp_arp_offload {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_long_sleep_config {
+ struct host_cmd_header hdr;
+ u8 enabled;
+} __packed;
+
+struct host_cmd_resp_set_long_sleep_config {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+#define HOST_CMD_DUTY_CYCLE_SET_CFG_DUTY_CYCLE BIT(0)
+#define HOST_CMD_DUTY_CYCLE_SET_CFG_OMIT_CONTROL_RESP BIT(1)
+#define HOST_CMD_DUTY_CYCLE_SET_CFG_EXT BIT(2)
+#define HOST_CMD_DUTY_CYCLE_SET_CFG_BURST_RECORD_UNIT BIT(3)
+
+enum host_cmd_duty_cycle_mode {
+ HOST_CMD_DUTY_CYCLE_MODE_SPREAD = 0,
+ HOST_CMD_DUTY_CYCLE_MODE_BURST = 1,
+ HOST_CMD_DUTY_CYCLE_MODE_LAST = HOST_CMD_DUTY_CYCLE_MODE_BURST,
+};
+
+struct host_cmd_duty_cycle_configuration {
+ u8 omit_control_responses;
+ __le32 duty_cycle;
+} __packed;
+
+struct host_cmd_duty_cycle_set_configuration_ext {
+ __le32 burst_record_unit_us;
+ u8 mode;
+} __packed;
+
+struct host_cmd_duty_cycle_configuration_ext {
+ __le32 airtime_remaining_us;
+ __le32 burst_window_duration_us;
+ struct host_cmd_duty_cycle_set_configuration_ext set;
+} __packed;
+
+struct host_cmd_req_set_duty_cycle {
+ struct host_cmd_header hdr;
+ struct host_cmd_duty_cycle_configuration config;
+ u8 set_cfgs;
+ struct host_cmd_duty_cycle_set_configuration_ext config_ext;
+} __packed;
+
+struct host_cmd_resp_get_duty_cycle {
+ struct host_cmd_header hdr;
+ __le32 status;
+ struct host_cmd_duty_cycle_configuration config;
+ struct host_cmd_duty_cycle_configuration_ext config_ext;
+} __packed;
+
+#define HOST_CMD_SET_S1G_CAP_FLAGS BIT(0)
+#define HOST_CMD_SET_S1G_CAP_AMPDU_MSS BIT(1)
+#define HOST_CMD_SET_S1G_CAP_BEAM_STS BIT(2)
+#define HOST_CMD_SET_S1G_CAP_NUM_SOUND_DIMS BIT(3)
+#define HOST_CMD_SET_S1G_CAP_MAX_AMPDU_LEXP BIT(4)
+#define HOST_CMD_SET_MORSE_CAP_MMSS_OFFSET BIT(5)
+#define HOST_CMD_S1G_CAPABILITY_FLAGS_WIDTH 4
+
+struct host_cmd_mm_capabilities {
+ __le32 flags[HOST_CMD_S1G_CAPABILITY_FLAGS_WIDTH];
+ u8 ampdu_mss;
+ u8 beamformee_sts_capability;
+ u8 number_sounding_dimensions;
+ u8 maximum_ampdu_length_exponent;
+} __packed;
+
+struct host_cmd_req_get_capabilities {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_capabilities {
+ struct host_cmd_header hdr;
+ __le32 status;
+ struct host_cmd_mm_capabilities capabilities;
+ u8 morse_mmss_offset;
+} __packed;
+
+#define HOST_CMD_DOT11_TWT_AGREEMENT_MAX_LEN 20
+
+struct host_cmd_req_twt_agreement_install {
+ struct host_cmd_header hdr;
+ u8 flow_id;
+ u8 agreement_len;
+ u8 agreement[HOST_CMD_DOT11_TWT_AGREEMENT_MAX_LEN];
+} __packed;
+
+struct host_cmd_resp_twt_agreement_install {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_twt_agreement_validate {
+ struct host_cmd_header hdr;
+ u8 flow_id;
+ u8 agreement_len;
+ u8 agreement[HOST_CMD_DOT11_TWT_AGREEMENT_MAX_LEN];
+} __packed;
+
+struct host_cmd_resp_twt_agreement_validate {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_twt_agreement_remove {
+ struct host_cmd_header hdr;
+ u8 flow_id;
+} __packed;
+
+struct host_cmd_req_get_tsf {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_tsf {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le64 now_tsf;
+ __le64 now_chip_ts;
+} __packed;
+
+struct host_cmd_req_mac_addr {
+ struct host_cmd_header hdr;
+ u8 write;
+ u8 octet[HOST_CMD_MAC_ADDR_LEN];
+} __packed;
+
+struct host_cmd_resp_mac_addr {
+ struct host_cmd_header hdr;
+ __le32 status;
+ u8 octet[HOST_CMD_MAC_ADDR_LEN];
+} __packed;
+
+#define HOST_CMD_SET_MPSW_CFG_AIRTIME_BOUNDS BIT(0)
+#define HOST_CMD_SET_MPSW_CFG_PKT_SPC_WIN_LEN BIT(1)
+#define HOST_CMD_SET_MPSW_CFG_ENABLED BIT(2)
+
+struct host_cmd_mpsw_configuration {
+ __le32 airtime_max_us;
+ __le32 airtime_min_us;
+ __le32 packet_space_window_length_us;
+ u8 enable;
+} __packed;
+
+struct host_cmd_req_mpsw_config {
+ struct host_cmd_header hdr;
+ struct host_cmd_mpsw_configuration config;
+ u8 set_cfgs;
+} __packed;
+
+struct host_cmd_resp_mpsw_config {
+ struct host_cmd_header hdr;
+ __le32 status;
+ struct host_cmd_mpsw_configuration config;
+} __packed;
+
+#define HOST_CMD_MAX_KEY_LEN 32
+
+enum host_cmd_key_cipher {
+ HOST_CMD_KEY_CIPHER_INVALID = 0,
+ HOST_CMD_KEY_CIPHER_AES_CCM = 1,
+ HOST_CMD_KEY_CIPHER_AES_GCM = 2,
+ HOST_CMD_KEY_CIPHER_AES_CMAC = 3,
+ HOST_CMD_KEY_CIPHER_AES_GMAC = 4,
+ HOST_CMD_KEY_CIPHER_LAST = HOST_CMD_KEY_CIPHER_AES_GMAC,
+};
+
+enum host_cmd_aes_key_len {
+ HOST_CMD_AES_KEY_LEN_INVALID = 0,
+ HOST_CMD_AES_KEY_LEN_LENGTH_128 = 1,
+ HOST_CMD_AES_KEY_LEN_LENGTH_256 = 2,
+ HOST_CMD_AES_KEY_LEN_LENGTH_LAST = HOST_CMD_AES_KEY_LEN_LENGTH_256,
+};
+
+enum host_cmd_temporal_key_type {
+ HOST_CMD_TEMPORAL_KEY_TYPE_INVALID = 0,
+ HOST_CMD_TEMPORAL_KEY_TYPE_GTK = 1,
+ HOST_CMD_TEMPORAL_KEY_TYPE_PTK = 2,
+ HOST_CMD_TEMPORAL_KEY_TYPE_IGTK = 3,
+ HOST_CMD_TEMPORAL_KEY_TYPE_LAST = HOST_CMD_TEMPORAL_KEY_TYPE_IGTK,
+};
+
+struct host_cmd_req_install_key {
+ struct host_cmd_header hdr;
+ __le64 pn;
+ __le32 aid;
+ u8 key_idx;
+ u8 cipher;
+ u8 key_length;
+ u8 key_type;
+ u8 __padding[2];
+ u8 key[HOST_CMD_MAX_KEY_LEN];
+} __packed;
+
+struct host_cmd_resp_install_key {
+ struct host_cmd_header hdr;
+ __le32 status;
+ u8 key_idx;
+} __packed;
+
+struct host_cmd_req_disable_key {
+ struct host_cmd_header hdr;
+ __le32 key_type;
+ __le32 aid;
+ u8 key_idx;
+} __packed;
+
+struct host_cmd_resp_disable_key {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+enum host_cmd_dhcp_opcode {
+ HOST_CMD_DHCP_OPCODE_ENABLE = 0,
+ HOST_CMD_DHCP_OPCODE_DO_DISCOVERY = 1,
+ HOST_CMD_DHCP_OPCODE_GET_LEASE = 2,
+ HOST_CMD_DHCP_OPCODE_CLEAR_LEASE = 3,
+ HOST_CMD_DHCP_OPCODE_RENEW_LEASE = 4,
+ HOST_CMD_DHCP_OPCODE_REBIND_LEASE = 5,
+ HOST_CMD_DHCP_OPCODE_SEND_LEASE_UPDATE = 6,
+};
+
+enum host_cmd_dhcp_retcode {
+ HOST_CMD_DHCP_RETCODE_SUCCESS = 0,
+ HOST_CMD_DHCP_RETCODE_NOT_ENABLED = 1,
+ HOST_CMD_DHCP_RETCODE_ALREADY_ENABLED = 2,
+ HOST_CMD_DHCP_RETCODE_NO_LEASE = 3,
+ HOST_CMD_DHCP_RETCODE_HAVE_LEASE = 4,
+ HOST_CMD_DHCP_RETCODE_BUSY = 5,
+ HOST_CMD_DHCP_RETCODE_BAD_VIF = 6,
+};
+
+struct host_cmd_req_dhcp_offload {
+ struct host_cmd_header hdr;
+ __le32 opcode;
+} __packed;
+
+struct host_cmd_resp_dhcp_offload {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 retcode;
+ __le32 my_ip;
+ __le32 netmask;
+ __le32 router;
+ __le32 dns;
+} __packed;
+
+struct host_cmd_req_set_keep_alive_offload {
+ struct host_cmd_header hdr;
+ __le16 bss_max_idle_period;
+ u8 interpret_as_11ah;
+} __packed;
+
+#define HOST_CMD_MAX_OUI_FILTERS 5
+#define HOST_CMD_OUI_SIZE 3
+#define HOST_CMD_MAX_OUI_FILTER_ARRAY_SIZE 15
+
+struct host_cmd_req_update_oui_filter {
+ struct host_cmd_header hdr;
+ u8 n_ouis;
+ u8 ouis[HOST_CMD_MAX_OUI_FILTERS][HOST_CMD_OUI_SIZE];
+} __packed;
+
+enum host_cmd_ibss_config_opcode {
+ HOST_CMD_IBSS_CONFIG_OPCODE_CREATE = 0,
+ HOST_CMD_IBSS_CONFIG_OPCODE_JOIN = 1,
+ HOST_CMD_IBSS_CONFIG_OPCODE_STOP = 2,
+};
+
+struct host_cmd_req_ibss_config {
+ struct host_cmd_header hdr;
+ u8 ibss_bssid[HOST_CMD_MAC_ADDR_LEN];
+ u8 ibss_cfg_opcode;
+ u8 ibss_probe_filtering;
+} __packed;
+
+enum host_cmd_ocs_type {
+ HOST_CMD_OCS_TYPE_QNULL = 0,
+ HOST_CMD_OCS_TYPE_RAW = 1,
+};
+
+struct host_cmd_ocs_config_req {
+ __le32 op_channel_freq_hz;
+ u8 op_channel_bw_mhz;
+ u8 pri_channel_bw_mhz;
+ u8 pri_1mhz_channel_index;
+ __le16 aid;
+ u8 type;
+} __packed;
+
+struct host_cmd_ocs_status_resp {
+ u8 running;
+} __packed;
+
+struct host_cmd_req_ocs {
+ struct host_cmd_header hdr;
+ __le32 subcmd;
+ union {
+ u8 opaque[0];
+ struct host_cmd_ocs_config_req config;
+ };
+} __packed;
+
+struct host_cmd_resp_ocs {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 subcmd;
+ union {
+ u8 opaque[0];
+ struct host_cmd_ocs_status_resp ocs_status;
+ };
+} __packed;
+
+enum host_cmd_mesh_config_opcode {
+ HOST_CMD_MESH_CONFIG_OPCODE_START = 0,
+ HOST_CMD_MESH_CONFIG_OPCODE_STOP = 1,
+};
+
+struct host_cmd_req_mesh_config {
+ struct host_cmd_header hdr;
+ u8 mesh_cfg_opcode;
+ u8 enable_beaconing;
+ u8 mbca_config;
+ u8 min_beacon_gap_ms;
+ __le16 mbss_start_scan_duration_ms;
+ __le16 tbtt_adj_timer_interval_ms;
+} __packed;
+
+struct host_cmd_req_set_offset_tsf {
+ struct host_cmd_header hdr;
+ __sle64 offset_tsf;
+} __packed;
+
+struct host_cmd_req_get_channel_usage {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_channel_usage {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le64 time_listen;
+ __le64 busy_time;
+ __le32 freq_hz;
+ s8 noise;
+ u8 bw_mhz;
+} __packed;
+
+#define HOST_CMD_MAX_MCAST_FILTERS 12
+
+struct host_cmd_req_mcast_filter {
+ struct host_cmd_header hdr;
+ u8 count;
+ __le32 hw_addr[];
+} __packed;
+
+struct host_cmd_req_bss_beacon_config {
+ struct host_cmd_header hdr;
+ u8 enable;
+} __packed;
+
+struct host_cmd_resp_bss_beacon_config {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le16 interface_id;
+} __packed;
+
+struct host_cmd_req_uapsd_config {
+ struct host_cmd_header hdr;
+ u8 auto_trigger_enabled;
+ __le32 auto_trigger_timeout;
+} __packed;
+
+struct host_cmd_resp_uapsd_config {
+ struct host_cmd_header hdr;
+ __le32 status;
+ u8 auto_trigger_enabled;
+} __packed;
+
+struct host_cmd_req_page_slicing_config {
+ struct host_cmd_header hdr;
+ u8 enable;
+} __packed;
+
+#define HOST_CMD_HW_SCAN_FLAGS_START BIT(0)
+#define HOST_CMD_HW_SCAN_FLAGS_ABORT BIT(1)
+#define HOST_CMD_HW_SCAN_FLAGS_SURVEY BIT(2)
+#define HOST_CMD_HW_SCAN_FLAGS_STORE BIT(3)
+#define HOST_CMD_HW_SCAN_FLAGS_1MHZ_PROBES BIT(4)
+#define HOST_CMD_HW_SCAN_FLAGS_SCHED_START BIT(5)
+#define HOST_CMD_HW_SCAN_FLAGS_SCHED_STOP BIT(6)
+#define HOST_CMD_HW_SCAN_FLAGS_PROBE_ON_DOZE_BEACON BIT(7)
+
+enum host_cmd_hw_scan_tlv_tag {
+ HOST_CMD_HW_SCAN_TLV_TAG_PAD = 0,
+ HOST_CMD_HW_SCAN_TLV_TAG_PROBE_REQ = 1,
+ HOST_CMD_HW_SCAN_TLV_TAG_CHAN_LIST = 2,
+ HOST_CMD_HW_SCAN_TLV_TAG_POWER_LIST = 3,
+ HOST_CMD_HW_SCAN_TLV_TAG_DWELL_ON_HOME = 4,
+ HOST_CMD_HW_SCAN_TLV_TAG_SCHED = 5,
+ HOST_CMD_HW_SCAN_TLV_TAG_FILTER = 6,
+ HOST_CMD_HW_SCAN_TLV_TAG_SCHED_PARAMS = 7,
+};
+
+struct host_cmd_hw_scan_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 value[];
+} __packed;
+
+struct host_cmd_req_hw_scan {
+ struct host_cmd_header hdr;
+ __le32 flags;
+ __le32 dwell_time_ms;
+ u8 variable[];
+} __packed;
+
+#define HOST_CMD_WHITELIST_FLAGS_CLEAR BIT(0)
+
+struct host_cmd_req_set_whitelist {
+ struct host_cmd_header hdr;
+ u8 flags;
+ u8 ip_protocol;
+ __be16 llc_protocol;
+ __be32 src_ip;
+ __be32 dest_ip;
+ __be32 netmask;
+ __be16 src_port;
+ __be16 dest_port;
+} __packed;
+
+struct host_cmd_arp_periodic_params {
+ __le32 refresh_period_s;
+ __le32 destination_ip;
+ u8 send_as_garp;
+} __packed;
+
+struct host_cmd_req_arp_periodic_refresh {
+ struct host_cmd_header hdr;
+ struct host_cmd_arp_periodic_params config;
+} __packed;
+
+#define HOST_CMD_TCP_KEEPALIVE_SET_CFG_PERIOD BIT(0)
+#define HOST_CMD_TCP_KEEPALIVE_SET_CFG_RETRY_COUNT BIT(1)
+#define HOST_CMD_TCP_KEEPALIVE_SET_CFG_RETRY_INTERVAL BIT(2)
+#define HOST_CMD_TCP_KEEPALIVE_SET_CFG_SRC_IP_ADDR BIT(3)
+#define HOST_CMD_TCP_KEEPALIVE_SET_CFG_DEST_IP_ADDR BIT(4)
+#define HOST_CMD_TCP_KEEPALIVE_SET_CFG_SRC_PORT BIT(5)
+#define HOST_CMD_TCP_KEEPALIVE_SET_CFG_DEST_PORT BIT(6)
+
+struct host_cmd_req_set_tcp_keepalive {
+ struct host_cmd_header hdr;
+ u8 enabled;
+ u8 retry_count;
+ u8 retry_interval_s;
+ u8 set_cfgs;
+ __be32 src_ip;
+ __be32 dest_ip;
+ __be16 src_port;
+ __be16 dest_port;
+ __le16 period_s;
+} __packed;
+
+enum host_cmd_power_mode {
+ HOST_CMD_POWER_MODE_SNOOZE = 0,
+ HOST_CMD_POWER_MODE_DEEP_SLEEP = 1,
+ HOST_CMD_POWER_MODE_HIBERNATE = 2,
+};
+
+struct host_cmd_req_force_power_mode {
+ struct host_cmd_header hdr;
+ __le32 mode;
+} __packed;
+
+struct host_cmd_req_li_sleep {
+ struct host_cmd_header hdr;
+ __le32 listen_interval;
+} __packed;
+
+struct host_cmd_disabled_channel_entry {
+ __le16 freq_100khz;
+ u8 bw_mhz;
+} __packed;
+
+struct host_cmd_resp_get_disabled_channels {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 n_channels;
+ struct host_cmd_disabled_channel_entry channels[];
+} __packed;
+
+struct host_cmd_req_set_cqm_rssi {
+ struct host_cmd_header hdr;
+ __sle32 threshold;
+ __le32 hysteresis;
+} __packed;
+
+struct host_cmd_req_get_apf_capabilities {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_apf_capabilities {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 max_length;
+ u8 version;
+} __packed;
+
+struct host_cmd_req_read_write_apf {
+ struct host_cmd_header hdr;
+ __le32 offset;
+ __le16 program_length;
+ u8 write;
+ u8 program[];
+} __packed;
+
+struct host_cmd_resp_read_write_apf {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le16 program_length;
+ u8 program[];
+} __packed;
+
+struct host_cmd_req_bssid_set {
+ struct host_cmd_header hdr;
+ struct host_cmd_mac_addr bssid;
+} __packed;
+
+#define HOST_CMD_BEACON_OFFLOAD_FLAGS_START BIT(0)
+#define HOST_CMD_BEACON_OFFLOAD_FLAGS_STOP BIT(1)
+#define HOST_CMD_BEACON_OFFLOAD_CSSID_LEN 4
+
+enum host_cmd_beacon_offload_tlv_tag {
+ HOST_CMD_BEACON_OFFLOAD_TLV_TAG_DTIM_CNT = 0,
+ HOST_CMD_BEACON_OFFLOAD_TLV_TAG_FRAME_CTRL = 1,
+ HOST_CMD_BEACON_OFFLOAD_TLV_TAG_CHANGE_SEQ = 2,
+ HOST_CMD_BEACON_OFFLOAD_TLV_TAG_CSSID = 3,
+ HOST_CMD_BEACON_OFFLOAD_TLV_TAG_IES = 4,
+ HOST_CMD_BEACON_OFFLOAD_TLV_TAG_TX_INFO = 5,
+};
+
+struct host_cmd_beacon_offload_tlv_hdr {
+ __le16 tag;
+ __le16 len;
+} __packed;
+
+struct host_cmd_beacon_offload_tlv_generic {
+ struct host_cmd_beacon_offload_tlv_hdr hdr;
+ u8 value[];
+} __packed;
+
+struct host_cmd_beacon_offload_tlv_dtim_cnt {
+ struct host_cmd_beacon_offload_tlv_hdr hdr;
+ __le16 dtim_cnt;
+} __packed;
+
+struct host_cmd_beacon_offload_tlv_frame_ctrl {
+ struct host_cmd_beacon_offload_tlv_hdr hdr;
+ u8 frame_ctrl[2];
+} __packed;
+
+struct host_cmd_beacon_offload_tlv_change_seq {
+ struct host_cmd_beacon_offload_tlv_hdr hdr;
+ __le16 change_seq;
+} __packed;
+
+struct host_cmd_beacon_offload_tlv_tx_info {
+ struct host_cmd_beacon_offload_tlv_hdr hdr;
+ u8 bw_mhz;
+} __packed;
+
+struct host_cmd_beacon_offload_tlv_cssid {
+ struct host_cmd_beacon_offload_tlv_hdr hdr;
+ u8 cssid[HOST_CMD_BEACON_OFFLOAD_CSSID_LEN];
+} __packed;
+
+struct host_cmd_beacon_offload_tlv_ies {
+ struct host_cmd_beacon_offload_tlv_hdr hdr;
+ u8 buf[];
+} __packed;
+
+struct host_cmd_req_beacon_offload {
+ struct host_cmd_header hdr;
+ __le32 flags;
+ u8 variable[];
+} __packed;
+
+struct host_cmd_resp_beacon_offload {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le16 dtim_count;
+} __packed;
+
+struct host_cmd_req_probe_response_offload {
+ struct host_cmd_header hdr;
+ u8 enable;
+ __le16 probe_resp_len;
+ u8 probe_resp_buf[];
+} __packed;
+
+struct host_cmd_resp_probe_response_offload {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_sta_type {
+ struct host_cmd_header hdr;
+ u8 sta_type;
+} __packed;
+
+struct host_cmd_req_set_enc_mode {
+ struct host_cmd_header hdr;
+ u8 enc_mode;
+} __packed;
+
+struct host_cmd_req_test_ba {
+ struct host_cmd_header hdr;
+ u8 addr[HOST_CMD_MAC_ADDR_LEN];
+ u8 start;
+ u8 tx;
+ __le32 tid;
+} __packed;
+
+struct host_cmd_req_set_listen_interval {
+ struct host_cmd_header hdr;
+ __le16 listen_interval;
+} __packed;
+
+struct host_cmd_req_set_ampdu {
+ struct host_cmd_header hdr;
+ u8 ampdu_enabled;
+} __packed;
+
+struct host_cmd_req_set_s1g_op_class {
+ struct host_cmd_header hdr;
+ u8 opclass;
+ u8 prim_opclass;
+} __packed;
+
+struct host_cmd_req_send_wake_action_frame {
+ struct host_cmd_header hdr;
+ u8 dest_addr[HOST_CMD_MAC_ADDR_LEN];
+ __le32 payload_size;
+ u8 payload[];
+} __packed;
+
+#define HOST_CMD_MAX_VENDOR_IE_LENGTH 255
+#define HOST_CMD_VENDOR_IE_TYPE_FLAG_BEACON BIT(0)
+#define HOST_CMD_VENDOR_IE_TYPE_FLAG_PROBE_REQ BIT(1)
+#define HOST_CMD_VENDOR_IE_TYPE_FLAG_PROBE_RESP BIT(2)
+#define HOST_CMD_VENDOR_IE_TYPE_FLAG_ASSOC_REQ BIT(3)
+#define HOST_CMD_VENDOR_IE_TYPE_FLAG_ASSOC_RESP BIT(4)
+
+enum host_cmd_vendor_ie_op {
+ HOST_CMD_VENDOR_IE_OP_ADD_ELEMENT = 0,
+ HOST_CMD_VENDOR_IE_OP_CLEAR_ELEMENTS = 1,
+ HOST_CMD_VENDOR_IE_OP_ADD_FILTER = 2,
+ HOST_CMD_VENDOR_IE_OP_CLEAR_FILTERS = 3,
+ HOST_CMD_VENDOR_IE_OP_INVALID = U16_MAX,
+};
+
+struct host_cmd_req_vendor_ie_config {
+ struct host_cmd_header hdr;
+ __le16 opcode;
+ __le16 mgmt_type_mask;
+ u8 data[HOST_CMD_MAX_VENDOR_IE_LENGTH];
+} __packed;
+
+struct host_cmd_resp_vendor_ie_config {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+enum host_cmd_twt_conf_op {
+ HOST_CMD_TWT_CONF_OP_CONFIGURE = 0,
+ HOST_CMD_TWT_CONF_OP_FORCE_INSTALL_AGREEMENT = 1,
+ HOST_CMD_TWT_CONF_OP_REMOVE_AGREEMENT = 2,
+ HOST_CMD_TWT_CONF_OP_CONFIGURE_EXPLICIT = 3,
+};
+
+struct host_cmd_explicit_twt_wake_interval {
+ __le16 wake_interval_mantissa;
+ u8 wake_interval_exponent;
+ u8 __padding[5];
+} __packed;
+
+union host_cmd_wake_interval {
+ __le64 wake_interval_us;
+ struct host_cmd_explicit_twt_wake_interval explicit_twt;
+} __packed;
+
+struct host_cmd_req_set_twt_conf {
+ struct host_cmd_header hdr;
+ u8 opcode;
+ u8 flow_id;
+ __le64 target_wake_time;
+ union host_cmd_wake_interval wake_interval;
+ __le32 wake_duration_us;
+ u8 twt_setup_command;
+ u8 __padding[3];
+} __packed;
+
+#define HOST_CMD_MAX_AVAILABLE_CHANNELS 255
+
+struct host_cmd_channel_info {
+ __le32 frequency_khz;
+ u8 channel_5g;
+ u8 channel_s1g;
+ u8 bandwidth_mhz;
+} __packed;
+
+struct host_cmd_resp_get_available_channels {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 num_channels;
+ struct host_cmd_channel_info channels[HOST_CMD_MAX_AVAILABLE_CHANNELS];
+} __packed;
+
+#define HOST_CMD_S1G_CAP0_S1G_LONG BIT(0)
+#define HOST_CMD_S1G_CAP0_SGI_1MHZ BIT(1)
+#define HOST_CMD_S1G_CAP0_SGI_2MHZ BIT(2)
+#define HOST_CMD_S1G_CAP0_SGI_4MHZ BIT(3)
+#define HOST_CMD_S1G_CAP0_SGI_8MHZ BIT(4)
+#define HOST_CMD_S1G_CAP0_SGI_16MHZ BIT(5)
+
+struct host_cmd_req_set_ecsa_s1g_info {
+ struct host_cmd_header hdr;
+ __le32 operating_channel_freq_hz;
+ u8 opclass;
+ u8 primary_channel_bw_mhz;
+ u8 prim_1mhz_ch_idx;
+ u8 operating_channel_bw_mhz;
+ u8 prim_opclass;
+ u8 s1g_cap0;
+ u8 s1g_cap1;
+ u8 s1g_cap2;
+ u8 s1g_cap3;
+} __packed;
+
+struct host_cmd_resp_get_hw_version {
+ struct host_cmd_header hdr;
+ __le32 status;
+ u8 hw_version[64];
+} __packed;
+
+#define HOST_CMD_CAC_CFG_CHANGE_RULE_MAX 8
+#define HOST_CMD_CAC_CFG_ARFS_MAX 99
+#define HOST_CMD_CAC_CFG_CHANGE_MAX 99
+#define HOST_CMD_CAC_CFG_CHANGE_STEP 5
+
+enum host_cmd_cac_op {
+ HOST_CMD_CAC_OP_DISABLE = 0,
+ HOST_CMD_CAC_OP_ENABLE = 1,
+ HOST_CMD_CAC_OP_CFG_GET = 2,
+ HOST_CMD_CAC_OP_CFG_SET = 3,
+};
+
+struct host_cmd_cac_change_rule {
+ __le16 arfs;
+ __sle16 threshold_change;
+} __packed;
+
+struct host_cmd_req_cac {
+ struct host_cmd_header hdr;
+ u8 opcode;
+ u8 rule_tot;
+ struct host_cmd_cac_change_rule rule[HOST_CMD_CAC_CFG_CHANGE_RULE_MAX];
+} __packed;
+
+struct host_cmd_resp_cac {
+ struct host_cmd_header hdr;
+ __le32 status;
+ u8 rule_tot;
+ struct host_cmd_cac_change_rule rule[HOST_CMD_CAC_CFG_CHANGE_RULE_MAX];
+} __packed;
+
+struct host_cmd_ocs_driver_req {
+ __le32 op_channel_freq_hz;
+ u8 op_channel_bw_mhz;
+ u8 pri_channel_bw_mhz;
+ u8 pri_1mhz_channel_index;
+} __packed;
+
+struct host_cmd_ocs_driver_resp {
+ u8 running;
+} __packed;
+
+struct host_cmd_req_ocs_driver {
+ struct host_cmd_header hdr;
+ __le32 subcmd;
+ union {
+ u8 opaque[0];
+ struct host_cmd_ocs_driver_req config;
+ };
+} __packed;
+
+struct host_cmd_resp_ocs_driver {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 subcmd;
+ union {
+ u8 opaque[0];
+ struct host_cmd_ocs_driver_resp ocs_status;
+ };
+} __packed;
+
+#define HOST_CMD_IFNAMSIZ 16
+
+struct host_cmd_req_mbssid {
+ struct host_cmd_header hdr;
+ u8 max_bssid_indicator;
+ s8 transmitter_iface[HOST_CMD_IFNAMSIZ];
+} __packed;
+
+#define HOST_CMD_MESH_ID_LEN_MAX 32
+#define HOST_CMD_MESH_BEACONLESS_MODE_DISABLE 0
+#define HOST_CMD_MESH_BEACONLESS_MODE_ENABLE 1
+#define HOST_CMD_MESH_PEER_LINKS_MIN 0
+#define HOST_CMD_MESH_PEER_LINKS_MAX 20
+
+struct host_cmd_req_set_mesh_config {
+ struct host_cmd_header hdr;
+ u8 mesh_id_len;
+ u8 mesh_id[HOST_CMD_MESH_ID_LEN_MAX];
+ u8 mesh_beaconless_mode;
+ u8 max_plinks;
+} __packed;
+
+struct host_cmd_req_set_mcba_conf {
+ struct host_cmd_header hdr;
+ u8 mbca_config;
+ u8 beacon_timing_report_interval;
+ u8 min_beacon_gap_ms;
+ __le16 mbss_start_scan_duration_ms;
+ __le16 tbtt_adj_interval_ms;
+} __packed;
+
+struct host_cmd_req_dynamic_peering_config {
+ struct host_cmd_header hdr;
+ u8 enabled;
+ u8 rssi_margin;
+ __le32 blacklist_timeout;
+} __packed;
+
+#define HOST_CMD_CFG_RAW_FLAG_ENABLE BIT(0)
+#define HOST_CMD_CFG_RAW_FLAG_DELETE BIT(1)
+#define HOST_CMD_CFG_RAW_FLAG_UPDATE BIT(2)
+#define HOST_CMD_CFG_RAW_FLAG_DYNAMIC BIT(3)
+#define HOST_CMD_RAW_RESERVED_AID_DCS 2008
+#define HOST_CMD_RAW_RESERVED_AID_DOWNLINK 2009
+
+enum host_cmd_raw_tlv_tag {
+ HOST_CMD_RAW_TLV_TAG_SLOT_DEF = 0,
+ HOST_CMD_RAW_TLV_TAG_GROUP = 1,
+ HOST_CMD_RAW_TLV_TAG_START_TIME = 2,
+ HOST_CMD_RAW_TLV_TAG_PRAW = 3,
+ HOST_CMD_RAW_TLV_TAG_BCN_SPREAD = 4,
+ HOST_CMD_RAW_TLV_TAG_DYN_GLOBAL = 5,
+ HOST_CMD_RAW_TLV_TAG_DYN_CONFIG = 6,
+ HOST_CMD_RAW_TLV_TAG_LAST = 7,
+};
+
+struct host_cmd_raw_tlv_slot_def {
+ u8 tag;
+ __le32 raw_duration_us;
+ u8 num_slots;
+ u8 cross_slot_bleed;
+} __packed;
+
+struct host_cmd_raw_tlv_group {
+ u8 tag;
+ __le16 aid_start;
+ __le16 aid_end;
+} __packed;
+
+struct host_cmd_raw_tlv_start_time {
+ u8 tag;
+ __le32 start_time_us;
+} __packed;
+
+struct host_cmd_raw_tlv_praw {
+ u8 tag;
+ u8 periodicity;
+ u8 validity;
+ u8 start_offset;
+ u8 refresh_on_expiry;
+} __packed;
+
+struct host_cmd_raw_tlv_bcn_spread {
+ u8 tag;
+ __le16 max_spread;
+ __le16 nominal_sta_per_bcn;
+} __packed;
+
+struct host_cmd_raw_tlv_dyn_global {
+ u8 tag;
+ __le16 num_configs;
+ __le16 num_bcn_indexes;
+} __packed;
+
+struct host_cmd_raw_tlv_dyn_config {
+ u8 tag;
+ __le16 id;
+ __le16 index;
+ __le16 len;
+ u8 variable[];
+} __packed;
+
+union host_cmd_raw_tlvs {
+ u8 tag;
+ struct host_cmd_raw_tlv_slot_def slot_def;
+ struct host_cmd_raw_tlv_group group;
+ struct host_cmd_raw_tlv_start_time start_time;
+ struct host_cmd_raw_tlv_praw praw;
+ struct host_cmd_raw_tlv_bcn_spread bcn_spread;
+ struct host_cmd_raw_tlv_dyn_global dyn_global;
+ struct host_cmd_raw_tlv_dyn_config dyn_config;
+} __packed;
+
+struct host_cmd_req_config_raw {
+ struct host_cmd_header hdr;
+ __le32 flags;
+ __le16 id;
+ u8 variable[];
+} __packed;
+
+struct host_cmd_req_config_bss_stats {
+ struct host_cmd_header hdr;
+ u8 enable;
+ __le32 monitor_window_ms;
+} __packed;
+
+struct host_cmd_req_get_rssi {
+ struct host_cmd_header hdr;
+} __packed;
+
+struct host_cmd_resp_get_rssi {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __sle32 rssi0;
+ __sle32 rssi1;
+ __sle32 rssi2;
+ __sle32 rssi3;
+ __sle32 rssi4;
+ __sle32 rssi5;
+ __sle32 rssi6;
+ __sle32 rssi7;
+} __packed;
+
+#define HOST_CMD_SET_IFS_MIN_USECS 160
+
+struct host_cmd_req_set_ifs {
+ struct host_cmd_header hdr;
+ __le32 period_usecs;
+} __packed;
+
+struct host_cmd_resp_set_ifs {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_fem_settings {
+ struct host_cmd_header hdr;
+ __le32 tx_antenna;
+ __le32 rx_antenna;
+ __le32 lna_enabled;
+ __le32 pa_enabled;
+} __packed;
+
+struct host_cmd_resp_set_fem_settings {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_txop {
+ struct host_cmd_header hdr;
+ u8 min_packet_count;
+} __packed;
+
+struct host_cmd_resp_set_txop {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_control_response {
+ struct host_cmd_header hdr;
+ u8 direction;
+ u8 control_response_1mhz_en;
+} __packed;
+
+struct host_cmd_resp_set_control_response {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_periodic_cal {
+ struct host_cmd_header hdr;
+ __le32 periodic_cal_en_mask;
+} __packed;
+
+struct host_cmd_resp_set_periodic_cal {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_bcn_rssi_threshold {
+ struct host_cmd_header hdr;
+ u8 threshold_db;
+} __packed;
+
+struct host_cmd_resp_set_bcn_rssi_threshold {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_tx_pkt_lifetime_usecs {
+ struct host_cmd_header hdr;
+ __le32 lifetime_usecs;
+} __packed;
+
+struct host_cmd_resp_set_tx_pkt_lifetime_usecs {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_physm_watchdog {
+ struct host_cmd_header hdr;
+ u8 physm_watchdog_en;
+} __packed;
+
+struct host_cmd_req_tx_polar {
+ struct host_cmd_header hdr;
+ u8 enable;
+} __packed;
+
+struct host_cmd_evt_sta_state {
+ struct host_cmd_header hdr;
+ u8 sta_addr[HOST_CMD_MAC_ADDR_LEN];
+ __le16 aid;
+ __le16 state;
+} __packed;
+
+struct host_cmd_evt_beacon_loss {
+ struct host_cmd_header hdr;
+ __le32 num_bcns;
+} __packed;
+
+struct host_cmd_evt_sig_field_error {
+ struct host_cmd_header hdr;
+ __le64 start_timestamp;
+ __le64 end_timestamp;
+} __packed;
+
+#define HOST_CMD_UMAC_TRAFFIC_CONTROL_SOURCE_TWT BIT(0)
+#define HOST_CMD_UMAC_TRAFFIC_CONTROL_SOURCE_DUTY_CYCLE BIT(1)
+
+struct host_cmd_evt_umac_traffic_control {
+ struct host_cmd_header hdr;
+ u8 pause_data_traffic;
+ __le32 sources;
+} __packed;
+
+struct host_cmd_evt_dhcp_lease_update {
+ struct host_cmd_header hdr;
+ __le32 my_ip;
+ __le32 netmask;
+ __le32 router;
+ __le32 dns;
+} __packed;
+
+struct host_cmd_evt_ocs_done {
+ struct host_cmd_header hdr;
+ __le64 time_listen;
+ __le64 time_rx;
+ s8 noise;
+ u8 metric;
+} __packed;
+
+struct host_cmd_evt_hw_scan_done {
+ struct host_cmd_header hdr;
+ u8 aborted;
+} __packed;
+
+struct host_cmd_evt_channel_usage {
+ struct host_cmd_header hdr;
+ __le64 time_listen;
+ __le64 busy_time;
+ __le32 freq_hz;
+ u8 noise;
+ u8 bw_mhz;
+} __packed;
+
+enum host_cmd_connection_loss_reason {
+ HOST_CMD_CONNECTION_LOSS_REASON_TSF_RESET = 0,
+};
+
+struct host_cmd_evt_connection_loss {
+ struct host_cmd_header hdr;
+ __le32 reason;
+} __packed;
+
+struct host_cmd_evt_sched_scan_results {
+ struct host_cmd_header hdr;
+} __packed;
+
+enum host_cmd_cqm_rssi_threshold_event {
+ HOST_CMD_CQM_RSSI_THRESHOLD_EVENT_LOW = 0,
+ HOST_CMD_CQM_RSSI_THRESHOLD_EVENT_HIGH = 1,
+};
+
+struct host_cmd_evt_cqm_rssi_notify {
+ struct host_cmd_header hdr;
+ __sle16 rssi;
+ __le16 event;
+} __packed;
+
+struct host_cmd_evt_ndp_probe_request_received {
+ struct host_cmd_header hdr;
+ u8 rx_bw_mhz;
+ u8 is_pv1;
+} __packed;
+
+struct host_cmd_evt_scan_done {
+ struct host_cmd_header hdr;
+ u8 aborted;
+} __packed;
+
+enum host_cmd_scan_result_frame {
+ HOST_CMD_SCAN_RESULT_FRAME_UNKNOWN = 0,
+ HOST_CMD_SCAN_RESULT_FRAME_BEACON = 1,
+ HOST_CMD_SCAN_RESULT_FRAME_PROBE_RESPONSE = 2,
+};
+
+struct host_cmd_evt_scan_result {
+ struct host_cmd_header hdr;
+ __le32 channel_freq_hz;
+ u8 bw_mhz;
+ u8 frame_type;
+ __sle16 rssi;
+ u8 bssid[HOST_CMD_MAC_ADDR_LEN];
+ __le16 beacon_interval;
+ __le16 capability_info;
+ __le64 tsf;
+ __le16 ies_len;
+ u8 ies[];
+} __packed;
+
+struct host_cmd_evt_connected {
+ struct host_cmd_header hdr;
+ u8 bssid[HOST_CMD_MAC_ADDR_LEN];
+ __sle16 rssi;
+ u8 padding_0[8];
+ __le16 assoc_resp_ies_len;
+ u8 assoc_resp_ies[];
+} __packed;
+
+struct host_cmd_evt_beacon_filter_match {
+ struct host_cmd_header hdr;
+ u8 padding_0[4];
+ __le32 ies_len;
+ u8 ies[];
+} __packed;
+
+struct host_cmd_req_set_capabilities {
+ struct host_cmd_header hdr;
+ struct host_cmd_mm_capabilities capabilities;
+ u8 set_caps;
+ u8 morse_mmss_offset;
+} __packed;
+
+struct host_cmd_resp_set_capabilities {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+struct host_cmd_req_set_transmission_rate {
+ struct host_cmd_header hdr;
+ __sle32 mcs_index;
+ __sle32 bandwidth_mhz;
+ __sle32 tx_80211ah_format;
+ s8 use_traveling_pilots;
+ s8 use_sgi;
+ u8 enabled;
+ s8 nss_idx;
+ s8 use_ldpc;
+ s8 use_stbc;
+} __packed;
+
+struct host_cmd_resp_set_transmission_rate {
+ struct host_cmd_header hdr;
+ __le32 status;
+} __packed;
+
+enum host_cmd_hart_id {
+ HOST_CMD_HART_ID_HOST = 0,
+ HOST_CMD_HART_ID_MAC = 1,
+ HOST_CMD_HART_ID_UPHY = 2,
+ HOST_CMD_HART_ID_LPHY = 3,
+};
+
+struct host_cmd_req_force_assert {
+ struct host_cmd_header hdr;
+ __le32 hart_id;
+ __le32 delay;
+} __packed;
+
+#define HOST_CMD_HOST_BLOCK_TX_FRAMES BIT(0)
+#define HOST_CMD_HOST_BLOCK_TX_CMD BIT(1)
+
+enum host_cmd_param_action {
+ HOST_CMD_PARAM_ACTION_SET = 0,
+ HOST_CMD_PARAM_ACTION_GET = 1,
+ HOST_CMD_PARAM_ACTION_LAST = 2,
+};
+
+enum host_cmd_slow_clock_mode {
+ HOST_CMD_SLOW_CLOCK_MODE_AUTO = 0,
+ HOST_CMD_SLOW_CLOCK_MODE_INTERNAL = 1,
+};
+
+enum host_cmd_param_id {
+ HOST_CMD_PARAM_ID_MAX_TRAFFIC_DELIVERY_WAIT_US = 0,
+ HOST_CMD_PARAM_ID_EXTRA_ACK_TIMEOUT_ADJUST_US = 1,
+ HOST_CMD_PARAM_ID_TX_STATUS_FLUSH_WATERMARK = 2,
+ HOST_CMD_PARAM_ID_TX_STATUS_FLUSH_MIN_AMPDU_SIZE = 3,
+ HOST_CMD_PARAM_ID_POWERSAVE_TYPE = 4,
+ HOST_CMD_PARAM_ID_SNOOZE_DURATION_ADJUST_US = 5,
+ HOST_CMD_PARAM_ID_TX_BLOCK = 6,
+ HOST_CMD_PARAM_ID_FORCED_SNOOZE_PERIOD_US = 7,
+ HOST_CMD_PARAM_ID_WAKE_ACTION_GPIO = 8,
+ HOST_CMD_PARAM_ID_WAKE_ACTION_GPIO_PULSE_MS = 9,
+ HOST_CMD_PARAM_ID_CONNECTION_MONITOR_GPIO = 10,
+ HOST_CMD_PARAM_ID_INPUT_TRIGGER_GPIO = 11,
+ HOST_CMD_PARAM_ID_INPUT_TRIGGER_MODE = 12,
+ HOST_CMD_PARAM_ID_COUNTRY = 13,
+ HOST_CMD_PARAM_ID_RTS_THRESHOLD = 14,
+ HOST_CMD_PARAM_ID_HOST_TX_BLOCK = 15,
+ HOST_CMD_PARAM_ID_MEM_RETENTION_CODE = 16,
+ HOST_CMD_PARAM_ID_NON_TIM_MODE = 17,
+ HOST_CMD_PARAM_ID_DYNAMIC_PS_TIMEOUT_MS = 18,
+ HOST_CMD_PARAM_ID_HOME_CHANNEL_DWELL_MS = 19,
+ HOST_CMD_PARAM_ID_SLOW_CLOCK_MODE = 20,
+ HOST_CMD_PARAM_ID_FRAGMENT_THRESHOLD = 21,
+ HOST_CMD_PARAM_ID_BEACON_LOSS_COUNT = 22,
+ HOST_CMD_PARAM_ID_AP_POWER_SAVE = 23,
+ HOST_CMD_PARAM_ID_BEACON_OFFLOAD = 24,
+ HOST_CMD_PARAM_ID_PROBE_RESP_OFFLOAD = 25,
+ HOST_CMD_PARAM_ID_BSS_MAX_AWAY_DURATION = 26,
+ HOST_CMD_PARAM_ID_DEFAULT_ACTIVE_SCAN_DWELL_MS = 27,
+ HOST_CMD_PARAM_ID_CTS_TO_SELF = 28,
+ HOST_CMD_PARAM_ID_CHANNELIZATION = 29,
+ HOST_CMD_PARAM_ID_CRYPTO_IN_HOST = 30,
+ HOST_CMD_PARAM_ID_AUTOCONNECT = 31,
+ HOST_CMD_PARAM_ID_LAST = 32,
+};
+
+struct host_cmd_req_get_set_generic_param {
+ struct host_cmd_header hdr;
+ __le32 param_id;
+ __le32 action;
+ __le32 flags;
+ __le32 value;
+} __packed;
+
+struct host_cmd_resp_get_set_generic_param {
+ struct host_cmd_header hdr;
+ __le32 status;
+ __le32 flags;
+ __le32 value;
+} __packed;
+
+#endif
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 04/35] wifi: mm81x: add command.h
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
.../net/wireless/morsemicro/mm81x/command.h | 84 +++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/command.h
diff --git a/drivers/net/wireless/morsemicro/mm81x/command.h b/drivers/net/wireless/morsemicro/mm81x/command.h
new file mode 100644
index 000000000000..67c4f6962e85
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/command.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+
+#ifndef _MM81X_COMMAND_H_
+#define _MM81X_COMMAND_H_
+
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include "core.h"
+#include "command_defs.h"
+
+#define HOST_CMD_IS_REQ(cmd) (le16_to_cpu((cmd)->hdr.flags) & HOST_CMD_TYPE_REQ)
+#define HOST_CMD_IS_RESP(cmd) \
+ (le16_to_cpu((cmd)->hdr.flags) & HOST_CMD_TYPE_RESP)
+#define HOST_CMD_IS_EVT(cmd) (le16_to_cpu((cmd)->hdr.flags) & HOST_CMD_TYPE_EVT)
+
+struct mm81x_queue_params;
+
+enum mm81x_cmd_return_code {
+ MM81X_RET_SUCCESS = 0,
+ MM81X_RET_EPERM = -1,
+ MM81X_RET_ENOMEM = -12,
+ MM81X_RET_CMD_NOT_HANDLED = -32757,
+};
+
+#define HOST_CMD_HOST_ID_SEQ_MAX 0xFFF
+#define HOST_CMD_HOST_ID_RETRY_MASK 0x000F
+#define HOST_CMD_HOST_ID_SEQ_SHIFT 4
+#define HOST_CMD_HOST_ID_SEQ_MASK 0xFFF0
+
+struct host_cmd_req {
+ struct host_cmd_header hdr;
+ u8 data[];
+} __packed;
+
+struct host_cmd_resp {
+ struct host_cmd_header hdr;
+ __le32 status;
+ u8 data[];
+} __packed;
+
+struct host_cmd_event {
+ struct host_cmd_header hdr;
+ u8 data[];
+} __packed;
+
+int mm81x_cmd_resp_process(struct mm81x *mm, struct sk_buff *skb);
+int mm81x_cmd_add_if(struct mm81x *mm, u16 *vif_id, const u8 *addr,
+ enum nl80211_iftype type);
+int mm81x_cmd_get_capabilities(struct mm81x *mm, u16 vif_id,
+ struct mm81x_fw_caps *capabilities);
+int mm81x_cmd_cfg_qos(struct mm81x *mm, struct mm81x_queue_params *params);
+int mm81x_cmd_config_beacon_timer(struct mm81x *mm, void *mm81x_vif,
+ bool enabled);
+int mm81x_cmd_cfg_bss(struct mm81x *mm, u16 vif_id, u16 beacon_int,
+ u16 dtim_period, u32 cssid);
+int mm81x_cmd_set_channel(struct mm81x *mm, u32 op_chan_freq_hz,
+ u8 pri_1mhz_chan_idx, u8 op_bw_mhz, u8 pri_bw_mhz,
+ s32 *power_mbm);
+int mm81x_cmd_get_max_txpower(struct mm81x *mm, s32 *out_power_mbm);
+int mm81x_cmd_set_txpower(struct mm81x *mm, s32 *out_power_mbm,
+ int txpower_mbm);
+int mm81x_cmd_hw_scan(struct mm81x *mm, struct mm81x_hw_scan_params *params,
+ bool store);
+int mm81x_cmd_set_ps(struct mm81x *mm, bool enabled);
+int mm81x_cmd_cfg_multicast_filter(struct mm81x *mm, struct mm81x_vif *mm_vif);
+int mm81x_cmd_sta_state(struct mm81x *mm, struct mm81x_vif *mm_vif, u16 aid,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state state);
+int mm81x_cmd_install_key(struct mm81x *mm, struct mm81x_vif *mm_vif, u16 aid,
+ struct ieee80211_key_conf *key,
+ enum host_cmd_key_cipher cipher,
+ enum host_cmd_aes_key_len length);
+int mm81x_cmd_disable_key(struct mm81x *mm, struct mm81x_vif *mm_vif, u16 aid,
+ struct ieee80211_key_conf *key);
+int mm81x_cmd_rm_if(struct mm81x *mm, u16 vif_id);
+int mm81x_cmd_set_frag_threshold(struct mm81x *mm, u32 frag_threshold);
+int mm81x_cmd_get_disabled_channels(
+ struct mm81x *mm, struct host_cmd_resp_get_disabled_channels *resp,
+ uint resp_len);
+
+#endif /* !_MM81X_COMMAND_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 05/35] wifi: mm81x: add core.c
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/core.c | 157 +++++++++++++++++++
1 file changed, 157 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/core.c
diff --git a/drivers/net/wireless/morsemicro/mm81x/core.c b/drivers/net/wireless/morsemicro/mm81x/core.c
new file mode 100644
index 000000000000..1bcb9b5a00c9
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/core.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+#include <linux/module.h>
+#include "core.h"
+#include "debug.h"
+#include "bus.h"
+#include "hif.h"
+
+unsigned int mm81x_debug_mask;
+module_param_named(debug_mask, mm81x_debug_mask, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "mm81x debug mask");
+
+char board_config_file[BCF_SIZE_MAX] = "";
+module_param_string(bcf, board_config_file, sizeof(board_config_file), 0644);
+MODULE_PARM_DESC(bcf, "BCF filename to load");
+
+int mm81x_core_attach_regs(struct mm81x *mm)
+{
+ int ret = 0;
+
+ mm81x_claim_bus(mm);
+ ret = mm81x_reg32_read(mm, MM8108_REG_CHIP_ID, &mm->chip_id);
+ mm81x_release_bus(mm);
+
+ if (ret < 0) {
+ mm81x_err(mm, "failed to read chip id %d", ret);
+ return ret;
+ }
+
+ switch (mm->chip_id) {
+ case (MM8108B2_ID):
+ mm->regs = &mm8108_regs;
+ mm->hif.ops = &mm81x_yaps_ops;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return ret;
+}
+
+static char *mm81x_core_get_revision_string(u32 chip_id)
+{
+ u8 chip_rev = MM81X_DEVICE_GET_CHIP_REV(chip_id);
+
+ switch (chip_rev) {
+ case MM8108B2_REV:
+ return MM8108B2_REV_STRING;
+ default:
+ return "??";
+ }
+}
+
+void mm81x_core_init_mac_addr(struct mm81x *mm)
+{
+ int ret = mm81x_hw_otp_get_mac_addr(mm);
+
+ if (ret || !is_valid_ether_addr(mm->macaddr))
+ eth_random_addr(mm->macaddr);
+}
+
+char *mm81x_core_get_fw_path(u32 chip_id)
+{
+ return kasprintf(GFP_KERNEL,
+ MM81X_FW_DIR "/" MM8108_FW_BASE
+ "%s" FW_ROM_LINKED_STRING MM81X_FW_EXT,
+ mm81x_core_get_revision_string(chip_id));
+}
+
+int mm81x_core_create(struct mm81x *mm)
+{
+ int ret;
+
+ set_bit(MM81X_STATE_CHIP_UNRESPONSIVE, &mm->state_flags);
+ set_bit(MM81X_STATE_RELOAD_FW_AFTER_START, &mm->state_flags);
+
+ mm->chip_wq = create_singlethread_workqueue("chip_wq");
+ if (!mm->chip_wq) {
+ mm81x_err(mm, "create_singlethread_workqueue failed");
+ return -ENOMEM;
+ }
+
+ mm->net_wq = create_singlethread_workqueue("net_wq");
+ if (!mm->net_wq) {
+ mm81x_err(mm, "create_singlethread_workqueue failed");
+ ret = -ENOMEM;
+ goto err_chip_wq;
+ }
+
+ ret = mm81x_hif_init(mm);
+ if (ret) {
+ mm81x_err(mm, "mm81x_hif_init failed: %d", ret);
+ goto err_wqs;
+ }
+
+ return 0;
+
+err_wqs:
+ flush_workqueue(mm->net_wq);
+ destroy_workqueue(mm->net_wq);
+
+err_chip_wq:
+ flush_workqueue(mm->chip_wq);
+ destroy_workqueue(mm->chip_wq);
+
+ return ret;
+}
+
+void mm81x_core_destroy(struct mm81x *mm)
+{
+ mm81x_hif_finish(mm);
+ flush_workqueue(mm->net_wq);
+ destroy_workqueue(mm->net_wq);
+ flush_workqueue(mm->chip_wq);
+ destroy_workqueue(mm->chip_wq);
+}
+
+static int __init mm81x_init(void)
+{
+ int ret = 0;
+
+ pr_info("Morse Micro mm81x driver registration. Version %s\n",
+ DRV_VERSION);
+
+#ifdef CONFIG_MM81X_USB
+ ret = mm81x_usb_init();
+ if (ret)
+ pr_err("mm81x_usb_init() failed: %d\n", ret);
+#endif
+#ifdef CONFIG_MM81X_SDIO
+ ret = mm81x_sdio_init();
+ if (ret)
+ pr_err("mm81x_sdio_init() failed: %d\n", ret);
+#endif
+
+ return ret;
+}
+
+static void __exit mm81x_exit(void)
+{
+#ifdef CONFIG_MM81X_USB
+ mm81x_usb_exit();
+#endif
+#ifdef CONFIG_MM81X_SDIO
+ mm81x_sdio_exit();
+#endif
+}
+
+module_init(mm81x_init);
+module_exit(mm81x_exit);
+
+MODULE_AUTHOR("Morse Micro");
+MODULE_DESCRIPTION("Driver support for Morse Micro MM81X SDIO/USB devices");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION("1.0");
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 06/35] wifi: mm81x: add core.h
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/core.h | 499 +++++++++++++++++++
1 file changed, 499 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/core.h
diff --git a/drivers/net/wireless/morsemicro/mm81x/core.h b/drivers/net/wireless/morsemicro/mm81x/core.h
new file mode 100644
index 000000000000..1cd079985ed7
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/core.h
@@ -0,0 +1,499 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+
+#ifndef _MM81X_CORE_H_
+#define _MM81X_CORE_H_
+
+#include <net/mac80211.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/crc32.h>
+#include <linux/notifier.h>
+#include <linux/nospec.h>
+#include "yaps.h"
+#include "yaps_hw.h"
+#include "hw.h"
+#include "fw.h"
+#include "rc.h"
+
+#define MM81X_DRIVER_SEMVER_MAJOR 56
+#define MM81X_DRIVER_SEMVER_MINOR 3
+#define MM81X_DRIVER_SEMVER_PATCH 0
+
+#define MM81X_SEMVER_GET_MAJOR(x) (((x) >> 22) & 0x3FF)
+#define MM81X_SEMVER_GET_MINOR(x) (((x) >> 10) & 0xFFF)
+#define MM81X_SEMVER_GET_PATCH(x) ((x) & 0x3FF)
+
+#define DRV_VERSION __stringify(MM81X_VERSION)
+
+#define MM8108_FW_BASE "mm8108"
+
+#define BCF_SIZE_MAX 48
+
+/* Generate a device ID from chip ID, revision, and chip type */
+#define MM81X_DEVICE_ID(chip_id, chip_rev, chip_type) \
+ ((chip_id) | ((chip_rev) << 8) | ((chip_type) << 12))
+
+/* Get constituents of the device ID */
+#define MM81X_DEVICE_GET_CHIP_ID(device_id) ((device_id) & 0xff)
+#define MM81X_DEVICE_GET_CHIP_REV(device_id) ((((device_id) >> 8) & 0xf))
+#define MM81X_DEVICE_GET_CHIP_TYPE(device_id) ((((device_id) >> 12) & 0xf))
+
+#define KHZ_TO_HZ(x) ((x) * 1000)
+#define KHZ100_TO_MHZ(x) ((x) / 10)
+#define KHZ100_TO_KHZ(freq) ((freq) * 100)
+#define KHZ100_TO_HZ(freq) ((freq) * 100000)
+
+#define QDBM_TO_MBM(gain) (((gain) * 100) >> 2)
+#define MBM_TO_QDBM(gain) (((gain) << 2) / 100)
+#define QDBM_TO_DBM(gain) ((gain) / 4)
+
+#define BPS_TO_KBPS(x) ((x) / 1000)
+
+#define UNUSED(x) ((void)x)
+
+#define NSS_IDX_TO_NSS(x) ((x) + 1)
+#define NSS_TO_NSS_IDX(x) ((x) - 1)
+
+#define ROUND_BYTES_TO_WORD(_nbytes) \
+ (((_nbytes) + 3) & ~((typeof(_nbytes))0x03))
+
+static inline u32 mm81x_fle32_to_cpu(u32 v)
+{
+ return le32_to_cpu((__force __le32)v);
+}
+
+static inline u16 mm81x_fle16_to_cpu(u16 v)
+{
+ return le16_to_cpu((__force __le16)v);
+}
+
+struct mm81x_bus_ops;
+struct mm81x_hif_ops;
+
+/* modparam variables */
+extern char board_config_file[];
+
+#define MM81X_CAPS_MAX_FW_VAL (128)
+
+/* Max number of interfaces */
+#define MM81X_MAX_IF (2)
+
+enum mm81x_caps_flags {
+ MM81X_CAPS_FW_START = 0,
+ MM81X_CAPS_2MHZ = MM81X_CAPS_FW_START,
+ MM81X_CAPS_4MHZ,
+ MM81X_CAPS_8MHZ,
+ MM81X_CAPS_16MHZ,
+ MM81X_CAPS_SGI,
+ MM81X_CAPS_S1G_LONG,
+ MM81X_CAPS_TRAVELING_PILOT_ONE_STREAM,
+ MM81X_CAPS_TRAVELING_PILOT_TWO_STREAM,
+ MM81X_CAPS_MU_BEAMFORMEE,
+ MM81X_CAPS_MU_BEAMFORMER,
+ MM81X_CAPS_RD_RESPONDER,
+ MM81X_CAPS_STA_TYPE_SENSOR,
+ MM81X_CAPS_STA_TYPE_NON_SENSOR,
+ MM81X_CAPS_GROUP_AID,
+ MM81X_CAPS_NON_TIM,
+ MM81X_CAPS_TIM_ADE,
+ MM81X_CAPS_BAT,
+ MM81X_CAPS_DYNAMIC_AID,
+ MM81X_CAPS_UPLINK_SYNC,
+ MM81X_CAPS_FLOW_CONTROL,
+ MM81X_CAPS_AMPDU,
+ MM81X_CAPS_AMSDU,
+ MM81X_CAPS_1MHZ_CONTROL_RESPONSE_PREAMBLE,
+ MM81X_CAPS_PAGE_SLICING,
+ MM81X_CAPS_RAW,
+ MM81X_CAPS_MCS8,
+ MM81X_CAPS_MCS9,
+ MM81X_CAPS_ASYMMETRIC_BA_SUPPORT,
+ MM81X_CAPS_DAC,
+ MM81X_CAPS_CAC,
+ MM81X_CAPS_TXOP_SHARING_IMPLICIT_ACK,
+ MM81X_CAPS_NDP_PSPOLL,
+ MM81X_CAPS_FRAGMENT_BA,
+ MM81X_CAPS_OBSS_MITIGATION,
+ MM81X_CAPS_TMP_PS_MODE_SWITCH,
+ MM81X_CAPS_SECTOR_TRAINING,
+ MM81X_CAPS_UNSOLICIT_DYNAMIC_AID,
+ MM81X_CAPS_NDP_BEAMFORMING_REPORT,
+ MM81X_CAPS_MCS_NEGOTIATION,
+ MM81X_CAPS_DUPLICATE_1MHZ,
+ MM81X_CAPS_TACK_AS_PSPOLL,
+ MM81X_CAPS_PV1,
+ MM81X_CAPS_TWT_RESPONDER,
+ MM81X_CAPS_TWT_REQUESTER,
+ MM81X_CAPS_BDT,
+ MM81X_CAPS_TWT_GROUPING,
+ MM81X_CAPS_LINK_ADAPTATION_WO_NDP_CMAC,
+ MM81X_CAPS_LONG_MPDU,
+ MM81X_CAPS_TXOP_SECTORIZATION,
+ MM81X_CAPS_GROUP_SECTORIZATION,
+ MM81X_CAPS_HTC_VHT,
+ MM81X_CAPS_HTC_VHT_MFB,
+ MM81X_CAPS_HTC_VHT_MRQ,
+ MM81X_CAPS_2SS,
+ MM81X_CAPS_3SS,
+ MM81X_CAPS_4SS,
+ MM81X_CAPS_SU_BEAMFORMEE,
+ MM81X_CAPS_SU_BEAMFORMER,
+ MM81X_CAPS_RX_STBC,
+ MM81X_CAPS_TX_STBC,
+ MM81X_CAPS_RX_LDPC,
+ MM81X_CAPS_HW_FRAGMENT,
+
+ MM81X_CAPS_FW_END = MM81X_CAPS_MAX_FW_VAL,
+ MM81X_CAPS_LAST = MM81X_CAPS_FW_END,
+};
+
+struct mm81x_fw_caps {
+ u32 flags[FW_CAPABILITIES_FLAGS_WIDTH];
+ u8 ampdu_mss;
+ u8 beamformee_sts_capability;
+ u8 number_sounding_dimensions;
+ u8 maximum_ampdu_length_exponent;
+ u8 mm81x_mmss_offset;
+};
+
+#define MM81X_FW_SUPP(MM81X_CAPS, CAPABILITY) \
+ mm81x_caps_supported(MM81X_CAPS, MM81X_CAPS_##CAPABILITY)
+
+static inline bool mm81x_caps_supported(struct mm81x_fw_caps *caps,
+ enum mm81x_caps_flags flag)
+{
+ const unsigned long *flags_ptr = (unsigned long *)caps->flags;
+
+ return test_bit(flag, flags_ptr);
+}
+
+struct mm81x_ps {
+ u32 wakers;
+ bool enable;
+ bool suspended;
+ /* PS state lock */
+ struct mutex lock;
+ struct work_struct async_wake_work;
+ struct delayed_work delayed_eval_work;
+ struct completion *awake;
+ /* hardware config supports powersave through hardware GPIOs */
+ bool gpios_supported;
+ struct gpio_desc *wake_gpio;
+ struct gpio_desc *busy_gpio;
+};
+
+enum mm81x_page_aci {
+ MM81X_ACI_BE = 0,
+ MM81X_ACI_BK = 1,
+ MM81X_ACI_VI = 2,
+ MM81X_ACI_VO = 3,
+};
+
+enum mm81x_qos_tid_up_index {
+ MM81X_QOS_TID_UP_BK = 1,
+ MM81X_QOS_TID_UP_XX = 2,
+ MM81X_QOS_TID_UP_BE = 0,
+ MM81X_QOS_TID_UP_EE = 3,
+ MM81X_QOS_TID_UP_CL = 4,
+ MM81X_QOS_TID_UP_VI = 5,
+ MM81X_QOS_TID_UP_VO = 6,
+ MM81X_QOS_TID_UP_NC = 7,
+
+ MM81X_QOS_TID_UP_LOWEST = MM81X_QOS_TID_UP_BK,
+ MM81X_QOS_TID_UP_HIGHEST = MM81X_QOS_TID_UP_NC
+};
+
+struct mm81x_sw_version {
+ u8 major;
+ u8 minor;
+ u8 patch;
+};
+
+struct mm81x_sta {
+ const struct ieee80211_vif *vif;
+ u8 addr[ETH_ALEN];
+ enum ieee80211_sta_state state;
+ bool tid_tx[IEEE80211_NUM_TIDS];
+ bool tid_start_tx[IEEE80211_NUM_TIDS];
+ u8 tid_params[IEEE80211_NUM_TIDS];
+ int max_bw_mhz;
+ struct mm81x_rc_sta rc;
+ struct mmrc_rate last_sta_tx_rate;
+ s16 avg_rssi;
+ bool tx_ps_filter_en;
+};
+
+struct mm81x_vif {
+ struct mm81x *mm;
+ u16 id;
+
+ union {
+ struct {
+ bool is_assoc;
+ } sta;
+ struct {
+ u32 num_stas;
+ struct tasklet_struct beacon_tasklet;
+ bool beaconing_enabled;
+ } ap;
+ } u;
+};
+
+struct mm81x_stale_tx_status {
+ /* Stale Tx lock */
+ spinlock_t lock;
+ struct timer_list timer;
+};
+
+struct mcast_filter {
+ u8 count;
+ /*
+ * Integer representation of the last four bytes of a multicast MAC
+ * address. The first two bytes are always 0x0100 (IPv4) or 0x3333
+ * (IPv6).
+ */
+ __le32 addr_list[];
+};
+
+enum mm81x_hw_scan_op {
+ MM81X_HW_SCAN_OP_START,
+ MM81X_HW_SCAN_OP_STOP,
+};
+
+struct mm81x_hw_scan_params {
+ struct ieee80211_hw *hw;
+
+ /* vif which initiated the scan */
+ struct ieee80211_vif *vif;
+ bool has_directed_ssid;
+ u32 dwell_time_ms;
+ u32 dwell_on_home_ms;
+ enum mm81x_hw_scan_op operation;
+ bool store;
+ struct sk_buff *probe_req;
+ u16 num_chans;
+ u16 allocated_chans;
+
+ struct {
+ struct ieee80211_channel *channel;
+ /* Index into @ref powers_qdbm for the power of this channel */
+ u8 power_idx;
+ } *channels;
+
+ s32 *powers_qdbm;
+ u8 n_powers;
+ bool use_1mhz_probes;
+};
+
+enum mm81x_hw_scan_state {
+ HW_SCAN_STATE_IDLE,
+ HW_SCAN_STATE_RUNNING,
+ HW_SCAN_STATE_ABORTING,
+};
+
+struct mm81x_hw_scan {
+ enum mm81x_hw_scan_state state;
+ struct completion scan_done;
+ struct mm81x_hw_scan_params *params;
+ struct delayed_work timeout;
+ u32 home_dwell_ms;
+};
+
+enum mm81x_hif_event_flags {
+ MM81X_HIF_EVT_RX_PEND,
+ MM81X_HIF_EVT_PAGE_RETURN_PEND,
+ MM81X_HIF_EVT_TX_COMMAND_PEND,
+ MM81X_HIF_EVT_TX_BEACON_PEND,
+ MM81X_HIF_EVT_TX_MGMT_PEND,
+ MM81X_HIF_EVT_TX_DATA_PEND,
+ MM81X_HIF_EVT_TX_PACKET_FREED_UP_PEND,
+ MM81X_HIF_EVT_DATA_TRAFFIC_PAUSE_PEND,
+ MM81X_HIF_EVT_DATA_TRAFFIC_RESUME_PEND,
+ MM81X_HIF_EVT_UPDATE_HW_CLOCK_REFERENCE,
+};
+
+enum mm81x_state_flags {
+ MM81X_STATE_CHIP_UNRESPONSIVE,
+ MM81X_STATE_DATA_QS_STOPPED,
+ MM81X_STATE_DATA_TX_STOPPED,
+ MM81X_STATE_REGDOM_SET_BY_USER,
+ MM81X_STATE_REGDOM_SET_BY_OTP,
+ MM81X_STATE_RELOAD_FW_AFTER_START,
+ MM81X_STATE_HOST_TO_CHIP_TX_BLOCKED,
+ MM81X_STATE_HOST_TO_CHIP_CMD_BLOCKED,
+};
+
+#define MM81X_COUNTRY_LEN (3)
+#define INVALID_VIF_INDEX 0xFF
+
+struct mm81x {
+ u32 chip_id;
+ u32 host_table_ptr;
+
+ /* Refer to @enum mm81x_bus_type */
+ u32 bus_type;
+ u32 bcf_address;
+
+ /* Serialise high-level operations to the mm81x structure */
+ struct mutex lock;
+
+ /*
+ * Parsed from the release tag, which should be in the format
+ * 'rel_<major>_<minor>_<patch>'. If the tag is not in this format
+ * then corresponding version field will be 0.
+ */
+ struct mm81x_sw_version sw_ver;
+ u8 macaddr[ETH_ALEN];
+ u8 country[MM81X_COUNTRY_LEN];
+
+ /* Mask of type @enum host_table_firmware_flags */
+ u32 firmware_flags;
+ struct mm81x_fw_caps fw_caps;
+ bool started;
+ bool chip_was_reset;
+ struct wiphy *wiphy;
+ struct mm81x_hw_scan hw_scan;
+ struct ieee80211_hw *hw;
+ struct device *dev;
+
+ struct ieee80211_vif __rcu *vifs[MM81X_MAX_IF];
+
+ /* @mm81x_state_flags */
+ unsigned long state_flags;
+
+ u16 cmd_seq;
+ struct completion *cmd_comp;
+ /* Serialises commands */
+ struct mutex cmd_lock;
+
+ /* Serialises command completion */
+ struct mutex cmd_wait;
+
+ const struct mm81x_regs *regs;
+
+ struct {
+ union {
+ struct mm81x_yaps yaps;
+ } u;
+ const struct mm81x_hif_ops *ops;
+ /* See @enum mm81x_hif_event_flags for values */
+ unsigned long event_flags;
+ bool validate_skb_checksum;
+ } hif;
+
+ struct workqueue_struct *chip_wq;
+ struct work_struct hif_work;
+ struct work_struct usb_irq_work;
+ struct mm81x_stale_tx_status stale_status;
+ bool config_ps;
+ struct mm81x_ps ps;
+
+ /* Tx power in mBm received from the FW before association */
+ s32 tx_power_mbm;
+ s32 tx_max_power_mbm;
+
+ const struct mm81x_bus_ops *bus_ops;
+ struct mm81x_rc mrc;
+ int rts_threshold;
+ struct workqueue_struct *net_wq;
+ struct work_struct tx_stale_work;
+
+ struct cfg80211_chan_def chandef;
+ struct mcast_filter *mcast_filter;
+ atomic_t num_bcn_vifs;
+ unsigned long beacon_irqs_enabled;
+ u8 drv_priv[] __aligned(sizeof(void *));
+};
+
+/* Map from mac80211 queue to Morse ACI value for page metadata */
+static inline u8 map_mac80211q_2_mm81x_aci(u16 mac80211queue)
+{
+ switch (mac80211queue) {
+ case IEEE80211_AC_VO:
+ return MM81X_ACI_VO;
+ case IEEE80211_AC_VI:
+ return MM81X_ACI_VI;
+ case IEEE80211_AC_BK:
+ return MM81X_ACI_BK;
+ default:
+ return MM81X_ACI_BE;
+ }
+}
+
+static inline enum mm81x_page_aci
+dot11_tid_to_ac(enum mm81x_qos_tid_up_index tid)
+{
+ switch (tid) {
+ case MM81X_QOS_TID_UP_BK:
+ case MM81X_QOS_TID_UP_XX:
+ return MM81X_ACI_BK;
+ case MM81X_QOS_TID_UP_CL:
+ case MM81X_QOS_TID_UP_VI:
+ return MM81X_ACI_VI;
+ case MM81X_QOS_TID_UP_VO:
+ case MM81X_QOS_TID_UP_NC:
+ return MM81X_ACI_VO;
+ case MM81X_QOS_TID_UP_BE:
+ case MM81X_QOS_TID_UP_EE:
+ default:
+ return MM81X_ACI_BE;
+ }
+}
+
+#ifdef CONFIG_MM81X_USB
+int __init mm81x_usb_init(void);
+void __exit mm81x_usb_exit(void);
+#endif
+
+#ifdef CONFIG_MM81X_SDIO
+int __init mm81x_sdio_init(void);
+void __exit mm81x_sdio_exit(void);
+#endif
+
+static inline bool mm81x_is_data_tx_allowed(struct mm81x *mm)
+{
+ return !test_bit(MM81X_STATE_DATA_TX_STOPPED, &mm->state_flags) &&
+ !test_bit(MM81X_HIF_EVT_DATA_TRAFFIC_PAUSE_PEND,
+ &mm->hif.event_flags);
+}
+
+static inline struct ieee80211_vif *
+mm81x_vif_to_ieee80211_vif(struct mm81x_vif *mm_vif)
+{
+ return container_of((void *)mm_vif, struct ieee80211_vif, drv_priv);
+}
+
+static inline struct mm81x_vif *
+ieee80211_vif_to_mm_vif(struct ieee80211_vif *vif)
+{
+ return (struct mm81x_vif *)vif->drv_priv;
+}
+
+static inline struct mm81x *mm81x_vif_to_mm(struct mm81x_vif *mm_vif)
+{
+ return mm_vif->mm;
+}
+
+static inline u32 mm81x_generate_cssid(const u8 *ssid, u8 len)
+{
+ return ~crc32(~0, ssid, len);
+}
+
+int mm81x_beacon_init(struct mm81x_vif *mm_vif);
+void mm81x_beacon_finish(struct mm81x_vif *mm_vif);
+void mm81x_beacon_irq_handle(struct mm81x *mm, u32 status);
+int mm81x_usb_ndr_reset(struct mm81x *mm);
+
+int mm81x_core_attach_regs(struct mm81x *mm);
+void mm81x_core_init_mac_addr(struct mm81x *mm);
+char *mm81x_core_get_fw_path(u32 chip_id);
+int mm81x_core_create(struct mm81x *mm);
+void mm81x_core_destroy(struct mm81x *mm);
+
+#endif /* !_MM81X_MM81X_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 07/35] wifi: mm81x: add debug.c
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/debug.c | 87 +++++++++++++++++++
1 file changed, 87 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/debug.c
diff --git a/drivers/net/wireless/morsemicro/mm81x/debug.c b/drivers/net/wireless/morsemicro/mm81x/debug.c
new file mode 100644
index 000000000000..6c9720fa452c
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/debug.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/stdarg.h>
+#include "core.h"
+#include "debug.h"
+
+void mm81x_info(struct mm81x *mm, const char *fmt, ...)
+{
+ struct va_format vaf = { .fmt = fmt };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ dev_info(mm->dev, "%pV\n", &vaf);
+ va_end(args);
+}
+
+void mm81x_err(struct mm81x *mm, const char *fmt, ...)
+{
+ struct va_format vaf = { .fmt = fmt };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ dev_err(mm->dev, "%pV\n", &vaf);
+ va_end(args);
+}
+
+void __mm81x_warn(struct device *dev, const char *fmt, ...)
+{
+ struct va_format vaf = { .fmt = fmt };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ dev_warn_ratelimited(dev, "%pV\n", &vaf);
+ va_end(args);
+}
+
+#ifdef CONFIG_MM81X_DEBUG
+void __mm81x_dbg(struct mm81x *mm, enum mm81x_debug_mask mask, const char *fmt,
+ ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ dev_dbg(mm->dev, "%pV\n", &vaf);
+ va_end(args);
+}
+
+void mm81x_dbg_dump(struct mm81x *mm, enum mm81x_debug_mask mask,
+ const char *msg, const char *prefix, const void *buf,
+ size_t len)
+{
+ const u8 *ptr = buf;
+ char line[256];
+
+ if (!(mm81x_debug_mask & mask))
+ return;
+
+ if (msg)
+ __mm81x_dbg(mm, mask, "%s", msg);
+
+ if (!buf || !len)
+ return;
+
+ while (ptr < (const u8 *)buf + len) {
+ size_t off = ptr - (const u8 *)buf;
+ size_t n = min_t(size_t, 16, (const u8 *)buf + len - ptr);
+ size_t p = 0;
+
+ p += scnprintf(line + p, sizeof(line) - p,
+ "%s%08zx: ", prefix ? prefix : "", off);
+ hex_dump_to_buffer(ptr, n, 16, 1, line + p, sizeof(line) - p,
+ true);
+ dev_dbg(mm->dev, "%s\n", line);
+ ptr += n;
+ }
+}
+#endif /* CONFIG_MM81X_DEBUG */
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 08/35] wifi: mm81x: add debug.h
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/debug.h | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/debug.h
diff --git a/drivers/net/wireless/morsemicro/mm81x/debug.h b/drivers/net/wireless/morsemicro/mm81x/debug.h
new file mode 100644
index 000000000000..9f78386c3ab1
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/debug.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+
+#ifndef _MM81X_DEBUG_H_
+#define _MM81X_DEBUG_H_
+
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+struct mm81x;
+
+enum mm81x_debug_mask {
+ MM81X_DBG_FW = BIT(0),
+ MM81X_DBG_MAC = BIT(1),
+ MM81X_DBG_SKBQ = BIT(2),
+ MM81X_DBG_USB = BIT(3),
+ MM81X_DBG_SDIO = BIT(4),
+ MM81X_DBG_ANY = ~0U,
+};
+
+extern unsigned int mm81x_debug_mask;
+
+__printf(2, 3) void mm81x_info(struct mm81x *mm, const char *fmt, ...);
+__printf(2, 3) void mm81x_err(struct mm81x *mm, const char *fmt, ...);
+__printf(2, 3) void __mm81x_warn(struct device *dev, const char *fmt, ...);
+#define mm81x_warn(mm, fmt, ...) __mm81x_warn((mm)->dev, fmt, ##__VA_ARGS__)
+
+#ifdef CONFIG_MM81X_DEBUG
+__printf(3, 4) void __mm81x_dbg(struct mm81x *mm, enum mm81x_debug_mask mask,
+ const char *fmt, ...);
+
+void mm81x_dbg_dump(struct mm81x *mm, enum mm81x_debug_mask mask,
+ const char *msg, const char *prefix, const void *buf,
+ size_t len);
+#else
+static inline void __mm81x_dbg(struct mm81x *mm, enum mm81x_debug_mask mask,
+ const char *fmt, ...)
+{
+}
+
+static inline void mm81x_dbg_dump(struct mm81x *mm, enum mm81x_debug_mask mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len)
+{
+}
+#endif
+
+#define mm81x_dbg(mm, dbg_mask, fmt, ...) \
+ do { \
+ typeof(dbg_mask) __mask = (dbg_mask); \
+ if (mm81x_debug_mask & __mask) \
+ __mm81x_dbg((mm), __mask, fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#endif /* _MM81X_DEBUG_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 09/35] wifi: mm81x: add fw.c
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/fw.c | 743 +++++++++++++++++++++
1 file changed, 743 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/fw.c
diff --git a/drivers/net/wireless/morsemicro/mm81x/fw.c b/drivers/net/wireless/morsemicro/mm81x/fw.c
new file mode 100644
index 000000000000..6d138419abce
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/fw.c
@@ -0,0 +1,743 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/string_choices.h>
+#include <net/mac80211.h>
+#include <linux/elf.h>
+#include <linux/crc32.h>
+#include "debug.h"
+#include "fw.h"
+#include "mac.h"
+#include "bus.h"
+
+/*
+ * Maximum wait time (milliseconds) for firmware to boot (for host table
+ * pointer to be available)
+ */
+#define MAX_WAIT_FOR_HOST_TABLE_PTR_MS 1200
+
+/* Number of times to attempt flashing FW */
+#define FW_FLASH_ATTEMPT_COUNT 3
+
+static int mm81x_fw_get_header(const u8 *data, mm81x_elf_ehdr *ehdr)
+{
+ mm81x_elf_ehdr *p = (mm81x_elf_ehdr *)data;
+
+ /* Magic check */
+ if (p->e_ident[EI_MAG0] != ELFMAG0 || p->e_ident[EI_MAG1] != ELFMAG1 ||
+ p->e_ident[EI_MAG2] != ELFMAG2 || p->e_ident[EI_MAG3] != ELFMAG3)
+ return -EINVAL;
+
+ /* elf32 and little endian */
+ if (p->e_ident[EI_DATA] != ELFDATA2LSB ||
+ p->e_ident[EI_CLASS] != ELFCLASS32)
+ return -EINVAL;
+
+ ehdr->e_phoff = mm81x_fle32_to_cpu(p->e_phoff);
+ ehdr->e_phentsize = mm81x_fle16_to_cpu(p->e_phentsize);
+ ehdr->e_phnum = mm81x_fle16_to_cpu(p->e_phnum);
+ ehdr->e_shoff = mm81x_fle32_to_cpu(p->e_shoff);
+ ehdr->e_shentsize = mm81x_fle16_to_cpu(p->e_shentsize);
+ ehdr->e_shnum = mm81x_fle16_to_cpu(p->e_shnum);
+ ehdr->e_shstrndx = mm81x_fle16_to_cpu(p->e_shstrndx);
+ ehdr->e_entry = mm81x_fle32_to_cpu(p->e_entry);
+
+ return 0;
+}
+
+static void mm81x_fw_parse_info(struct mm81x *mm, const u8 *data, int length)
+{
+ const struct mm81x_fw_info_tlv *tlv =
+ (const struct mm81x_fw_info_tlv *)data;
+
+ while ((u8 *)tlv < (data + length)) {
+ switch (le16_to_cpu(tlv->type)) {
+ case MM81X_FW_INFO_TLV_BCF_ADDR:
+ mm->bcf_address =
+ get_unaligned_le32((__force __le32 *)tlv->val);
+ break;
+ default:
+ break;
+ }
+ tlv = (const struct mm81x_fw_info_tlv *)((u8 *)tlv +
+ le16_to_cpu(
+ tlv->length) +
+ sizeof(*tlv));
+ }
+}
+
+static int mm81x_fw_get_section_header(const u8 *data, mm81x_elf_ehdr *ehdr,
+ mm81x_elf_shdr *shdr, int i)
+{
+ mm81x_elf_shdr *p = (mm81x_elf_shdr *)(data + ehdr->e_shoff +
+ (i * ehdr->e_shentsize));
+
+ shdr->sh_name = mm81x_fle32_to_cpu(p->sh_name);
+ shdr->sh_type = mm81x_fle32_to_cpu(p->sh_type);
+ shdr->sh_offset = mm81x_fle32_to_cpu(p->sh_offset);
+ shdr->sh_addr = mm81x_fle32_to_cpu(p->sh_addr);
+ shdr->sh_size = mm81x_fle32_to_cpu(p->sh_size);
+ shdr->sh_flags = mm81x_fle32_to_cpu(p->sh_flags);
+
+ return 0;
+}
+
+static int mm81x_fw_set_boot_addr(struct mm81x *mm, uint32_t addr)
+{
+ int status;
+
+ mm81x_dbg(mm, MM81X_DBG_FW, "Overwriting boot address to 0x%x", addr);
+ mm81x_claim_bus(mm);
+ status = mm81x_reg32_write(mm, MM81X_REG_BOOT_ADDR(mm), addr);
+ mm81x_release_bus(mm);
+ return status;
+}
+
+static int mm81x_fw_load_fw(struct mm81x *mm, const struct firmware *fw)
+{
+ int i;
+ int ret = 0;
+ mm81x_elf_ehdr ehdr;
+ mm81x_elf_phdr phdr;
+ mm81x_elf_shdr shdr;
+ mm81x_elf_shdr sh_strtab;
+ const char *sh_strs;
+
+ u8 *fw_buf = devm_kmalloc(mm->dev, ROUND_BYTES_TO_WORD(fw->size),
+ GFP_KERNEL);
+
+ if (!fw_buf)
+ return -ENOMEM;
+
+ if (mm81x_fw_get_header(fw->data, &ehdr)) {
+ mm81x_err(mm, "Wrong file format");
+ return -EINVAL;
+ }
+
+ if (mm81x_fw_get_section_header(fw->data, &ehdr, &sh_strtab,
+ ehdr.e_shstrndx)) {
+ mm81x_err(mm, "Invalid firmware. Missing string table");
+ return -ENOENT;
+ }
+
+ sh_strs = (const char *)fw->data + sh_strtab.sh_offset;
+
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ int status;
+ int address;
+
+ mm81x_elf_phdr *p = (mm81x_elf_phdr *)(fw->data + ehdr.e_phoff +
+ i * ehdr.e_phentsize);
+
+ phdr.p_type = le32_to_cpu((__force __le32)p->p_type);
+ phdr.p_offset = le32_to_cpu((__force __le32)p->p_offset);
+ phdr.p_paddr = le32_to_cpu((__force __le32)p->p_paddr);
+ phdr.p_filesz = le32_to_cpu((__force __le32)p->p_filesz);
+ phdr.p_memsz = le32_to_cpu((__force __le32)p->p_memsz);
+
+ address = phdr.p_paddr;
+ if (address == IFLASH_BASE_ADDR || address == DFLASH_BASE_ADDR)
+ continue;
+
+ if (phdr.p_type != PT_LOAD || !phdr.p_memsz)
+ continue;
+
+ if (phdr.p_filesz && phdr.p_offset &&
+ (phdr.p_offset + phdr.p_filesz) < fw->size) {
+ u32 padded_size = ROUND_BYTES_TO_WORD(phdr.p_filesz);
+
+ memcpy(fw_buf, fw->data + phdr.p_offset, padded_size);
+ /* Set padding to 0xff */
+ memset(fw_buf + phdr.p_filesz, 0xff,
+ padded_size - phdr.p_filesz);
+ mm81x_claim_bus(mm);
+ status = mm81x_dm_write(mm, address, fw_buf,
+ padded_size);
+ mm81x_release_bus(mm);
+ if (status) {
+ ret = -EIO;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ if (mm81x_fw_get_section_header(fw->data, &ehdr, &shdr, i))
+ continue;
+
+ /* This is the firmware info. Parse it */
+ if (!strncmp(sh_strs + shdr.sh_name, ".fw_info",
+ sizeof(".fw_info")))
+ mm81x_fw_parse_info(mm, fw->data + shdr.sh_offset,
+ shdr.sh_size);
+ }
+
+ if (ehdr.e_entry)
+ ret = mm81x_fw_set_boot_addr(mm, ehdr.e_entry);
+
+ devm_kfree(mm->dev, fw_buf);
+ return ret;
+}
+
+static int __mm81x_fw_load_bcf(struct mm81x *mm, unsigned int addr,
+ const void *src, size_t src_len, u8 *scratch,
+ size_t scratch_cap)
+{
+ size_t rounded = ROUND_BYTES_TO_WORD(src_len);
+ int st;
+
+ if (rounded > scratch_cap)
+ return -EINVAL;
+ if (rounded > BCF_DATABASE_SIZE)
+ return -EFBIG;
+
+ memcpy(scratch, src, src_len);
+ if (rounded > src_len)
+ memset(scratch + src_len, 0xff, rounded - src_len);
+
+ mm81x_claim_bus(mm);
+ st = mm81x_dm_write(mm, addr, scratch, rounded);
+ mm81x_release_bus(mm);
+
+ return st ? -EIO : 0;
+}
+
+static int mm81x_fw_load_bcf(struct mm81x *mm, const struct firmware *bcf,
+ unsigned int bcf_address)
+{
+ int i, ret = 0;
+ size_t reg_prefix_len, cfg_len_rounded = 0, reg_len_rounded;
+ mm81x_elf_ehdr ehdr;
+ mm81x_elf_shdr shdr, sh_strtab;
+ const char *sh_strs, *reg_prefix = ".regdom_", *reg_src;
+ size_t reg_len;
+ u8 *bcf_buf;
+
+ bcf_buf = devm_kmalloc(mm->dev, ROUND_BYTES_TO_WORD(bcf->size),
+ GFP_KERNEL);
+ if (!bcf_buf)
+ return -ENOMEM;
+
+ if (mm81x_fw_get_header(bcf->data, &ehdr)) {
+ mm81x_err(mm, "Wrong file format");
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ if (mm81x_fw_get_section_header(bcf->data, &ehdr, &sh_strtab,
+ ehdr.e_shstrndx)) {
+ mm81x_err(mm, "Invalid BCF - missing string table");
+ ret = -ENOENT;
+ goto out_free;
+ }
+
+ sh_strs = (const char *)bcf->data + sh_strtab.sh_offset;
+ reg_prefix_len = strlen(reg_prefix);
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ if (mm81x_fw_get_section_header(bcf->data, &ehdr, &shdr, i))
+ continue;
+ if (strcmp(sh_strs + shdr.sh_name, ".board_config"))
+ continue;
+
+ cfg_len_rounded = ROUND_BYTES_TO_WORD(shdr.sh_size);
+ mm81x_dbg(mm, MM81X_DBG_FW,
+ "Write BCF board_config - addr 0x%x size %zu",
+ bcf_address, cfg_len_rounded);
+
+ ret = __mm81x_fw_load_bcf(mm, bcf_address,
+ bcf->data + shdr.sh_offset,
+ shdr.sh_size, bcf_buf,
+ ROUND_BYTES_TO_WORD(bcf->size));
+ if (ret)
+ goto out_free;
+
+ bcf_address += cfg_len_rounded;
+ break;
+ }
+
+ ret = -EINVAL;
+ for (; i < ehdr.e_shnum; i++) {
+ if (mm81x_fw_get_section_header(bcf->data, &ehdr, &shdr, i))
+ continue;
+ if (strncmp(sh_strs + shdr.sh_name, reg_prefix, reg_prefix_len))
+ continue;
+ if (strncmp(sh_strs + shdr.sh_name + reg_prefix_len,
+ mm->country, 2))
+ continue;
+
+ reg_src = bcf->data + shdr.sh_offset;
+ reg_len = shdr.sh_size;
+ mm81x_dbg(mm, MM81X_DBG_FW, "Write BCF %s - addr 0x%x size %zu",
+ sh_strs + shdr.sh_name, bcf_address,
+ ROUND_BYTES_TO_WORD(reg_len));
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ goto out_free;
+
+ reg_len_rounded = ROUND_BYTES_TO_WORD(reg_len);
+ if ((cfg_len_rounded + reg_len_rounded) > BCF_DATABASE_SIZE) {
+ ret = -EFBIG;
+ goto out_free;
+ }
+
+ ret = __mm81x_fw_load_bcf(mm, bcf_address, reg_src, reg_len, bcf_buf,
+ ROUND_BYTES_TO_WORD(bcf->size));
+
+out_free:
+ devm_kfree(mm->dev, bcf_buf);
+ return ret;
+}
+
+static void mm81x_fw_clear_aon(struct mm81x *mm)
+{
+ int idx;
+ u8 count = MM81X_REG_AON_COUNT(mm);
+ u32 address = MM81X_REG_AON_ADDR(mm);
+
+ if (address) {
+ for (idx = 0; idx < count; idx++, address += 4) {
+ if (mm->bus_type == MM81X_BUS_TYPE_USB && idx == 0)
+ /* Keep the USB power domain enabled in AON. */
+ mm81x_reg32_write(mm, address,
+ MM81X_REG_AON_USB_RESET(mm));
+ else
+ /* clear AON */
+ mm81x_reg32_write(mm, address, 0x0);
+ }
+ }
+
+ mm81x_hw_toggle_aon_latch(mm);
+}
+
+static void mm81x_fw_trigger(struct mm81x *mm)
+{
+ const unsigned int wait_after_msi_trigger_ms = 1;
+
+ mm81x_claim_bus(mm);
+ /*
+ * If not coming from a full reset, some AON flags may be latched.
+ * Make sure to clear any hanging AON bits (can affect booting).
+ */
+ mm81x_fw_clear_aon(mm);
+
+ if (MM81X_REG_CLK_CTRL(mm))
+ mm81x_reg32_write(mm, MM81X_REG_CLK_CTRL(mm),
+ MM81X_REG_CLK_CTRL_VALUE(mm));
+
+ mm81x_reg32_write(mm, MM81X_REG_MSI(mm), MM81X_REG_MSI_HOST_INT(mm));
+ mm81x_release_bus(mm);
+
+ /* Give the chip a chance to boot */
+ mdelay(wait_after_msi_trigger_ms);
+}
+
+static int mm81x_fw_verify_magic(struct mm81x *mm)
+{
+ int ret = 0;
+ int magic = ~MM81X_REG_HOST_MAGIC_VALUE(mm);
+
+ mm81x_claim_bus(mm);
+ mm81x_reg32_read(mm,
+ mm->host_table_ptr +
+ offsetof(struct host_table, magic_number),
+ &magic);
+
+ if (magic != MM81X_REG_HOST_MAGIC_VALUE(mm)) {
+ mm81x_err(mm, "FW magic mismatch 0x%08x:0x%08x",
+ MM81X_REG_HOST_MAGIC_VALUE(mm), magic);
+ ret = -EIO;
+ }
+
+ mm81x_release_bus(mm);
+ return ret;
+}
+
+static int mm81x_fw_get_flags(struct mm81x *mm)
+{
+ int ret = 0;
+ int fw_flags = 0;
+
+ mm81x_claim_bus(mm);
+ ret = mm81x_reg32_read(mm,
+ mm->host_table_ptr + offsetof(struct host_table,
+ firmware_flags),
+ &fw_flags);
+ mm->firmware_flags = fw_flags;
+ mm81x_release_bus(mm);
+
+ return ret;
+}
+
+static int mm81x_fw_check_compatibility(struct mm81x *mm)
+{
+ int ret = 0;
+ u32 fw_version;
+ u32 major;
+ u32 minor;
+ u32 patch;
+
+ mm81x_claim_bus(mm);
+ ret = mm81x_reg32_read(mm,
+ mm->host_table_ptr + offsetof(struct host_table,
+ fw_version_number),
+ &fw_version);
+ mm81x_release_bus(mm);
+
+ major = MM81X_SEMVER_GET_MAJOR(fw_version);
+ minor = MM81X_SEMVER_GET_MINOR(fw_version);
+ patch = MM81X_SEMVER_GET_PATCH(fw_version);
+
+ /* Firmware on device must be recent enough for driver */
+ if (ret == 0 && major != HOST_CMD_SEMVER_MAJOR) {
+ mm81x_err(
+ mm,
+ "Incompatible FW version: (Driver) %d.%d.%d, (Chip) %d.%d.%d\n",
+ HOST_CMD_SEMVER_MAJOR, HOST_CMD_SEMVER_MINOR,
+ HOST_CMD_SEMVER_PATCH, major, minor, patch);
+ ret = -EPERM;
+ } else if (ret == 0 && minor != HOST_CMD_SEMVER_MINOR) {
+ mm81x_warn(
+ mm,
+ "FW version mismatch, some features might not be supported: (Driver) %d.%d.%d, (Chip) %d.%d.%d",
+ HOST_CMD_SEMVER_MAJOR, HOST_CMD_SEMVER_MINOR,
+ HOST_CMD_SEMVER_PATCH, major, minor, patch);
+ }
+
+ return ret;
+}
+
+static int mm81x_fw_invalidate_host_ptr(struct mm81x *mm)
+{
+ int ret;
+
+ mm->host_table_ptr = 0;
+ mm81x_claim_bus(mm);
+ ret = mm81x_reg32_write(mm, MM81X_REG_HOST_MANIFEST_PTR(mm), 0);
+ mm81x_release_bus(mm);
+ return ret;
+}
+
+static int mm81x_fw_get_host_table_ptr(struct mm81x *mm)
+{
+ int ret = 0;
+ unsigned long timeout =
+ jiffies + msecs_to_jiffies(MAX_WAIT_FOR_HOST_TABLE_PTR_MS);
+
+ mm81x_claim_bus(mm);
+ while (1) {
+ ret = mm81x_reg32_read(mm, MM81X_REG_HOST_MANIFEST_PTR(mm),
+ &mm->host_table_ptr);
+
+ if (mm->host_table_ptr)
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ ret = -EIO;
+ break;
+ }
+
+ usleep_range(5000, 10000);
+ }
+
+ mm81x_release_bus(mm);
+ return ret;
+}
+
+static int mm81x_fw_read_ext_host_table(struct mm81x *mm,
+ struct ext_host_tbl **ext_host_table)
+{
+ int ret = 0;
+ u32 host_tbl_ptr = mm->host_table_ptr;
+ u32 ext_host_tbl_ptr;
+ u32 ext_host_tbl_ptr_addr =
+ host_tbl_ptr + offsetof(struct host_table, ext_host_tbl_addr);
+ u32 ext_host_tbl_len;
+ u32 ext_host_tbl_len_ptr_addr;
+ struct ext_host_tbl *host_tbl = NULL;
+
+ mm81x_claim_bus(mm);
+ ret = mm81x_reg32_read(mm, ext_host_tbl_ptr_addr, &ext_host_tbl_ptr);
+ if (ret)
+ goto exit;
+
+ if (!ext_host_tbl_ptr) {
+ ret = -ENXIO;
+ goto exit;
+ }
+
+ ext_host_tbl_len_ptr_addr =
+ ext_host_tbl_ptr +
+ offsetof(struct ext_host_tbl, ext_host_tbl_length);
+
+ ret = mm81x_reg32_read(mm, ext_host_tbl_len_ptr_addr,
+ &ext_host_tbl_len);
+ if (ret)
+ goto exit;
+
+ ext_host_tbl_len = ROUND_BYTES_TO_WORD(ext_host_tbl_len);
+ if (WARN_ON(ext_host_tbl_len == 0 || ext_host_tbl_len > INT_MAX)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ host_tbl = kmalloc(ext_host_tbl_len, GFP_KERNEL);
+ if (!host_tbl) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = mm81x_dm_read(mm, ext_host_tbl_ptr, (u8 *)host_tbl,
+ (int)ext_host_tbl_len);
+ if (ret)
+ goto exit;
+
+ mm81x_release_bus(mm);
+ *ext_host_table = host_tbl;
+ return ret;
+
+exit:
+ mm81x_release_bus(mm);
+ kfree(host_tbl);
+ return ret;
+}
+
+static void mm81x_fw_update_capabilities(struct mm81x *mm,
+ struct ext_host_tbl_s1g_caps *caps)
+{
+ int i;
+
+ for (i = 0; i < FW_CAPABILITIES_FLAGS_WIDTH; i++) {
+ mm->fw_caps.flags[i] = le32_to_cpu(caps->flags[i]);
+ mm81x_dbg(mm, MM81X_DBG_FW, "Firmware Manifest Flags%d: 0x%x",
+ i, le32_to_cpu(caps->flags[i]));
+ }
+ mm->fw_caps.ampdu_mss = caps->ampdu_mss;
+ mm->fw_caps.mm81x_mmss_offset = caps->mm81x_mmss_offset;
+ mm->fw_caps.beamformee_sts_capability = caps->beamformee_sts_capability;
+ mm->fw_caps.maximum_ampdu_length_exponent = caps->maximum_ampdu_length;
+ mm->fw_caps.number_sounding_dimensions =
+ caps->number_sounding_dimensions;
+
+ mm81x_dbg(mm, MM81X_DBG_FW, "\tAMPDU Minimum start spacing: %u",
+ caps->ampdu_mss);
+ mm81x_dbg(mm, MM81X_DBG_FW, "\tMorse Minimum Start Spacing offset: %u",
+ caps->mm81x_mmss_offset);
+ mm81x_dbg(mm, MM81X_DBG_FW, "\tBeamformee STS Capability: %u",
+ caps->beamformee_sts_capability);
+ mm81x_dbg(mm, MM81X_DBG_FW, "\tNumber of Sounding Dimensions: %u",
+ caps->number_sounding_dimensions);
+ mm81x_dbg(mm, MM81X_DBG_FW, "\tMaximum AMPDU Length Exponent: %u",
+ caps->maximum_ampdu_length);
+}
+
+static void mm81x_fw_update_validate_skb_checksum(
+ struct mm81x *mm,
+ struct ext_host_tbl_insert_skb_checksum *validate_checksum)
+{
+ mm->hif.validate_skb_checksum =
+ validate_checksum->insert_and_validate_checksum;
+ mm81x_dbg(mm, MM81X_DBG_ANY, "Validate checksum inserted by fw %s",
+ str_enabled_disabled(mm->hif.validate_skb_checksum));
+}
+
+int mm81x_fw_parse_ext_host_tbl(struct mm81x *mm)
+{
+ int ret;
+ u8 *head;
+ u8 *end;
+ struct ext_host_tbl *ext_host_table = NULL;
+
+ ret = mm81x_fw_read_ext_host_table(mm, &ext_host_table);
+ if (ret || !ext_host_table)
+ goto exit;
+
+ /* Parse the TLVs */
+ head = ext_host_table->ext_host_table_data_tlvs;
+ end = ((u8 *)ext_host_table) +
+ le32_to_cpu(ext_host_table->ext_host_tbl_length);
+
+ while (head < end) {
+ struct ext_host_tbl_tlv_hdr *hdr =
+ (struct ext_host_tbl_tlv_hdr *)head;
+
+ switch (le16_to_cpu(hdr->tag)) {
+ case MM81X_FW_HOST_TABLE_TAG_S1G_CAPABILITIES:
+ mm81x_fw_update_capabilities(
+ mm, (struct ext_host_tbl_s1g_caps *)hdr);
+ break;
+
+ case MM81X_FW_HOST_TABLE_TAG_INSERT_SKB_CHECKSUM:
+ mm81x_fw_update_validate_skb_checksum(
+ mm,
+ (struct ext_host_tbl_insert_skb_checksum *)hdr);
+ break;
+
+ case MM81X_FW_HOST_TABLE_TAG_YAPS_TABLE:
+ mm81x_yaps_hw_read_table(
+ mm, &((struct ext_host_tbl_yaps_table *)hdr)
+ ->yaps_table);
+ break;
+ default:
+ break;
+ }
+
+ head += le16_to_cpu(hdr->length);
+ if (!hdr->length)
+ break;
+ }
+
+ kfree(ext_host_table);
+ return ret;
+exit:
+ mm81x_err(mm, "failed to parse ext host table %d", ret);
+ return ret;
+}
+
+static int __mm81x_fw_flash(struct mm81x *mm, const struct firmware *fw,
+ const struct firmware *bcf, bool reset)
+{
+ int ret;
+
+ if (reset || !mm->chip_was_reset) {
+ ret = mm81x_hw_digital_reset(mm);
+ if (ret)
+ return ret;
+ }
+
+ mm81x_hw_pre_firmware_ndr_hook(mm);
+
+ ret = mm81x_fw_invalidate_host_ptr(mm);
+ if (ret)
+ return ret;
+
+ ret = mm81x_fw_load_fw(mm, fw);
+ if (ret)
+ return ret;
+
+ ret = mm81x_fw_load_bcf(mm, bcf, mm->bcf_address);
+ if (ret)
+ return ret;
+
+ mm81x_fw_trigger(mm);
+ mm81x_hw_post_firmware_ndr_hook(mm);
+
+ ret = mm81x_fw_get_host_table_ptr(mm);
+ if (ret)
+ return ret;
+
+ ret = mm81x_fw_verify_magic(mm);
+ if (ret)
+ return ret;
+
+ return mm81x_fw_check_compatibility(mm);
+}
+
+static int mm81x_fw_flash(struct mm81x *mm, const struct firmware *fw,
+ const struct firmware *bcf, bool reset)
+{
+ int ret;
+ int retries = FW_FLASH_ATTEMPT_COUNT;
+
+ while (retries--) {
+ ret = __mm81x_fw_flash(mm, fw, bcf, reset);
+ if (!ret)
+ return 0;
+
+ mm->chip_was_reset = false;
+ }
+
+ return ret;
+}
+
+static uint32_t binary_crc(const struct firmware *fw)
+{
+ return ~crc32_le(~0, (unsigned char const *)fw->data, fw->size) &
+ 0xffffffff;
+}
+
+int mm81x_fw_init(struct mm81x *mm, bool reset)
+{
+ int ret;
+ int n;
+ int board_id;
+ char *fw_path;
+ char bcf_path[MAX_BCF_NAME_LEN];
+ const struct firmware *fw = NULL;
+ const struct firmware *bcf = NULL;
+
+ fw_path = mm81x_core_get_fw_path(mm->chip_id);
+ if (!fw_path)
+ return -ENOMEM;
+
+ board_id = mm81x_hw_otp_get_board_type(mm);
+
+ if (strlen(board_config_file) > 0) {
+ n = snprintf(bcf_path, sizeof(bcf_path), "%s/%s", MM81X_FW_DIR,
+ board_config_file);
+ } else if (mm81x_hw_otp_valid_board_type(board_id)) {
+ mm81x_dbg(mm, MM81X_DBG_FW, "Using board type 0x%04x from OTP",
+ board_id);
+ n = snprintf(bcf_path, sizeof(bcf_path),
+ "%s/bcf_boardtype_%04x.bin", MM81X_FW_DIR,
+ board_id);
+ } else {
+ mm81x_err(mm, "BCF or Serial parameters are not defined");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (n < 0 || n >= sizeof(bcf_path)) {
+ mm81x_err(mm, "Failed to create BCF path");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = request_firmware(&fw, fw_path, mm->dev);
+ if (ret) {
+ if (ret == -ENOENT)
+ dev_err(mm->dev, "Firmware %s not found\n", fw_path);
+ goto out;
+ }
+
+ dev_info(mm->dev, "Loaded firmware from %s, size %zu, crc32 0x%08x\n",
+ fw_path, fw->size, binary_crc(fw));
+
+ ret = request_firmware(&bcf, bcf_path, mm->dev);
+ if (ret) {
+ if (ret == -ENOENT)
+ dev_err(mm->dev, "BCF %s not found\n", bcf_path);
+ goto out;
+ }
+
+ dev_info(mm->dev, "Loaded BCF from %s, size %zu, crc32 0x%08x\n",
+ bcf_path, bcf->size, binary_crc(bcf));
+
+ ret = mm81x_fw_flash(mm, fw, bcf, reset);
+ if (ret) {
+ mm81x_err(mm, "failed to flash firmware: %d", ret);
+ goto out;
+ }
+
+ ret = mm81x_fw_get_flags(mm);
+
+out:
+ release_firmware(fw);
+ release_firmware(bcf);
+ kfree(fw_path);
+
+ if (ret)
+ mm81x_err(mm, "failed to init firmware: %d", ret);
+ else
+ mm81x_dbg(mm, MM81X_DBG_FW, "firmware initialised");
+
+ return ret;
+}
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 10/35] wifi: mm81x: add fw.h
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/fw.h | 107 +++++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/fw.h
diff --git a/drivers/net/wireless/morsemicro/mm81x/fw.h b/drivers/net/wireless/morsemicro/mm81x/fw.h
new file mode 100644
index 000000000000..6d1e66934248
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/fw.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+
+#ifndef _MM81X_FW_H_
+#define _MM81X_FW_H_
+
+#include <linux/firmware.h>
+#include <linux/completion.h>
+#include "yaps_hw.h"
+
+#define BCF_DATABASE_SIZE (1024)
+#define MM81X_FW_DIR "morsemicro"
+#define MM81X_FW_EXT ".bin"
+
+#define IFLASH_BASE_ADDR 0x400000
+#define DFLASH_BASE_ADDR 0xC00000
+
+#define MAX_BCF_NAME_LEN 64
+
+/* FW_CAPABILITIES_FLAGS_WIDTH = ceil(MM81X_CAPS_MAX_HW_LEN / 32) */
+#define FW_CAPABILITIES_FLAGS_WIDTH (4)
+
+/* Checkpatch does not like Camel Case */
+#define mm81x_elf_ehdr Elf32_Ehdr
+#define mm81x_elf_shdr Elf32_Shdr
+#define mm81x_elf_phdr Elf32_Phdr
+
+enum mm81x_fw_info_tlv_type {
+ MM81X_FW_INFO_TLV_BCF_ADDR = 1,
+};
+
+struct mm81x_fw_info_tlv {
+ __le16 type;
+ __le16 length;
+ u8 val[];
+} __packed;
+
+enum mm81x_fw_ext_host_tbl_tag {
+ /* The S1G capability tag */
+ MM81X_FW_HOST_TABLE_TAG_S1G_CAPABILITIES = 0,
+ MM81X_FW_HOST_TABLE_TAG_PAGER_BYPASS_TX_STATUS = 1,
+ MM81X_FW_HOST_TABLE_TAG_INSERT_SKB_CHECKSUM = 2,
+ MM81X_FW_HOST_TABLE_TAG_YAPS_TABLE = 3,
+ MM81X_FW_HOST_TABLE_TAG_PAGER_PKT_MEMORY = 4,
+ MM81X_FW_HOST_TABLE_TAG_PAGER_BYPASS_CMD_RESP = 5,
+};
+
+struct ext_host_tbl_tlv_hdr {
+ /* The tag used to identify which capability this represents */
+ __le16 tag;
+ /* The length of the capability structure including this header */
+ __le16 length;
+} __packed;
+
+struct ext_host_tbl_s1g_caps {
+ struct ext_host_tbl_tlv_hdr header;
+ __le32 flags[FW_CAPABILITIES_FLAGS_WIDTH];
+ /*
+ * The minimum A-MPDU start spacing required by firmware.
+ * Value | Description
+ * ------|------------
+ * 0 | No restriction
+ * 1 | 1/4 us
+ * 2 | 1/2 us
+ * 3 | 1 us
+ * 4 | 2 us
+ * 5 | 4 us
+ * 6 | 8 us
+ * 7 | 16 us
+ */
+ u8 ampdu_mss;
+ u8 beamformee_sts_capability;
+ u8 number_sounding_dimensions;
+ /*
+ * The maximum A-MPDU length. This is the exponent value such that
+ * (2^(13 + exponent) - 1) is the length
+ */
+ u8 maximum_ampdu_length;
+ /*
+ * Offset to apply to the specification's MMSS table to signal further
+ * minimum MPDU start spacing.
+ */
+ u8 mm81x_mmss_offset;
+} __packed;
+
+struct ext_host_tbl_insert_skb_checksum {
+ struct ext_host_tbl_tlv_hdr header;
+ u8 insert_and_validate_checksum;
+};
+
+struct ext_host_tbl_yaps_table {
+ struct ext_host_tbl_tlv_hdr header;
+ struct mm81x_yaps_hw_table yaps_table;
+} __packed;
+
+struct ext_host_tbl {
+ __le32 ext_host_tbl_length;
+ u8 dev_mac_addr[6];
+ u8 ext_host_table_data_tlvs[];
+} __packed;
+
+int mm81x_fw_init(struct mm81x *mm, bool reset);
+int mm81x_fw_parse_ext_host_tbl(struct mm81x *mm);
+
+#endif /* !_MM81X_FW_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 11/35] wifi: mm81x: add hif.h
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/hif.h | 116 ++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/hif.h
diff --git a/drivers/net/wireless/morsemicro/mm81x/hif.h b/drivers/net/wireless/morsemicro/mm81x/hif.h
new file mode 100644
index 000000000000..73c23a39d14b
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/hif.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+
+#ifndef _MM81X_HIF_H_
+#define _MM81X_HIF_H_
+
+#include "core.h"
+
+struct mm81x_skbq;
+
+#define MM81X_HIF_BYPASS_TX_STATUS_IRQ_NUM (15)
+#define MM81X_HIF_BYPASS_CMD_RESP_IRQ_NUM (29)
+#define MM81X_HIF_IRQ_BYPASS_TX_STATUS_AVAILABLE \
+ BIT(MM81X_HIF_BYPASS_TX_STATUS_IRQ_NUM)
+#define MM81X_HIF_IRQ_BYPASS_CMD_RESP_AVAILABLE \
+ BIT(MM81X_HIF_BYPASS_CMD_RESP_IRQ_NUM)
+
+/* Hardware IF interrupt mask. We may use any interrupts in this range */
+#define MM81X_HIF_IRQ_MASK_ALL \
+ (GENMASK(13, 0) | MM81X_HIF_IRQ_BYPASS_TX_STATUS_AVAILABLE | \
+ MM81X_HIF_IRQ_BYPASS_CMD_RESP_AVAILABLE)
+
+enum mm81x_hif_flags {
+ MM81X_HIF_FLAGS_DIR_TO_HOST = BIT(0),
+ MM81X_HIF_FLAGS_DIR_TO_CHIP = BIT(1),
+ MM81X_HIF_FLAGS_COMMAND = BIT(2),
+ MM81X_HIF_FLAGS_BEACON = BIT(3),
+ MM81X_HIF_FLAGS_DATA = BIT(4)
+};
+
+struct mm81x_hif_ops {
+ int (*init)(struct mm81x *mm);
+ void (*flush_tx_data)(struct mm81x *mm);
+ void (*flush_cmds)(struct mm81x *mm);
+ void (*finish)(struct mm81x *mm);
+ void (*skbq_get_tx_qs)(struct mm81x *mm, struct mm81x_skbq **qs,
+ int *num_qs);
+ struct mm81x_skbq *(*get_tx_cmd_queue)(struct mm81x *mm);
+ struct mm81x_skbq *(*get_tx_beacon_queue)(struct mm81x *mm);
+ struct mm81x_skbq *(*get_tx_mgmt_queue)(struct mm81x *mm);
+ struct mm81x_skbq *(*get_tx_data_queue)(struct mm81x *mm, int aci);
+ int (*handle_irq)(struct mm81x *mm, u32 status);
+ int (*get_tx_buffered_count)(struct mm81x *mm);
+ int (*get_tx_status_pending_count)(struct mm81x *mm);
+};
+
+static inline void mm81x_hif_clear_events(struct mm81x *mm)
+{
+ mm->hif.event_flags = 0;
+}
+
+static inline int mm81x_hif_init(struct mm81x *mm)
+{
+ return mm->hif.ops->init(mm);
+}
+
+static inline void mm81x_hif_flush_tx_data(struct mm81x *mm)
+{
+ mm->hif.ops->flush_tx_data(mm);
+}
+
+static inline void mm81x_hif_flush_cmds(struct mm81x *mm)
+{
+ mm->hif.ops->flush_cmds(mm);
+}
+
+static inline void mm81x_hif_finish(struct mm81x *mm)
+{
+ mm->hif.ops->finish(mm);
+}
+
+static inline void mm81x_hif_skbq_get_tx_qs(struct mm81x *mm,
+ struct mm81x_skbq **qs, int *num_qs)
+{
+ mm->hif.ops->skbq_get_tx_qs(mm, qs, num_qs);
+}
+
+static inline struct mm81x_skbq *mm81x_hif_get_tx_cmd_queue(struct mm81x *mm)
+{
+ return mm->hif.ops->get_tx_cmd_queue(mm);
+}
+
+static inline struct mm81x_skbq *mm81x_hif_get_tx_beacon_queue(struct mm81x *mm)
+{
+ return mm->hif.ops->get_tx_beacon_queue(mm);
+}
+
+static inline struct mm81x_skbq *mm81x_hif_get_tx_mgmt_queue(struct mm81x *mm)
+{
+ return mm->hif.ops->get_tx_mgmt_queue(mm);
+}
+
+static inline struct mm81x_skbq *mm81x_hif_get_tx_data_queue(struct mm81x *mm,
+ int aci)
+{
+ return mm->hif.ops->get_tx_data_queue(mm, aci);
+}
+
+static inline int mm81x_hif_handle_irq(struct mm81x *mm, u32 status)
+{
+ return mm->hif.ops->handle_irq(mm, status);
+}
+
+static inline int mm81x_hif_get_tx_buffered_count(struct mm81x *mm)
+{
+ return mm->hif.ops->get_tx_buffered_count(mm);
+}
+
+static inline int mm81x_hif_get_tx_status_pending_count(struct mm81x *mm)
+{
+ return mm->hif.ops->get_tx_status_pending_count(mm);
+}
+
+#endif /* _MM81X_HIF_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH wireless-next 12/35] wifi: mm81x: add hw.c
From: Lachlan Hodges @ 2026-02-27 4:10 UTC (permalink / raw)
To: johannes, Lachlan Hodges, Dan Callaghan, Arien Judge
Cc: ayman.grais, linux-wireless, linux-kernel
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
(Patches split per file for review, see cover letter for more
information)
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
drivers/net/wireless/morsemicro/mm81x/hw.c | 372 +++++++++++++++++++++
1 file changed, 372 insertions(+)
create mode 100644 drivers/net/wireless/morsemicro/mm81x/hw.c
diff --git a/drivers/net/wireless/morsemicro/mm81x/hw.c b/drivers/net/wireless/morsemicro/mm81x/hw.c
new file mode 100644
index 000000000000..83cfc2c693af
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/hw.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+#include "hif.h"
+#include "debug.h"
+#include "mac.h"
+#include "bus.h"
+#include "core.h"
+#include "fw.h"
+#include "yaps.h"
+
+#define MM8108_REG_HOST_MAGIC_VALUE 0xDEADBEEF
+#define MM8108_REG_RESET_VALUE 0xDEAD
+
+#define MM8108_REG_SDIO_DEVICE_ADDR 0x0000207C
+
+#define MM8108_REG_SDIO_DEVICE_BURST_OFFSET 9
+#define MM8108_REG_TRGR_BASE 0x00003c00
+#define MM8108_REG_INT_BASE 0x00003c50
+#define MM8108_REG_MSI_ADDRESS 0x00004100
+#define MM8108_REG_MSI_VALUE 0x1
+#define MM8108_REG_MANIFEST_PTR_ADDRESS 0x00002d40
+#define MM8108_REG_APPS_BOOT_ADDR 0x00002084
+#define MM8108_REG_RESET 0x000020AC
+#define MM8108_REG_AON_COUNT 2
+
+#define MM8108_REG_AON_ADDR 0x00002114
+#define MM8108_REG_AON_LATCH_ADDR 0x00405020
+#define MM8108_REG_AON_LATCH_MASK 0x1
+#define MM8108_REG_AON_RESET_USB_VALUE 0x8
+#define MM8108_APPS_MAC_DMEM_ADDR_START 0x00100000
+
+#define MM8108_REG_RC_CLK_POWER_OFF_ADDR 0x00405020
+#define MM8108_REG_RC_CLK_POWER_OFF_MASK 0x00000040
+#define MM8108_SLOW_RC_POWER_ON_DELAY_MS 2
+
+#define MM8108_RESET_DELAY_TIME_MS 400
+
+#define MM8108_REG_OTPCTRL_PLDO 0x00004014
+#define MM8108_REG_OTPCTRL_PENVDD2 0x00004010
+#define MM8108_REG_OTPCTRL_PDSTB 0x00004018
+#define MM8108_REG_OTPCTRL_PTM 0x0000401c
+#define MM8108_REG_OTPCTRL_PCE 0x00004020
+#define MM8108_REG_OTPCTRL_PA 0x00004034
+#define MM8108_REG_OTPCTRL_PECCRDB 0x00004048
+#define MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START 0x0000400c
+#define MM8108_REG_OTPCTRL_PDOUT 0x00004040
+
+#define MM81X_OTP_MAC_ADDR_2_BANK_NUM 27
+#define MM81X_OTP_MAC_ADDR_1_BANK_NUM 26
+#define MM81X_OTP_MAC_ADDR_1_MASK GENMASK(31, 16)
+#define MM81X_OTP_BOARD_TYPE_BANK_NUM 26
+#define MM81X_OTP_BOARD_TYPE_MASK GENMASK(15, 0)
+
+#define MM810X_BOARD_TYPE_MAX_VALUE (MM81X_OTP_BOARD_TYPE_MASK - 1)
+
+static void mm81x_hw_otp_power_up(struct mm81x *mm)
+{
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PENVDD2, 1);
+ udelay(2);
+
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PLDO, 1);
+ usleep_range(10, 20);
+
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PDSTB, 1);
+ udelay(3);
+}
+
+static void mm81x_hw_otp_power_down(struct mm81x *mm)
+{
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PDSTB, 0);
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PLDO, 0);
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PENVDD2, 0);
+}
+
+static void mm81x_hw_otp_read_enable(struct mm81x *mm)
+{
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PTM, 0);
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PCE, 1);
+ usleep_range(10, 20);
+}
+
+static void mm81x_hw_otp_read_disable(struct mm81x *mm)
+{
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PCE, 0);
+ udelay(1);
+}
+
+static int mm81x_hw_otp_read(struct mm81x *mm, u8 bank_num, u32 *buf,
+ u8 ignore_ecc)
+{
+ u32 auto_rd_start_tmp;
+ u32 auto_rd_start = 1;
+ int i;
+
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PA, bank_num);
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PECCRDB, ignore_ecc);
+
+ mm81x_reg32_read(mm, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START,
+ &auto_rd_start_tmp);
+ auto_rd_start_tmp &= 0xfffffffe;
+
+ mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START,
+ auto_rd_start | auto_rd_start_tmp);
+
+ /* Attempt reading up to 5 times. */
+ for (i = 0; i < 5 && auto_rd_start; i++) {
+ usleep_range(15, 20);
+ mm81x_reg32_read(mm, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START,
+ &auto_rd_start_tmp);
+ auto_rd_start = auto_rd_start_tmp & 0x1;
+ }
+
+ if (i == 5)
+ return -EIO;
+
+ mm81x_reg32_read(mm, MM8108_REG_OTPCTRL_PDOUT, buf);
+
+ return 0;
+}
+
+int mm81x_hw_otp_get_board_type(struct mm81x *mm)
+{
+ int board_type = 0;
+ u32 otp_word = 0;
+ int ret;
+
+ mm81x_claim_bus(mm);
+ mm81x_hw_otp_power_up(mm);
+ mm81x_hw_otp_read_enable(mm);
+
+ ret = mm81x_hw_otp_read(mm, MM81X_OTP_BOARD_TYPE_BANK_NUM, &otp_word,
+ 1);
+
+ mm81x_hw_otp_read_disable(mm);
+ mm81x_hw_otp_power_down(mm);
+ mm81x_release_bus(mm);
+
+ if (ret)
+ return -EINVAL;
+
+ board_type = otp_word & MM81X_OTP_BOARD_TYPE_MASK;
+
+ return board_type;
+}
+
+bool mm81x_hw_otp_valid_board_type(u32 board_type)
+{
+ return board_type > 0 && board_type < MM810X_BOARD_TYPE_MAX_VALUE;
+}
+
+int mm81x_hw_otp_get_mac_addr(struct mm81x *mm)
+{
+ u32 mac1 = 0;
+ u32 mac2 = 0;
+ int ret = 0;
+
+ mm81x_claim_bus(mm);
+ mm81x_hw_otp_power_up(mm);
+ mm81x_hw_otp_read_enable(mm);
+
+ ret = mm81x_hw_otp_read(mm, MM81X_OTP_MAC_ADDR_1_BANK_NUM, &mac1, 1);
+ if (ret)
+ goto exit;
+
+ ret = mm81x_hw_otp_read(mm, MM81X_OTP_MAC_ADDR_2_BANK_NUM, &mac2, 1);
+ if (ret)
+ goto exit;
+
+ *((u16 *)&mm->macaddr[0]) = (mac1 & MM81X_OTP_MAC_ADDR_1_MASK) >> 16;
+ *((u32 *)&mm->macaddr[2]) = mac2;
+
+exit:
+ mm81x_hw_otp_read_disable(mm);
+ mm81x_hw_otp_power_down(mm);
+ mm81x_release_bus(mm);
+
+ return ret;
+}
+
+void mm81x_hw_irq_enable(struct mm81x *mm, u32 irq, bool enable)
+{
+ u32 irq_en, irq_en_addr = irq < 32 ? MM81X_REG_INT1_EN(mm) :
+ MM81X_REG_INT2_EN(mm);
+ u32 irq_clr_addr = irq < 32 ? MM81X_REG_INT1_CLR(mm) :
+ MM81X_REG_INT2_CLR(mm);
+ u32 mask = irq < 32 ? (1 << irq) : (1 << (irq - 32));
+
+ mm81x_claim_bus(mm);
+ mm81x_reg32_read(mm, irq_en_addr, &irq_en);
+ if (enable)
+ irq_en |= (mask);
+ else
+ irq_en &= ~(mask);
+ mm81x_reg32_write(mm, irq_clr_addr, mask);
+ mm81x_reg32_write(mm, irq_en_addr, irq_en);
+ mm81x_release_bus(mm);
+}
+
+int mm81x_hw_irq_handle(struct mm81x *mm)
+{
+ u32 status1 = 0;
+
+ mm81x_reg32_read(mm, MM81X_REG_INT1_STS(mm), &status1);
+
+ if (status1 & MM81X_HIF_IRQ_MASK_ALL)
+ mm81x_hif_handle_irq(mm, status1);
+
+ if (status1 & MM81X_INT_BEACON_VIF_MASK_ALL)
+ mm81x_mac_beacon_irq_handle(mm, status1);
+
+ mm81x_reg32_write(mm, MM81X_REG_INT1_CLR(mm), status1);
+
+ return status1 ? 1 : 0;
+}
+
+void mm81x_hw_irq_clear(struct mm81x *mm)
+{
+ mm81x_claim_bus(mm);
+ mm81x_reg32_write(mm, MM81X_REG_INT1_CLR(mm), 0xFFFFFFFF);
+ mm81x_reg32_write(mm, MM81X_REG_INT2_CLR(mm), 0xFFFFFFFF);
+ mm81x_release_bus(mm);
+}
+
+int mm81x_hw_toggle_aon_latch(struct mm81x *mm)
+{
+ u32 address = MM81X_REG_AON_LATCH_ADDR(mm);
+ u32 mask = MM81X_REG_AON_LATCH_MASK(mm);
+ u32 latch;
+
+ if (address) {
+ mm81x_reg32_read(mm, address, &latch);
+ mm81x_reg32_write(mm, address, latch & ~(mask));
+ mdelay(5);
+ mm81x_reg32_write(mm, address, latch | mask);
+ mdelay(5);
+ mm81x_reg32_write(mm, address, latch & ~(mask));
+ mdelay(5);
+ }
+
+ return 0;
+}
+
+void mm81x_hw_enable_stop_notifications(struct mm81x *mm, bool enable)
+{
+ mm81x_hw_irq_enable(mm, MM81X_INT_HW_STOP_NOTIFICATION_NUM, enable);
+}
+
+void mm81x_hw_enable_burst_mode(struct mm81x *mm, const u8 burst_mode)
+{
+ u32 reg32_value;
+
+ mm81x_claim_bus(mm);
+ if (mm81x_reg32_read(mm, MM8108_REG_SDIO_DEVICE_ADDR, ®32_value))
+ goto end;
+
+ reg32_value &= ~(u32)(SDIO_WORD_BURST_MASK
+ << MM8108_REG_SDIO_DEVICE_BURST_OFFSET);
+ reg32_value |= (u32)(burst_mode << MM8108_REG_SDIO_DEVICE_BURST_OFFSET);
+
+ mm81x_dbg(mm, MM81X_DBG_ANY,
+ "Setting Burst mode to %d Writing 0x%08X to the register",
+ burst_mode, reg32_value);
+
+ if (mm81x_reg32_write(mm, MM8108_REG_SDIO_DEVICE_ADDR, reg32_value))
+ goto end;
+
+end:
+ mm81x_release_bus(mm);
+}
+
+static int mm81x_hw_enable_internal_slow_clock(struct mm81x *mm)
+{
+ u32 rc_clock_reg_value;
+ int ret = 0;
+
+ mm81x_dbg(mm, MM81X_DBG_ANY, "Enabling internal slow clock");
+
+ ret = mm81x_reg32_read(mm, MM8108_REG_RC_CLK_POWER_OFF_ADDR,
+ &rc_clock_reg_value);
+ if (ret)
+ goto exit;
+
+ rc_clock_reg_value &= ~MM8108_REG_RC_CLK_POWER_OFF_MASK;
+ ret = mm81x_reg32_write(mm, MM8108_REG_RC_CLK_POWER_OFF_ADDR,
+ rc_clock_reg_value);
+ if (ret)
+ goto exit;
+
+ ret = mm81x_hw_toggle_aon_latch(mm);
+ if (ret)
+ goto exit;
+
+ /* Wait for the clock to turn on and settle */
+ mdelay(MM8108_SLOW_RC_POWER_ON_DELAY_MS);
+exit:
+ return ret;
+}
+
+int mm81x_hw_digital_reset(struct mm81x *mm)
+{
+ int ret = 0;
+
+ mm81x_claim_bus(mm);
+
+ /* This should be the first step in digital reset, do not reorder */
+ ret = mm81x_hw_enable_internal_slow_clock(mm);
+ if (ret)
+ goto exit;
+
+ if (mm->bus_type == MM81X_BUS_TYPE_USB) {
+#ifdef CONFIG_MM81X_USB
+ ret = mm81x_usb_ndr_reset(mm);
+#endif
+ goto usb_done;
+ }
+
+ if (MM81X_REG_RESET(mm) != 0)
+ ret = mm81x_reg32_write(mm, MM81X_REG_RESET(mm),
+ MM81X_REG_RESET_VALUE(mm));
+
+usb_done:
+ msleep(MM8108_RESET_DELAY_TIME_MS);
+exit:
+ mm81x_release_bus(mm);
+
+ if (!ret)
+ mm->chip_was_reset = true;
+
+ return ret;
+}
+
+void mm81x_hw_pre_firmware_ndr_hook(struct mm81x *mm)
+{
+ /* We need disable bursting for firmware download/init procedure */
+ mm81x_bus_config_burst_mode(mm, false);
+}
+
+void mm81x_hw_post_firmware_ndr_hook(struct mm81x *mm)
+{
+ /* We are safe here to reenable bursting again, if supported */
+ mm81x_bus_config_burst_mode(mm, true);
+}
+
+const struct mm81x_regs mm8108_regs = {
+ .chip_id_address = MM8108_REG_CHIP_ID,
+ .irq_base_address = MM8108_REG_INT_BASE,
+ .trgr_base_address = MM8108_REG_TRGR_BASE,
+ .cpu_reset_address = MM8108_REG_RESET,
+ .cpu_reset_value = MM8108_REG_RESET_VALUE,
+ .manifest_ptr_address = MM8108_REG_MANIFEST_PTR_ADDRESS,
+ .msi_address = MM8108_REG_MSI_ADDRESS,
+ .msi_value = MM8108_REG_MSI_VALUE,
+ .magic_num_value = MM8108_REG_HOST_MAGIC_VALUE,
+ .early_clk_ctrl_value = 0,
+ .pager_base_address = MM8108_APPS_MAC_DMEM_ADDR_START,
+ .aon_latch = MM8108_REG_AON_LATCH_ADDR,
+ .aon_latch_mask = MM8108_REG_AON_LATCH_MASK,
+ .aon_reset_usb_value = MM8108_REG_AON_RESET_USB_VALUE,
+ .aon = MM8108_REG_AON_ADDR,
+ .aon_count = MM8108_REG_AON_COUNT,
+ .boot_address = MM8108_REG_APPS_BOOT_ADDR,
+};
+
+/* B2 ROM_LINKED */
+MODULE_FIRMWARE(MM81X_FW_DIR "/" MM8108_FW_BASE MM8108B2_REV_STRING
+ FW_ROM_LINKED_STRING MM81X_FW_EXT);
--
2.43.0
^ 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