Skip to content

Commit 31083e3

Browse files
committed
wifi: mt76: add code for emulating hardware scanning
Preparation for supporting multiple radios on a single wiphy on devices where firmware does not support actual hardware scanning. This is also a prerequisite for full MLO support Co-developed-by: Peter Chiu <[email protected]> Signed-off-by: Peter Chiu <[email protected]> Co-developed-by: Michael-CY Lee <[email protected]> Signed-off-by: Michael-CY Lee <[email protected]> Co-developed-by: Shayne Chen <[email protected]> Signed-off-by: Shayne Chen <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Felix Fietkau <[email protected]>
1 parent 387ab04 commit 31083e3

File tree

8 files changed

+189
-11
lines changed

8 files changed

+189
-11
lines changed

drivers/net/wireless/mediatek/mt76/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ obj-$(CONFIG_MT792x_USB) += mt792x-usb.o
1010

1111
mt76-y := \
1212
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
13-
tx.o agg-rx.o mcu.o wed.o
13+
tx.o agg-rx.o mcu.o wed.o scan.o
1414

1515
mt76-$(CONFIG_PCI) += pci.o
1616
mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o

drivers/net/wireless/mediatek/mt76/mac80211.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
690690
INIT_LIST_HEAD(&dev->txwi_cache);
691691
INIT_LIST_HEAD(&dev->rxwi_cache);
692692
dev->token_size = dev->drv->token_size;
693+
INIT_DELAYED_WORK(&dev->scan_work, mt76_scan_work);
693694

694695
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
695696
skb_queue_head_init(&dev->rx_skb[i]);
@@ -954,9 +955,9 @@ int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
954955
phy->offchannel = offchannel;
955956

956957
if (!offchannel)
957-
phy->main_chan = chandef->chan;
958+
phy->main_chandef = *chandef;
958959

959-
if (chandef->chan != phy->main_chan)
960+
if (chandef->chan != phy->main_chandef.chan)
960961
memset(phy->chan_state, 0, sizeof(*phy->chan_state));
961962

962963
ret = dev->drv->set_channel(phy);
@@ -1021,7 +1022,7 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
10211022
if (state->noise)
10221023
survey->filled |= SURVEY_INFO_NOISE_DBM;
10231024

1024-
if (chan == phy->main_chan) {
1025+
if (chan == phy->main_chandef.chan) {
10251026
survey->filled |= SURVEY_INFO_IN_USE;
10261027

10271028
if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME)
@@ -1466,6 +1467,7 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif,
14661467
mt76_wcid_mask_set(dev->wcid_phy_mask, wcid->idx);
14671468
wcid->phy_idx = phy->band_idx;
14681469
rcu_assign_pointer(dev->wcid[wcid->idx], wcid);
1470+
phy->num_sta++;
14691471

14701472
mt76_wcid_init(wcid);
14711473
out:
@@ -1474,9 +1476,10 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif,
14741476
return ret;
14751477
}
14761478

1477-
void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
1479+
void __mt76_sta_remove(struct mt76_phy *phy, struct ieee80211_vif *vif,
14781480
struct ieee80211_sta *sta)
14791481
{
1482+
struct mt76_dev *dev = phy->dev;
14801483
struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
14811484
int i, idx = wcid->idx;
14821485

@@ -1490,15 +1493,18 @@ void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
14901493

14911494
mt76_wcid_mask_clear(dev->wcid_mask, idx);
14921495
mt76_wcid_mask_clear(dev->wcid_phy_mask, idx);
1496+
phy->num_sta--;
14931497
}
14941498
EXPORT_SYMBOL_GPL(__mt76_sta_remove);
14951499

14961500
static void
1497-
mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
1501+
mt76_sta_remove(struct mt76_phy *phy, struct ieee80211_vif *vif,
14981502
struct ieee80211_sta *sta)
14991503
{
1504+
struct mt76_dev *dev = phy->dev;
1505+
15001506
mutex_lock(&dev->mutex);
1501-
__mt76_sta_remove(dev, vif, sta);
1507+
__mt76_sta_remove(phy, vif, sta);
15021508
mutex_unlock(&dev->mutex);
15031509
}
15041510

@@ -1517,7 +1523,7 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
15171523

15181524
if (old_state == IEEE80211_STA_NONE &&
15191525
new_state == IEEE80211_STA_NOTEXIST)
1520-
mt76_sta_remove(dev, vif, sta);
1526+
mt76_sta_remove(phy, vif, sta);
15211527

15221528
if (!dev->drv->sta_event)
15231529
return 0;

