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