Quite a number of fixes still:

- mt76 (hadn't sent any fixes so far)
    - RCU
    - scanning
    - decapsulation offload
    - interface combinations
  - rt2x00: build fix (bad function pointer prototype)
  - cfg80211: prevent A-MSDU flipping attacks in mesh
  - zd1211rw: prevent race ending with NULL ptr deref
  - cfg80211/mac80211: more S1G fixes
  - mwifiex: avoid WARN on certain RX frames
  - mac80211:
    - avoid stack data leak in WARN cases
    - fix non-transmitted BSSID search
      (on certain multi-BSSID APs)
    - always initialize key list so driver
      iteration won't crash
    - fix monitor interface in device restart
    - fix __free() annotation usage
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmhvr/IACgkQ10qiO8sP
 aAB6axAAkDobxlyAB2SwqM9Es5JQcK/iAmQg3mdAsYxFGMRMz5nzzBszCfqAoAX/
 2PQvODLfd14Bbfc5svWtWmUz/Fie0Agk6GavOr9zMtIPLJL6/Q7lInjhbZ4zCNiD
 zxM2HjUzZMkomKHBfniiUPLd9WBQwrBKjvV5ub/f+w5ExCV+xILoP5+Mm42cPTCU
 in96FKqe2j0TSXrrPBnmKwMlS93s2NRmzKdyO5U94q8kCZXbQ93mosLKqjH8dpW5
 UGvcu1tY0gWqLR434UcBeCKEQhWsrgVq6YgmuBya2OlnQ0Z29lVsK6Jjf3bYuJJK
 6+zJS0vp6yRYUehVukhqyosz4WtHoMbNZz6JMF+glzJTm2pheke3XByHWkNVvbhL
 d6kTsMmtYkQHSFe9d3y54HO2eg910MnRSQwaxOA0id0FbcZ+57l7VpuNK6/Y8SF6
 OvhPt6Rsm0zwtd4rCrdcnMJwxYLFMzdlFw3rkXgAoHrU5yXxlB7mG+Nbh6rAn5t9
 VcT1iXqPZqsevgoGiWa0/VRd/U5sL/pXoV/7zigvOQZ78v6q2GJ5LD0Uwyx+0kMm
 T+cIPxjMb9kGHfvKRQ1aGCUm97415CdMNBKFErkQIXYxAykstN0RXZ8Ad5ZB9ZUg
 zL4TCThsWpSv0mJfVQD/KbgsBRMunnZNXiabJ40XIIFksLJHpO4=
 =Gldt
 -----END PGP SIGNATURE-----

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

Johannes Berg says:

====================
Quite a number of fixes still:

 - mt76 (hadn't sent any fixes so far)
   - RCU
   - scanning
   - decapsulation offload
   - interface combinations
 - rt2x00: build fix (bad function pointer prototype)
 - cfg80211: prevent A-MSDU flipping attacks in mesh
 - zd1211rw: prevent race ending with NULL ptr deref
 - cfg80211/mac80211: more S1G fixes
 - mwifiex: avoid WARN on certain RX frames
 - mac80211:
   - avoid stack data leak in WARN cases
   - fix non-transmitted BSSID search
     (on certain multi-BSSID APs)
   - always initialize key list so driver
     iteration won't crash
   - fix monitor interface in device restart
   - fix __free() annotation usage

* tag 'wireless-2025-07-10' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless: (26 commits)
  wifi: mac80211: add the virtual monitor after reconfig complete
  wifi: mac80211: always initialize sdata::key_list
  wifi: mac80211: Fix uninitialized variable with __free() in ieee80211_ml_epcs()
  wifi: mt76: mt792x: Limit the concurrent STA and SoftAP to operate on the same channel
  wifi: mt76: mt7925: Fix null-ptr-deref in mt7925_thermal_init()
  wifi: mt76: fix queue assignment for deauth packets
  wifi: mt76: add a wrapper for wcid access with validation
  wifi: mt76: mt7921: prevent decap offload config before STA initialization
  wifi: mt76: mt7925: prevent NULL pointer dereference in mt7925_sta_set_decap_offload()
  wifi: mt76: mt7925: fix incorrect scan probe IE handling for hw_scan
  wifi: mt76: mt7925: fix invalid array index in ssid assignment during hw scan
  wifi: mt76: mt7925: fix the wrong config for tx interrupt
  wifi: mt76: Remove RCU section in mt7996_mac_sta_rc_work()
  wifi: mt76: Move RCU section in mt7996_mcu_add_rate_ctrl()
  wifi: mt76: Move RCU section in mt7996_mcu_add_rate_ctrl_fixed()
  wifi: mt76: Move RCU section in mt7996_mcu_set_fixed_field()
  wifi: mt76: Assume __mt76_connac_mcu_alloc_sta_req runs in atomic context
  wifi: prevent A-MSDU attacks in mesh networks
  wifi: rt2x00: fix remove callback type mismatch
  wifi: mac80211: reject VHT opmode for unsupported channel widths
  ...
====================

Link: https://patch.msgid.link/20250710122212.24272-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-07-10 17:13:46 -07:00
commit 7ac5cc2616
39 changed files with 430 additions and 233 deletions

View File

@ -459,7 +459,9 @@ mwifiex_process_mgmt_packet(struct mwifiex_private *priv,
"auth: receive authentication from %pM\n",
ieee_hdr->addr3);
} else {
if (!priv->wdev.connected)
if (!priv->wdev.connected ||
!ether_addr_equal(ieee_hdr->addr3,
priv->curr_bss_params.bss_descriptor.mac_address))
return 0;
if (ieee80211_is_deauth(ieee_hdr->frame_control)) {

View File

@ -1224,6 +1224,16 @@ static inline int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q,
#define mt76_dereference(p, dev) \
rcu_dereference_protected(p, lockdep_is_held(&(dev)->mutex))
static inline struct mt76_wcid *
__mt76_wcid_ptr(struct mt76_dev *dev, u16 idx)
{
if (idx >= ARRAY_SIZE(dev->wcid))
return NULL;
return rcu_dereference(dev->wcid[idx]);
}
#define mt76_wcid_ptr(dev, idx) __mt76_wcid_ptr(&(dev)->mt76, idx)
struct mt76_dev *mt76_alloc_device(struct device *pdev, unsigned int size,
const struct ieee80211_ops *ops,
const struct mt76_driver_ops *drv_ops);

View File

@ -44,7 +44,7 @@ mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb)
if (idx >= MT7603_WTBL_STA - 1)
goto free;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (!wcid)
goto free;

View File

@ -487,10 +487,7 @@ mt7603_rx_get_wcid(struct mt7603_dev *dev, u8 idx, bool unicast)
struct mt7603_sta *sta;
struct mt76_wcid *wcid;
if (idx >= MT7603_WTBL_SIZE)
return NULL;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (unicast || !wcid)
return wcid;
@ -1266,12 +1263,9 @@ void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data)
if (pid == MT_PACKET_ID_NO_ACK)
return;
if (wcidx >= MT7603_WTBL_SIZE)
return;
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;

View File

@ -90,10 +90,7 @@ static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev,
struct mt7615_sta *sta;
struct mt76_wcid *wcid;
if (idx >= MT7615_WTBL_SIZE)
return NULL;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (unicast || !wcid)
return wcid;
@ -1504,7 +1501,7 @@ static void mt7615_mac_add_txs(struct mt7615_dev *dev, void *data)
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;

View File

@ -1172,7 +1172,7 @@ void mt76_connac2_txwi_free(struct mt76_dev *dev, struct mt76_txwi_cache *t,
wcid_idx = wcid->idx;
} else {
wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
wcid = rcu_dereference(dev->wcid[wcid_idx]);
wcid = __mt76_wcid_ptr(dev, wcid_idx);
if (wcid && wcid->sta) {
sta = container_of((void *)wcid, struct ieee80211_sta,

View File

@ -287,7 +287,7 @@ __mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif_link *mvif
mt76_connac_mcu_get_wlan_idx(dev, wcid, &hdr.wlan_idx_lo,
&hdr.wlan_idx_hi);
skb = mt76_mcu_msg_alloc(dev, NULL, len);
skb = __mt76_mcu_msg_alloc(dev, NULL, len, len, GFP_ATOMIC);
if (!skb)
return ERR_PTR(-ENOMEM);
@ -1740,8 +1740,8 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
if (!sreq->ssids[i].ssid_len)
continue;
req->ssids[i].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len);
memcpy(req->ssids[i].ssid, sreq->ssids[i].ssid,
req->ssids[n_ssids].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len);
memcpy(req->ssids[n_ssids].ssid, sreq->ssids[i].ssid,
sreq->ssids[i].ssid_len);
n_ssids++;
}

View File

@ -262,10 +262,7 @@ mt76x02_rx_get_sta(struct mt76_dev *dev, u8 idx)
{
struct mt76_wcid *wcid;
if (idx >= MT76x02_N_WCIDS)
return NULL;
wcid = rcu_dereference(dev->wcid[idx]);
wcid = __mt76_wcid_ptr(dev, idx);
if (!wcid)
return NULL;

View File

@ -564,9 +564,7 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev,
rcu_read_lock();
if (stat->wcid < MT76x02_N_WCIDS)
wcid = rcu_dereference(dev->mt76.wcid[stat->wcid]);
wcid = mt76_wcid_ptr(dev, stat->wcid);
if (wcid && wcid->sta) {
void *priv;

View File

@ -56,10 +56,7 @@ static struct mt76_wcid *mt7915_rx_get_wcid(struct mt7915_dev *dev,
struct mt7915_sta *sta;
struct mt76_wcid *wcid;
if (idx >= ARRAY_SIZE(dev->mt76.wcid))
return NULL;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (unicast || !wcid)
return wcid;
@ -917,7 +914,7 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
u16 idx;
idx = FIELD_GET(MT_TX_FREE_WLAN_ID, info);
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
sta = wcid_to_sta(wcid);
if (!sta)
continue;
@ -1013,12 +1010,9 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
if (pid < MT_PACKET_ID_WED)
return;
if (wcidx >= mt7915_wtbl_size(dev))
return;
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;

View File

@ -3986,7 +3986,7 @@ int mt7915_mcu_wed_wa_tx_stats(struct mt7915_dev *dev, u16 wlan_idx)
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
wcid = mt76_wcid_ptr(dev, wlan_idx);
if (wcid)
wcid->stats.tx_packets += le32_to_cpu(res->tx_packets);
else

View File

@ -587,12 +587,9 @@ static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed,
dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
if (idx >= mt7915_wtbl_size(dev))
return;
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (wcid) {
wcid->stats.rx_bytes += le32_to_cpu(stats->rx_byte_cnt);
wcid->stats.rx_packets += le32_to_cpu(stats->rx_pkt_cnt);

View File

@ -465,7 +465,7 @@ void mt7921_mac_add_txs(struct mt792x_dev *dev, void *data)
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;
@ -516,7 +516,7 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
count++;
idx = FIELD_GET(MT_TX_FREE_WLAN_ID, info);
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
sta = wcid_to_sta(wcid);
if (!sta)
continue;
@ -816,7 +816,7 @@ void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
u16 idx;
idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
wcid = rcu_dereference(mdev->wcid[idx]);
wcid = __mt76_wcid_ptr(mdev, idx);
sta = wcid_to_sta(wcid);
if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE)))

View File

@ -1180,6 +1180,9 @@ static void mt7921_sta_set_decap_offload(struct ieee80211_hw *hw,
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
struct mt792x_dev *dev = mt792x_hw_dev(hw);
if (!msta->deflink.wcid.sta)
return;
mt792x_mutex_acquire(dev);
if (enabled)

View File

@ -52,6 +52,8 @@ static int mt7925_thermal_init(struct mt792x_phy *phy)
name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7925_%s",
wiphy_name(wiphy));
if (!name)
return -ENOMEM;
hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
mt7925_hwmon_groups);

View File

@ -1040,7 +1040,7 @@ void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data)
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;
@ -1122,7 +1122,7 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
u16 idx;
idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
sta = wcid_to_sta(wcid);
if (!sta)
continue;
@ -1445,7 +1445,7 @@ void mt7925_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
u16 idx;
idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
wcid = rcu_dereference(mdev->wcid[idx]);
wcid = __mt76_wcid_ptr(mdev, idx);
sta = wcid_to_sta(wcid);
if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE)))

