xref: /dflybsd-src/contrib/wpa_supplicant/src/common/hw_features_common.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
1*a1157835SDaniel Fojt /*
2*a1157835SDaniel Fojt  * Common hostapd/wpa_supplicant HW features
3*a1157835SDaniel Fojt  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4*a1157835SDaniel Fojt  * Copyright (c) 2015, Qualcomm Atheros, Inc.
5*a1157835SDaniel Fojt  *
6*a1157835SDaniel Fojt  * This software may be distributed under the terms of the BSD license.
7*a1157835SDaniel Fojt  * See README for more details.
8*a1157835SDaniel Fojt  */
9*a1157835SDaniel Fojt 
10*a1157835SDaniel Fojt #include "includes.h"
11*a1157835SDaniel Fojt 
12*a1157835SDaniel Fojt #include "common.h"
13*a1157835SDaniel Fojt #include "defs.h"
14*a1157835SDaniel Fojt #include "ieee802_11_defs.h"
15*a1157835SDaniel Fojt #include "ieee802_11_common.h"
16*a1157835SDaniel Fojt #include "hw_features_common.h"
17*a1157835SDaniel Fojt 
18*a1157835SDaniel Fojt 
hw_get_channel_chan(struct hostapd_hw_modes * mode,int chan,int * freq)19*a1157835SDaniel Fojt struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
20*a1157835SDaniel Fojt 						  int chan, int *freq)
21*a1157835SDaniel Fojt {
22*a1157835SDaniel Fojt 	int i;
23*a1157835SDaniel Fojt 
24*a1157835SDaniel Fojt 	if (freq)
25*a1157835SDaniel Fojt 		*freq = 0;
26*a1157835SDaniel Fojt 
27*a1157835SDaniel Fojt 	if (!mode)
28*a1157835SDaniel Fojt 		return NULL;
29*a1157835SDaniel Fojt 
30*a1157835SDaniel Fojt 	for (i = 0; i < mode->num_channels; i++) {
31*a1157835SDaniel Fojt 		struct hostapd_channel_data *ch = &mode->channels[i];
32*a1157835SDaniel Fojt 		if (ch->chan == chan) {
33*a1157835SDaniel Fojt 			if (freq)
34*a1157835SDaniel Fojt 				*freq = ch->freq;
35*a1157835SDaniel Fojt 			return ch;
36*a1157835SDaniel Fojt 		}
37*a1157835SDaniel Fojt 	}
38*a1157835SDaniel Fojt 
39*a1157835SDaniel Fojt 	return NULL;
40*a1157835SDaniel Fojt }
41*a1157835SDaniel Fojt 
42*a1157835SDaniel Fojt 
hw_get_channel_freq(struct hostapd_hw_modes * mode,int freq,int * chan)43*a1157835SDaniel Fojt struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
44*a1157835SDaniel Fojt 						  int freq, int *chan)
45*a1157835SDaniel Fojt {
46*a1157835SDaniel Fojt 	int i;
47*a1157835SDaniel Fojt 
48*a1157835SDaniel Fojt 	if (chan)
49*a1157835SDaniel Fojt 		*chan = 0;
50*a1157835SDaniel Fojt 
51*a1157835SDaniel Fojt 	if (!mode)
52*a1157835SDaniel Fojt 		return NULL;
53*a1157835SDaniel Fojt 
54*a1157835SDaniel Fojt 	for (i = 0; i < mode->num_channels; i++) {
55*a1157835SDaniel Fojt 		struct hostapd_channel_data *ch = &mode->channels[i];
56*a1157835SDaniel Fojt 		if (ch->freq == freq) {
57*a1157835SDaniel Fojt 			if (chan)
58*a1157835SDaniel Fojt 				*chan = ch->chan;
59*a1157835SDaniel Fojt 			return ch;
60*a1157835SDaniel Fojt 		}
61*a1157835SDaniel Fojt 	}
62*a1157835SDaniel Fojt 
63*a1157835SDaniel Fojt 	return NULL;
64*a1157835SDaniel Fojt }
65*a1157835SDaniel Fojt 
66*a1157835SDaniel Fojt 
hw_get_freq(struct hostapd_hw_modes * mode,int chan)67*a1157835SDaniel Fojt int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
68*a1157835SDaniel Fojt {
69*a1157835SDaniel Fojt 	int freq;
70*a1157835SDaniel Fojt 
71*a1157835SDaniel Fojt 	hw_get_channel_chan(mode, chan, &freq);
72*a1157835SDaniel Fojt 
73*a1157835SDaniel Fojt 	return freq;
74*a1157835SDaniel Fojt }
75*a1157835SDaniel Fojt 
76*a1157835SDaniel Fojt 
hw_get_chan(struct hostapd_hw_modes * mode,int freq)77*a1157835SDaniel Fojt int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
78*a1157835SDaniel Fojt {
79*a1157835SDaniel Fojt 	int chan;
80*a1157835SDaniel Fojt 
81*a1157835SDaniel Fojt 	hw_get_channel_freq(mode, freq, &chan);
82*a1157835SDaniel Fojt 
83*a1157835SDaniel Fojt 	return chan;
84*a1157835SDaniel Fojt }
85*a1157835SDaniel Fojt 
86*a1157835SDaniel Fojt 
allowed_ht40_channel_pair(struct hostapd_hw_modes * mode,int pri_chan,int sec_chan)87*a1157835SDaniel Fojt int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
88*a1157835SDaniel Fojt 			      int sec_chan)
89*a1157835SDaniel Fojt {
90*a1157835SDaniel Fojt 	int ok, first;
91*a1157835SDaniel Fojt 	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
92*a1157835SDaniel Fojt 			  149, 157, 165, 184, 192 };
93*a1157835SDaniel Fojt 	size_t k;
94*a1157835SDaniel Fojt 	struct hostapd_channel_data *p_chan, *s_chan;
95*a1157835SDaniel Fojt 	const int ht40_plus = pri_chan < sec_chan;
96*a1157835SDaniel Fojt 
97*a1157835SDaniel Fojt 	p_chan = hw_get_channel_chan(mode, pri_chan, NULL);
98*a1157835SDaniel Fojt 	if (!p_chan)
99*a1157835SDaniel Fojt 		return 0;
100*a1157835SDaniel Fojt 
101*a1157835SDaniel Fojt 	if (pri_chan == sec_chan || !sec_chan) {
102*a1157835SDaniel Fojt 		if (chan_pri_allowed(p_chan))
103*a1157835SDaniel Fojt 			return 1; /* HT40 not used */
104*a1157835SDaniel Fojt 
105*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary",
106*a1157835SDaniel Fojt 			   pri_chan);
107*a1157835SDaniel Fojt 		return 0;
108*a1157835SDaniel Fojt 	}
109*a1157835SDaniel Fojt 
110*a1157835SDaniel Fojt 	s_chan = hw_get_channel_chan(mode, sec_chan, NULL);
111*a1157835SDaniel Fojt 	if (!s_chan)
112*a1157835SDaniel Fojt 		return 0;
113*a1157835SDaniel Fojt 
114*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG,
115*a1157835SDaniel Fojt 		   "HT40: control channel: %d  secondary channel: %d",
116*a1157835SDaniel Fojt 		   pri_chan, sec_chan);
117*a1157835SDaniel Fojt 
118*a1157835SDaniel Fojt 	/* Verify that HT40 secondary channel is an allowed 20 MHz
119*a1157835SDaniel Fojt 	 * channel */
120*a1157835SDaniel Fojt 	if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) ||
121*a1157835SDaniel Fojt 	    (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) ||
122*a1157835SDaniel Fojt 	    (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) {
123*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
124*a1157835SDaniel Fojt 			   sec_chan);
125*a1157835SDaniel Fojt 		return 0;
126*a1157835SDaniel Fojt 	}
127*a1157835SDaniel Fojt 
128*a1157835SDaniel Fojt 	/*
129*a1157835SDaniel Fojt 	 * Verify that HT40 primary,secondary channel pair is allowed per
130*a1157835SDaniel Fojt 	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
131*a1157835SDaniel Fojt 	 * 2.4 GHz rules allow all cases where the secondary channel fits into
132*a1157835SDaniel Fojt 	 * the list of allowed channels (already checked above).
133*a1157835SDaniel Fojt 	 */
134*a1157835SDaniel Fojt 	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
135*a1157835SDaniel Fojt 		return 1;
136*a1157835SDaniel Fojt 
137*a1157835SDaniel Fojt 	first = pri_chan < sec_chan ? pri_chan : sec_chan;
138*a1157835SDaniel Fojt 
139*a1157835SDaniel Fojt 	ok = 0;
140*a1157835SDaniel Fojt 	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
141*a1157835SDaniel Fojt 		if (first == allowed[k]) {
142*a1157835SDaniel Fojt 			ok = 1;
143*a1157835SDaniel Fojt 			break;
144*a1157835SDaniel Fojt 		}
145*a1157835SDaniel Fojt 	}
146*a1157835SDaniel Fojt 	if (!ok) {
147*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
148*a1157835SDaniel Fojt 			   pri_chan, sec_chan);
149*a1157835SDaniel Fojt 		return 0;
150*a1157835SDaniel Fojt 	}
151*a1157835SDaniel Fojt 
152*a1157835SDaniel Fojt 	return 1;
153*a1157835SDaniel Fojt }
154*a1157835SDaniel Fojt 
155*a1157835SDaniel Fojt 
get_pri_sec_chan(struct wpa_scan_res * bss,int * pri_chan,int * sec_chan)156*a1157835SDaniel Fojt void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
157*a1157835SDaniel Fojt {
158*a1157835SDaniel Fojt 	struct ieee80211_ht_operation *oper;
159*a1157835SDaniel Fojt 	struct ieee802_11_elems elems;
160*a1157835SDaniel Fojt 
161*a1157835SDaniel Fojt 	*pri_chan = *sec_chan = 0;
162*a1157835SDaniel Fojt 
163*a1157835SDaniel Fojt 	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
164*a1157835SDaniel Fojt 	if (elems.ht_operation) {
165*a1157835SDaniel Fojt 		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
166*a1157835SDaniel Fojt 		*pri_chan = oper->primary_chan;
167*a1157835SDaniel Fojt 		if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
168*a1157835SDaniel Fojt 			int sec = oper->ht_param &
169*a1157835SDaniel Fojt 				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
170*a1157835SDaniel Fojt 			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
171*a1157835SDaniel Fojt 				*sec_chan = *pri_chan + 4;
172*a1157835SDaniel Fojt 			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
173*a1157835SDaniel Fojt 				*sec_chan = *pri_chan - 4;
174*a1157835SDaniel Fojt 		}
175*a1157835SDaniel Fojt 	}
176*a1157835SDaniel Fojt }
177*a1157835SDaniel Fojt 
178*a1157835SDaniel Fojt 
check_40mhz_5g(struct hostapd_hw_modes * mode,struct wpa_scan_results * scan_res,int pri_chan,int sec_chan)179*a1157835SDaniel Fojt int check_40mhz_5g(struct hostapd_hw_modes *mode,
180*a1157835SDaniel Fojt 		   struct wpa_scan_results *scan_res, int pri_chan,
181*a1157835SDaniel Fojt 		   int sec_chan)
182*a1157835SDaniel Fojt {
183*a1157835SDaniel Fojt 	int pri_freq, sec_freq, pri_bss, sec_bss;
184*a1157835SDaniel Fojt 	int bss_pri_chan, bss_sec_chan;
185*a1157835SDaniel Fojt 	size_t i;
186*a1157835SDaniel Fojt 	int match;
187*a1157835SDaniel Fojt 
188*a1157835SDaniel Fojt 	if (!mode || !scan_res || !pri_chan || !sec_chan ||
189*a1157835SDaniel Fojt 	    pri_chan == sec_chan)
190*a1157835SDaniel Fojt 		return 0;
191*a1157835SDaniel Fojt 
192*a1157835SDaniel Fojt 	pri_freq = hw_get_freq(mode, pri_chan);
193*a1157835SDaniel Fojt 	sec_freq = hw_get_freq(mode, sec_chan);
194*a1157835SDaniel Fojt 
195*a1157835SDaniel Fojt 	/*
196*a1157835SDaniel Fojt 	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
197*a1157835SDaniel Fojt 	 * channel, but not on selected PRI channel.
198*a1157835SDaniel Fojt 	 */
199*a1157835SDaniel Fojt 	pri_bss = sec_bss = 0;
200*a1157835SDaniel Fojt 	for (i = 0; i < scan_res->num; i++) {
201*a1157835SDaniel Fojt 		struct wpa_scan_res *bss = scan_res->res[i];
202*a1157835SDaniel Fojt 		if (bss->freq == pri_freq)
203*a1157835SDaniel Fojt 			pri_bss++;
204*a1157835SDaniel Fojt 		else if (bss->freq == sec_freq)
205*a1157835SDaniel Fojt 			sec_bss++;
206*a1157835SDaniel Fojt 	}
207*a1157835SDaniel Fojt 	if (sec_bss && !pri_bss) {
208*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO,
209*a1157835SDaniel Fojt 			   "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
210*a1157835SDaniel Fojt 		return 2;
211*a1157835SDaniel Fojt 	}
212*a1157835SDaniel Fojt 
213*a1157835SDaniel Fojt 	/*
214*a1157835SDaniel Fojt 	 * Match PRI/SEC channel with any existing HT40 BSS on the same
215*a1157835SDaniel Fojt 	 * channels that we are about to use (if already mixed order in
216*a1157835SDaniel Fojt 	 * existing BSSes, use own preference).
217*a1157835SDaniel Fojt 	 */
218*a1157835SDaniel Fojt 	match = 0;
219*a1157835SDaniel Fojt 	for (i = 0; i < scan_res->num; i++) {
220*a1157835SDaniel Fojt 		struct wpa_scan_res *bss = scan_res->res[i];
221*a1157835SDaniel Fojt 		get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
222*a1157835SDaniel Fojt 		if (pri_chan == bss_pri_chan &&
223*a1157835SDaniel Fojt 		    sec_chan == bss_sec_chan) {
224*a1157835SDaniel Fojt 			match = 1;
225*a1157835SDaniel Fojt 			break;
226*a1157835SDaniel Fojt 		}
227*a1157835SDaniel Fojt 	}
228*a1157835SDaniel Fojt 	if (!match) {
229*a1157835SDaniel Fojt 		for (i = 0; i < scan_res->num; i++) {
230*a1157835SDaniel Fojt 			struct wpa_scan_res *bss = scan_res->res[i];
231*a1157835SDaniel Fojt 			get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
232*a1157835SDaniel Fojt 			if (pri_chan == bss_sec_chan &&
233*a1157835SDaniel Fojt 			    sec_chan == bss_pri_chan) {
234*a1157835SDaniel Fojt 				wpa_printf(MSG_INFO, "Switch own primary and "
235*a1157835SDaniel Fojt 					   "secondary channel due to BSS "
236*a1157835SDaniel Fojt 					   "overlap with " MACSTR,
237*a1157835SDaniel Fojt 					   MAC2STR(bss->bssid));
238*a1157835SDaniel Fojt 				return 2;
239*a1157835SDaniel Fojt 			}
240*a1157835SDaniel Fojt 		}
241*a1157835SDaniel Fojt 	}
242*a1157835SDaniel Fojt 
243*a1157835SDaniel Fojt 	return 1;
244*a1157835SDaniel Fojt }
245*a1157835SDaniel Fojt 
246*a1157835SDaniel Fojt 
check_20mhz_bss(struct wpa_scan_res * bss,int pri_freq,int start,int end)247*a1157835SDaniel Fojt static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start,
248*a1157835SDaniel Fojt 			   int end)
249*a1157835SDaniel Fojt {
250*a1157835SDaniel Fojt 	struct ieee802_11_elems elems;
251*a1157835SDaniel Fojt 	struct ieee80211_ht_operation *oper;
252*a1157835SDaniel Fojt 
253*a1157835SDaniel Fojt 	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
254*a1157835SDaniel Fojt 		return 0;
255*a1157835SDaniel Fojt 
256*a1157835SDaniel Fojt 	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
257*a1157835SDaniel Fojt 	if (!elems.ht_capabilities) {
258*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
259*a1157835SDaniel Fojt 			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
260*a1157835SDaniel Fojt 		return 1;
261*a1157835SDaniel Fojt 	}
262*a1157835SDaniel Fojt 
263*a1157835SDaniel Fojt 	if (elems.ht_operation) {
264*a1157835SDaniel Fojt 		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
265*a1157835SDaniel Fojt 		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
266*a1157835SDaniel Fojt 			return 0;
267*a1157835SDaniel Fojt 
268*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
269*a1157835SDaniel Fojt 			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
270*a1157835SDaniel Fojt 		return 1;
271*a1157835SDaniel Fojt 	}
272*a1157835SDaniel Fojt 	return 0;
273*a1157835SDaniel Fojt }
274*a1157835SDaniel Fojt 
275*a1157835SDaniel Fojt 
check_40mhz_2g4(struct hostapd_hw_modes * mode,struct wpa_scan_results * scan_res,int pri_chan,int sec_chan)276*a1157835SDaniel Fojt int check_40mhz_2g4(struct hostapd_hw_modes *mode,
277*a1157835SDaniel Fojt 		    struct wpa_scan_results *scan_res, int pri_chan,
278*a1157835SDaniel Fojt 		    int sec_chan)
279*a1157835SDaniel Fojt {
280*a1157835SDaniel Fojt 	int pri_freq, sec_freq;
281*a1157835SDaniel Fojt 	int affected_start, affected_end;
282*a1157835SDaniel Fojt 	size_t i;
283*a1157835SDaniel Fojt 
284*a1157835SDaniel Fojt 	if (!mode || !scan_res || !pri_chan || !sec_chan ||
285*a1157835SDaniel Fojt 	    pri_chan == sec_chan)
286*a1157835SDaniel Fojt 		return 0;
287*a1157835SDaniel Fojt 
288*a1157835SDaniel Fojt 	pri_freq = hw_get_freq(mode, pri_chan);
289*a1157835SDaniel Fojt 	sec_freq = hw_get_freq(mode, sec_chan);
290*a1157835SDaniel Fojt 
291*a1157835SDaniel Fojt 	affected_start = (pri_freq + sec_freq) / 2 - 25;
292*a1157835SDaniel Fojt 	affected_end = (pri_freq + sec_freq) / 2 + 25;
293*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
294*a1157835SDaniel Fojt 		   affected_start, affected_end);
295*a1157835SDaniel Fojt 	for (i = 0; i < scan_res->num; i++) {
296*a1157835SDaniel Fojt 		struct wpa_scan_res *bss = scan_res->res[i];
297*a1157835SDaniel Fojt 		int pri = bss->freq;
298*a1157835SDaniel Fojt 		int sec = pri;
299*a1157835SDaniel Fojt 		struct ieee802_11_elems elems;
300*a1157835SDaniel Fojt 
301*a1157835SDaniel Fojt 		/* Check for overlapping 20 MHz BSS */
302*a1157835SDaniel Fojt 		if (check_20mhz_bss(bss, pri_freq, affected_start,
303*a1157835SDaniel Fojt 				    affected_end)) {
304*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
305*a1157835SDaniel Fojt 				   "Overlapping 20 MHz BSS is found");
306*a1157835SDaniel Fojt 			return 0;
307*a1157835SDaniel Fojt 		}
308*a1157835SDaniel Fojt 
309*a1157835SDaniel Fojt 		get_pri_sec_chan(bss, &pri_chan, &sec_chan);
310*a1157835SDaniel Fojt 
311*a1157835SDaniel Fojt 		if (sec_chan) {
312*a1157835SDaniel Fojt 			if (sec_chan < pri_chan)
313*a1157835SDaniel Fojt 				sec = pri - 20;
314*a1157835SDaniel Fojt 			else
315*a1157835SDaniel Fojt 				sec = pri + 20;
316*a1157835SDaniel Fojt 		}
317*a1157835SDaniel Fojt 
318*a1157835SDaniel Fojt 		if ((pri < affected_start || pri > affected_end) &&
319*a1157835SDaniel Fojt 		    (sec < affected_start || sec > affected_end))
320*a1157835SDaniel Fojt 			continue; /* not within affected channel range */
321*a1157835SDaniel Fojt 
322*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
323*a1157835SDaniel Fojt 			   " freq=%d pri=%d sec=%d",
324*a1157835SDaniel Fojt 			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
325*a1157835SDaniel Fojt 
326*a1157835SDaniel Fojt 		if (sec_chan) {
327*a1157835SDaniel Fojt 			if (pri_freq != pri || sec_freq != sec) {
328*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG,
329*a1157835SDaniel Fojt 					   "40 MHz pri/sec mismatch with BSS "
330*a1157835SDaniel Fojt 					   MACSTR
331*a1157835SDaniel Fojt 					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
332*a1157835SDaniel Fojt 					   MAC2STR(bss->bssid),
333*a1157835SDaniel Fojt 					   pri, sec, pri_chan,
334*a1157835SDaniel Fojt 					   sec > pri ? '+' : '-',
335*a1157835SDaniel Fojt 					   pri_freq, sec_freq);
336*a1157835SDaniel Fojt 				return 0;
337*a1157835SDaniel Fojt 			}
338*a1157835SDaniel Fojt 		}
339*a1157835SDaniel Fojt 
340*a1157835SDaniel Fojt 		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
341*a1157835SDaniel Fojt 				       0);
342*a1157835SDaniel Fojt 		if (elems.ht_capabilities) {
343*a1157835SDaniel Fojt 			struct ieee80211_ht_capabilities *ht_cap =
344*a1157835SDaniel Fojt 				(struct ieee80211_ht_capabilities *)
345*a1157835SDaniel Fojt 				elems.ht_capabilities;
346*a1157835SDaniel Fojt 
347*a1157835SDaniel Fojt 			if (le_to_host16(ht_cap->ht_capabilities_info) &
348*a1157835SDaniel Fojt 			    HT_CAP_INFO_40MHZ_INTOLERANT) {
349*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG,
350*a1157835SDaniel Fojt 					   "40 MHz Intolerant is set on channel %d in BSS "
351*a1157835SDaniel Fojt 					   MACSTR, pri, MAC2STR(bss->bssid));
352*a1157835SDaniel Fojt 				return 0;
353*a1157835SDaniel Fojt 			}
354*a1157835SDaniel Fojt 		}
355*a1157835SDaniel Fojt 	}
356*a1157835SDaniel Fojt 
357*a1157835SDaniel Fojt 	return 1;
358*a1157835SDaniel Fojt }
359*a1157835SDaniel Fojt 
360*a1157835SDaniel Fojt 
hostapd_set_freq_params(struct hostapd_freq_params * data,enum hostapd_hw_mode mode,int freq,int channel,int ht_enabled,int vht_enabled,int he_enabled,int sec_channel_offset,int oper_chwidth,int center_segment0,int center_segment1,u32 vht_caps,struct he_capabilities * he_cap)361*a1157835SDaniel Fojt int hostapd_set_freq_params(struct hostapd_freq_params *data,
362*a1157835SDaniel Fojt 			    enum hostapd_hw_mode mode,
363*a1157835SDaniel Fojt 			    int freq, int channel, int ht_enabled,
364*a1157835SDaniel Fojt 			    int vht_enabled, int he_enabled,
365*a1157835SDaniel Fojt 			    int sec_channel_offset,
366*a1157835SDaniel Fojt 			    int oper_chwidth, int center_segment0,
367*a1157835SDaniel Fojt 			    int center_segment1, u32 vht_caps,
368*a1157835SDaniel Fojt 			    struct he_capabilities *he_cap)
369*a1157835SDaniel Fojt {
370*a1157835SDaniel Fojt 	if (!he_cap)
371*a1157835SDaniel Fojt 		he_enabled = 0;
372*a1157835SDaniel Fojt 	os_memset(data, 0, sizeof(*data));
373*a1157835SDaniel Fojt 	data->mode = mode;
374*a1157835SDaniel Fojt 	data->freq = freq;
375*a1157835SDaniel Fojt 	data->channel = channel;
376*a1157835SDaniel Fojt 	data->ht_enabled = ht_enabled;
377*a1157835SDaniel Fojt 	data->vht_enabled = vht_enabled;
378*a1157835SDaniel Fojt 	data->he_enabled = he_enabled;
379*a1157835SDaniel Fojt 	data->sec_channel_offset = sec_channel_offset;
380*a1157835SDaniel Fojt 	data->center_freq1 = freq + sec_channel_offset * 10;
381*a1157835SDaniel Fojt 	data->center_freq2 = 0;
382*a1157835SDaniel Fojt 	data->bandwidth = sec_channel_offset ? 40 : 20;
383*a1157835SDaniel Fojt 
384*a1157835SDaniel Fojt 	if (data->vht_enabled) switch (oper_chwidth) {
385*a1157835SDaniel Fojt 	case CHANWIDTH_USE_HT:
386*a1157835SDaniel Fojt 		if (center_segment1 ||
387*a1157835SDaniel Fojt 		    (center_segment0 != 0 &&
388*a1157835SDaniel Fojt 		     5000 + center_segment0 * 5 != data->center_freq1 &&
389*a1157835SDaniel Fojt 		     2407 + center_segment0 * 5 != data->center_freq1))
390*a1157835SDaniel Fojt 			return -1;
391*a1157835SDaniel Fojt 		break;
392*a1157835SDaniel Fojt 	case CHANWIDTH_80P80MHZ:
393*a1157835SDaniel Fojt 		if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
394*a1157835SDaniel Fojt 			wpa_printf(MSG_ERROR,
395*a1157835SDaniel Fojt 				   "80+80 channel width is not supported!");
396*a1157835SDaniel Fojt 			return -1;
397*a1157835SDaniel Fojt 		}
398*a1157835SDaniel Fojt 		if (center_segment1 == center_segment0 + 4 ||
399*a1157835SDaniel Fojt 		    center_segment1 == center_segment0 - 4)
400*a1157835SDaniel Fojt 			return -1;
401*a1157835SDaniel Fojt 		data->center_freq2 = 5000 + center_segment1 * 5;
402*a1157835SDaniel Fojt 		/* fall through */
403*a1157835SDaniel Fojt 	case CHANWIDTH_80MHZ:
404*a1157835SDaniel Fojt 		data->bandwidth = 80;
405*a1157835SDaniel Fojt 		if ((oper_chwidth == CHANWIDTH_80MHZ &&
406*a1157835SDaniel Fojt 		     center_segment1) ||
407*a1157835SDaniel Fojt 		    (oper_chwidth == CHANWIDTH_80P80MHZ &&
408*a1157835SDaniel Fojt 		     !center_segment1) ||
409*a1157835SDaniel Fojt 		    !sec_channel_offset)
410*a1157835SDaniel Fojt 			return -1;
411*a1157835SDaniel Fojt 		if (!center_segment0) {
412*a1157835SDaniel Fojt 			if (channel <= 48)
413*a1157835SDaniel Fojt 				center_segment0 = 42;
414*a1157835SDaniel Fojt 			else if (channel <= 64)
415*a1157835SDaniel Fojt 				center_segment0 = 58;
416*a1157835SDaniel Fojt 			else if (channel <= 112)
417*a1157835SDaniel Fojt 				center_segment0 = 106;
418*a1157835SDaniel Fojt 			else if (channel <= 128)
419*a1157835SDaniel Fojt 				center_segment0 = 122;
420*a1157835SDaniel Fojt 			else if (channel <= 144)
421*a1157835SDaniel Fojt 				center_segment0 = 138;
422*a1157835SDaniel Fojt 			else if (channel <= 161)
423*a1157835SDaniel Fojt 				center_segment0 = 155;
424*a1157835SDaniel Fojt 			data->center_freq1 = 5000 + center_segment0 * 5;
425*a1157835SDaniel Fojt 		} else {
426*a1157835SDaniel Fojt 			/*
427*a1157835SDaniel Fojt 			 * Note: HT/VHT config and params are coupled. Check if
428*a1157835SDaniel Fojt 			 * HT40 channel band is in VHT80 Pri channel band
429*a1157835SDaniel Fojt 			 * configuration.
430*a1157835SDaniel Fojt 			 */
431*a1157835SDaniel Fojt 			if (center_segment0 == channel + 6 ||
432*a1157835SDaniel Fojt 			    center_segment0 == channel + 2 ||
433*a1157835SDaniel Fojt 			    center_segment0 == channel - 2 ||
434*a1157835SDaniel Fojt 			    center_segment0 == channel - 6)
435*a1157835SDaniel Fojt 				data->center_freq1 = 5000 + center_segment0 * 5;
436*a1157835SDaniel Fojt 			else
437*a1157835SDaniel Fojt 				return -1;
438*a1157835SDaniel Fojt 		}
439*a1157835SDaniel Fojt 		break;
440*a1157835SDaniel Fojt 	case CHANWIDTH_160MHZ:
441*a1157835SDaniel Fojt 		data->bandwidth = 160;
442*a1157835SDaniel Fojt 		if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
443*a1157835SDaniel Fojt 				  VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
444*a1157835SDaniel Fojt 			wpa_printf(MSG_ERROR,
445*a1157835SDaniel Fojt 				   "160MHZ channel width is not supported!");
446*a1157835SDaniel Fojt 			return -1;
447*a1157835SDaniel Fojt 		}
448*a1157835SDaniel Fojt 		if (center_segment1)
449*a1157835SDaniel Fojt 			return -1;
450*a1157835SDaniel Fojt 		if (!sec_channel_offset)
451*a1157835SDaniel Fojt 			return -1;
452*a1157835SDaniel Fojt 		/*
453*a1157835SDaniel Fojt 		 * Note: HT/VHT config and params are coupled. Check if
454*a1157835SDaniel Fojt 		 * HT40 channel band is in VHT160 channel band configuration.
455*a1157835SDaniel Fojt 		 */
456*a1157835SDaniel Fojt 		if (center_segment0 == channel + 14 ||
457*a1157835SDaniel Fojt 		    center_segment0 == channel + 10 ||
458*a1157835SDaniel Fojt 		    center_segment0 == channel + 6 ||
459*a1157835SDaniel Fojt 		    center_segment0 == channel + 2 ||
460*a1157835SDaniel Fojt 		    center_segment0 == channel - 2 ||
461*a1157835SDaniel Fojt 		    center_segment0 == channel - 6 ||
462*a1157835SDaniel Fojt 		    center_segment0 == channel - 10 ||
463*a1157835SDaniel Fojt 		    center_segment0 == channel - 14)
464*a1157835SDaniel Fojt 			data->center_freq1 = 5000 + center_segment0 * 5;
465*a1157835SDaniel Fojt 		else
466*a1157835SDaniel Fojt 			return -1;
467*a1157835SDaniel Fojt 		break;
468*a1157835SDaniel Fojt 	}
469*a1157835SDaniel Fojt 
470*a1157835SDaniel Fojt 	return 0;
471*a1157835SDaniel Fojt }
472*a1157835SDaniel Fojt 
473*a1157835SDaniel Fojt 
set_disable_ht40(struct ieee80211_ht_capabilities * htcaps,int disabled)474*a1157835SDaniel Fojt void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
475*a1157835SDaniel Fojt 		      int disabled)
476*a1157835SDaniel Fojt {
477*a1157835SDaniel Fojt 	/* Masking these out disables HT40 */
478*a1157835SDaniel Fojt 	le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
479*a1157835SDaniel Fojt 				HT_CAP_INFO_SHORT_GI40MHZ);
480*a1157835SDaniel Fojt 
481*a1157835SDaniel Fojt 	if (disabled)
482*a1157835SDaniel Fojt 		htcaps->ht_capabilities_info &= ~msk;
483*a1157835SDaniel Fojt 	else
484*a1157835SDaniel Fojt 		htcaps->ht_capabilities_info |= msk;
485*a1157835SDaniel Fojt }
486*a1157835SDaniel Fojt 
487*a1157835SDaniel Fojt 
488*a1157835SDaniel Fojt #ifdef CONFIG_IEEE80211AC
489*a1157835SDaniel Fojt 
_ieee80211ac_cap_check(u32 hw,u32 conf,u32 cap,const char * name)490*a1157835SDaniel Fojt static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap,
491*a1157835SDaniel Fojt 				  const char *name)
492*a1157835SDaniel Fojt {
493*a1157835SDaniel Fojt 	u32 req_cap = conf & cap;
494*a1157835SDaniel Fojt 
495*a1157835SDaniel Fojt 	/*
496*a1157835SDaniel Fojt 	 * Make sure we support all requested capabilities.
497*a1157835SDaniel Fojt 	 * NOTE: We assume that 'cap' represents a capability mask,
498*a1157835SDaniel Fojt 	 * not a discrete value.
499*a1157835SDaniel Fojt 	 */
500*a1157835SDaniel Fojt 	if ((hw & req_cap) != req_cap) {
501*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR,
502*a1157835SDaniel Fojt 			   "Driver does not support configured VHT capability [%s]",
503*a1157835SDaniel Fojt 			   name);
504*a1157835SDaniel Fojt 		return 0;
505*a1157835SDaniel Fojt 	}
506*a1157835SDaniel Fojt 	return 1;
507*a1157835SDaniel Fojt }
508*a1157835SDaniel Fojt 
509*a1157835SDaniel Fojt 
ieee80211ac_cap_check_max(u32 hw,u32 conf,u32 mask,unsigned int shift,const char * name)510*a1157835SDaniel Fojt static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
511*a1157835SDaniel Fojt 				     unsigned int shift,
512*a1157835SDaniel Fojt 				     const char *name)
513*a1157835SDaniel Fojt {
514*a1157835SDaniel Fojt 	u32 hw_max = hw & mask;
515*a1157835SDaniel Fojt 	u32 conf_val = conf & mask;
516*a1157835SDaniel Fojt 
517*a1157835SDaniel Fojt 	if (conf_val > hw_max) {
518*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR,
519*a1157835SDaniel Fojt 			   "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
520*a1157835SDaniel Fojt 			   name, conf_val >> shift, hw_max >> shift);
521*a1157835SDaniel Fojt 		return 0;
522*a1157835SDaniel Fojt 	}
523*a1157835SDaniel Fojt 	return 1;
524*a1157835SDaniel Fojt }
525*a1157835SDaniel Fojt 
526*a1157835SDaniel Fojt 
ieee80211ac_cap_check(u32 hw,u32 conf)527*a1157835SDaniel Fojt int ieee80211ac_cap_check(u32 hw, u32 conf)
528*a1157835SDaniel Fojt {
529*a1157835SDaniel Fojt #define VHT_CAP_CHECK(cap) \
530*a1157835SDaniel Fojt 	do { \
531*a1157835SDaniel Fojt 		if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \
532*a1157835SDaniel Fojt 			return 0; \
533*a1157835SDaniel Fojt 	} while (0)
534*a1157835SDaniel Fojt 
535*a1157835SDaniel Fojt #define VHT_CAP_CHECK_MAX(cap) \
536*a1157835SDaniel Fojt 	do { \
537*a1157835SDaniel Fojt 		if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
538*a1157835SDaniel Fojt 					       #cap)) \
539*a1157835SDaniel Fojt 			return 0; \
540*a1157835SDaniel Fojt 	} while (0)
541*a1157835SDaniel Fojt 
542*a1157835SDaniel Fojt 	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
543*a1157835SDaniel Fojt 	VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK);
544*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_RXLDPC);
545*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
546*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
547*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_TXSTBC);
548*a1157835SDaniel Fojt 	VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
549*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
550*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
551*a1157835SDaniel Fojt 	VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
552*a1157835SDaniel Fojt 	VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
553*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
554*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
555*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
556*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
557*a1157835SDaniel Fojt 	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
558*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
559*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
560*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
561*a1157835SDaniel Fojt 	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
562*a1157835SDaniel Fojt 
563*a1157835SDaniel Fojt #undef VHT_CAP_CHECK
564*a1157835SDaniel Fojt #undef VHT_CAP_CHECK_MAX
565*a1157835SDaniel Fojt 
566*a1157835SDaniel Fojt 	return 1;
567*a1157835SDaniel Fojt }
568*a1157835SDaniel Fojt 
569*a1157835SDaniel Fojt #endif /* CONFIG_IEEE80211AC */
570*a1157835SDaniel Fojt 
571*a1157835SDaniel Fojt 
num_chan_to_bw(int num_chans)572*a1157835SDaniel Fojt u32 num_chan_to_bw(int num_chans)
573*a1157835SDaniel Fojt {
574*a1157835SDaniel Fojt 	switch (num_chans) {
575*a1157835SDaniel Fojt 	case 2:
576*a1157835SDaniel Fojt 	case 4:
577*a1157835SDaniel Fojt 	case 8:
578*a1157835SDaniel Fojt 		return num_chans * 20;
579*a1157835SDaniel Fojt 	default:
580*a1157835SDaniel Fojt 		return 20;
581*a1157835SDaniel Fojt 	}
582*a1157835SDaniel Fojt }
583*a1157835SDaniel Fojt 
584*a1157835SDaniel Fojt 
585*a1157835SDaniel Fojt /* check if BW is applicable for channel */
chan_bw_allowed(const struct hostapd_channel_data * chan,u32 bw,int ht40_plus,int pri)586*a1157835SDaniel Fojt int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
587*a1157835SDaniel Fojt 		    int ht40_plus, int pri)
588*a1157835SDaniel Fojt {
589*a1157835SDaniel Fojt 	u32 bw_mask;
590*a1157835SDaniel Fojt 
591*a1157835SDaniel Fojt 	switch (bw) {
592*a1157835SDaniel Fojt 	case 20:
593*a1157835SDaniel Fojt 		bw_mask = HOSTAPD_CHAN_WIDTH_20;
594*a1157835SDaniel Fojt 		break;
595*a1157835SDaniel Fojt 	case 40:
596*a1157835SDaniel Fojt 		/* HT 40 MHz support declared only for primary channel,
597*a1157835SDaniel Fojt 		 * just skip 40 MHz secondary checking */
598*a1157835SDaniel Fojt 		if (pri && ht40_plus)
599*a1157835SDaniel Fojt 			bw_mask = HOSTAPD_CHAN_WIDTH_40P;
600*a1157835SDaniel Fojt 		else if (pri && !ht40_plus)
601*a1157835SDaniel Fojt 			bw_mask = HOSTAPD_CHAN_WIDTH_40M;
602*a1157835SDaniel Fojt 		else
603*a1157835SDaniel Fojt 			bw_mask = 0;
604*a1157835SDaniel Fojt 		break;
605*a1157835SDaniel Fojt 	case 80:
606*a1157835SDaniel Fojt 		bw_mask = HOSTAPD_CHAN_WIDTH_80;
607*a1157835SDaniel Fojt 		break;
608*a1157835SDaniel Fojt 	case 160:
609*a1157835SDaniel Fojt 		bw_mask = HOSTAPD_CHAN_WIDTH_160;
610*a1157835SDaniel Fojt 		break;
611*a1157835SDaniel Fojt 	default:
612*a1157835SDaniel Fojt 		bw_mask = 0;
613*a1157835SDaniel Fojt 		break;
614*a1157835SDaniel Fojt 	}
615*a1157835SDaniel Fojt 
616*a1157835SDaniel Fojt 	return (chan->allowed_bw & bw_mask) == bw_mask;
617*a1157835SDaniel Fojt }
618*a1157835SDaniel Fojt 
619*a1157835SDaniel Fojt 
620*a1157835SDaniel Fojt /* check if channel is allowed to be used as primary */
chan_pri_allowed(const struct hostapd_channel_data * chan)621*a1157835SDaniel Fojt int chan_pri_allowed(const struct hostapd_channel_data *chan)
622*a1157835SDaniel Fojt {
623*a1157835SDaniel Fojt 	return !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
624*a1157835SDaniel Fojt 		(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20);
625*a1157835SDaniel Fojt }
626