hostapd/src/common/hw_features_common.c
Janusz Dziedzic a828f626af Make check_40mhz_2g4 common
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
2015-01-10 17:35:53 +02:00

356 lines
8.3 KiB
C

/*
* Common hostapd/wpa_supplicant HW features
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
* Copyright (c) 2015, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "defs.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
#include "hw_features_common.h"
struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
int chan, int *freq)
{
int i;
if (freq)
*freq = 0;
if (!mode)
return NULL;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *ch = &mode->channels[i];
if (ch->chan == chan) {
if (freq)
*freq = ch->freq;
return ch;
}
}
return NULL;
}
struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
int freq, int *chan)
{
int i;
if (chan)
*chan = 0;
if (!mode)
return NULL;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *ch = &mode->channels[i];
if (ch->freq == freq) {
if (chan)
*chan = ch->chan;
return ch;
}
}
return NULL;
}
int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
{
int freq;
hw_get_channel_chan(mode, chan, &freq);
return freq;
}
int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
{
int chan;
hw_get_channel_freq(mode, freq, &chan);
return chan;
}
int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
int sec_chan)
{
int ok, j, first;
int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
184, 192 };
size_t k;
if (pri_chan == sec_chan || !sec_chan)
return 1; /* HT40 not used */
wpa_printf(MSG_DEBUG,
"HT40: control channel: %d secondary channel: %d",
pri_chan, sec_chan);
/* Verify that HT40 secondary channel is an allowed 20 MHz
* channel */
ok = 0;
for (j = 0; j < mode->num_channels; j++) {
struct hostapd_channel_data *chan = &mode->channels[j];
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
chan->chan == sec_chan) {
ok = 1;
break;
}
}
if (!ok) {
wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
sec_chan);
return 0;
}
/*
* Verify that HT40 primary,secondary channel pair is allowed per
* IEEE 802.11n Annex J. This is only needed for 5 GHz band since
* 2.4 GHz rules allow all cases where the secondary channel fits into
* the list of allowed channels (already checked above).
*/
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
return 1;
first = pri_chan < sec_chan ? pri_chan : sec_chan;
ok = 0;
for (k = 0; k < ARRAY_SIZE(allowed); k++) {
if (first == allowed[k]) {
ok = 1;
break;
}
}
if (!ok) {
wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
pri_chan, sec_chan);
return 0;
}
return 1;
}
void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
{
struct ieee80211_ht_operation *oper;
struct ieee802_11_elems elems;
*pri_chan = *sec_chan = 0;
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
if (elems.ht_operation &&
elems.ht_operation_len >= sizeof(*oper)) {
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
*pri_chan = oper->primary_chan;
if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
int sec = oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
*sec_chan = *pri_chan + 4;
else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
*sec_chan = *pri_chan - 4;
}
}
}
int check_40mhz_5g(struct hostapd_hw_modes *mode,
struct wpa_scan_results *scan_res, int pri_chan,
int sec_chan)
{
int pri_freq, sec_freq, pri_bss, sec_bss;
int bss_pri_chan, bss_sec_chan;
size_t i;
int match;
if (!mode || !scan_res || !pri_chan || !sec_chan)
return 0;
if (pri_chan == sec_chan)
return 0;
pri_freq = hw_get_freq(mode, pri_chan);
sec_freq = hw_get_freq(mode, sec_chan);
/*
* Switch PRI/SEC channels if Beacons were detected on selected SEC
* channel, but not on selected PRI channel.
*/
pri_bss = sec_bss = 0;
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
if (bss->freq == pri_freq)
pri_bss++;
else if (bss->freq == sec_freq)
sec_bss++;
}
if (sec_bss && !pri_bss) {
wpa_printf(MSG_INFO,
"Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
return 2;
}
/*
* Match PRI/SEC channel with any existing HT40 BSS on the same
* channels that we are about to use (if already mixed order in
* existing BSSes, use own preference).
*/
match = 0;
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
if (pri_chan == bss_pri_chan &&
sec_chan == bss_sec_chan) {
match = 1;
break;
}
}
if (!match) {
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
if (pri_chan == bss_sec_chan &&
sec_chan == bss_pri_chan) {
wpa_printf(MSG_INFO, "Switch own primary and "
"secondary channel due to BSS "
"overlap with " MACSTR,
MAC2STR(bss->bssid));
return 2;
}
}
}
return 1;
}
int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end)
{
struct ieee802_11_elems elems;
struct ieee80211_ht_operation *oper;
if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
return 0;
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
if (!elems.ht_capabilities) {
wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
return 1;
}
if (elems.ht_operation &&
elems.ht_operation_len >= sizeof(*oper)) {
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
return 0;
wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
return 1;
}
return 0;
}
int check_40mhz_2g4(struct hostapd_hw_modes *mode,
struct wpa_scan_results *scan_res, int pri_chan,
int sec_chan)
{
int pri_freq, sec_freq;
int affected_start, affected_end;
size_t i;
if (!mode || !scan_res || !pri_chan || !sec_chan)
return 0;
if (pri_chan == sec_chan)
return 0;
pri_freq = hw_get_freq(mode, pri_chan);
sec_freq = hw_get_freq(mode, sec_chan);
affected_start = (pri_freq + sec_freq) / 2 - 25;
affected_end = (pri_freq + sec_freq) / 2 + 25;
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
affected_start, affected_end);
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
int pri = bss->freq;
int sec = pri;
struct ieee802_11_elems elems;
/* Check for overlapping 20 MHz BSS */
if (check_20mhz_bss(bss, pri_freq, affected_start,
affected_end)) {
wpa_printf(MSG_DEBUG,
"Overlapping 20 MHz BSS is found");
return 0;
}
get_pri_sec_chan(bss, &pri_chan, &sec_chan);
if (sec_chan) {
if (sec_chan < pri_chan)
sec = pri - 20;
else
sec = pri + 20;
}
if ((pri < affected_start || pri > affected_end) &&
(sec < affected_start || sec > affected_end))
continue; /* not within affected channel range */
wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
" freq=%d pri=%d sec=%d",
MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
if (sec_chan) {
if (pri_freq != pri || sec_freq != sec) {
wpa_printf(MSG_DEBUG,
"40 MHz pri/sec mismatch with BSS "
MACSTR
" <%d,%d> (chan=%d%c) vs. <%d,%d>",
MAC2STR(bss->bssid),
pri, sec, pri_chan,
sec > pri ? '+' : '-',
pri_freq, sec_freq);
return 0;
}
}
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
0);
if (elems.ht_capabilities &&
elems.ht_capabilities_len >=
sizeof(struct ieee80211_ht_capabilities)) {
struct ieee80211_ht_capabilities *ht_cap =
(struct ieee80211_ht_capabilities *)
elems.ht_capabilities;
if (le_to_host16(ht_cap->ht_capabilities_info) &
HT_CAP_INFO_40MHZ_INTOLERANT) {
wpa_printf(MSG_DEBUG,
"40 MHz Intolerant is set on channel %d in BSS "
MACSTR, pri, MAC2STR(bss->bssid));
return 0;
}
}
}
return 1;
}