1 // SPDX-License-Identifier: ISC 2 /* 3 * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name> 4 */ 5 #include "mt76.h" 6 7 static int 8 mt76_phy_update_channel(struct mt76_phy *phy, 9 struct ieee80211_chanctx_conf *conf) 10 { 11 phy->radar_enabled = conf->radar_enabled; 12 phy->main_chandef = conf->def; 13 phy->chanctx = (struct mt76_chanctx *)conf->drv_priv; 14 15 return __mt76_set_channel(phy, &phy->main_chandef, false); 16 } 17 18 int mt76_add_chanctx(struct ieee80211_hw *hw, 19 struct ieee80211_chanctx_conf *conf) 20 { 21 struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; 22 struct mt76_phy *phy = hw->priv; 23 struct mt76_dev *dev = phy->dev; 24 int ret = -EINVAL; 25 26 phy = ctx->phy = dev->band_phys[conf->def.chan->band]; 27 if (WARN_ON_ONCE(!phy)) 28 return ret; 29 30 if (dev->scan.phy == phy) 31 mt76_abort_scan(dev); 32 33 mutex_lock(&dev->mutex); 34 if (!phy->chanctx) 35 ret = mt76_phy_update_channel(phy, conf); 36 else 37 ret = 0; 38 mutex_unlock(&dev->mutex); 39 40 return ret; 41 } 42 EXPORT_SYMBOL_GPL(mt76_add_chanctx); 43 44 void mt76_remove_chanctx(struct ieee80211_hw *hw, 45 struct ieee80211_chanctx_conf *conf) 46 { 47 struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; 48 struct mt76_phy *phy = hw->priv; 49 struct mt76_dev *dev = phy->dev; 50 51 phy = ctx->phy; 52 if (WARN_ON_ONCE(!phy)) 53 return; 54 55 if (dev->scan.phy == phy) 56 mt76_abort_scan(dev); 57 58 mutex_lock(&dev->mutex); 59 if (phy->chanctx == ctx) 60 phy->chanctx = NULL; 61 mutex_unlock(&dev->mutex); 62 } 63 EXPORT_SYMBOL_GPL(mt76_remove_chanctx); 64 65 void mt76_change_chanctx(struct ieee80211_hw *hw, 66 struct ieee80211_chanctx_conf *conf, 67 u32 changed) 68 { 69 struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; 70 struct mt76_phy *phy = ctx->phy; 71 struct mt76_dev *dev = phy->dev; 72 73 if (!(changed & (IEEE80211_CHANCTX_CHANGE_WIDTH | 74 IEEE80211_CHANCTX_CHANGE_RADAR))) 75 return; 76 77 cancel_delayed_work_sync(&phy->mac_work); 78 79 mutex_lock(&dev->mutex); 80 mt76_phy_update_channel(phy, conf); 81 mutex_unlock(&dev->mutex); 82 } 83 EXPORT_SYMBOL_GPL(mt76_change_chanctx); 84 85 86 int mt76_assign_vif_chanctx(struct ieee80211_hw *hw, 87 struct ieee80211_vif *vif, 88 struct ieee80211_bss_conf *link_conf, 89 struct ieee80211_chanctx_conf *conf) 90 { 91 struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; 92 struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; 93 struct mt76_vif_data *mvif = mlink->mvif; 94 int link_id = link_conf->link_id; 95 struct mt76_phy *phy = ctx->phy; 96 struct mt76_dev *dev = phy->dev; 97 bool mlink_alloc = false; 98 int ret = 0; 99 100 if (dev->scan.vif == vif) 101 mt76_abort_scan(dev); 102 103 mutex_lock(&dev->mutex); 104 105 if (vif->type == NL80211_IFTYPE_MONITOR && 106 is_zero_ether_addr(vif->addr)) 107 goto out; 108 109 mlink = mt76_vif_conf_link(dev, vif, link_conf); 110 if (!mlink) { 111 mlink = kzalloc(dev->drv->link_data_size, GFP_KERNEL); 112 if (!mlink) { 113 ret = -ENOMEM; 114 goto out; 115 } 116 mlink_alloc = true; 117 } 118 119 mlink->ctx = conf; 120 ret = dev->drv->vif_link_add(phy, vif, link_conf, mlink); 121 if (ret) { 122 if (mlink_alloc) 123 kfree(mlink); 124 goto out; 125 } 126 127 if (link_conf != &vif->bss_conf) 128 rcu_assign_pointer(mvif->link[link_id], mlink); 129 130 out: 131 mutex_unlock(&dev->mutex); 132 133 return ret; 134 } 135 EXPORT_SYMBOL_GPL(mt76_assign_vif_chanctx); 136 137 void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw, 138 struct ieee80211_vif *vif, 139 struct ieee80211_bss_conf *link_conf, 140 struct ieee80211_chanctx_conf *conf) 141 { 142 struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; 143 struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; 144 struct mt76_vif_data *mvif = mlink->mvif; 145 int link_id = link_conf->link_id; 146 struct mt76_phy *phy = ctx->phy; 147 struct mt76_dev *dev = phy->dev; 148 149 if (dev->scan.vif == vif) 150 mt76_abort_scan(dev); 151 152 mutex_lock(&dev->mutex); 153 154 if (vif->type == NL80211_IFTYPE_MONITOR && 155 is_zero_ether_addr(vif->addr)) 156 goto out; 157 158 mlink = mt76_vif_conf_link(dev, vif, link_conf); 159 if (!mlink) 160 goto out; 161 162 if (link_conf != &vif->bss_conf) 163 rcu_assign_pointer(mvif->link[link_id], NULL); 164 165 dev->drv->vif_link_remove(phy, vif, link_conf, mlink); 166 mlink->ctx = NULL; 167 168 if (link_conf != &vif->bss_conf) 169 kfree_rcu(mlink, rcu_head); 170 171 out: 172 mutex_unlock(&dev->mutex); 173 } 174 EXPORT_SYMBOL_GPL(mt76_unassign_vif_chanctx); 175 176 int mt76_switch_vif_chanctx(struct ieee80211_hw *hw, 177 struct ieee80211_vif_chanctx_switch *vifs, 178 int n_vifs, 179 enum ieee80211_chanctx_switch_mode mode) 180 { 181 struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv; 182 struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv; 183 struct ieee80211_chanctx_conf *conf = vifs->new_ctx; 184 struct mt76_phy *old_phy = old_ctx->phy; 185 struct mt76_phy *phy = hw->priv; 186 struct mt76_dev *dev = phy->dev; 187 struct mt76_vif_link *mlink; 188 bool update_chan; 189 int i, ret = 0; 190 191 if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS) 192 phy = new_ctx->phy = dev->band_phys[conf->def.chan->band]; 193 else 194 phy = new_ctx->phy; 195 if (!phy) 196 return -EINVAL; 197 198 update_chan = phy->chanctx != new_ctx; 199 if (update_chan) { 200 if (dev->scan.phy == phy) 201 mt76_abort_scan(dev); 202 203 cancel_delayed_work_sync(&phy->mac_work); 204 } 205 206 mutex_lock(&dev->mutex); 207 208 if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS && 209 phy != old_phy && old_phy->chanctx == old_ctx) 210 old_phy->chanctx = NULL; 211 212 if (update_chan) 213 ret = mt76_phy_update_channel(phy, vifs->new_ctx); 214 215 if (ret) 216 goto out; 217 218 if (old_phy == phy) 219 goto skip_link_replace; 220 221 for (i = 0; i < n_vifs; i++) { 222 mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf); 223 if (!mlink) 224 continue; 225 226 dev->drv->vif_link_remove(old_phy, vifs[i].vif, 227 vifs[i].link_conf, mlink); 228 229 ret = dev->drv->vif_link_add(phy, vifs[i].vif, 230 vifs[i].link_conf, mlink); 231 if (ret) 232 goto out; 233 234 } 235 236 skip_link_replace: 237 for (i = 0; i < n_vifs; i++) { 238 mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf); 239 if (!mlink) 240 continue; 241 242 mlink->ctx = vifs->new_ctx; 243 } 244 245 out: 246 mutex_unlock(&dev->mutex); 247 248 return ret; 249 } 250 EXPORT_SYMBOL_GPL(mt76_switch_vif_chanctx); 251