13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * hostapd / Hardware feature query and different modes
33ff40c12SJohn Marino * Copyright 2002-2003, Instant802 Networks, Inc.
43ff40c12SJohn Marino * Copyright 2005-2006, Devicescape Software, Inc.
53ff40c12SJohn Marino * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
63ff40c12SJohn Marino *
73ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
83ff40c12SJohn Marino * See README for more details.
93ff40c12SJohn Marino */
103ff40c12SJohn Marino
113ff40c12SJohn Marino #include "utils/includes.h"
123ff40c12SJohn Marino
133ff40c12SJohn Marino #include "utils/common.h"
143ff40c12SJohn Marino #include "utils/eloop.h"
153ff40c12SJohn Marino #include "common/ieee802_11_defs.h"
163ff40c12SJohn Marino #include "common/ieee802_11_common.h"
173ff40c12SJohn Marino #include "common/wpa_ctrl.h"
18*a1157835SDaniel Fojt #include "common/hw_features_common.h"
193ff40c12SJohn Marino #include "hostapd.h"
203ff40c12SJohn Marino #include "ap_config.h"
213ff40c12SJohn Marino #include "ap_drv_ops.h"
223ff40c12SJohn Marino #include "acs.h"
23*a1157835SDaniel Fojt #include "ieee802_11.h"
24*a1157835SDaniel Fojt #include "beacon.h"
253ff40c12SJohn Marino #include "hw_features.h"
263ff40c12SJohn Marino
273ff40c12SJohn Marino
hostapd_free_hw_features(struct hostapd_hw_modes * hw_features,size_t num_hw_features)283ff40c12SJohn Marino void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
293ff40c12SJohn Marino size_t num_hw_features)
303ff40c12SJohn Marino {
313ff40c12SJohn Marino size_t i;
323ff40c12SJohn Marino
333ff40c12SJohn Marino if (hw_features == NULL)
343ff40c12SJohn Marino return;
353ff40c12SJohn Marino
363ff40c12SJohn Marino for (i = 0; i < num_hw_features; i++) {
373ff40c12SJohn Marino os_free(hw_features[i].channels);
383ff40c12SJohn Marino os_free(hw_features[i].rates);
393ff40c12SJohn Marino }
403ff40c12SJohn Marino
413ff40c12SJohn Marino os_free(hw_features);
423ff40c12SJohn Marino }
433ff40c12SJohn Marino
443ff40c12SJohn Marino
453ff40c12SJohn Marino #ifndef CONFIG_NO_STDOUT_DEBUG
dfs_info(struct hostapd_channel_data * chan)463ff40c12SJohn Marino static char * dfs_info(struct hostapd_channel_data *chan)
473ff40c12SJohn Marino {
483ff40c12SJohn Marino static char info[256];
493ff40c12SJohn Marino char *state;
503ff40c12SJohn Marino
513ff40c12SJohn Marino switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
523ff40c12SJohn Marino case HOSTAPD_CHAN_DFS_UNKNOWN:
533ff40c12SJohn Marino state = "unknown";
543ff40c12SJohn Marino break;
553ff40c12SJohn Marino case HOSTAPD_CHAN_DFS_USABLE:
563ff40c12SJohn Marino state = "usable";
573ff40c12SJohn Marino break;
583ff40c12SJohn Marino case HOSTAPD_CHAN_DFS_UNAVAILABLE:
593ff40c12SJohn Marino state = "unavailable";
603ff40c12SJohn Marino break;
613ff40c12SJohn Marino case HOSTAPD_CHAN_DFS_AVAILABLE:
623ff40c12SJohn Marino state = "available";
633ff40c12SJohn Marino break;
643ff40c12SJohn Marino default:
653ff40c12SJohn Marino return "";
663ff40c12SJohn Marino }
673ff40c12SJohn Marino os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
683ff40c12SJohn Marino info[sizeof(info) - 1] = '\0';
693ff40c12SJohn Marino
703ff40c12SJohn Marino return info;
713ff40c12SJohn Marino }
723ff40c12SJohn Marino #endif /* CONFIG_NO_STDOUT_DEBUG */
733ff40c12SJohn Marino
743ff40c12SJohn Marino
hostapd_get_hw_features(struct hostapd_iface * iface)753ff40c12SJohn Marino int hostapd_get_hw_features(struct hostapd_iface *iface)
763ff40c12SJohn Marino {
773ff40c12SJohn Marino struct hostapd_data *hapd = iface->bss[0];
78*a1157835SDaniel Fojt int i, j;
793ff40c12SJohn Marino u16 num_modes, flags;
803ff40c12SJohn Marino struct hostapd_hw_modes *modes;
81*a1157835SDaniel Fojt u8 dfs_domain;
823ff40c12SJohn Marino
833ff40c12SJohn Marino if (hostapd_drv_none(hapd))
843ff40c12SJohn Marino return -1;
85*a1157835SDaniel Fojt modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
86*a1157835SDaniel Fojt &dfs_domain);
873ff40c12SJohn Marino if (modes == NULL) {
883ff40c12SJohn Marino hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
893ff40c12SJohn Marino HOSTAPD_LEVEL_DEBUG,
903ff40c12SJohn Marino "Fetching hardware channel/rate support not "
913ff40c12SJohn Marino "supported.");
923ff40c12SJohn Marino return -1;
933ff40c12SJohn Marino }
943ff40c12SJohn Marino
953ff40c12SJohn Marino iface->hw_flags = flags;
96*a1157835SDaniel Fojt iface->dfs_domain = dfs_domain;
973ff40c12SJohn Marino
983ff40c12SJohn Marino hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
993ff40c12SJohn Marino iface->hw_features = modes;
1003ff40c12SJohn Marino iface->num_hw_features = num_modes;
1013ff40c12SJohn Marino
1023ff40c12SJohn Marino for (i = 0; i < num_modes; i++) {
1033ff40c12SJohn Marino struct hostapd_hw_modes *feature = &modes[i];
1043ff40c12SJohn Marino int dfs_enabled = hapd->iconf->ieee80211h &&
1053ff40c12SJohn Marino (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
1063ff40c12SJohn Marino
1073ff40c12SJohn Marino /* set flag for channels we can use in current regulatory
1083ff40c12SJohn Marino * domain */
1093ff40c12SJohn Marino for (j = 0; j < feature->num_channels; j++) {
1103ff40c12SJohn Marino int dfs = 0;
1113ff40c12SJohn Marino
1123ff40c12SJohn Marino /*
1133ff40c12SJohn Marino * Disable all channels that are marked not to allow
114*a1157835SDaniel Fojt * to initiate radiation (a.k.a. passive scan and no
115*a1157835SDaniel Fojt * IBSS).
1163ff40c12SJohn Marino * Use radar channels only if the driver supports DFS.
1173ff40c12SJohn Marino */
1183ff40c12SJohn Marino if ((feature->channels[j].flag &
1193ff40c12SJohn Marino HOSTAPD_CHAN_RADAR) && dfs_enabled) {
1203ff40c12SJohn Marino dfs = 1;
121*a1157835SDaniel Fojt } else if (((feature->channels[j].flag &
122*a1157835SDaniel Fojt HOSTAPD_CHAN_RADAR) &&
123*a1157835SDaniel Fojt !(iface->drv_flags &
124*a1157835SDaniel Fojt WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
125*a1157835SDaniel Fojt (feature->channels[j].flag &
126*a1157835SDaniel Fojt HOSTAPD_CHAN_NO_IR)) {
1273ff40c12SJohn Marino feature->channels[j].flag |=
1283ff40c12SJohn Marino HOSTAPD_CHAN_DISABLED;
1293ff40c12SJohn Marino }
1303ff40c12SJohn Marino
1313ff40c12SJohn Marino if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
1323ff40c12SJohn Marino continue;
1333ff40c12SJohn Marino
1343ff40c12SJohn Marino wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
1353ff40c12SJohn Marino "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
1363ff40c12SJohn Marino feature->mode,
1373ff40c12SJohn Marino feature->channels[j].chan,
1383ff40c12SJohn Marino feature->channels[j].freq,
1393ff40c12SJohn Marino feature->channels[j].max_tx_power,
1403ff40c12SJohn Marino dfs ? dfs_info(&feature->channels[j]) : "");
1413ff40c12SJohn Marino }
1423ff40c12SJohn Marino }
1433ff40c12SJohn Marino
144*a1157835SDaniel Fojt return 0;
1453ff40c12SJohn Marino }
1463ff40c12SJohn Marino
1473ff40c12SJohn Marino
hostapd_prepare_rates(struct hostapd_iface * iface,struct hostapd_hw_modes * mode)1483ff40c12SJohn Marino int hostapd_prepare_rates(struct hostapd_iface *iface,
1493ff40c12SJohn Marino struct hostapd_hw_modes *mode)
1503ff40c12SJohn Marino {
1513ff40c12SJohn Marino int i, num_basic_rates = 0;
1523ff40c12SJohn Marino int basic_rates_a[] = { 60, 120, 240, -1 };
1533ff40c12SJohn Marino int basic_rates_b[] = { 10, 20, -1 };
1543ff40c12SJohn Marino int basic_rates_g[] = { 10, 20, 55, 110, -1 };
1553ff40c12SJohn Marino int *basic_rates;
1563ff40c12SJohn Marino
1573ff40c12SJohn Marino if (iface->conf->basic_rates)
1583ff40c12SJohn Marino basic_rates = iface->conf->basic_rates;
1593ff40c12SJohn Marino else switch (mode->mode) {
1603ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211A:
1613ff40c12SJohn Marino basic_rates = basic_rates_a;
1623ff40c12SJohn Marino break;
1633ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211B:
1643ff40c12SJohn Marino basic_rates = basic_rates_b;
1653ff40c12SJohn Marino break;
1663ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211G:
1673ff40c12SJohn Marino basic_rates = basic_rates_g;
1683ff40c12SJohn Marino break;
1693ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211AD:
1703ff40c12SJohn Marino return 0; /* No basic rates for 11ad */
1713ff40c12SJohn Marino default:
1723ff40c12SJohn Marino return -1;
1733ff40c12SJohn Marino }
1743ff40c12SJohn Marino
1753ff40c12SJohn Marino i = 0;
1763ff40c12SJohn Marino while (basic_rates[i] >= 0)
1773ff40c12SJohn Marino i++;
1783ff40c12SJohn Marino if (i)
1793ff40c12SJohn Marino i++; /* -1 termination */
1803ff40c12SJohn Marino os_free(iface->basic_rates);
1813ff40c12SJohn Marino iface->basic_rates = os_malloc(i * sizeof(int));
1823ff40c12SJohn Marino if (iface->basic_rates)
1833ff40c12SJohn Marino os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
1843ff40c12SJohn Marino
1853ff40c12SJohn Marino os_free(iface->current_rates);
1863ff40c12SJohn Marino iface->num_rates = 0;
1873ff40c12SJohn Marino
1883ff40c12SJohn Marino iface->current_rates =
1893ff40c12SJohn Marino os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
1903ff40c12SJohn Marino if (!iface->current_rates) {
1913ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
1923ff40c12SJohn Marino "table.");
1933ff40c12SJohn Marino return -1;
1943ff40c12SJohn Marino }
1953ff40c12SJohn Marino
1963ff40c12SJohn Marino for (i = 0; i < mode->num_rates; i++) {
1973ff40c12SJohn Marino struct hostapd_rate_data *rate;
1983ff40c12SJohn Marino
1993ff40c12SJohn Marino if (iface->conf->supported_rates &&
2003ff40c12SJohn Marino !hostapd_rate_found(iface->conf->supported_rates,
2013ff40c12SJohn Marino mode->rates[i]))
2023ff40c12SJohn Marino continue;
2033ff40c12SJohn Marino
2043ff40c12SJohn Marino rate = &iface->current_rates[iface->num_rates];
2053ff40c12SJohn Marino rate->rate = mode->rates[i];
2063ff40c12SJohn Marino if (hostapd_rate_found(basic_rates, rate->rate)) {
2073ff40c12SJohn Marino rate->flags |= HOSTAPD_RATE_BASIC;
2083ff40c12SJohn Marino num_basic_rates++;
2093ff40c12SJohn Marino }
2103ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
2113ff40c12SJohn Marino iface->num_rates, rate->rate, rate->flags);
2123ff40c12SJohn Marino iface->num_rates++;
2133ff40c12SJohn Marino }
2143ff40c12SJohn Marino
2153ff40c12SJohn Marino if ((iface->num_rates == 0 || num_basic_rates == 0) &&
2163ff40c12SJohn Marino (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
2173ff40c12SJohn Marino wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
2183ff40c12SJohn Marino "rate sets (%d,%d).",
2193ff40c12SJohn Marino iface->num_rates, num_basic_rates);
2203ff40c12SJohn Marino return -1;
2213ff40c12SJohn Marino }
2223ff40c12SJohn Marino
2233ff40c12SJohn Marino return 0;
2243ff40c12SJohn Marino }
2253ff40c12SJohn Marino
2263ff40c12SJohn Marino
2273ff40c12SJohn Marino #ifdef CONFIG_IEEE80211N
ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface * iface)2283ff40c12SJohn Marino static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
2293ff40c12SJohn Marino {
230*a1157835SDaniel Fojt int pri_chan, sec_chan;
2313ff40c12SJohn Marino
232*a1157835SDaniel Fojt pri_chan = iface->conf->channel;
233*a1157835SDaniel Fojt sec_chan = pri_chan + iface->conf->secondary_channel * 4;
2343ff40c12SJohn Marino
235*a1157835SDaniel Fojt return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
2363ff40c12SJohn Marino sec_chan);
2373ff40c12SJohn Marino }
2383ff40c12SJohn Marino
2393ff40c12SJohn Marino
ieee80211n_switch_pri_sec(struct hostapd_iface * iface)2403ff40c12SJohn Marino static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
2413ff40c12SJohn Marino {
2423ff40c12SJohn Marino if (iface->conf->secondary_channel > 0) {
2433ff40c12SJohn Marino iface->conf->channel += 4;
2443ff40c12SJohn Marino iface->conf->secondary_channel = -1;
2453ff40c12SJohn Marino } else {
2463ff40c12SJohn Marino iface->conf->channel -= 4;
2473ff40c12SJohn Marino iface->conf->secondary_channel = 1;
2483ff40c12SJohn Marino }
2493ff40c12SJohn Marino }
2503ff40c12SJohn Marino
2513ff40c12SJohn Marino
ieee80211n_check_40mhz_5g(struct hostapd_iface * iface,struct wpa_scan_results * scan_res)2523ff40c12SJohn Marino static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
2533ff40c12SJohn Marino struct wpa_scan_results *scan_res)
2543ff40c12SJohn Marino {
255*a1157835SDaniel Fojt int pri_chan, sec_chan;
256*a1157835SDaniel Fojt int res;
2573ff40c12SJohn Marino
2583ff40c12SJohn Marino pri_chan = iface->conf->channel;
259*a1157835SDaniel Fojt sec_chan = pri_chan + iface->conf->secondary_channel * 4;
2603ff40c12SJohn Marino
261*a1157835SDaniel Fojt res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
262*a1157835SDaniel Fojt
263*a1157835SDaniel Fojt if (res == 2) {
264*a1157835SDaniel Fojt if (iface->conf->no_pri_sec_switch) {
265*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
266*a1157835SDaniel Fojt "Cannot switch PRI/SEC channels due to local constraint");
267*a1157835SDaniel Fojt } else {
2683ff40c12SJohn Marino ieee80211n_switch_pri_sec(iface);
2693ff40c12SJohn Marino }
2703ff40c12SJohn Marino }
2713ff40c12SJohn Marino
272*a1157835SDaniel Fojt return !!res;
2733ff40c12SJohn Marino }
2743ff40c12SJohn Marino
2753ff40c12SJohn Marino
ieee80211n_check_40mhz_2g4(struct hostapd_iface * iface,struct wpa_scan_results * scan_res)2763ff40c12SJohn Marino static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
2773ff40c12SJohn Marino struct wpa_scan_results *scan_res)
2783ff40c12SJohn Marino {
279*a1157835SDaniel Fojt int pri_chan, sec_chan;
2803ff40c12SJohn Marino
281*a1157835SDaniel Fojt pri_chan = iface->conf->channel;
282*a1157835SDaniel Fojt sec_chan = pri_chan + iface->conf->secondary_channel * 4;
2833ff40c12SJohn Marino
284*a1157835SDaniel Fojt return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
285*a1157835SDaniel Fojt sec_chan);
2863ff40c12SJohn Marino }
2873ff40c12SJohn Marino
2883ff40c12SJohn Marino
ieee80211n_check_scan(struct hostapd_iface * iface)2893ff40c12SJohn Marino static void ieee80211n_check_scan(struct hostapd_iface *iface)
2903ff40c12SJohn Marino {
2913ff40c12SJohn Marino struct wpa_scan_results *scan_res;
2923ff40c12SJohn Marino int oper40;
2933ff40c12SJohn Marino int res;
2943ff40c12SJohn Marino
2953ff40c12SJohn Marino /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
2963ff40c12SJohn Marino * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
2973ff40c12SJohn Marino
2983ff40c12SJohn Marino iface->scan_cb = NULL;
2993ff40c12SJohn Marino
3003ff40c12SJohn Marino scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
3013ff40c12SJohn Marino if (scan_res == NULL) {
3023ff40c12SJohn Marino hostapd_setup_interface_complete(iface, 1);
3033ff40c12SJohn Marino return;
3043ff40c12SJohn Marino }
3053ff40c12SJohn Marino
3063ff40c12SJohn Marino if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
3073ff40c12SJohn Marino oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
3083ff40c12SJohn Marino else
3093ff40c12SJohn Marino oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
3103ff40c12SJohn Marino wpa_scan_results_free(scan_res);
3113ff40c12SJohn Marino
312*a1157835SDaniel Fojt iface->secondary_ch = iface->conf->secondary_channel;
3133ff40c12SJohn Marino if (!oper40) {
3143ff40c12SJohn Marino wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
3153ff40c12SJohn Marino "channel pri=%d sec=%d based on overlapping BSSes",
3163ff40c12SJohn Marino iface->conf->channel,
3173ff40c12SJohn Marino iface->conf->channel +
3183ff40c12SJohn Marino iface->conf->secondary_channel * 4);
3193ff40c12SJohn Marino iface->conf->secondary_channel = 0;
320*a1157835SDaniel Fojt if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
321*a1157835SDaniel Fojt /*
322*a1157835SDaniel Fojt * TODO: Could consider scheduling another scan to check
323*a1157835SDaniel Fojt * if channel width can be changed if no coex reports
324*a1157835SDaniel Fojt * are received from associating stations.
325*a1157835SDaniel Fojt */
326*a1157835SDaniel Fojt }
3273ff40c12SJohn Marino }
3283ff40c12SJohn Marino
3293ff40c12SJohn Marino res = ieee80211n_allowed_ht40_channel_pair(iface);
330*a1157835SDaniel Fojt if (!res) {
331*a1157835SDaniel Fojt iface->conf->secondary_channel = 0;
332*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
333*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
334*a1157835SDaniel Fojt hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT);
335*a1157835SDaniel Fojt res = 1;
336*a1157835SDaniel Fojt wpa_printf(MSG_INFO, "Fallback to 20 MHz");
337*a1157835SDaniel Fojt }
338*a1157835SDaniel Fojt
3393ff40c12SJohn Marino hostapd_setup_interface_complete(iface, !res);
3403ff40c12SJohn Marino }
3413ff40c12SJohn Marino
3423ff40c12SJohn Marino
ieee80211n_scan_channels_2g4(struct hostapd_iface * iface,struct wpa_driver_scan_params * params)3433ff40c12SJohn Marino static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
3443ff40c12SJohn Marino struct wpa_driver_scan_params *params)
3453ff40c12SJohn Marino {
3463ff40c12SJohn Marino /* Scan only the affected frequency range */
3473ff40c12SJohn Marino int pri_freq, sec_freq;
3483ff40c12SJohn Marino int affected_start, affected_end;
3493ff40c12SJohn Marino int i, pos;
3503ff40c12SJohn Marino struct hostapd_hw_modes *mode;
3513ff40c12SJohn Marino
3523ff40c12SJohn Marino if (iface->current_mode == NULL)
3533ff40c12SJohn Marino return;
3543ff40c12SJohn Marino
3553ff40c12SJohn Marino pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
3563ff40c12SJohn Marino if (iface->conf->secondary_channel > 0)
3573ff40c12SJohn Marino sec_freq = pri_freq + 20;
3583ff40c12SJohn Marino else
3593ff40c12SJohn Marino sec_freq = pri_freq - 20;
360*a1157835SDaniel Fojt /*
361*a1157835SDaniel Fojt * Note: Need to find the PRI channel also in cases where the affected
362*a1157835SDaniel Fojt * channel is the SEC channel of a 40 MHz BSS, so need to include the
363*a1157835SDaniel Fojt * scanning coverage here to be 40 MHz from the center frequency.
364*a1157835SDaniel Fojt */
365*a1157835SDaniel Fojt affected_start = (pri_freq + sec_freq) / 2 - 40;
366*a1157835SDaniel Fojt affected_end = (pri_freq + sec_freq) / 2 + 40;
3673ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
3683ff40c12SJohn Marino affected_start, affected_end);
3693ff40c12SJohn Marino
3703ff40c12SJohn Marino mode = iface->current_mode;
3713ff40c12SJohn Marino params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
3723ff40c12SJohn Marino if (params->freqs == NULL)
3733ff40c12SJohn Marino return;
3743ff40c12SJohn Marino pos = 0;
3753ff40c12SJohn Marino
3763ff40c12SJohn Marino for (i = 0; i < mode->num_channels; i++) {
3773ff40c12SJohn Marino struct hostapd_channel_data *chan = &mode->channels[i];
3783ff40c12SJohn Marino if (chan->flag & HOSTAPD_CHAN_DISABLED)
3793ff40c12SJohn Marino continue;
3803ff40c12SJohn Marino if (chan->freq < affected_start ||
3813ff40c12SJohn Marino chan->freq > affected_end)
3823ff40c12SJohn Marino continue;
3833ff40c12SJohn Marino params->freqs[pos++] = chan->freq;
3843ff40c12SJohn Marino }
3853ff40c12SJohn Marino }
3863ff40c12SJohn Marino
3873ff40c12SJohn Marino
ieee80211n_scan_channels_5g(struct hostapd_iface * iface,struct wpa_driver_scan_params * params)3883ff40c12SJohn Marino static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
3893ff40c12SJohn Marino struct wpa_driver_scan_params *params)
3903ff40c12SJohn Marino {
3913ff40c12SJohn Marino /* Scan only the affected frequency range */
3923ff40c12SJohn Marino int pri_freq;
3933ff40c12SJohn Marino int affected_start, affected_end;
3943ff40c12SJohn Marino int i, pos;
3953ff40c12SJohn Marino struct hostapd_hw_modes *mode;
3963ff40c12SJohn Marino
3973ff40c12SJohn Marino if (iface->current_mode == NULL)
3983ff40c12SJohn Marino return;
3993ff40c12SJohn Marino
4003ff40c12SJohn Marino pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
4013ff40c12SJohn Marino if (iface->conf->secondary_channel > 0) {
4023ff40c12SJohn Marino affected_start = pri_freq - 10;
4033ff40c12SJohn Marino affected_end = pri_freq + 30;
4043ff40c12SJohn Marino } else {
4053ff40c12SJohn Marino affected_start = pri_freq - 30;
4063ff40c12SJohn Marino affected_end = pri_freq + 10;
4073ff40c12SJohn Marino }
4083ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
4093ff40c12SJohn Marino affected_start, affected_end);
4103ff40c12SJohn Marino
4113ff40c12SJohn Marino mode = iface->current_mode;
4123ff40c12SJohn Marino params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
4133ff40c12SJohn Marino if (params->freqs == NULL)
4143ff40c12SJohn Marino return;
4153ff40c12SJohn Marino pos = 0;
4163ff40c12SJohn Marino
4173ff40c12SJohn Marino for (i = 0; i < mode->num_channels; i++) {
4183ff40c12SJohn Marino struct hostapd_channel_data *chan = &mode->channels[i];
4193ff40c12SJohn Marino if (chan->flag & HOSTAPD_CHAN_DISABLED)
4203ff40c12SJohn Marino continue;
4213ff40c12SJohn Marino if (chan->freq < affected_start ||
4223ff40c12SJohn Marino chan->freq > affected_end)
4233ff40c12SJohn Marino continue;
4243ff40c12SJohn Marino params->freqs[pos++] = chan->freq;
4253ff40c12SJohn Marino }
4263ff40c12SJohn Marino }
4273ff40c12SJohn Marino
4283ff40c12SJohn Marino
ap_ht40_scan_retry(void * eloop_data,void * user_data)429*a1157835SDaniel Fojt static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
430*a1157835SDaniel Fojt {
431*a1157835SDaniel Fojt #define HT2040_COEX_SCAN_RETRY 15
432*a1157835SDaniel Fojt struct hostapd_iface *iface = eloop_data;
433*a1157835SDaniel Fojt struct wpa_driver_scan_params params;
434*a1157835SDaniel Fojt int ret;
435*a1157835SDaniel Fojt
436*a1157835SDaniel Fojt os_memset(¶ms, 0, sizeof(params));
437*a1157835SDaniel Fojt if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
438*a1157835SDaniel Fojt ieee80211n_scan_channels_2g4(iface, ¶ms);
439*a1157835SDaniel Fojt else
440*a1157835SDaniel Fojt ieee80211n_scan_channels_5g(iface, ¶ms);
441*a1157835SDaniel Fojt
442*a1157835SDaniel Fojt ret = hostapd_driver_scan(iface->bss[0], ¶ms);
443*a1157835SDaniel Fojt iface->num_ht40_scan_tries++;
444*a1157835SDaniel Fojt os_free(params.freqs);
445*a1157835SDaniel Fojt
446*a1157835SDaniel Fojt if (ret == -EBUSY &&
447*a1157835SDaniel Fojt iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
448*a1157835SDaniel Fojt wpa_printf(MSG_ERROR,
449*a1157835SDaniel Fojt "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
450*a1157835SDaniel Fojt ret, strerror(-ret), iface->num_ht40_scan_tries);
451*a1157835SDaniel Fojt eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
452*a1157835SDaniel Fojt return;
453*a1157835SDaniel Fojt }
454*a1157835SDaniel Fojt
455*a1157835SDaniel Fojt if (ret == 0) {
456*a1157835SDaniel Fojt iface->scan_cb = ieee80211n_check_scan;
457*a1157835SDaniel Fojt return;
458*a1157835SDaniel Fojt }
459*a1157835SDaniel Fojt
460*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
461*a1157835SDaniel Fojt "Failed to request a scan in device, bringing up in HT20 mode");
462*a1157835SDaniel Fojt iface->conf->secondary_channel = 0;
463*a1157835SDaniel Fojt iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
464*a1157835SDaniel Fojt hostapd_setup_interface_complete(iface, 0);
465*a1157835SDaniel Fojt }
466*a1157835SDaniel Fojt
467*a1157835SDaniel Fojt
hostapd_stop_setup_timers(struct hostapd_iface * iface)468*a1157835SDaniel Fojt void hostapd_stop_setup_timers(struct hostapd_iface *iface)
469*a1157835SDaniel Fojt {
470*a1157835SDaniel Fojt eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
471*a1157835SDaniel Fojt }
472*a1157835SDaniel Fojt
473*a1157835SDaniel Fojt
ieee80211n_check_40mhz(struct hostapd_iface * iface)4743ff40c12SJohn Marino static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
4753ff40c12SJohn Marino {
4763ff40c12SJohn Marino struct wpa_driver_scan_params params;
477*a1157835SDaniel Fojt int ret;
4783ff40c12SJohn Marino
479*a1157835SDaniel Fojt /* Check that HT40 is used and PRI / SEC switch is allowed */
480*a1157835SDaniel Fojt if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
481*a1157835SDaniel Fojt return 0;
4823ff40c12SJohn Marino
4833ff40c12SJohn Marino hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
4843ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
4853ff40c12SJohn Marino "40 MHz channel");
4863ff40c12SJohn Marino os_memset(¶ms, 0, sizeof(params));
4873ff40c12SJohn Marino if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
4883ff40c12SJohn Marino ieee80211n_scan_channels_2g4(iface, ¶ms);
4893ff40c12SJohn Marino else
4903ff40c12SJohn Marino ieee80211n_scan_channels_5g(iface, ¶ms);
491*a1157835SDaniel Fojt
492*a1157835SDaniel Fojt ret = hostapd_driver_scan(iface->bss[0], ¶ms);
4933ff40c12SJohn Marino os_free(params.freqs);
494*a1157835SDaniel Fojt
495*a1157835SDaniel Fojt if (ret == -EBUSY) {
496*a1157835SDaniel Fojt wpa_printf(MSG_ERROR,
497*a1157835SDaniel Fojt "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
498*a1157835SDaniel Fojt ret, strerror(-ret));
499*a1157835SDaniel Fojt iface->num_ht40_scan_tries = 1;
500*a1157835SDaniel Fojt eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
501*a1157835SDaniel Fojt eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
502*a1157835SDaniel Fojt return 1;
503*a1157835SDaniel Fojt }
504*a1157835SDaniel Fojt
505*a1157835SDaniel Fojt if (ret < 0) {
506*a1157835SDaniel Fojt wpa_printf(MSG_ERROR,
507*a1157835SDaniel Fojt "Failed to request a scan of neighboring BSSes ret=%d (%s)",
508*a1157835SDaniel Fojt ret, strerror(-ret));
5093ff40c12SJohn Marino return -1;
5103ff40c12SJohn Marino }
5113ff40c12SJohn Marino
5123ff40c12SJohn Marino iface->scan_cb = ieee80211n_check_scan;
5133ff40c12SJohn Marino return 1;
5143ff40c12SJohn Marino }
5153ff40c12SJohn Marino
5163ff40c12SJohn Marino
ieee80211n_supported_ht_capab(struct hostapd_iface * iface)5173ff40c12SJohn Marino static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
5183ff40c12SJohn Marino {
5193ff40c12SJohn Marino u16 hw = iface->current_mode->ht_capab;
5203ff40c12SJohn Marino u16 conf = iface->conf->ht_capab;
5213ff40c12SJohn Marino
5223ff40c12SJohn Marino if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
5233ff40c12SJohn Marino !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
5243ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5253ff40c12SJohn Marino "HT capability [LDPC]");
5263ff40c12SJohn Marino return 0;
5273ff40c12SJohn Marino }
5283ff40c12SJohn Marino
529*a1157835SDaniel Fojt /*
530*a1157835SDaniel Fojt * Driver ACS chosen channel may not be HT40 due to internal driver
531*a1157835SDaniel Fojt * restrictions.
532*a1157835SDaniel Fojt */
533*a1157835SDaniel Fojt if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
5343ff40c12SJohn Marino !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
5353ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5363ff40c12SJohn Marino "HT capability [HT40*]");
5373ff40c12SJohn Marino return 0;
5383ff40c12SJohn Marino }
5393ff40c12SJohn Marino
540*a1157835SDaniel Fojt switch (conf & HT_CAP_INFO_SMPS_MASK) {
541*a1157835SDaniel Fojt case HT_CAP_INFO_SMPS_STATIC:
542*a1157835SDaniel Fojt if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) {
543*a1157835SDaniel Fojt wpa_printf(MSG_ERROR,
544*a1157835SDaniel Fojt "Driver does not support configured HT capability [SMPS-STATIC]");
5453ff40c12SJohn Marino return 0;
5463ff40c12SJohn Marino }
547*a1157835SDaniel Fojt break;
548*a1157835SDaniel Fojt case HT_CAP_INFO_SMPS_DYNAMIC:
549*a1157835SDaniel Fojt if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) {
550*a1157835SDaniel Fojt wpa_printf(MSG_ERROR,
551*a1157835SDaniel Fojt "Driver does not support configured HT capability [SMPS-DYNAMIC]");
552*a1157835SDaniel Fojt return 0;
553*a1157835SDaniel Fojt }
554*a1157835SDaniel Fojt break;
555*a1157835SDaniel Fojt case HT_CAP_INFO_SMPS_DISABLED:
556*a1157835SDaniel Fojt default:
557*a1157835SDaniel Fojt break;
558*a1157835SDaniel Fojt }
5593ff40c12SJohn Marino
5603ff40c12SJohn Marino if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
5613ff40c12SJohn Marino !(hw & HT_CAP_INFO_GREEN_FIELD)) {
5623ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5633ff40c12SJohn Marino "HT capability [GF]");
5643ff40c12SJohn Marino return 0;
5653ff40c12SJohn Marino }
5663ff40c12SJohn Marino
5673ff40c12SJohn Marino if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
5683ff40c12SJohn Marino !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
5693ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5703ff40c12SJohn Marino "HT capability [SHORT-GI-20]");
5713ff40c12SJohn Marino return 0;
5723ff40c12SJohn Marino }
5733ff40c12SJohn Marino
5743ff40c12SJohn Marino if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
5753ff40c12SJohn Marino !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
5763ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5773ff40c12SJohn Marino "HT capability [SHORT-GI-40]");
5783ff40c12SJohn Marino return 0;
5793ff40c12SJohn Marino }
5803ff40c12SJohn Marino
5813ff40c12SJohn Marino if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
5823ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5833ff40c12SJohn Marino "HT capability [TX-STBC]");
5843ff40c12SJohn Marino return 0;
5853ff40c12SJohn Marino }
5863ff40c12SJohn Marino
5873ff40c12SJohn Marino if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
5883ff40c12SJohn Marino (hw & HT_CAP_INFO_RX_STBC_MASK)) {
5893ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5903ff40c12SJohn Marino "HT capability [RX-STBC*]");
5913ff40c12SJohn Marino return 0;
5923ff40c12SJohn Marino }
5933ff40c12SJohn Marino
5943ff40c12SJohn Marino if ((conf & HT_CAP_INFO_DELAYED_BA) &&
5953ff40c12SJohn Marino !(hw & HT_CAP_INFO_DELAYED_BA)) {
5963ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
5973ff40c12SJohn Marino "HT capability [DELAYED-BA]");
5983ff40c12SJohn Marino return 0;
5993ff40c12SJohn Marino }
6003ff40c12SJohn Marino
6013ff40c12SJohn Marino if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
6023ff40c12SJohn Marino !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
6033ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
6043ff40c12SJohn Marino "HT capability [MAX-AMSDU-7935]");
6053ff40c12SJohn Marino return 0;
6063ff40c12SJohn Marino }
6073ff40c12SJohn Marino
6083ff40c12SJohn Marino if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
6093ff40c12SJohn Marino !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
6103ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
6113ff40c12SJohn Marino "HT capability [DSSS_CCK-40]");
6123ff40c12SJohn Marino return 0;
6133ff40c12SJohn Marino }
6143ff40c12SJohn Marino
6153ff40c12SJohn Marino if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
6163ff40c12SJohn Marino !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
6173ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Driver does not support configured "
6183ff40c12SJohn Marino "HT capability [LSIG-TXOP-PROT]");
6193ff40c12SJohn Marino return 0;
6203ff40c12SJohn Marino }
6213ff40c12SJohn Marino
6223ff40c12SJohn Marino return 1;
6233ff40c12SJohn Marino }
6243ff40c12SJohn Marino
6253ff40c12SJohn Marino
6263ff40c12SJohn Marino #ifdef CONFIG_IEEE80211AC
ieee80211ac_supported_vht_capab(struct hostapd_iface * iface)6273ff40c12SJohn Marino static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
6283ff40c12SJohn Marino {
629*a1157835SDaniel Fojt struct hostapd_hw_modes *mode = iface->current_mode;
630*a1157835SDaniel Fojt u32 hw = mode->vht_capab;
6313ff40c12SJohn Marino u32 conf = iface->conf->vht_capab;
6323ff40c12SJohn Marino
6333ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
6343ff40c12SJohn Marino hw, conf);
6353ff40c12SJohn Marino
636*a1157835SDaniel Fojt if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
637*a1157835SDaniel Fojt iface->conf->bss[0]->vendor_vht &&
638*a1157835SDaniel Fojt mode->vht_capab == 0 && iface->hw_features) {
639*a1157835SDaniel Fojt int i;
6403ff40c12SJohn Marino
641*a1157835SDaniel Fojt for (i = 0; i < iface->num_hw_features; i++) {
642*a1157835SDaniel Fojt if (iface->hw_features[i].mode ==
643*a1157835SDaniel Fojt HOSTAPD_MODE_IEEE80211A) {
644*a1157835SDaniel Fojt mode = &iface->hw_features[i];
645*a1157835SDaniel Fojt hw = mode->vht_capab;
646*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
647*a1157835SDaniel Fojt "update hw vht capab based on 5 GHz band: 0x%x",
648*a1157835SDaniel Fojt hw);
649*a1157835SDaniel Fojt break;
650*a1157835SDaniel Fojt }
651*a1157835SDaniel Fojt }
652*a1157835SDaniel Fojt }
6533ff40c12SJohn Marino
654*a1157835SDaniel Fojt return ieee80211ac_cap_check(hw, conf);
6553ff40c12SJohn Marino }
6563ff40c12SJohn Marino #endif /* CONFIG_IEEE80211AC */
6573ff40c12SJohn Marino
658*a1157835SDaniel Fojt
659*a1157835SDaniel Fojt #ifdef CONFIG_IEEE80211AX
ieee80211ax_supported_he_capab(struct hostapd_iface * iface)660*a1157835SDaniel Fojt static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
661*a1157835SDaniel Fojt {
662*a1157835SDaniel Fojt return 1;
663*a1157835SDaniel Fojt }
664*a1157835SDaniel Fojt #endif /* CONFIG_IEEE80211AX */
665*a1157835SDaniel Fojt
6663ff40c12SJohn Marino #endif /* CONFIG_IEEE80211N */
6673ff40c12SJohn Marino
6683ff40c12SJohn Marino
hostapd_check_ht_capab(struct hostapd_iface * iface)6693ff40c12SJohn Marino int hostapd_check_ht_capab(struct hostapd_iface *iface)
6703ff40c12SJohn Marino {
6713ff40c12SJohn Marino #ifdef CONFIG_IEEE80211N
6723ff40c12SJohn Marino int ret;
6733ff40c12SJohn Marino if (!iface->conf->ieee80211n)
6743ff40c12SJohn Marino return 0;
675*a1157835SDaniel Fojt
676*a1157835SDaniel Fojt if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
677*a1157835SDaniel Fojt iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
678*a1157835SDaniel Fojt (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
679*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
680*a1157835SDaniel Fojt "Disable HT capability [DSSS_CCK-40] on 5 GHz band");
681*a1157835SDaniel Fojt iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
682*a1157835SDaniel Fojt }
683*a1157835SDaniel Fojt
6843ff40c12SJohn Marino if (!ieee80211n_supported_ht_capab(iface))
6853ff40c12SJohn Marino return -1;
686*a1157835SDaniel Fojt #ifdef CONFIG_IEEE80211AX
687*a1157835SDaniel Fojt if (iface->conf->ieee80211ax &&
688*a1157835SDaniel Fojt !ieee80211ax_supported_he_capab(iface))
689*a1157835SDaniel Fojt return -1;
690*a1157835SDaniel Fojt #endif /* CONFIG_IEEE80211AX */
6913ff40c12SJohn Marino #ifdef CONFIG_IEEE80211AC
692*a1157835SDaniel Fojt if (iface->conf->ieee80211ac &&
693*a1157835SDaniel Fojt !ieee80211ac_supported_vht_capab(iface))
6943ff40c12SJohn Marino return -1;
6953ff40c12SJohn Marino #endif /* CONFIG_IEEE80211AC */
6963ff40c12SJohn Marino ret = ieee80211n_check_40mhz(iface);
6973ff40c12SJohn Marino if (ret)
6983ff40c12SJohn Marino return ret;
6993ff40c12SJohn Marino if (!ieee80211n_allowed_ht40_channel_pair(iface))
7003ff40c12SJohn Marino return -1;
7013ff40c12SJohn Marino #endif /* CONFIG_IEEE80211N */
7023ff40c12SJohn Marino
7033ff40c12SJohn Marino return 0;
7043ff40c12SJohn Marino }
7053ff40c12SJohn Marino
7063ff40c12SJohn Marino
hostapd_is_usable_chan(struct hostapd_iface * iface,int channel,int primary)7073ff40c12SJohn Marino static int hostapd_is_usable_chan(struct hostapd_iface *iface,
7083ff40c12SJohn Marino int channel, int primary)
7093ff40c12SJohn Marino {
7103ff40c12SJohn Marino struct hostapd_channel_data *chan;
7113ff40c12SJohn Marino
712*a1157835SDaniel Fojt if (!iface->current_mode)
713*a1157835SDaniel Fojt return 0;
7143ff40c12SJohn Marino
715*a1157835SDaniel Fojt chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
716*a1157835SDaniel Fojt if (!chan)
717*a1157835SDaniel Fojt return 0;
718*a1157835SDaniel Fojt
719*a1157835SDaniel Fojt if ((primary && chan_pri_allowed(chan)) ||
720*a1157835SDaniel Fojt (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
7213ff40c12SJohn Marino return 1;
7223ff40c12SJohn Marino
723*a1157835SDaniel Fojt wpa_printf(MSG_INFO,
724*a1157835SDaniel Fojt "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
725*a1157835SDaniel Fojt channel, primary ? "primary" : "secondary",
726*a1157835SDaniel Fojt chan->flag,
727*a1157835SDaniel Fojt chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
7283ff40c12SJohn Marino chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
7293ff40c12SJohn Marino return 0;
7303ff40c12SJohn Marino }
7313ff40c12SJohn Marino
7323ff40c12SJohn Marino
hostapd_is_usable_chans(struct hostapd_iface * iface)7333ff40c12SJohn Marino static int hostapd_is_usable_chans(struct hostapd_iface *iface)
7343ff40c12SJohn Marino {
735*a1157835SDaniel Fojt int secondary_chan;
736*a1157835SDaniel Fojt struct hostapd_channel_data *pri_chan;
737*a1157835SDaniel Fojt
738*a1157835SDaniel Fojt pri_chan = hw_get_channel_chan(iface->current_mode,
739*a1157835SDaniel Fojt iface->conf->channel, NULL);
740*a1157835SDaniel Fojt if (!pri_chan)
741*a1157835SDaniel Fojt return 0;
742*a1157835SDaniel Fojt
7433ff40c12SJohn Marino if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
7443ff40c12SJohn Marino return 0;
7453ff40c12SJohn Marino
7463ff40c12SJohn Marino if (!iface->conf->secondary_channel)
7473ff40c12SJohn Marino return 1;
7483ff40c12SJohn Marino
749*a1157835SDaniel Fojt if (!iface->conf->ht40_plus_minus_allowed)
750*a1157835SDaniel Fojt return hostapd_is_usable_chan(
751*a1157835SDaniel Fojt iface, iface->conf->channel +
7523ff40c12SJohn Marino iface->conf->secondary_channel * 4, 0);
753*a1157835SDaniel Fojt
754*a1157835SDaniel Fojt /* Both HT40+ and HT40- are set, pick a valid secondary channel */
755*a1157835SDaniel Fojt secondary_chan = iface->conf->channel + 4;
756*a1157835SDaniel Fojt if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
757*a1157835SDaniel Fojt (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
758*a1157835SDaniel Fojt iface->conf->secondary_channel = 1;
759*a1157835SDaniel Fojt return 1;
760*a1157835SDaniel Fojt }
761*a1157835SDaniel Fojt
762*a1157835SDaniel Fojt secondary_chan = iface->conf->channel - 4;
763*a1157835SDaniel Fojt if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
764*a1157835SDaniel Fojt (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
765*a1157835SDaniel Fojt iface->conf->secondary_channel = -1;
766*a1157835SDaniel Fojt return 1;
767*a1157835SDaniel Fojt }
768*a1157835SDaniel Fojt
769*a1157835SDaniel Fojt return 0;
7703ff40c12SJohn Marino }
7713ff40c12SJohn Marino
7723ff40c12SJohn Marino
7733ff40c12SJohn Marino static enum hostapd_chan_status
hostapd_check_chans(struct hostapd_iface * iface)7743ff40c12SJohn Marino hostapd_check_chans(struct hostapd_iface *iface)
7753ff40c12SJohn Marino {
7763ff40c12SJohn Marino if (iface->conf->channel) {
7773ff40c12SJohn Marino if (hostapd_is_usable_chans(iface))
7783ff40c12SJohn Marino return HOSTAPD_CHAN_VALID;
7793ff40c12SJohn Marino else
7803ff40c12SJohn Marino return HOSTAPD_CHAN_INVALID;
7813ff40c12SJohn Marino }
7823ff40c12SJohn Marino
7833ff40c12SJohn Marino /*
7843ff40c12SJohn Marino * The user set channel=0 or channel=acs_survey
7853ff40c12SJohn Marino * which is used to trigger ACS.
7863ff40c12SJohn Marino */
7873ff40c12SJohn Marino
7883ff40c12SJohn Marino switch (acs_init(iface)) {
7893ff40c12SJohn Marino case HOSTAPD_CHAN_ACS:
7903ff40c12SJohn Marino return HOSTAPD_CHAN_ACS;
7913ff40c12SJohn Marino case HOSTAPD_CHAN_VALID:
7923ff40c12SJohn Marino case HOSTAPD_CHAN_INVALID:
7933ff40c12SJohn Marino default:
7943ff40c12SJohn Marino return HOSTAPD_CHAN_INVALID;
7953ff40c12SJohn Marino }
7963ff40c12SJohn Marino }
7973ff40c12SJohn Marino
7983ff40c12SJohn Marino
hostapd_notify_bad_chans(struct hostapd_iface * iface)7993ff40c12SJohn Marino static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
8003ff40c12SJohn Marino {
801*a1157835SDaniel Fojt if (!iface->current_mode) {
802*a1157835SDaniel Fojt hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
803*a1157835SDaniel Fojt HOSTAPD_LEVEL_WARNING,
804*a1157835SDaniel Fojt "Hardware does not support configured mode");
805*a1157835SDaniel Fojt return;
806*a1157835SDaniel Fojt }
8073ff40c12SJohn Marino hostapd_logger(iface->bss[0], NULL,
8083ff40c12SJohn Marino HOSTAPD_MODULE_IEEE80211,
8093ff40c12SJohn Marino HOSTAPD_LEVEL_WARNING,
8103ff40c12SJohn Marino "Configured channel (%d) not found from the "
8113ff40c12SJohn Marino "channel list of current mode (%d) %s",
8123ff40c12SJohn Marino iface->conf->channel,
8133ff40c12SJohn Marino iface->current_mode->mode,
8143ff40c12SJohn Marino hostapd_hw_mode_txt(iface->current_mode->mode));
8153ff40c12SJohn Marino hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
8163ff40c12SJohn Marino HOSTAPD_LEVEL_WARNING,
8173ff40c12SJohn Marino "Hardware does not support configured channel");
8183ff40c12SJohn Marino }
8193ff40c12SJohn Marino
8203ff40c12SJohn Marino
hostapd_acs_completed(struct hostapd_iface * iface,int err)8213ff40c12SJohn Marino int hostapd_acs_completed(struct hostapd_iface *iface, int err)
8223ff40c12SJohn Marino {
8233ff40c12SJohn Marino int ret = -1;
8243ff40c12SJohn Marino
8253ff40c12SJohn Marino if (err)
8263ff40c12SJohn Marino goto out;
8273ff40c12SJohn Marino
8283ff40c12SJohn Marino switch (hostapd_check_chans(iface)) {
8293ff40c12SJohn Marino case HOSTAPD_CHAN_VALID:
8303ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
8313ff40c12SJohn Marino ACS_EVENT_COMPLETED "freq=%d channel=%d",
8323ff40c12SJohn Marino hostapd_hw_get_freq(iface->bss[0],
8333ff40c12SJohn Marino iface->conf->channel),
8343ff40c12SJohn Marino iface->conf->channel);
8353ff40c12SJohn Marino break;
8363ff40c12SJohn Marino case HOSTAPD_CHAN_ACS:
8373ff40c12SJohn Marino wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
8383ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
8393ff40c12SJohn Marino hostapd_notify_bad_chans(iface);
8403ff40c12SJohn Marino goto out;
8413ff40c12SJohn Marino case HOSTAPD_CHAN_INVALID:
8423ff40c12SJohn Marino default:
8433ff40c12SJohn Marino wpa_printf(MSG_ERROR, "ACS picked unusable channels");
8443ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
8453ff40c12SJohn Marino hostapd_notify_bad_chans(iface);
8463ff40c12SJohn Marino goto out;
8473ff40c12SJohn Marino }
8483ff40c12SJohn Marino
8493ff40c12SJohn Marino ret = hostapd_check_ht_capab(iface);
8503ff40c12SJohn Marino if (ret < 0)
8513ff40c12SJohn Marino goto out;
8523ff40c12SJohn Marino if (ret == 1) {
8533ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
8543ff40c12SJohn Marino return 0;
8553ff40c12SJohn Marino }
8563ff40c12SJohn Marino
8573ff40c12SJohn Marino ret = 0;
8583ff40c12SJohn Marino out:
8593ff40c12SJohn Marino return hostapd_setup_interface_complete(iface, ret);
8603ff40c12SJohn Marino }
8613ff40c12SJohn Marino
8623ff40c12SJohn Marino
8633ff40c12SJohn Marino /**
8643ff40c12SJohn Marino * hostapd_select_hw_mode - Select the hardware mode
8653ff40c12SJohn Marino * @iface: Pointer to interface data.
8663ff40c12SJohn Marino * Returns: 0 on success, < 0 on failure
8673ff40c12SJohn Marino *
8683ff40c12SJohn Marino * Sets up the hardware mode, channel, rates, and passive scanning
8693ff40c12SJohn Marino * based on the configuration.
8703ff40c12SJohn Marino */
hostapd_select_hw_mode(struct hostapd_iface * iface)8713ff40c12SJohn Marino int hostapd_select_hw_mode(struct hostapd_iface *iface)
8723ff40c12SJohn Marino {
8733ff40c12SJohn Marino int i;
8743ff40c12SJohn Marino
8753ff40c12SJohn Marino if (iface->num_hw_features < 1)
8763ff40c12SJohn Marino return -1;
8773ff40c12SJohn Marino
878*a1157835SDaniel Fojt if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
879*a1157835SDaniel Fojt iface->conf->ieee80211n || iface->conf->ieee80211ac ||
880*a1157835SDaniel Fojt iface->conf->ieee80211ax) &&
881*a1157835SDaniel Fojt iface->conf->channel == 14) {
882*a1157835SDaniel Fojt wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14");
883*a1157835SDaniel Fojt iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
884*a1157835SDaniel Fojt iface->conf->ieee80211n = 0;
885*a1157835SDaniel Fojt iface->conf->ieee80211ac = 0;
886*a1157835SDaniel Fojt iface->conf->ieee80211ax = 0;
887*a1157835SDaniel Fojt }
888*a1157835SDaniel Fojt
8893ff40c12SJohn Marino iface->current_mode = NULL;
8903ff40c12SJohn Marino for (i = 0; i < iface->num_hw_features; i++) {
8913ff40c12SJohn Marino struct hostapd_hw_modes *mode = &iface->hw_features[i];
8923ff40c12SJohn Marino if (mode->mode == iface->conf->hw_mode) {
8933ff40c12SJohn Marino iface->current_mode = mode;
8943ff40c12SJohn Marino break;
8953ff40c12SJohn Marino }
8963ff40c12SJohn Marino }
8973ff40c12SJohn Marino
8983ff40c12SJohn Marino if (iface->current_mode == NULL) {
899*a1157835SDaniel Fojt if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
900*a1157835SDaniel Fojt !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
901*a1157835SDaniel Fojt {
902*a1157835SDaniel Fojt wpa_printf(MSG_ERROR,
903*a1157835SDaniel Fojt "Hardware does not support configured mode");
904*a1157835SDaniel Fojt hostapd_logger(iface->bss[0], NULL,
905*a1157835SDaniel Fojt HOSTAPD_MODULE_IEEE80211,
9063ff40c12SJohn Marino HOSTAPD_LEVEL_WARNING,
907*a1157835SDaniel Fojt "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
9083ff40c12SJohn Marino (int) iface->conf->hw_mode);
9093ff40c12SJohn Marino return -2;
9103ff40c12SJohn Marino }
911*a1157835SDaniel Fojt }
9123ff40c12SJohn Marino
9133ff40c12SJohn Marino switch (hostapd_check_chans(iface)) {
9143ff40c12SJohn Marino case HOSTAPD_CHAN_VALID:
9153ff40c12SJohn Marino return 0;
9163ff40c12SJohn Marino case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
9173ff40c12SJohn Marino return 1;
9183ff40c12SJohn Marino case HOSTAPD_CHAN_INVALID:
9193ff40c12SJohn Marino default:
9203ff40c12SJohn Marino hostapd_notify_bad_chans(iface);
9213ff40c12SJohn Marino return -3;
9223ff40c12SJohn Marino }
9233ff40c12SJohn Marino }
9243ff40c12SJohn Marino
9253ff40c12SJohn Marino
hostapd_hw_mode_txt(int mode)9263ff40c12SJohn Marino const char * hostapd_hw_mode_txt(int mode)
9273ff40c12SJohn Marino {
9283ff40c12SJohn Marino switch (mode) {
9293ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211A:
9303ff40c12SJohn Marino return "IEEE 802.11a";
9313ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211B:
9323ff40c12SJohn Marino return "IEEE 802.11b";
9333ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211G:
9343ff40c12SJohn Marino return "IEEE 802.11g";
9353ff40c12SJohn Marino case HOSTAPD_MODE_IEEE80211AD:
9363ff40c12SJohn Marino return "IEEE 802.11ad";
9373ff40c12SJohn Marino default:
9383ff40c12SJohn Marino return "UNKNOWN";
9393ff40c12SJohn Marino }
9403ff40c12SJohn Marino }
9413ff40c12SJohn Marino
9423ff40c12SJohn Marino
hostapd_hw_get_freq(struct hostapd_data * hapd,int chan)9433ff40c12SJohn Marino int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
9443ff40c12SJohn Marino {
945*a1157835SDaniel Fojt return hw_get_freq(hapd->iface->current_mode, chan);
9463ff40c12SJohn Marino }
9473ff40c12SJohn Marino
9483ff40c12SJohn Marino
hostapd_hw_get_channel(struct hostapd_data * hapd,int freq)9493ff40c12SJohn Marino int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
9503ff40c12SJohn Marino {
951*a1157835SDaniel Fojt int i, channel;
952*a1157835SDaniel Fojt struct hostapd_hw_modes *mode;
9533ff40c12SJohn Marino
954*a1157835SDaniel Fojt if (hapd->iface->current_mode) {
955*a1157835SDaniel Fojt channel = hw_get_chan(hapd->iface->current_mode, freq);
956*a1157835SDaniel Fojt if (channel)
957*a1157835SDaniel Fojt return channel;
9583ff40c12SJohn Marino }
9593ff40c12SJohn Marino
960*a1157835SDaniel Fojt /* Check other available modes since the channel list for the current
961*a1157835SDaniel Fojt * mode did not include the specified frequency. */
962*a1157835SDaniel Fojt if (!hapd->iface->hw_features)
963*a1157835SDaniel Fojt return 0;
964*a1157835SDaniel Fojt for (i = 0; i < hapd->iface->num_hw_features; i++) {
965*a1157835SDaniel Fojt mode = &hapd->iface->hw_features[i];
966*a1157835SDaniel Fojt channel = hw_get_chan(mode, freq);
967*a1157835SDaniel Fojt if (channel)
968*a1157835SDaniel Fojt return channel;
969*a1157835SDaniel Fojt }
9703ff40c12SJohn Marino return 0;
9713ff40c12SJohn Marino }
972