View File

@ -1481,7 +1481,7 @@ mt7925_start_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mt792x_mutex_acquire(dev);
err = mt7925_mcu_sched_scan_req(mphy, vif, req);
err = mt7925_mcu_sched_scan_req(mphy, vif, req, ies);
if (err < 0)
goto out;
@ -1603,6 +1603,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
unsigned long valid = mvif->valid_links;
u8 i;
if (!msta->vif)
return;
mt792x_mutex_acquire(dev);
valid = ieee80211_vif_is_mld(vif) ? mvif->valid_links : BIT(0);
@ -1617,6 +1620,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
else
clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
if (!mlink->wcid.sta)
continue;
mt7925_mcu_wtbl_update_hdr_trans(dev, vif, sta, i);
}

View File

@ -164,6 +164,7 @@ mt7925_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif,
bool suspend, struct cfg80211_wowlan *wowlan)
{
struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv;
struct ieee80211_scan_ies ies = {};
struct mt76_dev *dev = phy->dev;
struct {
struct {
@ -194,7 +195,7 @@ mt7925_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif,
req.wow_ctrl_tlv.trigger |= (UNI_WOW_DETECT_TYPE_DISCONNECT |
UNI_WOW_DETECT_TYPE_BCN_LOST);
if (wowlan->nd_config) {
mt7925_mcu_sched_scan_req(phy, vif, wowlan->nd_config);
mt7925_mcu_sched_scan_req(phy, vif, wowlan->nd_config, &ies);
req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_SCH_SCAN_HIT;
mt7925_mcu_sched_scan_enable(phy, vif, suspend);
}
@ -2818,6 +2819,54 @@ int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable)
return err;
}
static void
mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev,
struct sk_buff *skb,
struct ieee80211_scan_ies *scan_ies)
{
u32 max_len = sizeof(struct scan_ie_tlv) + MT76_CONNAC_SCAN_IE_LEN;
struct scan_ie_tlv *ie;
enum nl80211_band i;
struct tlv *tlv;
const u8 *ies;
u16 ies_len;
for (i = 0; i <= NL80211_BAND_6GHZ; i++) {
if (i == NL80211_BAND_60GHZ)
continue;
ies = scan_ies->ies[i];
ies_len = scan_ies->len[i];
if (!ies || !ies_len)
continue;
if (ies_len > max_len)
return;
tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE,
sizeof(*ie) + ies_len);
ie = (struct scan_ie_tlv *)tlv;
memcpy(ie->ies, ies, ies_len);
ie->ies_len = cpu_to_le16(ies_len);
switch (i) {
case NL80211_BAND_2GHZ:
ie->band = 1;
break;
case NL80211_BAND_6GHZ:
ie->band = 3;
break;
default:
ie->band = 2;
break;
}
max_len -= (sizeof(*ie) + ies_len);
}
}
int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_scan_request *scan_req)
{
@ -2843,7 +2892,8 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) +
sizeof(*bssid) * MT7925_RNR_SCAN_MAX_BSSIDS +
sizeof(*chan_info) + sizeof(*misc) + sizeof(*ie);
sizeof(*chan_info) + sizeof(*misc) + sizeof(*ie) +
MT76_CONNAC_SCAN_IE_LEN;
skb = mt76_mcu_msg_alloc(mdev, NULL, max_len);
if (!skb)
@ -2869,8 +2919,8 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
if (i > MT7925_RNR_SCAN_MAX_BSSIDS)
break;
ssid->ssids[i].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len);
memcpy(ssid->ssids[i].ssid, sreq->ssids[i].ssid,
ssid->ssids[n_ssids].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len);
memcpy(ssid->ssids[n_ssids].ssid, sreq->ssids[i].ssid,
sreq->ssids[i].ssid_len);
n_ssids++;
}
@ -2925,13 +2975,6 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
}
chan_info->channel_type = sreq->n_channels ? 4 : 0;
tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, sizeof(*ie));
ie = (struct scan_ie_tlv *)tlv;
if (sreq->ie_len > 0) {
memcpy(ie->ies, sreq->ie, sreq->ie_len);
ie->ies_len = cpu_to_le16(sreq->ie_len);
}
req->scan_func |= SCAN_FUNC_SPLIT_SCAN;
tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_MISC, sizeof(*misc));
@ -2942,6 +2985,9 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
req->scan_func |= SCAN_FUNC_RANDOM_MAC;
}
/* Append scan probe IEs as the last tlv */
mt7925_mcu_build_scan_ie_tlv(mdev, skb, &scan_req->ies);
err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
true);
if (err < 0)
@ -2953,7 +2999,8 @@ EXPORT_SYMBOL_GPL(mt7925_mcu_hw_scan);
int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *sreq)
struct cfg80211_sched_scan_request *sreq,
struct ieee80211_scan_ies *ies)
{
struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv;
struct ieee80211_channel **scan_list = sreq->channels;
@ -3041,12 +3088,8 @@ int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
}
chan_info->channel_type = sreq->n_channels ? 4 : 0;
tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, sizeof(*ie));
ie = (struct scan_ie_tlv *)tlv;
if (sreq->ie_len > 0) {
memcpy(ie->ies, sreq->ie, sreq->ie_len);
ie->ies_len = cpu_to_le16(sreq->ie_len);
}
/* Append scan probe IEs as the last tlv */
mt7925_mcu_build_scan_ie_tlv(mdev, skb, ies);
return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
true);

