Lots of fixes:

- mostly driver security/robustness/warning fixes
  - ath12k: fix MLO throughput regression
  - iwlwifi: add UNII-9 to avoid regression
  - brcmfmac:
    - fix 802.1X-SHA256
    - SDIO fix for some boards
  - mac80211:
    - fix traffic indication for sleeping STAs
    - fix NAN throughput
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmpgjBQACgkQ10qiO8sP
 aACNoQ//dgLEKav5m9IfUvhZUV/G++3klPkCTUNU3dQesrHrGRxOk1sRuzoODINz
 rC4IfDtoznIX964Lxc+RGLtTE1zUXJ9lR9YDvjXHyF+9kk8CvMbiKJo37IG4GCTc
 DsQWL3cSK7X4yDbosd5gCXihjxOKFT65S2e9inFPLUWevi7mbb+Qny/jMd5yqh32
 /q/YUKUPSjZqqUFnG1x9nrp4n+0ygBsqUK9o8w6cU9ed7bw2r6fJo+j0BkzxUsPH
 Ntde80QKNYgqY73qJ0qEybm7o7XldBMSdHnaO0Iz9qOnlTclpyV7zrfca8ELx2Sx
 GQ/1ws1G+/rg3AWsWkXsu8QrUk0e4WuKFel59BBYyfRsvmDjz9Hta9qW9WMioykQ
 eGEM0EMJ683BtpYN4RKMmPG20jCxCi8dUBE8OGuRQX/c6exXyVysTNi3rt2oKsls
 VMtd74DrdjYrm+MwnxAfnB1CIDlgCbbGMvoZMcQLSQTPmSM5822XKAlGCcNQjkL9
 HC0D7KItFXp/LYEoELkXGy+D8ta0BVxJTtHzuf5Msx4DgVu+fmFOwyHud13Hfwei
 JfrMMYjym3Aaa+X5y21HgzkqGmPJ9zg7vD5kXRt2vtwax9NiGxWqI/4EPSHD19l1
 m6dhiOzYWmHPBsJMtLuvkG9Xmc8V3K40YDK6Dw/1i9yAbUBIpZE=
 =BfIb
 -----END PGP SIGNATURE-----

Merge tag 'wireless-2026-07-22' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless

Johannes Berg says:

====================
Lots of fixes:
 - mostly driver security/robustness/warning fixes
 - ath12k: fix MLO throughput regression
 - iwlwifi: add UNII-9 to avoid regression
 - brcmfmac:
   - fix 802.1X-SHA256
   - SDIO fix for some boards
 - mac80211:
   - fix traffic indication for sleeping STAs
   - fix NAN throughput

* tag 'wireless-2026-07-22' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless: (80 commits)
  wifi: brcmfmac: fix 802.1X-SHA256 call trace warning
  wifi: mt76: mt7996: fix possible NULL-pointer deref in mt7996_mcu_sta_bfer_eht()
  wifi: mt76: mt7925: fix crash in reset link replay
  wifi: mt76: fix airoha_npu dependency tracking
  wifi: mt76: restrict NPU/PPE active checks to MMIO devices
  wifi: mt76: fix MAC address for non OF pcie cards
  wifi: mt76: mt7996: check pointer returned by mt76_connac_get_he_phy_cap()
  wifi: mt76: mt7925: fix possible NULL-pointer deref in mt7925_mcu_bss_he_tlv()
  wifi: mt76: connac: fix possible NULL-pointer deref in mt76_connac_mcu_uni_bss_he_tlv()
  wifi: mt76: mt7915: guard HE capability lookups
  wifi: mt76: mt7925: guard link STA in decap offload
  wifi: mt76: Disable napi when removing device
  wifi: mt76: mt7615: drop TXRX_NOTIFY on non-mmio buses
  wifi: mt76: mt7925: drop TXRX_NOTIFY on non-mmio buses
  wifi: mt76: mt7921: drop TXRX_NOTIFY on non-mmio buses
  wifi: brcmfmac: set F2 blocksize to 256 for BCM43752
  wifi: cfg80211: guard optional PMSR nominal time
  wifi: mac80211_hwsim: reject undersized HWSIM_ATTR_TX_INFO
  wifi: brcmfmac: drain bus_reset work on device removal
  wifi: brcmfmac: make release_scratchbuffers idempotent
  ...
====================

Link: https://patch.msgid.link/20260722092647.119094-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-07-22 09:07:35 -07:00
commit fec15bb3da
87 changed files with 745 additions and 204 deletions

View File

@ -706,6 +706,7 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
if (!(__le32_to_cpu(rxd_attention->flags) &
RX_ATTENTION_FLAGS_MSDU_DONE)) {
ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
__skb_queue_purge(list);
return -EIO;
}
}
@ -770,6 +771,7 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt,
if (!(__le32_to_cpu(rxd_attention->flags) &
RX_ATTENTION_FLAGS_MSDU_DONE)) {
ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
__skb_queue_purge(list);
return -EIO;
}
}

View File

@ -996,6 +996,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab)
ret = ath11k_ahb_setup_msa_resources(ab);
if (ret) {
ath11k_err(ab, "failed to setup msa resources\n");
of_node_put(node);
return ret;
}

View File

@ -4618,6 +4618,9 @@ static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar,
msdu_details = &msdu_link->msdu_link[0];
for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) {
if (!i && FIELD_GET(BUFFER_ADDR_INFO0_ADDR,
msdu_details[i].buf_addr_info.info0) == 0)
break;
if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR,
msdu_details[i].buf_addr_info.info0) == 0) {
msdu_desc_info = &msdu_details[i - 1].rx_msdu_info;

View File

@ -199,6 +199,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
val |= PCIE_SOC_GLOBAL_RESET_V;
ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
/* Flush the posted write to the device */
ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);
/* TODO: exact time to sleep is uncertain */
delay = 10;
@ -208,6 +210,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
val &= ~PCIE_SOC_GLOBAL_RESET_V;
ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
/* Flush the posted write to the device */
ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);
mdelay(delay);

View File

@ -3294,9 +3294,14 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work)
clear_bit(ATH11K_FLAG_CRASH_FLUSH,
&ab->dev_flags);
clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags);
ath11k_core_qmi_firmware_ready(ab);
set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags);
if (!test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) {
ret = ath11k_core_qmi_firmware_ready(ab);
if (ret) {
set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags);
break;
}
set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags);
}
break;
case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE:
break;

View File

@ -274,11 +274,14 @@ int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp)
void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp)
{
mutex_lock(&dp->link_peer_rhash_tbl_lock);
guard(mutex)(&dp->link_peer_rhash_tbl_lock);
if (!dp->rhead_peer_addr)
return;
rhashtable_destroy(dp->rhead_peer_addr);
kfree(dp->rhead_peer_addr);
dp->rhead_peer_addr = NULL;
mutex_unlock(&dp->link_peer_rhash_tbl_lock);
}
static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp,
@ -570,6 +573,8 @@ int ath12k_dp_link_peer_assign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_hw,
peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);
rcu_assign_pointer(dp_peer->link_peers[peer->link_id], peer);
WRITE_ONCE(dp_peer->link_peers_map,
READ_ONCE(dp_peer->link_peers_map) | BIT(peer->link_id));
rcu_assign_pointer(dp_hw->dp_peers[peerid_index], dp_peer);
@ -632,6 +637,8 @@ void ath12k_dp_link_peer_unassign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_
peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);
rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL);
WRITE_ONCE(dp_peer->link_peers_map,
READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id));
rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL);

View File

@ -140,6 +140,7 @@ struct ath12k_dp_peer {
/* Info used in MMIC verification of * RX fragments */
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
unsigned long link_peers_map;
struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS];
struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1];
struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];

View File

@ -1384,10 +1384,9 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
pubsta = peer ? peer->sta : NULL;
if (pubsta && pubsta->valid_links) {
status->link_valid = 1;
status->link_id = peer->hw_links[rxcb->hw_link_id];
}
status->link_valid = 0;
if (pubsta && pubsta->valid_links)
ath12k_hw_set_rx_link_id(dp->hw_params, peer, rxcb, status);
ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
"rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",

View File

@ -13,6 +13,10 @@
#include "wmi.h"
#include "hal.h"
struct ath12k_dp_peer;
struct ath12k_skb_rxcb;
struct ieee80211_rx_status;
/* Target configuration defines */
/* Num VDEVS per radio */
@ -243,6 +247,9 @@ struct ath12k_hw_ops {
bool (*dp_srng_is_tx_comp_ring)(int ring_num);
bool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif,
struct ieee80211_mgmt *mgmt);
void (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer,
struct ath12k_skb_rxcb *rxcb,
struct ieee80211_rx_status *status);
};
static inline
@ -273,6 +280,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw,
return 0;
}
static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw,
struct ath12k_dp_peer *dp_peer,
struct ath12k_skb_rxcb *rxcb,
struct ieee80211_rx_status *status)
{
if (hw->hw_ops->set_rx_link_id)
hw->hw_ops->set_rx_link_id(dp_peer, rxcb, status);
}
struct ath12k_fw_ie {
__le32 id;
__le32 len;

View File

@ -1238,9 +1238,13 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar)
/* cleanup dp peer */
spin_lock_bh(&dp_hw->peer_lock);
dp_peer = peer->dp_peer;
peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);
rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL);
rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL);
if (dp_peer) {
peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);
rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL);
WRITE_ONCE(dp_peer->link_peers_map,
READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id));
rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL);
}
spin_unlock_bh(&dp_hw->peer_lock);
ath12k_dp_link_peer_rhash_delete(dp, peer);

View File

