hostapd: Add supported channel bandwidth checking infrastructure
This adds checks to common code to verify supported bandwidth options for each channel using nl80211-provided info. No support of additional modes is added, just additional checks. Such checks are needed because driver/hardware can declare more strict limitations than declared in the IEEE 802.11 standard. Without this patch hostapd might select unsupported channel and that will fail because Linux kernel does check channel bandwidth limitations. Signed-off-by: Dmitry Lebed <dlebed@quantenna.com>
This commit is contained in:
parent
4299ad826d
commit
ce6d9ce15b
3 changed files with 105 additions and 35 deletions
|
@ -229,9 +229,6 @@ static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
|
||||||
{
|
{
|
||||||
int pri_chan, sec_chan;
|
int pri_chan, sec_chan;
|
||||||
|
|
||||||
if (!iface->conf->secondary_channel)
|
|
||||||
return 1; /* HT40 not used */
|
|
||||||
|
|
||||||
pri_chan = iface->conf->channel;
|
pri_chan = iface->conf->channel;
|
||||||
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
|
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,
|
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
|
||||||
int channel, int primary)
|
int channel, int primary)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
struct hostapd_channel_data *chan;
|
struct hostapd_channel_data *chan;
|
||||||
|
|
||||||
if (!iface->current_mode)
|
if (!iface->current_mode)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
|
||||||
chan = &iface->current_mode->channels[i];
|
if (!chan)
|
||||||
if (chan->chan != channel)
|
return 0;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
|
if ((primary && chan_pri_allowed(chan)) ||
|
||||||
|
(!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG,
|
wpa_printf(MSG_INFO,
|
||||||
"%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
|
"Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
|
||||||
primary ? "" : "Configured HT40 secondary ",
|
channel, primary ? "primary" : "secondary",
|
||||||
i, chan->chan, chan->flag,
|
chan->flag,
|
||||||
chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
|
chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
|
||||||
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
|
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
|
||||||
}
|
|
||||||
|
|
||||||
wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode",
|
|
||||||
channel, primary ? "primary" : "secondary");
|
|
||||||
return 0;
|
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)
|
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
|
||||||
{
|
{
|
||||||
int secondary_chan;
|
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))
|
if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
|
||||||
return 0;
|
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 */
|
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
|
||||||
secondary_chan = iface->conf->channel + 4;
|
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;
|
iface->conf->secondary_channel = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
secondary_chan = iface->conf->channel - 4;
|
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;
|
iface->conf->secondary_channel = -1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,30 +87,39 @@ 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 allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
|
||||||
int sec_chan)
|
int sec_chan)
|
||||||
{
|
{
|
||||||
int ok, j, first;
|
int ok, first;
|
||||||
int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
|
int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
|
||||||
149, 157, 165, 184, 192 };
|
149, 157, 165, 184, 192 };
|
||||||
size_t k;
|
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)
|
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 */
|
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,
|
wpa_printf(MSG_DEBUG,
|
||||||
"HT40: control channel: %d secondary channel: %d",
|
"HT40: control channel: %d secondary channel: %d",
|
||||||
pri_chan, sec_chan);
|
pri_chan, sec_chan);
|
||||||
|
|
||||||
/* Verify that HT40 secondary channel is an allowed 20 MHz
|
/* Verify that HT40 secondary channel is an allowed 20 MHz
|
||||||
* channel */
|
* channel */
|
||||||
ok = 0;
|
if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) ||
|
||||||
for (j = 0; j < mode->num_channels; j++) {
|
(ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) ||
|
||||||
struct hostapd_channel_data *chan = &mode->channels[j];
|
(!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) {
|
||||||
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",
|
wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
|
||||||
sec_chan);
|
sec_chan);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -553,3 +562,59 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_IEEE80211AC */
|
#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);
|
||||||
|
}
|
||||||
|
|
|
@ -39,4 +39,9 @@ void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
|
||||||
int disabled);
|
int disabled);
|
||||||
int ieee80211ac_cap_check(u32 hw, u32 conf);
|
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 */
|
#endif /* HW_FEATURES_COMMON_H */
|
||||||
|
|
Loading…
Reference in a new issue