View File

@ -269,7 +269,7 @@ struct scan_ie_tlv {
__le16 ies_len;
u8 band;
u8 pad;
u8 ies[MT76_CONNAC_SCAN_IE_LEN];
u8 ies[];
};
struct scan_misc_tlv {
@ -673,7 +673,8 @@ int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy,
struct ieee80211_vif *vif);
int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *sreq);
struct cfg80211_sched_scan_request *sreq,
struct ieee80211_scan_ies *ies);
int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy,
struct ieee80211_vif *vif,
bool enable);

View File

@ -58,7 +58,7 @@
#define MT_INT_TX_DONE_MCU (MT_INT_TX_DONE_MCU_WM | \
MT_INT_TX_DONE_FWDL)
#define MT_INT_TX_DONE_ALL (MT_INT_TX_DONE_MCU_WM | \
#define MT_INT_TX_DONE_ALL (MT_INT_TX_DONE_MCU | \
MT_INT_TX_DONE_BAND0 | \
GENMASK(18, 4))

View File

@ -28,7 +28,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
},
};
static const struct ieee80211_iface_limit if_limits_chanctx[] = {
static const struct ieee80211_iface_limit if_limits_chanctx_mcc[] = {
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION) |
@ -36,8 +36,23 @@ static const struct ieee80211_iface_limit if_limits_chanctx[] = {
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO)
.types = BIT(NL80211_IFTYPE_P2P_GO)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_DEVICE)
}
};
static const struct ieee80211_iface_limit if_limits_chanctx_scc[] = {
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP)
},
{
.max = 1,
@ -47,11 +62,18 @@ static const struct ieee80211_iface_limit if_limits_chanctx[] = {
static const struct ieee80211_iface_combination if_comb_chanctx[] = {
{
.limits = if_limits_chanctx,
.n_limits = ARRAY_SIZE(if_limits_chanctx),
.limits = if_limits_chanctx_mcc,
.n_limits = ARRAY_SIZE(if_limits_chanctx_mcc),
.max_interfaces = 3,
.num_different_channels = 2,
.beacon_int_infra_match = false,
},
{
.limits = if_limits_chanctx_scc,
.n_limits = ARRAY_SIZE(if_limits_chanctx_scc),
.max_interfaces = 3,
.num_different_channels = 1,
.beacon_int_infra_match = false,
}
};

View File

@ -142,10 +142,7 @@ struct mt76_wcid *mt792x_rx_get_wcid(struct mt792x_dev *dev, u16 idx,
struct mt792x_sta *sta;
struct mt76_wcid *wcid;
if (idx >= ARRAY_SIZE(dev->mt76.wcid))
return NULL;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (unicast || !wcid)
return wcid;

View File

@ -61,10 +61,7 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
struct mt76_wcid *wcid;
int i;
if (idx >= ARRAY_SIZE(dev->mt76.wcid))
return NULL;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (!wcid)
return NULL;
@ -1249,7 +1246,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
u16 idx;
idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
sta = wcid_to_sta(wcid);
if (!sta)
goto next;
@ -1471,12 +1468,9 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
if (pid < MT_PACKET_ID_NO_SKB)
return;
if (wcidx >= mt7996_wtbl_size(dev))
return;
rcu_read_lock();
wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;
@ -2353,20 +2347,12 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
void mt7996_mac_sta_rc_work(struct work_struct *work)
{
struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
struct ieee80211_bss_conf *link_conf;
struct ieee80211_link_sta *link_sta;
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
struct mt76_vif_link *mlink;
struct ieee80211_sta *sta;
struct ieee80211_vif *vif;
struct mt7996_sta *msta;
struct mt7996_vif *mvif;
LIST_HEAD(list);
u32 changed;
u8 link_id;
rcu_read_lock();
spin_lock_bh(&dev->mt76.sta_poll_lock);
list_splice_init(&dev->sta_rc_list, &list);
@ -2377,46 +2363,28 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
changed = msta_link->changed;
msta_link->changed = 0;
sta = wcid_to_sta(&msta_link->wcid);
link_id = msta_link->wcid.link_id;
msta = msta_link->sta;
mvif = msta->vif;
vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
mlink = rcu_dereference(mvif->mt76.link[link_id]);
if (!mlink)
continue;
link_sta = rcu_dereference(sta->link[link_id]);
if (!link_sta)
continue;
link_conf = rcu_dereference(vif->link_conf[link_id]);
if (!link_conf)
continue;
mvif = msta_link->sta->vif;
vif = container_of((void *)mvif, struct ieee80211_vif,
drv_priv);
spin_unlock_bh(&dev->mt76.sta_poll_lock);
link = (struct mt7996_vif_link *)mlink;
if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
IEEE80211_RC_NSS_CHANGED |
IEEE80211_RC_BW_CHANGED))
mt7996_mcu_add_rate_ctrl(dev, vif, link_conf,
link_sta, link, msta_link,
mt7996_mcu_add_rate_ctrl(dev, msta_link->sta, vif,
msta_link->wcid.link_id,
true);
if (changed & IEEE80211_RC_SMPS_CHANGED)
mt7996_mcu_set_fixed_field(dev, link_sta, link,
msta_link, NULL,
mt7996_mcu_set_fixed_field(dev, msta_link->sta, NULL,
msta_link->wcid.link_id,
RATE_PARAM_MMPS_UPDATE);
spin_lock_bh(&dev->mt76.sta_poll_lock);
}
spin_unlock_bh(&dev->mt76.sta_poll_lock);
rcu_read_unlock();
}
void mt7996_mac_work(struct work_struct *work)

View File

@ -1112,9 +1112,8 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
if (err)
return err;
err = mt7996_mcu_add_rate_ctrl(dev, vif, link_conf,
link_sta, link,
msta_link, false);
err = mt7996_mcu_add_rate_ctrl(dev, msta_link->sta, vif,
link_id, false);
if (err)
return err;

View File

@ -555,7 +555,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
switch (le16_to_cpu(res->tag)) {
case UNI_ALL_STA_TXRX_RATE:
wlan_idx = le16_to_cpu(res->rate[i].wlan_idx);
wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
wcid = mt76_wcid_ptr(dev, wlan_idx);
if (!wcid)
break;
@ -565,7 +565,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
break;
case UNI_ALL_STA_TXRX_ADM_STAT:
wlan_idx = le16_to_cpu(res->adm_stat[i].wlan_idx);
wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
wcid = mt76_wcid_ptr(dev, wlan_idx);
if (!wcid)
break;
@ -579,7 +579,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
break;
case UNI_ALL_STA_TXRX_MSDU_COUNT:
wlan_idx = le16_to_cpu(res->msdu_cnt[i].wlan_idx);
wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
wcid = mt76_wcid_ptr(dev, wlan_idx);
if (!wcid)
break;
@ -676,10 +676,7 @@ mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb)
e = (void *)skb->data;
idx = le16_to_cpu(e->wlan_id);
if (idx >= ARRAY_SIZE(dev->mt76.wcid))
break;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
wcid = mt76_wcid_ptr(dev, idx);
if (!wcid || !wcid->sta)
break;
@ -1905,22 +1902,35 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
MCU_WM_UNI_CMD(RA), true);
}
int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link,
void *data, u32 field)
int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta,
void *data, u8 link_id, u32 field)
{
struct sta_phy_uni *phy = data;
struct mt7996_vif *mvif = msta->vif;
struct mt7996_sta_link *msta_link;
struct sta_rec_ra_fixed_uni *ra;
struct sta_phy_uni *phy = data;
struct mt76_vif_link *mlink;
struct sk_buff *skb;
int err = -ENODEV;
struct tlv *tlv;
skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
rcu_read_lock();
mlink = rcu_dereference(mvif->mt76.link[link_id]);
if (!mlink)
goto error_unlock;
msta_link = rcu_dereference(msta->link[link_id]);
if (!msta_link)
goto error_unlock;
skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mlink,
&msta_link->wcid,
MT7996_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto error_unlock;
}
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra));
ra = (struct sta_rec_ra_fixed_uni *)tlv;
@ -1935,106 +1945,149 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
if (phy)
ra->phy = *phy;
break;
case RATE_PARAM_MMPS_UPDATE:
case RATE_PARAM_MMPS_UPDATE: {
struct ieee80211_sta *sta = wcid_to_sta(&msta_link->wcid);
struct ieee80211_link_sta *link_sta;
link_sta = rcu_dereference(sta->link[link_id]);
if (!link_sta) {
dev_kfree_skb(skb);
goto error_unlock;
}
ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
break;
}
default:
break;
}
ra->field = cpu_to_le32(field);
rcu_read_unlock();
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
error_unlock:
rcu_read_unlock();
return err;
}
static int
mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link)
mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta,
struct ieee80211_vif *vif, u8 link_id)
{
struct cfg80211_chan_def *chandef = &link->phy->mt76->chandef;
struct cfg80211_bitrate_mask *mask = &link->bitrate_mask;
enum nl80211_band band = chandef->chan->band;
struct ieee80211_link_sta *link_sta;
struct cfg80211_bitrate_mask mask;
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
struct sta_phy_uni phy = {};
int ret, nrates = 0;
struct ieee80211_sta *sta;
int ret, nrates = 0, idx;
enum nl80211_band band;
bool has_he;
#define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \
do { \
u8 i, gi = mask->control[band]._gi; \
u8 i, gi = mask.control[band]._gi; \
gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI; \
phy.sgi = gi; \
phy.he_ltf = mask->control[band].he_ltf; \
for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) { \
if (!mask->control[band]._mcs[i]) \
phy.he_ltf = mask.control[band].he_ltf; \
for (i = 0; i < ARRAY_SIZE(mask.control[band]._mcs); i++) { \
if (!mask.control[band]._mcs[i]) \
continue; \
nrates += hweight16(mask->control[band]._mcs[i]); \
phy.mcs = ffs(mask->control[band]._mcs[i]) - 1; \
nrates += hweight16(mask.control[band]._mcs[i]); \
phy.mcs = ffs(mask.control[band]._mcs[i]) - 1; \
if (_ht) \
phy.mcs += 8 * i; \
} \
} while (0)
if (link_sta->he_cap.has_he) {
rcu_read_lock();
link = mt7996_vif_link(dev, vif, link_id);
if (!link)
goto error_unlock;
msta_link = rcu_dereference(msta->link[link_id]);
if (!msta_link)
goto error_unlock;
sta = wcid_to_sta(&msta_link->wcid);
link_sta = rcu_dereference(sta->link[link_id]);
if (!link_sta)
goto error_unlock;
band = link->phy->mt76->chandef.chan->band;
has_he = link_sta->he_cap.has_he;
mask = link->bitrate_mask;
idx = msta_link->wcid.idx;
if (has_he) {
__sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
} else if (link_sta->vht_cap.vht_supported) {
__sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
} else if (link_sta->ht_cap.ht_supported) {
__sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
} else {
nrates = hweight32(mask->control[band].legacy);
phy.mcs = ffs(mask->control[band].legacy) - 1;
nrates = hweight32(mask.control[band].legacy);
phy.mcs = ffs(mask.control[band].legacy) - 1;
}
rcu_read_unlock();
#undef __sta_phy_bitrate_mask_check
/* fall back to auto rate control */
if (mask->control[band].gi == NL80211_TXRATE_DEFAULT_GI &&
mask->control[band].he_gi == GENMASK(7, 0) &&
mask->control[band].he_ltf == GENMASK(7, 0) &&
if (mask.control[band].gi == NL80211_TXRATE_DEFAULT_GI &&
mask.control[band].he_gi == GENMASK(7, 0) &&
mask.control[band].he_ltf == GENMASK(7, 0) &&
nrates != 1)
return 0;
/* fixed single rate */
if (nrates == 1) {
ret = mt7996_mcu_set_fixed_field(dev, link_sta, link,
msta_link, &phy,
ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id,
RATE_PARAM_FIXED_MCS);
if (ret)
return ret;
}
/* fixed GI */
if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
mask->control[band].he_gi != GENMASK(7, 0)) {
if (mask.control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
mask.control[band].he_gi != GENMASK(7, 0)) {
u32 addr;
/* firmware updates only TXCMD but doesn't take WTBL into
* account, so driver should update here to reflect the
* actual txrate hardware sends out.
*/
addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 7);
if (link_sta->he_cap.has_he)
addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 7);
if (has_he)
mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
else
mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
ret = mt7996_mcu_set_fixed_field(dev, link_sta, link,
msta_link, &phy,
ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id,
RATE_PARAM_FIXED_GI);
if (ret)
return ret;
}
/* fixed HE_LTF */
if (mask->control[band].he_ltf != GENMASK(7, 0)) {
ret = mt7996_mcu_set_fixed_field(dev, link_sta, link,
msta_link, &phy,
if (mask.control[band].he_ltf != GENMASK(7, 0)) {
ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id,
RATE_PARAM_FIXED_HE_LTF);
if (ret)
return ret;
}
return 0;
error_unlock:
rcu_read_unlock();
return -ENODEV;
}
static void
@ -2145,21 +2198,44 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi));
}
int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link, bool changed)
int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta,
struct ieee80211_vif *vif, u8 link_id,
bool changed)
{
struct ieee80211_bss_conf *link_conf;
struct ieee80211_link_sta *link_sta;
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
struct ieee80211_sta *sta;
struct sk_buff *skb;
int ret;
int ret = -ENODEV;
rcu_read_lock();
link = mt7996_vif_link(dev, vif, link_id);
if (!link)
goto error_unlock;
msta_link = rcu_dereference(msta->link[link_id]);
if (!msta_link)
goto error_unlock;
sta = wcid_to_sta(&msta_link->wcid);
link_sta = rcu_dereference(sta->link[link_id]);
if (!link_sta)
goto error_unlock;
link_conf = rcu_dereference(vif->link_conf[link_id]);
if (!link_conf)
goto error_unlock;
skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
&msta_link->wcid,
MT7996_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
goto error_unlock;
}
/* firmware rc algorithm refers to sta_rec_he for HE control.
* once dev->rc_work changes the settings driver should also
@ -2173,12 +2249,19 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
*/
mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link);
rcu_read_unlock();
ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
if (ret)
return ret;
return mt7996_mcu_add_rate_ctrl_fixed(dev, link_sta, link, msta_link);
return mt7996_mcu_add_rate_ctrl_fixed(dev, msta, vif, link_id);
error_unlock:
rcu_read_unlock();
return ret;
}
static int