@ -188,6 +188,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
val |= PCIE_SOC_GLOBAL_RESET_V;
ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
/* Flush the posted write to the device */
ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
/* TODO: exact time to sleep is uncertain */
delay = 10;
@ -197,6 +199,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
val &= ~PCIE_SOC_GLOBAL_RESET_V;
ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
/* Flush the posted write to the device */
ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
mdelay(delay);

View File

@ -453,6 +453,9 @@ int ath12k_link_sta_rhash_tbl_init(struct ath12k_base *ab)
void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab)
{
if (!ab->rhead_sta_addr)
return;
rhashtable_destroy(ab->rhead_sta_addr);
kfree(ab->rhead_sta_addr);
ab->rhead_sta_addr = NULL;

View File

@ -5,6 +5,7 @@
*/
#include "dp_rx.h"
#include "../dp_peer.h"
#include "../dp_tx.h"
#include "../peer.h"
#include "hal_qcn9274.h"
@ -2315,3 +2316,35 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
return tlv_tag == HAL_RX_MPDU_START;
}
void
ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
struct ath12k_skb_rxcb *rxcb,
struct ieee80211_rx_status *status)
{
status->link_valid = 1;
status->link_id = dp_peer->hw_links[rxcb->hw_link_id];
}
void
ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
struct ath12k_skb_rxcb *rxcb,
struct ieee80211_rx_status *status)
{
struct ath12k_dp_link_peer *link_peer;
unsigned long links_map;
int i;
RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
"ath12k set rx link id called without rcu lock");
links_map = READ_ONCE(dp_peer->link_peers_map);
for_each_set_bit(i, &links_map, ATH12K_NUM_MAX_LINKS) {
link_peer = rcu_dereference(dp_peer->link_peers[i]);
if (link_peer && link_peer->peer_id == rxcb->peer_id) {
status->link_valid = 1;
status->link_id = link_peer->link_id;
return;
}
}
}

View File

@ -57,4 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
struct hal_rx_desc *rx_desc);
int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab,
struct ath12k_dp_rx_tid_rxq *rx_tid);
void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
struct ath12k_skb_rxcb *rxcb,
struct ieee80211_rx_status *status);
void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
struct ath12k_skb_rxcb *rxcb,
struct ieee80211_rx_status *status);
#endif

View File

@ -158,6 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = {
.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274,
.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274,
.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274,
.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274,
};
static const struct ath12k_hw_ops wcn7850_ops = {
@ -168,6 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = {
.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
};
static const struct ath12k_hw_ops qcc2072_ops = {
@ -178,6 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = {
.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
};
#define ATH12K_TX_RING_MASK_0 0x1

View File

@ -1723,13 +1723,15 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no,
rxtid = &aggr_conn->rx_tid[tid];
if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX)
ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
__func__, win_sz, tid);
if (rxtid->aggr)
aggr_delete_tid_state(aggr_conn, tid);
if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) {
ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
__func__, win_sz, tid);
return;
}
rxtid->seq_next = seq_no;
hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q);
rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL);
@ -1828,7 +1830,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)
return;
if (aggr_conn->timer_scheduled) {
timer_delete(&aggr_conn->timer);
timer_delete_sync(&aggr_conn->timer);
aggr_conn->timer_scheduled = false;
}

View File

@ -484,6 +484,18 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
evt = (struct wmi_tx_complete_event *) datap;
if (len < sizeof(*evt)) {
ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d\n",
len);
return -EINVAL;
}
if (len < sizeof(*evt) + evt->num_msg * sizeof(struct tx_complete_msg_v1)) {
ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d for %u msgs\n",
len, evt->num_msg);
return -EINVAL;
}
ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n",
evt->num_msg, evt->msg_len, evt->msg_type);
@ -862,6 +874,14 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len,
ev = (struct wmi_connect_event *) datap;
if (len < sizeof(*ev) + ev->beacon_ie_len +
ev->assoc_req_len + ev->assoc_resp_len) {
ath6kl_dbg(ATH6KL_DBG_WMI,
"connect event: IE lengths %u+%u+%u exceed buffer %d\n",
ev->beacon_ie_len, ev->assoc_req_len,
ev->assoc_resp_len, len);
return -EINVAL;
}
if (vif->nw_type == AP_NETWORK) {
/* AP mode start/STA connected event */
struct net_device *dev = vif->ndev;

View File

@ -1215,15 +1215,10 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev,
ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name,
&hif_dev->udev->dev, GFP_KERNEL,
hif_dev, ath9k_hif_usb_firmware_cb);
if (ret) {
if (ret)
dev_err(&hif_dev->udev->dev,
"ath9k_htc: Async request for firmware %s failed\n",
hif_dev->fw_name);
return ret;
}
dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n",
hif_dev->fw_name);
return ret;
}

View File

@ -150,7 +150,8 @@ static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer)
spin_lock(&ar->cmd_lock);
if (ar->readbuf) {
if (len >= 4)
memcpy(ar->readbuf, buffer + 4, len - 4);
memcpy(ar->readbuf, buffer + 4,
min_t(u32, len - 4, ar->readlen));
ar->readbuf = NULL;
}
@ -917,7 +918,9 @@ static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len)
}
}
skb_put_data(ar->rx_failover, tbuf, tlen);
skb_put_data(ar->rx_failover, tbuf,
min_t(unsigned int, tlen,
ar->rx_failover_missing));
ar->rx_failover_missing -= tlen;
if (ar->rx_failover_missing <= 0) {

View File

@ -692,7 +692,7 @@ void carl9170_tx_process_status(struct ar9170 *ar,
unsigned int i;
for (i = 0; i < cmd->hdr.ext; i++) {
if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) {
if (WARN_ON(i >= (cmd->hdr.len / 2))) {
print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE,
(void *) cmd, cmd->hdr.len + 4);
break;

View File

@ -1521,13 +1521,16 @@ static inline int at76_guess_freq(struct at76_priv *priv)
if (ieee80211_is_probe_resp(hdr->frame_control)) {
el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable;
} else if (ieee80211_is_beacon(hdr->frame_control)) {
el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable);
el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable;
} else {
goto exit;
}
if (len < el_off)
goto exit;
el = priv->rx_skb->data + el_off;
len -= el_off;
el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len);

View File

@ -911,6 +911,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
return ret;
}
switch (sdiodev->func2->device) {
case SDIO_DEVICE_ID_BROADCOM_43752:
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE;
break;
@ -1069,6 +1070,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
bus_if = kzalloc_obj(*bus_if);
if (!bus_if)
return -ENOMEM;
mutex_init(&bus_if->bus_reset_lock);
sdiodev = kzalloc_obj(*sdiodev);
if (!sdiodev) {
kfree(bus_if);
@ -1130,6 +1132,14 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func)
if (func->num != 1)
return;
/* Drain bus_reset before the shared brcmf_sdiod_remove()
* teardown, which the SDIO reset callback also reaches. The
* data worker can arm bus_reset via brcmf_fw_crashed(); cancel
* it first.
*/
brcmf_sdio_cancel_datawork(sdiodev->bus);
brcmf_bus_cancel_reset_work(bus_if);
/* only proceed with rest of cleanup if func 1 */
brcmf_sdiod_remove(sdiodev);
@ -1204,6 +1214,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
} else {
/* power will be cut so remove device, probe again in resume */
brcmf_sdiod_intr_unregister(sdiodev);
brcmf_sdio_cancel_datawork(sdiodev->bus);
brcmf_bus_cancel_reset_work(bus_if);
ret = brcmf_sdiod_remove(sdiodev);
if (ret)
brcmf_err("Failed to remove device on suspend\n");
@ -1229,6 +1241,8 @@ static int brcmf_ops_sdio_resume(struct device *dev)
ret = brcmf_sdiod_probe(sdiodev);
if (ret)
brcmf_err("Failed to probe device on resume\n");
else
brcmf_bus_allow_reset_work(bus_if);
} else {
if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);

View File

@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include "debug.h"
/* IDs of the 6 default common rings of msgbuf protocol */
@ -179,6 +180,8 @@ struct brcmf_bus {
enum brcmf_fwvendor fwvid;
bool always_use_fws_queue;
bool wowl_supported;
bool removing; /* device removal in progress; quiesce async work */
struct mutex bus_reset_lock;
const struct brcmf_bus_ops *ops;
struct brcmf_bus_msgbuf *msgbuf;
@ -186,6 +189,9 @@ struct brcmf_bus {
struct list_head list;
};
void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if);
void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if);
/*
* callback wrappers
*/

View File

