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