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