drivers/net/wireless/mediatek/mt76/mt76.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,7 @@ struct mt76_vif {
765765
u8 mcast_rates_idx;
766766
u8 beacon_rates_idx;
767767
struct ieee80211_chanctx_conf *ctx;
768+
struct mt76_wcid *wcid;
768769
};
769770

770771
struct mt76_phy {
@@ -773,14 +774,15 @@ struct mt76_phy {
773774
void *priv;
774775

775776
unsigned long state;
777+
unsigned int num_sta;
776778
u8 band_idx;
777779

778780
spinlock_t tx_lock;
779781
struct list_head tx_list;
780782
struct mt76_queue *q_tx[__MT_TXQ_MAX];
781783

782784
struct cfg80211_chan_def chandef;
783-
struct ieee80211_channel *main_chan;
785+
struct cfg80211_chan_def main_chandef;
784786
bool offchannel;
785787

786788
struct mt76_channel_state *chan_state;
@@ -910,6 +912,15 @@ struct mt76_dev {
910912

911913
u32 rxfilter;
912914

915+
struct delayed_work scan_work;
916+
struct {
917+
struct cfg80211_scan_request *req;
918+
struct ieee80211_channel *chan;
919+
struct ieee80211_vif *vif;
920+
struct mt76_phy *phy;
921+
int chan_idx;
922+
} scan;
923+
913924
#ifdef CONFIG_NL80211_TESTMODE
914925
const struct mt76_testmode_ops *test_ops;
915926
struct {
@@ -1422,7 +1433,7 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
14221433
struct ieee80211_sta *sta,
14231434
enum ieee80211_sta_state old_state,
14241435
enum ieee80211_sta_state new_state);
1425-
void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
1436+
void __mt76_sta_remove(struct mt76_phy *phy, struct ieee80211_vif *vif,
14261437
struct ieee80211_sta *sta);
14271438
void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
14281439
struct ieee80211_sta *sta);
@@ -1446,6 +1457,9 @@ void mt76_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id);
14461457
int mt76_get_rate(struct mt76_dev *dev,
14471458
struct ieee80211_supported_band *sband,
14481459
int idx, bool cck);
1460+
int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1461+
struct ieee80211_scan_request *hw_req);
1462+
void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
14491463
void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
14501464
const u8 *mac);
14511465
void mt76_sw_scan_complete(struct ieee80211_hw *hw,
@@ -1498,6 +1512,8 @@ void mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q,
14981512
struct mt76_queue_entry *e);
14991513
int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
15001514
bool offchannel);
1515+
void mt76_scan_work(struct work_struct *work);
1516+
void mt76_abort_scan(struct mt76_dev *dev);
15011517

15021518
/* usb */
15031519
static inline bool mt76u_urb_error(struct urb *urb)

drivers/net/wireless/mediatek/mt76/mt7615/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ static int mt7615_add_interface(struct ieee80211_hw *hw,
209209

210210
mvif->mt76.band_idx = ext_phy;
211211
mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
212+
mvif->mt76.wcid = &mvif->sta.wcid;
212213
if (ext_phy)
213214
mvif->mt76.wmm_idx += 2;
214215

drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ static void mt76x02_reset_state(struct mt76x02_dev *dev)
423423
priv = msta->vif;
424424
vif = container_of(priv, struct ieee80211_vif, drv_priv);
425425

426-
__mt76_sta_remove(&dev->mt76, vif, sta);
426+
__mt76_sta_remove(&dev->mphy, vif, sta);
427427
memset(msta, 0, sizeof(*msta));
428428
}
429429

drivers/net/wireless/mediatek/mt76/mt7915/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
233233
mvif->mt76.omac_idx = idx;
234234
mvif->phy = phy;
235235
mvif->mt76.band_idx = phy->mt76->band_idx;
236+
mvif->mt76.wcid = &mvif->sta.wcid;
236237

237238
mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
238239
if (ext_phy)

drivers/net/wireless/mediatek/mt76/mt7996/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
203203
mvif->phy = phy;
204204
mvif->mt76.band_idx = band_idx;
205205
mvif->mt76.wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
206+
mvif->mt76.wcid = &mvif->sta.wcid;
206207

