Extend offloaded ACS QCA vendor command to support VHT
authorManikandan Mohan <manikand@qca.qualcomm.com>
Wed, 11 Mar 2015 20:03:58 +0000 (13:03 -0700)
committerJouni Malinen <j@w1.fi>
Mon, 23 Mar 2015 10:18:05 +0000 (12:18 +0200)
Update ACS driver offload feature for VHT configuration. In addition,
this allows the chanlist parameter to be used to specify which channels
are included as options for the offloaded ACS case.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
15 files changed:
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/acs.c
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/ap_drv_ops.c
src/ap/dfs.c
src/ap/drv_callbacks.c
src/ap/hostapd.c
src/ap/hw_features.c
src/common/hw_features_common.c
src/common/qca-vendor.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211_event.c

index 53143f7..cae9fd3 100644 (file)
@@ -775,6 +775,24 @@ static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx,
 }
 
 
+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;
@@ -2542,12 +2560,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                                   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;
index 9e81e9e..90d1523 100644 (file)
@@ -170,8 +170,11 @@ channel=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
index ae7f6c3..652d020 100644 (file)
@@ -479,16 +479,10 @@ static int acs_usable_chan(struct hostapd_channel_data *chan)
 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);
 }
 
 
index 76011dc..cccbfab 100644 (file)
@@ -181,6 +181,8 @@ struct hostapd_config * hostapd_config_defaults(void)
        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 */
@@ -579,7 +581,7 @@ void hostapd_config_free(struct hostapd_config *conf)
        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);
index 961d2dd..b9d6832 100644 (file)
@@ -568,7 +568,8 @@ struct hostapd_config {
        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,
index e16306c..aae544f 100644 (file)
@@ -715,13 +715,66 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
 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(&params, 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, &params);
+       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, &params);
+       os_free(channels);
+
+       return ret;
 }
index d458a42..715f19b 100644 (file)
@@ -165,16 +165,10 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
 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);
 }
 
 
index a0adc67..507053e 100644 (file)
@@ -532,9 +532,8 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
 
 #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) {
@@ -543,29 +542,55 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
                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,
@@ -1248,9 +1273,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                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:
index 3e4e16b..6cdb6d3 100644 (file)
@@ -179,6 +179,7 @@ int hostapd_reload_config(struct hostapd_iface *iface)
                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;
index 05431d3..96744c4 100644 (file)
@@ -510,7 +510,11 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
                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*]");
index 309215e..8d83de6 100644 (file)
@@ -363,8 +363,6 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
                            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;
@@ -404,13 +402,34 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
                        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;
@@ -424,13 +443,21 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
                        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;
        }
 
index 2117ee7..5ff6817 100644 (file)
@@ -195,6 +195,11 @@ enum qca_wlan_vendor_attr_acs_offload {
        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 =
index 5566201..6a9cd74 100644 (file)
@@ -1588,6 +1588,16 @@ struct drv_acs_params {
 
        /* 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;
 };
 
 
@@ -4546,10 +4556,18 @@ union wpa_event_data {
         * 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;
 };
 
index 023823f..7b3dc51 100644 (file)
@@ -8382,12 +8382,24 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
            (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,
index 87e412d..a5b3230 100644 (file)
@@ -1500,6 +1500,25 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
                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);
 }