xref: /openbmc/linux/net/wireless/chan.c (revision b6bec26c)
1 /*
2  * This file contains helper code to handle channel
3  * settings and keeping track of what is possible at
4  * any point in time.
5  *
6  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
7  */
8 
9 #include <linux/export.h>
10 #include <net/cfg80211.h>
11 #include "core.h"
12 #include "rdev-ops.h"
13 
14 void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
15 			     struct ieee80211_channel *chan,
16 			     enum nl80211_channel_type chan_type)
17 {
18 	if (WARN_ON(!chan))
19 		return;
20 
21 	chandef->chan = chan;
22 	chandef->center_freq2 = 0;
23 
24 	switch (chan_type) {
25 	case NL80211_CHAN_NO_HT:
26 		chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
27 		chandef->center_freq1 = chan->center_freq;
28 		break;
29 	case NL80211_CHAN_HT20:
30 		chandef->width = NL80211_CHAN_WIDTH_20;
31 		chandef->center_freq1 = chan->center_freq;
32 		break;
33 	case NL80211_CHAN_HT40PLUS:
34 		chandef->width = NL80211_CHAN_WIDTH_40;
35 		chandef->center_freq1 = chan->center_freq + 10;
36 		break;
37 	case NL80211_CHAN_HT40MINUS:
38 		chandef->width = NL80211_CHAN_WIDTH_40;
39 		chandef->center_freq1 = chan->center_freq - 10;
40 		break;
41 	default:
42 		WARN_ON(1);
43 	}
44 }
45 EXPORT_SYMBOL(cfg80211_chandef_create);
46 
47 bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
48 {
49 	u32 control_freq;
50 
51 	if (!chandef->chan)
52 		return false;
53 
54 	control_freq = chandef->chan->center_freq;
55 
56 	switch (chandef->width) {
57 	case NL80211_CHAN_WIDTH_20:
58 	case NL80211_CHAN_WIDTH_20_NOHT:
59 		if (chandef->center_freq1 != control_freq)
60 			return false;
61 		if (chandef->center_freq2)
62 			return false;
63 		break;
64 	case NL80211_CHAN_WIDTH_40:
65 		if (chandef->center_freq1 != control_freq + 10 &&
66 		    chandef->center_freq1 != control_freq - 10)
67 			return false;
68 		if (chandef->center_freq2)
69 			return false;
70 		break;
71 	case NL80211_CHAN_WIDTH_80P80:
72 		if (chandef->center_freq1 != control_freq + 30 &&
73 		    chandef->center_freq1 != control_freq + 10 &&
74 		    chandef->center_freq1 != control_freq - 10 &&
75 		    chandef->center_freq1 != control_freq - 30)
76 			return false;
77 		if (!chandef->center_freq2)
78 			return false;
79 		break;
80 	case NL80211_CHAN_WIDTH_80:
81 		if (chandef->center_freq1 != control_freq + 30 &&
82 		    chandef->center_freq1 != control_freq + 10 &&
83 		    chandef->center_freq1 != control_freq - 10 &&
84 		    chandef->center_freq1 != control_freq - 30)
85 			return false;
86 		if (chandef->center_freq2)
87 			return false;
88 		break;
89 	case NL80211_CHAN_WIDTH_160:
90 		if (chandef->center_freq1 != control_freq + 70 &&
91 		    chandef->center_freq1 != control_freq + 50 &&
92 		    chandef->center_freq1 != control_freq + 30 &&
93 		    chandef->center_freq1 != control_freq + 10 &&
94 		    chandef->center_freq1 != control_freq - 10 &&
95 		    chandef->center_freq1 != control_freq - 30 &&
96 		    chandef->center_freq1 != control_freq - 50 &&
97 		    chandef->center_freq1 != control_freq - 70)
98 			return false;
99 		if (chandef->center_freq2)
100 			return false;
101 		break;
102 	default:
103 		return false;
104 	}
105 
106 	return true;
107 }
108 EXPORT_SYMBOL(cfg80211_chandef_valid);
109 
110 static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
111 				  int *pri40, int *pri80)
112 {
113 	int tmp;
114 
115 	switch (c->width) {
116 	case NL80211_CHAN_WIDTH_40:
117 		*pri40 = c->center_freq1;
118 		*pri80 = 0;
119 		break;
120 	case NL80211_CHAN_WIDTH_80:
121 	case NL80211_CHAN_WIDTH_80P80:
122 		*pri80 = c->center_freq1;
123 		/* n_P20 */
124 		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
125 		/* n_P40 */
126 		tmp /= 2;
127 		/* freq_P40 */
128 		*pri40 = c->center_freq1 - 20 + 40 * tmp;
129 		break;
130 	case NL80211_CHAN_WIDTH_160:
131 		/* n_P20 */
132 		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
133 		/* n_P40 */
134 		tmp /= 2;
135 		/* freq_P40 */
136 		*pri40 = c->center_freq1 - 60 + 40 * tmp;
137 		/* n_P80 */
138 		tmp /= 2;
139 		*pri80 = c->center_freq1 - 40 + 80 * tmp;
140 		break;
141 	default:
142 		WARN_ON_ONCE(1);
143 	}
144 }
145 
146 const struct cfg80211_chan_def *
147 cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
148 			    const struct cfg80211_chan_def *c2)
149 {
150 	u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
151 
152 	/* If they are identical, return */
153 	if (cfg80211_chandef_identical(c1, c2))
154 		return c1;
155 
156 	/* otherwise, must have same control channel */
157 	if (c1->chan != c2->chan)
158 		return NULL;
159 
160 	/*
161 	 * If they have the same width, but aren't identical,
162 	 * then they can't be compatible.
163 	 */
164 	if (c1->width == c2->width)
165 		return NULL;
166 
167 	if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
168 	    c1->width == NL80211_CHAN_WIDTH_20)
169 		return c2;
170 
171 	if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
172 	    c2->width == NL80211_CHAN_WIDTH_20)
173 		return c1;
174 
175 	chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
176 	chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
177 
178 	if (c1_pri40 != c2_pri40)
179 		return NULL;
180 
181 	WARN_ON(!c1_pri80 && !c2_pri80);
182 	if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
183 		return NULL;
184 
185 	if (c1->width > c2->width)
186 		return c1;
187 	return c2;
188 }
189 EXPORT_SYMBOL(cfg80211_chandef_compatible);
190 
191 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
192 					u32 center_freq, u32 bandwidth,
193 					u32 prohibited_flags)
194 {
195 	struct ieee80211_channel *c;
196 	u32 freq;
197 
198 	for (freq = center_freq - bandwidth/2 + 10;
199 	     freq <= center_freq + bandwidth/2 - 10;
200 	     freq += 20) {
201 		c = ieee80211_get_channel(wiphy, freq);
202 		if (!c || c->flags & prohibited_flags)
203 			return false;
204 	}
205 
206 	return true;
207 }
208 
209 bool cfg80211_chandef_usable(struct wiphy *wiphy,
210 			     const struct cfg80211_chan_def *chandef,
211 			     u32 prohibited_flags)
212 {
213 	struct ieee80211_sta_ht_cap *ht_cap;
214 	struct ieee80211_sta_vht_cap *vht_cap;
215 	u32 width, control_freq;
216 
217 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
218 		return false;
219 
220 	ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
221 	vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
222 
223 	control_freq = chandef->chan->center_freq;
224 
225 	switch (chandef->width) {
226 	case NL80211_CHAN_WIDTH_20:
227 		if (!ht_cap->ht_supported)
228 			return false;
229 	case NL80211_CHAN_WIDTH_20_NOHT:
230 		width = 20;
231 		break;
232 	case NL80211_CHAN_WIDTH_40:
233 		width = 40;
234 		if (!ht_cap->ht_supported)
235 			return false;
236 		if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
237 		    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
238 			return false;
239 		if (chandef->center_freq1 < control_freq &&
240 		    chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
241 			return false;
242 		if (chandef->center_freq1 > control_freq &&
243 		    chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
244 			return false;
245 		break;
246 	case NL80211_CHAN_WIDTH_80P80:
247 		if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
248 			return false;
249 	case NL80211_CHAN_WIDTH_80:
250 		if (!vht_cap->vht_supported)
251 			return false;
252 		width = 80;
253 		break;
254 	case NL80211_CHAN_WIDTH_160:
255 		if (!vht_cap->vht_supported)
256 			return false;
257 		if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
258 			return false;
259 		width = 160;
260 		break;
261 	default:
262 		WARN_ON_ONCE(1);
263 		return false;
264 	}
265 
266 	/* TODO: missing regulatory check on 80/160 bandwidth */
267 
268 	if (width > 20)
269 		prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
270 
271 	if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
272 					 width, prohibited_flags))
273 		return false;
274 
275 	if (!chandef->center_freq2)
276 		return true;
277 	return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
278 					   width, prohibited_flags);
279 }
280 EXPORT_SYMBOL(cfg80211_chandef_usable);
281 
282 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
283 			     struct cfg80211_chan_def *chandef)
284 {
285 	bool res;
286 
287 	trace_cfg80211_reg_can_beacon(wiphy, chandef);
288 
289 	res = cfg80211_chandef_usable(wiphy, chandef,
290 				      IEEE80211_CHAN_DISABLED |
291 				      IEEE80211_CHAN_PASSIVE_SCAN |
292 				      IEEE80211_CHAN_NO_IBSS |
293 				      IEEE80211_CHAN_RADAR);
294 
295 	trace_cfg80211_return_bool(res);
296 	return res;
297 }
298 EXPORT_SYMBOL(cfg80211_reg_can_beacon);
299 
300 int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
301 				 struct cfg80211_chan_def *chandef)
302 {
303 	if (!rdev->ops->set_monitor_channel)
304 		return -EOPNOTSUPP;
305 	if (!cfg80211_has_monitors_only(rdev))
306 		return -EBUSY;
307 
308 	return rdev_set_monitor_channel(rdev, chandef);
309 }
310 
311 void
312 cfg80211_get_chan_state(struct wireless_dev *wdev,
313 		        struct ieee80211_channel **chan,
314 		        enum cfg80211_chan_mode *chanmode)
315 {
316 	*chan = NULL;
317 	*chanmode = CHAN_MODE_UNDEFINED;
318 
319 	ASSERT_WDEV_LOCK(wdev);
320 
321 	if (wdev->netdev && !netif_running(wdev->netdev))
322 		return;
323 
324 	switch (wdev->iftype) {
325 	case NL80211_IFTYPE_ADHOC:
326 		if (wdev->current_bss) {
327 			*chan = wdev->current_bss->pub.channel;
328 			*chanmode = wdev->ibss_fixed
329 				  ? CHAN_MODE_SHARED
330 				  : CHAN_MODE_EXCLUSIVE;
331 			return;
332 		}
333 	case NL80211_IFTYPE_STATION:
334 	case NL80211_IFTYPE_P2P_CLIENT:
335 		if (wdev->current_bss) {
336 			*chan = wdev->current_bss->pub.channel;
337 			*chanmode = CHAN_MODE_SHARED;
338 			return;
339 		}
340 		break;
341 	case NL80211_IFTYPE_AP:
342 	case NL80211_IFTYPE_P2P_GO:
343 		if (wdev->beacon_interval) {
344 			*chan = wdev->channel;
345 			*chanmode = CHAN_MODE_SHARED;
346 		}
347 		return;
348 	case NL80211_IFTYPE_MESH_POINT:
349 		if (wdev->mesh_id_len) {
350 			*chan = wdev->channel;
351 			*chanmode = CHAN_MODE_SHARED;
352 		}
353 		return;
354 	case NL80211_IFTYPE_MONITOR:
355 	case NL80211_IFTYPE_AP_VLAN:
356 	case NL80211_IFTYPE_WDS:
357 		/* these interface types don't really have a channel */
358 		return;
359 	case NL80211_IFTYPE_P2P_DEVICE:
360 		if (wdev->wiphy->features &
361 				NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
362 			*chanmode = CHAN_MODE_EXCLUSIVE;
363 		return;
364 	case NL80211_IFTYPE_UNSPECIFIED:
365 	case NUM_NL80211_IFTYPES:
366 		WARN_ON(1);
367 	}
368 
369 	return;
370 }
371