]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
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 53143f76cfe8d76902df46dd4fec2b1f061eec94..cae9fd30d008a824afd2f735bc4101400b7112b7 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 9e81e9e986830a19a18a189cdd9c4fb35e091d5a..90d15232b70df5ce2788b10f76d03e3ce300397a 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 ae7f6c3092899b7ad5258e1f3ed9209b60396267..652d020d56dd18e79c5a60be54177275bea19bf6 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 76011dc07fd4b6199822dba2f9268165ceabcc2b..cccbfabb70d3846d140dba014ec39b7384329a22 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 961d2dd389f8720960eb55aafb938311e5dd454a..b9d68321c4b35b2584e7f29b238546f358f005ff 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 e16306c4e10656219e35033ed5396508e980e340..aae544fc876938568391ad795f3fbaa5e3941bd5 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 d458a427593f48aa3b05e3d11c8adf3a49137b35..715f19b6ac7bd12726af876f94bf13d9a30593d3 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 a0adc67db430b970a767707eb61591906193cc19..507053eaa230ed3f6fbc27daf4589ff6741d7939 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 3e4e16b4f396a4ca0d04e9e12f79d7c696d03684..6cdb6d37ffe015208aedb9f84d4f19c6c62de5a3 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 05431d32c205dabafcf49d6f7b5848f9237386cf..96744c4f3e26f6b7d348faddf73f226bd2b55f5e 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 309215e5528f17106f7ce373ba06f070c0e8d172..8d83de65dd15f38ef4fa9fb790ea9f93e7cdce08 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 2117ee7028c1d4d4857a01234f8b9ea6ae8a6566..5ff681788e2e679b48887660af77c4485a5b4790 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 556620140d9327c147e9a9848943b9f2d37a9f06..6a9cd74a21adf810f9a87a8aaf2d004fb413944b 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 023823f33d09b1544907c5c7a9cd2b3c21ffca6d..7b3dc515f0109a2d9ac69946af51368770580a64 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 87e412dc596a56d4018a1de37f7ae50c8971b81b..a5b323026649c99566a4fb8092f20ff2b299faca 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);
 }