@ -2146,7 +2146,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
sme->crypto.akm_suites[0]);
return -EINVAL;
}
} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_1X_SHA256)) {
switch (sme->crypto.akm_suites[0]) {
case WLAN_AKM_SUITE_8021X:
val = WPA2_AUTH_UNSPECIFIED;

View File

@ -1167,6 +1167,35 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data)
return 0;
}
/*
* Serialize arming from debugfs reset and brcmf_fw_crashed() against
* teardown. The remove path sets ->removing and drains the work while
* holding bus_reset_lock, so a racing armer is either drained or skips it.
*/
static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if)
{
mutex_lock(&bus_if->bus_reset_lock);
if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing)
schedule_work(&bus_if->drvr->bus_reset);
mutex_unlock(&bus_if->bus_reset_lock);
}
void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if)
{
mutex_lock(&bus_if->bus_reset_lock);
bus_if->removing = true;
if (bus_if->drvr)
cancel_work_sync(&bus_if->drvr->bus_reset);
mutex_unlock(&bus_if->bus_reset_lock);
}
void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if)
{
mutex_lock(&bus_if->bus_reset_lock);
bus_if->removing = false;
mutex_unlock(&bus_if->bus_reset_lock);
}
static void brcmf_core_bus_reset(struct work_struct *work)
{
struct brcmf_pub *drvr = container_of(work, struct brcmf_pub,
@ -1187,7 +1216,7 @@ static ssize_t bus_reset_write(struct file *file, const char __user *user_buf,
if (value != 1)
return -EINVAL;
schedule_work(&drvr->bus_reset);
brcmf_bus_schedule_reset(drvr->bus_if);
return count;
}
@ -1417,14 +1446,23 @@ void brcmf_dev_coredump(struct device *dev)
void brcmf_fw_crashed(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
struct brcmf_pub *drvr;
/* May fire before brcmf_attach() wires up drvr, or after removal
* has cleared it; guard the derefs below (and the arming gate in
* brcmf_bus_schedule_reset() already checks drvr/->removing).
*/
if (!bus_if)
return;
drvr = bus_if->drvr;
if (!drvr)
return;
bphy_err(drvr, "Firmware has halted or crashed\n");
brcmf_dev_coredump(dev);
if (drvr->bus_reset.func)
schedule_work(&drvr->bus_reset);
brcmf_bus_schedule_reset(bus_if);
}
void brcmf_detach(struct device *dev)

View File

@ -1383,16 +1383,20 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo)
static void
brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo)
{
if (devinfo->shared.scratch)
if (devinfo->shared.scratch) {
dma_free_coherent(&devinfo->pdev->dev,
BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
devinfo->shared.scratch,
devinfo->shared.scratch_dmahandle);
if (devinfo->shared.ringupd)
devinfo->shared.scratch = NULL;
}
if (devinfo->shared.ringupd) {
dma_free_coherent(&devinfo->pdev->dev,
BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
devinfo->shared.ringupd,
devinfo->shared.ringupd_dmahandle);
devinfo->shared.ringupd = NULL;
}
}
static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo)
@ -2499,6 +2503,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = -ENOMEM;
goto fail;
}
mutex_init(&bus->bus_reset_lock);
bus->msgbuf = kzalloc_obj(*bus->msgbuf);
if (!bus->msgbuf) {
ret = -ENOMEM;
@ -2594,6 +2599,11 @@ brcmf_pcie_remove(struct pci_dev *pdev)
if (devinfo->ci)
brcmf_pcie_intr_disable(devinfo);
if (devinfo->irq_allocated)
synchronize_irq(pdev->irq);
brcmf_bus_cancel_reset_work(bus);
brcmf_detach(&pdev->dev);
brcmf_free(&pdev->dev);

View File

@ -4560,6 +4560,12 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
return ret;
}
void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus)
{
if (bus)
cancel_work_sync(&bus->datawork);
}
/* Detach and free everything */
void brcmf_sdio_remove(struct brcmf_sdio *bus)
{

View File

@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev);
int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
void brcmf_sdio_remove(struct brcmf_sdio *bus);
void brcmf_sdio_isr(struct brcmf_sdio *bus, bool in_isr);
void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus);
void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active);
void brcmf_sdio_wowl_config(struct device *dev, bool enabled);

View File

@ -1260,6 +1260,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo,
ret = -ENOMEM;
goto fail;
}
mutex_init(&bus->bus_reset_lock);
bus->dev = dev;
bus_pub->bus = bus;
@ -1329,6 +1330,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
return;
brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo);
brcmf_bus_cancel_reset_work(devinfo->bus_pub.bus);
brcmf_detach(devinfo->dev);
brcmf_free(devinfo->dev);
kfree(devinfo->bus_pub.bus);

View File

@ -15,6 +15,7 @@
.non_shared_ant = ANT_B, \
.vht_mu_mimo_supported = true, \
.uhb_supported = true, \
.unii9_supported = true, \
.eht_supported = true, \
.uhr_supported = true, \
.num_rbds = IWL_NUM_RBDS_EHT, \

View File

@ -865,6 +865,11 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
min_size, max_size,
&tbl_rev);
if (!IS_ERR(wifi_pkg)) {
if (tbl_rev < 0 ||
tbl_rev >= BITS_PER_BYTE *
sizeof(rev_data[idx].revisions))
continue;
if (!(BIT(tbl_rev) & rev_data[idx].revisions))
continue;

View File

@ -205,6 +205,7 @@ struct iwl_nvm_get_info_phy {
#define IWL_NUM_CHANNELS_V1 51
#define IWL_NUM_CHANNELS_V2 110
#define IWL_NUM_CHANNELS_V3 115
/**
* struct iwl_nvm_get_info_regulatory_v1 - regulatory information
@ -219,12 +220,12 @@ struct iwl_nvm_get_info_regulatory_v1 {
} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */
/**
* struct iwl_nvm_get_info_regulatory - regulatory information
* struct iwl_nvm_get_info_regulatory_v2 - regulatory information
* @lar_enabled: is LAR enabled
* @n_channels: number of valid channels in the array
* @channel_profile: regulatory data of this channel
*/
struct iwl_nvm_get_info_regulatory {
struct iwl_nvm_get_info_regulatory_v2 {
__le32 lar_enabled;
__le32 n_channels;
__le32 channel_profile[IWL_NUM_CHANNELS_V2];
@ -244,6 +245,32 @@ struct iwl_nvm_get_info_rsp_v3 {
struct iwl_nvm_get_info_regulatory_v1 regulatory;
} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_3 */
/**
* struct iwl_nvm_get_info_rsp_v4 - response to get NVM data
* @general: general NVM data
* @mac_sku: data relating to MAC sku
* @phy_sku: data relating to PHY sku
* @regulatory: regulatory data
*/
struct iwl_nvm_get_info_rsp_v4 {
struct iwl_nvm_get_info_general general;
struct iwl_nvm_get_info_sku mac_sku;
struct iwl_nvm_get_info_phy phy_sku;
struct iwl_nvm_get_info_regulatory_v2 regulatory;
} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */
/**
* struct iwl_nvm_get_info_regulatory - regulatory information
* @lar_enabled: is LAR enabled
* @n_channels: number of valid channels in the array
* @channel_profile: regulatory data of this channel
*/
struct iwl_nvm_get_info_regulatory {
__le32 lar_enabled;
__le32 n_channels;
__le32 channel_profile[IWL_NUM_CHANNELS_V3];
} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_3 */
/**
* struct iwl_nvm_get_info_rsp - response to get NVM data
* @general: general NVM data
@ -256,7 +283,7 @@ struct iwl_nvm_get_info_rsp {
struct iwl_nvm_get_info_sku mac_sku;
struct iwl_nvm_get_info_phy phy_sku;
struct iwl_nvm_get_info_regulatory regulatory;
} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */
} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_5 */
/**
* struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@ -1319,7 +1319,7 @@ struct iwl_scan_offload_match_info {
u8 resume_while_scanning;
u8 self_recovery;
__le16 reserved;
struct iwl_scan_offload_profile_match matches[];
struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES_V2];
} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_3 and
* SCAN_OFFLOAD_MATCH_INFO_NOTIFICATION_S_VER_1
*/

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright(c) 2020-2025 Intel Corporation
* Copyright(c) 2020-2026 Intel Corporation
*/
#include "iwl-drv.h"
@ -12,6 +12,7 @@
#include "fw/api/alive.h"
#include "fw/uefi.h"
#include "fw/img.h"
#include "fw/dbg.h"
#define IWL_PNVM_REDUCED_CAP_BIT BIT(25)
@ -26,6 +27,12 @@ static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,
struct iwl_trans *trans = (struct iwl_trans *)data;
struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data;
if (IWL_FW_CHECK(trans,
iwl_rx_packet_payload_len(pkt) < sizeof(*pnvm_ntf),
"Bad notif len: %d\n",
iwl_rx_packet_payload_len(pkt)))
return true;
IWL_DEBUG_FW(trans,
"PNVM complete notification received with status 0x%0x\n",
le32_to_cpu(pnvm_ntf->status));

View File

@ -389,7 +389,7 @@ bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc)
if (*size >= IWL_WTAS_BLACK_LIST_MAX)
return false;
list[*size++] = mcc;
list[(*size)++] = mcc;
return true;
}
IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2021, 2025 Intel Corporation
* Copyright (C) 2012-2014, 2018-2021, 2025-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@ -14,9 +14,17 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt,
{
struct iwl_shared_mem_cfg *mem_cfg = (void *)pkt->data;
int i, lmac;
int lmac_num = le32_to_cpu(mem_cfg->lmac_num);
u8 api_ver = iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP,
SHARED_MEM_CFG_CMD, 0);
int lmac_num;
u8 api_ver;
if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) <
offsetofend(struct iwl_shared_mem_cfg, lmac_smem[1]),
"bad shared mem notification size\n"))
return;
lmac_num = le32_to_cpu(mem_cfg->lmac_num);
api_ver = iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP,
SHARED_MEM_CFG_CMD, 0);
/* Note: notification has 3 entries, but we only expect 2 */
if (IWL_FW_CHECK(fwrt, lmac_num > ARRAY_SIZE(fwrt->smem_cfg.lmac),
@ -30,7 +38,7 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt,
if (api_ver >= 4 &&
!IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < sizeof(*mem_cfg),
"bad shared mem notification size\n")) {
"bad shared mem notification size (v4)\n")) {
fwrt->smem_cfg.rxfifo2_control_size =
le32_to_cpu(mem_cfg->rxfifo2_control_size);
}
@ -53,6 +61,11 @@ static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt,
struct iwl_shared_mem_cfg_v2 *mem_cfg = (void *)pkt->data;
int i;
if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) <
offsetof(struct iwl_shared_mem_cfg_v2, rxfifo_addr),
"bad shared mem notification size\n"))
return;
fwrt->smem_cfg.num_lmacs = 1;
fwrt->smem_cfg.num_txfifo_entries = ARRAY_SIZE(mem_cfg->txfifo_size);
@ -67,6 +80,11 @@ static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt,
/* new API has more data, from rxfifo_addr field and on */
if (fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) {
if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) <
sizeof(*mem_cfg),
"bad shared mem notification size (extend)\n"))
return;
BUILD_BUG_ON(sizeof(fwrt->smem_cfg.internal_txfifo_size) !=
sizeof(mem_cfg->internal_txfifo_size));

