xref: /linux/drivers/net/wireless/mediatek/mt76/scan.c (revision bf18f7172aa429ec6a68852984a2e9468560c066)
1 // SPDX-License-Identifier: ISC
2 /*
3  * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
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_link *mvif = (struct mt76_vif_link *)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);
154