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