xref: /openbmc/linux/net/mac802154/scan.c (revision 57588c71177f0bfc08509c2c3a9bfe32850c0786)
1*57588c71SMiquel Raynal // SPDX-License-Identifier: GPL-2.0-only
2*57588c71SMiquel Raynal /*
3*57588c71SMiquel Raynal  * IEEE 802.15.4 scanning management
4*57588c71SMiquel Raynal  *
5*57588c71SMiquel Raynal  * Copyright (C) 2021 Qorvo US, Inc
6*57588c71SMiquel Raynal  * Authors:
7*57588c71SMiquel Raynal  *   - David Girault <david.girault@qorvo.com>
8*57588c71SMiquel Raynal  *   - Miquel Raynal <miquel.raynal@bootlin.com>
9*57588c71SMiquel Raynal  */
10*57588c71SMiquel Raynal 
11*57588c71SMiquel Raynal #include <linux/module.h>
12*57588c71SMiquel Raynal #include <linux/rtnetlink.h>
13*57588c71SMiquel Raynal #include <net/mac802154.h>
14*57588c71SMiquel Raynal 
15*57588c71SMiquel Raynal #include "ieee802154_i.h"
16*57588c71SMiquel Raynal #include "driver-ops.h"
17*57588c71SMiquel Raynal #include "../ieee802154/nl802154.h"
18*57588c71SMiquel Raynal 
19*57588c71SMiquel Raynal /* mac802154_scan_cleanup_locked() must be called upon scan completion or abort.
20*57588c71SMiquel Raynal  * - Completions are asynchronous, not locked by the rtnl and decided by the
21*57588c71SMiquel Raynal  *   scan worker.
22*57588c71SMiquel Raynal  * - Aborts are decided by userspace, and locked by the rtnl.
23*57588c71SMiquel Raynal  *
24*57588c71SMiquel Raynal  * Concurrent modifications to the PHY, the interfaces or the hardware is in
25*57588c71SMiquel Raynal  * general prevented by the rtnl. So in most cases we don't need additional
26*57588c71SMiquel Raynal  * protection.
27*57588c71SMiquel Raynal  *
28*57588c71SMiquel Raynal  * However, the scan worker get's triggered without anybody noticing and thus we
29*57588c71SMiquel Raynal  * must ensure the presence of the devices as well as data consistency:
30*57588c71SMiquel Raynal  * - The sub-interface and device driver module get both their reference
31*57588c71SMiquel Raynal  *   counters incremented whenever we start a scan, so they cannot disappear
32*57588c71SMiquel Raynal  *   during operation.
33*57588c71SMiquel Raynal  * - Data consistency is achieved by the use of rcu protected pointers.
34*57588c71SMiquel Raynal  */
35*57588c71SMiquel Raynal static int mac802154_scan_cleanup_locked(struct ieee802154_local *local,
36*57588c71SMiquel Raynal 					 struct ieee802154_sub_if_data *sdata,
37*57588c71SMiquel Raynal 					 bool aborted)
38*57588c71SMiquel Raynal {
39*57588c71SMiquel Raynal 	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
40*57588c71SMiquel Raynal 	struct wpan_phy *wpan_phy = local->phy;
41*57588c71SMiquel Raynal 	struct cfg802154_scan_request *request;
42*57588c71SMiquel Raynal 	u8 arg;
43*57588c71SMiquel Raynal 
44*57588c71SMiquel Raynal 	/* Prevent any further use of the scan request */
45*57588c71SMiquel Raynal 	clear_bit(IEEE802154_IS_SCANNING, &local->ongoing);
46*57588c71SMiquel Raynal 	cancel_delayed_work(&local->scan_work);
47*57588c71SMiquel Raynal 	request = rcu_replace_pointer(local->scan_req, NULL, 1);
48*57588c71SMiquel Raynal 	if (!request)
49*57588c71SMiquel Raynal 		return 0;
50*57588c71SMiquel Raynal 	kfree_rcu(request);
51*57588c71SMiquel Raynal 
52*57588c71SMiquel Raynal 	/* Advertize first, while we know the devices cannot be removed */
53*57588c71SMiquel Raynal 	if (aborted)
54*57588c71SMiquel Raynal 		arg = NL802154_SCAN_DONE_REASON_ABORTED;
55*57588c71SMiquel Raynal 	else
56*57588c71SMiquel Raynal 		arg = NL802154_SCAN_DONE_REASON_FINISHED;
57*57588c71SMiquel Raynal 	nl802154_scan_done(wpan_phy, wpan_dev, arg);
58*57588c71SMiquel Raynal 
59*57588c71SMiquel Raynal 	/* Cleanup software stack */
60*57588c71SMiquel Raynal 	ieee802154_mlme_op_post(local);
61*57588c71SMiquel Raynal 
62*57588c71SMiquel Raynal 	/* Set the hardware back in its original state */
63*57588c71SMiquel Raynal 	drv_set_channel(local, wpan_phy->current_page,
64*57588c71SMiquel Raynal 			wpan_phy->current_channel);
65*57588c71SMiquel Raynal 	ieee802154_configure_durations(wpan_phy, wpan_phy->current_page,
66*57588c71SMiquel Raynal 				       wpan_phy->current_channel);
67*57588c71SMiquel Raynal 	drv_stop(local);
68*57588c71SMiquel Raynal 	synchronize_net();
69*57588c71SMiquel Raynal 	sdata->required_filtering = sdata->iface_default_filtering;
70*57588c71SMiquel Raynal 	drv_start(local, sdata->required_filtering, &local->addr_filt);
71*57588c71SMiquel Raynal 
72*57588c71SMiquel Raynal 	return 0;
73*57588c71SMiquel Raynal }
74*57588c71SMiquel Raynal 
75*57588c71SMiquel Raynal int mac802154_abort_scan_locked(struct ieee802154_local *local,
76*57588c71SMiquel Raynal 				struct ieee802154_sub_if_data *sdata)
77*57588c71SMiquel Raynal {
78*57588c71SMiquel Raynal 	ASSERT_RTNL();
79*57588c71SMiquel Raynal 
80*57588c71SMiquel Raynal 	if (!mac802154_is_scanning(local))
81*57588c71SMiquel Raynal 		return -ESRCH;
82*57588c71SMiquel Raynal 
83*57588c71SMiquel Raynal 	return mac802154_scan_cleanup_locked(local, sdata, true);
84*57588c71SMiquel Raynal }
85*57588c71SMiquel Raynal 
86*57588c71SMiquel Raynal static unsigned int mac802154_scan_get_channel_time(u8 duration_order,
87*57588c71SMiquel Raynal 						    u8 symbol_duration)
88*57588c71SMiquel Raynal {
89*57588c71SMiquel Raynal 	u64 base_super_frame_duration = (u64)symbol_duration *
90*57588c71SMiquel Raynal 		IEEE802154_SUPERFRAME_PERIOD * IEEE802154_SLOT_PERIOD;
91*57588c71SMiquel Raynal 
92*57588c71SMiquel Raynal 	return usecs_to_jiffies(base_super_frame_duration *
93*57588c71SMiquel Raynal 				(BIT(duration_order) + 1));
94*57588c71SMiquel Raynal }
95*57588c71SMiquel Raynal 
96*57588c71SMiquel Raynal static void mac802154_flush_queued_beacons(struct ieee802154_local *local)
97*57588c71SMiquel Raynal {
98*57588c71SMiquel Raynal 	struct cfg802154_mac_pkt *mac_pkt, *tmp;
99*57588c71SMiquel Raynal 
100*57588c71SMiquel Raynal 	list_for_each_entry_safe(mac_pkt, tmp, &local->rx_beacon_list, node) {
101*57588c71SMiquel Raynal 		list_del(&mac_pkt->node);
102*57588c71SMiquel Raynal 		kfree_skb(mac_pkt->skb);
103*57588c71SMiquel Raynal 		kfree(mac_pkt);
104*57588c71SMiquel Raynal 	}
105*57588c71SMiquel Raynal }
106*57588c71SMiquel Raynal 
107*57588c71SMiquel Raynal static void
108*57588c71SMiquel Raynal mac802154_scan_get_next_channel(struct ieee802154_local *local,
109*57588c71SMiquel Raynal 				struct cfg802154_scan_request *scan_req,
110*57588c71SMiquel Raynal 				u8 *channel)
111*57588c71SMiquel Raynal {
112*57588c71SMiquel Raynal 	(*channel)++;
113*57588c71SMiquel Raynal 	*channel = find_next_bit((const unsigned long *)&scan_req->channels,
114*57588c71SMiquel Raynal 				 IEEE802154_MAX_CHANNEL + 1,
115*57588c71SMiquel Raynal 				 *channel);
116*57588c71SMiquel Raynal }
117*57588c71SMiquel Raynal 
118*57588c71SMiquel Raynal static int mac802154_scan_find_next_chan(struct ieee802154_local *local,
119*57588c71SMiquel Raynal 					 struct cfg802154_scan_request *scan_req,
120*57588c71SMiquel Raynal 					 u8 page, u8 *channel)
121*57588c71SMiquel Raynal {
122*57588c71SMiquel Raynal 	mac802154_scan_get_next_channel(local, scan_req, channel);
123*57588c71SMiquel Raynal 	if (*channel > IEEE802154_MAX_CHANNEL)
124*57588c71SMiquel Raynal 		return -EINVAL;
125*57588c71SMiquel Raynal 
126*57588c71SMiquel Raynal 	return 0;
127*57588c71SMiquel Raynal }
128*57588c71SMiquel Raynal 
129*57588c71SMiquel Raynal void mac802154_scan_worker(struct work_struct *work)
130*57588c71SMiquel Raynal {
131*57588c71SMiquel Raynal 	struct ieee802154_local *local =
132*57588c71SMiquel Raynal 		container_of(work, struct ieee802154_local, scan_work.work);
133*57588c71SMiquel Raynal 	struct cfg802154_scan_request *scan_req;
134*57588c71SMiquel Raynal 	struct ieee802154_sub_if_data *sdata;
135*57588c71SMiquel Raynal 	unsigned int scan_duration = 0;
136*57588c71SMiquel Raynal 	struct wpan_phy *wpan_phy;
137*57588c71SMiquel Raynal 	u8 scan_req_duration;
138*57588c71SMiquel Raynal 	u8 page, channel;
139*57588c71SMiquel Raynal 	int ret;
140*57588c71SMiquel Raynal 
141*57588c71SMiquel Raynal 	/* Ensure the device receiver is turned off when changing channels
142*57588c71SMiquel Raynal 	 * because there is no atomic way to change the channel and know on
143*57588c71SMiquel Raynal 	 * which one a beacon might have been received.
144*57588c71SMiquel Raynal 	 */
145*57588c71SMiquel Raynal 	drv_stop(local);
146*57588c71SMiquel Raynal 	synchronize_net();
147*57588c71SMiquel Raynal 	mac802154_flush_queued_beacons(local);
148*57588c71SMiquel Raynal 
149*57588c71SMiquel Raynal 	rcu_read_lock();
150*57588c71SMiquel Raynal 	scan_req = rcu_dereference(local->scan_req);
151*57588c71SMiquel Raynal 	if (unlikely(!scan_req)) {
152*57588c71SMiquel Raynal 		rcu_read_unlock();
153*57588c71SMiquel Raynal 		return;
154*57588c71SMiquel Raynal 	}
155*57588c71SMiquel Raynal 
156*57588c71SMiquel Raynal 	sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(scan_req->wpan_dev);
157*57588c71SMiquel Raynal 
158*57588c71SMiquel Raynal 	/* Wait an arbitrary amount of time in case we cannot use the device */
159*57588c71SMiquel Raynal 	if (local->suspended || !ieee802154_sdata_running(sdata)) {
160*57588c71SMiquel Raynal 		rcu_read_unlock();
161*57588c71SMiquel Raynal 		queue_delayed_work(local->mac_wq, &local->scan_work,
162*57588c71SMiquel Raynal 				   msecs_to_jiffies(1000));
163*57588c71SMiquel Raynal 		return;
164*57588c71SMiquel Raynal 	}
165*57588c71SMiquel Raynal 
166*57588c71SMiquel Raynal 	wpan_phy = scan_req->wpan_phy;
167*57588c71SMiquel Raynal 	scan_req_duration = scan_req->duration;
168*57588c71SMiquel Raynal 
169*57588c71SMiquel Raynal 	/* Look for the next valid chan */
170*57588c71SMiquel Raynal 	page = local->scan_page;
171*57588c71SMiquel Raynal 	channel = local->scan_channel;
172*57588c71SMiquel Raynal 	do {
173*57588c71SMiquel Raynal 		ret = mac802154_scan_find_next_chan(local, scan_req, page, &channel);
174*57588c71SMiquel Raynal 		if (ret) {
175*57588c71SMiquel Raynal 			rcu_read_unlock();
176*57588c71SMiquel Raynal 			goto end_scan;
177*57588c71SMiquel Raynal 		}
178*57588c71SMiquel Raynal 	} while (!ieee802154_chan_is_valid(scan_req->wpan_phy, page, channel));
179*57588c71SMiquel Raynal 
180*57588c71SMiquel Raynal 	rcu_read_unlock();
181*57588c71SMiquel Raynal 
182*57588c71SMiquel Raynal 	/* Bypass the stack on purpose when changing the channel */
183*57588c71SMiquel Raynal 	rtnl_lock();
184*57588c71SMiquel Raynal 	ret = drv_set_channel(local, page, channel);
185*57588c71SMiquel Raynal 	rtnl_unlock();
186*57588c71SMiquel Raynal 	if (ret) {
187*57588c71SMiquel Raynal 		dev_err(&sdata->dev->dev,
188*57588c71SMiquel Raynal 			"Channel change failure during scan, aborting (%d)\n", ret);
189*57588c71SMiquel Raynal 		goto end_scan;
190*57588c71SMiquel Raynal 	}
191*57588c71SMiquel Raynal 
192*57588c71SMiquel Raynal 	local->scan_page = page;
193*57588c71SMiquel Raynal 	local->scan_channel = channel;
194*57588c71SMiquel Raynal 
195*57588c71SMiquel Raynal 	rtnl_lock();
196*57588c71SMiquel Raynal 	ret = drv_start(local, IEEE802154_FILTERING_3_SCAN, &local->addr_filt);
197*57588c71SMiquel Raynal 	rtnl_unlock();
198*57588c71SMiquel Raynal 	if (ret) {
199*57588c71SMiquel Raynal 		dev_err(&sdata->dev->dev,
200*57588c71SMiquel Raynal 			"Restarting failure after channel change, aborting (%d)\n", ret);
201*57588c71SMiquel Raynal 		goto end_scan;
202*57588c71SMiquel Raynal 	}
203*57588c71SMiquel Raynal 
204*57588c71SMiquel Raynal 	ieee802154_configure_durations(wpan_phy, page, channel);
205*57588c71SMiquel Raynal 	scan_duration = mac802154_scan_get_channel_time(scan_req_duration,
206*57588c71SMiquel Raynal 							wpan_phy->symbol_duration);
207*57588c71SMiquel Raynal 	dev_dbg(&sdata->dev->dev,
208*57588c71SMiquel Raynal 		"Scan page %u channel %u for %ums\n",
209*57588c71SMiquel Raynal 		page, channel, jiffies_to_msecs(scan_duration));
210*57588c71SMiquel Raynal 	queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration);
211*57588c71SMiquel Raynal 	return;
212*57588c71SMiquel Raynal 
213*57588c71SMiquel Raynal end_scan:
214*57588c71SMiquel Raynal 	rtnl_lock();
215*57588c71SMiquel Raynal 	mac802154_scan_cleanup_locked(local, sdata, false);
216*57588c71SMiquel Raynal 	rtnl_unlock();
217*57588c71SMiquel Raynal }
218*57588c71SMiquel Raynal 
219*57588c71SMiquel Raynal int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata,
220*57588c71SMiquel Raynal 				  struct cfg802154_scan_request *request)
221*57588c71SMiquel Raynal {
222*57588c71SMiquel Raynal 	struct ieee802154_local *local = sdata->local;
223*57588c71SMiquel Raynal 
224*57588c71SMiquel Raynal 	ASSERT_RTNL();
225*57588c71SMiquel Raynal 
226*57588c71SMiquel Raynal 	if (mac802154_is_scanning(local))
227*57588c71SMiquel Raynal 		return -EBUSY;
228*57588c71SMiquel Raynal 
229*57588c71SMiquel Raynal 	/* TODO: support other scanning type */
230*57588c71SMiquel Raynal 	if (request->type != NL802154_SCAN_PASSIVE)
231*57588c71SMiquel Raynal 		return -EOPNOTSUPP;
232*57588c71SMiquel Raynal 
233*57588c71SMiquel Raynal 	/* Store scanning parameters */
234*57588c71SMiquel Raynal 	rcu_assign_pointer(local->scan_req, request);
235*57588c71SMiquel Raynal 
236*57588c71SMiquel Raynal 	/* Software scanning requires to set promiscuous mode, so we need to
237*57588c71SMiquel Raynal 	 * pause the Tx queue during the entire operation.
238*57588c71SMiquel Raynal 	 */
239*57588c71SMiquel Raynal 	ieee802154_mlme_op_pre(local);
240*57588c71SMiquel Raynal 
241*57588c71SMiquel Raynal 	sdata->required_filtering = IEEE802154_FILTERING_3_SCAN;
242*57588c71SMiquel Raynal 	local->scan_page = request->page;
243*57588c71SMiquel Raynal 	local->scan_channel = -1;
244*57588c71SMiquel Raynal 	set_bit(IEEE802154_IS_SCANNING, &local->ongoing);
245*57588c71SMiquel Raynal 
246*57588c71SMiquel Raynal 	nl802154_scan_started(request->wpan_phy, request->wpan_dev);
247*57588c71SMiquel Raynal 
248*57588c71SMiquel Raynal 	queue_delayed_work(local->mac_wq, &local->scan_work, 0);
249*57588c71SMiquel Raynal 
250*57588c71SMiquel Raynal 	return 0;
251*57588c71SMiquel Raynal }
252*57588c71SMiquel Raynal 
253*57588c71SMiquel Raynal int mac802154_process_beacon(struct ieee802154_local *local,
254*57588c71SMiquel Raynal 			     struct sk_buff *skb,
255*57588c71SMiquel Raynal 			     u8 page, u8 channel)
256*57588c71SMiquel Raynal {
257*57588c71SMiquel Raynal 	struct ieee802154_beacon_hdr *bh = (void *)skb->data;
258*57588c71SMiquel Raynal 	struct ieee802154_addr *src = &mac_cb(skb)->source;
259*57588c71SMiquel Raynal 	struct cfg802154_scan_request *scan_req;
260*57588c71SMiquel Raynal 	struct ieee802154_coord_desc desc;
261*57588c71SMiquel Raynal 
262*57588c71SMiquel Raynal 	if (skb->len != sizeof(*bh))
263*57588c71SMiquel Raynal 		return -EINVAL;
264*57588c71SMiquel Raynal 
265*57588c71SMiquel Raynal 	if (unlikely(src->mode == IEEE802154_ADDR_NONE))
266*57588c71SMiquel Raynal 		return -EINVAL;
267*57588c71SMiquel Raynal 
268*57588c71SMiquel Raynal 	dev_dbg(&skb->dev->dev,
269*57588c71SMiquel Raynal 		"BEACON received on page %u channel %u\n",
270*57588c71SMiquel Raynal 		page, channel);
271*57588c71SMiquel Raynal 
272*57588c71SMiquel Raynal 	memcpy(&desc.addr, src, sizeof(desc.addr));
273*57588c71SMiquel Raynal 	desc.page = page;
274*57588c71SMiquel Raynal 	desc.channel = channel;
275*57588c71SMiquel Raynal 	desc.link_quality = mac_cb(skb)->lqi;
276*57588c71SMiquel Raynal 	desc.superframe_spec = get_unaligned_le16(skb->data);
277*57588c71SMiquel Raynal 	desc.gts_permit = bh->gts_permit;
278*57588c71SMiquel Raynal 
279*57588c71SMiquel Raynal 	trace_802154_scan_event(&desc);
280*57588c71SMiquel Raynal 
281*57588c71SMiquel Raynal 	rcu_read_lock();
282*57588c71SMiquel Raynal 	scan_req = rcu_dereference(local->scan_req);
283*57588c71SMiquel Raynal 	if (likely(scan_req))
284*57588c71SMiquel Raynal 		nl802154_scan_event(scan_req->wpan_phy, scan_req->wpan_dev, &desc);
285*57588c71SMiquel Raynal 	rcu_read_unlock();
286*57588c71SMiquel Raynal 
287*57588c71SMiquel Raynal 	return 0;
288*57588c71SMiquel Raynal }
289