diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 5279abca1..9d3d990a2 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -229,9 +229,6 @@ static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) { int pri_chan, sec_chan; - if (!iface->conf->secondary_channel) - return 1; /* HT40 not used */ - pri_chan = iface->conf->channel; sec_chan = pri_chan + iface->conf->secondary_channel * 4; @@ -697,30 +694,25 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) static int hostapd_is_usable_chan(struct hostapd_iface *iface, int channel, int primary) { - int i; struct hostapd_channel_data *chan; if (!iface->current_mode) return 0; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; - if (chan->chan != channel) - continue; - - if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) - return 1; + chan = hw_get_channel_chan(iface->current_mode, channel, NULL); + if (!chan) + return 0; - wpa_printf(MSG_DEBUG, - "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s", - primary ? "" : "Configured HT40 secondary ", - i, chan->chan, chan->flag, - chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", - chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); - } + if ((primary && chan_pri_allowed(chan)) || + (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED))) + return 1; - wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode", - channel, primary ? "primary" : "secondary"); + wpa_printf(MSG_INFO, + "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s", + channel, primary ? "primary" : "secondary", + chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); return 0; } @@ -728,6 +720,12 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, static int hostapd_is_usable_chans(struct hostapd_iface *iface) { int secondary_chan; + struct hostapd_channel_data *pri_chan; + + pri_chan = hw_get_channel_chan(iface->current_mode, + iface->conf->channel, NULL); + if (!pri_chan) + return 0; if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) return 0; @@ -742,13 +740,15 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) /* Both HT40+ and HT40- are set, pick a valid secondary channel */ secondary_chan = iface->conf->channel + 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { iface->conf->secondary_channel = 1; return 1; } secondary_chan = iface->conf->channel - 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { iface->conf->secondary_channel = -1; return 1; } diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c index db4037927..49ed80657 100644 --- a/src/common/hw_features_common.c +++ b/src/common/hw_features_common.c @@ -87,13 +87,29 @@ int hw_get_chan(struct hostapd_hw_modes *mode, int freq) int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, int sec_chan) { - int ok, j, first; + int ok, first; int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 184, 192 }; size_t k; + struct hostapd_channel_data *p_chan, *s_chan; + const int ht40_plus = pri_chan < sec_chan; - if (pri_chan == sec_chan || !sec_chan) - return 1; /* HT40 not used */ + p_chan = hw_get_channel_chan(mode, pri_chan, NULL); + if (!p_chan) + return 0; + + if (pri_chan == sec_chan || !sec_chan) { + if (chan_pri_allowed(p_chan)) + return 1; /* HT40 not used */ + + wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary", + pri_chan); + return 0; + } + + s_chan = hw_get_channel_chan(mode, sec_chan, NULL); + if (!s_chan) + return 0; wpa_printf(MSG_DEBUG, "HT40: control channel: %d secondary channel: %d", @@ -101,16 +117,9 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_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) { + if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) || + (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) || + (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) { wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", sec_chan); return 0; @@ -553,3 +562,59 @@ int ieee80211ac_cap_check(u32 hw, u32 conf) } #endif /* CONFIG_IEEE80211AC */ + + +u32 num_chan_to_bw(int num_chans) +{ + switch (num_chans) { + case 2: + case 4: + case 8: + return num_chans * 20; + default: + return 20; + } +} + + +/* check if BW is applicable for channel */ +int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, + int ht40_plus, int pri) +{ + u32 bw_mask; + + switch (bw) { + case 20: + bw_mask = HOSTAPD_CHAN_WIDTH_20; + break; + case 40: + /* HT 40 MHz support declared only for primary channel, + * just skip 40 MHz secondary checking */ + if (pri && ht40_plus) + bw_mask = HOSTAPD_CHAN_WIDTH_40P; + else if (pri && !ht40_plus) + bw_mask = HOSTAPD_CHAN_WIDTH_40M; + else + bw_mask = 0; + break; + case 80: + bw_mask = HOSTAPD_CHAN_WIDTH_80; + break; + case 160: + bw_mask = HOSTAPD_CHAN_WIDTH_160; + break; + default: + bw_mask = 0; + break; + } + + return (chan->allowed_bw & bw_mask) == bw_mask; +} + + +/* check if channel is allowed to be used as primary */ +int chan_pri_allowed(const struct hostapd_channel_data *chan) +{ + return !(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20); +} diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h index 9cddbd50e..eb1f1c57f 100644 --- a/src/common/hw_features_common.h +++ b/src/common/hw_features_common.h @@ -39,4 +39,9 @@ void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, int disabled); int ieee80211ac_cap_check(u32 hw, u32 conf); +u32 num_chan_to_bw(int num_chans); +int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, + int ht40_plus, int pri); +int chan_pri_allowed(const struct hostapd_channel_data *chan); + #endif /* HW_FEATURES_COMMON_H */