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