View File

@ -620,23 +620,17 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
struct mt7996_vif_link *link,
struct ieee80211_he_obss_pd *he_obss_pd);
int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link, bool changed);
int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta,
struct ieee80211_vif *vif, u8 link_id,
bool changed);
int mt7996_set_channel(struct mt76_phy *mphy);
int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf);
int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
void *data, u16 version);
int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link,
void *data, u32 field);
int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta,
void *data, u8 link_id, u32 field);
int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len);
int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);

View File

@ -64,7 +64,7 @@ mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
struct mt76_wcid *wcid;
wcid = rcu_dereference(dev->wcid[cb->wcid]);
wcid = __mt76_wcid_ptr(dev, cb->wcid);
if (wcid) {
status.sta = wcid_to_sta(wcid);
if (status.sta && (wcid->rate.flags || wcid->rate.legacy)) {
@ -251,9 +251,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
rcu_read_lock();
if (wcid_idx < ARRAY_SIZE(dev->wcid))
wcid = rcu_dereference(dev->wcid[wcid_idx]);
wcid = __mt76_wcid_ptr(dev, wcid_idx);
mt76_tx_check_non_aql(dev, wcid, skb);
#ifdef CONFIG_NL80211_TESTMODE
@ -538,7 +536,7 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid)
break;
mtxq = (struct mt76_txq *)txq->drv_priv;
wcid = rcu_dereference(dev->wcid[mtxq->wcid]);
wcid = __mt76_wcid_ptr(dev, mtxq->wcid);
if (!wcid || test_bit(MT_WCID_FLAG_PS, &wcid->flags))
continue;
@ -617,7 +615,8 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid,
if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) &&
!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
!ieee80211_is_data(hdr->frame_control) &&
!ieee80211_is_bufferable_mmpdu(skb))
(!ieee80211_is_bufferable_mmpdu(skb) ||
ieee80211_is_deauth(hdr->frame_control)))
qid = MT_TXQ_PSD;
q = phy->q_tx[qid];