View File

@ -700,7 +700,7 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
return -EINVAL;
parse_table:
if (!(BIT(data->revision) & valid_rev)) {
if (data->revision >= 32 || !(BIT(data->revision) & valid_rev)) {
ret = -EINVAL;
IWL_DEBUG_RADIO(fwrt,
"Unsupported UEFI PPAG revision:%d\n",

View File

@ -415,6 +415,7 @@ struct iwl_mac_cfg {
* @vht_mu_mimo_supported: VHT MU-MIMO support
* @nvm_type: see &enum iwl_nvm_type
* @uhb_supported: ultra high band channels supported
* @unii9_supported: UNII-9 channels supported
* @eht_supported: EHT supported
* @uhr_supported: UHR supported
* @num_rbds: number of receive buffer descriptors to use
@ -450,6 +451,7 @@ struct iwl_rf_cfg {
lp_xtal_workaround:1,
vht_mu_mimo_supported:1,
uhb_supported:1,
unii9_supported:1,
eht_supported:1,
uhr_supported:1;
u8 valid_tx_ant;

View File

@ -475,6 +475,7 @@ static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data,
{
const struct iwl_ucode_tlv *tlv;
u32 tlv_len;
size_t aligned_tlv_len;
while (len >= sizeof(*tlv)) {
len -= sizeof(*tlv);
@ -487,8 +488,16 @@ static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data,
len, tlv_len);
return -EINVAL;
}
len -= ALIGN(tlv_len, 4);
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
aligned_tlv_len = ALIGN(tlv_len, 4);
if (len < aligned_tlv_len) {
IWL_ERR(trans, "invalid aligned TLV len: %zd/%zu\n",
len, aligned_tlv_len);
return -EINVAL;
}
len -= aligned_tlv_len;
data += sizeof(*tlv) + aligned_tlv_len;
iwl_dbg_tlv_alloc(trans, tlv, true);
}

View File

@ -492,7 +492,7 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces,
* Gets uCode section from tlv.
*/
static int iwl_store_ucode_sec(struct fw_img_parsing *img,
const void *data, int size)
const void *data, size_t size)
{
struct fw_sec *sec;
const struct fw_sec_parsing *sec_parse;
@ -501,6 +501,9 @@ static int iwl_store_ucode_sec(struct fw_img_parsing *img,
if (WARN_ON(!img || !data))
return -EINVAL;
if (size < sizeof(sec_parse->offset))
return -EINVAL;
sec_parse = (const struct fw_sec_parsing *)data;
alloc_size = sizeof(*img->sec) * (img->sec_counter + 1);
@ -804,6 +807,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
u32 build, paging_mem_size;
int num_of_cpus;
bool usniffer_req = false;
size_t aligned_tlv_len;
if (len < sizeof(*ucode)) {
IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@ -852,8 +856,16 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
len, tlv_len);
return -EINVAL;
}
len -= ALIGN(tlv_len, 4);
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
aligned_tlv_len = ALIGN(tlv_len, 4);
if (len < aligned_tlv_len) {
IWL_ERR(drv, "invalid aligned TLV len: %zd/%zu\n",
len, aligned_tlv_len);
return -EINVAL;
}
len -= aligned_tlv_len;
data += sizeof(*tlv) + aligned_tlv_len;
switch (tlv_type) {
case IWL_UCODE_TLV_INST:

View File

@ -85,16 +85,7 @@ static const u16 iwl_nvm_channels[] = {
149, 153, 157, 161, 165
};
static const u16 iwl_ext_nvm_channels[] = {
/* 2.4 GHz */
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/* 5 GHz */
36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
149, 153, 157, 161, 165, 169, 173, 177, 181
};
static const u16 iwl_uhb_nvm_channels[] = {
static const u16 iwl_unii9_nvm_channels[] = {
/* 2.4 GHz */
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/* 5 GHz */
@ -105,12 +96,16 @@ static const u16 iwl_uhb_nvm_channels[] = {
1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69,
73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129,
133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185,
189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233
189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233,
/* UNII-9 */
237, 241, 245, 249, 253
};
#define IWL_NVM_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
#define IWL_NVM_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels)
#define IWL_NVM_NUM_CHANNELS_UHB ARRAY_SIZE(iwl_uhb_nvm_channels)
#define IWL_NVM_NUM_CHANNELS_EXT 51
#define IWL_NVM_NUM_CHANNELS_UHB 110
#define IWL_NVM_NUM_CHANNELS_UNII9 ARRAY_SIZE(iwl_unii9_nvm_channels)
#define NUM_2GHZ_CHANNELS 14
#define NUM_5GHZ_CHANNELS 37
#define FIRST_2GHZ_HT_MINUS 5
@ -351,12 +346,15 @@ static int iwl_init_channel_map(struct iwl_trans *trans,
int num_of_ch;
const u16 *nvm_chan;
if (cfg->uhb_supported) {
if (cfg->unii9_supported) {
num_of_ch = IWL_NVM_NUM_CHANNELS_UNII9;
nvm_chan = iwl_unii9_nvm_channels;
} else if (cfg->uhb_supported) {
num_of_ch = IWL_NVM_NUM_CHANNELS_UHB;
nvm_chan = iwl_uhb_nvm_channels;
nvm_chan = iwl_unii9_nvm_channels;
} else if (cfg->nvm_type == IWL_NVM_EXT) {
num_of_ch = IWL_NVM_NUM_CHANNELS_EXT;
nvm_chan = iwl_ext_nvm_channels;
nvm_chan = iwl_unii9_nvm_channels;
} else {
num_of_ch = IWL_NVM_NUM_CHANNELS;
nvm_chan = iwl_nvm_channels;
@ -1441,7 +1439,9 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
u8 rx_chains = fw->valid_rx_ant;
u8 tx_chains = fw->valid_rx_ant;
if (cfg->uhb_supported)
if (cfg->unii9_supported)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9);
else if (cfg->uhb_supported)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB);
else
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_EXT);
@ -1506,7 +1506,9 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
u16 lar_config;
const __le16 *ch_section;
if (cfg->uhb_supported)
if (cfg->unii9_supported)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9);
else if (cfg->uhb_supported)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB);
else if (cfg->nvm_type != IWL_NVM_EXT)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS);
@ -1727,12 +1729,15 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
int max_num_ch;
struct iwl_reg_capa reg_capa;
if (cfg->uhb_supported) {
if (cfg->unii9_supported) {
max_num_ch = IWL_NVM_NUM_CHANNELS_UNII9;
nvm_chan = iwl_unii9_nvm_channels;
} else if (cfg->uhb_supported) {
max_num_ch = IWL_NVM_NUM_CHANNELS_UHB;
nvm_chan = iwl_uhb_nvm_channels;
nvm_chan = iwl_unii9_nvm_channels;
} else if (cfg->nvm_type == IWL_NVM_EXT) {
max_num_ch = IWL_NVM_NUM_CHANNELS_EXT;
nvm_chan = iwl_ext_nvm_channels;
nvm_chan = iwl_unii9_nvm_channels;
} else {
max_num_ch = IWL_NVM_NUM_CHANNELS;
nvm_chan = iwl_nvm_channels;
@ -2087,13 +2092,26 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
struct iwl_nvm_get_info_rsp_v3 *rsp_v3;
bool v4 = fw_has_api(&fw->ucode_capa,
IWL_UCODE_TLV_API_REGULATORY_NVM_INFO);
size_t rsp_size = v4 ? sizeof(*rsp) : sizeof(*rsp_v3);
size_t rsp_size;
void *channel_profile;
ret = iwl_trans_send_cmd(trans, &hcmd);
if (ret)
return ERR_PTR(ret);
switch (iwl_fw_lookup_notif_ver(fw, REGULATORY_AND_NVM_GROUP,
NVM_GET_INFO, 0)) {
case 5:
rsp_size = sizeof(struct iwl_nvm_get_info_rsp);
break;
case 4:
rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v4);
break;
default:
rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v3);
break;
}
if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != rsp_size,
"Invalid payload len in NVM response from FW %d",
iwl_rx_packet_payload_len(hcmd.resp_pkt))) {
@ -2107,7 +2125,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
if (empty_otp)
IWL_INFO(trans, "OTP is empty\n");
nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V2);
nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V3);
if (!nvm) {
ret = -ENOMEM;
goto out;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2024-2026 Intel Corporation
*/
#include "agg.h"
#include "sta.h"
@ -222,6 +222,11 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
if (baid == IWL_RX_REORDER_DATA_INVALID_BAID)
return IWL_MLD_PASS_SKB;
if (IWL_FW_CHECK(mld, baid >= ARRAY_SIZE(mld->fw_id_to_ba),
"Got out-of-range BAID %u in reorder_data=0x%x\n",
baid, reorder))
return IWL_MLD_PASS_SKB;
/* no sta yet */
if (WARN_ONCE(!sta,
"Got valid BAID without a valid station assigned\n"))

View File

@ -1708,7 +1708,7 @@ static void iwl_mld_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,
elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data,
ies->len);
if (!elem || elem->datalen < 10 ||
if (!elem || elem->datalen < 11 ||
!(elem->data[10] &
WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) {
*tolerated = false;

View File

@ -18,9 +18,15 @@ static struct iwl_mcc_update_resp_v8 *
iwl_mld_copy_mcc_resp(const struct iwl_rx_packet *pkt)
{
const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data;
int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels);
struct iwl_mcc_update_resp_v8 *resp_cp;
int notif_len = struct_size(resp_cp, channels, n_channels);
int n_channels;
int notif_len;
if (iwl_rx_packet_payload_len(pkt) < sizeof(*mcc_resp_v8))
return ERR_PTR(-EINVAL);
n_channels = __le32_to_cpu(mcc_resp_v8->n_channels);
notif_len = struct_size(resp_cp, channels, n_channels);
if (iwl_rx_packet_payload_len(pkt) != notif_len)
return ERR_PTR(-EINVAL);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2024-2026 Intel Corporation
*/
#ifdef CONFIG_THERMAL
#include <linux/sort.h>
@ -272,6 +272,7 @@ static void iwl_mld_thermal_zone_register(struct iwl_mld *mld)
if (ret) {
IWL_DEBUG_TEMP(mld, "Failed to enable thermal zone\n");
thermal_zone_device_unregister(mld->tzone);
mld->tzone = NULL;
}
}
@ -385,10 +386,8 @@ static void iwl_mld_thermal_zone_unregister(struct iwl_mld *mld)
return;
IWL_DEBUG_TEMP(mld, "Thermal zone device unregister\n");
if (mld->tzone) {
thermal_zone_device_unregister(mld->tzone);
mld->tzone = NULL;
}
thermal_zone_device_unregister(mld->tzone);
mld->tzone = NULL;
}
static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld)

View File

@ -1120,6 +1120,10 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld,
ssn = le32_to_cpup((__le32 *)agg_status +
tx_resp->frame_count) & 0xFFFF;
if (IWL_FW_CHECK(mld, txq_id >= ARRAY_SIZE(mld->fw_id_to_txq),
"Invalid txq id %d\n", txq_id))
return;
__skb_queue_head_init(&skbs);
/* we can free until ssn % q.n_bd not inclusive */

View File

@ -1509,6 +1509,10 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
if (WARN_ON_ONCE(truncated < 0))
truncated = 0;
/* this would be a firmware bug */
if (WARN_ON_ONCE(pktsize < sizeof(*hdr)))
return;
if (ieee80211_is_data(hdr->frame_control)) {
int hdrlen = ieee80211_hdrlen(hdr->frame_control);
int ivlen = 0, icvlen = 4; /* also FCS */
@ -1517,10 +1521,6 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
if (!pkt)
goto report;
skb_put_data(pkt, pktdata, hdrlen);
pktdata += hdrlen;
pktsize -= hdrlen;
if (ieee80211_has_protected(hdr->frame_control)) {
/*
* This is unlocked and using gtk_i(c)vlen,
@ -1539,13 +1539,24 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
/* if truncated, FCS/ICV is (partially) gone */
if (truncated >= icvlen) {
icvlen = 0;
truncated -= icvlen;
icvlen = 0;
} else {
icvlen -= truncated;
truncated = 0;
}
if (IWL_FW_CHECK(mvm,
pktsize <= hdrlen + ivlen + icvlen,
"pktsize is too small %d\n",
pktsize)) {
kfree_skb(pkt);
return;
}
skb_put_data(pkt, pktdata, hdrlen);
pktdata += hdrlen;
pktsize -= hdrlen;
pktsize -= ivlen + icvlen;
pktdata += ivlen;
@ -2123,16 +2134,16 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
struct iwl_wowlan_status_data *status,
u32 len)
{
if (IWL_FW_CHECK(mvm, data->num_mlo_link_keys,
"MLO is not supported, shouldn't receive MLO keys\n"))
return;
if (len < sizeof(*data)) {
IWL_ERR(mvm, "Invalid WoWLAN info notification!\n");
status = NULL;
return;
}
if (IWL_FW_CHECK(mvm, data->num_mlo_link_keys,
"MLO is not supported, shouldn't receive MLO keys\n"))
return;
if (mvm->fast_resume)
return;
@ -2448,13 +2459,15 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
if (fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) {
query_len = sizeof(struct iwl_scan_offload_match_info);
matches_len = sizeof(struct iwl_scan_offload_profile_match) *
max_profiles;
query_len = offsetof(struct iwl_scan_offload_match_info,
matches) + matches_len;
} else {
query_len = sizeof(struct iwl_scan_offload_profiles_query_v1);
matches_len = sizeof(struct iwl_scan_offload_profile_match_v1) *
max_profiles;
query_len = sizeof(struct iwl_scan_offload_profiles_query_v1) +
matches_len;
}
len = iwl_rx_packet_payload_len(cmd.resp_pkt);
@ -2756,7 +2769,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm,
struct iwl_wowlan_status_data *status,
u32 len)
{
u32 data_size, packet_len = le32_to_cpu(notif->wake_packet_length);
u32 data_size, packet_len;
if (len < sizeof(*notif)) {
IWL_ERR(mvm, "Invalid WoWLAN wake packet notification!\n");
@ -2775,6 +2788,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm,
return -EIO;
}
packet_len = le32_to_cpu(notif->wake_packet_length);
data_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, wake_packet);
/* data_size got the padding from the notification, remove it. */
@ -2807,7 +2821,8 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm,
if (IS_ERR_OR_NULL(vif))
return;
if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) {
if (len < offsetof(struct iwl_scan_offload_match_info, matches) +
matches_len) {
IWL_ERR(mvm, "Invalid scan match info notification\n");
return;
}
@ -2942,6 +2957,11 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait,
case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): {
struct iwl_d3_end_notif *notif = (void *)pkt->data;
if (len < sizeof(*notif)) {
IWL_ERR(mvm, "Invalid D3 end notification size\n");
break;
}
d3_data->d3_end_flags = __le32_to_cpu(notif->flags);
d3_data->notif_received |= IWL_D3_NOTIF_D3_END_NOTIF;

View File

@ -646,7 +646,8 @@ static void iter(struct ieee80211_hw *hw,
WARN_ON(*target->cipher == IWL_LOCATION_CIPHER_INVALID);
}
static void
/* The tk buffer is assumed to be exactly TK_11AZ_LEN bytes */
static int
iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
u8 *bssid, u8 *cipher, u8 *hltk, u8 *tk,
u8 *rx_pn, u8 *tx_pn, __le32 *flags)
@ -656,12 +657,12 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (mvmvif->ftm_unprotected)
return;
return 0;
#endif
if (!(le32_to_cpu(*flags) & (IWL_INITIATOR_AP_FLAGS_NON_TB |
IWL_INITIATOR_AP_FLAGS_TB)))
return;
return 0;
lockdep_assert_held(&mvm->mutex);
@ -679,14 +680,31 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
if (vif->cfg.assoc &&
!memcmp(vif->bss_conf.bssid, bssid, ETH_ALEN)) {
struct iwl_mvm_ftm_iter_data target;
u32 key_len;
target.bssid = bssid;
target.cipher = cipher;
target.tk = NULL;
ieee80211_iter_keys(mvm->hw, vif, iter, &target);
if (!WARN_ON(!target.tk))
memcpy(tk, target.tk, TK_11AZ_LEN);
if (WARN_ON(!target.tk))
return -EINVAL;
switch (*target.cipher) {
case IWL_LOCATION_CIPHER_CCMP_128:
case IWL_LOCATION_CIPHER_GCMP_128:
key_len = WLAN_KEY_LEN_CCMP;
break;
case IWL_LOCATION_CIPHER_GCMP_256:
key_len = WLAN_KEY_LEN_GCMP_256;
break;
default:
WARN_ON(1);
return -EINVAL;
}
memset(tk, 0, TK_11AZ_LEN);
memcpy(tk, target.tk, key_len);
} else {
memcpy(tk, entry->tk, sizeof(entry->tk));
}
@ -695,8 +713,10 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
memcpy(tx_pn, entry->tx_pn, sizeof(entry->tx_pn));
FTM_SET_FLAG(SECURED);
return;
return 0;
}
return 0;
}
static int
@ -708,12 +728,11 @@ iwl_mvm_ftm_put_target_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
if (err)
return err;
iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,
&target->cipher, target->hltk,
target->tk, target->rx_pn,
target->tx_pn,
&target->initiator_ap_flags);
return err;
return iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,
&target->cipher, target->hltk,
target->tk, target->rx_pn,
target->tx_pn,
&target->initiator_ap_flags);
}
static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm,
@ -881,11 +900,13 @@ iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags);
iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id,
&target->initiator_ap_flags);
iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,
&target->cipher, target->hltk,
target->tk, target->rx_pn,
target->tx_pn,
&target->initiator_ap_flags);
ret = iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,
&target->cipher, target->hltk,
target->tk, target->rx_pn,
target->tx_pn,
&target->initiator_ap_flags);
if (ret)
return ret;
i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 :
IWL_MVM_FTM_I2R_MAX_STS;

