| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * hostapd / Hardware feature query and different modes | 
|  | 3 | * Copyright 2002-2003, Instant802 Networks, Inc. | 
|  | 4 | * Copyright 2005-2006, Devicescape Software, Inc. | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 5 | * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi> | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License version 2 as | 
|  | 9 | * published by the Free Software Foundation. | 
|  | 10 | * | 
|  | 11 | * Alternatively, this software may be distributed under the terms of BSD | 
|  | 12 | * license. | 
|  | 13 | * | 
|  | 14 | * See README and COPYING for more details. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include "utils/includes.h" | 
|  | 18 |  | 
|  | 19 | #include "utils/common.h" | 
|  | 20 | #include "utils/eloop.h" | 
|  | 21 | #include "common/ieee802_11_defs.h" | 
|  | 22 | #include "common/ieee802_11_common.h" | 
|  | 23 | #include "drivers/driver.h" | 
|  | 24 | #include "hostapd.h" | 
|  | 25 | #include "ap_config.h" | 
|  | 26 | #include "ap_drv_ops.h" | 
|  | 27 | #include "hw_features.h" | 
|  | 28 |  | 
|  | 29 |  | 
|  | 30 | void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, | 
|  | 31 | size_t num_hw_features) | 
|  | 32 | { | 
|  | 33 | size_t i; | 
|  | 34 |  | 
|  | 35 | if (hw_features == NULL) | 
|  | 36 | return; | 
|  | 37 |  | 
|  | 38 | for (i = 0; i < num_hw_features; i++) { | 
|  | 39 | os_free(hw_features[i].channels); | 
|  | 40 | os_free(hw_features[i].rates); | 
|  | 41 | } | 
|  | 42 |  | 
|  | 43 | os_free(hw_features); | 
|  | 44 | } | 
|  | 45 |  | 
|  | 46 |  | 
|  | 47 | int hostapd_get_hw_features(struct hostapd_iface *iface) | 
|  | 48 | { | 
|  | 49 | struct hostapd_data *hapd = iface->bss[0]; | 
|  | 50 | int ret = 0, i, j; | 
|  | 51 | u16 num_modes, flags; | 
|  | 52 | struct hostapd_hw_modes *modes; | 
|  | 53 |  | 
|  | 54 | if (hostapd_drv_none(hapd)) | 
|  | 55 | return -1; | 
|  | 56 | modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); | 
|  | 57 | if (modes == NULL) { | 
|  | 58 | hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, | 
|  | 59 | HOSTAPD_LEVEL_DEBUG, | 
|  | 60 | "Fetching hardware channel/rate support not " | 
|  | 61 | "supported."); | 
|  | 62 | return -1; | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | iface->hw_flags = flags; | 
|  | 66 |  | 
|  | 67 | hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); | 
|  | 68 | iface->hw_features = modes; | 
|  | 69 | iface->num_hw_features = num_modes; | 
|  | 70 |  | 
|  | 71 | for (i = 0; i < num_modes; i++) { | 
|  | 72 | struct hostapd_hw_modes *feature = &modes[i]; | 
|  | 73 | /* set flag for channels we can use in current regulatory | 
|  | 74 | * domain */ | 
|  | 75 | for (j = 0; j < feature->num_channels; j++) { | 
|  | 76 | /* | 
|  | 77 | * Disable all channels that are marked not to allow | 
|  | 78 | * IBSS operation or active scanning. In addition, | 
|  | 79 | * disable all channels that require radar detection, | 
|  | 80 | * since that (in addition to full DFS) is not yet | 
|  | 81 | * supported. | 
|  | 82 | */ | 
|  | 83 | if (feature->channels[j].flag & | 
|  | 84 | (HOSTAPD_CHAN_NO_IBSS | | 
|  | 85 | HOSTAPD_CHAN_PASSIVE_SCAN | | 
|  | 86 | HOSTAPD_CHAN_RADAR)) | 
|  | 87 | feature->channels[j].flag |= | 
|  | 88 | HOSTAPD_CHAN_DISABLED; | 
|  | 89 | if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) | 
|  | 90 | continue; | 
|  | 91 | wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " | 
|  | 92 | "chan=%d freq=%d MHz max_tx_power=%d dBm", | 
|  | 93 | feature->mode, | 
|  | 94 | feature->channels[j].chan, | 
|  | 95 | feature->channels[j].freq, | 
|  | 96 | feature->channels[j].max_tx_power); | 
|  | 97 | } | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | return ret; | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 104 | int hostapd_prepare_rates(struct hostapd_iface *iface, | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 105 | struct hostapd_hw_modes *mode) | 
|  | 106 | { | 
|  | 107 | int i, num_basic_rates = 0; | 
|  | 108 | int basic_rates_a[] = { 60, 120, 240, -1 }; | 
|  | 109 | int basic_rates_b[] = { 10, 20, -1 }; | 
|  | 110 | int basic_rates_g[] = { 10, 20, 55, 110, -1 }; | 
|  | 111 | int *basic_rates; | 
|  | 112 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 113 | if (iface->conf->basic_rates) | 
|  | 114 | basic_rates = iface->conf->basic_rates; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 115 | else switch (mode->mode) { | 
|  | 116 | case HOSTAPD_MODE_IEEE80211A: | 
|  | 117 | basic_rates = basic_rates_a; | 
|  | 118 | break; | 
|  | 119 | case HOSTAPD_MODE_IEEE80211B: | 
|  | 120 | basic_rates = basic_rates_b; | 
|  | 121 | break; | 
|  | 122 | case HOSTAPD_MODE_IEEE80211G: | 
|  | 123 | basic_rates = basic_rates_g; | 
|  | 124 | break; | 
|  | 125 | default: | 
|  | 126 | return -1; | 
|  | 127 | } | 
|  | 128 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 129 | i = 0; | 
|  | 130 | while (basic_rates[i] >= 0) | 
|  | 131 | i++; | 
|  | 132 | os_free(iface->basic_rates); | 
|  | 133 | iface->basic_rates = os_malloc(i * sizeof(int)); | 
|  | 134 | if (iface->basic_rates) | 
|  | 135 | os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 136 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 137 | os_free(iface->current_rates); | 
|  | 138 | iface->num_rates = 0; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 139 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 140 | iface->current_rates = | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 141 | os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data)); | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 142 | if (!iface->current_rates) { | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 143 | wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " | 
|  | 144 | "table."); | 
|  | 145 | return -1; | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | for (i = 0; i < mode->num_rates; i++) { | 
|  | 149 | struct hostapd_rate_data *rate; | 
|  | 150 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 151 | if (iface->conf->supported_rates && | 
|  | 152 | !hostapd_rate_found(iface->conf->supported_rates, | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 153 | mode->rates[i])) | 
|  | 154 | continue; | 
|  | 155 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 156 | rate = &iface->current_rates[iface->num_rates]; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 157 | rate->rate = mode->rates[i]; | 
|  | 158 | if (hostapd_rate_found(basic_rates, rate->rate)) { | 
|  | 159 | rate->flags |= HOSTAPD_RATE_BASIC; | 
|  | 160 | num_basic_rates++; | 
|  | 161 | } | 
|  | 162 | wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 163 | iface->num_rates, rate->rate, rate->flags); | 
|  | 164 | iface->num_rates++; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 165 | } | 
|  | 166 |  | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 167 | if ((iface->num_rates == 0 || num_basic_rates == 0) && | 
|  | 168 | (!iface->conf->ieee80211n || !iface->conf->require_ht)) { | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 169 | wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " | 
|  | 170 | "rate sets (%d,%d).", | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 171 | iface->num_rates, num_basic_rates); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 172 | return -1; | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | return 0; | 
|  | 176 | } | 
|  | 177 |  | 
|  | 178 |  | 
|  | 179 | #ifdef CONFIG_IEEE80211N | 
|  | 180 | static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) | 
|  | 181 | { | 
|  | 182 | int sec_chan, ok, j, first; | 
|  | 183 | int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, | 
|  | 184 | 184, 192 }; | 
|  | 185 | size_t k; | 
|  | 186 |  | 
|  | 187 | if (!iface->conf->secondary_channel) | 
|  | 188 | return 1; /* HT40 not used */ | 
|  | 189 |  | 
|  | 190 | sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; | 
|  | 191 | wpa_printf(MSG_DEBUG, "HT40: control channel: %d  " | 
|  | 192 | "secondary channel: %d", | 
|  | 193 | iface->conf->channel, sec_chan); | 
|  | 194 |  | 
|  | 195 | /* Verify that HT40 secondary channel is an allowed 20 MHz | 
|  | 196 | * channel */ | 
|  | 197 | ok = 0; | 
|  | 198 | for (j = 0; j < iface->current_mode->num_channels; j++) { | 
|  | 199 | struct hostapd_channel_data *chan = | 
|  | 200 | &iface->current_mode->channels[j]; | 
|  | 201 | if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && | 
|  | 202 | chan->chan == sec_chan) { | 
|  | 203 | ok = 1; | 
|  | 204 | break; | 
|  | 205 | } | 
|  | 206 | } | 
|  | 207 | if (!ok) { | 
|  | 208 | wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", | 
|  | 209 | sec_chan); | 
|  | 210 | return 0; | 
|  | 211 | } | 
|  | 212 |  | 
|  | 213 | /* | 
|  | 214 | * Verify that HT40 primary,secondary channel pair is allowed per | 
|  | 215 | * IEEE 802.11n Annex J. This is only needed for 5 GHz band since | 
|  | 216 | * 2.4 GHz rules allow all cases where the secondary channel fits into | 
|  | 217 | * the list of allowed channels (already checked above). | 
|  | 218 | */ | 
|  | 219 | if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) | 
|  | 220 | return 1; | 
|  | 221 |  | 
|  | 222 | if (iface->conf->secondary_channel > 0) | 
|  | 223 | first = iface->conf->channel; | 
|  | 224 | else | 
|  | 225 | first = sec_chan; | 
|  | 226 |  | 
|  | 227 | ok = 0; | 
|  | 228 | for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { | 
|  | 229 | if (first == allowed[k]) { | 
|  | 230 | ok = 1; | 
|  | 231 | break; | 
|  | 232 | } | 
|  | 233 | } | 
|  | 234 | if (!ok) { | 
|  | 235 | wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", | 
|  | 236 | iface->conf->channel, | 
|  | 237 | iface->conf->secondary_channel); | 
|  | 238 | return 0; | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | return 1; | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 |  | 
|  | 245 | static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) | 
|  | 246 | { | 
|  | 247 | if (iface->conf->secondary_channel > 0) { | 
|  | 248 | iface->conf->channel += 4; | 
|  | 249 | iface->conf->secondary_channel = -1; | 
|  | 250 | } else { | 
|  | 251 | iface->conf->channel -= 4; | 
|  | 252 | iface->conf->secondary_channel = 1; | 
|  | 253 | } | 
|  | 254 | } | 
|  | 255 |  | 
|  | 256 |  | 
|  | 257 | static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss, | 
|  | 258 | int *pri_chan, int *sec_chan) | 
|  | 259 | { | 
|  | 260 | struct ieee80211_ht_operation *oper; | 
|  | 261 | struct ieee802_11_elems elems; | 
|  | 262 |  | 
|  | 263 | *pri_chan = *sec_chan = 0; | 
|  | 264 |  | 
|  | 265 | ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); | 
|  | 266 | if (elems.ht_operation && | 
|  | 267 | elems.ht_operation_len >= sizeof(*oper)) { | 
|  | 268 | oper = (struct ieee80211_ht_operation *) elems.ht_operation; | 
|  | 269 | *pri_chan = oper->control_chan; | 
|  | 270 | if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { | 
|  | 271 | int sec = oper->ht_param & | 
|  | 272 | HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; | 
|  | 273 | if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) | 
|  | 274 | *sec_chan = *pri_chan + 4; | 
|  | 275 | else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) | 
|  | 276 | *sec_chan = *pri_chan - 4; | 
|  | 277 | } | 
|  | 278 | } | 
|  | 279 | } | 
|  | 280 |  | 
|  | 281 |  | 
|  | 282 | static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, | 
|  | 283 | struct wpa_scan_results *scan_res) | 
|  | 284 | { | 
|  | 285 | int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss; | 
|  | 286 | int bss_pri_chan, bss_sec_chan; | 
|  | 287 | size_t i; | 
|  | 288 | int match; | 
|  | 289 |  | 
|  | 290 | pri_chan = iface->conf->channel; | 
|  | 291 | sec_chan = iface->conf->secondary_channel * 4; | 
|  | 292 | pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan); | 
|  | 293 | if (iface->conf->secondary_channel > 0) | 
|  | 294 | sec_freq = pri_freq + 20; | 
|  | 295 | else | 
|  | 296 | sec_freq = pri_freq - 20; | 
|  | 297 |  | 
|  | 298 | /* | 
|  | 299 | * Switch PRI/SEC channels if Beacons were detected on selected SEC | 
|  | 300 | * channel, but not on selected PRI channel. | 
|  | 301 | */ | 
|  | 302 | pri_bss = sec_bss = 0; | 
|  | 303 | for (i = 0; i < scan_res->num; i++) { | 
|  | 304 | struct wpa_scan_res *bss = scan_res->res[i]; | 
|  | 305 | if (bss->freq == pri_freq) | 
|  | 306 | pri_bss++; | 
|  | 307 | else if (bss->freq == sec_freq) | 
|  | 308 | sec_bss++; | 
|  | 309 | } | 
|  | 310 | if (sec_bss && !pri_bss) { | 
|  | 311 | wpa_printf(MSG_INFO, "Switch own primary and secondary " | 
|  | 312 | "channel to get secondary channel with no Beacons " | 
|  | 313 | "from other BSSes"); | 
|  | 314 | ieee80211n_switch_pri_sec(iface); | 
|  | 315 | } | 
|  | 316 |  | 
|  | 317 | /* | 
|  | 318 | * Match PRI/SEC channel with any existing HT40 BSS on the same | 
|  | 319 | * channels that we are about to use (if already mixed order in | 
|  | 320 | * existing BSSes, use own preference). | 
|  | 321 | */ | 
|  | 322 | match = 0; | 
|  | 323 | for (i = 0; i < scan_res->num; i++) { | 
|  | 324 | struct wpa_scan_res *bss = scan_res->res[i]; | 
|  | 325 | ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); | 
|  | 326 | if (pri_chan == bss_pri_chan && | 
|  | 327 | sec_chan == bss_sec_chan) { | 
|  | 328 | match = 1; | 
|  | 329 | break; | 
|  | 330 | } | 
|  | 331 | } | 
|  | 332 | if (!match) { | 
|  | 333 | for (i = 0; i < scan_res->num; i++) { | 
|  | 334 | struct wpa_scan_res *bss = scan_res->res[i]; | 
|  | 335 | ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, | 
|  | 336 | &bss_sec_chan); | 
|  | 337 | if (pri_chan == bss_sec_chan && | 
|  | 338 | sec_chan == bss_pri_chan) { | 
|  | 339 | wpa_printf(MSG_INFO, "Switch own primary and " | 
|  | 340 | "secondary channel due to BSS " | 
|  | 341 | "overlap with " MACSTR, | 
|  | 342 | MAC2STR(bss->bssid)); | 
|  | 343 | ieee80211n_switch_pri_sec(iface); | 
|  | 344 | break; | 
|  | 345 | } | 
|  | 346 | } | 
|  | 347 | } | 
|  | 348 |  | 
|  | 349 | return 1; | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 |  | 
|  | 353 | static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, | 
|  | 354 | struct wpa_scan_results *scan_res) | 
|  | 355 | { | 
|  | 356 | int pri_freq, sec_freq; | 
|  | 357 | int affected_start, affected_end; | 
|  | 358 | size_t i; | 
|  | 359 |  | 
|  | 360 | pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); | 
|  | 361 | if (iface->conf->secondary_channel > 0) | 
|  | 362 | sec_freq = pri_freq + 20; | 
|  | 363 | else | 
|  | 364 | sec_freq = pri_freq - 20; | 
|  | 365 | affected_start = (pri_freq + sec_freq) / 2 - 25; | 
|  | 366 | affected_end = (pri_freq + sec_freq) / 2 + 25; | 
|  | 367 | wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", | 
|  | 368 | affected_start, affected_end); | 
|  | 369 | for (i = 0; i < scan_res->num; i++) { | 
|  | 370 | struct wpa_scan_res *bss = scan_res->res[i]; | 
|  | 371 | int pri = bss->freq; | 
|  | 372 | int sec = pri; | 
|  | 373 | int sec_chan, pri_chan; | 
|  | 374 |  | 
|  | 375 | ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan); | 
|  | 376 |  | 
|  | 377 | if (sec_chan) { | 
|  | 378 | if (sec_chan < pri_chan) | 
|  | 379 | sec = pri - 20; | 
|  | 380 | else | 
|  | 381 | sec = pri + 20; | 
|  | 382 | } | 
|  | 383 |  | 
|  | 384 | if ((pri < affected_start || pri > affected_end) && | 
|  | 385 | (sec < affected_start || sec > affected_end)) | 
|  | 386 | continue; /* not within affected channel range */ | 
|  | 387 |  | 
|  | 388 | wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR | 
|  | 389 | " freq=%d pri=%d sec=%d", | 
|  | 390 | MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); | 
|  | 391 |  | 
|  | 392 | if (sec_chan) { | 
|  | 393 | if (pri_freq != pri || sec_freq != sec) { | 
|  | 394 | wpa_printf(MSG_DEBUG, "40 MHz pri/sec " | 
|  | 395 | "mismatch with BSS " MACSTR | 
|  | 396 | " <%d,%d> (chan=%d%c) vs. <%d,%d>", | 
|  | 397 | MAC2STR(bss->bssid), | 
|  | 398 | pri, sec, pri_chan, | 
|  | 399 | sec > pri ? '+' : '-', | 
|  | 400 | pri_freq, sec_freq); | 
|  | 401 | return 0; | 
|  | 402 | } | 
|  | 403 | } | 
|  | 404 |  | 
|  | 405 | /* TODO: 40 MHz intolerant */ | 
|  | 406 | } | 
|  | 407 |  | 
|  | 408 | return 1; | 
|  | 409 | } | 
|  | 410 |  | 
|  | 411 |  | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 412 | static void ieee80211n_check_scan(struct hostapd_iface *iface) | 
|  | 413 | { | 
|  | 414 | struct wpa_scan_results *scan_res; | 
|  | 415 | int oper40; | 
|  | 416 | int res; | 
|  | 417 |  | 
|  | 418 | /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is | 
|  | 419 | * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */ | 
|  | 420 |  | 
|  | 421 | iface->scan_cb = NULL; | 
|  | 422 |  | 
|  | 423 | scan_res = hostapd_driver_get_scan_results(iface->bss[0]); | 
|  | 424 | if (scan_res == NULL) { | 
|  | 425 | hostapd_setup_interface_complete(iface, 1); | 
|  | 426 | return; | 
|  | 427 | } | 
|  | 428 |  | 
|  | 429 | if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A) | 
|  | 430 | oper40 = ieee80211n_check_40mhz_5g(iface, scan_res); | 
|  | 431 | else | 
|  | 432 | oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); | 
|  | 433 | wpa_scan_results_free(scan_res); | 
|  | 434 |  | 
|  | 435 | if (!oper40) { | 
|  | 436 | wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " | 
|  | 437 | "channel pri=%d sec=%d based on overlapping BSSes", | 
|  | 438 | iface->conf->channel, | 
|  | 439 | iface->conf->channel + | 
|  | 440 | iface->conf->secondary_channel * 4); | 
|  | 441 | iface->conf->secondary_channel = 0; | 
|  | 442 | iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; | 
|  | 443 | } | 
|  | 444 |  | 
|  | 445 | res = ieee80211n_allowed_ht40_channel_pair(iface); | 
|  | 446 | hostapd_setup_interface_complete(iface, !res); | 
|  | 447 | } | 
|  | 448 |  | 
|  | 449 |  | 
|  | 450 | static int ieee80211n_check_40mhz(struct hostapd_iface *iface) | 
|  | 451 | { | 
|  | 452 | struct wpa_driver_scan_params params; | 
|  | 453 |  | 
|  | 454 | if (!iface->conf->secondary_channel) | 
|  | 455 | return 0; /* HT40 not used */ | 
|  | 456 |  | 
|  | 457 | wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " | 
|  | 458 | "40 MHz channel"); | 
|  | 459 | os_memset(¶ms, 0, sizeof(params)); | 
|  | 460 | /* TODO: scan only the needed frequency */ | 
|  | 461 | if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { | 
|  | 462 | wpa_printf(MSG_ERROR, "Failed to request a scan of " | 
|  | 463 | "neighboring BSSes"); | 
|  | 464 | return -1; | 
|  | 465 | } | 
|  | 466 |  | 
|  | 467 | iface->scan_cb = ieee80211n_check_scan; | 
|  | 468 | return 1; | 
|  | 469 | } | 
|  | 470 |  | 
|  | 471 |  | 
|  | 472 | static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) | 
|  | 473 | { | 
|  | 474 | u16 hw = iface->current_mode->ht_capab; | 
|  | 475 | u16 conf = iface->conf->ht_capab; | 
|  | 476 |  | 
|  | 477 | if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && | 
|  | 478 | !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { | 
|  | 479 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 480 | "HT capability [LDPC]"); | 
|  | 481 | return 0; | 
|  | 482 | } | 
|  | 483 |  | 
|  | 484 | if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && | 
|  | 485 | !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { | 
|  | 486 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 487 | "HT capability [HT40*]"); | 
|  | 488 | return 0; | 
|  | 489 | } | 
|  | 490 |  | 
|  | 491 | if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && | 
|  | 492 | (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { | 
|  | 493 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 494 | "HT capability [SMPS-*]"); | 
|  | 495 | return 0; | 
|  | 496 | } | 
|  | 497 |  | 
|  | 498 | if ((conf & HT_CAP_INFO_GREEN_FIELD) && | 
|  | 499 | !(hw & HT_CAP_INFO_GREEN_FIELD)) { | 
|  | 500 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 501 | "HT capability [GF]"); | 
|  | 502 | return 0; | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && | 
|  | 506 | !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { | 
|  | 507 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 508 | "HT capability [SHORT-GI-20]"); | 
|  | 509 | return 0; | 
|  | 510 | } | 
|  | 511 |  | 
|  | 512 | if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && | 
|  | 513 | !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { | 
|  | 514 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 515 | "HT capability [SHORT-GI-40]"); | 
|  | 516 | return 0; | 
|  | 517 | } | 
|  | 518 |  | 
|  | 519 | if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { | 
|  | 520 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 521 | "HT capability [TX-STBC]"); | 
|  | 522 | return 0; | 
|  | 523 | } | 
|  | 524 |  | 
|  | 525 | if ((conf & HT_CAP_INFO_RX_STBC_MASK) > | 
|  | 526 | (hw & HT_CAP_INFO_RX_STBC_MASK)) { | 
|  | 527 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 528 | "HT capability [RX-STBC*]"); | 
|  | 529 | return 0; | 
|  | 530 | } | 
|  | 531 |  | 
|  | 532 | if ((conf & HT_CAP_INFO_DELAYED_BA) && | 
|  | 533 | !(hw & HT_CAP_INFO_DELAYED_BA)) { | 
|  | 534 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 535 | "HT capability [DELAYED-BA]"); | 
|  | 536 | return 0; | 
|  | 537 | } | 
|  | 538 |  | 
|  | 539 | if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && | 
|  | 540 | !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { | 
|  | 541 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 542 | "HT capability [MAX-AMSDU-7935]"); | 
|  | 543 | return 0; | 
|  | 544 | } | 
|  | 545 |  | 
|  | 546 | if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && | 
|  | 547 | !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { | 
|  | 548 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 549 | "HT capability [DSSS_CCK-40]"); | 
|  | 550 | return 0; | 
|  | 551 | } | 
|  | 552 |  | 
|  | 553 | if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { | 
|  | 554 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 555 | "HT capability [PSMP]"); | 
|  | 556 | return 0; | 
|  | 557 | } | 
|  | 558 |  | 
|  | 559 | if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && | 
|  | 560 | !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { | 
|  | 561 | wpa_printf(MSG_ERROR, "Driver does not support configured " | 
|  | 562 | "HT capability [LSIG-TXOP-PROT]"); | 
|  | 563 | return 0; | 
|  | 564 | } | 
|  | 565 |  | 
|  | 566 | return 1; | 
|  | 567 | } | 
|  | 568 |  | 
|  | 569 | #endif /* CONFIG_IEEE80211N */ | 
|  | 570 |  | 
|  | 571 |  | 
|  | 572 | int hostapd_check_ht_capab(struct hostapd_iface *iface) | 
|  | 573 | { | 
|  | 574 | #ifdef CONFIG_IEEE80211N | 
|  | 575 | int ret; | 
|  | 576 | if (!iface->conf->ieee80211n) | 
|  | 577 | return 0; | 
|  | 578 | if (!ieee80211n_supported_ht_capab(iface)) | 
|  | 579 | return -1; | 
|  | 580 | ret = ieee80211n_check_40mhz(iface); | 
|  | 581 | if (ret) | 
|  | 582 | return ret; | 
|  | 583 | if (!ieee80211n_allowed_ht40_channel_pair(iface)) | 
|  | 584 | return -1; | 
|  | 585 | #endif /* CONFIG_IEEE80211N */ | 
|  | 586 |  | 
|  | 587 | return 0; | 
|  | 588 | } | 
|  | 589 |  | 
|  | 590 |  | 
|  | 591 | /** | 
|  | 592 | * hostapd_select_hw_mode - Select the hardware mode | 
|  | 593 | * @iface: Pointer to interface data. | 
| Jouni Malinen | 87fd279 | 2011-05-16 18:35:42 +0300 | [diff] [blame] | 594 | * Returns: 0 on success, < 0 on failure | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 595 | * | 
|  | 596 | * Sets up the hardware mode, channel, rates, and passive scanning | 
|  | 597 | * based on the configuration. | 
|  | 598 | */ | 
|  | 599 | int hostapd_select_hw_mode(struct hostapd_iface *iface) | 
|  | 600 | { | 
|  | 601 | int i, j, ok; | 
|  | 602 |  | 
|  | 603 | if (iface->num_hw_features < 1) | 
|  | 604 | return -1; | 
|  | 605 |  | 
|  | 606 | iface->current_mode = NULL; | 
|  | 607 | for (i = 0; i < iface->num_hw_features; i++) { | 
|  | 608 | struct hostapd_hw_modes *mode = &iface->hw_features[i]; | 
|  | 609 | if (mode->mode == iface->conf->hw_mode) { | 
|  | 610 | iface->current_mode = mode; | 
|  | 611 | break; | 
|  | 612 | } | 
|  | 613 | } | 
|  | 614 |  | 
|  | 615 | if (iface->current_mode == NULL) { | 
|  | 616 | wpa_printf(MSG_ERROR, "Hardware does not support configured " | 
|  | 617 | "mode"); | 
|  | 618 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | 
|  | 619 | HOSTAPD_LEVEL_WARNING, | 
|  | 620 | "Hardware does not support configured mode " | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 621 | "(%d) (hw_mode in hostapd.conf)", | 
|  | 622 | (int) iface->conf->hw_mode); | 
| Jouni Malinen | 87fd279 | 2011-05-16 18:35:42 +0300 | [diff] [blame] | 623 | return -2; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 624 | } | 
|  | 625 |  | 
|  | 626 | ok = 0; | 
|  | 627 | for (j = 0; j < iface->current_mode->num_channels; j++) { | 
|  | 628 | struct hostapd_channel_data *chan = | 
|  | 629 | &iface->current_mode->channels[j]; | 
| Jouni Malinen | 87fd279 | 2011-05-16 18:35:42 +0300 | [diff] [blame] | 630 | if (chan->chan == iface->conf->channel) { | 
|  | 631 | if (chan->flag & HOSTAPD_CHAN_DISABLED) { | 
|  | 632 | wpa_printf(MSG_ERROR, | 
|  | 633 | "channel [%i] (%i) is disabled for " | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 634 | "use in AP mode, flags: 0x%x%s%s%s", | 
|  | 635 | j, chan->chan, chan->flag, | 
|  | 636 | chan->flag & HOSTAPD_CHAN_NO_IBSS ? | 
|  | 637 | " NO-IBSS" : "", | 
|  | 638 | chan->flag & | 
|  | 639 | HOSTAPD_CHAN_PASSIVE_SCAN ? | 
|  | 640 | " PASSIVE-SCAN" : "", | 
|  | 641 | chan->flag & HOSTAPD_CHAN_RADAR ? | 
|  | 642 | " RADAR" : ""); | 
| Jouni Malinen | 87fd279 | 2011-05-16 18:35:42 +0300 | [diff] [blame] | 643 | } else { | 
|  | 644 | ok = 1; | 
|  | 645 | break; | 
|  | 646 | } | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 647 | } | 
|  | 648 | } | 
|  | 649 | if (ok && iface->conf->secondary_channel) { | 
|  | 650 | int sec_ok = 0; | 
|  | 651 | int sec_chan = iface->conf->channel + | 
|  | 652 | iface->conf->secondary_channel * 4; | 
|  | 653 | for (j = 0; j < iface->current_mode->num_channels; j++) { | 
|  | 654 | struct hostapd_channel_data *chan = | 
|  | 655 | &iface->current_mode->channels[j]; | 
|  | 656 | if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && | 
|  | 657 | (chan->chan == sec_chan)) { | 
|  | 658 | sec_ok = 1; | 
|  | 659 | break; | 
|  | 660 | } | 
|  | 661 | } | 
|  | 662 | if (!sec_ok) { | 
|  | 663 | hostapd_logger(iface->bss[0], NULL, | 
|  | 664 | HOSTAPD_MODULE_IEEE80211, | 
|  | 665 | HOSTAPD_LEVEL_WARNING, | 
|  | 666 | "Configured HT40 secondary channel " | 
|  | 667 | "(%d) not found from the channel list " | 
|  | 668 | "of current mode (%d) %s", | 
|  | 669 | sec_chan, iface->current_mode->mode, | 
|  | 670 | hostapd_hw_mode_txt( | 
|  | 671 | iface->current_mode->mode)); | 
|  | 672 | ok = 0; | 
|  | 673 | } | 
|  | 674 | } | 
|  | 675 | if (iface->conf->channel == 0) { | 
|  | 676 | /* TODO: could request a scan of neighboring BSSes and select | 
|  | 677 | * the channel automatically */ | 
|  | 678 | wpa_printf(MSG_ERROR, "Channel not configured " | 
|  | 679 | "(hw_mode/channel in hostapd.conf)"); | 
| Jouni Malinen | 87fd279 | 2011-05-16 18:35:42 +0300 | [diff] [blame] | 680 | return -3; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 681 | } | 
|  | 682 | if (ok == 0 && iface->conf->channel != 0) { | 
|  | 683 | hostapd_logger(iface->bss[0], NULL, | 
|  | 684 | HOSTAPD_MODULE_IEEE80211, | 
|  | 685 | HOSTAPD_LEVEL_WARNING, | 
|  | 686 | "Configured channel (%d) not found from the " | 
|  | 687 | "channel list of current mode (%d) %s", | 
|  | 688 | iface->conf->channel, | 
|  | 689 | iface->current_mode->mode, | 
|  | 690 | hostapd_hw_mode_txt(iface->current_mode->mode)); | 
|  | 691 | iface->current_mode = NULL; | 
|  | 692 | } | 
|  | 693 |  | 
|  | 694 | if (iface->current_mode == NULL) { | 
|  | 695 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | 
|  | 696 | HOSTAPD_LEVEL_WARNING, | 
|  | 697 | "Hardware does not support configured channel"); | 
| Jouni Malinen | 87fd279 | 2011-05-16 18:35:42 +0300 | [diff] [blame] | 698 | return -4; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 699 | } | 
|  | 700 |  | 
|  | 701 | return 0; | 
|  | 702 | } | 
|  | 703 |  | 
|  | 704 |  | 
|  | 705 | const char * hostapd_hw_mode_txt(int mode) | 
|  | 706 | { | 
|  | 707 | switch (mode) { | 
|  | 708 | case HOSTAPD_MODE_IEEE80211A: | 
|  | 709 | return "IEEE 802.11a"; | 
|  | 710 | case HOSTAPD_MODE_IEEE80211B: | 
|  | 711 | return "IEEE 802.11b"; | 
|  | 712 | case HOSTAPD_MODE_IEEE80211G: | 
|  | 713 | return "IEEE 802.11g"; | 
|  | 714 | default: | 
|  | 715 | return "UNKNOWN"; | 
|  | 716 | } | 
|  | 717 | } | 
|  | 718 |  | 
|  | 719 |  | 
|  | 720 | int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) | 
|  | 721 | { | 
|  | 722 | int i; | 
|  | 723 |  | 
|  | 724 | if (!hapd->iface->current_mode) | 
|  | 725 | return 0; | 
|  | 726 |  | 
|  | 727 | for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { | 
|  | 728 | struct hostapd_channel_data *ch = | 
|  | 729 | &hapd->iface->current_mode->channels[i]; | 
|  | 730 | if (ch->chan == chan) | 
|  | 731 | return ch->freq; | 
|  | 732 | } | 
|  | 733 |  | 
|  | 734 | return 0; | 
|  | 735 | } | 
|  | 736 |  | 
|  | 737 |  | 
|  | 738 | int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) | 
|  | 739 | { | 
|  | 740 | int i; | 
|  | 741 |  | 
|  | 742 | if (!hapd->iface->current_mode) | 
|  | 743 | return 0; | 
|  | 744 |  | 
|  | 745 | for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { | 
|  | 746 | struct hostapd_channel_data *ch = | 
|  | 747 | &hapd->iface->current_mode->channels[i]; | 
|  | 748 | if (ch->freq == freq) | 
|  | 749 | return ch->chan; | 
|  | 750 | } | 
|  | 751 |  | 
|  | 752 | return 0; | 
|  | 753 | } |