blob: c76593ab3f479c14c6b70abedb425e32999292b0 [file] [log] [blame]
From 8a1c29f6a9b99d2f3d699fe6100704349406fc4b Mon Sep 17 00:00:00 2001
From: Shayne Chen <shayne.chen@mediatek.com>
Date: Fri, 3 Nov 2023 21:44:45 +0800
Subject: [PATCH 082/116] wifi: mt76: mt7996: implement and switch to hw scan
callbacks
To support MLO, hw_scan callbacks are mandatory. However, the
firmware of AP-segment doesn't support hw_scan commands, so we need
to implement it in the driver.
This is a preliminary patch to add MLO support for mt7996 chipsets.
If the cfg80211_scan_request contains an unicast BSSID, the probe
request should be unicast.
This works for ML probe request, which should be unicast.
Co-developed-by: Peter Chiu <chui-hao.chiu@mediatek.com>
Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
Co-developed-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
---
mac80211.c | 3 +-
mt76.h | 2 +
mt7996/init.c | 5 ++
mt7996/mac.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++
mt7996/main.c | 95 ++++++++++++++++++++++++++++----
mt7996/mcu.c | 3 +-
mt7996/mt7996.h | 11 +++-
7 files changed, 248 insertions(+), 12 deletions(-)
diff --git a/mac80211.c b/mac80211.c
index 3c2c2af..0ad3f25 100644
--- a/mac80211.c
+++ b/mac80211.c
@@ -821,7 +821,7 @@ bool mt76_has_tx_pending(struct mt76_phy *phy)
}
EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
-static struct mt76_channel_state *
+struct mt76_channel_state *
mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
{
struct mt76_sband *msband;
@@ -837,6 +837,7 @@ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
idx = c - &msband->sband.channels[0];
return &msband->chan[idx];
}
+EXPORT_SYMBOL_GPL(mt76_channel_state);
void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time)
{
diff --git a/mt76.h b/mt76.h
index 28defd4..8ca7907 100644
--- a/mt76.h
+++ b/mt76.h
@@ -1477,6 +1477,8 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
enum ieee80211_frame_release_type reason,
bool more_data);
bool mt76_has_tx_pending(struct mt76_phy *phy);
+struct mt76_channel_state *
+mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c);
void mt76_set_channel(struct mt76_phy *phy);
void mt76_update_survey(struct mt76_phy *phy);
void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time);
diff --git a/mt7996/init.c b/mt7996/init.c
index 768979e..9523568 100644
--- a/mt7996/init.c
+++ b/mt7996/init.c
@@ -457,6 +457,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
wiphy->available_antennas_rx = phy->mt76->antenna_mask;
wiphy->available_antennas_tx = phy->mt76->antenna_mask;
+
+ wiphy->max_scan_ssids = 4;
+ wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
}
static void
@@ -677,6 +680,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
mphy->dev->phys[band] = mphy;
INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work);
+ INIT_DELAYED_WORK(&phy->scan_work, mt7996_scan_work);
ret = mt7996_eeprom_parse_hw_cap(dev, phy);
if (ret)
@@ -1574,6 +1578,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
dev->mt76.phy.priv = &dev->phy;
INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
+ INIT_DELAYED_WORK(&dev->phy.scan_work, mt7996_scan_work);
INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
INIT_LIST_HEAD(&dev->sta_rc_list);
INIT_LIST_HEAD(&dev->twt_list);
diff --git a/mt7996/mac.c b/mt7996/mac.c
index 8c44442..4e9dd2c 100644
--- a/mt7996/mac.c
+++ b/mt7996/mac.c
@@ -2693,3 +2693,144 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
dev->twt.table_mask &= ~BIT(flow->table_id);
dev->twt.n_agrt--;
}
+
+static void
+mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ const u8 *dst)
+{
+ struct ieee80211_hw *hw = phy->mt76->hw;
+ struct cfg80211_scan_request *req = phy->scan_req;
+ struct ieee80211_vif *vif = phy->scan_vif;
+ struct mt7996_vif *mvif;
+ struct mt76_wcid *wcid;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb;
+
+ if (!req || !vif)
+ return;
+
+ mvif = (struct mt7996_vif *)vif->drv_priv;
+ wcid = &mvif->sta.wcid;
+
+ skb = ieee80211_probereq_get(hw, vif->addr,
+ ssid->ssid, ssid->ssid_len, req->ie_len);
+ if (!skb)
+ return;
+
+ if (is_unicast_ether_addr(dst)) {
+ struct ieee80211_hdr_3addr *hdr =
+ (struct ieee80211_hdr_3addr *)skb->data;
+ memcpy(hdr->addr1, dst, ETH_ALEN);
+ memcpy(hdr->addr3, dst, ETH_ALEN);
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ if (req->no_cck)
+ info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
+
+ if (req->ie_len)
+ skb_put_data(skb, req->ie, req->ie_len);
+
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+
+ rcu_read_lock();
+ if (!ieee80211_tx_prepare_skb(hw, vif, skb,
+ phy->scan_chan->band,
+ NULL)) {
+ rcu_read_unlock();
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+
+ local_bh_disable();
+ mt76_tx(phy->mt76, NULL, wcid, skb);
+ local_bh_enable();
+
+ rcu_read_unlock();
+}
+
+void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
+{
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ };
+
+ ieee80211_scan_completed(phy->mt76->hw, &info);
+ phy->scan_chan = NULL;
+ phy->scan_req = NULL;
+ phy->scan_vif = NULL;
+ clear_bit(MT76_SCANNING, &phy->mt76->state);
+}
+
+static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
+{
+ bool *has_sta = data;
+
+ if (*has_sta)
+ return;
+ *has_sta = true;
+}
+
+void mt7996_scan_work(struct work_struct *work)
+{
+ struct mt7996_phy *phy = container_of(work, struct mt7996_phy, scan_work.work);
+ struct ieee80211_hw *hw = phy->mt76->hw;
+ struct cfg80211_scan_request *req = phy->scan_req;
+ struct cfg80211_chan_def chandef = {};
+ int duration;
+ bool has_sta = false, active_scan = false;
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ if (phy->scan_chan_idx >= req->n_channels) {
+ mt7996_scan_complete(phy, false);
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ mt7996_set_channel(phy, &hw->conf.chandef);
+
+ return;
+ }
+
+ ieee80211_iterate_stations_atomic(hw, mt7996_scan_check_sta, &has_sta);
+
+ /* go back to operating channel */
+ if (has_sta && phy->scan_chan) {
+ phy->scan_chan = NULL;
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ mt7996_set_channel(phy, &phy->mt76->chandef);
+
+ ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
+
+ return;
+ }
+
+ wiphy_info(hw->wiphy, "hw scan %d MHz\n",
+ req->channels[phy->scan_chan_idx]->center_freq);
+
+ phy->scan_chan = req->channels[phy->scan_chan_idx++];
+
+ if (!req->n_ssids ||
+ (phy->scan_chan->flags & (IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR))) {
+ duration = HZ / 9; /* ~110 ms */
+ } else {
+ duration = HZ / 16; /* ~60 ms */
+ active_scan = true;
+ }
+
+ cfg80211_chandef_create(&chandef, phy->scan_chan, NL80211_CHAN_HT20);
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ mt7996_set_channel(phy, &chandef);
+
+ if (active_scan) {
+ int i;
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ for (i = 0; i < req->n_ssids; i++)
+ mt7996_scan_send_probe(phy, &req->ssids[i], req->bssid);
+ mutex_unlock(&phy->dev->mt76.mutex);
+ }
+
+ ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
+}
diff --git a/mt7996/main.c b/mt7996/main.c
index c9e8108..5c1735e 100644
--- a/mt7996/main.c
+++ b/mt7996/main.c
@@ -312,6 +312,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
struct mt7996_phy *phy = mt7996_hw_phy(hw);
int idx = msta->wcid.idx;
+ cancel_delayed_work_sync(&phy->scan_work);
+
mt7996_mcu_add_sta(dev, vif, NULL, false, false);
mt7996_mcu_add_bss_info(phy, vif, false);
@@ -323,6 +325,10 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
mutex_lock(&dev->mt76.mutex);
+
+ if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ mt7996_scan_complete(phy, true);
+
dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
mutex_unlock(&dev->mt76.mutex);
@@ -335,7 +341,33 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
}
-int mt7996_set_channel(struct mt7996_phy *phy)
+static void ___mt7996_set_channel(struct mt7996_phy *phy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct mt76_dev *mdev = phy->mt76->dev;
+ struct mt76_phy *mphy = phy->mt76;
+ bool offchannel = phy->scan_chan != NULL;
+ int timeout = HZ / 5;
+
+ wait_event_timeout(mdev->tx_wait, !mt76_has_tx_pending(mphy), timeout);
+ mt76_update_survey(mphy);
+
+ if (mphy->chandef.chan->center_freq != chandef->chan->center_freq ||
+ mphy->chandef.width != chandef->width)
+ mphy->dfs_state = MT_DFS_STATE_UNKNOWN;
+
+ mphy->chandef = *chandef;
+ mphy->chan_state = mt76_channel_state(mphy, chandef->chan);
+
+ if (!offchannel)
+ mphy->main_chan = chandef->chan;
+
+ if (chandef->chan != mphy->main_chan)
+ memset(mphy->chan_state, 0, sizeof(*mphy->chan_state));
+}
+
+static int __mt7996_set_channel(struct mt7996_phy *phy,
+ struct cfg80211_chan_def *chandef)
{
struct mt7996_dev *dev = phy->dev;
int ret;
@@ -345,7 +377,7 @@ int mt7996_set_channel(struct mt7996_phy *phy)
mutex_lock(&dev->mt76.mutex);
set_bit(MT76_RESET, &phy->mt76->state);
- mt76_set_channel(phy->mt76);
+ ___mt7996_set_channel(phy, chandef);
if (dev->cal) {
ret = mt7996_mcu_apply_tx_dpd(phy);
@@ -381,6 +413,19 @@ out:
return ret;
}
+int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
+{
+ int ret;
+
+ ieee80211_stop_queues(phy->mt76->hw);
+ ret = __mt7996_set_channel(phy, chandef);
+ if (ret)
+ return ret;
+ ieee80211_wake_queues(phy->mt76->hw);
+
+ return 0;
+}
+
static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
@@ -477,11 +522,7 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
if (ret)
return ret;
- ieee80211_stop_queues(hw);
- ret = mt7996_set_channel(phy);
- if (ret)
- return ret;
- ieee80211_wake_queues(hw);
+ mt7996_set_channel(phy, &hw->conf.chandef);
}
if (changed & (IEEE80211_CONF_CHANGE_POWER |
@@ -1648,6 +1689,42 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
#endif
+static int
+mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+{
+ struct cfg80211_scan_request *req = &hw_req->req;
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ if (WARN_ON(phy->scan_req || phy->scan_chan)) {
+ mutex_unlock(&phy->dev->mt76.mutex);
+ return -EBUSY;
+ }
+
+ set_bit(MT76_SCANNING, &phy->mt76->state);
+ phy->scan_req = req;
+ phy->scan_vif = vif;
+ phy->scan_chan_idx = 0;
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ ieee80211_queue_delayed_work(hw, &phy->scan_work, 0);
+
+ return 0;
+}
+
+static void
+mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+
+ cancel_delayed_work_sync(&phy->scan_work);
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ mt7996_scan_complete(phy, true);
+ mutex_unlock(&phy->dev->mt76.mutex);
+}
+
const struct ieee80211_ops mt7996_ops = {
.tx = mt7996_tx,
.start = mt7996_start,
@@ -1666,8 +1743,8 @@ const struct ieee80211_ops mt7996_ops = {
.ampdu_action = mt7996_ampdu_action,
.set_rts_threshold = mt7996_set_rts_threshold,
.wake_tx_queue = mt76_wake_tx_queue,
- .sw_scan_start = mt76_sw_scan,
- .sw_scan_complete = mt76_sw_scan_complete,
+ .hw_scan = mt7996_hw_scan,
+ .cancel_hw_scan = mt7996_cancel_hw_scan,
.release_buffered_frames = mt76_release_buffered_frames,
.get_txpower = mt76_get_txpower,
.channel_switch_beacon = mt7996_channel_switch_beacon,
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index e38a507..9f2d125 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -3775,7 +3775,8 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
req.switch_reason = CH_SWITCH_NORMAL;
else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL ||
- phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
+ phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE ||
+ phy->scan_chan)
req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
NL80211_IFTYPE_AP))
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index d03d3d9..fbf1e83 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -458,6 +458,13 @@ struct mt7996_phy {
u8 pp_mode;
u16 punct_bitmap;
+ /* for hw_scan */
+ struct delayed_work scan_work;
+ struct ieee80211_channel *scan_chan;
+ struct cfg80211_scan_request *scan_req;
+ struct ieee80211_vif *scan_vif;
+ int scan_chan_idx;
+
struct mt7996_scs_ctrl scs_ctrl;
u32 red_drop;
@@ -806,7 +813,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_he_obss_pd *he_obss_pd);
int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool changed);
-int mt7996_set_channel(struct mt7996_phy *phy);
+int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif);
int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
@@ -964,6 +971,8 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
struct sk_buff *skb, u32 *info);
bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
void mt7996_stats_work(struct work_struct *work);
+void mt7996_scan_work(struct work_struct *work);
+void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
--
2.18.0