View File

@ -964,12 +964,22 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
return ret;
}
if (IWL_FW_CHECK(mvm,
iwl_rx_packet_payload_len(cmd.resp_pkt) !=
sizeof(*resp),
"Wrong size for iwl_geo_tx_power_profiles_resp: %d\n",
iwl_rx_packet_payload_len(cmd.resp_pkt))) {
ret = -EIO;
goto out;
}
resp = (void *)cmd.resp_pkt->data;
ret = le32_to_cpu(resp->profile_idx);
if (WARN_ON(ret > BIOS_GEO_MAX_PROFILE_NUM))
ret = -EIO;
out:
iwl_free_resp(&cmd);
return ret;
}

View File

@ -1493,49 +1493,54 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
struct iwl_extended_beacon_notif_v5 *beacon_v5 = (void *)pkt->data;
struct ieee80211_vif *csa_vif;
struct ieee80211_vif *tx_blocked_vif;
struct agg_tx_status *agg_status;
u32 beacon_gp2;
u16 status;
lockdep_assert_held(&mvm->mutex);
mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
if (!iwl_mvm_is_short_beacon_notif_supported(mvm)) {
struct iwl_extended_beacon_notif_v5 *beacon = (void *)pkt->data;
struct iwl_tx_resp *beacon_notify_hdr =
&beacon_v5->beacon_notify_hdr;
&beacon->beacon_notify_hdr;
if (unlikely(pkt_len < sizeof(*beacon_v5)))
if (unlikely(pkt_len < sizeof(*beacon)))
return;
mvm->ibss_manager = beacon_v5->ibss_mgr_status != 0;
beacon_gp2 = le32_to_cpu(beacon->gp2);
mvm->ibss_manager = beacon->ibss_mgr_status != 0;
agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr);
status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK;
IWL_DEBUG_RX(mvm,
"beacon status %#x retries:%d tsf:0x%016llX gp2:0x%X rate:%d\n",
status, beacon_notify_hdr->failure_frame,
le64_to_cpu(beacon->tsf),
mvm->ap_last_beacon_gp2,
beacon_gp2,
le32_to_cpu(beacon_notify_hdr->initial_rate));
} else {
const struct iwl_extended_beacon_notif *beacon =
(void *)pkt->data;
if (unlikely(pkt_len < sizeof(*beacon)))
return;
beacon_gp2 = le32_to_cpu(beacon->gp2);
mvm->ibss_manager = beacon->ibss_mgr_status != 0;
status = le32_to_cpu(beacon->status) & TX_STATUS_MSK;
IWL_DEBUG_RX(mvm,
"beacon status %#x tsf:0x%016llX gp2:0x%X\n",
status, le64_to_cpu(beacon->tsf),
mvm->ap_last_beacon_gp2);
beacon_gp2);
}
csa_vif = rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (unlikely(csa_vif && csa_vif->bss_conf.csa_active))
iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2,
iwl_mvm_csa_count_down(mvm, csa_vif, beacon_gp2,
(status == TX_STATUS_SUCCESS));
tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,