View File

@ -83,7 +83,7 @@ int mt76_get_min_avg_rssi(struct mt76_dev *dev, u8 phy_idx)
if (!(mask & 1))
continue;
wcid = rcu_dereference(dev->wcid[j]);
wcid = __mt76_wcid_ptr(dev, j);
if (!wcid || wcid->phy_idx != phy_idx)
continue;

View File

@ -108,7 +108,7 @@ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops)
}
EXPORT_SYMBOL_GPL(rt2x00soc_probe);
int rt2x00soc_remove(struct platform_device *pdev)
void rt2x00soc_remove(struct platform_device *pdev)
{
struct ieee80211_hw *hw = platform_get_drvdata(pdev);
struct rt2x00_dev *rt2x00dev = hw->priv;
@ -119,8 +119,6 @@ int rt2x00soc_remove(struct platform_device *pdev)
rt2x00lib_remove_dev(rt2x00dev);
rt2x00soc_free_reg(rt2x00dev);
ieee80211_free_hw(hw);
return 0;
}
EXPORT_SYMBOL_GPL(rt2x00soc_remove);

View File

@ -17,7 +17,7 @@
* SoC driver handlers.
*/
int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops);
int rt2x00soc_remove(struct platform_device *pdev);
void rt2x00soc_remove(struct platform_device *pdev);
#ifdef CONFIG_PM
int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state);
int rt2x00soc_resume(struct platform_device *pdev);

