1214501Srpaulo/*
2214501Srpaulo * hostapd / Hardware feature query and different modes
3214501Srpaulo * Copyright 2002-2003, Instant802 Networks, Inc.
4214501Srpaulo * Copyright 2005-2006, Devicescape Software, Inc.
5214501Srpaulo * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
6214501Srpaulo *
7214501Srpaulo * This program is free software; you can redistribute it and/or modify
8214501Srpaulo * it under the terms of the GNU General Public License version 2 as
9214501Srpaulo * published by the Free Software Foundation.
10214501Srpaulo *
11214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD
12214501Srpaulo * license.
13214501Srpaulo *
14214501Srpaulo * See README and COPYING for more details.
15214501Srpaulo */
16214501Srpaulo
17214501Srpaulo#include "utils/includes.h"
18214501Srpaulo
19214501Srpaulo#include "utils/common.h"
20214501Srpaulo#include "utils/eloop.h"
21214501Srpaulo#include "common/ieee802_11_defs.h"
22214501Srpaulo#include "common/ieee802_11_common.h"
23214501Srpaulo#include "drivers/driver.h"
24214501Srpaulo#include "hostapd.h"
25214501Srpaulo#include "ap_config.h"
26214501Srpaulo#include "ap_drv_ops.h"
27214501Srpaulo#include "hw_features.h"
28214501Srpaulo
29214501Srpaulo
30214501Srpaulovoid hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
31214501Srpaulo			      size_t num_hw_features)
32214501Srpaulo{
33214501Srpaulo	size_t i;
34214501Srpaulo
35214501Srpaulo	if (hw_features == NULL)
36214501Srpaulo		return;
37214501Srpaulo
38214501Srpaulo	for (i = 0; i < num_hw_features; i++) {
39214501Srpaulo		os_free(hw_features[i].channels);
40214501Srpaulo		os_free(hw_features[i].rates);
41214501Srpaulo	}
42214501Srpaulo
43214501Srpaulo	os_free(hw_features);
44214501Srpaulo}
45214501Srpaulo
46214501Srpaulo
47214501Srpauloint hostapd_get_hw_features(struct hostapd_iface *iface)
48214501Srpaulo{
49214501Srpaulo	struct hostapd_data *hapd = iface->bss[0];
50214501Srpaulo	int ret = 0, i, j;
51214501Srpaulo	u16 num_modes, flags;
52214501Srpaulo	struct hostapd_hw_modes *modes;
53214501Srpaulo
54214501Srpaulo	if (hostapd_drv_none(hapd))
55214501Srpaulo		return -1;
56214501Srpaulo	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
57214501Srpaulo	if (modes == NULL) {
58214501Srpaulo		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
59214501Srpaulo			       HOSTAPD_LEVEL_DEBUG,
60214501Srpaulo			       "Fetching hardware channel/rate support not "
61214501Srpaulo			       "supported.");
62214501Srpaulo		return -1;
63214501Srpaulo	}
64214501Srpaulo
65214501Srpaulo	iface->hw_flags = flags;
66214501Srpaulo
67214501Srpaulo	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
68214501Srpaulo	iface->hw_features = modes;
69214501Srpaulo	iface->num_hw_features = num_modes;
70214501Srpaulo
71214501Srpaulo	for (i = 0; i < num_modes; i++) {
72214501Srpaulo		struct hostapd_hw_modes *feature = &modes[i];
73214501Srpaulo		/* set flag for channels we can use in current regulatory
74214501Srpaulo		 * domain */
75214501Srpaulo		for (j = 0; j < feature->num_channels; j++) {
76214501Srpaulo			/*
77214501Srpaulo			 * Disable all channels that are marked not to allow
78214501Srpaulo			 * IBSS operation or active scanning. In addition,
79214501Srpaulo			 * disable all channels that require radar detection,
80214501Srpaulo			 * since that (in addition to full DFS) is not yet
81214501Srpaulo			 * supported.
82214501Srpaulo			 */
83214501Srpaulo			if (feature->channels[j].flag &
84214501Srpaulo			    (HOSTAPD_CHAN_NO_IBSS |
85214501Srpaulo			     HOSTAPD_CHAN_PASSIVE_SCAN |
86214501Srpaulo			     HOSTAPD_CHAN_RADAR))
87214501Srpaulo				feature->channels[j].flag |=
88214501Srpaulo					HOSTAPD_CHAN_DISABLED;
89214501Srpaulo			if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
90214501Srpaulo				continue;
91214501Srpaulo			wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
92214501Srpaulo				   "chan=%d freq=%d MHz max_tx_power=%d dBm",
93214501Srpaulo				   feature->mode,
94214501Srpaulo				   feature->channels[j].chan,
95214501Srpaulo				   feature->channels[j].freq,
96214501Srpaulo				   feature->channels[j].max_tx_power);
97214501Srpaulo		}
98214501Srpaulo	}
99214501Srpaulo
100214501Srpaulo	return ret;
101214501Srpaulo}
102214501Srpaulo
103214501Srpaulo
104214501Srpaulostatic int hostapd_prepare_rates(struct hostapd_data *hapd,
105214501Srpaulo				 struct hostapd_hw_modes *mode)
106214501Srpaulo{
107214501Srpaulo	int i, num_basic_rates = 0;
108214501Srpaulo	int basic_rates_a[] = { 60, 120, 240, -1 };
109214501Srpaulo	int basic_rates_b[] = { 10, 20, -1 };
110214501Srpaulo	int basic_rates_g[] = { 10, 20, 55, 110, -1 };
111214501Srpaulo	int *basic_rates;
112214501Srpaulo
113214501Srpaulo	if (hapd->iconf->basic_rates)
114214501Srpaulo		basic_rates = hapd->iconf->basic_rates;
115214501Srpaulo	else switch (mode->mode) {
116214501Srpaulo	case HOSTAPD_MODE_IEEE80211A:
117214501Srpaulo		basic_rates = basic_rates_a;
118214501Srpaulo		break;
119214501Srpaulo	case HOSTAPD_MODE_IEEE80211B:
120214501Srpaulo		basic_rates = basic_rates_b;
121214501Srpaulo		break;
122214501Srpaulo	case HOSTAPD_MODE_IEEE80211G:
123214501Srpaulo		basic_rates = basic_rates_g;
124214501Srpaulo		break;
125214501Srpaulo	default:
126214501Srpaulo		return -1;
127214501Srpaulo	}
128214501Srpaulo
129214501Srpaulo	if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates,
130214501Srpaulo				  basic_rates, mode->mode)) {
131214501Srpaulo		wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel "
132214501Srpaulo			   "module");
133214501Srpaulo	}
134214501Srpaulo
135214501Srpaulo	os_free(hapd->iface->current_rates);
136214501Srpaulo	hapd->iface->num_rates = 0;
137214501Srpaulo
138214501Srpaulo	hapd->iface->current_rates =
139214501Srpaulo		os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data));
140214501Srpaulo	if (!hapd->iface->current_rates) {
141214501Srpaulo		wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
142214501Srpaulo			   "table.");
143214501Srpaulo		return -1;
144214501Srpaulo	}
145214501Srpaulo
146214501Srpaulo	for (i = 0; i < mode->num_rates; i++) {
147214501Srpaulo		struct hostapd_rate_data *rate;
148214501Srpaulo
149214501Srpaulo		if (hapd->iconf->supported_rates &&
150214501Srpaulo		    !hostapd_rate_found(hapd->iconf->supported_rates,
151214501Srpaulo					mode->rates[i]))
152214501Srpaulo			continue;
153214501Srpaulo
154214501Srpaulo		rate = &hapd->iface->current_rates[hapd->iface->num_rates];
155214501Srpaulo		rate->rate = mode->rates[i];
156214501Srpaulo		if (hostapd_rate_found(basic_rates, rate->rate)) {
157214501Srpaulo			rate->flags |= HOSTAPD_RATE_BASIC;
158214501Srpaulo			num_basic_rates++;
159214501Srpaulo		}
160214501Srpaulo		wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
161214501Srpaulo			   hapd->iface->num_rates, rate->rate, rate->flags);
162214501Srpaulo		hapd->iface->num_rates++;
163214501Srpaulo	}
164214501Srpaulo
165214501Srpaulo	if (hapd->iface->num_rates == 0 || num_basic_rates == 0) {
166214501Srpaulo		wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
167214501Srpaulo			   "rate sets (%d,%d).",
168214501Srpaulo			   hapd->iface->num_rates, num_basic_rates);
169214501Srpaulo		return -1;
170214501Srpaulo	}
171214501Srpaulo
172214501Srpaulo	return 0;
173214501Srpaulo}
174214501Srpaulo
175214501Srpaulo
176214501Srpaulo#ifdef CONFIG_IEEE80211N
177214501Srpaulostatic int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
178214501Srpaulo{
179214501Srpaulo	int sec_chan, ok, j, first;
180214501Srpaulo	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
181214501Srpaulo			  184, 192 };
182214501Srpaulo	size_t k;
183214501Srpaulo
184214501Srpaulo	if (!iface->conf->secondary_channel)
185214501Srpaulo		return 1; /* HT40 not used */
186214501Srpaulo
187214501Srpaulo	sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
188214501Srpaulo	wpa_printf(MSG_DEBUG, "HT40: control channel: %d  "
189214501Srpaulo		   "secondary channel: %d",
190214501Srpaulo		   iface->conf->channel, sec_chan);
191214501Srpaulo
192214501Srpaulo	/* Verify that HT40 secondary channel is an allowed 20 MHz
193214501Srpaulo	 * channel */
194214501Srpaulo	ok = 0;
195214501Srpaulo	for (j = 0; j < iface->current_mode->num_channels; j++) {
196214501Srpaulo		struct hostapd_channel_data *chan =
197214501Srpaulo			&iface->current_mode->channels[j];
198214501Srpaulo		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
199214501Srpaulo		    chan->chan == sec_chan) {
200214501Srpaulo			ok = 1;
201214501Srpaulo			break;
202214501Srpaulo		}
203214501Srpaulo	}
204214501Srpaulo	if (!ok) {
205214501Srpaulo		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
206214501Srpaulo			   sec_chan);
207214501Srpaulo		return 0;
208214501Srpaulo	}
209214501Srpaulo
210214501Srpaulo	/*
211214501Srpaulo	 * Verify that HT40 primary,secondary channel pair is allowed per
212214501Srpaulo	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
213214501Srpaulo	 * 2.4 GHz rules allow all cases where the secondary channel fits into
214214501Srpaulo	 * the list of allowed channels (already checked above).
215214501Srpaulo	 */
216214501Srpaulo	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
217214501Srpaulo		return 1;
218214501Srpaulo
219214501Srpaulo	if (iface->conf->secondary_channel > 0)
220214501Srpaulo		first = iface->conf->channel;
221214501Srpaulo	else
222214501Srpaulo		first = sec_chan;
223214501Srpaulo
224214501Srpaulo	ok = 0;
225214501Srpaulo	for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
226214501Srpaulo		if (first == allowed[k]) {
227214501Srpaulo			ok = 1;
228214501Srpaulo			break;
229214501Srpaulo		}
230214501Srpaulo	}
231214501Srpaulo	if (!ok) {
232214501Srpaulo		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
233214501Srpaulo			   iface->conf->channel,
234214501Srpaulo			   iface->conf->secondary_channel);
235214501Srpaulo		return 0;
236214501Srpaulo	}
237214501Srpaulo
238214501Srpaulo	return 1;
239214501Srpaulo}
240214501Srpaulo
241214501Srpaulo
242214501Srpaulostatic void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
243214501Srpaulo{
244214501Srpaulo	if (iface->conf->secondary_channel > 0) {
245214501Srpaulo		iface->conf->channel += 4;
246214501Srpaulo		iface->conf->secondary_channel = -1;
247214501Srpaulo	} else {
248214501Srpaulo		iface->conf->channel -= 4;
249214501Srpaulo		iface->conf->secondary_channel = 1;
250214501Srpaulo	}
251214501Srpaulo}
252214501Srpaulo
253214501Srpaulo
254214501Srpaulostatic void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
255214501Srpaulo					int *pri_chan, int *sec_chan)
256214501Srpaulo{
257214501Srpaulo	struct ieee80211_ht_operation *oper;
258214501Srpaulo	struct ieee802_11_elems elems;
259214501Srpaulo
260214501Srpaulo	*pri_chan = *sec_chan = 0;
261214501Srpaulo
262214501Srpaulo	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
263214501Srpaulo	if (elems.ht_operation &&
264214501Srpaulo	    elems.ht_operation_len >= sizeof(*oper)) {
265214501Srpaulo		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
266214501Srpaulo		*pri_chan = oper->control_chan;
267214501Srpaulo		if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
268214501Srpaulo			if (oper->ht_param &
269214501Srpaulo			    HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
270214501Srpaulo				*sec_chan = *pri_chan + 4;
271214501Srpaulo			else if (oper->ht_param &
272214501Srpaulo				 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
273214501Srpaulo				*sec_chan = *pri_chan - 4;
274214501Srpaulo		}
275214501Srpaulo	}
276214501Srpaulo}
277214501Srpaulo
278214501Srpaulo
279214501Srpaulostatic int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
280214501Srpaulo				     struct wpa_scan_results *scan_res)
281214501Srpaulo{
282214501Srpaulo	int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
283214501Srpaulo	int bss_pri_chan, bss_sec_chan;
284214501Srpaulo	size_t i;
285214501Srpaulo	int match;
286214501Srpaulo
287214501Srpaulo	pri_chan = iface->conf->channel;
288214501Srpaulo	sec_chan = iface->conf->secondary_channel * 4;
289214501Srpaulo	pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
290214501Srpaulo	if (iface->conf->secondary_channel > 0)
291214501Srpaulo		sec_freq = pri_freq + 20;
292214501Srpaulo	else
293214501Srpaulo		sec_freq = pri_freq - 20;
294214501Srpaulo
295214501Srpaulo	/*
296214501Srpaulo	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
297214501Srpaulo	 * channel, but not on selected PRI channel.
298214501Srpaulo	 */
299214501Srpaulo	pri_bss = sec_bss = 0;
300214501Srpaulo	for (i = 0; i < scan_res->num; i++) {
301214501Srpaulo		struct wpa_scan_res *bss = scan_res->res[i];
302214501Srpaulo		if (bss->freq == pri_freq)
303214501Srpaulo			pri_bss++;
304214501Srpaulo		else if (bss->freq == sec_freq)
305214501Srpaulo			sec_bss++;
306214501Srpaulo	}
307214501Srpaulo	if (sec_bss && !pri_bss) {
308214501Srpaulo		wpa_printf(MSG_INFO, "Switch own primary and secondary "
309214501Srpaulo			   "channel to get secondary channel with no Beacons "
310214501Srpaulo			   "from other BSSes");
311214501Srpaulo		ieee80211n_switch_pri_sec(iface);
312214501Srpaulo	}
313214501Srpaulo
314214501Srpaulo	/*
315214501Srpaulo	 * Match PRI/SEC channel with any existing HT40 BSS on the same
316214501Srpaulo	 * channels that we are about to use (if already mixed order in
317214501Srpaulo	 * existing BSSes, use own preference).
318214501Srpaulo	 */
319214501Srpaulo	match = 0;
320214501Srpaulo	for (i = 0; i < scan_res->num; i++) {
321214501Srpaulo		struct wpa_scan_res *bss = scan_res->res[i];
322214501Srpaulo		ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
323214501Srpaulo		if (pri_chan == bss_pri_chan &&
324214501Srpaulo		    sec_chan == bss_sec_chan) {
325214501Srpaulo			match = 1;
326214501Srpaulo			break;
327214501Srpaulo		}
328214501Srpaulo	}
329214501Srpaulo	if (!match) {
330214501Srpaulo		for (i = 0; i < scan_res->num; i++) {
331214501Srpaulo			struct wpa_scan_res *bss = scan_res->res[i];
332214501Srpaulo			ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
333214501Srpaulo						    &bss_sec_chan);
334214501Srpaulo			if (pri_chan == bss_sec_chan &&
335214501Srpaulo			    sec_chan == bss_pri_chan) {
336214501Srpaulo				wpa_printf(MSG_INFO, "Switch own primary and "
337214501Srpaulo					   "secondary channel due to BSS "
338214501Srpaulo					   "overlap with " MACSTR,
339214501Srpaulo					   MAC2STR(bss->bssid));
340214501Srpaulo				ieee80211n_switch_pri_sec(iface);
341214501Srpaulo				break;
342214501Srpaulo			}
343214501Srpaulo		}
344214501Srpaulo	}
345214501Srpaulo
346214501Srpaulo	return 1;
347214501Srpaulo}
348214501Srpaulo
349214501Srpaulo
350214501Srpaulostatic int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
351214501Srpaulo				      struct wpa_scan_results *scan_res)
352214501Srpaulo{
353214501Srpaulo	int pri_freq, sec_freq;
354214501Srpaulo	int affected_start, affected_end;
355214501Srpaulo	size_t i;
356214501Srpaulo
357214501Srpaulo	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
358214501Srpaulo	if (iface->conf->secondary_channel > 0)
359214501Srpaulo		sec_freq = pri_freq + 20;
360214501Srpaulo	else
361214501Srpaulo		sec_freq = pri_freq - 20;
362214501Srpaulo	affected_start = (pri_freq + sec_freq) / 2 - 25;
363214501Srpaulo	affected_end = (pri_freq + sec_freq) / 2 + 25;
364214501Srpaulo	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
365214501Srpaulo		   affected_start, affected_end);
366214501Srpaulo	for (i = 0; i < scan_res->num; i++) {
367214501Srpaulo		struct wpa_scan_res *bss = scan_res->res[i];
368214501Srpaulo		int pri = bss->freq;
369214501Srpaulo		int sec = pri;
370214501Srpaulo		int sec_chan, pri_chan;
371214501Srpaulo
372214501Srpaulo		ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
373214501Srpaulo
374214501Srpaulo		if (sec_chan) {
375214501Srpaulo			if (sec_chan < pri_chan)
376214501Srpaulo				sec = pri - 20;
377214501Srpaulo			else
378214501Srpaulo				sec = pri + 20;
379214501Srpaulo		}
380214501Srpaulo
381214501Srpaulo		if ((pri < affected_start || pri > affected_end) &&
382214501Srpaulo		    (sec < affected_start || sec > affected_end))
383214501Srpaulo			continue; /* not within affected channel range */
384214501Srpaulo
385214501Srpaulo		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
386214501Srpaulo			   " freq=%d pri=%d sec=%d",
387214501Srpaulo			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
388214501Srpaulo
389214501Srpaulo		if (sec_chan) {
390214501Srpaulo			if (pri_freq != pri || sec_freq != sec) {
391214501Srpaulo				wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
392214501Srpaulo					   "mismatch with BSS " MACSTR
393214501Srpaulo					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
394214501Srpaulo					   MAC2STR(bss->bssid),
395214501Srpaulo					   pri, sec, pri_chan,
396214501Srpaulo					   sec > pri ? '+' : '-',
397214501Srpaulo					   pri_freq, sec_freq);
398214501Srpaulo				return 0;
399214501Srpaulo			}
400214501Srpaulo		}
401214501Srpaulo
402214501Srpaulo		/* TODO: 40 MHz intolerant */
403214501Srpaulo	}
404214501Srpaulo
405214501Srpaulo	return 1;
406214501Srpaulo}
407214501Srpaulo
408214501Srpaulo
409214501Srpaulostatic void wpa_scan_results_free(struct wpa_scan_results *res)
410214501Srpaulo{
411214501Srpaulo	size_t i;
412214501Srpaulo
413214501Srpaulo	if (res == NULL)
414214501Srpaulo		return;
415214501Srpaulo
416214501Srpaulo	for (i = 0; i < res->num; i++)
417214501Srpaulo		os_free(res->res[i]);
418214501Srpaulo	os_free(res->res);
419214501Srpaulo	os_free(res);
420214501Srpaulo}
421214501Srpaulo
422214501Srpaulo
423214501Srpaulostatic void ieee80211n_check_scan(struct hostapd_iface *iface)
424214501Srpaulo{
425214501Srpaulo	struct wpa_scan_results *scan_res;
426214501Srpaulo	int oper40;
427214501Srpaulo
428214501Srpaulo	/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
429214501Srpaulo	 * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
430214501Srpaulo
431214501Srpaulo	iface->scan_cb = NULL;
432214501Srpaulo
433214501Srpaulo	scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
434214501Srpaulo	if (scan_res == NULL) {
435214501Srpaulo		hostapd_setup_interface_complete(iface, 1);
436214501Srpaulo		return;
437214501Srpaulo	}
438214501Srpaulo
439214501Srpaulo	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
440214501Srpaulo		oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
441214501Srpaulo	else
442214501Srpaulo		oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
443214501Srpaulo	wpa_scan_results_free(scan_res);
444214501Srpaulo
445214501Srpaulo	if (!oper40) {
446214501Srpaulo		wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
447214501Srpaulo			   "channel pri=%d sec=%d based on overlapping BSSes",
448214501Srpaulo			   iface->conf->channel,
449214501Srpaulo			   iface->conf->channel +
450214501Srpaulo			   iface->conf->secondary_channel * 4);
451214501Srpaulo		iface->conf->secondary_channel = 0;
452214501Srpaulo		iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
453214501Srpaulo	}
454214501Srpaulo
455214501Srpaulo	hostapd_setup_interface_complete(iface, 0);
456214501Srpaulo}
457214501Srpaulo
458214501Srpaulo
459214501Srpaulostatic int ieee80211n_check_40mhz(struct hostapd_iface *iface)
460214501Srpaulo{
461214501Srpaulo	struct wpa_driver_scan_params params;
462214501Srpaulo
463214501Srpaulo	if (!iface->conf->secondary_channel)
464214501Srpaulo		return 0; /* HT40 not used */
465214501Srpaulo
466214501Srpaulo	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
467214501Srpaulo		   "40 MHz channel");
468214501Srpaulo	os_memset(&params, 0, sizeof(params));
469214501Srpaulo	/* TODO: scan only the needed frequency */
470214501Srpaulo	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
471214501Srpaulo		wpa_printf(MSG_ERROR, "Failed to request a scan of "
472214501Srpaulo			   "neighboring BSSes");
473214501Srpaulo		return -1;
474214501Srpaulo	}
475214501Srpaulo
476214501Srpaulo	iface->scan_cb = ieee80211n_check_scan;
477214501Srpaulo	return 1;
478214501Srpaulo}
479214501Srpaulo
480214501Srpaulo
481214501Srpaulostatic int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
482214501Srpaulo{
483214501Srpaulo	u16 hw = iface->current_mode->ht_capab;
484214501Srpaulo	u16 conf = iface->conf->ht_capab;
485214501Srpaulo
486214501Srpaulo	if (!iface->conf->ieee80211n)
487214501Srpaulo		return 1;
488214501Srpaulo
489214501Srpaulo	if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
490214501Srpaulo	    !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
491214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
492214501Srpaulo			   "HT capability [LDPC]");
493214501Srpaulo		return 0;
494214501Srpaulo	}
495214501Srpaulo
496214501Srpaulo	if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
497214501Srpaulo	    !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
498214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
499214501Srpaulo			   "HT capability [HT40*]");
500214501Srpaulo		return 0;
501214501Srpaulo	}
502214501Srpaulo
503214501Srpaulo	if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
504214501Srpaulo	    (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
505214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
506214501Srpaulo			   "HT capability [SMPS-*]");
507214501Srpaulo		return 0;
508214501Srpaulo	}
509214501Srpaulo
510214501Srpaulo	if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
511214501Srpaulo	    !(hw & HT_CAP_INFO_GREEN_FIELD)) {
512214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
513214501Srpaulo			   "HT capability [GF]");
514214501Srpaulo		return 0;
515214501Srpaulo	}
516214501Srpaulo
517214501Srpaulo	if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
518214501Srpaulo	    !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
519214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
520214501Srpaulo			   "HT capability [SHORT-GI-20]");
521214501Srpaulo		return 0;
522214501Srpaulo	}
523214501Srpaulo
524214501Srpaulo	if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
525214501Srpaulo	    !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
526214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
527214501Srpaulo			   "HT capability [SHORT-GI-40]");
528214501Srpaulo		return 0;
529214501Srpaulo	}
530214501Srpaulo
531214501Srpaulo	if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
532214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
533214501Srpaulo			   "HT capability [TX-STBC]");
534214501Srpaulo		return 0;
535214501Srpaulo	}
536214501Srpaulo
537214501Srpaulo	if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
538214501Srpaulo	    (hw & HT_CAP_INFO_RX_STBC_MASK)) {
539214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
540214501Srpaulo			   "HT capability [RX-STBC*]");
541214501Srpaulo		return 0;
542214501Srpaulo	}
543214501Srpaulo
544214501Srpaulo	if ((conf & HT_CAP_INFO_DELAYED_BA) &&
545214501Srpaulo	    !(hw & HT_CAP_INFO_DELAYED_BA)) {
546214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
547214501Srpaulo			   "HT capability [DELAYED-BA]");
548214501Srpaulo		return 0;
549214501Srpaulo	}
550214501Srpaulo
551214501Srpaulo	if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
552214501Srpaulo	    !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
553214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
554214501Srpaulo			   "HT capability [MAX-AMSDU-7935]");
555214501Srpaulo		return 0;
556214501Srpaulo	}
557214501Srpaulo
558214501Srpaulo	if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
559214501Srpaulo	    !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
560214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
561214501Srpaulo			   "HT capability [DSSS_CCK-40]");
562214501Srpaulo		return 0;
563214501Srpaulo	}
564214501Srpaulo
565214501Srpaulo	if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
566214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
567214501Srpaulo			   "HT capability [PSMP]");
568214501Srpaulo		return 0;
569214501Srpaulo	}
570214501Srpaulo
571214501Srpaulo	if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
572214501Srpaulo	    !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
573214501Srpaulo		wpa_printf(MSG_ERROR, "Driver does not support configured "
574214501Srpaulo			   "HT capability [LSIG-TXOP-PROT]");
575214501Srpaulo		return 0;
576214501Srpaulo	}
577214501Srpaulo
578214501Srpaulo	return 1;
579214501Srpaulo}
580214501Srpaulo
581214501Srpaulo#endif /* CONFIG_IEEE80211N */
582214501Srpaulo
583214501Srpaulo
584214501Srpauloint hostapd_check_ht_capab(struct hostapd_iface *iface)
585214501Srpaulo{
586214501Srpaulo#ifdef CONFIG_IEEE80211N
587214501Srpaulo	int ret;
588214501Srpaulo	ret = ieee80211n_check_40mhz(iface);
589214501Srpaulo	if (ret)
590214501Srpaulo		return ret;
591214501Srpaulo	if (!ieee80211n_allowed_ht40_channel_pair(iface))
592214501Srpaulo		return -1;
593214501Srpaulo	if (!ieee80211n_supported_ht_capab(iface))
594214501Srpaulo		return -1;
595214501Srpaulo#endif /* CONFIG_IEEE80211N */
596214501Srpaulo
597214501Srpaulo	return 0;
598214501Srpaulo}
599214501Srpaulo
600214501Srpaulo
601214501Srpaulo/**
602214501Srpaulo * hostapd_select_hw_mode - Select the hardware mode
603214501Srpaulo * @iface: Pointer to interface data.
604214501Srpaulo * Returns: 0 on success, -1 on failure
605214501Srpaulo *
606214501Srpaulo * Sets up the hardware mode, channel, rates, and passive scanning
607214501Srpaulo * based on the configuration.
608214501Srpaulo */
609214501Srpauloint hostapd_select_hw_mode(struct hostapd_iface *iface)
610214501Srpaulo{
611214501Srpaulo	int i, j, ok;
612214501Srpaulo
613214501Srpaulo	if (iface->num_hw_features < 1)
614214501Srpaulo		return -1;
615214501Srpaulo
616214501Srpaulo	iface->current_mode = NULL;
617214501Srpaulo	for (i = 0; i < iface->num_hw_features; i++) {
618214501Srpaulo		struct hostapd_hw_modes *mode = &iface->hw_features[i];
619214501Srpaulo		if (mode->mode == iface->conf->hw_mode) {
620214501Srpaulo			iface->current_mode = mode;
621214501Srpaulo			break;
622214501Srpaulo		}
623214501Srpaulo	}
624214501Srpaulo
625214501Srpaulo	if (iface->current_mode == NULL) {
626214501Srpaulo		wpa_printf(MSG_ERROR, "Hardware does not support configured "
627214501Srpaulo			   "mode");
628214501Srpaulo		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
629214501Srpaulo			       HOSTAPD_LEVEL_WARNING,
630214501Srpaulo			       "Hardware does not support configured mode "
631214501Srpaulo			       "(%d)", (int) iface->conf->hw_mode);
632214501Srpaulo		return -1;
633214501Srpaulo	}
634214501Srpaulo
635214501Srpaulo	ok = 0;
636214501Srpaulo	for (j = 0; j < iface->current_mode->num_channels; j++) {
637214501Srpaulo		struct hostapd_channel_data *chan =
638214501Srpaulo			&iface->current_mode->channels[j];
639214501Srpaulo		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
640214501Srpaulo		    (chan->chan == iface->conf->channel)) {
641214501Srpaulo			ok = 1;
642214501Srpaulo			break;
643214501Srpaulo		}
644214501Srpaulo	}
645214501Srpaulo	if (iface->conf->channel == 0) {
646214501Srpaulo		/* TODO: could request a scan of neighboring BSSes and select
647214501Srpaulo		 * the channel automatically */
648214501Srpaulo		wpa_printf(MSG_ERROR, "Channel not configured "
649214501Srpaulo			   "(hw_mode/channel in hostapd.conf)");
650214501Srpaulo		return -1;
651214501Srpaulo	}
652214501Srpaulo	if (ok == 0 && iface->conf->channel != 0) {
653214501Srpaulo		hostapd_logger(iface->bss[0], NULL,
654214501Srpaulo			       HOSTAPD_MODULE_IEEE80211,
655214501Srpaulo			       HOSTAPD_LEVEL_WARNING,
656214501Srpaulo			       "Configured channel (%d) not found from the "
657214501Srpaulo			       "channel list of current mode (%d) %s",
658214501Srpaulo			       iface->conf->channel,
659214501Srpaulo			       iface->current_mode->mode,
660214501Srpaulo			       hostapd_hw_mode_txt(iface->current_mode->mode));
661214501Srpaulo		iface->current_mode = NULL;
662214501Srpaulo	}
663214501Srpaulo
664214501Srpaulo	if (iface->current_mode == NULL) {
665214501Srpaulo		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
666214501Srpaulo			       HOSTAPD_LEVEL_WARNING,
667214501Srpaulo			       "Hardware does not support configured channel");
668214501Srpaulo		return -1;
669214501Srpaulo	}
670214501Srpaulo
671214501Srpaulo	if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) {
672214501Srpaulo		wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
673214501Srpaulo		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
674214501Srpaulo					   HOSTAPD_LEVEL_WARNING,
675214501Srpaulo					   "Failed to prepare rates table.");
676214501Srpaulo		return -1;
677214501Srpaulo	}
678214501Srpaulo
679214501Srpaulo	return 0;
680214501Srpaulo}
681214501Srpaulo
682214501Srpaulo
683214501Srpauloconst char * hostapd_hw_mode_txt(int mode)
684214501Srpaulo{
685214501Srpaulo	switch (mode) {
686214501Srpaulo	case HOSTAPD_MODE_IEEE80211A:
687214501Srpaulo		return "IEEE 802.11a";
688214501Srpaulo	case HOSTAPD_MODE_IEEE80211B:
689214501Srpaulo		return "IEEE 802.11b";
690214501Srpaulo	case HOSTAPD_MODE_IEEE80211G:
691214501Srpaulo		return "IEEE 802.11g";
692214501Srpaulo	default:
693214501Srpaulo		return "UNKNOWN";
694214501Srpaulo	}
695214501Srpaulo}
696214501Srpaulo
697214501Srpaulo
698214501Srpauloint hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
699214501Srpaulo{
700214501Srpaulo	int i;
701214501Srpaulo
702214501Srpaulo	if (!hapd->iface->current_mode)
703214501Srpaulo		return 0;
704214501Srpaulo
705214501Srpaulo	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
706214501Srpaulo		struct hostapd_channel_data *ch =
707214501Srpaulo			&hapd->iface->current_mode->channels[i];
708214501Srpaulo		if (ch->chan == chan)
709214501Srpaulo			return ch->freq;
710214501Srpaulo	}
711214501Srpaulo
712214501Srpaulo	return 0;
713214501Srpaulo}
714214501Srpaulo
715214501Srpaulo
716214501Srpauloint hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
717214501Srpaulo{
718214501Srpaulo	int i;
719214501Srpaulo
720214501Srpaulo	if (!hapd->iface->current_mode)
721214501Srpaulo		return 0;
722214501Srpaulo
723214501Srpaulo	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
724214501Srpaulo		struct hostapd_channel_data *ch =
725214501Srpaulo			&hapd->iface->current_mode->channels[i];
726214501Srpaulo		if (ch->freq == freq)
727214501Srpaulo			return ch->chan;
728214501Srpaulo	}
729214501Srpaulo
730214501Srpaulo	return 0;
731214501Srpaulo}
732