View File

@ -625,9 +625,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->max_sched_scan_reqs = 1;
hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
hw->wiphy->max_match_sets = iwl_umac_scan_get_max_profiles(mvm->fw);
/* we create the 802.11 header and zero length SSID IE. */
hw->wiphy->max_sched_scan_ie_len =
SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
hw->wiphy->max_sched_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS;
hw->wiphy->max_sched_scan_plan_interval = U16_MAX;
@ -3061,7 +3059,6 @@ void iwl_mvm_stop_ap_ibss_common(struct iwl_mvm *mvm,
}
mvmvif->ap_ibss_active = false;
mvm->ap_last_beacon_gp2 = 0;
if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
iwl_mvm_vif_set_low_latency(mvmvif, false,
@ -3490,7 +3487,7 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,
elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data,
ies->len);
if (!elem || elem->datalen < 10 ||
if (!elem || elem->datalen < 11 ||
!(elem->data[10] &
WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) {
data->tolerated = false;

View File

@ -1168,9 +1168,6 @@ struct iwl_mvm {
struct ieee80211_vif __rcu *csa_tx_blocked_vif;
u8 csa_tx_block_bcn_timeout;
/* system time of last beacon (for AP/GO interface) */
u32 ap_last_beacon_gp2;
/* indicates that we transmitted the last beacon */
bool ibss_manager;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
* Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@ -416,6 +416,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
int ret, resp_ver;
u32 status;
int resp_len, n_channels;
unsigned int pkt_len;
u16 mcc;
if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
@ -431,6 +432,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
return ERR_PTR(ret);
pkt = cmd.resp_pkt;
pkt_len = iwl_rx_packet_payload_len(pkt);
resp_ver = iwl_fw_lookup_notif_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP,
MCC_UPDATE_CMD, 0);
@ -439,9 +441,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
if (resp_ver >= 8) {
struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (void *)pkt->data;
if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v8),
"MCC v8 response too short: %u\n", pkt_len)) {
resp_cp = ERR_PTR(-EINVAL);
goto exit;
}
n_channels = __le32_to_cpu(mcc_resp_v8->n_channels);
if (iwl_rx_packet_payload_len(pkt) !=
struct_size(mcc_resp_v8, channels, n_channels)) {
if (IWL_FW_CHECK(mvm,
pkt_len !=
struct_size(mcc_resp_v8, channels, n_channels),
"invalid MCC v8 response size: %u (n_channels=%d)\n",
pkt_len, n_channels)) {
resp_cp = ERR_PTR(-EINVAL);
goto exit;
}
@ -464,9 +475,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT)) {
struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (void *)pkt->data;
if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v4),
"MCC v4 response too short: %u\n", pkt_len)) {
resp_cp = ERR_PTR(-EINVAL);
goto exit;
}
n_channels = __le32_to_cpu(mcc_resp_v4->n_channels);
if (iwl_rx_packet_payload_len(pkt) !=
struct_size(mcc_resp_v4, channels, n_channels)) {
if (IWL_FW_CHECK(mvm,
pkt_len !=
struct_size(mcc_resp_v4, channels, n_channels),
"invalid MCC v4 response size: %u (n_channels=%d)\n",
pkt_len, n_channels)) {
resp_cp = ERR_PTR(-EINVAL);
goto exit;
}
@ -489,9 +509,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
} else {
struct iwl_mcc_update_resp_v3 *mcc_resp_v3 = (void *)pkt->data;
if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v3),
"MCC v3 response too short: %u\n", pkt_len)) {
resp_cp = ERR_PTR(-EINVAL);
goto exit;
}
n_channels = __le32_to_cpu(mcc_resp_v3->n_channels);
if (iwl_rx_packet_payload_len(pkt) !=
struct_size(mcc_resp_v3, channels, n_channels)) {
if (IWL_FW_CHECK(mvm,
pkt_len !=
struct_size(mcc_resp_v3, channels, n_channels),
"invalid MCC v3 response size: %u (n_channels=%d)\n",
pkt_len, n_channels)) {
resp_cp = ERR_PTR(-EINVAL);
goto exit;
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2021 - 2023, 2025 Intel Corporation
* Copyright (C) 2021 - 2023, 2025-2026 Intel Corporation
*/
#include "mvm.h"
@ -121,6 +121,8 @@ iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm *mvm, u32 *gp2, u64 *sys_time)
*sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 |
le32_to_cpu(resp->platform_timestamp_lo);
iwl_free_resp(&cmd);
return ret;
}

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
* Copyright (C) 2018-2025 Intel Corporation
* Copyright (C) 2018-2026 Intel Corporation
*/
#include "rs.h"
#include "fw-api.h"
@ -423,9 +423,14 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,
struct iwl_lq_sta_rs_fw *lq_sta;
u32 flags;
notif = (void *)pkt->data;
if (IWL_FW_CHECK(mvm, notif->sta_id >= mvm->fw->ucode_capa.num_stations,
"Invalid sta id (%d) in TLC notification\n",
notif->sta_id))
return;
rcu_read_lock();
notif = (void *)pkt->data;
link_sta = rcu_dereference(mvm->fw_id_to_link_sta[notif->sta_id]);
sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]);
if (IS_ERR_OR_NULL(sta) || !link_sta) {

View File

@ -1227,6 +1227,11 @@ void iwl_mvm_window_status_notif(struct iwl_mvm *mvm,
/* get the station */
sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK)
>> BA_WINDOW_STATUS_STA_ID_POS;
if (IWL_FW_CHECK(mvm,
sta_id >= mvm->fw->ucode_capa.num_stations,
"Invalid sta id (%d) in BA window status notification\n",
sta_id))
continue;
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
if (IS_ERR_OR_NULL(sta))
continue;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@ -3144,8 +3144,6 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
if (ret)
return ret;
iwl_mvm_build_scan_probe(mvm, vif, ies, &params);
/* for 6 GHZ band only PSC channels need to be added */
for (i = 0; i < params.n_channels; i++) {
struct ieee80211_channel *channel = params.channels[i];
@ -3179,6 +3177,8 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
goto out;
}
iwl_mvm_build_scan_probe(mvm, vif, ies, &params);
uid = iwl_mvm_build_scan_cmd(mvm, vif, &hcmd, &params, type);
if (uid < 0) {
ret = uid;
@ -3217,6 +3217,10 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
mvm->mei_scan_filter.is_mei_limited_scan = false;
if (IWL_FW_CHECK(mvm, uid >= ARRAY_SIZE(mvm->scan_uid_status),
"FW reports out-of-range scan UID %d\n", uid))
return;
IWL_DEBUG_SCAN(mvm,
"Scan completed: uid=%u type=%u, status=%s, EBS=%s\n",
uid, mvm->scan_uid_status[uid],

View File

@ -951,6 +951,11 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,
struct ieee80211_vif *vif;
struct iwl_mvm_vif *mvmvif;
if (IWL_FW_CHECK(mvm, id >= ARRAY_SIZE(mvm->vif_id_to_mac),
"Invalid mac_link_id (%d) in session protect notif\n",
id))
return;
rcu_read_lock();
/* note we use link ID == MAC ID */

View File

@ -1544,6 +1544,17 @@ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm,
return val & 0xFFF;
}
static inline size_t iwl_mvm_tx_resp_min_len(struct iwl_mvm *mvm,
struct iwl_tx_resp *tx_resp)
{
struct agg_tx_status *agg_status =
iwl_mvm_get_agg_status(mvm, tx_resp);
/* The aggregate response ends with a trailing SCD SSN __le32 word. */
return (u8 *)(agg_status + tx_resp->frame_count) - (u8 *)tx_resp +
sizeof(__le32);
}
static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
@ -1847,6 +1858,9 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
int queue = SEQ_TO_QUEUE(sequence);
struct ieee80211_sta *sta;
if (WARN_ON_ONCE(iwl_mvm_has_new_tx_api(mvm)))
return;
if (WARN_ON_ONCE(queue < IWL_MVM_DQA_MIN_DATA_QUEUE &&
(queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE)))
return;
@ -1881,6 +1895,26 @@ void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_tx_resp *tx_resp = (void *)pkt->data;
size_t min_len;
if (IWL_FW_CHECK(mvm, !tx_resp->frame_count,
"invalid TX_CMD frame_count %u\n",
tx_resp->frame_count))
return;
if (IWL_FW_CHECK(mvm,
iwl_mvm_has_new_tx_api(mvm) &&
tx_resp->frame_count != 1,
"invalid TX_CMD frame_count %u for new TX API\n",
tx_resp->frame_count))
return;
min_len = iwl_mvm_tx_resp_min_len(mvm, tx_resp);
if (IWL_FW_CHECK(mvm, iwl_rx_packet_payload_len(pkt) < min_len,
"invalid TX_CMD len %u (frame_count %u, min %zu)\n",
iwl_rx_packet_payload_len(pkt), tx_resp->frame_count,
min_len))
return;
if (tx_resp->frame_count == 1)
iwl_mvm_rx_tx_cmd_single(mvm, pkt);
@ -2100,8 +2134,14 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
if (tid == IWL_MGMT_TID)
tid = IWL_MAX_TID_COUNT;
if (IWL_FW_CHECK(mvm, tid >=
ARRAY_SIZE(mvmsta->tid_data),
"invalid TID %d in compressed BA\n",
tid))
continue;
if (mvmsta)
mvmsta->tid_data[i].lq_color = lq_color;
mvmsta->tid_data[tid].lq_color = lq_color;
iwl_mvm_tx_reclaim(mvm, sta_id, tid,
(int)(le16_to_cpu(ba_tfd->q_num)),
@ -2121,6 +2161,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
ba_notif = (void *)pkt->data;
sta_id = ba_notif->sta_id;
tid = ba_notif->tid;
if (IWL_FW_CHECK(mvm, tid >= ARRAY_SIZE(mvmsta->tid_data),
"invalid TID %d in BA notif\n", tid))
return;
/* "flow" corresponds to Tx queue */
txq = le16_to_cpu(ba_notif->scd_flow);
/* "ssn" is start of block-ack Tx window, corresponds to index

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
* Copyright (C) 2018-2025 Intel Corporation
* Copyright (C) 2018-2026 Intel Corporation
*/
#include "iwl-trans.h"
#include "iwl-fh.h"
@ -99,6 +99,11 @@ int iwl_pcie_init_fw_sec(struct iwl_trans *trans,
/* add 2 due to separators */
paging_cnt = iwl_pcie_get_num_sections(fw, lmac_cnt + umac_cnt + 2);
if (WARN_ON(lmac_cnt > ARRAY_SIZE(ctxt_dram->lmac_img) ||
umac_cnt > ARRAY_SIZE(ctxt_dram->umac_img) ||
paging_cnt > ARRAY_SIZE(ctxt_dram->virtual_img)))
return -EINVAL;
dram->fw = kzalloc_objs(*dram->fw, umac_cnt + lmac_cnt);
if (!dram->fw)
return -ENOMEM;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2003-2014, 2018-2024 Intel Corporation
* Copyright (C) 2003-2014, 2018-2024, 2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@ -1238,11 +1238,16 @@ void iwl_pcie_rx_free(struct iwl_trans *trans)
}
}
kfree(trans_pcie->rx_pool);
trans_pcie->rx_pool = NULL;
kfree(trans_pcie->global_table);
trans_pcie->global_table = NULL;
kfree(trans_pcie->rxq);
trans_pcie->rxq = NULL;
if (trans_pcie->alloc_page)
if (trans_pcie->alloc_page) {
__free_pages(trans_pcie->alloc_page, trans_pcie->rx_page_order);
trans_pcie->alloc_page = NULL;
}
}
static void iwl_pcie_rx_move_to_allocator(struct iwl_rxq *rxq,

View File

@ -823,7 +823,7 @@ struct chan_band_param_set {
struct mwifiex_ie_types_chan_band_list_param_set {
struct mwifiex_ie_types_header header;
struct chan_band_param_set chan_band_param[1];
struct chan_band_param_set chan_band_param[];
} __packed;
struct mwifiex_ie_types_rates_param_set {
@ -886,7 +886,7 @@ struct mwifiex_ie_types_wildcard_ssid_params {
#define TSF_DATA_SIZE 8
struct mwifiex_ie_types_tsf_timestamp {
struct mwifiex_ie_types_header header;
u8 tsf_data[1];
u8 tsf_data[];
} __packed;
struct mwifiex_cf_param_set {
@ -903,8 +903,8 @@ struct mwifiex_ibss_param_set {
struct mwifiex_ie_types_ss_param_set {
struct mwifiex_ie_types_header header;
union {
struct mwifiex_cf_param_set cf_param_set[1];
struct mwifiex_ibss_param_set ibss_param_set[1];
DECLARE_FLEX_ARRAY(struct mwifiex_cf_param_set, cf_param_set);
DECLARE_FLEX_ARRAY(struct mwifiex_ibss_param_set, ibss_param_set);
} cf_ibss;
} __packed;
@ -922,8 +922,8 @@ struct mwifiex_ds_param_set {
struct mwifiex_ie_types_phy_param_set {
struct mwifiex_ie_types_header header;
union {
struct mwifiex_fh_param_set fh_param_set[1];
struct mwifiex_ds_param_set ds_param_set[1];
DECLARE_FLEX_ARRAY(struct mwifiex_fh_param_set, fh_param_set);
DECLARE_FLEX_ARRAY(struct mwifiex_ds_param_set, ds_param_set);
} fh_ds;
} __packed;
@ -1383,7 +1383,7 @@ struct host_cmd_ds_802_11_snmp_mib {
__le16 query_type;
__le16 oid;
__le16 buf_size;
u8 value[1];
u8 value[];
} __packed;
struct mwifiex_rate_scope {
@ -1551,7 +1551,7 @@ struct mwifiex_scan_cmd_config {
* TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set
* WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set
*/
u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored
u8 tlv_buf[]; /* SSID TLV(s) and ChanList TLVs are stored
here */
} __packed;
@ -1683,7 +1683,7 @@ struct host_cmd_ds_802_11_bg_scan_query_rsp {
struct mwifiex_ietypes_domain_param_set {
struct mwifiex_ie_types_header header;
u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
struct ieee80211_country_ie_triplet triplet[1];
struct ieee80211_country_ie_triplet triplet[];
} __packed;
struct host_cmd_ds_802_11d_domain_info {

View File

@ -421,15 +421,15 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos;
phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS);
phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set));
memcpy(&phy_tlv->fh_ds.ds_param_set,
phy_tlv->header.len = cpu_to_le16(sizeof(*phy_tlv->fh_ds.ds_param_set));
memcpy(phy_tlv->fh_ds.ds_param_set,
&bss_desc->phy_param_set.ds_param_set.current_chan,
sizeof(phy_tlv->fh_ds.ds_param_set));
sizeof(*phy_tlv->fh_ds.ds_param_set));
pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len);
ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos;
ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS);
ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set));
ss_tlv->header.len = cpu_to_le16(sizeof(*ss_tlv->cf_ibss.cf_param_set));
pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len);
/* Get the common rates supported between the driver and the BSS Desc */

View File

@ -108,7 +108,7 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv,
"cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid);
cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB);
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib)
- 1 + S_DS_GEN);
+ S_DS_GEN);
snmp_mib->oid = cpu_to_le16((u16)cmd_oid);
if (cmd_action == HostCmd_ACT_GEN_GET) {

View File

@ -196,6 +196,7 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
struct mwifiex_adapter *adapter = priv->adapter;
char rgpower_table_name[30];
char country_code[3];
int ret;
strscpy(country_code, domain_info->country_code, sizeof(country_code));
@ -214,16 +215,17 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
adapter->rgpower_data = NULL;
}
if ((request_firmware(&adapter->rgpower_data, rgpower_table_name,
adapter->dev))) {
ret = request_firmware_direct(&adapter->rgpower_data, rgpower_table_name,
adapter->dev);
if (ret) {
mwifiex_dbg(
adapter, INFO,
"info: %s: failed to request regulatory power table\n",
__func__);
return -EIO;
"info: %s: failed to request regulatory power table: %d\n",
__func__, ret);
}
return 0;
return ret;
}
static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv)