View File

@ -583,7 +583,11 @@ void zd_mac_tx_to_dev(struct sk_buff *skb, int error)
skb_queue_tail(q, skb);
while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) {
zd_mac_tx_status(hw, skb_dequeue(q),
skb = skb_dequeue(q);
if (!skb)
break;
zd_mac_tx_status(hw, skb,
mac->ack_pending ? mac->ack_signal : 0,
NULL);
mac->ack_pending = 0;

View File

@ -662,18 +662,6 @@ static inline bool ieee80211_s1g_has_cssid(__le16 fc)
(fc & cpu_to_le16(IEEE80211_S1G_BCN_CSSID));
}
/**
* ieee80211_is_s1g_short_beacon - check if frame is an S1G short beacon
* @fc: frame control bytes in little-endian byteorder
* Return: whether or not the frame is an S1G short beacon,
* i.e. it is an S1G beacon with 'next TBTT' flag set
*/
static inline bool ieee80211_is_s1g_short_beacon(__le16 fc)
{
return ieee80211_is_s1g_beacon(fc) &&
(fc & cpu_to_le16(IEEE80211_S1G_BCN_NEXT_TBTT));
}
/**
* ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM
* @fc: frame control bytes in little-endian byteorder
@ -4901,6 +4889,39 @@ static inline bool ieee80211_is_ftm(struct sk_buff *skb)
return false;
}
/**
* ieee80211_is_s1g_short_beacon - check if frame is an S1G short beacon
* @fc: frame control bytes in little-endian byteorder
* @variable: pointer to the beacon frame elements
* @variable_len: length of the frame elements
* Return: whether or not the frame is an S1G short beacon. As per
* IEEE80211-2024 11.1.3.10.1, The S1G beacon compatibility element shall
* always be present as the first element in beacon frames generated at a
* TBTT (Target Beacon Transmission Time), so any frame not containing
* this element must have been generated at a TSBTT (Target Short Beacon
* Transmission Time) that is not a TBTT. Additionally, short beacons are
* prohibited from containing the S1G beacon compatibility element as per
* IEEE80211-2024 9.3.4.3 Table 9-76, so if we have an S1G beacon with
* either no elements or the first element is not the beacon compatibility
* element, we have a short beacon.
*/
static inline bool ieee80211_is_s1g_short_beacon(__le16 fc, const u8 *variable,
size_t variable_len)
{
if (!ieee80211_is_s1g_beacon(fc))
return false;
/*
* If the frame does not contain at least 1 element (this is perfectly
* valid in a short beacon) and is an S1G beacon, we have a short
* beacon.
*/
if (variable_len < 2)
return true;
return variable[0] != WLAN_EID_S1G_BCN_COMPAT;
}
struct element {
u8 id;
u8 datalen;

View File

@ -1959,6 +1959,20 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
ieee80211_sta_init_nss(link_sta);
if (params->opmode_notif_used) {
enum nl80211_chan_width width = link->conf->chanreq.oper.width;
switch (width) {
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_40:
case NL80211_CHAN_WIDTH_80:
case NL80211_CHAN_WIDTH_160:
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_320: /* not VHT, allowed for HE/EHT */
break;
default:
return -EINVAL;
}
/* returned value is only needed for rc update, but the
* rc isn't initialized here yet, so ignore it
*/

View File

@ -1150,6 +1150,8 @@ static void ieee80211_sdata_init(struct ieee80211_local *local,
{
sdata->local = local;
INIT_LIST_HEAD(&sdata->key_list);
/*
* Initialize the default link, so we can use link_id 0 for non-MLD,
* and that continues to work for non-MLD-aware drivers that use just
@ -2210,8 +2212,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ieee80211_init_frag_cache(&sdata->frags);
INIT_LIST_HEAD(&sdata->key_list);
wiphy_delayed_work_init(&sdata->dec_tailroom_needed_wk,
ieee80211_delayed_tailroom_dec);

View File

@ -3934,6 +3934,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
lockdep_assert_wiphy(local->hw.wiphy);
if (frame_buf)
memset(frame_buf, 0, IEEE80211_DEAUTH_FRAME_LEN);
if (WARN_ON(!ap_sta))
return;
@ -7195,6 +7198,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
struct ieee80211_bss_conf *bss_conf = link->conf;
struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
struct ieee80211_mgmt *mgmt = (void *) hdr;
struct ieee80211_ext *ext = NULL;
size_t baselen;
struct ieee802_11_elems *elems;
struct ieee80211_local *local = sdata->local;
@ -7220,7 +7224,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
/* Process beacon from the current BSS */
bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
struct ieee80211_ext *ext = (void *) mgmt;
ext = (void *)mgmt;
variable = ext->u.s1g_beacon.variable +
ieee80211_s1g_optional_len(ext->frame_control);
}
@ -7407,7 +7411,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
}
if ((ncrc == link->u.mgd.beacon_crc && link->u.mgd.beacon_crc_valid) ||
ieee80211_is_s1g_short_beacon(mgmt->frame_control))
(ext && ieee80211_is_s1g_short_beacon(ext->frame_control,
parse_params.start,
parse_params.len)))
goto free;
link->u.mgd.beacon_crc = ncrc;
link->u.mgd.beacon_crc_valid = true;
@ -10699,8 +10705,8 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata,
*/
for_each_mle_subelement(sub, (const u8 *)elems->ml_epcs,
elems->ml_epcs_len) {
struct ieee802_11_elems *link_elems __free(kfree) = NULL;
struct ieee80211_link_data *link;
struct ieee802_11_elems *link_elems __free(kfree);
u8 *pos = (void *)sub->data;
u16 control;
ssize_t len;

View File

@ -758,7 +758,6 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
{
const struct element *elem, *sub;
size_t profile_len = 0;
bool found = false;
if (!bss || !bss->transmitted_bss)
return profile_len;
@ -809,15 +808,14 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
index[2],
new_bssid);
if (ether_addr_equal(new_bssid, bss->bssid)) {
found = true;
elems->bssid_index_len = index[1];
elems->bssid_index = (void *)&index[2];
break;
return profile_len;
}
}
}
return found ? profile_len : 0;
return 0;
}
static void