207208
ret = mt7996_mcu_add_dev_info(phy, vif, true);
208209
if (ret)
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: ISC
2+
/*
3+
* Copyright (C) 2024 Felix Fietkau <[email protected]>
4+
*/
5+
#include "mt76.h"
6+
7+
static void mt76_scan_complete(struct mt76_dev *dev, bool abort)
8+
{
9+
struct mt76_phy *phy = dev->scan.phy;
10+
struct cfg80211_scan_info info = {
11+
.aborted = abort,
12+
};
13+
14+
if (!phy)
15+
return;
16+
17+
clear_bit(MT76_SCANNING, &phy->state);
18+
19+
if (dev->scan.chan && phy->main_chandef.chan)
20+
mt76_set_channel(phy, &phy->main_chandef, false);
21+
memset(&dev->scan, 0, sizeof(dev->scan));
22+
ieee80211_scan_completed(phy->hw, &info);
23+
}
24+
25+
void mt76_abort_scan(struct mt76_dev *dev)
26+
{
27+
cancel_delayed_work_sync(&dev->scan_work);
28+
mt76_scan_complete(dev, true);
29+
}
30+
31+
static void
32+
mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid)
33+
{
34+
struct cfg80211_scan_request *req = dev->scan.req;
35+
struct ieee80211_vif *vif = dev->scan.vif;
36+
struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
37+
enum nl80211_band band = dev->scan.chan->band;
38+
struct mt76_phy *phy = dev->scan.phy;
39+
struct ieee80211_tx_info *info;
40+
struct sk_buff *skb;
41+
42+
skb = ieee80211_probereq_get(phy->hw, vif->addr, ssid->ssid,
43+
ssid->ssid_len, req->ie_len);
44+
if (!skb)
45+
return;
46+
47+
if (is_unicast_ether_addr(req->bssid)) {
48+
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
49+
50+
ether_addr_copy(hdr->addr1, req->bssid);
51+
ether_addr_copy(hdr->addr3, req->bssid);
52+
}
53+
54+
info = IEEE80211_SKB_CB(skb);
55+
if (req->no_cck)
56+
info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
57+
info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK;
58+
59+
if (req->ie_len)
60+
skb_put_data(skb, req->ie, req->ie_len);
61+
62+
skb->priority = 7;
63+
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
64+
65+
rcu_read_lock();
66+
if (ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL))
67+
mt76_tx(phy, NULL, mvif->wcid, skb);
68+
else
69+
ieee80211_free_txskb(phy->hw, skb);
70+
rcu_read_unlock();
71+
}
72+
73+
void mt76_scan_work(struct work_struct *work)
74+
{
75+
struct mt76_dev *dev = container_of(work, struct mt76_dev,
76+
scan_work.work);
77+
struct cfg80211_scan_request *req = dev->scan.req;
78+
struct cfg80211_chan_def chandef = {};
79+
struct mt76_phy *phy = dev->scan.phy;
80+
int duration = HZ / 9; /* ~110 ms */
81+
int i;
82+
83+
if (dev->scan.chan_idx >= req->n_channels) {
84+
mt76_scan_complete(dev, false);
85+
return;
86+
}
87+
88+
if (dev->scan.chan && phy->num_sta) {
89+
dev->scan.chan = NULL;
90+
mt76_set_channel(phy, &phy->main_chandef, false);
91+
goto out;
92+
}
93+
94+
dev->scan.chan = req->channels[dev->scan.chan_idx++];
95+
cfg80211_chandef_create(&chandef, dev->scan.chan, NL80211_CHAN_HT20);
96+
mt76_set_channel(phy, &chandef, true);
97+
98+
if (!req->n_ssids ||
99+
chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))
100+
goto out;
101+
102+
duration = HZ / 16; /* ~60 ms */
103+
local_bh_disable();
104+
for (i = 0; i < req->n_ssids; i++)
105+
mt76_scan_send_probe(dev, &req->ssids[i]);
106+
local_bh_enable();
107+
108+
out:
109+
if (!duration)
110+
return;
111+
112+
if (dev->scan.chan)
113+
duration = max_t(int, duration,
114+
msecs_to_jiffies(req->duration +
115+
(req->duration >> 5)));
116+
117+
ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, duration);
118+
}
119+
120+
int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
121+
struct ieee80211_scan_request *req)
122+
{
123+
struct mt76_phy *phy = hw->priv;
124+
struct mt76_dev *dev = phy->dev;
125+
int ret = 0;
126+
127+
mutex_lock(&dev->mutex);
128+
129+
if (dev->scan.req) {
130+
ret = -EBUSY;
131+
goto out;
132+
}
133+
134+
memset(&dev->scan, 0, sizeof(dev->scan));
135+
dev->scan.req = &req->req;
136+
dev->scan.vif = vif;
137+
dev->scan.phy = phy;
138+
ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, 0);
139+
140+
out:
141+
mutex_unlock(&dev->mutex);
142+
143+
return ret;
144+
}
145+
EXPORT_SYMBOL_GPL(mt76_hw_scan);
146+
147+
void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
148+
{
149+
struct mt76_phy *phy = hw->priv;
150+
151+
mt76_abort_scan(phy->dev);
152+
}
153+
EXPORT_SYMBOL_GPL(mt76_cancel_hw_scan);

0 commit comments

Comments
 (0)