View File

@ -215,7 +215,7 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac,
/* follow AP's channel bandwidth */
if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
bss_desc->bcn_ht_cap &&
bss_desc->bcn_ht_oper &&
ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))
ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param;

View File

@ -123,11 +123,31 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
len = ETH_ALEN;
if (len != -1) {
u16 evt_len = le16_to_cpu(event->len);
sinfo->assoc_req_ies = &event->data[len];
len = (u8 *)sinfo->assoc_req_ies -
(u8 *)&event->frame_control;
sinfo->assoc_req_ies_len =
le16_to_cpu(event->len) - (u16)len;
/*
* event->len is reported by the device firmware
* and is not otherwise validated. Reject a
* length that underflows the header, or that
* would place the association request IEs
* outside the fixed-size event_body[] buffer the
* event was copied into; otherwise the IE walk
* in mwifiex_set_sta_ht_cap() reads past
* event_body and out of the adapter slab object.
*/
if (evt_len < len ||
(u8 *)&event->frame_control + evt_len >
adapter->event_body + MAX_EVENT_SIZE) {
mwifiex_dbg(adapter, ERROR,
"invalid STA assoc event length\n");
kfree(sinfo);
return -1;
}
sinfo->assoc_req_ies_len = evt_len - (u16)len;
}
}
cfg80211_new_sta(priv->netdev->ieee80211_ptr, event->sta_addr,

View File

@ -38,8 +38,8 @@ config MT792x_USB
select MT76_USB
config MT76_NPU
bool
depends on MT76_CORE
tristate
depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig"

View File

@ -12,7 +12,11 @@ mt76-y := \
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
tx.o agg-rx.o mcu.o wed.o scan.o channel.o
mt76-$(CONFIG_MT76_NPU) += npu.o
ifdef CONFIG_MT76_NPU
# CONFIG_MT76_NPU is tristate to simplify dependency tracking,
# but it behaves as a bool symbol here.
mt76-y += npu.o
endif
mt76-$(CONFIG_PCI) += pci.o
mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o

View File

@ -1189,7 +1189,10 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
mt76_for_each_q_rx(dev, i) {
struct mt76_queue *q = &dev->q_rx[i];
netif_napi_del(&dev->napi[i]);
if (!mt76_queue_is_wed_rro(q)) {
napi_disable(&dev->napi[i]);
netif_napi_del(&dev->napi[i]);
}
mt76_dma_rx_cleanup(dev, q);
page_pool_destroy(q->page_pool);

View File

@ -181,7 +181,7 @@ mt76_eeprom_override(struct mt76_phy *phy)
if (err == -EPROBE_DEFER)
return err;
if (err) {
if (!is_valid_ether_addr(phy->macaddr)) {
eth_random_addr(phy->macaddr);
dev_info(dev->dev,
"Invalid MAC address, using random address %pM\n",

View File

@ -1647,7 +1647,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
#ifdef CONFIG_MT76_NPU
#if IS_ENABLED(CONFIG_MT76_NPU)
void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb,
u32 info);
int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q,
@ -1736,12 +1736,12 @@ static inline int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex,
static inline bool mt76_npu_device_active(struct mt76_dev *dev)
{
return !!rcu_access_pointer(dev->mmio.npu);
return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.npu);
}
static inline bool mt76_ppe_device_active(struct mt76_dev *dev)
{
return !!rcu_access_pointer(dev->mmio.ppe_dev);
return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.ppe_dev);
}
static inline int mt76_npu_send_msg(struct airoha_npu *npu, int ifindex,

View File

@ -1601,6 +1601,8 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len)
switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
if (!mt76_is_mmio(mdev))
return false;
mt7615_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
@ -1634,6 +1636,10 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
dev_kfree_skb(skb);
break;
case PKT_TYPE_TXRX_NOTIFY:
if (!mt76_is_mmio(mdev)) {
dev_kfree_skb(skb);
break;
}
mt7615_mac_tx_free(dev, skb->data, skb->len);
dev_kfree_skb(skb);
break;

View File

@ -1458,6 +1458,8 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct bss_info_uni_he *he;
cap = mt76_connac_get_he_phy_cap(phy, vif);
if (!cap)
return;
he = (struct bss_info_uni_he *)tlv;
he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;

View File

@ -595,6 +595,8 @@ mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct tlv *tlv;
cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
if (!cap)
return;
tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he));
@ -1177,13 +1179,12 @@ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
}
static void
mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
struct mt7915_phy *phy, struct sta_rec_bf *bf)
mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta,
const struct ieee80211_sta_he_cap *vc,
struct sta_rec_bf *bf)
{
struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
const struct ieee80211_sta_he_cap *vc =
mt76_connac_get_he_phy_cap(phy->mt76, vif);
const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
@ -1242,6 +1243,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_phy *phy = mvif->phy;
const struct ieee80211_sta_he_cap *vc = NULL;
int tx_ant = hweight8(phy->mt76->chainmask) - 1;
struct sta_rec_bf *bf;
struct tlv *tlv;
@ -1260,6 +1262,12 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
if (!ebf && !dev->ibf)
return;
if (sta->deflink.he_cap.has_he && ebf) {
vc = mt76_connac_get_he_phy_cap(phy->mt76, vif);
if (!vc)
return;
}
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf));
bf = (struct sta_rec_bf *)tlv;
@ -1268,7 +1276,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
* ht: iBF only, since mac80211 lacks of eBF support
*/
if (sta->deflink.he_cap.has_he && ebf)
mt7915_mcu_sta_bfer_he(sta, vif, phy, bf);
mt7915_mcu_sta_bfer_he(sta, vc, bf);
else if (sta->deflink.vht_cap.vht_supported)
mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf);
else if (sta->deflink.ht_cap.ht_supported)

