}
+static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val)
+{
+ char *pos;
+
+ /* for backwards compatibility, translate ' ' in conf str to ',' */
+ pos = val;
+ while (pos) {
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ *pos++ = ',';
+ }
+ if (freq_range_list_parse(&conf->acs_ch_list, val))
+ return -1;
+
+ return 0;
+}
+
+
static int hostapd_parse_intlist(int **int_list, char *val)
{
int *list;
line);
return 1;
#else /* CONFIG_ACS */
+ conf->acs = 1;
conf->channel = 0;
#endif /* CONFIG_ACS */
- } else
+ } else {
conf->channel = atoi(pos);
+ conf->acs = conf->channel == 0;
+ }
} else if (os_strcmp(buf, "chanlist") == 0) {
- if (hostapd_parse_intlist(&conf->chanlist, pos)) {
+ if (hostapd_parse_chanlist(conf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
line);
return 1;
# Channel list restriction. This option allows hostapd to select one of the
# provided channels when a channel should be automatically selected.
-# Default: not set (allow any enabled channel to be selected)
+# Channel list can be provided as range using hyphen ('-') or individual
+# channels can be specified by space (' ') seperated values
+# Default: all channels allowed in selected hw_mode
#chanlist=100 104 108 112 116
+#chanlist=1 6 11-13
# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
beacon_int=100
static int is_in_chanlist(struct hostapd_iface *iface,
struct hostapd_channel_data *chan)
{
- int *entry;
-
- if (!iface->conf->chanlist)
+ if (!iface->conf->acs_ch_list.num)
return 1;
- for (entry = iface->conf->chanlist; *entry != -1; entry++) {
- if (*entry == chan->chan)
- return 1;
- }
- return 0;
+ return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
}
conf->corrupt_gtk_rekey_mic_probability = 0.0;
#endif /* CONFIG_TESTING_OPTIONS */
+ conf->acs = 0;
+ conf->acs_ch_list.num = 0;
#ifdef CONFIG_ACS
conf->acs_num_scans = 5;
#endif /* CONFIG_ACS */
os_free(conf->bss);
os_free(conf->supported_rates);
os_free(conf->basic_rates);
- os_free(conf->chanlist);
+ os_free(conf->acs_ch_list.range);
os_free(conf->driver_params);
#ifdef CONFIG_ACS
os_free(conf->acs_chan_bias);
int fragm_threshold;
u8 send_probe_response;
u8 channel;
- int *chanlist;
+ u8 acs;
+ struct wpa_freq_range_list acs_ch_list;
enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
enum {
LONG_PREAMBLE = 0,
int hostapd_drv_do_acs(struct hostapd_data *hapd)
{
struct drv_acs_params params;
+ int ret, i, acs_ch_list_all = 0;
+ u8 *channels = NULL;
+ unsigned int num_channels = 0;
+ struct hostapd_hw_modes *mode;
if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
return 0;
+
os_memset(¶ms, 0, sizeof(params));
params.hw_mode = hapd->iface->conf->hw_mode;
+
+ /*
+ * If no chanlist config parameter is provided, include all enabled
+ * channels of the selected hw_mode.
+ */
+ if (!hapd->iface->conf->acs_ch_list.num)
+ acs_ch_list_all = 1;
+
+ mode = hapd->iface->current_mode;
+ if (mode == NULL)
+ return -1;
+ channels = os_malloc(mode->num_channels);
+ if (channels == NULL)
+ return -1;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (!acs_ch_list_all &&
+ !freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+ chan->chan))
+ continue;
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
+ channels[num_channels++] = chan->chan;
+ }
+
+ params.ch_list = channels;
+ params.ch_list_len = num_channels;
+
params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
- return hapd->driver->do_acs(hapd->drv_priv, ¶ms);
+ params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+ params.ch_width = 20;
+ if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
+ params.ch_width = 40;
+
+ /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
+ */
+ if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
+ if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+ params.ch_width = 80;
+ else if (hapd->iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_160MHZ ||
+ hapd->iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_80P80MHZ)
+ params.ch_width = 160;
+ }
+
+ ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms);
+ os_free(channels);
+
+ return ret;
}
static int is_in_chanlist(struct hostapd_iface *iface,
struct hostapd_channel_data *chan)
{
- int *entry;
-
- if (!iface->conf->chanlist)
+ if (!iface->conf->acs_ch_list.num)
return 1;
- for (entry = iface->conf->chanlist; *entry != -1; entry++) {
- if (*entry == chan->chan)
- return 1;
- }
- return 0;
+ return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
}
#ifdef CONFIG_ACS
static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
- u8 pri_channel, u8 sec_channel)
+ struct acs_selected_channels *acs_res)
{
- int channel;
int ret;
if (hapd->iconf->channel) {
return;
}
- hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel);
+ hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
- channel = pri_channel;
- if (!channel) {
+ if (!acs_res->pri_channel) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"driver switched to bad channel");
return;
}
- hapd->iconf->channel = channel;
+ hapd->iconf->channel = acs_res->pri_channel;
+ hapd->iconf->acs = 1;
- if (sec_channel == 0)
+ if (acs_res->sec_channel == 0)
hapd->iconf->secondary_channel = 0;
- else if (sec_channel < pri_channel)
+ else if (acs_res->sec_channel < acs_res->pri_channel)
hapd->iconf->secondary_channel = -1;
- else if (sec_channel > pri_channel)
+ else if (acs_res->sec_channel > acs_res->pri_channel)
hapd->iconf->secondary_channel = 1;
else {
wpa_printf(MSG_ERROR, "Invalid secondary channel!");
return;
}
+ if (hapd->iface->conf->ieee80211ac) {
+ /* set defaults for backwards compatibility */
+ hapd->iconf->vht_oper_centr_freq_seg1_idx = 0;
+ hapd->iconf->vht_oper_centr_freq_seg0_idx = 0;
+ hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+ if (acs_res->ch_width == 80) {
+ hapd->iconf->vht_oper_centr_freq_seg0_idx =
+ acs_res->vht_seg0_center_ch;
+ hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+ } else if (acs_res->ch_width == 160) {
+ if (acs_res->vht_seg1_center_ch == 0) {
+ hapd->iconf->vht_oper_centr_freq_seg0_idx =
+ acs_res->vht_seg0_center_ch;
+ hapd->iconf->vht_oper_chwidth =
+ VHT_CHANWIDTH_160MHZ;
+ } else {
+ hapd->iconf->vht_oper_centr_freq_seg0_idx =
+ acs_res->vht_seg0_center_ch;
+ hapd->iconf->vht_oper_centr_freq_seg1_idx =
+ acs_res->vht_seg1_center_ch;
+ hapd->iconf->vht_oper_chwidth =
+ VHT_CHANWIDTH_80P80MHZ;
+ }
+ }
+ }
+
ret = hostapd_acs_completed(hapd->iface, 0);
if (ret) {
wpa_printf(MSG_ERROR,
break;
#ifdef CONFIG_ACS
case EVENT_ACS_CHANNEL_SELECTED:
- hostapd_acs_channel_selected(
- hapd, data->acs_selected_channels.pri_channel,
- data->acs_selected_channels.sec_channel);
+ hostapd_acs_channel_selected(hapd,
+ &data->acs_selected_channels);
break;
#endif /* CONFIG_ACS */
default:
hapd = iface->bss[j];
hapd->iconf = newconf;
hapd->iconf->channel = oldconf->channel;
+ hapd->iconf->acs = oldconf->acs;
hapd->iconf->secondary_channel = oldconf->secondary_channel;
hapd->iconf->ieee80211n = oldconf->ieee80211n;
hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
return 0;
}
- if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ /*
+ * Driver ACS chosen channel may not be HT40 due to internal driver
+ * restrictions.
+ */
+ if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
!(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [HT40*]");
int vht_oper_chwidth, int center_segment0,
int center_segment1, u32 vht_caps)
{
- int tmp;
-
os_memset(data, 0, sizeof(*data));
data->mode = mode;
data->freq = freq;
return -1;
if (!sec_channel_offset)
return -1;
- /* primary 40 part must match the HT configuration */
- tmp = (30 + freq - 5000 - center_segment0 * 5) / 20;
- tmp /= 2;
- if (data->center_freq1 != 5000 +
- center_segment0 * 5 - 20 + 40 * tmp)
- return -1;
- data->center_freq1 = 5000 + center_segment0 * 5;
+ if (!center_segment0) {
+ if (channel <= 48)
+ center_segment0 = 42;
+ else if (channel <= 64)
+ center_segment0 = 58;
+ else if (channel <= 112)
+ center_segment0 = 106;
+ else if (channel <= 128)
+ center_segment0 = 122;
+ else if (channel <= 144)
+ center_segment0 = 138;
+ else if (channel <= 161)
+ center_segment0 = 155;
+ data->center_freq1 = 5000 + center_segment0 * 5;
+ } else {
+ /*
+ * Note: HT/VHT config and params are coupled. Check if
+ * HT40 channel band is in VHT80 Pri channel band
+ * configuration.
+ */
+ if (center_segment0 == channel + 6 ||
+ center_segment0 == channel + 2 ||
+ center_segment0 == channel - 2 ||
+ center_segment0 == channel - 6)
+ data->center_freq1 = 5000 + center_segment0 * 5;
+ else
+ return -1;
+ }
break;
case VHT_CHANWIDTH_160MHZ:
data->bandwidth = 160;
return -1;
if (!sec_channel_offset)
return -1;
- /* primary 40 part must match the HT configuration */
- tmp = (70 + freq - 5000 - center_segment0 * 5) / 20;
- tmp /= 2;
- if (data->center_freq1 != 5000 +
- center_segment0 * 5 - 60 + 40 * tmp)
+ /*
+ * Note: HT/VHT config and params are coupled. Check if
+ * HT40 channel band is in VHT160 channel band configuration.
+ */
+ if (center_segment0 == channel + 14 ||
+ center_segment0 == channel + 10 ||
+ center_segment0 == channel + 6 ||
+ center_segment0 == channel + 2 ||
+ center_segment0 == channel - 2 ||
+ center_segment0 == channel - 6 ||
+ center_segment0 == channel - 10 ||
+ center_segment0 == channel - 14)
+ data->center_freq1 = 5000 + center_segment0 * 5;
+ else
return -1;
- data->center_freq1 = 5000 + center_segment0 * 5;
break;
}
QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED,
QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED,
+ QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED,
+ QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+ QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+ QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ACS_MAX =
/* Indicates whether HT40 is enabled */
int ht40_enabled;
+
+ /* Indicates whether VHT is enabled */
+ int vht_enabled;
+
+ /* Configured ACS channel width */
+ u16 ch_width;
+
+ /* ACS channel list info */
+ unsigned int ch_list_len;
+ const u8 *ch_list;
};
* struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
* @pri_channel: Selected primary channel
* @sec_channel: Selected secondary channel
+ * @vht_seg0_center_ch: VHT mode Segment0 center channel
+ * @vht_seg1_center_ch: VHT mode Segment1 center channel
+ * @ch_width: Selected Channel width by driver. Driver may choose to
+ * change hostapd configured ACS channel width due driver internal
+ * channel restrictions.
*/
struct acs_selected_channels {
u8 pri_channel;
u8 sec_channel;
+ u8 vht_seg0_center_ch;
+ u8 vht_seg1_center_ch;
+ u16 ch_width;
} acs_selected_channels;
};
(params->ht_enabled &&
nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
(params->ht40_enabled &&
- nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED))) {
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
+ (params->vht_enabled &&
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
+ nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+ params->ch_width) ||
+ (params->ch_list_len &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
+ params->ch_list))) {
nlmsg_free(msg);
return -ENOBUFS;
}
nla_nest_end(msg, data);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u",
+ params->hw_mode, params->ht_enabled, params->ht40_enabled,
+ params->vht_enabled, params->ch_width, params->ch_list_len);
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
event.acs_selected_channels.sec_channel =
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+ event.acs_selected_channels.vht_seg0_center_ch =
+ nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+ event.acs_selected_channels.vht_seg1_center_ch =
+ nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
+ event.acs_selected_channels.ch_width =
+ nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+
+ wpa_printf(MSG_INFO,
+ "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d",
+ event.acs_selected_channels.pri_channel,
+ event.acs_selected_channels.sec_channel,
+ event.acs_selected_channels.ch_width,
+ event.acs_selected_channels.vht_seg0_center_ch,
+ event.acs_selected_channels.vht_seg1_center_ch);
+
+ /* Ignore ACS channel list check for backwards compatibility */
wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
}