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