View File

@ -570,8 +570,9 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len)
switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
mt7921_mac_tx_free(dev, data, len); /* mmio */
if (!mt76_is_mmio(mdev))
return false;
mt7921_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
for (rxd += 2; rxd + 8 <= end; rxd += 8)
@ -600,7 +601,10 @@ void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
if (!mt76_is_mmio(mdev)) {
napi_consume_skb(skb, 1);
break;
}
mt7921_mac_tx_free(dev, skb->data, skb->len);
napi_consume_skb(skb, 1);
break;

View File

@ -1203,8 +1203,9 @@ bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len)
switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
mt7925_mac_tx_free(dev, data, len); /* mmio */
if (!mt76_is_mmio(mdev))
return false;
mt7925_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
for (rxd += 4; rxd + 12 <= end; rxd += 12)
@ -1240,7 +1241,10 @@ void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
if (!mt76_is_mmio(mdev)) {
napi_consume_skb(skb, 1);
break;
}
mt7925_mac_tx_free(dev, skb->data, skb->len);
napi_consume_skb(skb, 1);
break;
@ -1284,6 +1288,9 @@ mt7925_vif_connect_iter(void *priv, u8 *mac,
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
bss_conf = mt792x_vif_to_bss_conf(vif, i);
if (!bss_conf)
continue;
mconf = mt792x_vif_to_link(mvif, i);
mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76,

View File

@ -1713,6 +1713,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
mconf = mt792x_vif_to_link(mvif, i);
mlink = mt792x_sta_to_link(msta, i);
if (!mlink)
continue;
if (enabled)
set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
else

View File

@ -2773,6 +2773,8 @@ mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf,
struct tlv *tlv;
cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif);
if (!cap)
return;
tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));

View File

@ -16,6 +16,6 @@ config MT7996E
config MT7996_NPU
bool "MT7996 (PCIe) NPU support"
depends on MT7996E
depends on NET_AIROHA_NPU=y || MT7996E=NET_AIROHA_NPU
depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
select MT76_NPU
default n

View File

@ -935,6 +935,8 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct tlv *tlv;
cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
if (!cap)
return;
tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
@ -1855,17 +1857,18 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta,
{
struct ieee80211_sta_he_cap *pc = &link_sta->he_cap;
struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
const struct ieee80211_sta_he_cap *vc =
mt76_connac_get_he_phy_cap(phy->mt76, vif);
const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
const struct ieee80211_he_cap_elem *ve;
const struct ieee80211_sta_he_cap *vc;
u8 snd_dim, sts;
vc = mt76_connac_get_he_phy_cap(phy->mt76, vif);
if (!vc)
return;
bf->tx_mode = MT_PHY_TYPE_HE_SU;
ve = &vc->he_cap_elem;
mt7996_mcu_sta_sounding_rate(bf, phy);
@ -1921,14 +1924,18 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta,
struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp;
const struct ieee80211_sta_eht_cap *vc =
mt76_connac_get_eht_phy_cap(phy->mt76, vif);
const struct ieee80211_eht_cap_elem_fixed *ve = &vc->eht_cap_elem;
u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss,
IEEE80211_EHT_MCS_NSS_RX) - 1;
const struct ieee80211_eht_cap_elem_fixed *ve;
const struct ieee80211_sta_eht_cap *vc;
u8 snd_dim, sts;
vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif);
if (!vc)
return;
bf->tx_mode = MT_PHY_TYPE_EHT_MU;
ve = &vc->eht_cap_elem;
mt7996_mcu_sta_sounding_rate(bf, phy);

View File

@ -600,6 +600,11 @@ static s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len,
u16 ies_len;
struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer;
if (buffer_len < sizeof(*res)) {
ret_conn_info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
return -EINVAL;
}
ret_conn_info->status = le16_to_cpu(res->status_code);
if (ret_conn_info->status == WLAN_STATUS_SUCCESS) {
ies = &buffer[sizeof(*res)];

View File

@ -870,9 +870,9 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
[HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
[HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
[HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
[HWSIM_ATTR_TX_INFO] = { .type = NLA_BINARY,
.len = IEEE80211_TX_MAX_RATES *
sizeof(struct hwsim_tx_rate)},
[HWSIM_ATTR_TX_INFO] =
NLA_POLICY_EXACT_LEN(IEEE80211_TX_MAX_RATES *
sizeof(struct hwsim_tx_rate)),
[HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
[HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 },
[HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 },

View File

@ -2611,6 +2611,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
memcpy(&sta->deflink.pub->supp_rates,
&nmi_sta->deflink.pub->supp_rates,
sizeof(sta->deflink.pub->supp_rates));
sta->deflink.pub->agg = nmi_sta->deflink.pub->agg;
__ieee80211_sta_recalc_aggregates(sta, 0);
}
/* set the STA state after all sta info from usermode has been set */

View File

@ -399,6 +399,10 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
memcpy(sdata->link, old_data, sizeof(old_data));
memcpy(sdata->vif.link_conf, old, sizeof(old));
ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links);
for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
ieee80211_link_debugfs_remove(&links[link_id]->data);
ieee80211_link_stop(&links[link_id]->data);
}
/* and free (only) the newly allocated links */
memset(to_free, 0, sizeof(links));
goto free;

View File

@ -1717,6 +1717,8 @@ static void sta_ps_start(struct sta_info *sta)
else
clear_bit(tid, &sta->txq_buffered_tids);
}
sta_info_recalc_tim(sta);
}
static void sta_ps_end(struct sta_info *sta)

View File

@ -263,8 +263,9 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
"FTM: nominal time is required for PD NTB ranging");
return -EINVAL;
}
out->ftm.nominal_time =
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME])
out->ftm.nominal_time =
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS])
out->ftm.min_time_between_measurements =