xref: /linux/drivers/net/wireless/mediatek/mt76/channel.c (revision 38a45bead2be0bd481f3143dc4fe451cb9d09823)
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