View File

@ -2144,11 +2144,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
wake_up:
if (local->virt_monitors > 0 &&
local->virt_monitors == local->open_count)
ieee80211_add_virtual_monitor(local);
/*
* Clear the WLAN_STA_BLOCK_BA flag so new aggregation
* sessions can be established after a resume.
@ -2202,6 +2197,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
}
if (local->virt_monitors > 0 &&
local->virt_monitors == local->open_count)
ieee80211_add_virtual_monitor(local);
if (!suspended)
return 0;

View File

@ -229,6 +229,7 @@ static int validate_beacon_head(const struct nlattr *attr,
unsigned int len = nla_len(attr);
const struct element *elem;
const struct ieee80211_mgmt *mgmt = (void *)data;
const struct ieee80211_ext *ext;
unsigned int fixedlen, hdrlen;
bool s1g_bcn;
@ -237,8 +238,10 @@ static int validate_beacon_head(const struct nlattr *attr,
s1g_bcn = ieee80211_is_s1g_beacon(mgmt->frame_control);
if (s1g_bcn) {
fixedlen = offsetof(struct ieee80211_ext,
u.s1g_beacon.variable);
ext = (struct ieee80211_ext *)mgmt;
fixedlen =
offsetof(struct ieee80211_ext, u.s1g_beacon.variable) +
ieee80211_s1g_optional_len(ext->frame_control);
hdrlen = offsetof(struct ieee80211_ext, u.s1g_beacon);
} else {
fixedlen = offsetof(struct ieee80211_mgmt,

View File

@ -820,6 +820,52 @@ bool ieee80211_is_valid_amsdu(struct sk_buff *skb, u8 mesh_hdr)
}
EXPORT_SYMBOL(ieee80211_is_valid_amsdu);
/*
* Detects if an MSDU frame was maliciously converted into an A-MSDU
* frame by an adversary. This is done by parsing the received frame
* as if it were a regular MSDU, even though the A-MSDU flag is set.
*
* For non-mesh interfaces, detection involves checking whether the
* payload, when interpreted as an MSDU, begins with a valid RFC1042
* header. This is done by comparing the A-MSDU subheader's destination
* address to the start of the RFC1042 header.
*
* For mesh interfaces, the MSDU includes a 6-byte Mesh Control field
* and an optional variable-length Mesh Address Extension field before
* the RFC1042 header. The position of the RFC1042 header must therefore
* be calculated based on the mesh header length.
*
* Since this function intentionally parses an A-MSDU frame as an MSDU,
* it only assumes that the A-MSDU subframe header is present, and
* beyond this it performs its own bounds checks under the assumption
* that the frame is instead parsed as a non-aggregated MSDU.
*/
static bool
is_amsdu_aggregation_attack(struct ethhdr *eth, struct sk_buff *skb,
enum nl80211_iftype iftype)
{
int offset;
/* Non-mesh case can be directly compared */
if (iftype != NL80211_IFTYPE_MESH_POINT)
return ether_addr_equal(eth->h_dest, rfc1042_header);
offset = __ieee80211_get_mesh_hdrlen(eth->h_dest[0]);
if (offset == 6) {
/* Mesh case with empty address extension field */
return ether_addr_equal(eth->h_source, rfc1042_header);
} else if (offset + ETH_ALEN <= skb->len) {
/* Mesh case with non-empty address extension field */
u8 temp[ETH_ALEN];
skb_copy_bits(skb, offset, temp, ETH_ALEN);
return ether_addr_equal(temp, rfc1042_header);
}
return false;
}
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
const u8 *addr, enum nl80211_iftype iftype,
const unsigned int extra_headroom,
@ -861,8 +907,10 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
/* the last MSDU has no padding */
if (subframe_len > remaining)
goto purge;
/* mitigate A-MSDU aggregation injection attacks */
if (ether_addr_equal(hdr.eth.h_dest, rfc1042_header))
/* mitigate A-MSDU aggregation injection attacks, to be
* checked when processing first subframe (offset == 0).
*/
if (offset == 0 && is_amsdu_aggregation_attack(&hdr.eth, skb, iftype))
goto purge;
offset += sizeof(struct ethhdr);