summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/mediatek/mt76/Kconfig1
-rw-r--r--drivers/net/wireless/mediatek/mt76/channel.c26
-rw-r--r--drivers/net/wireless/mediatek/mt76/eeprom.c17
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c32
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h31
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/dma.c18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/init.c17
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/main.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/usb.c17
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c20
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c19
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.c30
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/coredump.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/init.c17
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/main.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mac.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/main.c48
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mcu.c26
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/regd.c28
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c298
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h151
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mac.c5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/main.c115
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.c73
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/nan.c694
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/nan.h103
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci.c66
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c8
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regs.h3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/usb.c26
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x.h18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_core.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_mac.c9
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_usb.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Kconfig1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/coredump.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c323
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/init.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.c23
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/main.c784
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c153
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.h23
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h42
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/npu.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/pci.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/regs.h36
-rw-r--r--drivers/net/wireless/mediatek/mt76/scan.c219
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c9
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb.c47
63 files changed, 2853 insertions, 872 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig
index 2ca96e0527c0..a4ece38f938d 100644
--- a/drivers/net/wireless/mediatek/mt76/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/Kconfig
@@ -40,6 +40,7 @@ config MT792x_USB
config MT76_NPU
tristate
depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
+ depends on NET_AIROHA=y || NET_AIROHA=n || MT76_CORE=NET_AIROHA
source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig"
diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c
index 28ad7bcaffd4..192276cf19fd 100644
--- a/drivers/net/wireless/mediatek/mt76/channel.c
+++ b/drivers/net/wireless/mediatek/mt76/channel.c
@@ -41,7 +41,7 @@ int mt76_add_chanctx(struct ieee80211_hw *hw,
if (WARN_ON_ONCE(!phy))
return ret;
- if (dev->scan.phy == phy)
+ if (mt76_phy_scanning(phy))
mt76_abort_scan(dev);
mutex_lock(&dev->mutex);
@@ -66,7 +66,7 @@ void mt76_remove_chanctx(struct ieee80211_hw *hw,
if (WARN_ON_ONCE(!phy))
return;
- if (dev->scan.phy == phy)
+ if (mt76_phy_scanning(phy))
mt76_abort_scan(dev);
mutex_lock(&dev->mutex);
@@ -114,7 +114,7 @@ int mt76_assign_vif_chanctx(struct ieee80211_hw *hw,
bool mlink_alloc = false;
int ret = 0;
- if (dev->scan.vif == vif)
+ if (dev->scan.vif == vif && mt76_phy_scanning(phy))
mt76_abort_scan(dev);
mutex_lock(&dev->mutex);
@@ -161,7 +161,7 @@ void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
struct mt76_phy *phy = ctx->phy;
struct mt76_dev *dev = phy->dev;
- if (dev->scan.vif == vif)
+ if (dev->scan.vif == vif && mt76_phy_scanning(phy))
mt76_abort_scan(dev);
mutex_lock(&dev->mutex);
@@ -209,7 +209,7 @@ int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,
continue;
if (phy->chanctx != new_ctx) {
- if (dev->scan.phy == phy)
+ if (mt76_phy_scanning(phy))
mt76_abort_scan(dev);
cancel_delayed_work_sync(&phy->mac_work);
@@ -295,7 +295,6 @@ struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy,
kfree(mlink);
return ERR_PTR(ret);
}
- rcu_assign_pointer(mvif->offchannel_link, mlink);
return mlink;
}
@@ -304,14 +303,10 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct mt76_vif_link *mlink)
{
struct mt76_dev *dev = phy->dev;
- struct mt76_vif_data *mvif;
if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel)
return;
- mvif = mlink->mvif;
-
- rcu_assign_pointer(mvif->offchannel_link, NULL);
dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink);
kfree_rcu(mlink, rcu_head);
}
@@ -330,11 +325,13 @@ void mt76_roc_complete(struct mt76_phy *phy)
!test_bit(MT76_MCU_RESET, &dev->phy.state)) {
__mt76_set_channel(phy, &phy->main_chandef, false);
mt76_offchannel_notify(phy, false);
+ } else {
+ phy->offchannel = false;
}
mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link);
phy->roc_vif = NULL;
phy->roc_link = NULL;
- if (!test_bit(MT76_MCU_RESET, &dev->phy.state))
+ if (!test_bit(MT76_RESTART, &dev->phy.state))
ieee80211_remain_on_channel_expired(phy->hw);
}
@@ -379,8 +376,9 @@ int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mutex_lock(&dev->mutex);
- if (phy->roc_vif || dev->scan.phy == phy ||
- test_bit(MT76_MCU_RESET, &dev->phy.state)) {
+ if (phy->roc_vif || mt76_phy_scanning(phy) ||
+ test_bit(MT76_MCU_RESET, &dev->phy.state) ||
+ test_bit(MT76_RESTART, &dev->phy.state)) {
ret = -EBUSY;
goto out;
}
@@ -423,7 +421,7 @@ int mt76_cancel_remain_on_channel(struct ieee80211_hw *hw,
struct mt76_vif_data *mvif = mlink->mvif;
struct mt76_phy *phy = mvif->roc_phy;
- if (!phy)
+ if (!phy || phy->roc_vif != vif)
return 0;
mt76_abort_roc(phy);
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index afdb73661866..93552f6f7f34 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -231,12 +231,14 @@ mt76_find_power_limits_node(struct mt76_dev *dev)
struct property *regd = of_find_property(cur, "regdomain", NULL);
if (!country && !regd) {
- fallback = cur;
+ of_node_put(fallback);
+ fallback = of_node_get(cur);
continue;
}
if (mt76_string_prop_find(country, dev->alpha2) ||
mt76_string_prop_find(regd, region_name)) {
+ of_node_put(fallback);
of_node_put(np);
return cur;
}
@@ -429,7 +431,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
s8 target_power)
{
struct mt76_dev *dev = phy->dev;
- struct device_node *np;
+ struct device_node *np, *band_np;
const s8 *val;
char name[16];
char band;
@@ -459,15 +461,18 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
band = '6';
break;
default:
+ of_node_put(np);
return target_power;
}
snprintf(name, sizeof(name), "txpower-%cg", band);
- np = of_get_child_by_name(np, name);
- if (!np)
+ band_np = of_get_child_by_name(np, name);
+ of_node_put(np);
+ if (!band_np)
return target_power;
- np = mt76_find_channel_node(np, chan);
+ np = mt76_find_channel_node(band_np, chan);
+ of_node_put(band_np);
if (!np)
return target_power;
@@ -514,6 +519,8 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
ARRAY_SIZE(dest->path.ru_bf), val, len, target_power,
txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
+ of_node_put(np);
+
return max_power;
}
EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index cda5c5d51aab..c9762082f473 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -432,6 +432,7 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
INIT_LIST_HEAD(&phy->tx_list);
spin_lock_init(&phy->tx_lock);
INIT_DELAYED_WORK(&phy->roc_work, mt76_roc_complete_work);
+ INIT_DELAYED_WORK(&phy->scan_work, mt76_scan_work);
if ((void *)phy != hw->priv)
return 0;
@@ -729,7 +730,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
INIT_LIST_HEAD(&dev->txwi_cache);
INIT_LIST_HEAD(&dev->rxwi_cache);
dev->token_size = dev->drv->token_size;
- INIT_DELAYED_WORK(&dev->scan_work, mt76_scan_work);
spin_lock_init(&dev->scan_lock);
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
@@ -856,8 +856,8 @@ void mt76_reset_device(struct mt76_dev *dev)
continue;
wcid->sta = 0;
- mt76_wcid_cleanup(dev, wcid);
rcu_assign_pointer(dev->wcid[i], NULL);
+ mt76_wcid_cleanup(dev, wcid);
}
rcu_read_unlock();
@@ -1045,6 +1045,20 @@ void mt76_update_survey(struct mt76_phy *phy)
}
EXPORT_SYMBOL_GPL(mt76_update_survey);
+void mt76_phy_chandef_set(struct mt76_phy *phy,
+ struct cfg80211_chan_def *chandef)
+{
+ if (!chandef->chan)
+ return;
+
+ mt76_update_survey(phy);
+
+ phy->chandef = *chandef;
+ phy->main_chandef = *chandef;
+ phy->chan_state = mt76_channel_state(phy, chandef->chan);
+}
+EXPORT_SYMBOL_GPL(mt76_phy_chandef_set);
+
int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
bool offchannel)
{
@@ -1283,7 +1297,6 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
status->rate_idx = mstat.rate_idx;
status->nss = mstat.nss;
status->band = mstat.band;
- status->signal = mstat.signal;
status->chains = mstat.chains;
status->ampdu_reference = mstat.ampdu_ref;
status->device_timestamp = mstat.timestamp;
@@ -1471,6 +1484,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
struct ieee80211_hw *hw;
struct mt76_wcid *wcid = status->wcid;
u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
+ int signal;
bool ps;
hw = mt76_phy_hw(dev, status->phy_idx);
@@ -1488,8 +1502,9 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
- if (status->signal <= 0)
- ewma_signal_add(&wcid->rssi, -status->signal);
+ signal = mt76_rx_signal(status->chains, status->chain_signal);
+ if (signal <= 0 && signal > -128)
+ ewma_signal_add(&wcid->rssi, -signal);
wcid->inactive_count = 0;
@@ -1747,9 +1762,8 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
mt76_tx_status_lock(dev, &list);
mt76_tx_status_skb_get(dev, wcid, -1, &list);
- mt76_tx_status_unlock(dev, &list);
-
idr_destroy(&wcid->pktid);
+ mt76_tx_status_unlock(dev, &list);
/* Remove from sta_poll_list to prevent list corruption after reset.
* Without this, mt76_reset_device() reinitializes sta_poll_list but
@@ -2110,7 +2124,7 @@ void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif)
rcu_assign_pointer(mvif->link[0], NULL);
mt76_abort_scan(dev);
- if (mvif->roc_phy)
+ if (mvif->roc_phy && mvif->roc_phy->roc_vif == vif)
mt76_abort_roc(mvif->roc_phy);
}
EXPORT_SYMBOL_GPL(mt76_vif_cleanup);
@@ -2324,7 +2338,7 @@ void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb)
.bssid = hdr->addr3,
};
- mt76_scan_rx_beacon(phy->dev, phy->chandef.chan);
+ mt76_scan_rx_beacon(phy, phy->chandef.chan);
if (!phy->num_sta)
return;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 2ade848fe3bb..9e851dea71d9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -767,7 +767,6 @@ struct mt76_rx_status {
u8 amsdu:1, first_amsdu:1, last_amsdu:1;
u8 rate_idx;
u8 nss:5, band:3;
- s8 signal;
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
};
@@ -847,7 +846,6 @@ struct mt76_vif_link {
struct mt76_vif_data {
struct mt76_vif_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
- struct mt76_vif_link __rcu *offchannel_link;
struct mt76_phy *roc_phy;
u16 valid_links;
@@ -879,6 +877,15 @@ struct mt76_phy {
struct ieee80211_vif *roc_vif;
struct mt76_vif_link *roc_link;
+ struct delayed_work scan_work;
+ struct {
+ struct ieee80211_channel *chan;
+ struct mt76_vif_link *mlink;
+ int chan_idx;
+ bool beacon_wait;
+ bool beacon_received;
+ } scan;
+
struct mt76_chanctx *chanctx;
struct mt76_channel_state *chan_state;
@@ -1015,17 +1022,12 @@ struct mt76_dev {
u32 rxfilter;
- struct delayed_work scan_work;
spinlock_t scan_lock;
struct {
- struct cfg80211_scan_request *req;
- struct ieee80211_channel *chan;
+ struct ieee80211_scan_request *req;
struct ieee80211_vif *vif;
- struct mt76_vif_link *mlink;
- struct mt76_phy *phy;
- int chan_idx;
- bool beacon_wait;
- bool beacon_received;
+ unsigned long phy_mask;
+ bool aborted;
} scan;
#ifdef CONFIG_NL80211_TESTMODE
@@ -1398,6 +1400,11 @@ mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx)
return mt76_dev_phy(dev, phy_idx)->hw;
}
+static inline bool mt76_phy_scanning(struct mt76_phy *phy)
+{
+ return test_bit(phy->band_idx, &phy->dev->scan.phy_mask);
+}
+
static inline u8 *
mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t)
{
@@ -1555,6 +1562,8 @@ void mt76_sta_ps_transition(struct mt76_dev *dev, struct mt76_wcid *wcid,
bool mt76_has_tx_pending(struct mt76_phy *phy);
int mt76_update_channel(struct mt76_phy *phy);
void mt76_update_survey(struct mt76_phy *phy);
+void mt76_phy_chandef_set(struct mt76_phy *phy,
+ struct cfg80211_chan_def *chandef);
void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time);
int mt76_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey);
@@ -1623,7 +1632,7 @@ int mt76_get_rate(struct mt76_dev *dev,
int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req);
void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
-void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan);
+void mt76_scan_rx_beacon(struct mt76_phy *phy, struct ieee80211_channel *chan);
void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb);
void mt76_beacon_mon_check(struct mt76_phy *phy);
void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
index 477a9b7a8cf7..491c8c937031 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
@@ -34,7 +34,7 @@ mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb)
int idx;
u32 val;
- if (skb->len < MT_TXD_SIZE + sizeof(struct ieee80211_hdr))
+ if (skb->len < MT_TXD_SIZE + 2)
goto free;
val = le32_to_cpu(txd[1]);
@@ -52,6 +52,9 @@ mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb)
sta = container_of(priv, struct ieee80211_sta, drv_priv);
hdr = (struct ieee80211_hdr *)&skb->data[MT_TXD_SIZE];
+ if (skb->len < MT_TXD_SIZE + ieee80211_hdrlen(hdr->frame_control))
+ goto free;
+
hwq = wmm_queue_map[IEEE80211_AC_BE];
if (ieee80211_is_data_qos(hdr->frame_control)) {
tid = *ieee80211_get_qos_ctl(hdr) &
@@ -62,6 +65,19 @@ mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb)
} else if (ieee80211_is_data(hdr->frame_control)) {
skb_set_queue_mapping(skb, IEEE80211_AC_BE);
hwq = wmm_queue_map[IEEE80211_AC_BE];
+ } else if (ieee80211_is_back_req(hdr->frame_control)) {
+ struct ieee80211_bar *bar = (struct ieee80211_bar *)hdr;
+
+ if (skb->len < MT_TXD_SIZE + sizeof(*bar))
+ goto free;
+
+ tid = (le16_to_cpu(bar->control) &
+ IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
+ IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
+ tid &= IEEE80211_QOS_CTL_TAG1D_MASK;
+ qid = tid_to_ac[tid];
+ hwq = wmm_queue_map[qid];
+ skb_set_queue_mapping(skb, qid);
} else {
skb_pull(skb, MT_TXD_SIZE);
if (!ieee80211_is_bufferable_mmpdu(skb))
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
index 6a1475e3c899..83cffa94bc0e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
@@ -54,7 +54,7 @@ static int mt7615_config(void *data, u64 val)
int ret;
mt7615_mutex_acquire(dev);
- ret = mt76_connac_mcu_chip_config(&dev->mt76);
+ ret = mt76_connac_mcu_chip_config(&dev->mt76, "assert");
mt7615_mutex_release(dev);
return ret;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
index e437e088b2e9..665b0f1e939f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
@@ -541,8 +541,10 @@ int mt7615_register_ext_phy(struct mt7615_dev *dev)
mt7615_cap_dbdc_enable(dev);
mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7615_ops, MT_BAND1);
- if (!mphy)
- return -ENOMEM;
+ if (!mphy) {
+ ret = -ENOMEM;
+ goto error;
+ }
phy = mphy->priv;
phy->dev = dev;
@@ -572,7 +574,7 @@ int mt7615_register_ext_phy(struct mt7615_dev *dev)
ret = mt76_eeprom_override(mphy);
if (ret)
- return ret;
+ goto error_free_hw;
/* second phy can only handle 5 GHz */
mphy->cap.has_5ghz = true;
@@ -590,7 +592,14 @@ int mt7615_register_ext_phy(struct mt7615_dev *dev)
ret = mt76_register_phy(mphy, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
if (ret)
- ieee80211_free_hw(mphy->hw);
+ goto error_free_hw;
+
+ return 0;
+
+error_free_hw:
+ ieee80211_free_hw(mphy->hw);
+error:
+ mt7615_cap_dbdc_disable(dev);
return ret;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index 67f56e428d9a..175e847f49ef 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -254,8 +254,10 @@ static void mt7615_remove_interface(struct ieee80211_hw *hw,
mt7615_mutex_acquire(dev);
- mt7615_mcu_add_bss_info(phy, vif, NULL, false);
- mt7615_mcu_sta_add(phy, vif, NULL, false);
+ if (vif->type != NL80211_IFTYPE_MONITOR) {
+ mt7615_mcu_add_bss_info(phy, vif, NULL, false);
+ mt7615_mcu_sta_add(phy, vif, NULL, false);
+ }
mt76_testmode_reset(phy->mt76, true);
if (vif == phy->monitor_vif)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
index ff57ede87f71..aab4303fef65 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
@@ -145,6 +145,9 @@ int mt7615_mcu_parse_response(struct mt76_dev *mdev, int cmd,
}
rxd = (struct mt7615_mcu_rxd *)skb->data;
+ if (skb->len < sizeof(*rxd))
+ return -EMSGSIZE;
+
if (seq != rxd->seq)
return -EAGAIN;
@@ -152,9 +155,15 @@ int mt7615_mcu_parse_response(struct mt76_dev *mdev, int cmd,
skb_pull(skb, sizeof(*rxd) - 4);
ret = *skb->data;
} else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) {
+ if (skb->len < sizeof(*rxd) + (is_mt7663(mdev) ? 8 : 4))
+ return -EMSGSIZE;
skb_pull(skb, sizeof(*rxd));
+ if (is_mt7663(mdev))
+ skb_pull(skb, 4);
ret = le32_to_cpu(*(__le32 *)skb->data);
} else if (cmd == MCU_EXT_QUERY(RF_REG_ACCESS)) {
+ if (skb->len < sizeof(*rxd) + 8 + sizeof(__le32))
+ return -EMSGSIZE;
skb_pull(skb, sizeof(*rxd));
ret = le32_to_cpu(*(__le32 *)&skb->data[8]);
} else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) ||
@@ -165,12 +174,16 @@ int mt7615_mcu_parse_response(struct mt76_dev *mdev, int cmd,
cmd == MCU_UNI_CMD(SUSPEND)) {
struct mt76_connac_mcu_uni_event *event;
+ if (skb->len < sizeof(*rxd) + sizeof(*event))
+ return -EMSGSIZE;
skb_pull(skb, sizeof(*rxd));
event = (struct mt76_connac_mcu_uni_event *)skb->data;
ret = le32_to_cpu(event->status);
} else if (cmd == MCU_CE_QUERY(REG_READ)) {
struct mt76_connac_mcu_reg_event *event;
+ if (skb->len < sizeof(*rxd) + sizeof(*event))
+ return -EMSGSIZE;
skb_pull(skb, sizeof(*rxd));
event = (struct mt76_connac_mcu_reg_event *)skb->data;
ret = (int)le32_to_cpu(event->val);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
index a9178e077fd6..7d8580720cd8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
@@ -116,6 +116,7 @@ void mt7615_unregister_device(struct mt7615_dev *dev)
mcu_running = mt7615_wait_for_mcu_init(dev);
mt7615_unregister_ext_phy(dev);
+ cancel_work_sync(&dev->reset_work);
mt76_unregister_device(&dev->mt76);
if (mcu_running)
mt7615_mcu_exit(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
index bab7b91f14be..3629e3d9bf47 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
@@ -58,15 +58,22 @@ static void mt7663u_copy(struct mt76_dev *dev, u32 offset,
const void *data, int len)
{
struct mt76_usb *usb = &dev->usb;
- int ret, i = 0, batch_len;
const u8 *val = data;
+ int len_aligned;
+ int batch_len;
+ int copy_len;
+ int ret;
+ int i = 0;
- len = round_up(len, 4);
+ len_aligned = round_up(len, 4);
mutex_lock(&usb->usb_ctrl_mtx);
- while (i < len) {
- batch_len = min_t(int, usb->data_len, len - i);
- memcpy(usb->data, val + i, batch_len);
+ while (i < len_aligned) {
+ batch_len = min_t(int, usb->data_len, len_aligned - i);
+ copy_len = min_t(int, batch_len, len - i);
+ memcpy(usb->data, val + i, copy_len);
+ if (copy_len < batch_len)
+ memset(usb->data + copy_len, 0, batch_len - copy_len);
ret = __mt76u_vendor_request(dev, MT_VEND_WRITE_EXT,
USB_DIR_OUT | USB_TYPE_VENDOR,
(offset + i) >> 16, offset + i,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
index 8a8d608dd56e..8481eac8d783 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
@@ -600,6 +600,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
vif ? &vif->bss_conf : NULL,
beacon, multicast);
u32 val = MT_TXD6_FIXED_BW;
+ u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask);
/* hardware won't add HTC for mgmt/ctrl frame */
txwi[2] |= cpu_to_le32(MT_TXD2_HTC_VLD);
@@ -608,13 +609,18 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
txwi[6] |= cpu_to_le32(val);
txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
- if (!is_connac2(dev)) {
- u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask);
-
- if (!spe_idx)
- spe_idx = 24 + phy_idx;
- txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, spe_idx));
- }
+ /* Fixed-rate frames (all mgmt, so auth/assoc req too) otherwise
+ * carry spe_idx 0 and TX 1SS 1T on WF0. Fill spe_idx like the
+ * other connac generations do, but do NOT touch
+ * MT_TXD6_SPE_ID_IDX: on connac2 setting it points HW at the
+ * WTBL and the index below is ignored.
+ *
+ * spe_idx 0 is also a valid single-path index (WF0), so only
+ * take the duplicate fallback when a second path exists.
+ */
+ if (!spe_idx && hweight8(mphy->antenna_mask) > 1)
+ spe_idx = 24 + phy_idx;
+ txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, spe_idx));
txwi[7] &= ~cpu_to_le32(MT_TXD7_HW_AMSDU);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 2c49f3b493bd..9bdfff24b702 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -2041,13 +2041,13 @@ int mt76_connac_mcu_sched_scan_enable(struct mt76_phy *phy,
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_sched_scan_enable);
-int mt76_connac_mcu_chip_config(struct mt76_dev *dev)
+int mt76_connac_mcu_chip_config(struct mt76_dev *dev, const char *cmd)
{
struct mt76_connac_config req = {
.resp_type = 0,
};
- strscpy(req.data, "assert");
+ strscpy(req.data, cmd);
return mt76_mcu_send_msg(dev, MCU_CE_CMD(CHIP_CONFIG),
&req, sizeof(req), false);
@@ -2056,14 +2056,11 @@ EXPORT_SYMBOL_GPL(mt76_connac_mcu_chip_config);
int mt76_connac_mcu_set_deep_sleep(struct mt76_dev *dev, bool enable)
{
- struct mt76_connac_config req = {
- .resp_type = 0,
- };
+ char cmd[16];
- snprintf(req.data, sizeof(req.data), "KeepFullPwr %d", !enable);
+ snprintf(cmd, sizeof(cmd), "KeepFullPwr %d", !enable);
- return mt76_mcu_send_msg(dev, MCU_CE_CMD(CHIP_CONFIG),
- &req, sizeof(req), false);
+ return mt76_connac_mcu_chip_config(dev, cmd);
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_deep_sleep);
@@ -2487,9 +2484,7 @@ int mt76_connac_mcu_update_gtk_rekey(struct ieee80211_hw *hw,
gtk_tlv->rekey_mode = 2;
gtk_tlv->option = 1;
- rcu_read_lock();
- ieee80211_iter_keys_rcu(hw, vif, mt76_connac_mcu_key_iter, gtk_tlv);
- rcu_read_unlock();
+ ieee80211_iter_keys(hw, vif, mt76_connac_mcu_key_iter, gtk_tlv);
memcpy(gtk_tlv->kek, key->kek, NL80211_KEK_LEN);
memcpy(gtk_tlv->kck, key->kck, NL80211_KCK_LEN);
@@ -3127,7 +3122,7 @@ mt76_connac_mcu_send_phy_ram_firmware(struct mt76_dev *dev,
err = mt76_connac_mcu_init_download(dev, addr, len, mode);
if (err) {
dev_err(dev->dev,
- "The request to dowload PHY firmware failed.\n");
+ "The request to download PHY firmware failed.\n");
return err;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 0ededa569e9a..74e37d722710 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1357,9 +1357,11 @@ enum {
MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
MCU_UNI_CMD_EXT_EEPROM_CTRL = 0x74,
+ MCU_UNI_CMD_FACT_CAL = 0x7c,
MCU_UNI_CMD_RADIO_STATUS = 0x80,
MCU_UNI_CMD_MLD = 0x82,
MCU_UNI_CMD_SDO = 0x88,
+ MCU_UNI_CMD_AXIDMA = 0xa2,
};
enum {
@@ -2098,7 +2100,7 @@ void mt76_connac_mcu_set_suspend_iter(void *priv, u8 *mac,
int mt76_connac_sta_state_dp(struct mt76_dev *dev,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state);
-int mt76_connac_mcu_chip_config(struct mt76_dev *dev);
+int mt76_connac_mcu_chip_config(struct mt76_dev *dev, const char *cmd);
int mt76_connac_mcu_set_deep_sleep(struct mt76_dev *dev, bool enable);
void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb,
struct mt76_connac_coredump *coredump);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index 21f8b1e64101..158aa356b710 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -302,10 +302,14 @@ mt76x02_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD;
fallthrough;
case MT_PHY_TYPE_HT:
+ if (idx > 31)
+ return -EINVAL;
txrate->flags |= IEEE80211_TX_RC_MCS;
txrate->idx = idx;
break;
case MT_PHY_TYPE_VHT:
+ if ((idx & 0xf) > 9)
+ return -EINVAL;
txrate->flags |= IEEE80211_TX_RC_VHT_MCS;
txrate->idx = idx;
break;
@@ -493,18 +497,21 @@ mt76x02_mac_fill_tx_status(struct mt76x02_dev *dev, struct mt76x02_sta *msta,
first_rate = st->rate & ~MT_PKTID_RATE;
first_rate |= st->pktid & MT_PKTID_RATE;
- mt76x02_mac_process_tx_rate(&rate[0], first_rate,
- dev->mphy.chandef.chan->band);
+ if (mt76x02_mac_process_tx_rate(&rate[0], first_rate,
+ dev->mphy.chandef.chan->band))
+ return;
} else if (rate[0].idx < 0) {
if (!msta)
return;
- mt76x02_mac_process_tx_rate(&rate[0], msta->wcid.tx_info,
- dev->mphy.chandef.chan->band);
+ if (mt76x02_mac_process_tx_rate(&rate[0], msta->wcid.tx_info,
+ dev->mphy.chandef.chan->band))
+ return;
}
- mt76x02_mac_process_tx_rate(&last_rate, st->rate,
- dev->mphy.chandef.chan->band);
+ if (mt76x02_mac_process_tx_rate(&last_rate, st->rate,
+ dev->mphy.chandef.chan->band))
+ return;
for (i = 0; i < ARRAY_SIZE(info->status.rates); i++) {
retry--;
@@ -809,9 +816,13 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb,
len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
pn_len = FIELD_GET(MT_RXINFO_PN_LEN, rxinfo);
if (pn_len) {
- int offset = ieee80211_get_hdrlen_from_skb(skb) + pad_len;
- u8 *data = skb->data + offset;
+ unsigned int offset = ieee80211_get_hdrlen_from_skb(skb) + pad_len;
+ u8 *data;
+ if (offset + 8 > skb->len)
+ return -EINVAL;
+
+ data = skb->data + offset;
status->iv[0] = data[7];
status->iv[1] = data[6];
status->iv[2] = data[5];
@@ -831,6 +842,9 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb,
}
}
+ if (pad_len + ieee80211_get_hdrlen_from_skb(skb) > skb->len)
+ return -EINVAL;
+
mt76x02_remove_hdr_pad(skb, pad_len);
if ((rxinfo & MT_RXINFO_BA) && !(rxinfo & MT_RXINFO_NULL))
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
index 968c73e06a5f..6f931d483bd6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
@@ -90,7 +90,7 @@ __mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb,
MT_MCU_MSG_TYPE_CMD;
ret = mt76x02u_skb_dma_info(skb, CPU_TX_PORT, info);
if (ret)
- return ret;
+ goto out;
ret = mt76u_bulk_msg(dev, skb->data, skb->len, NULL, 500,
MT_EP_OUT_INBAND_CMD);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
index 6c7273e5b0bc..db079b274a93 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
@@ -345,11 +345,12 @@ static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev)
mt7915_coredump_fw_stack(dev, dump, exception);
/* gather memory content */
- dump_mem = (struct mt7915_coredump_mem *)(buf + sofar);
- dump_mem->len = crash_data->memdump_buf_len;
- if (coredump_memdump && crash_data->memdump_buf_len)
+ if (coredump_memdump && crash_data->memdump_buf_len) {
+ dump_mem = (struct mt7915_coredump_mem *)(buf + sofar);
+ dump_mem->len = crash_data->memdump_buf_len;
memcpy(dump_mem->data, crash_data->memdump_buf,
crash_data->memdump_buf_len);
+ }
mutex_unlock(&dev->dump_mutex);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
index fe7b29ebc0bf..145d14a1012d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
@@ -111,6 +111,12 @@ mt7915_eeprom_load_default(struct mt7915_dev *dev)
goto out;
}
+ if (fw->size < mt7915_eeprom_size(dev)) {
+ dev_err(dev->mt76.dev, "Invalid default bin size\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
memcpy(eeprom, fw->data, mt7915_eeprom_size(dev));
dev->flash_mode = true;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index c8e389db63a7..358c45acf79b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -200,11 +200,17 @@ static void mt7915_unregister_thermal(struct mt7915_phy *phy)
{
struct wiphy *wiphy = phy->mt76->hw->wiphy;
+ if (phy->tzone) {
+ devm_thermal_of_zone_unregister(phy->dev->mt76.dev, phy->tzone);
+ phy->tzone = NULL;
+ }
+
if (!phy->cdev)
return;
sysfs_remove_link(&wiphy->dev.kobj, "cooling_device");
thermal_cooling_device_unregister(phy->cdev);
+ phy->cdev = NULL;
}
static int mt7915_thermal_init(struct mt7915_phy *phy)
@@ -238,8 +244,8 @@ static int mt7915_thermal_init(struct mt7915_phy *phy)
if (IS_ERR(phy->tzone)) {
if (PTR_ERR(phy->tzone) != -ENODEV)
dev_warn(phy->dev->mt76.dev,
- "failed to register thermal zone: %ld\n",
- PTR_ERR(phy->tzone));
+ "failed to register thermal zone %d: %ld\n",
+ phy->mt76->band_idx, PTR_ERR(phy->tzone));
phy->tzone = NULL;
}
@@ -248,7 +254,12 @@ static int mt7915_thermal_init(struct mt7915_phy *phy)
hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
mt7915_hwmon_groups);
- return PTR_ERR_OR_ZERO(hwmon);
+ if (IS_ERR(hwmon)) {
+ mt7915_unregister_thermal(phy);
+ return PTR_ERR(hwmon);
+ }
+
+ return 0;
}
static void mt7915_led_set_config(struct led_classdev *led_cdev,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index a03d89aabe80..556f8948abac 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -273,9 +273,9 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
mt7915_init_bitrate_mask(vif);
memset(&mvif->cap, -1, sizeof(mvif->cap));
+ rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
mt7915_mcu_add_bss_info(phy, vif, true);
mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, true);
- rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
mutex_unlock(&dev->mt76.mutex);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
index a5a70d8e8544..62bf33347e9e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
@@ -237,7 +237,7 @@ static int mt7921_chip_reset(void *data, u64 val)
default:
/* Collect the core dump before reset wifisys. */
mt792x_mutex_acquire(dev);
- ret = mt76_connac_mcu_chip_config(&dev->mt76);
+ ret = mt76_connac_mcu_chip_config(&dev->mt76, "assert");
mt792x_mutex_release(dev);
break;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index e69978184f68..6f5e31489e2d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -188,7 +188,7 @@ mt7921_mac_fill_rx(struct mt792x_dev *dev, struct sk_buff *skb)
u16 seq_ctrl = 0;
__le16 fc = 0;
u8 mode = 0;
- int i, idx;
+ int idx;
memset(status, 0, sizeof(*status));
@@ -368,15 +368,6 @@ mt7921_mac_fill_rx(struct mt792x_dev *dev, struct sk_buff *skb)
status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1);
status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1);
status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v1);
- status->signal = -128;
- for (i = 0; i < hweight8(mphy->antenna_mask); i++) {
- if (!(status->chains & BIT(i)) ||
- status->chain_signal[i] >= 0)
- continue;
-
- status->signal = max(status->signal,
- status->chain_signal[i]);
- }
}
amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4);
@@ -831,11 +822,14 @@ void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
u16 idx;
idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
+
+ rcu_read_lock();
wcid = __mt76_wcid_ptr(mdev, idx);
sta = wcid_to_sta(wcid);
if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE)))
mt76_connac2_tx_check_aggr(sta, txwi);
+ rcu_read_unlock();
skb_pull(e->skb, headroom);
mt76_tx_complete_skb(mdev, e->wcid, e->skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 68a059504e83..09574d4417cd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -652,9 +652,9 @@ static int mt7921_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
}
if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
- ieee80211_iterate_active_interfaces(hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7921_sniffer_interface_iter, dev);
+ ieee80211_iterate_active_interfaces_mtx(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7921_sniffer_interface_iter, dev);
}
out:
@@ -668,12 +668,8 @@ static void mt7921_configure_filter(struct ieee80211_hw *hw,
unsigned int *total_flags,
u64 multicast)
{
-#define MT7921_FILTER_FCSFAIL BIT(2)
-#define MT7921_FILTER_CONTROL BIT(5)
-#define MT7921_FILTER_OTHER_BSS BIT(6)
-#define MT7921_FILTER_ENABLE BIT(31)
-
struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt792x_phy *phy = mt792x_hw_phy(hw);
u32 flags = MT7921_FILTER_ENABLE;
#define MT7921_FILTER(_fif, _type) do { \
@@ -685,6 +681,8 @@ static void mt7921_configure_filter(struct ieee80211_hw *hw,
MT7921_FILTER(FIF_CONTROL, CONTROL);
MT7921_FILTER(FIF_OTHER_BSS, OTHER_BSS);
+ phy->rxfilter = flags;
+
mt792x_mutex_acquire(dev);
mt7921_mcu_set_rxfilter(dev, flags, 0, 0);
mt792x_mutex_release(dev);
@@ -865,6 +863,12 @@ int mt7921_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
if (sta->aid > MT7921_MAX_AID)
return -ENOENT;
+ if (mvif->roc_join_held && !sta->tdls &&
+ (ev == MT76_STA_EVENT_AUTHORIZE || ev == MT76_STA_EVENT_DISASSOC)) {
+ mvif->roc_join_held = false;
+ mt7921_abort_roc(mvif->phy, mvif);
+ }
+
if (ev != MT76_STA_EVENT_ASSOC)
return 0;
@@ -906,6 +910,7 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
mvif->wep_sta = NULL;
+ mvif->roc_join_held = false;
ewma_rssi_init(&mvif->bss_conf.rssi);
if (!sta->tdls)
mt76_connac_mcu_uni_add_bss(&dev->mphy, vif,
@@ -1156,10 +1161,10 @@ static int mt7921_suspend(struct ieee80211_hw *hw,
mt792x_mutex_acquire(dev);
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- ieee80211_iterate_active_interfaces(hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7921_mcu_set_suspend_iter,
- &dev->mphy);
+ ieee80211_iterate_active_interfaces_mtx(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7921_mcu_set_suspend_iter,
+ &dev->mphy);
mt792x_mutex_release(dev);
@@ -1174,10 +1179,10 @@ static int mt7921_resume(struct ieee80211_hw *hw)
mt792x_mutex_acquire(dev);
set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- ieee80211_iterate_active_interfaces(hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt76_connac_mcu_set_suspend_iter,
- &dev->mphy);
+ ieee80211_iterate_active_interfaces_mtx(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt76_connac_mcu_set_suspend_iter,
+ &dev->mphy);
ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
MT792x_WATCHDOG_TIME);
@@ -1423,6 +1428,7 @@ mt7921_change_chanctx(struct ieee80211_hw *hw,
} else {
mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx);
}
+ mt76_phy_chandef_set(mvif->phy->mt76, &ctx->def);
mt792x_mutex_release(phy->dev);
}
@@ -1447,6 +1453,16 @@ static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw,
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ /* Keep the join ROC until the station is authorized, so that it
+ * covers the 4-way handshake. mt7921_mac_sta_event() releases it.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION && info->success &&
+ (info->subtype == IEEE80211_STYPE_ASSOC_REQ ||
+ info->subtype == IEEE80211_STYPE_REASSOC_REQ)) {
+ mvif->roc_join_held = true;
+ return;
+ }
+
mt7921_abort_roc(mvif->phy, mvif);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 40546005c743..25638c68c468 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -3,6 +3,7 @@
#include <linux/fs.h>
#include <linux/firmware.h>
+#include <linux/of.h>
#include "mt7921.h"
#include "regd.h"
#include "mcu.h"
@@ -676,6 +677,10 @@ int mt7921_run_firmware(struct mt792x_dev *dev)
return err;
set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+ err = mt7921_mcu_set_thermal_protect(dev);
+ if (err)
+ return err;
+
return mt7921_mcu_fw_log_2_host(dev, 1);
}
EXPORT_SYMBOL_GPL(mt7921_run_firmware);
@@ -1231,7 +1236,7 @@ int mt7921_mcu_config_sniffer(struct mt792x_vif *vif,
.len = cpu_to_le16(sizeof(req.tlv)),
.control_ch = chandef->chan->hw_value,
.center_ch = ieee80211_frequency_to_channel(freq1),
- .drop_err = 1,
+ .drop_err = !(vif->phy->rxfilter & MT7921_FILTER_FCSFAIL),
},
};
if (chandef->chan->band < ARRAY_SIZE(ch_band))
@@ -1359,6 +1364,7 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
.acpi_conf = mt792x_acpi_get_flags(&dev->phy),
.mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2),
};
+ struct device_node *np;
int ret, valid_cnt = 0;
u32 buf_len = 0;
u8 *pos;
@@ -1368,8 +1374,11 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
if (dev->phy.chip_cap & MT792x_CHIP_CAP_CLC_EVT_EN)
req.cap |= CLC_CAP_EVT_EN;
- if (mt76_find_power_limits_node(&dev->mt76))
+
+ np = mt76_find_power_limits_node(&dev->mt76);
+ if (np)
req.cap |= CLC_CAP_DTS_EN;
+ of_node_put(np);
buf_len = le32_to_cpu(clc->len) - sizeof(*clc);
pos = clc->data;
@@ -1479,6 +1488,19 @@ int mt7921_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy, u8 action)
sizeof(req), action ? true : false);
}
+#define MT7921_THERMAL_PROT_LOW_TEMP 105
+#define MT7921_THERMAL_PROT_HIGH_TEMP 115
+
+int mt7921_mcu_set_thermal_protect(struct mt792x_dev *dev)
+{
+ char cmd[64];
+
+ snprintf(cmd, sizeof(cmd), "DowngradeTxStreamTemp %d %d",
+ MT7921_THERMAL_PROT_LOW_TEMP, MT7921_THERMAL_PROT_HIGH_TEMP);
+
+ return mt76_connac_mcu_chip_config(&dev->mt76, cmd);
+}
+
int mt7921_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
u8 bit_op, u32 bit_map)
{
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
index fb8654b7c5e7..3baf6094612f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
@@ -7,6 +7,11 @@
#include "../mt792x.h"
#include "regs.h"
+#define MT7921_FILTER_FCSFAIL BIT(2)
+#define MT7921_FILTER_CONTROL BIT(5)
+#define MT7921_FILTER_OTHER_BSS BIT(6)
+#define MT7921_FILTER_ENABLE BIT(31)
+
#define MT7921_MAX_AID 20
#define MT7921_TX_RING_SIZE 2048
@@ -324,6 +329,7 @@ int mt7921_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
int mt7921_mcu_config_sniffer(struct mt792x_vif *vif,
struct ieee80211_chanctx_conf *ctx);
int mt7921_mcu_get_temperature(struct mt792x_phy *phy);
+int mt7921_mcu_set_thermal_protect(struct mt792x_dev *dev);
int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
enum mt76_txq_id qid, struct mt76_wcid *wcid,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index 56a914aaa81e..f87c44f2a8b6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -375,6 +375,7 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
}
map->rx.wm2_complete_mask = 0;
+ map->rx.all_complete_mask &= ~MT_INT_RX_DONE_WM2;
dev->irq_map = map;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
index 98b3469ac597..d5c33a0d409c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
@@ -57,7 +57,9 @@ int mt7921e_mac_reset(struct mt792x_dev *dev)
{
int i, err;
- mt792xe_mcu_drv_pmctrl(dev);
+ err = mt792xe_mcu_drv_pmctrl(dev);
+ if (err)
+ return err;
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
index fcdeb65cd8ef..699617d9e48f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
@@ -28,6 +28,21 @@ bool mt7921_regd_clc_supported(struct mt792x_dev *dev)
return true;
}
+static bool
+mt7921_regd_dt_has_channel(struct device_node *band_np,
+ struct ieee80211_channel *ch)
+{
+ struct device_node *chan_np;
+
+ if (!band_np)
+ return false;
+
+ chan_np = mt76_find_channel_node(band_np, ch);
+ of_node_put(chan_np);
+
+ return !!chan_np;
+}
+
static void
mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
{
@@ -43,14 +58,14 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
sband = wiphy->bands[NL80211_BAND_5GHZ];
if (!sband)
- return;
+ goto out;
band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
cfreq = ch->center_freq;
- if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
+ if (np && !mt7921_regd_dt_has_channel(band_np, ch)) {
ch->flags |= IEEE80211_CHAN_DISABLED;
continue;
}
@@ -59,17 +74,18 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
if (IS_UNII_INVALID(0, 5845, 5925))
ch->flags |= IEEE80211_CHAN_DISABLED;
}
+ of_node_put(band_np);
sband = wiphy->bands[NL80211_BAND_6GHZ];
if (!sband)
- return;
+ goto out;
band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
cfreq = ch->center_freq;
- if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
+ if (np && !mt7921_regd_dt_has_channel(band_np, ch)) {
ch->flags |= IEEE80211_CHAN_DISABLED;
continue;
}
@@ -81,6 +97,10 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
IS_UNII_INVALID(4, 6875, 7125))
ch->flags |= IEEE80211_CHAN_DISABLED;
}
+ of_node_put(band_np);
+
+out:
+ of_node_put(np);
}
int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index f9dcc0bba393..33539121a15c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -6,5 +6,5 @@ obj-$(CONFIG_MT7925U) += mt7925u.o
mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
-mt7925e-y := pci.o pci_mac.o pci_mcu.o
+mt7925e-y := pci.o pci_mac.o pci_mcu.o cached_cal.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c
new file mode 100644
index 000000000000..0209606e1a47
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#include <linux/dma-mapping.h>
+#include "../mt792x.h"
+#include "cached_cal.h"
+
+static void mt7925_cal_cache_calc_layout(struct mt7925_cal_cache_layout *l)
+{
+ u32 hdr = sizeof(struct mt7925_fact_cal_buf_info);
+
+ l->common_stride = hdr + MT7925_FACT_CAL_LEN_COM;
+ l->group_stride = hdr + MT7925_FACT_CAL_LEN_GRP;
+ l->channel_stride = hdr + MT7925_FACT_CAL_LEN_CH;
+
+ l->common_off = 0;
+ l->group_off = l->common_off +
+ l->common_stride * MT7925_FACT_CAL_COMMON_BAND_NUM;
+ l->channel_off = l->group_off +
+ l->group_stride * MT7925_FACT_CAL_GROUP_NUM;
+ l->mapping_off = l->channel_off +
+ l->channel_stride * MT7925_FACT_CAL_CH_NUM_ALL;
+ l->total = l->mapping_off +
+ sizeof(struct mt7925_fact_cal_mapping_tbl);
+}
+
+static u32 mt7925_cal_cache_region_size(void)
+{
+ struct mt7925_cal_cache_layout l;
+
+ mt7925_cal_cache_calc_layout(&l);
+
+ return l.total;
+}
+
+int mt7925_axidma_alloc(struct mt792x_dev *dev)
+{
+ u32 total_size;
+ dma_addr_t dma_addr;
+ void *va;
+
+ if (dev->cached_cal.va)
+ return 0;
+
+ total_size = mt7925_cal_cache_region_size();
+
+ va = dma_alloc_coherent(dev->mt76.dev, total_size, &dma_addr,
+ GFP_KERNEL);
+ if (!va)
+ return -ENOMEM;
+
+ dev->cached_cal.va = va;
+ dev->cached_cal.dma_addr = dma_addr;
+ dev->cached_cal.size = total_size;
+
+ return 0;
+}
+
+void mt7925_axidma_free(struct mt792x_dev *dev)
+{
+ if (!dev->cached_cal.va)
+ return;
+
+ dma_free_coherent(dev->mt76.dev, dev->cached_cal.size,
+ dev->cached_cal.va, dev->cached_cal.dma_addr);
+
+ memset(&dev->cached_cal, 0, sizeof(dev->cached_cal));
+}
+
+int mt7925_mcu_send_axidma_info(struct mt792x_dev *dev)
+{
+ struct mt7925_axidma_info_req *req;
+ u32 remap_base, pa_offset;
+ int ret;
+
+ if (!dev->cached_cal.va)
+ return -EINVAL;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ remap_base = (u32)(dev->cached_cal.dma_addr >> 16);
+ pa_offset = (u32)(dev->cached_cal.dma_addr & 0xFFFF);
+
+ req->remap.tag = cpu_to_le16(MT7925_AXIDMA_TAG_REMAP);
+ req->remap.len = cpu_to_le16(sizeof(req->remap));
+ req->remap.remap_addr = cpu_to_le32(remap_base);
+
+ req->fact_cal.tag = cpu_to_le16(MT7925_AXIDMA_TAG_FACT_CAL);
+ req->fact_cal.len = cpu_to_le16(sizeof(req->fact_cal));
+ req->fact_cal.host_addr_offset = cpu_to_le32(pa_offset);
+ req->fact_cal.size = cpu_to_le32(dev->cached_cal.size);
+ req->fact_cal.bound = cpu_to_le32(pa_offset + dev->cached_cal.size - 1);
+
+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(AXIDMA),
+ req, sizeof(*req), true);
+
+ kfree(req);
+
+ if (ret)
+ dev_err(dev->mt76.dev,
+ "failed to send AXI DMA info: %d\n", ret);
+
+ return ret;
+}
+
+static bool mt7925_cal_cache_read_slot(struct mt792x_dev *dev, u32 region_off,
+ u32 stride, u32 idx,
+ u32 *cal_param, u32 *total_buf_num)
+{
+ const struct mt7925_fact_cal_buf_info *bi;
+ u32 off = region_off + stride * idx;
+
+ if (!dev->cached_cal.va || off + sizeof(*bi) > dev->cached_cal.size)
+ return false;
+
+ bi = (const struct mt7925_fact_cal_buf_info *)
+ ((const u8 *)dev->cached_cal.va + off);
+ if (cal_param)
+ *cal_param = le32_to_cpu(bi->cal_param);
+ if (total_buf_num)
+ *total_buf_num = le32_to_cpu(bi->total_buf_num);
+ return true;
+}
+
+static void mt7925_fact_cal_build_mapping_tbl(struct mt792x_dev *dev,
+ struct mt7925_fact_cal_mapping_tbl *tbl)
+{
+ dma_addr_t common_pa, group_pa, channel_pa, mapping_pa;
+ u32 hdr = sizeof(struct mt7925_fact_cal_buf_info);
+ dma_addr_t base = dev->cached_cal.dma_addr;
+ struct mt7925_cal_cache_layout l;
+
+ mt7925_cal_cache_calc_layout(&l);
+
+ common_pa = base + l.common_off;
+ group_pa = base + l.group_off;
+ channel_pa = base + l.channel_off;
+ mapping_pa = base + l.mapping_off;
+
+ memset(tbl, 0, sizeof(*tbl));
+
+ tbl->common.phy_addr_h = cpu_to_le32(upper_32_bits(common_pa));
+ tbl->common.phy_addr_l = cpu_to_le32(lower_32_bits(common_pa));
+ tbl->common.offset = cpu_to_le32(l.common_stride);
+
+ tbl->group.phy_addr_h = cpu_to_le32(upper_32_bits(group_pa));
+ tbl->group.phy_addr_l = cpu_to_le32(lower_32_bits(group_pa));
+ tbl->group.offset = cpu_to_le32(l.group_stride);
+
+ tbl->channel.phy_addr_h = cpu_to_le32(upper_32_bits(channel_pa));
+ tbl->channel.phy_addr_l = cpu_to_le32(lower_32_bits(channel_pa));
+ tbl->channel.offset = cpu_to_le32(l.channel_stride);
+
+ if (dev->cached_cal.reused) {
+ u32 i, cal_param, total_buf_num;
+
+ for (i = 0; i < MT7925_FACT_CAL_COMMON_BAND_NUM; i++)
+ if (mt7925_cal_cache_read_slot(dev, l.common_off,
+ l.common_stride, i,
+ &cal_param, NULL))
+ tbl->common.band[i] = cal_param & 0xff;
+
+ for (i = 0; i < MT7925_FACT_CAL_GROUP_NUM; i++)
+ if (mt7925_cal_cache_read_slot(dev, l.group_off,
+ l.group_stride, i,
+ &cal_param, NULL))
+ tbl->group.group[i] = cal_param & 0xff;
+
+ for (i = 0; i < MT7925_FACT_CAL_CH_NUM_ALL; i++)
+ if (mt7925_cal_cache_read_slot(dev, l.channel_off,
+ l.channel_stride, i,
+ &cal_param,
+ &total_buf_num)) {
+ tbl->channel.cal_param[i] =
+ cpu_to_le32(cal_param);
+ tbl->channel.total_buf_num[i] =
+ total_buf_num & 0xff;
+ }
+ }
+
+ tbl->buf_info_size = cpu_to_le32(hdr);
+ tbl->phy_addr_h = cpu_to_le32(upper_32_bits(mapping_pa));
+ tbl->phy_addr_l = cpu_to_le32(lower_32_bits(mapping_pa));
+}
+
+static int mt7925_mcu_fact_cal_rapid_set(struct mt792x_dev *dev, u8 cal_type,
+ u32 seq_num, bool done,
+ const void *data, u32 data_len)
+{
+ struct mt7925_fact_cal_req *req;
+ int ret;
+
+ if (data_len > MT7925_FACT_CAL_DATA_BUF_LEN)
+ return -EINVAL;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->set.tag = cpu_to_le16(MT7925_FACT_CAL_TAG_RAPID_SET);
+ req->set.len = cpu_to_le16(sizeof(req->set));
+ req->set.seq_num = cpu_to_le32(seq_num);
+ req->set.buf_data_len = cpu_to_le32(data_len);
+ req->set.cal_type = cal_type;
+ req->set.done = done;
+ if (data && data_len)
+ memcpy(req->set.buf_data, data, data_len);
+
+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(FACT_CAL),
+ req, sizeof(*req), true);
+ kfree(req);
+
+ return ret;
+}
+
+int mt7925_mcu_send_fact_cal_mapping_tbl(struct mt792x_dev *dev)
+{
+ struct mt7925_fact_cal_mapping_tbl *tbl;
+ __le32 cfg[MT7925_FACT_CAL_BUF_CFG_LEN / sizeof(__le32)] = {0};
+ u32 seq = 0, off = 0, left;
+ int ret;
+
+ if (!dev->cached_cal.va)
+ return -EINVAL;
+
+ tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
+ if (!tbl)
+ return -ENOMEM;
+
+ mt7925_fact_cal_build_mapping_tbl(dev, tbl);
+
+ cfg[1] = cpu_to_le32(sizeof(*tbl));
+ cfg[2] = cpu_to_le32(MT7925_FACT_CAL_DATA_BUF_NUM_MAX);
+ ret = mt7925_mcu_fact_cal_rapid_set(dev, MT7925_FACT_CAL_TYPE_MAPPING_TBL,
+ seq, false, cfg, sizeof(cfg));
+ if (ret) {
+ dev_err(dev->mt76.dev,
+ "fact-cal mapping tbl header send failed (seq=%u): %d\n",
+ seq, ret);
+ goto out;
+ }
+
+ left = sizeof(*tbl);
+ while (left) {
+ u32 chunk = min_t(u32, left, MT7925_FACT_CAL_DATA_BUF_LEN);
+
+ ret = mt7925_mcu_fact_cal_rapid_set(dev,
+ MT7925_FACT_CAL_TYPE_MAPPING_TBL,
+ ++seq, false,
+ (u8 *)tbl + off, chunk);
+ if (ret) {
+ dev_err(dev->mt76.dev,
+ "fact-cal mapping tbl data send failed (seq=%u, off=%u, chunk=%u): %d\n",
+ seq, off, chunk, ret);
+ goto out;
+ }
+
+ off += chunk;
+ left -= chunk;
+ }
+
+ ret = mt7925_mcu_fact_cal_rapid_set(dev, MT7925_FACT_CAL_TYPE_MAPPING_TBL,
+ ++seq, true, NULL, 0);
+ if (ret) {
+ dev_err(dev->mt76.dev,
+ "fact-cal mapping tbl done send failed (seq=%u): %d\n",
+ seq, ret);
+ goto out;
+ }
+out:
+ kfree(tbl);
+
+ return ret;
+}
+
+int mt7925_mcu_fact_cal_set_bypass(struct mt792x_dev *dev, bool enable)
+{
+ struct {
+ u8 _rsv[4];
+
+ struct {
+ __le16 tag;
+ __le16 len;
+ __le32 data;
+ } __packed update_flag;
+ } req = {
+ .update_flag = {
+ .tag = cpu_to_le16(MT7925_FACT_CAL_TAG_UPDATE_FLAG),
+ .len = cpu_to_le16(sizeof(req.update_flag)),
+ .data = cpu_to_le32(enable ? 1 : 0),
+ },
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(FACT_CAL),
+ &req, sizeof(req), true);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h
new file mode 100644
index 000000000000..6572513b7646
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#ifndef __CACHED_CAL_H
+#define __CACHED_CAL_H
+
+enum mt7925_axidma_info_tag {
+ MT7925_AXIDMA_TAG_REMAP = 0,
+ MT7925_AXIDMA_TAG_FACT_CAL = 1,
+ MT7925_AXIDMA_TAG_NUM
+};
+
+enum mt7925_fact_cal_tag {
+ MT7925_FACT_CAL_TAG_TRIGGER = 2,
+ MT7925_FACT_CAL_TAG_UPDATE_FLAG = 3,
+ MT7925_FACT_CAL_TAG_RAPID_GET = 4,
+ MT7925_FACT_CAL_TAG_RAPID_SET = 5,
+};
+
+enum mt7925_fact_cal_type {
+ MT7925_FACT_CAL_TYPE_COMMON = 0,
+ MT7925_FACT_CAL_TYPE_GROUP = 1,
+ MT7925_FACT_CAL_TYPE_CHANNEL = 2,
+ MT7925_FACT_CAL_TYPE_MAPPING_TBL = 4,
+};
+
+#define MT7925_FACT_CAL_DATA_BUF_LEN 1400
+#define MT7925_FACT_CAL_BUF_CFG_LEN 16
+#define MT7925_FACT_CAL_DATA_BUF_NUM_MAX 4
+#define MT7925_FACT_CAL_BUF_CFG_U32_LEN 4
+#define MT7925_FACT_CAL_DATA_MAX_BUF_LEN \
+ (MT7925_FACT_CAL_DATA_BUF_NUM_MAX * MT7925_FACT_CAL_BUF_CFG_U32_LEN)
+
+#define MT7925_FACT_CAL_COMMON_BAND_NUM 2
+#define MT7925_FACT_CAL_GROUP_NUM 35
+#define MT7925_FACT_CAL_CH_NUM_2G 14
+#define MT7925_FACT_CAL_CH_NUM_5G 68
+#define MT7925_FACT_CAL_CH_NUM_6G 109
+#define MT7925_FACT_CAL_CH_NUM_ALL \
+ (MT7925_FACT_CAL_CH_NUM_2G + MT7925_FACT_CAL_CH_NUM_5G + \
+ MT7925_FACT_CAL_CH_NUM_6G)
+
+#define MT7925_FACT_CAL_LEN_COM 600
+#define MT7925_FACT_CAL_LEN_GRP 4500
+#define MT7925_FACT_CAL_LEN_CH 2800
+
+struct mt7925_fact_cal_buf_info {
+ __le32 cal_type;
+ __le32 cal_param;
+ __le32 buf_seq_num;
+ __le32 total_buf_num;
+ __le32 buf_data_len;
+ u8 valid;
+ u8 done;
+ u8 rsv[2];
+ __le32 buf_cfg_info[MT7925_FACT_CAL_DATA_MAX_BUF_LEN];
+} __packed;
+
+static_assert(sizeof(struct mt7925_fact_cal_buf_info) == 88,
+ "buf_info must match the MT7928 FW (88 bytes) or it rejects the mapping table");
+
+struct mt7925_fact_cal_common_map {
+ __le32 phy_addr_h;
+ __le32 phy_addr_l;
+ __le32 offset;
+ u8 band[MT7925_FACT_CAL_COMMON_BAND_NUM];
+ u8 rsv[2];
+} __packed;
+
+struct mt7925_fact_cal_group_map {
+ __le32 phy_addr_h;
+ __le32 phy_addr_l;
+ __le32 offset;
+ u8 group[MT7925_FACT_CAL_GROUP_NUM];
+ u8 rsv;
+} __packed;
+
+struct mt7925_fact_cal_channel_map {
+ __le32 phy_addr_h;
+ __le32 phy_addr_l;
+ __le32 offset;
+ __le32 cal_param[MT7925_FACT_CAL_CH_NUM_ALL];
+ u8 total_buf_num[MT7925_FACT_CAL_CH_NUM_ALL];
+ u8 rsv;
+} __packed;
+
+struct mt7925_fact_cal_mapping_tbl {
+ struct mt7925_fact_cal_common_map common;
+ struct mt7925_fact_cal_group_map group;
+ struct mt7925_fact_cal_channel_map channel;
+ __le32 buf_info_size;
+ __le32 phy_addr_h;
+ __le32 phy_addr_l;
+} __packed;
+
+static_assert(sizeof(struct mt7925_fact_cal_mapping_tbl) == 1044,
+ "mapping table must match the MT7928 FW (1044 bytes) or the FW parse corrupts");
+
+struct mt7925_cal_cache_layout {
+ u32 common_off, group_off, channel_off, mapping_off;
+ u32 common_stride, group_stride, channel_stride;
+ u32 total;
+};
+
+struct mt7925_axidma_info_req {
+ u8 _rsv[4];
+
+ struct {
+ __le16 tag;
+ __le16 len;
+ __le32 remap_addr;
+ __le32 reserved;
+ } __packed remap;
+
+ struct {
+ __le16 tag;
+ __le16 len;
+ __le32 host_addr_offset;
+ __le32 size;
+ __le32 bound;
+ __le32 reserved;
+ } __packed fact_cal;
+} __packed;
+
+struct mt7925_fact_cal_req {
+ u8 _rsv[4];
+
+ struct {
+ __le16 tag;
+ __le16 len;
+ __le32 data;
+ __le32 seq_num;
+ __le32 buf_data_len;
+ u8 cal_type;
+ u8 done;
+ u8 rsv[2];
+ u8 buf_data[MT7925_FACT_CAL_DATA_BUF_LEN];
+ } __packed set;
+} __packed;
+
+int mt7925_axidma_alloc(struct mt792x_dev *dev);
+
+void mt7925_axidma_free(struct mt792x_dev *dev);
+
+int mt7925_mcu_send_axidma_info(struct mt792x_dev *dev);
+
+int mt7925_mcu_send_fact_cal_mapping_tbl(struct mt792x_dev *dev);
+
+int mt7925_mcu_fact_cal_set_bypass(struct mt792x_dev *dev, bool enable);
+
+#endif /* __CACHED_CAL_H */
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index cd22fcc021b1..8c3049b66a85 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -270,6 +270,8 @@ int mt7925_register_device(struct mt792x_dev *dev)
INIT_WORK(&dev->reset_work, mt7925_mac_reset_work);
INIT_WORK(&dev->init_work, mt7925_init_work);
+ INIT_WORK(&dev->nan_deferred_work, mt7925_nan_deferred_work);
+ spin_lock_init(&dev->nan_deferred_lock);
INIT_WORK(&dev->phy.roc_work, mt7925_roc_work);
timer_setup(&dev->phy.roc_timer, mt792x_roc_timer, 0);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index 3b82c9de26c5..7af996abe294 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1503,7 +1503,7 @@ mt7925_vif_connect_iter(void *priv, u8 *mac,
true, NULL);
mt7925_mcu_sta_update(dev, NULL, vif,
&mvif->sta.deflink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true);
}
}
@@ -1688,11 +1688,14 @@ void mt7925_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
u16 idx;
idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
+
+ rcu_read_lock();
wcid = __mt76_wcid_ptr(mdev, idx);
sta = wcid_to_sta(wcid);
if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE)))
mt7925_tx_check_aggr(sta, e->skb, wcid);
+ rcu_read_unlock();
skb_pull(e->skb, headroom);
mt76_tx_complete_skb(mdev, e->wcid, e->skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 549f9c06b363..15d95ad32636 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -840,9 +840,9 @@ static int mt7925_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
}
if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
- ieee80211_iterate_active_interfaces(hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7925_sniffer_interface_iter, dev);
+ ieee80211_iterate_active_interfaces_mtx(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_sniffer_interface_iter, dev);
}
out:
@@ -856,11 +856,8 @@ static void mt7925_configure_filter(struct ieee80211_hw *hw,
unsigned int *total_flags,
u64 multicast)
{
-#define MT7925_FILTER_FCSFAIL BIT(2)
-#define MT7925_FILTER_CONTROL BIT(5)
-#define MT7925_FILTER_OTHER_BSS BIT(6)
-#define MT7925_FILTER_ENABLE BIT(31)
struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt792x_phy *phy = mt792x_hw_phy(hw);
u32 flags = MT7925_FILTER_ENABLE;
#define MT7925_FILTER(_fif, _type) do { \
@@ -872,6 +869,8 @@ static void mt7925_configure_filter(struct ieee80211_hw *hw,
MT7925_FILTER(FIF_CONTROL, CONTROL);
MT7925_FILTER(FIF_OTHER_BSS, OTHER_BSS);
+ phy->rxfilter = flags;
+
mt792x_mutex_acquire(dev);
mt7925_mcu_set_rxfilter(dev, flags, 0, 0);
mt792x_mutex_release(dev);
@@ -964,6 +963,21 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
link_conf = mt792x_vif_to_bss_conf(vif, link_id);
+ /* NAN_DATA (NDI) peers skip association - fill link_sta caps
+ * from sband and push BSS_INFO with correct phymode + RLM.
+ */
+ if (vif->type == NL80211_IFTYPE_NAN_DATA) {
+ struct ieee80211_chanctx_conf *nan_ctx;
+
+ nan_ctx = mt7925_nan_seed_link_sta(dev, link_sta);
+ mconf->mt76.ctx = nan_ctx;
+
+ ret = mt7925_mcu_add_bss_info(&dev->phy, nan_ctx,
+ link_conf, link_sta, true);
+ if (ret)
+ goto out_pm;
+ }
+
/* should update bss info before STA add */
if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
struct mt792x_link_sta *mlink_bc;
@@ -991,7 +1005,7 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
link_sta == mlink->pri_link) {
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
if (ret)
goto out_pm;
} else if (ieee80211_vif_is_mld(vif) &&
@@ -1013,19 +1027,19 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif,
pri_mlink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, mlink);
if (ret)
goto out_pm;
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, mlink);
if (ret)
goto out_pm;
} else {
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
if (ret)
goto out_pm;
}
@@ -1233,7 +1247,7 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac));
mt7925_mcu_sta_update(dev, link_sta, vif, mlink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, NULL);
mt792x_mutex_release(dev);
}
@@ -1243,6 +1257,13 @@ int mt7925_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct ieee80211_link_sta *link_sta = &sta->deflink;
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+ if (mvif->roc_join_held && !sta->tdls &&
+ (ev == MT76_STA_EVENT_AUTHORIZE || ev == MT76_STA_EVENT_DISASSOC)) {
+ mvif->roc_join_held = false;
+ mt7925_abort_roc(mvif->phy, &mvif->bss_conf);
+ }
switch (ev) {
case MT76_STA_EVENT_ASSOC:
@@ -1293,7 +1314,7 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
mt76_connac_pm_wake(&dev->mphy, &dev->pm);
mt7925_mcu_sta_update(dev, link_sta, vif, mlink, false,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
mt7925_mac_wtbl_update(dev, mlink->wcid.idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -1438,6 +1459,7 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
if (vif->type == NL80211_IFTYPE_STATION) {
mvif->wep_sta = NULL;
+ mvif->roc_join_held = false;
ewma_rssi_init(&mvif->bss_conf.rssi);
}
@@ -1717,10 +1739,10 @@ static int mt7925_suspend(struct ieee80211_hw *hw,
mt792x_mutex_acquire(dev);
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- ieee80211_iterate_active_interfaces(hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7925_mcu_set_suspend_iter,
- &dev->mphy);
+ ieee80211_iterate_active_interfaces_mtx(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_mcu_set_suspend_iter,
+ &dev->mphy);
mt792x_mutex_release(dev);
@@ -1735,10 +1757,10 @@ static int mt7925_resume(struct ieee80211_hw *hw)
mt792x_mutex_acquire(dev);
set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- ieee80211_iterate_active_interfaces(hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7925_mcu_set_suspend_iter,
- &dev->mphy);
+ ieee80211_iterate_active_interfaces_mtx(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_mcu_set_suspend_iter,
+ &dev->mphy);
ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
MT792x_WATCHDOG_TIME);
@@ -1964,7 +1986,7 @@ mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
err = mt7925_mcu_sta_update(dev, NULL, vif,
&mvif->sta.deflink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
out:
mt792x_mutex_release(dev);
@@ -2063,6 +2085,7 @@ mt7925_change_chanctx(struct ieee80211_hw *hw,
link_conf, ctx);
}
}
+ mt76_phy_chandef_set(mvif->phy->mt76, &ctx->def);
mt792x_mutex_release(phy->dev);
}
@@ -2089,6 +2112,19 @@ static void mt7925_mgd_complete_tx(struct ieee80211_hw *hw,
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ /* Keep the join ROC until the station is authorized, so that it
+ * covers the 4-way handshake. mt7925_mac_sta_event() releases it.
+ * On MLD, the active ROC is the MLO ROC, which must be released
+ * before the link activation work sets its own.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
+ info->success &&
+ (info->subtype == IEEE80211_STYPE_ASSOC_REQ ||
+ info->subtype == IEEE80211_STYPE_REASSOC_REQ)) {
+ mvif->roc_join_held = true;
+ return;
+ }
+
mt7925_abort_roc(mvif->phy, &mvif->bss_conf);
}
@@ -2108,7 +2144,7 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ASSOC) {
mt7925_mcu_sta_update(dev, NULL, vif,
&mvif->sta.deflink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, NULL);
mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
if (ieee80211_vif_is_mld(vif))
@@ -2411,6 +2447,7 @@ static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw,
mconf->mt76.ctx = ctx;
mctx->bss_conf = mconf;
+ mt76_phy_chandef_set(mvif->phy->mt76, &ctx->def);
mutex_unlock(&dev->mt76.mutex);
return 0;
@@ -2599,26 +2636,18 @@ static int mt7925_start_nan(struct ieee80211_hw *hw,
cfg80211_chandef_create(&link_conf->chanreq.oper, chan,
NL80211_CHAN_NO_HT);
- err = mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf,
- NULL, true);
+ err = mt7925_mcu_add_bss_info(&dev->phy, NULL,
+ link_conf, NULL, true);
if (err < 0)
goto out;
- dev->nan_vif = vif;
-
- err = mt7925_nan_set_nmi_addr(dev, vif->addr);
- if (err)
- goto rollback_bss;
-
err = mt7925_nan_enable(vif, dev, conf);
- if (err)
- goto rollback_bss;
-
- goto out;
+ if (err) {
+ mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, NULL, false);
+ goto out;
+ }
-rollback_bss:
- dev->nan_vif = NULL;
- mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, NULL, false);
+ err = mt7925_nan_update_phy_setting(dev);
out:
mt792x_mutex_release(dev);
@@ -2633,6 +2662,13 @@ static int mt7925_stop_nan(struct ieee80211_hw *hw,
struct mt792x_dev *dev = mt792x_hw_dev(hw);
int err, ret;
+ /* Drop a deferred event queued just before stop so a stale cluster_id
+ * cannot leak into a restart; the work re-checks liveness anyway.
+ */
+ spin_lock_bh(&dev->nan_deferred_lock);
+ dev->nan_deferred_pending = 0;
+ spin_unlock_bh(&dev->nan_deferred_lock);
+
mt792x_mutex_acquire(dev);
err = mt7925_nan_disable(vif, dev);
@@ -2642,9 +2678,6 @@ static int mt7925_stop_nan(struct ieee80211_hw *hw,
if (!err)
err = ret;
- if (dev->nan_vif == vif)
- dev->nan_vif = NULL;
-
mt792x_mutex_release(dev);
return err;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index aeb017516186..26cdb3f0a98a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1914,11 +1914,9 @@ mt7925_mcu_sta_phy_tlv(struct sk_buff *skb,
phy = (struct sta_rec_phy *)tlv;
if (mt7925_vif_is_nan(vif)) {
- enum nl80211_band band = chandef->chan ? chandef->chan->band
- : NL80211_BAND_2GHZ;
phy->phy_type = PHY_TYPE_BIT_OFDM | PHY_TYPE_BIT_ERP;
phy->phy_type |= mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif,
- band,
+ NL80211_BAND_5GHZ,
link_sta);
} else {
phy->phy_type = mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif,
@@ -2003,7 +2001,11 @@ mt7925_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb,
ra_info = (struct sta_rec_ra_info *)tlv;
if (mt7925_vif_is_nan(vif))
- band = chandef->chan ? chandef->chan->band : NL80211_BAND_2GHZ;
+ /* NAN is OFDM-only per spec; borrow the 5 GHz band lookup to
+ * avoid PHY_TYPE_BIT_HR_DSSS/CCK bits being added to phy_type.
+ * NAN interfaces have no chanctx, so chandef->chan is always NULL.
+ */
+ band = NL80211_BAND_5GHZ;
else
band = chandef->chan->band;
@@ -2063,14 +2065,18 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct mt792x_bss_conf *mconf,
- struct mt792x_link_sta *mlink)
+ struct mt792x_link_sta *mlink,
+ struct mt792x_link_sta *pending)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
struct mt792x_dev *dev = mvif->phy->dev;
+ unsigned long valid = msta->valid_links;
struct mt792x_bss_conf *mconf_pri;
struct sta_rec_mld *mld;
+ unsigned int link_id;
struct tlv *tlv;
+ u8 max_links;
u8 cnt = 0;
/* Primary link always uses driver's deflink WCID. */
@@ -2099,11 +2105,38 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
mld->link[cnt].wlan_id = cpu_to_le16(msta->deflink.wcid.idx);
mld->link[cnt++].bss_idx = mconf_pri->mt76.idx;
- /* Optionally encode the currently-updated secondary link. */
- if (mlink && mlink != &msta->deflink && mconf) {
- mld->secondary_id = cpu_to_le16(mlink->wcid.idx);
- mld->link[cnt].wlan_id = cpu_to_le16(mlink->wcid.idx);
- mld->link[cnt++].bss_idx = mconf->mt76.idx;
+ max_links = ARRAY_SIZE(mld->link);
+
+ /* A link being added is not set in msta->valid_links yet */
+ if (pending && pending != &msta->deflink)
+ valid |= BIT(pending->wcid.link_id);
+
+ for_each_set_bit(link_id, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt792x_link_sta *mlink_sec;
+ struct mt792x_bss_conf *mconf_sec;
+
+ if (cnt == max_links)
+ break;
+
+ if (link_id == msta->deflink_id)
+ continue;
+
+ mlink_sec = mt792x_sta_to_link(msta, link_id);
+ if (!mlink_sec && pending && link_id == pending->wcid.link_id)
+ mlink_sec = pending;
+ if (!mlink_sec || mlink_sec == &msta->deflink)
+ continue;
+
+ mconf_sec = rcu_dereference_protected(mvif->link_conf[link_id],
+ lockdep_is_held(&dev->mt76.mutex));
+ if (!mconf_sec)
+ continue;
+
+ if (cnt == 1)
+ mld->secondary_id = cpu_to_le16(mlink_sec->wcid.idx);
+
+ mld->link[cnt].wlan_id = cpu_to_le16(mlink_sec->wcid.idx);
+ mld->link[cnt++].bss_idx = mconf_sec->mt76.idx;
}
mld->link_num = cnt;
@@ -2122,7 +2155,8 @@ mt7925_mcu_sta_remove_tlv(struct sk_buff *skb)
static int
mt7925_mcu_sta_cmd(struct mt76_phy *phy,
- struct mt76_sta_cmd_info *info)
+ struct mt76_sta_cmd_info *info,
+ struct mt792x_link_sta *pending)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv;
struct mt76_dev *dev = phy->dev;
@@ -2163,7 +2197,7 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
if (info->state != MT76_STA_INFO_STATE_NONE) {
mt7925_mcu_sta_mld_tlv(skb, info->vif,
info->link_sta->sta,
- mconf, mlink);
+ mconf, mlink, pending);
mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta);
}
@@ -2188,7 +2222,8 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct mt792x_link_sta *mlink,
bool enable,
- enum mt76_sta_info_state state)
+ enum mt76_sta_info_state state,
+ struct mt792x_link_sta *pending)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
int rssi = -ewma_rssi_read(&mvif->bss_conf.rssi);
@@ -2206,7 +2241,7 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
info.wcid = &mlink->wcid;
info.newly = state != MT76_STA_INFO_STATE_ASSOC;
- return mt7925_mcu_sta_cmd(&dev->mphy, &info);
+ return mt7925_mcu_sta_cmd(&dev->mphy, &info, pending);
}
int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
@@ -2354,7 +2389,7 @@ int mt7925_mcu_config_sniffer(struct mt792x_vif *vif,
.len = cpu_to_le16(sizeof(req.tlv)),
.control_ch = chandef->chan->hw_value,
.center_ch = ieee80211_frequency_to_channel(freq1),
- .drop_err = 1,
+ .drop_err = !(vif->phy->rxfilter & MT7925_FILTER_FCSFAIL),
},
};
@@ -2392,10 +2427,13 @@ mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev,
u8 pad[3];
} __packed hdr;
struct bcn_content_tlv {
+ /* DW 0 */
__le16 tag;
__le16 len;
+ /* DW 1 */
__le16 tim_ie_pos;
__le16 csa_ie_pos;
+ /* DW 2 */
__le16 bcc_ie_pos;
/* 0: disable beacon offload
* 1: enable beacon offload
@@ -2406,8 +2444,9 @@ mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev,
* 1: only cap field IE
*/
u8 type;
+ /* DW 3~130 */
__le16 pkt_len;
- u8 pkt[512];
+ u8 pkt[510];
} __packed beacon_tlv;
} req = {
.hdr = {
@@ -2847,7 +2886,7 @@ mt7925_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt792x_phy *phy,
bmc = (struct bss_rate_tlv *)tlv;
if (mt7925_vif_is_nan(vif))
- band = chandef->chan ? chandef->chan->band : NL80211_BAND_2GHZ;
+ band = NL80211_BAND_5GHZ;
else
band = chandef->chan->band;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 321e732347f2..636efe038392 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -7,6 +7,11 @@
#include "../mt792x.h"
#include "regs.h"
+#define MT7925_FILTER_FCSFAIL BIT(2)
+#define MT7925_FILTER_CONTROL BIT(5)
+#define MT7925_FILTER_OTHER_BSS BIT(6)
+#define MT7925_FILTER_ENABLE BIT(31)
+
#define MT7925_BEACON_RATES_TBL 25
#define MT7925_TX_RING_SIZE 2048
@@ -286,7 +291,8 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct mt792x_link_sta *mlink,
bool enable,
- enum mt76_sta_info_state state);
+ enum mt76_sta_info_state state,
+ struct mt792x_link_sta *pending);
int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag);
int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_bss_conf *bss_conf);
int mt7925_mcu_set_eeprom(struct mt792x_dev *dev);
@@ -353,6 +359,7 @@ int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
int mt7925e_mac_reset(struct mt792x_dev *dev);
int mt7925e_mcu_init(struct mt792x_dev *dev);
+void mt7925e_axidma_cal_cache_init(struct mt792x_dev *dev);
void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data);
void mt7928_mac_add_txs_msg(struct mt792x_dev *dev, void *evt);
void mt7925_set_runtime_pm(struct mt792x_dev *dev);
@@ -361,6 +368,7 @@ void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,
void mt7925_connac_mcu_set_suspend_iter(void *priv, u8 *mac,
struct ieee80211_vif *vif);
void mt7925_set_ipv6_ns_work(struct work_struct *work);
+void mt7925_nan_deferred_work(struct work_struct *work);
int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
bool enable);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
index d260e803d056..ce2571832c08 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
@@ -32,8 +32,6 @@ static void mt7925_nan_set_5g_channel(struct mt792x_dev *dev,
if (!mt7925_regd_is_valid_channel(dev, NL80211_BAND_5GHZ, chan))
return;
- req->config_support_5g = 1;
- req->support_5g_val = 1;
req->config_5g_channel = 1;
if (chan->hw_value == NAN_5G_LOW_DISC_CHANNEL)
@@ -44,73 +42,6 @@ static void mt7925_nan_set_5g_channel(struct mt792x_dev *dev,
req->channel_5g_val = cpu_to_le32(ch5g);
}
-static void mt7925_nan_set_2g_support(struct mt7925_nan_enable_req_tlv *req,
- struct cfg80211_nan_conf *conf)
-{
- if (!conf->band_cfgs[NL80211_BAND_2GHZ].chan)
- return;
-
- req->config_2dot4g_support = 1;
- req->support_2dot4g_val = 1;
-}
-
-static void mt7925_nan_set_cluster_id(struct mt7925_nan_enable_req_tlv *req,
- const u8 *cluster_id)
-{
- if (!cluster_id)
- return;
-
- req->cluster_high = cpu_to_le16(cluster_id[4] | cluster_id[5] << 8);
- req->cluster_low = cpu_to_le16((u16)cluster_id[3]);
-}
-
-static void mt7925_nan_set_dw_interval(struct mt7925_nan_enable_req_tlv *req,
- struct cfg80211_nan_conf *conf)
-{
- if (conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval > 0) {
- req->config_dw.config_2dot4g_dw_band = 1;
- req->config_dw.dw_2dot4g_interval_val =
- cpu_to_le32(conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval);
- }
-
- if (conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval > 0) {
- req->config_dw.config_5g_dw_band = 1;
- req->config_dw.dw_5g_interval_val =
- cpu_to_le32(conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval);
- }
-}
-
-static void mt7925_nan_set_disc_beacon(struct mt7925_nan_enable_req_tlv *req,
- struct cfg80211_nan_conf *conf)
-{
- if (conf->discovery_beacon_interval > 0) {
- req->config_2dot4g_beacons = true;
- req->beacon_2dot4g_val = conf->discovery_beacon_interval;
- }
-}
-
-static void mt7925_nan_set_rssi_thresholds(struct mt7925_nan_enable_req_tlv *req,
- struct cfg80211_nan_conf *conf)
-{
- if (conf->band_cfgs[NL80211_BAND_2GHZ].chan) {
- req->config_2dot4g_rssi_close = 1;
- req->rssi_close_2dot4g_val =
- abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close);
- req->config_2dot4g_rssi_middle = 1;
- req->rssi_middle_2dot4g_val =
- abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle);
- }
-
- if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) {
- req->config_5g_rssi_close = 1;
- req->rssi_close_5g_val =
- abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close);
- req->config_5g_rssi_middle = 1;
- req->rssi_middle_5g_val =
- abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle);
- }
-}
-
static void mt7925_nan_set_scan_params(struct mt7925_nan_enable_req_tlv *req,
struct cfg80211_nan_conf *conf)
{
@@ -144,101 +75,6 @@ mt7925_nan_update_conf(struct mt792x_vif *mvif,
memcpy(mvif->nan.conf.cluster_id, conf->cluster_id, ETH_ALEN);
}
-int mt7925_nan_set_nmi_addr(struct mt792x_dev *dev, const u8 *addr)
-{
- struct mt76_dev *mdev;
- struct {
- u8 rsv[4];
- struct mt7925_nan_nmi_addr_tlv nmi_addr_tlv;
- } nmi_cmd = {
- .rsv = { 0 },
- .nmi_addr_tlv = {
- .tag = cpu_to_le16(NAN_UNI_CMD_CHANGE_NMI_ADDRESS),
- .len = cpu_to_le16(sizeof(struct mt7925_nan_nmi_addr_tlv)),
- },
- };
- int ret;
-
- if (!dev || !addr)
- return -EINVAL;
-
- if (is_zero_ether_addr(addr) || is_multicast_ether_addr(addr)) {
- dev_err(dev->mt76.dev, "NAN: invalid NMI address %pM\n", addr);
- return -EINVAL;
- }
-
- mdev = &dev->mt76;
- memcpy(nmi_cmd.nmi_addr_tlv.nmi_addr, addr, ETH_ALEN);
-
- ret = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nmi_cmd,
- sizeof(nmi_cmd), true);
-
- return ret;
-}
-
-int mt7925_nan_enable(struct ieee80211_vif *vif,
- struct mt792x_dev *dev,
- struct cfg80211_nan_conf *conf)
-{
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- struct mt76_dev *mdev = &dev->mt76;
- struct {
- u8 rsv[4];
- struct mt7925_nan_enable_req_tlv nan_req_tlv;
- } nan_cmd = {
- .rsv = { 0 },
- .nan_req_tlv = {
- .tag = cpu_to_le16(NAN_UNI_CMD_ENABLE_REQUEST),
- .len = cpu_to_le16(sizeof(struct mt7925_nan_enable_req_tlv)),
- .config_random_factor_force = 0,
- .random_factor_force_val = 0,
- .config_hop_count_force = 0,
- .hop_count_force_val = 0,
- },
- };
- struct mt7925_nan_enable_req_tlv *p_nan_req_tlv = &nan_cmd.nan_req_tlv;
- int ret;
-
- if (!vif || !dev || !conf)
- return -EINVAL;
-
- p_nan_req_tlv->master_pref = conf->master_pref;
-
- mt7925_nan_set_2g_support(p_nan_req_tlv, conf);
- mt7925_nan_set_5g_channel(dev, p_nan_req_tlv, conf);
- mt7925_nan_set_cluster_id(p_nan_req_tlv, conf->cluster_id);
- mt7925_nan_set_dw_interval(p_nan_req_tlv, conf);
- mt7925_nan_set_disc_beacon(p_nan_req_tlv, conf);
- mt7925_nan_set_rssi_thresholds(p_nan_req_tlv, conf);
- mt7925_nan_set_scan_params(p_nan_req_tlv, conf);
-
- mt7925_nan_update_conf(mvif, conf);
-
- ret = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true);
-
- return ret;
-}
-
-int mt7925_nan_disable(struct ieee80211_vif *vif, struct mt792x_dev *dev)
-{
- struct mt76_dev *mdev = &dev->mt76;
- struct {
- u8 rsv[4];
- struct tlv nan_dis_tlv;
- } nan_cmd = {
- .rsv = { 0 },
- .nan_dis_tlv = {
- .tag = cpu_to_le16(NAN_UNI_CMD_DISABLE_REQUEST),
- .len = cpu_to_le16(sizeof(struct tlv)),
- },
- };
-
- if (!dev)
- return -EINVAL;
-
- return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true);
-}
-
static int
mt7925_nan_mp_tlv(struct sk_buff *skb, u8 master_pref)
{
@@ -255,9 +91,6 @@ mt7925_nan_mp_tlv(struct sk_buff *skb, u8 master_pref)
mp_tlv = (struct mt7925_nan_master_preference_tlv *)tlv;
- if (master_pref > NAN_MAX_MASTER_PREFERENCE)
- return 0;
-
mp_tlv->master_preference = master_pref;
return 0;
@@ -357,6 +190,213 @@ mt7925_nan_sync_rssi_tlv(struct sk_buff *skb, struct cfg80211_nan_conf *conf)
return 0;
}
+/* FW rate set bit definitions (matches FW wlan_def_cmm.h) */
+#define NAN_RATE_SET_BIT_1M BIT(0)
+#define NAN_RATE_SET_BIT_2M BIT(1)
+#define NAN_RATE_SET_BIT_5_5M BIT(2)
+#define NAN_RATE_SET_BIT_11M BIT(3)
+#define NAN_RATE_SET_BIT_6M BIT(6)
+#define NAN_RATE_SET_BIT_9M BIT(7)
+#define NAN_RATE_SET_BIT_12M BIT(8)
+#define NAN_RATE_SET_BIT_18M BIT(9)
+#define NAN_RATE_SET_BIT_24M BIT(10)
+#define NAN_RATE_SET_BIT_36M BIT(11)
+#define NAN_RATE_SET_BIT_48M BIT(12)
+#define NAN_RATE_SET_BIT_54M BIT(13)
+
+#define NAN_RATE_SET_ALL_A (NAN_RATE_SET_BIT_6M | NAN_RATE_SET_BIT_9M | \
+ NAN_RATE_SET_BIT_12M | NAN_RATE_SET_BIT_18M | \
+ NAN_RATE_SET_BIT_24M | NAN_RATE_SET_BIT_36M | \
+ NAN_RATE_SET_BIT_48M | NAN_RATE_SET_BIT_54M)
+
+/* 5G basic: 6M+12M+24M (OFDM) */
+#define NAN_BASIC_RATE_SET_5G (NAN_RATE_SET_BIT_6M | NAN_RATE_SET_BIT_12M | \
+ NAN_RATE_SET_BIT_24M)
+
+/* GF_MODE_DISALLOWED = 2, RIFS_MODE_DISALLOWED = 1 */
+#define NAN_GF_MODE_DISALLOWED 2
+#define NAN_RIFS_MODE_DISALLOWED 1
+
+int mt7925_nan_update_phy_setting(struct mt792x_dev *dev)
+{
+ struct mt76_phy *mphy = &dev->mphy;
+ struct ieee80211_supported_band *sband_5g;
+ struct mt7925_nan_phy_setting *phy;
+ struct {
+ u8 rsv[4];
+ struct mt7925_nan_update_phy_setting_tlv tlv;
+ } req = {};
+
+ sband_5g = mphy->hw->wiphy->bands[NL80211_BAND_5GHZ];
+
+ req.tlv.tag = cpu_to_le16(NAN_UNI_CMD_UPDATE_PHY_SETTING);
+ req.tlv.len = cpu_to_le16(sizeof(req.tlv));
+
+ /* 2G: ERP + HT (no CCK/HR_DSSS - NAN uses OFDM only) */
+ phy = &req.tlv.phy_2g;
+ phy->phy_type_set = PHY_TYPE_BIT_ERP | PHY_TYPE_BIT_HT;
+ phy->non_ht_basic_phy_type = PHY_TYPE_ERP_INDEX;
+ phy->use_short_preamble = 1;
+ phy->use_short_slot_time = 1;
+ phy->operational_rate_set = cpu_to_le16(NAN_RATE_SET_ALL_A);
+ phy->bss_basic_rate_set = cpu_to_le16(NAN_BASIC_RATE_SET_5G);
+ phy->gf_operation_mode = cpu_to_le32(NAN_GF_MODE_DISALLOWED);
+ phy->rifs_operation_mode = cpu_to_le32(NAN_RIFS_MODE_DISALLOWED);
+
+ /* 5G: OFDM + HT + VHT (NAN_MODE_11A) */
+ phy = &req.tlv.phy_5g;
+ phy->phy_type_set = PHY_TYPE_BIT_OFDM | PHY_TYPE_BIT_HT |
+ PHY_TYPE_BIT_VHT;
+ phy->non_ht_basic_phy_type = PHY_TYPE_OFDM_INDEX;
+ phy->use_short_preamble = 1;
+ phy->use_short_slot_time = 1;
+ phy->operational_rate_set = cpu_to_le16(NAN_RATE_SET_ALL_A);
+ phy->bss_basic_rate_set = cpu_to_le16(NAN_BASIC_RATE_SET_5G);
+ phy->gf_operation_mode = cpu_to_le32(NAN_GF_MODE_DISALLOWED);
+ phy->rifs_operation_mode = cpu_to_le32(NAN_RIFS_MODE_DISALLOWED);
+
+ /* VHT basic MCS set from sband capability */
+ if (sband_5g && sband_5g->vht_cap.vht_supported)
+ phy->vht_basic_mcs_set =
+ sband_5g->vht_cap.vht_mcs.rx_mcs_map;
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(NAN),
+ &req, sizeof(req), true);
+}
+
+struct ieee80211_chanctx_conf *
+mt7925_nan_seed_link_sta(struct mt792x_dev *dev,
+ struct ieee80211_link_sta *link_sta)
+{
+ struct ieee80211_supported_band *sband_2g, *sband_5g;
+ struct ieee80211_chanctx_conf *nan_ctx = NULL;
+ struct ieee80211_vif *nan_vif = dev->nan_vif;
+
+ /* Fill HT cap from 2G sband */
+ sband_2g = dev->mphy.hw->wiphy->bands[NL80211_BAND_2GHZ];
+ sband_5g = dev->mphy.hw->wiphy->bands[NL80211_BAND_5GHZ];
+ if (sband_2g)
+ link_sta->ht_cap = sband_2g->ht_cap;
+
+ link_sta->sta->wme = true;
+ link_sta->rx_nss = hweight8(dev->mphy.antenna_mask);
+
+ /* Get chanctx from NAN schedule.
+ * Prefer 5G committed slot for wider BW (VHT), fallback
+ * to first valid slot if no 5G data slot is scheduled.
+ */
+ if (nan_vif) {
+ struct ieee80211_nan_channel **slots =
+ nan_vif->cfg.nan_sched.schedule;
+ int i;
+
+ for (i = 0; i < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; i++) {
+ struct ieee80211_chanctx_conf *ctx;
+
+ if (!slots[i] || IS_ERR(slots[i]) ||
+ !slots[i]->chanctx_conf)
+ continue;
+
+ ctx = slots[i]->chanctx_conf;
+ if (!nan_ctx)
+ nan_ctx = ctx;
+ if (ctx->def.chan->band == NL80211_BAND_5GHZ) {
+ nan_ctx = ctx;
+ break;
+ }
+ }
+ }
+
+ /* Capability describes what the device can do and must not be
+ * filtered by the current schedule - firmware gates the VHT rate
+ * mode per the data schedule and re-derives it on schedule
+ * change, which only works if the caps are present up front.
+ */
+ if (sband_5g)
+ link_sta->vht_cap = sband_5g->vht_cap;
+
+ /* Bandwidth here is the capability ceiling, not the operating
+ * width - the per-slot operating bandwidth follows the current
+ * slot channel via the firmware RLM sync, so deriving it from
+ * the schedule at STA-add time would cap a later 5 GHz schedule
+ * at the bring-up width.
+ */
+ link_sta->bandwidth = IEEE80211_STA_RX_BW_80;
+
+ return nan_ctx;
+}
+
+int mt7925_nan_enable(struct ieee80211_vif *vif,
+ struct mt792x_dev *dev,
+ struct cfg80211_nan_conf *conf)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ struct mt76_dev *mdev = &dev->mt76;
+ struct mt7925_nan_common_hdr *hdr;
+ struct mt7925_nan_enable_req_tlv *req;
+ struct sk_buff *skb;
+
+ if (!vif || !dev || !conf)
+ return -EINVAL;
+
+ skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_ENABLE_MAX_SIZE);
+ if (!skb)
+ return -ENOMEM;
+
+ hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr));
+ memset(hdr, 0, sizeof(*hdr));
+
+ /* Set cluster id before joining cluster */
+ if (mt7925_nan_cluster_id_tlv(skb, conf->cluster_id)) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ /* NAN enable request tlv */
+ req = (struct mt7925_nan_enable_req_tlv *)
+ mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_ENABLE_REQUEST,
+ sizeof(*req));
+ if (!req) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ req->master_pref = conf->master_pref;
+
+ mt7925_nan_set_5g_channel(dev, req, conf);
+ mt7925_nan_set_scan_params(req, conf);
+
+ if (mt7925_nan_dw_tlv(skb, conf) ||
+ mt7925_nan_sync_rssi_tlv(skb, conf)) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ mt7925_nan_update_conf(mvif, conf);
+
+ return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), true);
+}
+
+int mt7925_nan_disable(struct ieee80211_vif *vif, struct mt792x_dev *dev)
+{
+ struct mt76_dev *mdev = &dev->mt76;
+ struct {
+ u8 rsv[4];
+ struct tlv nan_dis_tlv;
+ } nan_cmd = {
+ .rsv = { 0 },
+ .nan_dis_tlv = {
+ .tag = cpu_to_le16(NAN_UNI_CMD_DISABLE_REQUEST),
+ .len = cpu_to_le16(sizeof(struct tlv)),
+ },
+ };
+
+ if (!dev)
+ return -EINVAL;
+
+ return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true);
+}
+
int mt7925_nan_change_configure(struct ieee80211_vif *vif,
struct mt792x_dev *dev,
struct cfg80211_nan_conf *conf)
@@ -473,8 +513,26 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)
dev_dbg(dev->mt76.dev, "nan: evt=%u cluster=%pM\n",
de_evt->event_type, de_evt->cluster_id);
- if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER)
+ if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER &&
+ de_evt->event_type != NAN_EVENT_ID_STARTED_CLUSTER)
+ return;
+
+ /* STARTED_CLUSTER fires during NAN_START, before nan.started is set and
+ * before the supplicant subscribes - defer past NAN_START via the work
+ * so ieee80211_nan_cluster_joined() actually reaches userspace.
+ */
+ if (de_evt->event_type == NAN_EVENT_ID_STARTED_CLUSTER) {
+ dev_dbg(dev->mt76.dev,
+ "nan: deferring STARTED_CLUSTER cluster=%pM\n",
+ cluster_id);
+ spin_lock_bh(&dev->nan_deferred_lock);
+ memcpy(dev->nan_started_cluster_id, cluster_id, ETH_ALEN);
+ set_bit(MT7925_NAN_DEFERRED_STARTED_CLUSTER,
+ &dev->nan_deferred_pending);
+ spin_unlock_bh(&dev->nan_deferred_lock);
+ ieee80211_queue_work(dev->mt76.hw, &dev->nan_deferred_work);
return;
+ }
if (!dev->nan_vif || !ieee80211_vif_nan_started(dev->nan_vif)) {
dev_warn(dev->mt76.dev, "nan: joined-cluster event but NAN not started\n");
@@ -487,7 +545,110 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)
dev_dbg(dev->mt76.dev, "nan: own_nmi=%pM master_nmi=%pM\n",
de_evt->own_nmi, de_evt->master_nmi);
- ieee80211_nan_cluster_joined(dev->nan_vif, cluster_id, true, GFP_KERNEL);
+ /* joined an existing cluster, not a self-anchored new one */
+ ieee80211_nan_cluster_joined(dev->nan_vif, cluster_id, false, GFP_KERNEL);
+}
+
+/* Runs the deferred NAN MCU events in process context; takes wiphy_lock
+ * before nan_vif, which the NAN stop path frees under that mutex.
+ */
+void
+mt7925_nan_deferred_work(struct work_struct *work)
+{
+ struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
+ nan_deferred_work);
+ struct ieee80211_vif *vif;
+ unsigned long pending;
+ u8 cluster_id[ETH_ALEN];
+
+ spin_lock_bh(&dev->nan_deferred_lock);
+ pending = dev->nan_deferred_pending;
+ dev->nan_deferred_pending = 0;
+ memcpy(cluster_id, dev->nan_started_cluster_id, ETH_ALEN);
+ spin_unlock_bh(&dev->nan_deferred_lock);
+
+ if (!pending)
+ return;
+
+ wiphy_lock(dev->mt76.hw->wiphy);
+ vif = dev->nan_vif;
+ if (!vif || !ieee80211_vif_nan_started(vif))
+ goto out;
+
+ if (test_bit(MT7925_NAN_DEFERRED_STARTED_CLUSTER, &pending))
+ ieee80211_nan_cluster_joined(vif, cluster_id, true, GFP_KERNEL);
+
+ if (test_bit(MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE, &pending))
+ ieee80211_nan_sched_update_done(vif);
+out:
+ wiphy_unlock(dev->mt76.hw->wiphy);
+}
+
+static void
+mt7925_nan_handle_ulw_update(struct mt792x_dev *dev, struct tlv *tlv)
+{
+ struct mt7925_nan_ulw_event *evt;
+ struct mt7925_nan_ulw_attr attr;
+ struct wireless_dev *wdev;
+ u16 len;
+
+ if (!dev || !tlv)
+ return;
+
+ if (!dev->nan_vif || !ieee80211_vif_nan_started(dev->nan_vif))
+ return;
+
+ len = le16_to_cpu(tlv->len);
+ if (len < sizeof(*tlv) + sizeof(*evt)) {
+ dev_warn(dev->mt76.dev,
+ "nan: short ulw event tlv len=%u\n", len);
+ return;
+ }
+
+ evt = (struct mt7925_nan_ulw_event *)tlv->data;
+ wdev = ieee80211_vif_to_wdev(dev->nan_vif);
+ if (!wdev)
+ return;
+
+ dev_dbg(dev->mt76.dev,
+ "nan: ulw_update wdev=%p owner_nlportid=%u sched_id=%u seq=%u dur=%u\n",
+ wdev, wdev->owner_nlportid,
+ evt->sched_id, evt->seq_id, le32_to_cpu(evt->duration));
+
+ /* Reorder the FW fields into NAN spec Table 109 attribute layout */
+ attr.attr_id = NAN_ULW_ATTR_ID;
+ attr.length = cpu_to_le16(NAN_ULW_FIXED_PAYLOAD);
+ attr.sched_id = evt->sched_id;
+ attr.seq_id = evt->seq_id;
+ attr.start_time = evt->start_time;
+ attr.duration = evt->duration;
+ attr.period = evt->period;
+ attr.count_down = evt->count_down;
+ attr.ulw_overwrite = evt->ulw_overwrite;
+
+ cfg80211_nan_ulw_update(wdev, (const u8 *)&attr, sizeof(attr),
+ GFP_KERNEL);
+}
+
+static void
+mt7925_nan_handle_sched_update_done(struct mt792x_dev *dev, struct tlv *tlv)
+{
+ struct ieee80211_vif *vif;
+
+ if (!dev || !tlv)
+ return;
+
+ vif = dev->nan_vif;
+ if (!vif || !ieee80211_vif_nan_started(vif))
+ return;
+
+ /* Runs in the BH-disabled MCU-event RX path; the mac80211 helper needs
+ * the wiphy mutex and may sleep, so hand it to the work instead.
+ */
+ spin_lock_bh(&dev->nan_deferred_lock);
+ set_bit(MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE, &dev->nan_deferred_pending);
+ spin_unlock_bh(&dev->nan_deferred_lock);
+ ieee80211_queue_work(dev->mt76.hw, &dev->nan_deferred_work);
}
void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb)
@@ -515,9 +676,15 @@ void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb)
case NAN_UNI_EVENT_ID_DE_EVENT_IND:
mt7925_nan_mcu_handle_de_event(dev, tlv);
break;
- case NAN_UNI_EVENT_REPORT_DW_END:
+ case NAN_UNI_EVENT_REPORT_DW_START:
mt7925_nan_handle_dw_ind(dev, tlv);
break;
+ case NAN_UNI_EVENT_ID_ULW_UPDATE:
+ mt7925_nan_handle_ulw_update(dev, tlv);
+ break;
+ case NAN_UNI_EVENT_ID_SCHED_UPDATE_DONE:
+ mt7925_nan_handle_sched_update_done(dev, tlv);
+ break;
default:
break;
}
@@ -555,6 +722,7 @@ static int mt7925_nan_avail_ctrl_tlv(struct sk_buff *skb,
avail_ctrl_tlv->avail_ctrl =
cpu_to_le16(ctrl & NAN_AVAIL_CTRL_CHECK_FOR_CHANGED);
avail_ctrl_tlv->seq_id = seq_id;
+ avail_ctrl_tlv->is_deferred = sched->deferred ? 1 : 0;
return 0;
}
@@ -584,9 +752,14 @@ static u32 mt7925_nan_slot_to_bitmap(struct ieee80211_vif *vif,
if (FIELD_GET(NAN_CH_CTRL_PRIMARY_CH, raw) ==
slot_chan->chan->hw_value) {
- u32 map = le32_to_cpu(ch_list[j].avail_map[0]);
+ u32 dw;
+
+ for (dw = 0; dw < NAN_TOTAL_DW; dw++) {
+ u32 map = le32_to_cpu(ch_list[j].avail_map[dw]);
- ch_list[j].avail_map[0] = cpu_to_le32(map | BIT(i));
+ ch_list[j].avail_map[dw] =
+ cpu_to_le32(map | BIT(i));
+ }
le32_add_cpu(&ch_list[j].num, 1);
is_found = true;
break;
@@ -594,12 +767,18 @@ static u32 mt7925_nan_slot_to_bitmap(struct ieee80211_vif *vif,
}
if (!is_found && num_channels < NAN_TIMELINE_MGMT_CHNL_LIST_NUM) {
+ u32 dw;
+
ch_list[num_channels].ch_info =
- cpu_to_le32(FIELD_PREP(NAN_CH_CTRL_OP_CLASS,
+ cpu_to_le32(FIELD_PREP(NAN_CH_CTRL_CH_TYPE,
+ NAN_BAND_CHANNEL_ENTRY_LIST_TYPE_CHANNEL) |
+ FIELD_PREP(NAN_CH_CTRL_OP_CLASS,
slot->channel_entry[0]) |
FIELD_PREP(NAN_CH_CTRL_PRIMARY_CH,
slot_chan->chan->hw_value));
- ch_list[num_channels].avail_map[0] = cpu_to_le32(BIT(i));
+ for (dw = 0; dw < NAN_TOTAL_DW; dw++)
+ ch_list[num_channels].avail_map[dw] =
+ cpu_to_le32(BIT(i));
le32_add_cpu(&ch_list[num_channels].num, 1);
ch_list[num_channels].is_valid++;
num_channels++;
@@ -647,15 +826,12 @@ void mt7925_nan_local_sched_changed(struct mt792x_dev *dev,
{
struct mt7925_nan_common_hdr *hdr;
struct mt76_dev *mdev;
- bool deferred;
struct sk_buff *skb;
- int ret = -ENOMEM;
if (!dev || !vif)
return;
mdev = &dev->mt76;
- deferred = vif->cfg.nan_sched.deferred;
mt792x_mutex_acquire(dev);
@@ -672,19 +848,9 @@ void mt7925_nan_local_sched_changed(struct mt792x_dev *dev,
goto out;
}
- ret = mt76_mcu_skb_send_msg(mdev, skb,
- MCU_UNI_CMD(NAN), true);
+ mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), false);
out:
mt792x_mutex_release(dev);
-
- if (deferred) {
- if (ret)
- dev_err(mdev->dev,
- "NAN: local schedule update failed: %d\n",
- ret);
-
- ieee80211_nan_sched_update_done(vif);
- }
}
static int mt7925_nan_peer_rec_tlv(struct sk_buff *skb,
@@ -712,23 +878,6 @@ static int mt7925_nan_peer_rec_tlv(struct sk_buff *skb,
return 0;
}
-static u8 mt7925_nan_get_supported_bands(struct mt792x_vif *mvif)
-{
- struct wiphy *wiphy;
- u8 bands = 0;
-
- if (!mvif || !mvif->phy)
- return BIT(NAN_SUPPORTED_BAND_ID_2P4G);
-
- wiphy = mvif->phy->mt76->hw->wiphy;
- if (wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ))
- bands |= BIT(NAN_SUPPORTED_BAND_ID_2P4G);
- if (wiphy->nan_supported_bands & BIT(NL80211_BAND_5GHZ))
- bands |= BIT(NAN_SUPPORTED_BAND_ID_5G);
-
- return bands ?: BIT(NAN_SUPPORTED_BAND_ID_2P4G);
-}
-
static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb,
struct ieee80211_sta *sta,
struct mt792x_sta *msta)
@@ -755,8 +904,7 @@ static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb,
peer_cap_tlv = (struct mt7925_nan_sched_update_peer_cap_tlv *)tlv;
peer_cap_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx);
- peer_cap_tlv->supported_bands =
- mt7925_nan_get_supported_bands(msta->vif);
+ peer_cap_tlv->supported_bands = BIT(NAN_SUPPORTED_BAND_ID_2P4G);
peer_cap_tlv->max_chnl_switch_time = cpu_to_le16(sched->max_chan_switch);
for (i = 0; i < sched->n_channels; i++) {
@@ -785,52 +933,40 @@ static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb,
static void
mt7925_nan_fill_crb_committed(struct mt7925_nan_sched_update_crb_tlv *crb_tlv,
- struct ieee80211_vif *vif,
struct ieee80211_nan_peer_sched *sched)
{
- struct ieee80211_nan_sched_cfg *local_sched;
- u8 local_map_id;
u32 m, slot;
- if (!vif || !sched)
+ if (!sched)
return;
- local_sched = &vif->cfg.nan_sched;
- local_map_id = mt7925_nan_avail_attr_ctrl(local_sched) &
- NAN_AVAIL_CTRL_MAPID;
-
for (m = 0; m < CFG80211_NAN_MAX_PEER_MAPS &&
m < NAN_TIMELINE_MGMT_SIZE; m++) {
+ struct ieee80211_nan_peer_map *map = &sched->maps[m];
struct mt7925_nan_sched_timeline *tl =
&crb_tlv->comm_faw_timeline[m];
- struct ieee80211_nan_peer_map *map = &sched->maps[m];
- u32 avail_map = 0;
if (map->map_id == CFG80211_NAN_INVALID_MAP_ID)
continue;
tl->map_id = map->map_id;
- tl->local_map_id = local_map_id;
+ /*
+ * Convert peer schedule slots to FW avail_map bitmap.
+ * Each bit represents one time slot where the peer has
+ * committed availability. Fill all DW intervals the same.
+ */
for (slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS;
slot++) {
- struct ieee80211_nan_channel *local_ch;
- struct ieee80211_nan_channel *peer_ch;
+ struct ieee80211_nan_channel *ch = map->slots[slot];
+ u32 dw;
- local_ch = local_sched->schedule[slot];
- peer_ch = map->slots[slot];
-
- if (!local_ch || !local_ch->chanctx_conf ||
- !peer_ch || !peer_ch->chanctx_conf)
+ if (!ch || !ch->chanctx_conf)
continue;
- if (local_ch->chanctx_conf != peer_ch->chanctx_conf)
- continue;
-
- avail_map |= BIT(slot);
+ for (dw = 0; dw < NAN_TOTAL_DW; dw++)
+ tl->avail_map[dw] |= cpu_to_le32(BIT(slot));
}
-
- tl->avail_map[0] = cpu_to_le32(avail_map);
}
}
@@ -856,8 +992,38 @@ static int mt7925_nan_update_crb_tlv(struct sk_buff *skb,
crb_tlv->is_use_ranging = false;
crb_tlv->comm_ndc_ctrl.is_valid = false;
- mt7925_nan_fill_crb_committed(crb_tlv, msta->vif->phy->dev->nan_vif,
- sta->nan_sched);
+ mt7925_nan_fill_crb_committed(crb_tlv, sta->nan_sched);
+
+ return 0;
+}
+
+static int
+mt7925_nan_peer_ulw_tlv(struct sk_buff *skb,
+ struct ieee80211_sta *sta,
+ struct mt792x_sta *msta)
+{
+ struct mt7925_nan_update_ulw_tlv *ulw_tlv = NULL;
+ struct ieee80211_nan_peer_sched *sched = NULL;
+ struct tlv *tlv = NULL;
+
+ if (!skb || !sta || !msta)
+ return -EINVAL;
+
+ sched = sta->nan_sched;
+ if (!sched || !sched->init_ulw || !sched->ulw_size)
+ return 0; /* No ULW to send, not an error */
+
+ if (sched->ulw_size > NAN_ULW_MAX_SIZE)
+ return -EINVAL;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_ULW,
+ sizeof(struct mt7925_nan_update_ulw_tlv));
+ if (!tlv)
+ return -ENOMEM;
+
+ ulw_tlv = (struct mt7925_nan_update_ulw_tlv *)tlv;
+ ether_addr_copy(ulw_tlv->nmi_addr, sta->addr);
+ memcpy(ulw_tlv->ulw_attr, sched->init_ulw, sched->ulw_size);
return 0;
}
@@ -914,6 +1080,10 @@ int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev,
goto free_skb;
}
+ ret = mt7925_nan_peer_ulw_tlv(skb, sta, msta);
+ if (ret)
+ goto free_skb;
+
ret = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), true);
if (ret && idx_allocated)
goto clear_idx;
@@ -935,18 +1105,18 @@ clear_idx:
int mt792x_nan_set_peer_rec(struct mt76_dev *mdev,
struct ieee80211_sta *sta)
{
+ struct mt7925_nan_sched_update_crb_tlv *crb_tlv;
struct mt7925_nan_common_hdr *hdr;
struct mt792x_sta *msta;
struct mt792x_nan *nan;
struct sk_buff *skb;
+ struct tlv *tlv;
int ret;
if (!mdev || !sta)
return -EINVAL;
- skb = mt76_mcu_msg_alloc(mdev, NULL,
- sizeof(struct mt7925_nan_common_hdr) +
- sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv));
+ skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_PEER_MAX_SIZE);
if (!skb)
return -ENOMEM;
@@ -961,6 +1131,25 @@ int mt792x_nan_set_peer_rec(struct mt76_dev *mdev,
return 0;
}
+ /* Send a zero-avail_map CRB TLV before deactivating the peer record so
+ * firmware clears the committed schedule slots for this peer. Without
+ * this, stale CRB entries linger and cause scheduling conflicts for
+ * subsequent NDP connections that reuse the same sch_idx.
+ */
+ tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_CRB,
+ sizeof(struct mt7925_nan_sched_update_crb_tlv));
+ if (!tlv) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+ crb_tlv = (struct mt7925_nan_sched_update_crb_tlv *)tlv;
+ crb_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx);
+ crb_tlv->flags = NAN_CRB_USE_DATA_PATH;
+ crb_tlv->is_use_ranging = false;
+ crb_tlv->comm_ndc_ctrl.is_valid = false;
+ /* avail_map is zero-initialised by mt76_connac_mcu_add_tlv */
+
+ /* Deactivate peer record and release connection index */
if (mt7925_nan_peer_rec_tlv(skb, sta, msta, false)) {
dev_kfree_skb(skb);
return -ENOMEM;
@@ -984,21 +1173,17 @@ int mt792x_nan_map_sta_rec(struct mt76_dev *mdev,
struct mt7925_nan_common_hdr *hdr;
struct ieee80211_sta *nmi_sta;
struct mt792x_sta *nmi_msta;
- struct mt792x_vif *mvif;
struct mt792x_sta *msta;
u8 nmi_addr[ETH_ALEN];
struct sk_buff *skb;
int ndp_ctx_id = 0;
int ret = -ENOMEM;
- struct mt792x_dev *dev;
struct tlv *tlv;
if (!mdev || !vif || !sta)
return -EINVAL;
- dev = container_of(mdev, struct mt792x_dev, mt76);
msta = (struct mt792x_sta *)sta->drv_priv;
- mvif = (struct mt792x_vif *)vif->drv_priv;
rcu_read_lock();
nmi_sta = rcu_dereference(sta->nmi);
@@ -1012,33 +1197,6 @@ int mt792x_nan_map_sta_rec(struct mt76_dev *mdev,
memcpy(nmi_addr, nmi_sta->addr, ETH_ALEN);
nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv;
- if (!nmi_msta->nan_sched.idx_assigned) {
- if (!nmi_sta->nan_sched) {
- rcu_read_unlock();
- dev_err(mdev->dev,
- "NAN: peer schedule missing for NDI sta %pM\n",
- sta->addr);
- return -EAGAIN;
- }
-
- rcu_read_unlock();
- ret = mt792x_nan_set_peer_schedule(dev, nmi_sta);
- if (ret)
- return ret;
-
- rcu_read_lock();
- nmi_sta = rcu_dereference(sta->nmi);
- if (!nmi_sta) {
- rcu_read_unlock();
- dev_err(mdev->dev,
- "NAN: NMI sta not found for NDI sta %pM\n",
- sta->addr);
- return -EINVAL;
- }
-
- nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv;
- }
-
ndp_ctx_id = find_first_zero_bit(&nmi_msta->nan_sched.ndp_ctx_bitmap,
NAN_MAX_NDP_CXT);
if (ndp_ctx_id >= NAN_MAX_NDP_CXT) {
@@ -1073,7 +1231,7 @@ int mt792x_nan_map_sta_rec(struct mt76_dev *mdev,
memcpy(map_tlv->nmi_addr, nmi_addr, ETH_ALEN);
map_tlv->sta_rec_idx = msta->deflink.wcid.idx;
map_tlv->ndp_ctx_id = ndp_ctx_id;
- map_tlv->role_idx = cpu_to_le32(mvif->bss_conf.mt76.idx);
+ map_tlv->role_idx = cpu_to_le32(NAN_BSS_INDEX_BAND0);
memcpy(map_tlv->ndi_addr, vif->addr, ETH_ALEN);
ret = mt76_mcu_skb_send_msg(mdev, skb,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
index f55730e25f46..9a0cd1d06d45 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
@@ -28,6 +28,13 @@
#define NAN_MAX_CONN_CFG 8
#define NAN_MAX_NDP_CXT 4
+#define MT7925_NAN_ENABLE_MAX_SIZE \
+ (sizeof(struct mt7925_nan_common_hdr) + \
+ sizeof(struct mt7925_nan_enable_req_tlv) + \
+ sizeof(struct mt7925_nan_dw_interval_tlv) + \
+ sizeof(struct mt7925_nan_cluster_id_tlv) + \
+ sizeof(struct mt7925_nan_sync_rssi_tlv))
+
#define MT7925_NAN_CONF_MAX_SIZE \
(sizeof(struct mt7925_nan_common_hdr) + \
sizeof(struct mt7925_nan_master_preference_tlv) + \
@@ -44,7 +51,8 @@
(sizeof(struct mt7925_nan_common_hdr) + \
sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv) + \
sizeof(struct mt7925_nan_sched_update_peer_cap_tlv) + \
- sizeof(struct mt7925_nan_sched_update_crb_tlv))
+ sizeof(struct mt7925_nan_sched_update_crb_tlv) + \
+ sizeof(struct mt7925_nan_update_ulw_tlv))
/* NAN Availability Attribute */
#define NAN_AVAIL_ATTR_ID_OFFSET 0
@@ -65,6 +73,8 @@
#define UNII3_LOWER_BOUND 149
#define UNII3_UPPER_BOUND 165
+#define NAN_BSS_INDEX_BAND0 0
+
enum nan_uni_cmd_tag {
NAN_UNI_CMD_SET_MASTER_PREFERENCE = 0,
NAN_UNI_CMD_ENABLE_REQUEST = 7,
@@ -73,6 +83,8 @@ enum nan_uni_cmd_tag {
NAN_UNI_CMD_UPDATE_CRB = 10,
NAN_UNI_CMD_MANAGE_PEER_SCH_RECORD = 12,
NAN_UNI_CMD_MAP_STA_RECORD = 13,
+ NAN_UNI_CMD_UPDATE_ULW = 16,
+ NAN_UNI_CMD_UPDATE_PHY_SETTING = 18,
NAN_UNI_CMD_UPDATE_AVAILABILITY_CTRL = 20,
NAN_UNI_CMD_UPDATE_PEER_CAPABILITY = 21,
NAN_UNI_CMD_CHANGE_NMI_ADDRESS = 24,
@@ -84,14 +96,26 @@ enum nan_uni_cmd_tag {
enum nan_uni_event_tag {
NAN_UNI_EVENT_ID_DE_EVENT_IND = 19,
+ NAN_UNI_EVENT_ID_ULW_UPDATE = 39,
+ NAN_UNI_EVENT_ID_SCHED_UPDATE_DONE = 43,
+ NAN_UNI_EVENT_REPORT_DW_START = 59,
NAN_UNI_EVENT_REPORT_DW_END = 60,
};
enum nan_disc_event_type {
NAN_EVENT_ID_DISC_MAC_ADDR = 0,
+ NAN_EVENT_ID_STARTED_CLUSTER = 1,
NAN_EVENT_ID_JOINED_CLUSTER = 2,
};
+/* bit indices into mt792x_dev->nan_deferred_pending, set from the atomic
+ * MCU-event RX path and consumed by mt7925_nan_deferred_work()
+ */
+enum mt7925_nan_deferred_event {
+ MT7925_NAN_DEFERRED_STARTED_CLUSTER,
+ MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE,
+};
+
/* NAN 4.0 Table 79. Device Capability attribute format, Supported Bands */
enum nan_supported_bands {
NAN_SUPPORTED_BAND_ID_2P4G = 2,
@@ -108,12 +132,15 @@ enum nan_peer_supported_bands {
NAN_SUPPORTED_BN_NUM
};
+#define NAN_CH_CTRL_CH_TYPE BIT(0)
#define NAN_CH_CTRL_OP_CLASS GENMASK(15, 8)
#define NAN_CH_CTRL_PRIMARY_CH GENMASK(23, 16)
#define NAN_CRB_USE_DATA_PATH BIT(0)
#define NAN_CRB_AVAIL_6G_FORMAT GENMASK(2, 1)
+#define NAN_BAND_CHANNEL_ENTRY_LIST_TYPE_CHANNEL 1
+
struct mt7925_nan_social_ch_scan_params {
u8 dwell_time[NAN_MAX_SOCIAL_CHANNELS];
__le16 scan_period[NAN_MAX_SOCIAL_CHANNELS];
@@ -153,6 +180,32 @@ struct nan_rpt_dw_evt {
__le16 dw_num;
};
+#define NAN_ULW_ATTR_ID 0x17
+#define NAN_ULW_FIXED_PAYLOAD 16 /* Table 109 fixed fields */
+
+struct mt7925_nan_ulw_event {
+ u8 sched_id;
+ u8 seq_id;
+ u8 count_down;
+ u8 ulw_overwrite;
+ __le32 start_time;
+ __le32 duration;
+ __le32 period;
+} __packed;
+
+/* ULW attribute laid out in NAN spec Table 109 field order */
+struct mt7925_nan_ulw_attr {
+ u8 attr_id;
+ __le16 length;
+ u8 sched_id;
+ u8 seq_id;
+ __le32 start_time;
+ __le32 duration;
+ __le32 period;
+ u8 count_down;
+ u8 ulw_overwrite;
+} __packed;
+
struct mt7925_nan_conf_dw {
u8 config_2dot4g_dw_band;
__le32 dw_2dot4g_interval_val;
@@ -302,7 +355,7 @@ struct mt7925_nan_avail_ctrl_tlv {
__le16 len;
__le16 avail_ctrl;
u8 seq_id;
- u8 reserved[1];
+ u8 is_deferred;
} __packed __aligned(4);
struct mt7925_nan_ch_timeline {
@@ -346,8 +399,7 @@ struct mt7925_nan_sched_update_peer_cap_tlv {
struct mt7925_nan_sched_timeline {
u8 map_id;
- u8 local_map_id;
- u8 reserved[2];
+ u8 reserved[3];
union {
__le32 avail_map[NAN_TOTAL_DW];
u8 avail_block[NAN_TOTAL_DW * 4];
@@ -378,6 +430,15 @@ struct mt7925_nan_sched_update_crb_tlv {
struct mt7925_nan_sched_faw_ndc_timeline faw_ndc_timeline[NAN_TIMELINE_MGMT_SIZE];
} __packed __aligned(4);
+#define NAN_ULW_MAX_SIZE 256
+
+struct mt7925_nan_update_ulw_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 nmi_addr[ETH_ALEN];
+ u8 ulw_attr[NAN_ULW_MAX_SIZE];
+} __packed __aligned(4);
+
struct mt7925_nan_sched_map_sta_rec_tlv {
__le16 tag;
__le16 len;
@@ -390,6 +451,38 @@ struct mt7925_nan_sched_map_sta_rec_tlv {
u8 reserved[2];
} __packed __aligned(4);
+/* Matches FW NAN_PHY_SETTING_T (enum fields are 4 bytes on ARM) */
+struct mt7925_nan_phy_setting {
+ u8 phy_type_set;
+ u8 non_ht_basic_phy_type;
+ u8 use_short_preamble;
+ u8 use_short_slot_time;
+ __le16 operational_rate_set;
+ __le16 bss_basic_rate_set;
+ __le16 vht_basic_mcs_set;
+ u8 erp_protect_mode;
+ u8 ht_op_info1;
+ __le16 ht_op_info2;
+ __le16 ht_op_info3;
+ __le32 ht_protect_mode;
+ __le32 gf_operation_mode;
+ __le32 rifs_operation_mode;
+} __packed;
+
+/* Matches FW NAN_SCHED_CMD_UPDATE_PHY_PARAM_T */
+struct mt7925_nan_update_phy_setting_tlv {
+ __le16 tag;
+ __le16 len;
+ struct mt7925_nan_phy_setting phy_2g;
+ struct mt7925_nan_phy_setting phy_5g;
+} __packed __aligned(4);
+
+int mt7925_nan_update_phy_setting(struct mt792x_dev *dev);
+
+struct ieee80211_chanctx_conf *
+mt7925_nan_seed_link_sta(struct mt792x_dev *dev,
+ struct ieee80211_link_sta *link_sta);
+
int mt7925_nan_enable(struct ieee80211_vif *vif,
struct mt792x_dev *dev,
struct cfg80211_nan_conf *conf);
@@ -403,8 +496,6 @@ int mt7925_nan_change_configure(struct ieee80211_vif *vif,
void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb);
-int mt7925_nan_set_nmi_addr(struct mt792x_dev *dev, const u8 *addr);
-
void mt7925_nan_local_sched_changed(struct mt792x_dev *dev,
struct ieee80211_vif *vif);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 02ef09dd797d..0d477dc1086c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -9,6 +9,7 @@
#include "mac.h"
#include "mcu.h"
#include "regd.h"
+#include "cached_cal.h"
#include "../dma.h"
static const struct pci_device_id mt7925_pci_device_table[] = {
@@ -38,6 +39,62 @@ static int mt7925e_init_reset(struct mt792x_dev *dev)
return mt792x_wpdma_reset(dev, true);
}
+/* Set up the factory-calibration cache in host memory. PCIe only: the FW
+ * reaches the buffer through AXI DMA, so it has no meaning on other buses.
+ * Every failure just leaves the cache off, which is not fatal.
+ */
+void mt7925e_axidma_cal_cache_init(struct mt792x_dev *dev)
+{
+ if (!(dev->phy.chip_cap & MT792x_CHIP_CAP_AXIDMA_EN))
+ return;
+
+ if (dev->cached_cal.va) {
+ /* Carried-over buffer still holds FW-written cal data. */
+ dev->cached_cal.reused = true;
+ } else {
+ if (mt7925_axidma_alloc(dev)) {
+ dev_warn(dev->mt76.dev,
+ "AXI DMA cal cache disabled: buffer alloc failed\n");
+ return;
+ }
+ /* Freshly allocated buffer is zeroed: no cal data yet. */
+ dev->cached_cal.reused = false;
+ }
+
+ if (mt7925_mcu_send_axidma_info(dev)) {
+ dev_warn(dev->mt76.dev,
+ "AXI DMA cal cache disabled: info cmd to FW failed\n");
+ return;
+ }
+
+ /* Enable the bypass-cal flow so the FW reuses cached cal data
+ * (common / power-on group) instead of running a full calibration;
+ * must be set before the mapping table is sent.
+ */
+ if (mt7925_mcu_fact_cal_set_bypass(dev, true)) {
+ dev_warn(dev->mt76.dev,
+ "AXI DMA cal cache disabled: bypass flag set failed\n");
+ return;
+ }
+
+ if (mt7925_mcu_send_fact_cal_mapping_tbl(dev)) {
+ dev_warn(dev->mt76.dev,
+ "AXI DMA cal cache disabled: mapping tbl send failed\n");
+ return;
+ }
+
+ if (mt7925_mcu_fact_cal_set_bypass(dev, false)) {
+ dev_warn(dev->mt76.dev,
+ "AXI DMA cal cache disabled: bypass flag set failed\n");
+ return;
+ }
+
+ dev_info(dev->mt76.dev,
+ "AXI DMA cal cache enabled (DMA=%pad, size=%u, reused=%d)\n",
+ &dev->cached_cal.dma_addr, dev->cached_cal.size,
+ dev->cached_cal.reused);
+}
+
static void mt7925e_unregister_device(struct mt792x_dev *dev)
{
int i;
@@ -47,19 +104,22 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)
if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)
wiphy_rfkill_stop_polling(hw->wiphy);
- cancel_work_sync(&dev->reset_work);
cancel_work_sync(&dev->init_work);
mt76_unregister_device(&dev->mt76);
+ set_bit(MT76_REMOVED, &dev->mphy.state);
+ cancel_work_sync(&dev->reset_work);
mt76_for_each_q_rx(&dev->mt76, i)
napi_disable(&dev->mt76.napi[i]);
cancel_delayed_work_sync(&pm->ps_work);
cancel_delayed_work_sync(&dev->mlo_pm_work);
cancel_work_sync(&pm->wake_work);
+ cancel_work_sync(&dev->nan_deferred_work);
mt7925_tx_token_put(dev);
__mt792x_mcu_drv_pmctrl(dev);
mt792x_dma_cleanup(dev);
mt792x_wfsys_reset(dev);
+ mt7925_axidma_free(dev);
skb_queue_purge(&dev->mt76.mcu.res_q);
tasklet_disable(&dev->mt76.irq_tasklet);
@@ -493,7 +553,6 @@ static const struct mt792x_irq_map mt7925_irq_map = {
.all_complete_mask = MT7925_INT_RX_DONE_ALL,
.data_complete_mask = MT7925_INT_RX_DONE_DATA,
.wm_complete_mask = MT7925_INT_RX_DONE_WM,
- .wm2_complete_mask = MT_INT_RX_DONE_WM2,
},
};
@@ -587,7 +646,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
is_mt7928_hw = (pdev->device == 0x7928 || pdev->device == 0x7935);
if (is_mt7928_hw)
- ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(34));
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
else
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
@@ -725,7 +784,6 @@ static void mt7925_pci_remove(struct pci_dev *pdev)
struct mt76_dev *mdev = pci_get_drvdata(pdev);
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
- set_bit(MT76_REMOVED, &mdev->phy.state);
mt7925e_unregister_device(dev);
devm_free_irq(&pdev->dev, pdev->irq, dev);
mt76_free_device(&dev->mt76);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
index 8477d21abc66..18f125bf3df4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
@@ -73,7 +73,9 @@ int mt7925e_mac_reset(struct mt792x_dev *dev)
const struct mt792x_irq_map *irq_map = dev->irq_map;
int i, err;
- mt792xe_mcu_drv_pmctrl(dev);
+ err = mt792xe_mcu_drv_pmctrl(dev);
+ if (err)
+ return err;
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
@@ -125,7 +127,7 @@ int mt7925e_mac_reset(struct mt792x_dev *dev)
err = mt792xe_mcu_fw_pmctrl(dev);
if (err)
- return err;
+ goto out;
err = __mt792xe_mcu_drv_pmctrl(dev);
if (err)
@@ -135,6 +137,8 @@ int mt7925e_mac_reset(struct mt792x_dev *dev)
if (err)
goto out;
+ mt7925e_axidma_cal_cache_init(dev);
+
err = mt7925_mcu_set_eeprom(dev);
if (err)
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
index 72707eddc3db..258f3753db1f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
@@ -46,6 +46,8 @@ int mt7925e_mcu_init(struct mt792x_dev *dev)
mt76_rmw_field(dev, dev->pcie_reg->pm, MT_PCIE_MAC_PM_L0S_DIS, 1);
err = mt7925_run_firmware(dev);
+ if (!err)
+ mt7925e_axidma_cal_cache_init(dev);
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
index ab6c33b4a180..e040dff321dd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
@@ -48,8 +48,7 @@
#define MT7925_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA2
#define MT7925_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA0
#define MT7925_INT_RX_DONE_ALL (MT7925_INT_RX_DONE_DATA | \
- MT7925_INT_RX_DONE_WM | \
- MT_INT_RX_DONE_WM2)
+ MT7925_INT_RX_DONE_WM)
#define MT7927_INT_RX_DONE_DATA MT7927_RX_DONE_INT_ENA4
#define MT7927_INT_RX_DONE_WM MT7927_RX_DONE_INT_ENA6
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
index 1757023ea70d..f97f787b2198 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
@@ -147,6 +147,15 @@ out:
return err;
}
+static void mt7925u_stop(struct ieee80211_hw *hw, bool suspend)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
+
+ mt792xu_stop(hw, suspend);
+}
+
static int mt7925u_probe(struct usb_interface *usb_intf,
const struct usb_device_id *id)
{
@@ -193,7 +202,7 @@ static int mt7925u_probe(struct usb_interface *usb_intf,
if (!ops)
return -ENOMEM;
- ops->stop = mt792xu_stop;
+ ops->stop = mt7925u_stop;
mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev)
@@ -348,6 +357,19 @@ failed:
}
#endif /* CONFIG_PM */
+static void mt7925u_disconnect(struct usb_interface *usb_intf)
+{
+ struct mt792x_dev *dev = usb_get_intfdata(usb_intf);
+
+ /* mt792xu_disconnect() ends in mt76_free_device(), so the work has to
+ * go before it rather than after mt76_unregister_device() as on PCI.
+ */
+ if (dev)
+ cancel_work_sync(&dev->nan_deferred_work);
+
+ mt792xu_disconnect(usb_intf);
+}
+
MODULE_DEVICE_TABLE(usb, mt7925u_device_table);
MODULE_FIRMWARE(MT7925_FIRMWARE_WM);
MODULE_FIRMWARE(MT7925_ROM_PATCH);
@@ -356,7 +378,7 @@ static struct usb_driver mt7925u_driver = {
.name = KBUILD_MODNAME,
.id_table = mt7925u_device_table,
.probe = mt7925u_probe,
- .disconnect = mt792xu_disconnect,
+ .disconnect = mt7925u_disconnect,
#ifdef CONFIG_PM
.suspend = mt7925u_suspend,
.resume = mt7925u_resume,
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 9efc251cb745..a7d71e007da0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -33,6 +33,7 @@
#define MT792x_CHIP_CAP_REGD_EN BIT(5)
#define MT792x_CHIP_CAP_MLO_EN BIT(8)
#define MT792x_CHIP_CAP_MLO_EML_EN BIT(9)
+#define MT792x_CHIP_CAP_AXIDMA_EN BIT(16)
/* NOTE: used to map mt76_rates. idx may change if firmware expands table */
#define MT792x_BASIC_RATES_TBL 14
@@ -191,6 +192,7 @@ struct mt792x_vif {
struct mt792x_sta *wep_sta;
struct mt792x_phy *phy;
+ bool roc_join_held; /* join ROC kept until the STA is authorized */
u16 valid_links;
u8 deflink_id;
enum mt792x_mlo_pm_state mlo_pm_state;
@@ -214,6 +216,7 @@ struct mt792x_phy {
s16 coverage_class;
u8 slottime;
+ u32 rxfilter;
u32 rx_ampdu_ts;
u32 ampdu_ref;
@@ -342,6 +345,21 @@ struct mt792x_dev {
struct ieee80211_vif *nan_vif;
const struct ieee80211_iface_combination *iface_combinations;
int n_iface_combinations;
+ /* deferred NAN MCU events run out of the atomic RX path on one shared
+ * work; see mt7925_nan_deferred_work() and enum mt7925_nan_deferred_event
+ */
+ struct work_struct nan_deferred_work;
+ /* protects @nan_deferred_pending */
+ spinlock_t nan_deferred_lock;
+ unsigned long nan_deferred_pending;
+ u8 nan_started_cluster_id[ETH_ALEN];
+
+ struct {
+ void *va;
+ dma_addr_t dma_addr;
+ u32 size;
+ bool reused;
+ } cached_cal;
};
static inline struct mt792x_bss_conf *
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index 0ad33f74c228..3ce6c6ceac81 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -472,6 +472,7 @@ int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw,
mutex_lock(&dev->mt76.mutex);
mvif->bss_conf.mt76.ctx = ctx;
mctx->bss_conf = &mvif->bss_conf;
+ mt76_phy_chandef_set(&dev->mphy, &ctx->def);
mutex_unlock(&dev->mt76.mutex);
return 0;
@@ -857,7 +858,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(mt792x_init_wiphy);
-static u8
+static int
mt792x_get_offload_capability(struct device *dev, const char *fw_wm)
{
const struct mt76_connac2_fw_trailer *hdr;
@@ -926,13 +927,20 @@ mt792x_get_mac80211_ops(struct device *dev,
void *drv_data, u8 *fw_features)
{
struct ieee80211_ops *ops;
+ int ret;
ops = devm_kmemdup(dev, mac80211_ops, sizeof(struct ieee80211_ops),
GFP_KERNEL);
if (!ops)
return NULL;
- *fw_features = mt792x_get_offload_capability(dev, drv_data);
+ ret = mt792x_get_offload_capability(dev, drv_data);
+ if (ret < 0) {
+ dev_warn(dev, "failed to read firmware offload caps: %d\n", ret);
+ *fw_features = 0;
+ } else {
+ *fw_features = ret;
+ }
if (mt792x_needs_cnm_runtime(drv_data))
*fw_features |= MT792x_FW_CAP_CNM;
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
index 888e5a505673..ebf4c32924c4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
@@ -165,14 +165,16 @@ mt792x_mac_rssi_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
+ int signal;
- if (status->signal > 0)
+ signal = mt76_rx_signal(status->chains, status->chain_signal);
+ if (signal > 0 || signal <= -128)
return;
if (!ether_addr_equal(vif->addr, hdr->addr1))
return;
- ewma_rssi_add(&mvif->bss_conf.rssi, -status->signal);
+ ewma_rssi_add(&mvif->bss_conf.rssi, -signal);
}
void mt792x_mac_assoc_rssi(struct mt792x_dev *dev, struct sk_buff *skb)
@@ -278,6 +280,9 @@ void mt792x_reset(struct mt76_dev *mdev)
if (pm->suspended)
return;
+ if (test_bit(MT76_REMOVED, &mdev->phy.state))
+ return;
+
queue_work(dev->mt76.wq, &dev->reset_work);
}
EXPORT_SYMBOL_GPL(mt792x_reset);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
index 850f6b2cfc4a..5e19e64a9a00 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
@@ -502,21 +502,26 @@ void mt792xu_disconnect(struct usb_interface *usb_intf)
return;
set_bit(MT76_RESET, &dev->mphy.state);
- set_bit(MT76_MCU_RESET, &dev->mphy.state);
clear_bit(MT76_STATE_RUNNING, &dev->mphy.state);
- wake_up(&dev->mt76.mcu.wait);
- skb_queue_purge(&dev->mt76.mcu.res_q);
- cancel_work_sync(&dev->reset_work);
cancel_work_sync(&dev->init_work);
mt76_worker_disable(&dev->mt76.tx_worker);
mt792xu_reset_work_cleanup(dev);
if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) {
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+ skb_queue_purge(&dev->mt76.mcu.res_q);
+ cancel_work_sync(&dev->reset_work);
set_bit(MT76_REMOVED, &dev->mphy.state);
return;
}
mt76_unregister_device(&dev->mt76);
+
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+ skb_queue_purge(&dev->mt76.mcu.res_q);
+ cancel_work_sync(&dev->reset_work);
mt792xu_cleanup(dev);
set_bit(MT76_REMOVED, &dev->mphy.state);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
index 5742bce12fbb..3c51b402f44e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
@@ -17,5 +17,6 @@ config MT7996_NPU
bool "MT7996 (PCIe) NPU support"
depends on MT7996E
depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
+ depends on NET_AIROHA=y || NET_AIROHA=n || MT76_CORE=NET_AIROHA
select MT76_NPU
default n
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
index 5c293ae965cd..be4c882b3184 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
@@ -202,11 +202,12 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
mt7996_coredump_fw_stack(dev, dump, exception);
/* gather memory content */
- dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
- dump_mem->len = crash_data->memdump_buf_len;
- if (coredump_memdump && crash_data->memdump_buf_len)
+ if (coredump_memdump && crash_data->memdump_buf_len) {
+ dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
+ dump_mem->len = crash_data->memdump_buf_len;
memcpy(dump_mem->data, crash_data->memdump_buf,
crash_data->memdump_buf_len);
+ }
mutex_unlock(&dev->dump_mutex);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
index b85c3e549f8b..644abd0a0216 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
@@ -13,13 +13,6 @@
/** global debugfs **/
-struct hw_queue_map {
- const char *name;
- u8 index;
- u8 pid;
- u8 qid;
-};
-
static int
mt7996_implicit_txbf_set(void *data, u64 val)
{
@@ -604,35 +597,176 @@ mt7996_tx_stats_show(struct seq_file *file, void *data)
DEFINE_SHOW_ATTRIBUTE(mt7996_tx_stats);
+enum {
+ MT_QUEUE_PORT_HIF,
+ MT_QUEUE_PORT_CPU,
+ MT_QUEUE_PORT_LMAC,
+ MT_QUEUE_PORT_PLE_CTRL,
+};
+
+struct mt7996_hw_queue {
+ const char *name;
+ u8 pid;
+ u8 qid;
+ u8 tgid;
+};
+
+#define MT7996_HW_QUEUE(_name, _pid, _qid, _tgid) \
+ { .name = _name, .pid = MT_QUEUE_PORT_##_pid, .qid = _qid, .tgid = _tgid }
+
+static const struct mt7996_hw_queue mt7996_ple_queues[32] = {
+ [0] = MT7996_HW_QUEUE("CPU Q0", CPU, 0, 0),
+ [1] = MT7996_HW_QUEUE("CPU Q1", CPU, 1, 0),
+ [2] = MT7996_HW_QUEUE("CPU Q2", CPU, 2, 0),
+ [3] = MT7996_HW_QUEUE("CPU Q3", CPU, 3, 0),
+ [4] = MT7996_HW_QUEUE("ALTX Q0", LMAC, 0x10, 0),
+ [5] = MT7996_HW_QUEUE("BMC Q0", LMAC, 0x11, 0),
+ [6] = MT7996_HW_QUEUE("BCN Q0", LMAC, 0x12, 0),
+ [7] = MT7996_HW_QUEUE("PSMP Q0", LMAC, 0x13, 0),
+ [8] = MT7996_HW_QUEUE("ALTX Q1", LMAC, 0x10, 1),
+ [9] = MT7996_HW_QUEUE("BMC Q1", LMAC, 0x11, 1),
+ [10] = MT7996_HW_QUEUE("BCN Q1", LMAC, 0x12, 1),
+ [11] = MT7996_HW_QUEUE("PSMP Q1", LMAC, 0x13, 1),
+ [12] = MT7996_HW_QUEUE("ALTX Q2", LMAC, 0x1c, 2),
+ [13] = MT7996_HW_QUEUE("BMC Q2", LMAC, 0x1d, 2),
+ [14] = MT7996_HW_QUEUE("BCN Q2", LMAC, 0x1e, 2),
+ [15] = MT7996_HW_QUEUE("PSMP Q2", LMAC, 0x1f, 2),
+ [16] = MT7996_HW_QUEUE("NAF Q", LMAC, 0x18, 0),
+ [17] = MT7996_HW_QUEUE("NBCN Q", LMAC, 0x19, 0),
+ [20] = MT7996_HW_QUEUE("FIXFID Q", LMAC, 0x1a, 0),
+ [28] = MT7996_HW_QUEUE("RLS4 Q", PLE_CTRL, 0x7c, 0),
+ [29] = MT7996_HW_QUEUE("RLS3 Q", PLE_CTRL, 0x7d, 0),
+ [30] = MT7996_HW_QUEUE("RLS2 Q", PLE_CTRL, 0x7e, 0),
+ [31] = MT7996_HW_QUEUE("RLS Q", PLE_CTRL, 0x7f, 0),
+};
+
+static const struct mt7996_hw_queue mt7996_pse_queues[32] = {
+ [0] = MT7996_HW_QUEUE("CPU Q0", CPU, 0, 0),
+ [1] = MT7996_HW_QUEUE("CPU Q1", CPU, 1, 0),
+ [2] = MT7996_HW_QUEUE("CPU Q2", CPU, 2, 0),
+ [3] = MT7996_HW_QUEUE("CPU Q3", CPU, 3, 0),
+ [16] = MT7996_HW_QUEUE("LMAC Q", LMAC, 0, 0),
+ [17] = MT7996_HW_QUEUE("MDP TX Q0", LMAC, 1, 0),
+ [18] = MT7996_HW_QUEUE("MDP RX Q", LMAC, 2, 0),
+ [19] = MT7996_HW_QUEUE("SEC TX Q0", LMAC, 3, 0),
+ [20] = MT7996_HW_QUEUE("SEC RX Q", LMAC, 4, 0),
+ [21] = MT7996_HW_QUEUE("SFD PARK Q", LMAC, 5, 0),
+ [22] = MT7996_HW_QUEUE("MDP TXIOC Q0", LMAC, 6, 0),
+ [23] = MT7996_HW_QUEUE("MDP RXIOC Q0", LMAC, 7, 0),
+ [24] = MT7996_HW_QUEUE("MDP TX Q1", LMAC, 0x11, 0),
+ [25] = MT7996_HW_QUEUE("SEC TX Q1", LMAC, 0x13, 0),
+ [26] = MT7996_HW_QUEUE("MDP TXIOC Q1", LMAC, 0x16, 0),
+ [27] = MT7996_HW_QUEUE("MDP RXIOC Q1", LMAC, 0x17, 0),
+ [28] = MT7996_HW_QUEUE("CPU Q4", CPU, 4, 0),
+ [31] = MT7996_HW_QUEUE("RLS Q", PLE_CTRL, 0x1f, 0),
+};
+
+static const struct mt7996_hw_queue mt7996_pse_queues_1[32] = {
+ [0] = MT7996_HW_QUEUE("MDP TDPIOC Q0", LMAC, 0x08, 0),
+ [1] = MT7996_HW_QUEUE("MDP RDPIOC Q0", LMAC, 0x09, 0),
+ [2] = MT7996_HW_QUEUE("MDP TDPIOC Q1", LMAC, 0x18, 0),
+ [3] = MT7996_HW_QUEUE("MDP RDPIOC Q1", LMAC, 0x19, 0),
+ [4] = MT7996_HW_QUEUE("MDP TDPIOC Q2", LMAC, 0x28, 0),
+ [5] = MT7996_HW_QUEUE("MDP RDPIOC Q2", LMAC, 0x29, 0),
+ [7] = MT7996_HW_QUEUE("MDP RDPIOC Q3", LMAC, 0x39, 0),
+ [8] = MT7996_HW_QUEUE("MDP TX Q2", LMAC, 0x21, 0),
+ [9] = MT7996_HW_QUEUE("SEC TX Q2", LMAC, 0x23, 0),
+ [10] = MT7996_HW_QUEUE("MDP TXIOC Q2", LMAC, 0x26, 0),
+ [11] = MT7996_HW_QUEUE("MDP RXIOC Q2", LMAC, 0x27, 0),
+ [15] = MT7996_HW_QUEUE("MDP RXIOC Q3", LMAC, 0x37, 0),
+ [16] = MT7996_HW_QUEUE("HIF Q0", HIF, 0, 0),
+ [17] = MT7996_HW_QUEUE("HIF Q1", HIF, 1, 0),
+ [18] = MT7996_HW_QUEUE("HIF Q2", HIF, 2, 0),
+ [19] = MT7996_HW_QUEUE("HIF Q3", HIF, 3, 0),
+ [20] = MT7996_HW_QUEUE("HIF Q4", HIF, 4, 0),
+ [21] = MT7996_HW_QUEUE("HIF Q5", HIF, 5, 0),
+ [22] = MT7996_HW_QUEUE("HIF Q6", HIF, 6, 0),
+ [23] = MT7996_HW_QUEUE("HIF Q7", HIF, 7, 0),
+ [24] = MT7996_HW_QUEUE("HIF Q8", HIF, 8, 0),
+ [25] = MT7996_HW_QUEUE("HIF Q9", HIF, 9, 0),
+ [26] = MT7996_HW_QUEUE("HIF Q10", HIF, 10, 0),
+ [27] = MT7996_HW_QUEUE("HIF Q11", HIF, 11, 0),
+ [28] = MT7996_HW_QUEUE("HIF Q12", HIF, 12, 0),
+ [29] = MT7996_HW_QUEUE("HIF Q13", HIF, 13, 0),
+};
+
static void
-mt7996_hw_queue_read(struct seq_file *s, u32 size,
- const struct hw_queue_map *map)
+mt7996_fl_queue_show(struct seq_file *s, struct mt7996_dev *dev, bool pse,
+ const struct mt7996_hw_queue *q, u16 wlan_idx)
{
- struct mt7996_phy *phy = s->private;
- struct mt7996_dev *dev = phy->dev;
- u32 i, val;
+ static DEFINE_SPINLOCK(lock);
+ u32 val, fid, num;
+
+ val = MT_FL_Q0_CTRL_EXECUTE |
+ u32_encode_bits(q->qid, MT_FL_Q0_CTRL_QID) |
+ u32_encode_bits(q->tgid, MT_FL_Q0_CTRL_TGID) |
+ u32_encode_bits(q->pid, MT_FL_Q0_CTRL_PID) |
+ u32_encode_bits(wlan_idx, MT_FL_Q0_CTRL_WLAN_IDX);
+
+ spin_lock(&lock);
+ mt76_wr(dev, pse ? MT_PSE_FL_Q0_CTRL : MT_FL_Q0_CTRL, val);
+ fid = mt76_rr(dev, pse ? MT_PSE_FL_Q2_CTRL : MT_FL_Q2_CTRL);
+ num = mt76_rr(dev, pse ? MT_PSE_FL_Q3_CTRL : MT_FL_Q3_CTRL);
+ spin_unlock(&lock);
+
+ seq_printf(s, "tail/head fid 0x%04x/0x%04x, packets %u",
+ u32_get_bits(fid, MT_FL_Q2_CTRL_TAIL_FID),
+ u32_get_bits(fid, MT_FL_Q2_CTRL_HEAD_FID),
+ u32_get_bits(num, MT_FL_Q3_CTRL_PKT_NUM));
+}
- val = mt76_rr(dev, MT_FL_Q_EMPTY);
- for (i = 0; i < size; i++) {
- u32 ctrl, head, tail, queued;
+static void
+mt7996_fl_queues_show(struct seq_file *s, struct mt7996_dev *dev, bool pse,
+ const struct mt7996_hw_queue *queues, unsigned long empty)
+{
+ unsigned int i;
- if (val & BIT(map[i].index))
+ for_each_clear_bit(i, &empty, 32) {
+ if (!queues[i].name)
continue;
- ctrl = BIT(31) | (map[i].pid << 10) | ((u32)map[i].qid << 24);
- mt76_wr(dev, MT_FL_Q0_CTRL, ctrl);
+ seq_printf(s, "\t%s: ", queues[i].name);
+ mt7996_fl_queue_show(s, dev, pse, &queues[i], 0);
+ seq_puts(s, "\n");
+ }
+}
+
+static void
+mt7996_freepg_show(struct seq_file *s, u32 cnt, u32 head_tail)
+{
+ seq_printf(s, "\tfree pages 0x%04x, free for all 0x%04x, head/tail 0x%04x/0x%04x\n",
+ u32_get_bits(cnt, MT_FREEPG_CNT_FREE),
+ u32_get_bits(cnt, MT_FREEPG_CNT_FFA),
+ u32_get_bits(head_tail, MT_FREEPG_HEAD_TAIL_HEAD),
+ u32_get_bits(head_tail, MT_FREEPG_HEAD_TAIL_TAIL));
+}
+
+static void
+mt7996_pg_group_show(struct seq_file *s, const char *name, u32 group, u32 info)
+{
+ seq_printf(s, "\t%s group: max/min quota 0x%04x/0x%04x, used/reserved 0x%04x/0x%04x\n",
+ name, u32_get_bits(group, MT_PG_GROUP_MAX_QUOTA),
+ u32_get_bits(group, MT_PG_GROUP_MIN_QUOTA),
+ u32_get_bits(info, MT_PG_INFO_SRC_CNT),
+ u32_get_bits(info, MT_PG_INFO_RSV_CNT));
+}
+
+static unsigned int
+mt7996_ple_sta_cr_num(struct mt7996_dev *dev)
+{
+ return is_mt7996(&dev->mt76) ? 34 : 17;
+}
- head = mt76_get_field(dev, MT_FL_Q2_CTRL,
- GENMASK(11, 0));
- tail = mt76_get_field(dev, MT_FL_Q2_CTRL,
- GENMASK(27, 16));
- queued = mt76_get_field(dev, MT_FL_Q3_CTRL,
- GENMASK(11, 0));
+static unsigned int
+mt7996_ple_sta_cr(struct mt7996_dev *dev, unsigned int n)
+{
+ /* MT7992 and MT7990 keep the last station group in the
+ * extension register that follows the first 32 groups
+ */
+ if (!is_mt7996(&dev->mt76) && n == mt7996_ple_sta_cr_num(dev) - 1)
+ return 32;
- seq_printf(s, "\t%s: ", map[i].name);
- seq_printf(s, "queued:0x%03x head:0x%03x tail:0x%03x\n",
- queued, head, tail);
- }
+ return n;
}
static void
@@ -640,51 +774,53 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct mt7996_vif *mvif = msta->vif;
- struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink);
struct ieee80211_link_sta *link_sta;
struct seq_file *s = data;
struct ieee80211_vif *vif;
- struct mt7996_dev *dev;
unsigned int link_id;
- if (!phy)
- return;
-
vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
- dev = phy->dev;
rcu_read_lock();
for_each_sta_active_link(vif, sta, link_sta, link_id) {
struct mt7996_sta_link *msta_link;
struct mt76_vif_link *mlink;
+ struct mt7996_phy *phy;
+ unsigned int cr;
+ u16 wlan_idx;
u8 ac;
mlink = rcu_dereference(mvif->mt76.link[link_id]);
if (!mlink)
continue;
+ phy = mt7996_vif_link_phy(container_of(mlink,
+ struct mt7996_vif_link,
+ mt76));
+ if (!phy)
+ continue;
+
msta_link = mt7996_sta_link(msta, link_id);
if (!msta_link)
continue;
- for (ac = 0; ac < 4; ac++) {
- u32 idx = msta_link->wcid.idx >> 5, qlen, ctrl, val;
- u8 offs = msta_link->wcid.idx & GENMASK(4, 0);
-
- ctrl = BIT(31) | BIT(11) | (ac << 24);
- val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx));
+ wlan_idx = msta_link->wcid.idx;
+ cr = mt7996_ple_sta_cr(phy->dev, wlan_idx / 32);
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct mt7996_hw_queue q = {
+ .pid = MT_QUEUE_PORT_LMAC,
+ .qid = ac,
+ };
- if (val & BIT(offs))
+ if (mt76_rr(phy->dev, MT_PLE_AC_QEMPTY(ac, cr)) &
+ BIT(wlan_idx % 32))
continue;
- mt76_wr(dev,
- MT_FL_Q0_CTRL, ctrl | msta_link->wcid.idx);
- qlen = mt76_get_field(dev, MT_FL_Q3_CTRL,
- GENMASK(11, 0));
- seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
- sta->addr, msta_link->wcid.idx,
- mlink->wmm_idx, ac, qlen);
+ seq_printf(s, "\tSTA %pM wcid %u AC%u%u: ", sta->addr,
+ wlan_idx, mlink->wmm_idx, ac);
+ mt7996_fl_queue_show(s, phy->dev, false, &q, wlan_idx);
+ seq_puts(s, "\n");
}
}
@@ -692,78 +828,27 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
}
static int
-mt7996_hw_queues_show(struct seq_file *file, void *data)
+mt7996_hw_queues_show(struct seq_file *s, void *data)
{
- struct mt7996_dev *dev = file->private;
- struct mt7996_phy *phy = &dev->phy;
- static const struct hw_queue_map ple_queue_map[] = {
- { "CPU_Q0", 0, 1, MT_CTX0 },
- { "CPU_Q1", 1, 1, MT_CTX0 + 1 },
- { "CPU_Q2", 2, 1, MT_CTX0 + 2 },
- { "CPU_Q3", 3, 1, MT_CTX0 + 3 },
- { "ALTX_Q0", 8, 2, MT_LMAC_ALTX0 },
- { "BMC_Q0", 9, 2, MT_LMAC_BMC0 },
- { "BCN_Q0", 10, 2, MT_LMAC_BCN0 },
- { "PSMP_Q0", 11, 2, MT_LMAC_PSMP0 },
- { "ALTX_Q1", 12, 2, MT_LMAC_ALTX0 + 4 },
- { "BMC_Q1", 13, 2, MT_LMAC_BMC0 + 4 },
- { "BCN_Q1", 14, 2, MT_LMAC_BCN0 + 4 },
- { "PSMP_Q1", 15, 2, MT_LMAC_PSMP0 + 4 },
- };
- static const struct hw_queue_map pse_queue_map[] = {
- { "CPU Q0", 0, 1, MT_CTX0 },
- { "CPU Q1", 1, 1, MT_CTX0 + 1 },
- { "CPU Q2", 2, 1, MT_CTX0 + 2 },
- { "CPU Q3", 3, 1, MT_CTX0 + 3 },
- { "HIF_Q0", 8, 0, MT_HIF0 },
- { "HIF_Q1", 9, 0, MT_HIF0 + 1 },
- { "HIF_Q2", 10, 0, MT_HIF0 + 2 },
- { "HIF_Q3", 11, 0, MT_HIF0 + 3 },
- { "HIF_Q4", 12, 0, MT_HIF0 + 4 },
- { "HIF_Q5", 13, 0, MT_HIF0 + 5 },
- { "LMAC_Q", 16, 2, 0 },
- { "MDP_TXQ", 17, 2, 1 },
- { "MDP_RXQ", 18, 2, 2 },
- { "SEC_TXQ", 19, 2, 3 },
- { "SEC_RXQ", 20, 2, 4 },
- };
- u32 val, head, tail;
-
- /* ple queue */
- val = mt76_rr(dev, MT_PLE_FREEPG_CNT);
- head = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(11, 0));
- tail = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(27, 16));
- seq_puts(file, "PLE page info:\n");
- seq_printf(file,
- "\tTotal free page: 0x%08x head: 0x%03x tail: 0x%03x\n",
- val, head, tail);
-
- val = mt76_rr(dev, MT_PLE_PG_HIF_GROUP);
- head = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(11, 0));
- tail = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(27, 16));
- seq_printf(file, "\tHIF free page: 0x%03x res: 0x%03x used: 0x%03x\n",
- val, head, tail);
-
- seq_puts(file, "PLE non-empty queue info:\n");
- mt7996_hw_queue_read(file, ARRAY_SIZE(ple_queue_map),
- &ple_queue_map[0]);
-
- /* iterate per-sta ple queue */
- ieee80211_iterate_stations_atomic(phy->mt76->hw,
- mt7996_sta_hw_queue_read, file);
- phy = mt7996_phy2(dev);
- if (phy)
- ieee80211_iterate_stations_atomic(phy->mt76->hw,
- mt7996_sta_hw_queue_read, file);
- phy = mt7996_phy3(dev);
- if (phy)
- ieee80211_iterate_stations_atomic(phy->mt76->hw,
- mt7996_sta_hw_queue_read, file);
-
- /* pse queue */
- seq_puts(file, "PSE non-empty queue info:\n");
- mt7996_hw_queue_read(file, ARRAY_SIZE(pse_queue_map),
- &pse_queue_map[0]);
+ struct mt7996_dev *dev = s->private;
+
+ seq_puts(s, "PLE page info:\n");
+ mt7996_freepg_show(s, mt76_rr(dev, MT_PLE_FREEPG_CNT),
+ mt76_rr(dev, MT_PLE_FREEPG_HEAD_TAIL));
+ mt7996_pg_group_show(s, "HIF", mt76_rr(dev, MT_PLE_PG_HIF_GROUP),
+ mt76_rr(dev, MT_PLE_HIF_PG_INFO));
+
+ seq_puts(s, "PLE non-empty queue info:\n");
+ mt7996_fl_queues_show(s, dev, false, mt7996_ple_queues,
+ mt76_rr(dev, MT_FL_Q_EMPTY));
+ ieee80211_iterate_stations_atomic(dev->mphy.hw,
+ mt7996_sta_hw_queue_read, s);
+
+ seq_puts(s, "PSE non-empty queue info:\n");
+ mt7996_fl_queues_show(s, dev, true, mt7996_pse_queues,
+ mt76_rr(dev, MT_PSE_QUEUE_EMPTY));
+ mt7996_fl_queues_show(s, dev, true, mt7996_pse_queues_1,
+ mt76_rr(dev, MT_PSE_QUEUE_EMPTY_1));
return 0;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
index c1d860029801..cc94eade9950 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
@@ -541,6 +541,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
ieee80211_hw_set(hw, CHANCTX_STA_CSA);
ieee80211_hw_set(hw, CONNECTION_MONITOR);
+ ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
hw->max_tx_fragments = 4;
wiphy->txq_memory_limit = 32 << 20; /* 32 MiB */
@@ -1810,6 +1811,8 @@ error:
void mt7996_unregister_device(struct mt7996_dev *dev)
{
+ int i;
+
cancel_work_sync(&dev->dump_work);
cancel_work_sync(&dev->wed_rro.work);
cancel_work_sync(&dev->reset_work);
@@ -1820,6 +1823,10 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
mt7996_coredump_unregister(dev);
mt76_unregister_device(&dev->mt76);
mt7996_wed_rro_free(dev);
+ for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++) {
+ if (dev->mt76.napi[i].dev)
+ napi_disable(&dev->mt76.napi[i]);
+ }
mt7996_mcu_exit(dev);
mt7996_tx_token_put(dev);
mt7996_dma_cleanup(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index 1cba381a78b8..f36da545045b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -915,7 +915,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL;
if (mvif) {
if (wcid->offchannel)
- mlink = rcu_dereference(mvif->mt76.offchannel_link);
+ mlink = mt7996_offchannel_link(wcid);
if (!mlink && link_id != IEEE80211_LINK_UNSPECIFIED)
mlink = rcu_dereference(mvif->mt76.link[link_id]);
}
@@ -1057,7 +1057,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
if (!wcid)
wcid = &dev->mt76.global_wcid;
- if ((is_8023 || ieee80211_is_data_qos(hdr->frame_control)) && sta->mlo &&
+ if (sta && (is_8023 || ieee80211_is_data_qos(hdr->frame_control)) && sta->mlo &&
likely(tx_info->skb->protocol != cpu_to_be16(ETH_P_PAE))) {
u8 tid = tx_info->skb->priority & IEEE80211_QOS_CTL_TID_MASK;
@@ -1097,7 +1097,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
* compatible with 802.11 EAPOL frame, we do the translation by
* software
*/
- if (tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE) && sta->mlo) {
+ if (sta && tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE) && sta->mlo) {
struct ieee80211_hdr *hdr = (void *)tx_info->skb->data;
struct ieee80211_bss_conf *link_conf;
struct ieee80211_link_sta *link_sta;
@@ -1196,7 +1196,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
if (mvif) {
if (wcid->offchannel)
- mlink = rcu_dereference(mvif->mt76.offchannel_link);
+ mlink = mt7996_offchannel_link(wcid);
if (!mlink)
mlink = rcu_dereference(mvif->mt76.link[wcid->link_id]);
@@ -1503,7 +1503,8 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
}
}
- if (mtk_wed_device_active(&dev->mt76.mmio.wed) && wcid->sta) {
+ if ((mtk_wed_device_active(&dev->mt76.mmio.wed) ||
+ mt76_npu_device_active(&dev->mt76)) && wcid->sta) {
struct ieee80211_sta *sta;
u8 tid;
@@ -1936,6 +1937,9 @@ void mt7996_rro_rx_process(struct mt76_dev *mdev, void *data)
struct mt7996_msdu_page *p = NULL;
int i, seq_num = 0;
+ if (seq_id > MT7996_RRO_MAX_SESSION)
+ return;
+
for (i = 0; i < ind_count; i++) {
struct mt7996_wed_rro_addr *e;
struct mt76_rx_status *status;
@@ -2463,6 +2467,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
dev->recovery.hw_full_reset = true;
+ set_bit(MT76_RESTART, &dev->mphy.state);
set_bit(MT76_MCU_RESET, &dev->mphy.state);
wake_up(&dev->mt76.mcu.wait);
ieee80211_stop_queues(hw);
@@ -2944,6 +2949,10 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
changed = msta_link->changed;
msta_link->changed = 0;
+
+ if (!msta_link->connected)
+ continue;
+
mvif = msta_link->sta->vif;
vif = container_of((void *)mvif, struct ieee80211_vif,
drv_priv);
@@ -3285,8 +3294,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
flow->tsf = le64_to_cpu(twt_agrt->twt);
if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
- MCU_TWT_AGRT_ADD))
+ MCU_TWT_AGRT_ADD)) {
+ list_del(&flow->list);
goto unlock;
+ }
setup_cmd = TWT_SETUP_CMD_ACCEPT;
dev->twt.table_mask |= BIT(table_id);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index dd7a01c02a7c..9321c8146e2a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -34,6 +34,11 @@ int mt7996_run(struct mt7996_phy *phy)
if (ret)
return ret;
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ true);
+ if (ret)
+ return ret;
+
set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
ieee80211_queue_delayed_work(dev->mphy.hw, &phy->mt76->mac_work,
@@ -54,6 +59,8 @@ static int mt7996_start(struct ieee80211_hw *hw)
flush_work(&dev->init_work);
+ clear_bit(MT76_RESTART, &dev->mphy.state);
+
mutex_lock(&dev->mt76.mutex);
ret = mt7996_mcu_set_hdr_trans(dev, true);
if (!ret && !is_mt7996(&dev->mt76)) {
@@ -272,8 +279,9 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
struct mt7996_key_iter_data {
- enum set_key_cmd cmd;
- unsigned int link_id;
+ enum set_key_cmd cmd;
+ unsigned int link_id;
+ struct ieee80211_sta *sta;
};
static void
@@ -283,10 +291,13 @@ mt7996_key_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
{
struct mt7996_key_iter_data *it = data;
- if (sta)
+ if (sta != it->sta)
+ return;
+
+ if (key->link_id >= 0 && key->link_id != it->link_id)
return;
- WARN_ON(mt7996_set_hw_key(hw, it->cmd, vif, NULL, it->link_id, key));
+ WARN_ON(mt7996_set_hw_key(hw, it->cmd, vif, sta, it->link_id, key));
}
int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
@@ -307,15 +318,15 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
if ((mvif->mt76.valid_links & BIT(link_conf->link_id)) &&
!mlink->offchannel) {
- if (vif->type == NL80211_IFTYPE_AP)
- return mt7996_mcu_mld_link_oper(dev, link_conf, link,
- true);
-
/* update the link address */
ret = mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, true);
if (ret)
return ret;
+ if (vif->type == NL80211_IFTYPE_AP)
+ return mt7996_mcu_mld_link_oper(dev, link_conf, link,
+ true);
+
return mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink,
msta_link, true);
}
@@ -333,6 +344,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
return -ENOSPC;
link->mld_idx = mld_idx;
+ eth_zero_addr(link->bmc_bssid);
mlink->omac_idx = idx;
mlink->band_idx = band_idx;
mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
@@ -417,19 +429,12 @@ static void mt7996_vif_link_destroy(struct mt7996_phy *phy,
struct mt7996_sta_link *msta_link = &link->msta_link;
unsigned int link_id = msta_link->wcid.link_id;
struct mt76_vif_link *mlink = &link->mt76;
- struct mt7996_key_iter_data it = {
- .cmd = SET_KEY,
- .link_id = link_id,
- };
struct mt7996_dev *dev = phy->dev;
int idx = msta_link->wcid.idx;
if (!link_conf)
link_conf = &vif->bss_conf;
- if (!mlink->wcid->offchannel)
- ieee80211_iter_keys(phy->mt76->hw, vif, mt7996_key_iter, &it);
-
mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
CONN_STATE_DISCONNECT, false);
mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, false);
@@ -565,13 +570,14 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
for (i = 0; i < MT7996_MAX_RADIOS; i++) {
struct mt7996_phy *phy = dev->radio_phy[i];
- if (!phy || !(wdev->radio_mask & BIT(i)) ||
- test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
+ if (!phy || !(wdev->radio_mask & BIT(i)))
continue;
- err = mt7996_run(phy);
- if (err)
- goto out;
+ if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) {
+ err = mt7996_run(phy);
+ if (err)
+ goto out;
+ }
if (vif->type == NL80211_IFTYPE_MONITOR)
mt7996_set_monitor(phy, true);
@@ -897,6 +903,23 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct mt7996_vif_link *link,
}
static void
+mt7996_vif_link_bss_add(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct mt7996_vif_link *link, bool newly)
+{
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ newly = !ether_addr_equal_unaligned(link->bmc_bssid,
+ link_conf->bssid);
+ memcpy(link->bmc_bssid, link_conf->bssid, ETH_ALEN);
+ }
+
+ mt7996_mcu_add_bss_info(phy, vif, link_conf, &link->mt76,
+ &link->msta_link, true);
+ mt7996_mcu_add_sta(phy->dev, link_conf, NULL, link, NULL,
+ CONN_STATE_PORT_SECURE, newly);
+}
+
+static void
mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u64 changed)
{
@@ -927,12 +950,8 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (!phy)
continue;
- mt7996_mcu_add_bss_info(phy, vif, link_conf,
- &link->mt76, &link->msta_link,
- true);
- mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
- CONN_STATE_PORT_SECURE,
- !!(changed & BSS_CHANGED_BSSID));
+ mt7996_vif_link_bss_add(phy, vif, link_conf, link,
+ !!(changed & BSS_CHANGED_BSSID));
}
}
@@ -978,13 +997,11 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
* and then peer references bss_info_rfch to set bandwidth cap.
*/
if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
- (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
- mt7996_mcu_add_bss_info(phy, vif, info, &link->mt76,
- &link->msta_link, true);
- mt7996_mcu_add_sta(dev, info, NULL, link, NULL,
- CONN_STATE_PORT_SECURE,
- !!(changed & BSS_CHANGED_BSSID));
- }
+ (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon))
+ mt7996_vif_link_bss_add(phy, vif, info, link,
+ !!(changed & BSS_CHANGED_BSSID));
+ else if (changed & BSS_CHANGED_BSSID)
+ eth_zero_addr(link->bmc_bssid);
if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
mt7996_mcu_set_protection(phy, link, info->ht_operation_mode,
@@ -1131,9 +1148,90 @@ mt7996_sta_init_txq_wcid(struct ieee80211_sta *sta, int idx)
}
}
+static void
+mt7996_sta_deflink_set(struct ieee80211_sta *sta, unsigned int link_id,
+ int idx)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+
+ msta->deflink_id = link_id;
+ mt7996_sta_init_txq_wcid(sta, idx);
+}
+
+static void
+mt7996_sta_link_hdr_trans_set(struct mt7996_sta *msta,
+ struct mt7996_sta_link *msta_link)
+{
+ assign_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags,
+ test_bit(MT_WCID_FLAG_4ADDR, &msta->wcid_flags));
+ assign_bit(MT_WCID_FLAG_HDR_TRANS, &msta_link->wcid.flags,
+ test_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid_flags));
+}
+
+static bool
+mt7996_sta_deflink_busy(struct mt7996_dev *dev, struct mt7996_sta *msta)
+{
+ unsigned int link_id;
+
+ for (link_id = 0; link_id < ARRAY_SIZE(msta->link); link_id++) {
+ if (mt7996_sta_link_protected(dev, msta, link_id) ==
+ &msta->deflink)
+ return true;
+ }
+
+ return false;
+}
+
+static void
+mt7996_sta_deflink_release(struct mt7996_dev *dev, struct ieee80211_sta *sta,
+ unsigned int link_id)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_sta_link *msta_seclink;
+
+ if (msta->deflink_id != link_id)
+ return;
+
+ msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+ if (msta->seclink_id == link_id)
+ return;
+
+ /* switch to the secondary link */
+ msta_seclink = mt76_dereference(msta->link[msta->seclink_id],
+ &dev->mt76);
+ if (msta_seclink)
+ mt7996_sta_deflink_set(sta, msta->seclink_id,
+ msta_seclink->wcid.idx);
+}
+
+static u8
+mt7996_sta_seclink_get(struct mt7996_dev *dev, struct mt7996_sta *msta)
+{
+ struct mt7996_sta_link *msta_link;
+ unsigned int link_id;
+
+ if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED)
+ return IEEE80211_LINK_UNSPECIFIED;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, msta->seclink_id);
+ if (msta->seclink_id != msta->deflink_id && msta_link &&
+ msta_link->wcid.link_valid)
+ return msta->seclink_id;
+
+ for (link_id = 0; link_id < ARRAY_SIZE(msta->link); link_id++) {
+ if (link_id == msta->deflink_id)
+ continue;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ if (msta_link && msta_link->wcid.link_valid)
+ return link_id;
+ }
+
+ return msta->deflink_id;
+}
+
static int
mt7996_mac_sta_init_link(struct mt7996_dev *dev,
- struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link, unsigned int link_id)
{
@@ -1150,25 +1248,24 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
if (idx < 0)
return -ENOSPC;
- if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) {
- msta_link = &msta->deflink;
- msta->deflink_id = link_id;
- msta->seclink_id = msta->deflink_id;
- mt7996_sta_init_txq_wcid(sta, idx);
- } else {
+ if (mt7996_sta_deflink_busy(dev, msta)) {
msta_link = kzalloc_obj(*msta_link);
- if (!msta_link)
+ if (!msta_link) {
+ mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx);
return -ENOMEM;
-
- if (msta->seclink_id == msta->deflink_id &&
- (sta->valid_links & ~BIT(msta->deflink_id)))
- msta->seclink_id = __ffs(sta->valid_links &
- ~BIT(msta->deflink_id));
+ }
+ } else {
+ msta_link = &msta->deflink;
}
+ if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED)
+ mt7996_sta_deflink_set(sta, link_id, idx);
+
INIT_LIST_HEAD(&msta_link->rc_list);
INIT_LIST_HEAD(&msta_link->wcid.poll_list);
msta_link->sta = msta;
+ msta_link->connected = false;
+ msta_link->wcid.tx_info = 0;
msta_link->wcid.sta = 1;
msta_link->wcid.idx = idx;
msta_link->wcid.link_id = link_id;
@@ -1178,14 +1275,15 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
if (link_sta->sta->tdls)
set_bit(MT_WCID_FLAG_TDLS_PEER, &msta_link->wcid.flags);
+ mt7996_sta_link_hdr_trans_set(msta, msta_link);
+
ewma_avg_signal_init(&msta_link->avg_ack_signal);
ewma_signal_init(&msta_link->wcid.rssi);
rcu_assign_pointer(msta->link[link_id], msta_link);
+ msta->seclink_id = mt7996_sta_seclink_get(dev, msta);
mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- mt7996_mcu_add_sta(dev, link_conf, link_sta, link, msta_link,
- CONN_STATE_DISCONNECT, true);
rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid);
mt76_wcid_init(&msta_link->wcid, phy->mt76->band_idx);
@@ -1217,28 +1315,9 @@ void mt7996_mac_sta_remove_link(struct mt7996_dev *dev,
mt7996_mac_wtbl_update(dev, msta_link->wcid.idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- if (msta->deflink_id == link_id) {
- msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
- if (msta->seclink_id == link_id) {
- /* no secondary link available */
- msta->seclink_id = msta->deflink_id;
- } else {
- struct mt7996_sta_link *msta_seclink;
-
- /* switch to the secondary link */
- msta_seclink = mt76_dereference(
- msta->link[msta->seclink_id],
- &dev->mt76);
- if (msta_seclink) {
- msta->deflink_id = msta->seclink_id;
- mt7996_sta_init_txq_wcid(sta,
- msta_seclink->wcid.idx);
- }
- }
- } else if (msta->seclink_id == link_id) {
- msta->seclink_id = msta->deflink_id;
- }
+ mt7996_sta_deflink_release(dev, sta, link_id);
msta_link->wcid.link_valid = false;
+ msta->seclink_id = mt7996_sta_seclink_get(dev, msta);
}
if (flush) {
@@ -1256,6 +1335,36 @@ void mt7996_mac_sta_remove_link(struct mt7996_dev *dev,
}
}
+u16 mt7996_mac_sta_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+ u16 links = 0;
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct mt7996_sta_link *msta_link;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ if (!msta_link)
+ continue;
+
+ if (sta->valid_links && !msta_link->wcid.link_valid)
+ continue;
+
+ if (!link_conf_dereference_protected(vif, link_id))
+ continue;
+
+ if (!mt7996_vif_link(dev, vif, link_id))
+ continue;
+
+ links |= BIT(link_id);
+ }
+
+ return links;
+}
+
static void
mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, unsigned long links,
@@ -1272,11 +1381,11 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, unsigned long new_links)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned long created = 0, returned = 0;
unsigned int link_id;
int err = 0;
for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) {
- struct ieee80211_bss_conf *link_conf;
struct ieee80211_link_sta *link_sta;
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
@@ -1285,11 +1394,16 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
if (msta_link) {
msta_link->wcid.link_valid = true;
+ mt7996_sta_link_hdr_trans_set(msta, msta_link);
+ if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED)
+ mt7996_sta_deflink_set(sta, link_id,
+ msta_link->wcid.idx);
+ msta->seclink_id = mt7996_sta_seclink_get(dev, msta);
+ returned |= BIT(link_id);
continue;
}
- link_conf = link_conf_dereference_protected(vif, link_id);
- if (!link_conf) {
+ if (!link_conf_dereference_protected(vif, link_id)) {
err = -EINVAL;
goto error_unlink;
}
@@ -1312,43 +1426,24 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
goto error_unlink;
}
- err = mt7996_mac_sta_init_link(dev, link_conf, link_sta, link,
- link_id);
+ err = mt7996_mac_sta_init_link(dev, link_sta, link, link_id);
if (err)
goto error_unlink;
mphy->num_sta++;
+ created |= BIT(link_id);
}
return 0;
error_unlink:
- mt7996_mac_sta_remove_links(dev, vif, sta, new_links, true);
+ mt7996_mac_sta_remove_links(dev, vif, sta, created, true);
+ mt7996_mac_sta_remove_links(dev, vif, sta, returned, false);
return err;
}
static int
-mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 old_links,
- u16 new_links)
-{
- struct mt7996_dev *dev = mt7996_hw_dev(hw);
- unsigned long add = new_links & ~old_links;
- unsigned long rem = old_links & ~new_links;
- int ret;
-
- mutex_lock(&dev->mt76.mutex);
-
- mt7996_mac_sta_remove_links(dev, vif, sta, rem, false);
- ret = mt7996_mac_sta_add_links(dev, vif, sta, add);
-
- mutex_unlock(&dev->mt76.mutex);
-
- return ret;
-}
-
-static int
mt7996_mac_sta_add(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
@@ -1361,6 +1456,9 @@ mt7996_mac_sta_add(struct mt7996_dev *dev, struct ieee80211_vif *vif,
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
msta->seclink_id = IEEE80211_LINK_UNSPECIFIED;
+ msta->conn_state = CONN_STATE_DISCONNECT;
+ msta->ba_tx_mask = 0;
+ msta->ba_rx_mask = 0;
msta->vif = mvif;
err = mt7996_mac_sta_add_links(dev, vif, sta, links);
@@ -1370,34 +1468,26 @@ mt7996_mac_sta_add(struct mt7996_dev *dev, struct ieee80211_vif *vif,
}
static int
-mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, enum mt76_sta_event ev)
+mt7996_mac_sta_links_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long links,
+ enum mt76_sta_event ev)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- unsigned long links = sta->valid_links;
- struct ieee80211_link_sta *link_sta;
+ unsigned long iter_links = links;
unsigned int link_id;
- int err = 0;
-
- mutex_lock(&dev->mt76.mutex);
+ int err;
- for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ for_each_set_bit(link_id, &iter_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_link_sta *link_sta;
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
int i;
link_conf = link_conf_dereference_protected(vif, link_id);
- if (!link_conf)
- continue;
-
- link = mt7996_vif_link(dev, vif, link_id);
- if (!link)
- continue;
-
+ link_sta = link_sta_dereference_protected(sta, link_id);
msta_link = mt7996_sta_link_protected(dev, msta, link_id);
- if (!msta_link)
- continue;
+ link = mt7996_vif_link(dev, vif, link_id);
switch (ev) {
case MT76_STA_EVENT_ASSOC:
@@ -1405,12 +1495,13 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
link, msta_link,
CONN_STATE_CONNECT, true);
if (err)
- goto unlock;
+ return err;
+ msta_link->connected = true;
err = mt7996_mcu_add_rate_ctrl(dev, msta_link->sta, vif,
link_id, false);
if (err)
- goto unlock;
+ return err;
msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET;
break;
@@ -1419,7 +1510,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
link, msta_link,
CONN_STATE_PORT_SECURE, false);
if (err)
- goto unlock;
+ return err;
break;
case MT76_STA_EVENT_DISASSOC:
for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++)
@@ -1440,13 +1531,350 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
break;
}
}
-unlock:
+
+ return 0;
+}
+
+static void
+mt7996_mac_sta_link_ps_leave(struct mt7996_dev *dev,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link)
+{
+ /* HW cannot derive the BSSID from 4-address non-AMSDU frames,
+ * so PS exit is missed on links other than the setup link.
+ * Let the firmware wake those links instead.
+ */
+ if (!is_mt7996(&dev->mt76) ||
+ !test_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags) ||
+ msta_link->wcid.link_id == msta_link->sta->deflink_id)
+ return;
+
+ mt7996_mcu_ps_leave(dev, link, msta_link);
+}
+
+static void
+mt7996_mac_sta_clear_connected(struct mt7996_dev *dev, struct mt7996_sta *msta)
+{
+ unsigned int link_id;
+
+ for (link_id = 0; link_id < ARRAY_SIZE(msta->link); link_id++) {
+ struct mt7996_sta_link *msta_link;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ if (msta_link)
+ msta_link->connected = false;
+ }
+}
+
+static int
+mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, enum mt76_sta_event ev)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ int err;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ err = mt7996_mac_sta_links_event(dev, vif, sta,
+ mt7996_mac_sta_links(dev, vif, sta),
+ ev);
+ if (err)
+ goto out;
+
+ switch (ev) {
+ case MT76_STA_EVENT_ASSOC:
+ msta->conn_state = CONN_STATE_CONNECT;
+ break;
+ case MT76_STA_EVENT_AUTHORIZE:
+ msta->conn_state = CONN_STATE_PORT_SECURE;
+ break;
+ case MT76_STA_EVENT_DISASSOC:
+ msta->conn_state = CONN_STATE_DISCONNECT;
+ mt7996_mac_sta_clear_connected(dev, msta);
+ break;
+ }
+
+out:
mutex_unlock(&dev->mt76.mutex);
return err;
}
static void
+mt7996_mac_sta_links_set_keys(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long links)
+{
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_key_iter_data it = {
+ .cmd = SET_KEY,
+ .link_id = link_id,
+ .sta = sta,
+ };
+
+ ieee80211_iter_keys(mt76_hw(dev), vif, mt7996_key_iter, &it);
+ }
+}
+
+static bool
+mt7996_rx_reorder_host(struct mt7996_dev *dev)
+{
+ return !mt7996_has_hwrro(dev) &&
+ !mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+ !mt76_npu_device_active(&dev->mt76);
+}
+
+static u16
+mt7996_mac_sta_ba_rx_ssn(struct mt7996_dev *dev, struct mt7996_sta *msta,
+ unsigned int tid)
+{
+ struct mt76_rx_tid *tid_rx;
+ u16 ssn;
+
+ tid_rx = mt76_dereference(msta->deflink.wcid.aggr[tid], &dev->mt76);
+ if (!tid_rx)
+ return msta->ba_rx[tid].ssn;
+
+ spin_lock_bh(&tid_rx->lock);
+ ssn = tid_rx->head;
+ spin_unlock_bh(&tid_rx->lock);
+
+ return ssn;
+}
+
+static int
+mt7996_mac_sta_links_ba_add(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long links)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ u16 rx_restart = 0;
+ unsigned int tid;
+ int err;
+
+ if (!links)
+ return 0;
+
+ for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
+ struct ieee80211_ampdu_params params = {
+ .sta = sta,
+ .tid = tid,
+ };
+
+ if (msta->ba_tx_mask & BIT(tid)) {
+ params.ssn = msta->ba_tx[tid].ssn;
+ params.buf_size = msta->ba_tx[tid].buf_size;
+ params.amsdu = msta->ba_tx[tid].amsdu;
+ err = mt7996_mcu_add_tx_ba(dev, &params, vif, links,
+ true);
+ if (err)
+ return err;
+ }
+
+ if (!(msta->ba_rx_mask & BIT(tid)))
+ continue;
+
+ /* without host reordering, the reorder head stays at the
+ * session start, so have the originator set up a new session
+ */
+ if (!mt7996_rx_reorder_host(dev)) {
+ rx_restart |= BIT(tid);
+ continue;
+ }
+
+ params.ssn = mt7996_mac_sta_ba_rx_ssn(dev, msta, tid);
+ params.buf_size = msta->ba_rx[tid].buf_size;
+ params.amsdu = msta->ba_rx[tid].amsdu;
+ err = mt7996_mcu_add_rx_ba(dev, &params, vif, links, true);
+ if (err)
+ return err;
+ }
+
+ if (rx_restart)
+ ieee80211_stop_rx_ba_session(vif, rx_restart, sta->addr);
+
+ return 0;
+}
+
+static int
+mt7996_mac_sta_links_sync(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long links,
+ unsigned long add)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned long new_links = 0, resumed = 0, joined, updated;
+ unsigned int link_id;
+ int err;
+
+ if (msta->conn_state == CONN_STATE_DISCONNECT)
+ return 0;
+
+ links &= mt7996_mac_sta_links(dev, vif, sta);
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_sta_link *msta_link;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ if (!msta_link->connected)
+ new_links |= BIT(link_id);
+ else if (!(msta_link->wcid.tx_info & MT_WCID_TX_INFO_SET))
+ resumed |= BIT(link_id);
+ }
+
+ err = mt7996_mac_sta_links_event(dev, vif, sta, new_links,
+ MT76_STA_EVENT_ASSOC);
+ if (err)
+ return err;
+
+ for_each_set_bit(link_id, &resumed, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ link = mt7996_vif_link(dev, vif, link_id);
+
+ if (msta->conn_state != CONN_STATE_PORT_SECURE) {
+ err = mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link,
+ msta_link);
+ if (err)
+ return err;
+ }
+
+ err = mt7996_mcu_add_rate_ctrl(dev, msta, vif, link_id, false);
+ if (err)
+ return err;
+
+ msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ }
+
+ mt7996_mac_sta_links_set_keys(dev, vif, sta, links & add);
+
+ updated = new_links;
+ if (msta->conn_state == CONN_STATE_PORT_SECURE) {
+ updated |= resumed;
+ err = mt7996_mac_sta_links_event(dev, vif, sta, updated,
+ MT76_STA_EVENT_AUTHORIZE);
+ if (err)
+ return err;
+ }
+
+ joined = new_links | resumed;
+ for_each_set_bit(link_id, &joined, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ link = mt7996_vif_link(dev, vif, link_id);
+ mt7996_mac_sta_link_ps_leave(dev, link, msta_link);
+ }
+
+ updated = sta->mlo ? links & ~updated : 0;
+ for_each_set_bit(link_id, &updated, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ link = mt7996_vif_link(dev, vif, link_id);
+ err = mt7996_mcu_update_mld_sta(dev, vif, sta, link, msta_link);
+ if (err)
+ return err;
+ }
+
+ return mt7996_mac_sta_links_ba_add(dev, vif, sta, new_links | resumed);
+}
+
+static void
+mt7996_mac_sta_links_reset(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long links)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+ int i;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ if (!msta_link)
+ continue;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (link) {
+ for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++)
+ mt7996_mac_twt_teardown_flow(dev, link,
+ msta_link, i);
+ }
+
+ msta_link->wcid.tx_info &= ~MT_WCID_TX_INFO_SET;
+ msta_link->wcid.hw_key_idx = -1;
+ }
+}
+
+static void
+mt7996_mac_sta_links_bss_add(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long links)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned int link_id;
+
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ msta->conn_state == CONN_STATE_DISCONNECT)
+ return;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_bss_conf *link_conf;
+ struct mt7996_vif_link *link;
+ struct mt7996_phy *phy;
+
+ link_conf = link_conf_dereference_protected(vif, link_id);
+ if (!link_conf || !link_conf->bssid ||
+ is_zero_ether_addr(link_conf->bssid))
+ continue;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ phy = mt7996_vif_link_phy(link);
+ if (phy)
+ mt7996_vif_link_bss_add(phy, vif, link_conf, link, true);
+ }
+}
+
+static int
+mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 old_links,
+ u16 new_links)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ unsigned long add = new_links & ~old_links;
+ unsigned long rem = old_links & ~new_links;
+ int ret;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ mt7996_mac_sta_links_reset(dev, vif, sta, rem);
+ mt7996_mac_sta_remove_links(dev, vif, sta, rem, false);
+ mt7996_mac_sta_links_bss_add(dev, vif, sta, add);
+ ret = mt7996_mac_sta_add_links(dev, vif, sta, add);
+ if (ret)
+ goto out;
+
+ ret = mt7996_mac_sta_links_sync(dev, vif, sta, new_links, add);
+ if (ret) {
+ mt7996_mac_sta_links_reset(dev, vif, sta, add);
+ mt7996_mac_sta_remove_links(dev, vif, sta, add, false);
+ mt7996_mac_sta_links_sync(dev, vif, sta, new_links & ~add, 0);
+ }
+
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static void
mt7996_mac_sta_remove(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
@@ -1477,6 +1905,30 @@ mt7996_set_active_links(struct ieee80211_vif *vif)
ieee80211_set_active_links_async(vif, active_links);
}
+static void
+mt7996_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ unsigned int link_id;
+
+ mutex_lock(&dev->mt76.mutex);
+ spin_lock_bh(&dev->mt76.status_lock);
+
+ for (link_id = 0; link_id < ARRAY_SIZE(msta->link); link_id++) {
+ struct mt7996_sta_link *msta_link;
+
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ if (msta_link)
+ rcu_assign_pointer(dev->mt76.wcid[msta_link->wcid.idx],
+ NULL);
+ }
+
+ spin_unlock_bh(&dev->mt76.status_lock);
+ mutex_unlock(&dev->mt76.mutex);
+}
+
static int
mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, enum ieee80211_sta_state old_state,
@@ -1493,6 +1945,17 @@ mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
new_state == IEEE80211_STA_NOTEXIST)
mt7996_mac_sta_remove(dev, vif, sta);
+ if (old_state == IEEE80211_STA_AUTHORIZED &&
+ new_state == IEEE80211_STA_ASSOC) {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+
+ mutex_lock(&dev->mt76.mutex);
+ msta->conn_state = CONN_STATE_CONNECT;
+ mutex_unlock(&dev->mt76.mutex);
+
+ return 0;
+ }
+
if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC) {
mt7996_set_active_links(vif);
@@ -1624,6 +2087,21 @@ static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx,
return ret;
}
+static void
+mt7996_sta_ba_update(u16 *mask, struct mt7996_sta_ba *ba,
+ struct ieee80211_ampdu_params *params, bool enable)
+{
+ if (!enable) {
+ *mask &= ~BIT(params->tid);
+ return;
+ }
+
+ *mask |= BIT(params->tid);
+ ba[params->tid].ssn = params->ssn;
+ ba[params->tid].buf_size = params->buf_size;
+ ba[params->tid].amsdu = params->amsdu;
+}
+
static int
mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params)
@@ -1635,6 +2113,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u16 tid = params->tid;
u16 ssn = params->ssn;
struct mt76_txq *mtxq;
+ unsigned long links;
int ret = 0;
if (!txq)
@@ -1644,6 +2123,8 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mutex_lock(&dev->mt76.mutex);
+ links = mt7996_mac_sta_links(dev, vif, sta);
+
switch (params->action) {
case IEEE80211_AMPDU_RX_START:
/* Since packets belonging to the same TID can be split over
@@ -1652,22 +2133,30 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
*/
mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid,
ssn, params->buf_size);
- ret = mt7996_mcu_add_rx_ba(dev, params, vif, true);
+ ret = mt7996_mcu_add_rx_ba(dev, params, vif, links, true);
+ mt7996_sta_ba_update(&msta->ba_rx_mask, msta->ba_rx, params,
+ !ret);
break;
case IEEE80211_AMPDU_RX_STOP:
mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid);
- ret = mt7996_mcu_add_rx_ba(dev, params, vif, false);
+ mt7996_sta_ba_update(&msta->ba_rx_mask, msta->ba_rx, params,
+ false);
+ ret = mt7996_mcu_add_rx_ba(dev, params, vif, links, false);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
mtxq->aggr = true;
mtxq->send_bar = false;
- ret = mt7996_mcu_add_tx_ba(dev, params, vif, true);
+ ret = mt7996_mcu_add_tx_ba(dev, params, vif, links, true);
+ mt7996_sta_ba_update(&msta->ba_tx_mask, msta->ba_tx, params,
+ !ret);
break;
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
mtxq->aggr = false;
clear_bit(tid, &msta->deflink.wcid.ampdu_state);
- ret = mt7996_mcu_add_tx_ba(dev, params, vif, false);
+ mt7996_sta_ba_update(&msta->ba_tx_mask, msta->ba_tx, params,
+ false);
+ ret = mt7996_mcu_add_tx_ba(dev, params, vif, links, false);
break;
case IEEE80211_AMPDU_TX_START:
set_bit(tid, &msta->deflink.wcid.ampdu_state);
@@ -1676,7 +2165,9 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_TX_STOP_CONT:
mtxq->aggr = false;
clear_bit(tid, &msta->deflink.wcid.ampdu_state);
- ret = mt7996_mcu_add_tx_ba(dev, params, vif, false);
+ mt7996_sta_ba_update(&msta->ba_tx_mask, msta->ba_tx, params,
+ false);
+ ret = mt7996_mcu_add_tx_ba(dev, params, vif, links, false);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
}
@@ -1884,9 +2375,16 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt7996_sta_link *msta_link = &msta->deflink;
- struct rate_info *txrate = &msta_link->wcid.rate;
+ struct mt7996_sta_link *msta_link;
+ struct rate_info *txrate;
+ rcu_read_lock();
+
+ msta_link = mt7996_sta_link(msta, msta->deflink_id);
+ if (!msta_link)
+ goto out;
+
+ txrate = &msta_link->wcid.rate;
if (txrate->legacy || txrate->flags) {
if (txrate->legacy) {
sinfo->txrate.legacy = txrate->legacy;
@@ -1929,6 +2427,9 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
sinfo->rx_packets = msta_link->wcid.stats.rx_packets;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
}
+
+out:
+ rcu_read_unlock();
}
static void mt7996_link_rate_ctrl_update(void *data,
@@ -2025,6 +2526,7 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
mutex_lock(&dev->mt76.mutex);
+ assign_bit(MT_WCID_FLAG_4ADDR, &msta->wcid_flags, enabled);
for_each_sta_active_link(vif, sta, link_sta, link_id) {
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
@@ -2037,23 +2539,12 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
if (!msta_link)
continue;
- if (enabled)
- set_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags);
- else
- clear_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags);
-
- if (!msta_link->wcid.sta)
+ mt7996_sta_link_hdr_trans_set(msta, msta_link);
+ if (!msta_link->connected)
continue;
mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link);
-
- /* HW cannot derive the BSSID from 4-address non-AMSDU frames,
- * so PS exit is missed on links other than the setup link.
- * Let the firmware wake those links instead.
- */
- if (enabled && msta->deflink_id != link_id &&
- is_mt7996(&dev->mt76))
- mt7996_mcu_ps_leave(dev, link, msta_link);
+ mt7996_mac_sta_link_ps_leave(dev, link, msta_link);
}
mutex_unlock(&dev->mt76.mutex);
@@ -2071,6 +2562,7 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
mutex_lock(&dev->mt76.mutex);
+ assign_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid_flags, enabled);
for_each_sta_active_link(vif, sta, link_sta, link_id) {
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
@@ -2083,14 +2575,8 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
if (!msta_link)
continue;
- if (enabled)
- set_bit(MT_WCID_FLAG_HDR_TRANS,
- &msta_link->wcid.flags);
- else
- clear_bit(MT_WCID_FLAG_HDR_TRANS,
- &msta_link->wcid.flags);
-
- if (!msta_link->wcid.sta)
+ mt7996_sta_link_hdr_trans_set(msta, msta_link);
+ if (!msta_link->connected)
continue;
mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link);
@@ -2228,11 +2714,15 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
{
struct mt76_ethtool_worker_info *wi = wi_data;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt7996_sta_link *msta_link = &msta->deflink;
+ struct mt7996_sta_link *msta_link;
if (msta->vif->deflink.mt76.idx != wi->idx)
return;
+ msta_link = mt7996_sta_link(msta, msta->deflink_id);
+ if (!msta_link)
+ return;
+
mt76_ethtool_worker(wi, &msta_link->wcid.stats, true);
}
@@ -2568,7 +3058,7 @@ const struct ieee80211_ops mt7996_ops = {
.vif_cfg_changed = mt7996_vif_cfg_changed,
.link_info_changed = mt7996_link_info_changed,
.sta_state = mt7996_sta_state,
- .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ .sta_pre_rcu_remove = mt7996_sta_pre_rcu_remove,
.link_sta_rc_update = mt7996_link_sta_rc_update,
.set_key = mt7996_set_key,
.ampdu_action = mt7996_ampdu_action,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index cd7587ce5b8a..c324d199b212 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -488,6 +488,9 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
struct mt7996_mcu_countdown_notify *event;
struct mt7996_mcu_countdown_data cdata;
+ if (skb->len < sizeof(*rxd) + sizeof(*hdr))
+ return;
+
if (hdr->band >= ARRAY_SIZE(dev->mt76.phys))
return;
@@ -497,9 +500,14 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
tail = skb->data + skb->len;
data += sizeof(*hdr);
- while (data + sizeof(*tlv) < tail && le16_to_cpu(tlv->len)) {
- event = (struct mt7996_mcu_countdown_notify *)tlv->data;
+ while (data + sizeof(*tlv) <= tail) {
+ u16 tag_len = le16_to_cpu(tlv->len);
+
+ if (tag_len < sizeof(*tlv) + sizeof(*event) ||
+ data + tag_len > tail)
+ break;
+ event = (struct mt7996_mcu_countdown_notify *)tlv->data;
cdata.omac_idx = event->omac_idx;
switch (le16_to_cpu(tlv->tag)) {
@@ -517,7 +525,7 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
break;
}
- data += le16_to_cpu(tlv->len);
+ data += tag_len;
tlv = (struct tlv *)data;
}
}
@@ -572,21 +580,32 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
{
#define UNI_EVENT_FW_LOG_FORMAT 0
struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
- const char *data = (char *)&rxd[1] + 4, *type;
- struct tlv *tlv = (struct tlv *)data;
+ const char *data, *type;
+ struct tlv *tlv;
int len;
+ if (skb->len < sizeof(*rxd))
+ return;
+
if (!(rxd->option & MCU_UNI_CMD_EVENT)) {
- len = skb->len - sizeof(*rxd);
data = (char *)&rxd[1];
+ len = skb->len - sizeof(*rxd);
goto out;
}
+ if (skb->len < sizeof(*rxd) + 4 + sizeof(*tlv))
+ return;
+
+ tlv = (struct tlv *)((char *)&rxd[1] + 4);
if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT)
return;
- data += sizeof(*tlv) + 4;
+ if (le16_to_cpu(tlv->len) < sizeof(*tlv) + 4)
+ return;
+
+ data = (char *)tlv + sizeof(*tlv) + 4;
len = le16_to_cpu(tlv->len) - sizeof(*tlv) - 4;
+ len = min_t(int, len, skb->len - (int)(data - (char *)skb->data));
out:
switch (rxd->s2d_index) {
@@ -648,13 +667,38 @@ static void
mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
{
struct mt7996_mcu_all_sta_info_event *res;
+ u32 elem_size;
+ u16 sta_num;
u16 i;
+ if (skb->len < sizeof(struct mt7996_mcu_rxd))
+ return;
+
skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
res = (struct mt7996_mcu_all_sta_info_event *)skb->data;
+ if (skb->len < sizeof(*res))
+ return;
- for (i = 0; i < le16_to_cpu(res->sta_num); i++) {
+ sta_num = le16_to_cpu(res->sta_num);
+ switch (le16_to_cpu(res->tag)) {
+ case UNI_ALL_STA_TXRX_RATE:
+ elem_size = sizeof(res->rate[0]);
+ break;
+ case UNI_ALL_STA_TXRX_ADM_STAT:
+ elem_size = sizeof(res->adm_stat[0]);
+ break;
+ case UNI_ALL_STA_TXRX_MSDU_COUNT:
+ elem_size = sizeof(res->msdu_cnt[0]);
+ break;
+ default:
+ return;
+ }
+
+ if (sta_num > (skb->len - sizeof(*res)) / elem_size)
+ sta_num = (skb->len - sizeof(*res)) / elem_size;
+
+ for (i = 0; i < sta_num; i++) {
u8 ac;
u16 wlan_idx;
struct mt76_wcid *wcid;
@@ -1467,15 +1511,15 @@ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif_link *mvif,
/** starec & wtbl **/
int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- struct ieee80211_vif *vif, bool enable)
+ struct ieee80211_vif *vif, unsigned long links,
+ bool enable)
{
struct ieee80211_sta *sta = params->sta;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct ieee80211_link_sta *link_sta;
unsigned int link_id;
int ret = 0;
- for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
@@ -1501,15 +1545,15 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- struct ieee80211_vif *vif, bool enable)
+ struct ieee80211_vif *vif, unsigned long links,
+ bool enable)
{
struct ieee80211_sta *sta = params->sta;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct ieee80211_link_sta *link_sta;
unsigned int link_id;
int ret = 0;
- for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
@@ -2715,18 +2759,20 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
struct ieee80211_sta *sta)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- unsigned int nlinks = hweight16(sta->valid_links);
+ unsigned long links = mt7996_mac_sta_links(dev, vif, sta);
+ unsigned int nlinks = hweight16(links);
struct mld_setup_link *mld_setup_link;
- struct ieee80211_link_sta *link_sta;
struct sta_rec_mld_setup *mld_setup;
struct mt7996_sta_link *msta_link;
+ u8 seclink_id = msta->deflink_id;
unsigned int link_id;
struct tlv *tlv;
- msta_link = mt7996_sta_link_protected(dev, msta, msta->deflink_id);
- if (!msta_link)
+ if (!(links & BIT(msta->deflink_id)))
return;
+ msta_link = mt7996_sta_link_protected(dev, msta, msta->deflink_id);
+
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD,
sizeof(struct sta_rec_mld_setup) +
sizeof(struct mld_setup_link) * nlinks);
@@ -2736,26 +2782,18 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
mld_setup->setup_wcid = cpu_to_le16(msta_link->wcid.idx);
mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx);
- if (nlinks > 1) {
- msta_link = mt7996_sta_link_protected(dev, msta,
- msta->seclink_id);
- if (!msta_link)
- return;
- }
+ if (links & BIT(msta->seclink_id))
+ seclink_id = msta->seclink_id;
+ msta_link = mt7996_sta_link_protected(dev, msta, seclink_id);
mld_setup->seconed_id = cpu_to_le16(msta_link->wcid.idx);
mld_setup->link_num = nlinks;
mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
- for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct mt7996_vif_link *link;
msta_link = mt7996_sta_link_protected(dev, msta, link_id);
- if (!msta_link)
- continue;
-
link = mt7996_vif_link(dev, vif, link_id);
- if (!link)
- continue;
mld_setup_link->wcid = cpu_to_le16(msta_link->wcid.idx);
mld_setup_link->bss_idx = link->mt76.idx;
@@ -2872,6 +2910,27 @@ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
}
+int mt7996_mcu_update_mld_sta(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link)
+{
+ struct sk_buff *skb;
+
+ 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);
+
+ mt7996_mcu_sta_mld_setup_tlv(dev, skb, vif, sta);
+ mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta)
{
struct mt7996_vif_link *link = (struct mt7996_vif_link *)data;
@@ -5444,6 +5503,40 @@ int mt7996_mcu_set_sniffer_mode(struct mt7996_phy *phy, bool enabled)
sizeof(req), true);
}
+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct tx_power_ctrl req = {
+ .tag = cpu_to_le16(power_ctrl_id),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .power_ctrl_id = power_ctrl_id,
+ .band_idx = phy->mt76->band_idx,
+ };
+
+ switch (power_ctrl_id) {
+ case UNI_TXPOWER_SKU_POWER_LIMIT_CTRL:
+ req.sku_enable = !!data;
+ break;
+ case UNI_TXPOWER_PERCENTAGE_CTRL:
+ req.percentage_ctrl_enable = !!data;
+ break;
+ case UNI_TXPOWER_PERCENTAGE_DROP_CTRL:
+ req.power_drop_level = data;
+ break;
+ case UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL:
+ req.bf_backoff_enable = !!data;
+ break;
+ case UNI_TXPOWER_ATE_MODE_CTRL:
+ req.ate_mode_enable = !!data;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
+ &req, sizeof(req), false);
+}
+
int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
{
#define TX_POWER_LIMIT_TABLE_RATE 0
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
index 1b272e904100..6078f8811b1e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -1039,8 +1039,31 @@ enum {
UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
};
+struct tx_power_ctrl {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ u8 power_ctrl_id;
+ union {
+ bool sku_enable;
+ bool ate_mode_enable;
+ bool percentage_ctrl_enable;
+ bool bf_backoff_enable;
+ u8 power_drop_level;
+ };
+ u8 band_idx;
+ u8 rsv[1];
+} __packed;
+
enum {
+ UNI_TXPOWER_SKU_POWER_LIMIT_CTRL = 0,
+ UNI_TXPOWER_PERCENTAGE_CTRL = 1,
+ UNI_TXPOWER_PERCENTAGE_DROP_CTRL = 2,
+ UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
+ UNI_TXPOWER_ATE_MODE_CTRL = 6,
};
enum {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
index 2a0cdb56f822..da9958647b54 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
@@ -244,6 +244,7 @@ struct mt7996_sta_link {
struct mt76_wcid wcid; /* must be first */
struct mt7996_sta *sta;
+ bool connected;
struct list_head rc_list;
u32 airtime_ac[8];
@@ -263,11 +264,25 @@ struct mt7996_sta_link {
struct rcu_head rcu_head;
};
+struct mt7996_sta_ba {
+ u16 ssn;
+ u16 buf_size;
+ bool amsdu;
+};
+
struct mt7996_sta {
struct mt7996_sta_link deflink; /* must be first */
struct mt7996_sta_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
u8 deflink_id;
u8 seclink_id;
+ u8 conn_state;
+
+ unsigned long wcid_flags;
+
+ u16 ba_tx_mask;
+ u16 ba_rx_mask;
+ struct mt7996_sta_ba ba_tx[IEEE80211_NUM_TIDS];
+ struct mt7996_sta_ba ba_rx[IEEE80211_NUM_TIDS];
struct mt7996_vif *vif;
};
@@ -279,6 +294,7 @@ struct mt7996_vif_link {
struct cfg80211_bitrate_mask bitrate_mask;
u8 mld_idx;
+ u8 bmc_bssid[ETH_ALEN];
};
struct mt7996_vif_link_info {
@@ -643,6 +659,18 @@ mt7996_vif_conf_link(struct mt7996_dev *dev, struct ieee80211_vif *vif,
link_conf);
}
+static inline struct mt76_vif_link *
+mt7996_offchannel_link(struct mt76_wcid *wcid)
+{
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+
+ msta_link = container_of(wcid, struct mt7996_sta_link, wcid);
+ link = container_of(msta_link, struct mt7996_vif_link, msta_link);
+
+ return &link->mt76;
+}
+
static inline struct mt7996_sta_link *
mt7996_sta_link(struct mt7996_sta *msta, u8 link_id)
{
@@ -725,13 +753,20 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev,
int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link);
+int mt7996_mcu_update_mld_sta(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link);
void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta);
int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- struct ieee80211_vif *vif, bool enable);
+ struct ieee80211_vif *vif, unsigned long links,
+ bool enable);
int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- struct ieee80211_vif *vif, bool enable);
+ struct ieee80211_vif *vif, unsigned long links,
+ bool enable);
int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
struct mt76_vif_link *mlink,
struct cfg80211_he_bss_color *he_bss_color);
@@ -778,6 +813,7 @@ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
int mt7996_mcu_rdd_resume_tx(struct mt7996_phy *phy);
int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val);
int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
@@ -893,6 +929,8 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
void mt7996_mac_sta_remove_link(struct mt7996_dev *dev,
struct ieee80211_sta *sta,
unsigned int link_id, bool flush);
+u16 mt7996_mac_sta_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
struct ieee80211_twt_setup *twt);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
index b8006b8729a1..3fb9f39c2f61 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
@@ -283,7 +283,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan rx ring0 addr\n");
+ "failed retrieving NPU wlan rx ring0 addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base);
@@ -292,7 +292,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan rx ring2 addr\n");
+ "failed retrieving NPU wlan rx ring2 addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2].regs->desc_base);
@@ -302,7 +302,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan msdu pg ring addr\n");
+ "failed retrieving NPU wlan msdu pg ring addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].regs->desc_base);
@@ -311,7 +311,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan msdu pg ring addr\n");
+ "failed retrieving NPU wlan msdu pg ring addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].regs->desc_base);
@@ -320,7 +320,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan msdu pg ring addr\n");
+ "failed retrieving NPU wlan msdu pg ring addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].regs->desc_base);
@@ -330,7 +330,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan ind_cmd ring addr\n");
+ "failed retrieving NPU wlan ind_cmd ring addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_IND].regs->desc_base);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
index b7d9193e042f..a7c23b33dde0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
@@ -63,12 +63,18 @@ static void mt7996_put_hif2(struct mt7996_hif *hif)
static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
{
+ struct pci_dev *tmp_pdev;
+
hif_idx++;
- if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2, NULL) &&
- !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2, NULL) &&
- !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2, NULL))
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2, NULL);
+ if (!tmp_pdev)
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2, NULL);
+ if (!tmp_pdev)
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2, NULL);
+ if (!tmp_pdev)
return NULL;
+ pci_dev_put(tmp_pdev);
writel(hif_idx | MT_PCIE_RECOG_ID_SEM,
pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
index 8ff78cf6eb04..6df6e4c3d45d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
@@ -155,14 +155,42 @@ enum offs_rev {
#define MT_FL_Q2_CTRL MT_PLE(0x3e8)
#define MT_FL_Q3_CTRL MT_PLE(0x3ec)
-#define MT_PLE_FREEPG_CNT MT_PLE(0x380)
-#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(0x384)
+#define MT_FL_Q0_CTRL_EXECUTE BIT(31)
+#define MT_FL_Q0_CTRL_QID GENMASK(30, 24)
+#define MT_FL_Q0_CTRL_TGID GENMASK(21, 20)
+#define MT_FL_Q0_CTRL_PID GENMASK(17, 16)
+#define MT_FL_Q0_CTRL_WLAN_IDX GENMASK(11, 0)
+#define MT_FL_Q2_CTRL_TAIL_FID GENMASK(28, 16)
+#define MT_FL_Q2_CTRL_HEAD_FID GENMASK(12, 0)
+#define MT_FL_Q3_CTRL_PKT_NUM GENMASK(12, 0)
+
+#define MT_PLE_FREEPG_CNT MT_PLE(0x3a0)
+#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(0x3a4)
#define MT_PLE_PG_HIF_GROUP MT_PLE(0x00c)
-#define MT_PLE_HIF_PG_INFO MT_PLE(0x388)
+#define MT_PLE_HIF_PG_INFO MT_PLE(0x3a8)
-#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x600 + 0x80 * (ac) + ((n) << 2))
+#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x600 + 0x100 * (ac) + ((n) << 2))
#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2))
+#define MT_FREEPG_CNT_FFA GENMASK(28, 16)
+#define MT_FREEPG_CNT_FREE GENMASK(12, 0)
+#define MT_FREEPG_HEAD_TAIL_TAIL GENMASK(28, 16)
+#define MT_FREEPG_HEAD_TAIL_HEAD GENMASK(12, 0)
+#define MT_PG_GROUP_MAX_QUOTA GENMASK(28, 16)
+#define MT_PG_GROUP_MIN_QUOTA GENMASK(12, 0)
+#define MT_PG_INFO_SRC_CNT GENMASK(28, 16)
+#define MT_PG_INFO_RSV_CNT GENMASK(12, 0)
+
+/* PSE */
+#define MT_PSE_BASE 0x820c8000
+#define MT_PSE(ofs) (MT_PSE_BASE + (ofs))
+
+#define MT_PSE_QUEUE_EMPTY MT_PSE(0x0b0)
+#define MT_PSE_QUEUE_EMPTY_1 MT_PSE(0x0bc)
+#define MT_PSE_FL_Q0_CTRL MT_PSE(0x1b0)
+#define MT_PSE_FL_Q2_CTRL MT_PSE(0x1b8)
+#define MT_PSE_FL_Q3_CTRL MT_PSE(0x1bc)
+
/* WF MDP TOP */
#define MT_MDP_BASE 0x820cc000
#define MT_MDP(ofs) (MT_MDP_BASE + (ofs))
diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c
index 3594b599662d..3271a2fa100d 100644
--- a/drivers/net/wireless/mediatek/mt76/scan.c
+++ b/drivers/net/wireless/mediatek/mt76/scan.c
@@ -4,16 +4,26 @@
*/
#include "mt76.h"
-static void mt76_scan_complete(struct mt76_dev *dev, bool abort)
+static struct mt76_phy *
+mt76_scan_chan_phy(struct ieee80211_hw *hw, struct ieee80211_channel *chan)
{
- struct mt76_phy *phy = dev->scan.phy;
- struct cfg80211_scan_info info = {
- .aborted = abort,
- };
+ struct mt76_phy *phy = hw->priv;
+
+ if (hw->wiphy->n_radio > 1)
+ return phy->dev->band_phys[chan->band];
+
+ return phy;
+}
+
+static void mt76_scan_phy_complete(struct mt76_phy *phy, bool abort)
+{
+ struct mt76_dev *dev = phy->dev;
+ struct cfg80211_scan_info info = {};
+ struct ieee80211_hw *hw = phy->hw;
lockdep_assert_held(&dev->mutex);
- if (!phy)
+ if (!mt76_phy_scanning(phy))
return;
clear_bit(MT76_SCANNING, &phy->state);
@@ -29,41 +39,75 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort)
__mt76_set_channel(phy, &phy->main_chandef, false);
if (offchannel)
mt76_offchannel_notify(phy, false);
+ } else {
+ /* A phy that has no operating channel has nothing to restore,
+ * but the flag also gates the TX queues of every station whose
+ * wcid points at this band, so leaving it set stalls them until
+ * something else happens to set a channel.
+ */
+ phy->offchannel = false;
}
- mt76_put_vif_phy_link(phy, dev->scan.vif, dev->scan.mlink);
+ mt76_put_vif_phy_link(phy, dev->scan.vif, phy->scan.mlink);
+ memset(&phy->scan, 0, sizeof(phy->scan));
+
+ clear_bit(phy->band_idx, &dev->scan.phy_mask);
+ if (abort)
+ dev->scan.aborted = true;
+
+ if (dev->scan.phy_mask)
+ return;
+
+ info.aborted = dev->scan.aborted;
memset(&dev->scan, 0, sizeof(dev->scan));
- if (!test_bit(MT76_MCU_RESET, &dev->phy.state))
- ieee80211_scan_completed(phy->hw, &info);
+ ieee80211_scan_completed(hw, &info);
}
void mt76_abort_scan(struct mt76_dev *dev)
{
- spin_lock_bh(&dev->scan_lock);
- dev->scan.beacon_wait = false;
- spin_unlock_bh(&dev->scan_lock);
+ struct mt76_phy *phy;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->phys); i++) {
+ phy = dev->phys[i];
+ if (!phy)
+ continue;
+
+ spin_lock_bh(&dev->scan_lock);
+ phy->scan.beacon_wait = false;
+ spin_unlock_bh(&dev->scan_lock);
- cancel_delayed_work_sync(&dev->scan_work);
+ disable_delayed_work_sync(&phy->scan_work);
+ }
mutex_lock(&dev->mutex);
- mt76_scan_complete(dev, true);
+ for (i = 0; i < ARRAY_SIZE(dev->phys); i++) {
+ phy = dev->phys[i];
+ if (!phy)
+ continue;
+
+ mt76_scan_phy_complete(phy, true);
+ enable_delayed_work(&phy->scan_work);
+ }
mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL_GPL(mt76_abort_scan);
static void
-mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid)
+mt76_scan_send_probe(struct mt76_phy *phy, struct cfg80211_ssid *ssid)
{
- struct cfg80211_scan_request *req = dev->scan.req;
+ struct mt76_dev *dev = phy->dev;
+ struct cfg80211_scan_request *req = &dev->scan.req->req;
+ struct ieee80211_scan_ies *ies = &dev->scan.req->ies;
struct ieee80211_vif *vif = dev->scan.vif;
- struct mt76_vif_link *mvif = dev->scan.mlink;
- enum nl80211_band band = dev->scan.chan->band;
- struct mt76_phy *phy = dev->scan.phy;
+ struct mt76_vif_link *mvif = phy->scan.mlink;
+ enum nl80211_band band = phy->scan.chan->band;
struct ieee80211_tx_info *info;
struct sk_buff *skb;
u8 link_id;
skb = ieee80211_probereq_get(phy->hw, vif->addr, ssid->ssid,
- ssid->ssid_len, req->ie_len);
+ ssid->ssid_len,
+ ies->len[band] + ies->common_ie_len);
if (!skb)
return;
@@ -74,8 +118,10 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid)
ether_addr_copy(hdr->addr3, req->bssid);
}
- if (req->ie_len)
- skb_put_data(skb, req->ie, req->ie_len);
+ if (ies->len[band])
+ skb_put_data(skb, ies->ies[band], ies->len[band]);
+ if (ies->common_ie_len)
+ skb_put_data(skb, ies->common_ies, ies->common_ie_len);
skb->priority = 7;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
@@ -100,65 +146,77 @@ out:
rcu_read_unlock();
}
-void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan)
+void mt76_scan_rx_beacon(struct mt76_phy *phy, struct ieee80211_channel *chan)
{
- struct mt76_phy *phy;
+ struct mt76_dev *dev = phy->dev;
spin_lock(&dev->scan_lock);
- if (!dev->scan.beacon_wait || dev->scan.beacon_received ||
- dev->scan.chan != chan)
- goto out;
-
- phy = dev->scan.phy;
- if (!phy)
+ if (!phy->scan.beacon_wait || phy->scan.beacon_received ||
+ phy->scan.chan != chan)
goto out;
- dev->scan.beacon_received = true;
- ieee80211_queue_delayed_work(phy->hw, &dev->scan_work, 0);
+ phy->scan.beacon_received = true;
+ ieee80211_queue_delayed_work(phy->hw, &phy->scan_work, 0);
out:
spin_unlock(&dev->scan_lock);
}
+static int mt76_scan_next_chan_idx(struct mt76_phy *phy)
+{
+ struct cfg80211_scan_request *req = &phy->dev->scan.req->req;
+ int i;
+
+ for (i = phy->scan.chan_idx; i < req->n_channels; i++)
+ if (mt76_scan_chan_phy(phy->hw, req->channels[i]) == phy)
+ return i;
+
+ return -1;
+}
+
void mt76_scan_work(struct work_struct *work)
{
- struct mt76_dev *dev = container_of(work, struct mt76_dev,
+ struct mt76_phy *phy = container_of(work, struct mt76_phy,
scan_work.work);
- struct cfg80211_scan_request *req = dev->scan.req;
+ struct mt76_dev *dev = phy->dev;
+ struct cfg80211_scan_request *req;
struct cfg80211_chan_def chandef = {};
- struct mt76_phy *phy = dev->scan.phy;
int duration = HZ / 9; /* ~110 ms */
bool beacon_rx, offchannel = true;
- int i;
+ int i, idx;
- if (!phy || !req)
+ if (!dev->scan.req || !mt76_phy_scanning(phy))
return;
+ req = &dev->scan.req->req;
+
spin_lock_bh(&dev->scan_lock);
- beacon_rx = dev->scan.beacon_wait && dev->scan.beacon_received;
- dev->scan.beacon_wait = false;
+ beacon_rx = phy->scan.beacon_wait && phy->scan.beacon_received;
+ phy->scan.beacon_wait = false;
spin_unlock_bh(&dev->scan_lock);
if (beacon_rx)
goto probe;
- if (dev->scan.chan_idx >= req->n_channels) {
+ idx = mt76_scan_next_chan_idx(phy);
+ if (idx < 0) {
mutex_lock(&dev->mutex);
- mt76_scan_complete(dev, false);
+ mt76_scan_phy_complete(phy, false);
mutex_unlock(&dev->mutex);
return;
}
- if (dev->scan.chan && phy->num_sta && phy->offchannel) {
- dev->scan.chan = NULL;
+ if (phy->scan.chan && phy->num_sta && phy->offchannel) {
+ phy->scan.chan = NULL;
mt76_set_channel(phy, &phy->main_chandef, false);
mt76_offchannel_notify(phy, false);
goto out;
}
- dev->scan.chan = req->channels[dev->scan.chan_idx++];
- offchannel = mt76_offchannel_chandef(phy, dev->scan.chan, &chandef);
+ phy->scan.chan = req->channels[idx];
+ phy->scan.chan_idx = idx + 1;
+ offchannel = mt76_offchannel_chandef(phy, phy->scan.chan, &chandef);
if (offchannel)
mt76_offchannel_notify(phy, true);
@@ -169,8 +227,8 @@ void mt76_scan_work(struct work_struct *work)
if (chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) {
spin_lock_bh(&dev->scan_lock);
- dev->scan.beacon_received = false;
- dev->scan.beacon_wait = true;
+ phy->scan.beacon_received = false;
+ phy->scan.beacon_wait = true;
spin_unlock_bh(&dev->scan_lock);
goto out;
}
@@ -180,16 +238,16 @@ probe:
duration = HZ / 16; /* ~60 ms */
local_bh_disable();
for (i = 0; i < req->n_ssids; i++)
- mt76_scan_send_probe(dev, &req->ssids[i]);
+ mt76_scan_send_probe(phy, &req->ssids[i]);
local_bh_enable();
out:
- if (dev->scan.chan && phy->offchannel)
+ if (phy->scan.chan && phy->offchannel)
duration = max_t(int, duration,
msecs_to_jiffies(req->duration +
(req->duration >> 5)));
- ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, duration);
+ ieee80211_queue_delayed_work(phy->hw, &phy->scan_work, duration);
}
int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
@@ -197,36 +255,67 @@ int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
{
struct mt76_phy *phy = hw->priv;
struct mt76_dev *dev = phy->dev;
+ unsigned long phy_mask = 0;
struct mt76_vif_link *mlink;
- int ret = 0;
+ int i, ret = 0;
- if (hw->wiphy->n_radio > 1) {
- phy = dev->band_phys[req->req.channels[0]->band];
+ for (i = 0; i < req->req.n_channels; i++) {
+ phy = mt76_scan_chan_phy(hw, req->req.channels[i]);
if (!phy)
return -EINVAL;
+
+ phy_mask |= BIT(phy->band_idx);
}
mutex_lock(&dev->mutex);
- if (dev->scan.req || phy->roc_vif ||
- test_bit(MT76_MCU_RESET, &dev->phy.state)) {
+ if (dev->scan.req || test_bit(MT76_MCU_RESET, &dev->phy.state) ||
+ test_bit(MT76_RESTART, &dev->phy.state)) {
ret = -EBUSY;
goto out;
}
- mlink = mt76_get_vif_phy_link(phy, vif);
- if (IS_ERR(mlink)) {
- ret = PTR_ERR(mlink);
- goto out;
+ for_each_set_bit(i, &phy_mask, ARRAY_SIZE(dev->phys)) {
+ if (dev->phys[i]->roc_vif) {
+ ret = -EBUSY;
+ goto out;
+ }
}
- memset(&dev->scan, 0, sizeof(dev->scan));
- dev->scan.req = &req->req;
+ for_each_set_bit(i, &phy_mask, ARRAY_SIZE(dev->phys)) {
+ phy = dev->phys[i];
+ mlink = mt76_get_vif_phy_link(phy, vif);
+ if (IS_ERR(mlink)) {
+ ret = PTR_ERR(mlink);
+ goto put_links;
+ }
+
+ memset(&phy->scan, 0, sizeof(phy->scan));
+ phy->scan.mlink = mlink;
+ }
+
+ dev->scan.req = req;
dev->scan.vif = vif;
- dev->scan.phy = phy;
- dev->scan.mlink = mlink;
- set_bit(MT76_SCANNING, &phy->state);
- ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, 0);
+ dev->scan.phy_mask = phy_mask;
+ dev->scan.aborted = false;
+
+ for_each_set_bit(i, &phy_mask, ARRAY_SIZE(dev->phys)) {
+ phy = dev->phys[i];
+ set_bit(MT76_SCANNING, &phy->state);
+ ieee80211_queue_delayed_work(phy->hw, &phy->scan_work, 0);
+ }
+
+ goto out;
+
+put_links:
+ for_each_set_bit(i, &phy_mask, ARRAY_SIZE(dev->phys)) {
+ phy = dev->phys[i];
+ if (!phy->scan.mlink)
+ break;
+
+ mt76_put_vif_phy_link(phy, vif, phy->scan.mlink);
+ phy->scan.mlink = NULL;
+ }
out:
mutex_unlock(&dev->mutex);
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 3c623df2e741..b022d3843473 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -129,7 +129,7 @@ mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
memset(cb, 0, sizeof(*cb));
- if (!wcid || !rcu_access_pointer(dev->wcid[wcid->idx]))
+ if (!wcid)
return MT_PACKET_ID_NO_ACK;
if (info->flags & IEEE80211_TX_CTL_NO_ACK)
@@ -147,6 +147,11 @@ mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
spin_lock_bh(&dev->status_lock);
+ if (rcu_access_pointer(dev->wcid[wcid->idx]) != wcid) {
+ pid = MT_PACKET_ID_NO_ACK;
+ goto out;
+ }
+
pid = idr_alloc(&wcid->pktid, skb, MT_PACKET_ID_FIRST,
MT_PACKET_ID_MASK, GFP_ATOMIC);
if (pid < 0) {
@@ -885,7 +890,7 @@ int mt76_skb_adjust_pad(struct sk_buff *skb, int pad)
}
}
- if (skb_pad(last, pad))
+ if (__skb_pad(last, pad, false))
return -ENOMEM;
__skb_put(last, pad);
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index a9af3aa6b80a..be48a88a3ad0 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -171,8 +171,10 @@ static void mt76u_copy(struct mt76_dev *dev, u32 offset,
{
struct mt76_usb *usb = &dev->usb;
const u8 *val = data;
- int ret;
int current_batch_size;
+ int len_aligned;
+ int copy_len;
+ int ret;
int i = 0;
/* Assure that always a multiple of 4 bytes are copied,
@@ -180,12 +182,16 @@ static void mt76u_copy(struct mt76_dev *dev, u32 offset,
* See: "mt76: round up length on mt76_wr_copy"
* Commit 850e8f6fbd5d0003b0
*/
- len = round_up(len, 4);
+ len_aligned = round_up(len, 4);
mutex_lock(&usb->usb_ctrl_mtx);
- while (i < len) {
- current_batch_size = min_t(int, usb->data_len, len - i);
- memcpy(usb->data, val + i, current_batch_size);
+ while (i < len_aligned) {
+ current_batch_size = min_t(int, usb->data_len, len_aligned - i);
+ copy_len = min_t(int, current_batch_size, len - i);
+ memcpy(usb->data, val + i, copy_len);
+ if (copy_len < current_batch_size)
+ memset(usb->data + copy_len, 0,
+ current_batch_size - copy_len);
ret = __mt76u_vendor_request(dev, MT_VEND_MULTI_WRITE,
USB_DIR_OUT | USB_TYPE_VENDOR,
0, offset + i, usb->data,
@@ -893,6 +899,9 @@ mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
enum mt76_txq_id qid, struct sk_buff *skb,
struct mt76_wcid *wcid, struct ieee80211_sta *sta)
{
+ struct ieee80211_tx_status status = {
+ .sta = sta,
+ };
struct mt76_tx_info tx_info = {
.skb = skb,
};
@@ -900,17 +909,27 @@ mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
u16 idx = q->head;
int err;
- if (q->queued == q->ndesc)
- return -ENOSPC;
+ if (q->queued == q->ndesc) {
+ err = -ENOSPC;
+ goto err_free_skb;
+ }
skb->prev = skb->next = NULL;
err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info);
if (err < 0)
- return err;
+ goto err_free_skb;
err = mt76u_tx_setup_buffers(dev, tx_info.skb, q->entry[idx].urb);
- if (err < 0)
- return err;
+ if (err < 0) {
+ /*
+ * mt76_tx_status_skb_get() walks the idr and dereferences
+ * a freed skb. This SKB is not counted in non-AQL counter
+ * due to error return, so using 0xffff as wcid to keep
+ * balanced.
+ */
+ mt76_tx_complete_skb(dev, 0xffff, tx_info.skb);
+ goto err_ret;
+ }
mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q->ep, q->entry[idx].urb,
mt76u_complete_tx, &q->entry[idx]);
@@ -921,6 +940,14 @@ mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
q->queued++;
return idx;
+
+err_free_skb:
+ status.skb = tx_info.skb;
+ spin_lock_bh(&dev->rx_lock);
+ ieee80211_tx_status_ext(dev->hw, &status);
+ spin_unlock_bh(&dev->rx_lock);
+err_ret:
+ return err;
}
static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)