xref: /freebsd/contrib/wpa/src/common/hw_features_common.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
1*5b9c547cSRui Paulo /*
2*5b9c547cSRui Paulo  * Common hostapd/wpa_supplicant HW features
3*5b9c547cSRui Paulo  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4*5b9c547cSRui Paulo  * Copyright (c) 2015, Qualcomm Atheros, Inc.
5*5b9c547cSRui Paulo  *
6*5b9c547cSRui Paulo  * This software may be distributed under the terms of the BSD license.
7*5b9c547cSRui Paulo  * See README for more details.
8*5b9c547cSRui Paulo  */
9*5b9c547cSRui Paulo 
10*5b9c547cSRui Paulo #include "includes.h"
11*5b9c547cSRui Paulo 
12*5b9c547cSRui Paulo #include "common.h"
13*5b9c547cSRui Paulo #include "defs.h"
14*5b9c547cSRui Paulo #include "ieee802_11_defs.h"
15*5b9c547cSRui Paulo #include "ieee802_11_common.h"
16*5b9c547cSRui Paulo #include "hw_features_common.h"
17*5b9c547cSRui Paulo 
18*5b9c547cSRui Paulo 
19*5b9c547cSRui Paulo struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
20*5b9c547cSRui Paulo 						  int chan, int *freq)
21*5b9c547cSRui Paulo {
22*5b9c547cSRui Paulo 	int i;
23*5b9c547cSRui Paulo 
24*5b9c547cSRui Paulo 	if (freq)
25*5b9c547cSRui Paulo 		*freq = 0;
26*5b9c547cSRui Paulo 
27*5b9c547cSRui Paulo 	if (!mode)
28*5b9c547cSRui Paulo 		return NULL;
29*5b9c547cSRui Paulo 
30*5b9c547cSRui Paulo 	for (i = 0; i < mode->num_channels; i++) {
31*5b9c547cSRui Paulo 		struct hostapd_channel_data *ch = &mode->channels[i];
32*5b9c547cSRui Paulo 		if (ch->chan == chan) {
33*5b9c547cSRui Paulo 			if (freq)
34*5b9c547cSRui Paulo 				*freq = ch->freq;
35*5b9c547cSRui Paulo 			return ch;
36*5b9c547cSRui Paulo 		}
37*5b9c547cSRui Paulo 	}
38*5b9c547cSRui Paulo 
39*5b9c547cSRui Paulo 	return NULL;
40*5b9c547cSRui Paulo }
41*5b9c547cSRui Paulo 
42*5b9c547cSRui Paulo 
43*5b9c547cSRui Paulo struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
44*5b9c547cSRui Paulo 						  int freq, int *chan)
45*5b9c547cSRui Paulo {
46*5b9c547cSRui Paulo 	int i;
47*5b9c547cSRui Paulo 
48*5b9c547cSRui Paulo 	if (chan)
49*5b9c547cSRui Paulo 		*chan = 0;
50*5b9c547cSRui Paulo 
51*5b9c547cSRui Paulo 	if (!mode)
52*5b9c547cSRui Paulo 		return NULL;
53*5b9c547cSRui Paulo 
54*5b9c547cSRui Paulo 	for (i = 0; i < mode->num_channels; i++) {
55*5b9c547cSRui Paulo 		struct hostapd_channel_data *ch = &mode->channels[i];
56*5b9c547cSRui Paulo 		if (ch->freq == freq) {
57*5b9c547cSRui Paulo 			if (chan)
58*5b9c547cSRui Paulo 				*chan = ch->chan;
59*5b9c547cSRui Paulo 			return ch;
60*5b9c547cSRui Paulo 		}
61*5b9c547cSRui Paulo 	}
62*5b9c547cSRui Paulo 
63*5b9c547cSRui Paulo 	return NULL;
64*5b9c547cSRui Paulo }
65*5b9c547cSRui Paulo 
66*5b9c547cSRui Paulo 
67*5b9c547cSRui Paulo int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
68*5b9c547cSRui Paulo {
69*5b9c547cSRui Paulo 	int freq;
70*5b9c547cSRui Paulo 
71*5b9c547cSRui Paulo 	hw_get_channel_chan(mode, chan, &freq);
72*5b9c547cSRui Paulo 
73*5b9c547cSRui Paulo 	return freq;
74*5b9c547cSRui Paulo }
75*5b9c547cSRui Paulo 
76*5b9c547cSRui Paulo 
77*5b9c547cSRui Paulo int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
78*5b9c547cSRui Paulo {
79*5b9c547cSRui Paulo 	int chan;
80*5b9c547cSRui Paulo 
81*5b9c547cSRui Paulo 	hw_get_channel_freq(mode, freq, &chan);
82*5b9c547cSRui Paulo 
83*5b9c547cSRui Paulo 	return chan;
84*5b9c547cSRui Paulo }
85*5b9c547cSRui Paulo 
86*5b9c547cSRui Paulo 
87*5b9c547cSRui Paulo int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
88*5b9c547cSRui Paulo 			      int sec_chan)
89*5b9c547cSRui Paulo {
90*5b9c547cSRui Paulo 	int ok, j, first;
91*5b9c547cSRui Paulo 	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
92*5b9c547cSRui Paulo 			  184, 192 };
93*5b9c547cSRui Paulo 	size_t k;
94*5b9c547cSRui Paulo 
95*5b9c547cSRui Paulo 	if (pri_chan == sec_chan || !sec_chan)
96*5b9c547cSRui Paulo 		return 1; /* HT40 not used */
97*5b9c547cSRui Paulo 
98*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
99*5b9c547cSRui Paulo 		   "HT40: control channel: %d  secondary channel: %d",
100*5b9c547cSRui Paulo 		   pri_chan, sec_chan);
101*5b9c547cSRui Paulo 
102*5b9c547cSRui Paulo 	/* Verify that HT40 secondary channel is an allowed 20 MHz
103*5b9c547cSRui Paulo 	 * channel */
104*5b9c547cSRui Paulo 	ok = 0;
105*5b9c547cSRui Paulo 	for (j = 0; j < mode->num_channels; j++) {
106*5b9c547cSRui Paulo 		struct hostapd_channel_data *chan = &mode->channels[j];
107*5b9c547cSRui Paulo 		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
108*5b9c547cSRui Paulo 		    chan->chan == sec_chan) {
109*5b9c547cSRui Paulo 			ok = 1;
110*5b9c547cSRui Paulo 			break;
111*5b9c547cSRui Paulo 		}
112*5b9c547cSRui Paulo 	}
113*5b9c547cSRui Paulo 	if (!ok) {
114*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
115*5b9c547cSRui Paulo 			   sec_chan);
116*5b9c547cSRui Paulo 		return 0;
117*5b9c547cSRui Paulo 	}
118*5b9c547cSRui Paulo 
119*5b9c547cSRui Paulo 	/*
120*5b9c547cSRui Paulo 	 * Verify that HT40 primary,secondary channel pair is allowed per
121*5b9c547cSRui Paulo 	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
122*5b9c547cSRui Paulo 	 * 2.4 GHz rules allow all cases where the secondary channel fits into
123*5b9c547cSRui Paulo 	 * the list of allowed channels (already checked above).
124*5b9c547cSRui Paulo 	 */
125*5b9c547cSRui Paulo 	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
126*5b9c547cSRui Paulo 		return 1;
127*5b9c547cSRui Paulo 
128*5b9c547cSRui Paulo 	first = pri_chan < sec_chan ? pri_chan : sec_chan;
129*5b9c547cSRui Paulo 
130*5b9c547cSRui Paulo 	ok = 0;
131*5b9c547cSRui Paulo 	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
132*5b9c547cSRui Paulo 		if (first == allowed[k]) {
133*5b9c547cSRui Paulo 			ok = 1;
134*5b9c547cSRui Paulo 			break;
135*5b9c547cSRui Paulo 		}
136*5b9c547cSRui Paulo 	}
137*5b9c547cSRui Paulo 	if (!ok) {
138*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
139*5b9c547cSRui Paulo 			   pri_chan, sec_chan);
140*5b9c547cSRui Paulo 		return 0;
141*5b9c547cSRui Paulo 	}
142*5b9c547cSRui Paulo 
143*5b9c547cSRui Paulo 	return 1;
144*5b9c547cSRui Paulo }
145*5b9c547cSRui Paulo 
146*5b9c547cSRui Paulo 
147*5b9c547cSRui Paulo void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
148*5b9c547cSRui Paulo {
149*5b9c547cSRui Paulo 	struct ieee80211_ht_operation *oper;
150*5b9c547cSRui Paulo 	struct ieee802_11_elems elems;
151*5b9c547cSRui Paulo 
152*5b9c547cSRui Paulo 	*pri_chan = *sec_chan = 0;
153*5b9c547cSRui Paulo 
154*5b9c547cSRui Paulo 	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
155*5b9c547cSRui Paulo 	if (elems.ht_operation &&
156*5b9c547cSRui Paulo 	    elems.ht_operation_len >= sizeof(*oper)) {
157*5b9c547cSRui Paulo 		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
158*5b9c547cSRui Paulo 		*pri_chan = oper->primary_chan;
159*5b9c547cSRui Paulo 		if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
160*5b9c547cSRui Paulo 			int sec = oper->ht_param &
161*5b9c547cSRui Paulo 				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
162*5b9c547cSRui Paulo 			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
163*5b9c547cSRui Paulo 				*sec_chan = *pri_chan + 4;
164*5b9c547cSRui Paulo 			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
165*5b9c547cSRui Paulo 				*sec_chan = *pri_chan - 4;
166*5b9c547cSRui Paulo 		}
167*5b9c547cSRui Paulo 	}
168*5b9c547cSRui Paulo }
169*5b9c547cSRui Paulo 
170*5b9c547cSRui Paulo 
171*5b9c547cSRui Paulo int check_40mhz_5g(struct hostapd_hw_modes *mode,
172*5b9c547cSRui Paulo 		   struct wpa_scan_results *scan_res, int pri_chan,
173*5b9c547cSRui Paulo 		   int sec_chan)
174*5b9c547cSRui Paulo {
175*5b9c547cSRui Paulo 	int pri_freq, sec_freq, pri_bss, sec_bss;
176*5b9c547cSRui Paulo 	int bss_pri_chan, bss_sec_chan;
177*5b9c547cSRui Paulo 	size_t i;
178*5b9c547cSRui Paulo 	int match;
179*5b9c547cSRui Paulo 
180*5b9c547cSRui Paulo 	if (!mode || !scan_res || !pri_chan || !sec_chan)
181*5b9c547cSRui Paulo 		return 0;
182*5b9c547cSRui Paulo 
183*5b9c547cSRui Paulo 	if (pri_chan == sec_chan)
184*5b9c547cSRui Paulo 		return 0;
185*5b9c547cSRui Paulo 
186*5b9c547cSRui Paulo 	pri_freq = hw_get_freq(mode, pri_chan);
187*5b9c547cSRui Paulo 	sec_freq = hw_get_freq(mode, sec_chan);
188*5b9c547cSRui Paulo 
189*5b9c547cSRui Paulo 	/*
190*5b9c547cSRui Paulo 	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
191*5b9c547cSRui Paulo 	 * channel, but not on selected PRI channel.
192*5b9c547cSRui Paulo 	 */
193*5b9c547cSRui Paulo 	pri_bss = sec_bss = 0;
194*5b9c547cSRui Paulo 	for (i = 0; i < scan_res->num; i++) {
195*5b9c547cSRui Paulo 		struct wpa_scan_res *bss = scan_res->res[i];
196*5b9c547cSRui Paulo 		if (bss->freq == pri_freq)
197*5b9c547cSRui Paulo 			pri_bss++;
198*5b9c547cSRui Paulo 		else if (bss->freq == sec_freq)
199*5b9c547cSRui Paulo 			sec_bss++;
200*5b9c547cSRui Paulo 	}
201*5b9c547cSRui Paulo 	if (sec_bss && !pri_bss) {
202*5b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
203*5b9c547cSRui Paulo 			   "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
204*5b9c547cSRui Paulo 		return 2;
205*5b9c547cSRui Paulo 	}
206*5b9c547cSRui Paulo 
207*5b9c547cSRui Paulo 	/*
208*5b9c547cSRui Paulo 	 * Match PRI/SEC channel with any existing HT40 BSS on the same
209*5b9c547cSRui Paulo 	 * channels that we are about to use (if already mixed order in
210*5b9c547cSRui Paulo 	 * existing BSSes, use own preference).
211*5b9c547cSRui Paulo 	 */
212*5b9c547cSRui Paulo 	match = 0;
213*5b9c547cSRui Paulo 	for (i = 0; i < scan_res->num; i++) {
214*5b9c547cSRui Paulo 		struct wpa_scan_res *bss = scan_res->res[i];
215*5b9c547cSRui Paulo 		get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
216*5b9c547cSRui Paulo 		if (pri_chan == bss_pri_chan &&
217*5b9c547cSRui Paulo 		    sec_chan == bss_sec_chan) {
218*5b9c547cSRui Paulo 			match = 1;
219*5b9c547cSRui Paulo 			break;
220*5b9c547cSRui Paulo 		}
221*5b9c547cSRui Paulo 	}
222*5b9c547cSRui Paulo 	if (!match) {
223*5b9c547cSRui Paulo 		for (i = 0; i < scan_res->num; i++) {
224*5b9c547cSRui Paulo 			struct wpa_scan_res *bss = scan_res->res[i];
225*5b9c547cSRui Paulo 			get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
226*5b9c547cSRui Paulo 			if (pri_chan == bss_sec_chan &&
227*5b9c547cSRui Paulo 			    sec_chan == bss_pri_chan) {
228*5b9c547cSRui Paulo 				wpa_printf(MSG_INFO, "Switch own primary and "
229*5b9c547cSRui Paulo 					   "secondary channel due to BSS "
230*5b9c547cSRui Paulo 					   "overlap with " MACSTR,
231*5b9c547cSRui Paulo 					   MAC2STR(bss->bssid));
232*5b9c547cSRui Paulo 				return 2;
233*5b9c547cSRui Paulo 			}
234*5b9c547cSRui Paulo 		}
235*5b9c547cSRui Paulo 	}
236*5b9c547cSRui Paulo 
237*5b9c547cSRui Paulo 	return 1;
238*5b9c547cSRui Paulo }
239*5b9c547cSRui Paulo 
240*5b9c547cSRui Paulo 
241*5b9c547cSRui Paulo int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end)
242*5b9c547cSRui Paulo {
243*5b9c547cSRui Paulo 	struct ieee802_11_elems elems;
244*5b9c547cSRui Paulo 	struct ieee80211_ht_operation *oper;
245*5b9c547cSRui Paulo 
246*5b9c547cSRui Paulo 	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
247*5b9c547cSRui Paulo 		return 0;
248*5b9c547cSRui Paulo 
249*5b9c547cSRui Paulo 	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
250*5b9c547cSRui Paulo 	if (!elems.ht_capabilities) {
251*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
252*5b9c547cSRui Paulo 			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
253*5b9c547cSRui Paulo 		return 1;
254*5b9c547cSRui Paulo 	}
255*5b9c547cSRui Paulo 
256*5b9c547cSRui Paulo 	if (elems.ht_operation &&
257*5b9c547cSRui Paulo 	    elems.ht_operation_len >= sizeof(*oper)) {
258*5b9c547cSRui Paulo 		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
259*5b9c547cSRui Paulo 		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
260*5b9c547cSRui Paulo 			return 0;
261*5b9c547cSRui Paulo 
262*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
263*5b9c547cSRui Paulo 			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
264*5b9c547cSRui Paulo 		return 1;
265*5b9c547cSRui Paulo 	}
266*5b9c547cSRui Paulo 	return 0;
267*5b9c547cSRui Paulo }
268*5b9c547cSRui Paulo 
269*5b9c547cSRui Paulo 
270*5b9c547cSRui Paulo int check_40mhz_2g4(struct hostapd_hw_modes *mode,
271*5b9c547cSRui Paulo 		    struct wpa_scan_results *scan_res, int pri_chan,
272*5b9c547cSRui Paulo 		    int sec_chan)
273*5b9c547cSRui Paulo {
274*5b9c547cSRui Paulo 	int pri_freq, sec_freq;
275*5b9c547cSRui Paulo 	int affected_start, affected_end;
276*5b9c547cSRui Paulo 	size_t i;
277*5b9c547cSRui Paulo 
278*5b9c547cSRui Paulo 	if (!mode || !scan_res || !pri_chan || !sec_chan)
279*5b9c547cSRui Paulo 		return 0;
280*5b9c547cSRui Paulo 
281*5b9c547cSRui Paulo 	if (pri_chan == sec_chan)
282*5b9c547cSRui Paulo 		return 0;
283*5b9c547cSRui Paulo 
284*5b9c547cSRui Paulo 	pri_freq = hw_get_freq(mode, pri_chan);
285*5b9c547cSRui Paulo 	sec_freq = hw_get_freq(mode, sec_chan);
286*5b9c547cSRui Paulo 
287*5b9c547cSRui Paulo 	affected_start = (pri_freq + sec_freq) / 2 - 25;
288*5b9c547cSRui Paulo 	affected_end = (pri_freq + sec_freq) / 2 + 25;
289*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
290*5b9c547cSRui Paulo 		   affected_start, affected_end);
291*5b9c547cSRui Paulo 	for (i = 0; i < scan_res->num; i++) {
292*5b9c547cSRui Paulo 		struct wpa_scan_res *bss = scan_res->res[i];
293*5b9c547cSRui Paulo 		int pri = bss->freq;
294*5b9c547cSRui Paulo 		int sec = pri;
295*5b9c547cSRui Paulo 		struct ieee802_11_elems elems;
296*5b9c547cSRui Paulo 
297*5b9c547cSRui Paulo 		/* Check for overlapping 20 MHz BSS */
298*5b9c547cSRui Paulo 		if (check_20mhz_bss(bss, pri_freq, affected_start,
299*5b9c547cSRui Paulo 				    affected_end)) {
300*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG,
301*5b9c547cSRui Paulo 				   "Overlapping 20 MHz BSS is found");
302*5b9c547cSRui Paulo 			return 0;
303*5b9c547cSRui Paulo 		}
304*5b9c547cSRui Paulo 
305*5b9c547cSRui Paulo 		get_pri_sec_chan(bss, &pri_chan, &sec_chan);
306*5b9c547cSRui Paulo 
307*5b9c547cSRui Paulo 		if (sec_chan) {
308*5b9c547cSRui Paulo 			if (sec_chan < pri_chan)
309*5b9c547cSRui Paulo 				sec = pri - 20;
310*5b9c547cSRui Paulo 			else
311*5b9c547cSRui Paulo 				sec = pri + 20;
312*5b9c547cSRui Paulo 		}
313*5b9c547cSRui Paulo 
314*5b9c547cSRui Paulo 		if ((pri < affected_start || pri > affected_end) &&
315*5b9c547cSRui Paulo 		    (sec < affected_start || sec > affected_end))
316*5b9c547cSRui Paulo 			continue; /* not within affected channel range */
317*5b9c547cSRui Paulo 
318*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
319*5b9c547cSRui Paulo 			   " freq=%d pri=%d sec=%d",
320*5b9c547cSRui Paulo 			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
321*5b9c547cSRui Paulo 
322*5b9c547cSRui Paulo 		if (sec_chan) {
323*5b9c547cSRui Paulo 			if (pri_freq != pri || sec_freq != sec) {
324*5b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG,
325*5b9c547cSRui Paulo 					   "40 MHz pri/sec mismatch with BSS "
326*5b9c547cSRui Paulo 					   MACSTR
327*5b9c547cSRui Paulo 					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
328*5b9c547cSRui Paulo 					   MAC2STR(bss->bssid),
329*5b9c547cSRui Paulo 					   pri, sec, pri_chan,
330*5b9c547cSRui Paulo 					   sec > pri ? '+' : '-',
331*5b9c547cSRui Paulo 					   pri_freq, sec_freq);
332*5b9c547cSRui Paulo 				return 0;
333*5b9c547cSRui Paulo 			}
334*5b9c547cSRui Paulo 		}
335*5b9c547cSRui Paulo 
336*5b9c547cSRui Paulo 		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
337*5b9c547cSRui Paulo 				       0);
338*5b9c547cSRui Paulo 		if (elems.ht_capabilities &&
339*5b9c547cSRui Paulo 		    elems.ht_capabilities_len >=
340*5b9c547cSRui Paulo 		    sizeof(struct ieee80211_ht_capabilities)) {
341*5b9c547cSRui Paulo 			struct ieee80211_ht_capabilities *ht_cap =
342*5b9c547cSRui Paulo 				(struct ieee80211_ht_capabilities *)
343*5b9c547cSRui Paulo 				elems.ht_capabilities;
344*5b9c547cSRui Paulo 
345*5b9c547cSRui Paulo 			if (le_to_host16(ht_cap->ht_capabilities_info) &
346*5b9c547cSRui Paulo 			    HT_CAP_INFO_40MHZ_INTOLERANT) {
347*5b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG,
348*5b9c547cSRui Paulo 					   "40 MHz Intolerant is set on channel %d in BSS "
349*5b9c547cSRui Paulo 					   MACSTR, pri, MAC2STR(bss->bssid));
350*5b9c547cSRui Paulo 				return 0;
351*5b9c547cSRui Paulo 			}
352*5b9c547cSRui Paulo 		}
353*5b9c547cSRui Paulo 	}
354*5b9c547cSRui Paulo 
355*5b9c547cSRui Paulo 	return 1;
356*5b9c547cSRui Paulo }
357*5b9c547cSRui Paulo 
358*5b9c547cSRui Paulo 
359*5b9c547cSRui Paulo int hostapd_set_freq_params(struct hostapd_freq_params *data,
360*5b9c547cSRui Paulo 			    enum hostapd_hw_mode mode,
361*5b9c547cSRui Paulo 			    int freq, int channel, int ht_enabled,
362*5b9c547cSRui Paulo 			    int vht_enabled, int sec_channel_offset,
363*5b9c547cSRui Paulo 			    int vht_oper_chwidth, int center_segment0,
364*5b9c547cSRui Paulo 			    int center_segment1, u32 vht_caps)
365*5b9c547cSRui Paulo {
366*5b9c547cSRui Paulo 	int tmp;
367*5b9c547cSRui Paulo 
368*5b9c547cSRui Paulo 	os_memset(data, 0, sizeof(*data));
369*5b9c547cSRui Paulo 	data->mode = mode;
370*5b9c547cSRui Paulo 	data->freq = freq;
371*5b9c547cSRui Paulo 	data->channel = channel;
372*5b9c547cSRui Paulo 	data->ht_enabled = ht_enabled;
373*5b9c547cSRui Paulo 	data->vht_enabled = vht_enabled;
374*5b9c547cSRui Paulo 	data->sec_channel_offset = sec_channel_offset;
375*5b9c547cSRui Paulo 	data->center_freq1 = freq + sec_channel_offset * 10;
376*5b9c547cSRui Paulo 	data->center_freq2 = 0;
377*5b9c547cSRui Paulo 	data->bandwidth = sec_channel_offset ? 40 : 20;
378*5b9c547cSRui Paulo 
379*5b9c547cSRui Paulo 	if (data->vht_enabled) switch (vht_oper_chwidth) {
380*5b9c547cSRui Paulo 	case VHT_CHANWIDTH_USE_HT:
381*5b9c547cSRui Paulo 		if (center_segment1)
382*5b9c547cSRui Paulo 			return -1;
383*5b9c547cSRui Paulo 		if (center_segment0 != 0 &&
384*5b9c547cSRui Paulo 		    5000 + center_segment0 * 5 != data->center_freq1 &&
385*5b9c547cSRui Paulo 		    2407 + center_segment0 * 5 != data->center_freq1)
386*5b9c547cSRui Paulo 			return -1;
387*5b9c547cSRui Paulo 		break;
388*5b9c547cSRui Paulo 	case VHT_CHANWIDTH_80P80MHZ:
389*5b9c547cSRui Paulo 		if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
390*5b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
391*5b9c547cSRui Paulo 				   "80+80 channel width is not supported!");
392*5b9c547cSRui Paulo 			return -1;
393*5b9c547cSRui Paulo 		}
394*5b9c547cSRui Paulo 		if (center_segment1 == center_segment0 + 4 ||
395*5b9c547cSRui Paulo 		    center_segment1 == center_segment0 - 4)
396*5b9c547cSRui Paulo 			return -1;
397*5b9c547cSRui Paulo 		data->center_freq2 = 5000 + center_segment1 * 5;
398*5b9c547cSRui Paulo 		/* fall through */
399*5b9c547cSRui Paulo 	case VHT_CHANWIDTH_80MHZ:
400*5b9c547cSRui Paulo 		data->bandwidth = 80;
401*5b9c547cSRui Paulo 		if (vht_oper_chwidth == 1 && center_segment1)
402*5b9c547cSRui Paulo 			return -1;
403*5b9c547cSRui Paulo 		if (vht_oper_chwidth == 3 && !center_segment1)
404*5b9c547cSRui Paulo 			return -1;
405*5b9c547cSRui Paulo 		if (!sec_channel_offset)
406*5b9c547cSRui Paulo 			return -1;
407*5b9c547cSRui Paulo 		/* primary 40 part must match the HT configuration */
408*5b9c547cSRui Paulo 		tmp = (30 + freq - 5000 - center_segment0 * 5) / 20;
409*5b9c547cSRui Paulo 		tmp /= 2;
410*5b9c547cSRui Paulo 		if (data->center_freq1 != 5000 +
411*5b9c547cSRui Paulo 		    center_segment0 * 5 - 20 + 40 * tmp)
412*5b9c547cSRui Paulo 			return -1;
413*5b9c547cSRui Paulo 		data->center_freq1 = 5000 + center_segment0 * 5;
414*5b9c547cSRui Paulo 		break;
415*5b9c547cSRui Paulo 	case VHT_CHANWIDTH_160MHZ:
416*5b9c547cSRui Paulo 		data->bandwidth = 160;
417*5b9c547cSRui Paulo 		if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
418*5b9c547cSRui Paulo 				  VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
419*5b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
420*5b9c547cSRui Paulo 				   "160MHZ channel width is not supported!");
421*5b9c547cSRui Paulo 			return -1;
422*5b9c547cSRui Paulo 		}
423*5b9c547cSRui Paulo 		if (center_segment1)
424*5b9c547cSRui Paulo 			return -1;
425*5b9c547cSRui Paulo 		if (!sec_channel_offset)
426*5b9c547cSRui Paulo 			return -1;
427*5b9c547cSRui Paulo 		/* primary 40 part must match the HT configuration */
428*5b9c547cSRui Paulo 		tmp = (70 + freq - 5000 - center_segment0 * 5) / 20;
429*5b9c547cSRui Paulo 		tmp /= 2;
430*5b9c547cSRui Paulo 		if (data->center_freq1 != 5000 +
431*5b9c547cSRui Paulo 		    center_segment0 * 5 - 60 + 40 * tmp)
432*5b9c547cSRui Paulo 			return -1;
433*5b9c547cSRui Paulo 		data->center_freq1 = 5000 + center_segment0 * 5;
434*5b9c547cSRui Paulo 		break;
435*5b9c547cSRui Paulo 	}
436*5b9c547cSRui Paulo 
437*5b9c547cSRui Paulo 	return 0;
438*5b9c547cSRui Paulo }
439