File Index
Files and directories
Sign in
FILE

scan.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/mediatek/mt76/scan.c C
  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		goto out;
 68
 69	info = IEEE80211_SKB_CB(skb);
 70	if (req->no_cck)
 71		info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
 72	info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK;
 73
 74	mt76_tx(phy, NULL, mvif->wcid, skb);
 75
 76out:
 77	rcu_read_unlock();
 78}
 79
 80void mt76_scan_work(struct work_struct *work)
 81{
 82	struct mt76_dev *dev = container_of(work, struct mt76_dev,
 83					    scan_work.work);
 84	struct cfg80211_scan_request *req = dev->scan.req;
 85	struct cfg80211_chan_def chandef = {};
 86	struct mt76_phy *phy = dev->scan.phy;
 87	int duration = HZ / 9; /* ~110 ms */
 88	int i;
 89
 90	if (dev->scan.chan_idx >= req->n_channels) {
 91		mt76_scan_complete(dev, false);
 92		return;
 93	}
 94
 95	if (dev->scan.chan && phy->num_sta) {
 96		dev->scan.chan = NULL;
 97		mt76_set_channel(phy, &phy->main_chandef, false);
 98		goto out;
 99	}
100
101	dev->scan.chan = req->channels[dev->scan.chan_idx++];
102	cfg80211_chandef_create(&chandef, dev->scan.chan, NL80211_CHAN_HT20);
103	mt76_set_channel(phy, &chandef, true);
104
105	if (!req->n_ssids ||
106	    chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))
107		goto out;
108
109	duration = HZ / 16; /* ~60 ms */
110	local_bh_disable();
111	for (i = 0; i < req->n_ssids; i++)
112		mt76_scan_send_probe(dev, &req->ssids[i]);
113	local_bh_enable();
114
115out:
116	if (dev->scan.chan)
117		duration = max_t(int, duration,
118			         msecs_to_jiffies(req->duration +
119						  (req->duration >> 5)));
120
121	ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, duration);
122}
123
124int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
125		 struct ieee80211_scan_request *req)
126{
127	struct mt76_phy *phy = hw->priv;
128	struct mt76_dev *dev = phy->dev;
129	struct mt76_vif_link *mlink;
130	int ret = 0;
131
132	if (hw->wiphy->n_radio > 1) {
133		phy = dev->band_phys[req->req.channels[0]->band];
134		if (!phy)
135			return -EINVAL;
136	}
137
138	mutex_lock(&dev->mutex);
139
140	if (dev->scan.req || phy->roc_vif ||
141	    test_bit(MT76_MCU_RESET, &dev->phy.state)) {
142		ret = -EBUSY;
143		goto out;
144	}
145
146	mlink = mt76_get_vif_phy_link(phy, vif);
147	if (IS_ERR(mlink)) {
148		ret = PTR_ERR(mlink);
149		goto out;
150	}
151
152	memset(&dev->scan, 0, sizeof(dev->scan));
153	dev->scan.req = &req->req;
154	dev->scan.vif = vif;
155	dev->scan.phy = phy;
156	dev->scan.mlink = mlink;
157	ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, 0);
158
159out:
160	mutex_unlock(&dev->mutex);
161
162	return ret;
163}
164EXPORT_SYMBOL_GPL(mt76_hw_scan);
165
166void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
167{
168	struct mt76_phy *phy = hw->priv;
169
170	mt76_abort_scan(phy->dev);
171}
172EXPORT_SYMBOL_GPL(mt76_cancel_hw_scan);
~/ Read-only file preview.