Extend hw_mode to support any band for offloaded ACS case
authorPeng Xu <pxu@qca.qualcomm.com>
Fri, 8 May 2015 17:53:08 +0000 (10:53 -0700)
committerJouni Malinen <j@w1.fi>
Wed, 27 May 2015 09:17:57 +0000 (12:17 +0300)
When device supports dual band operations with offloaded ACS, hw_mode
can now be set to any band (hw_mode=any) in order to allow ACS to select
the best channel from any band. After a channel is selected, the hw_mode
is updated for hostapd.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/drv_callbacks.c
src/ap/hw_features.c
src/common/defs.h
src/common/qca-vendor.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211_capa.c
src/drivers/driver_nl80211_event.c

index 0c1f401..9800ab2 100644 (file)
@@ -2539,6 +2539,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                        conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
                else if (os_strcmp(pos, "ad") == 0)
                        conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+               else if (os_strcmp(pos, "any") == 0)
+                       conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
                else {
                        wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'",
                                   line, pos);
index 390c753..1c1e43d 100644 (file)
@@ -127,7 +127,9 @@ ssid=test
 
 # Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g,
 # ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to
-# specify band)
+# specify band). When using ACS (see channel parameter), a special value "any"
+# can be used to indicate that any support band can be used. This special case
+# is currently supported only with drivers with which offloaded ACS is used.
 # Default: IEEE 802.11b
 hw_mode=g
 
index 80e4c2e..6ecd094 100644 (file)
@@ -532,7 +532,7 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
 static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
                                         struct acs_selected_channels *acs_res)
 {
-       int ret;
+       int ret, i;
 
        if (hapd->iconf->channel) {
                wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
@@ -540,6 +540,24 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
                return;
        }
 
+       if (!hapd->iface->current_mode) {
+               for (i = 0; i < hapd->iface->num_hw_features; i++) {
+                       struct hostapd_hw_modes *mode =
+                               &hapd->iface->hw_features[i];
+
+                       if (mode->mode == acs_res->hw_mode) {
+                               hapd->iface->current_mode = mode;
+                               break;
+                       }
+               }
+               if (!hapd->iface->current_mode) {
+                       hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_WARNING,
+                                      "driver selected to bad hw_mode");
+                       return;
+               }
+       }
+
        hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
 
        if (!acs_res->pri_channel) {
index 96744c4..069d1ae 100644 (file)
@@ -895,14 +895,18 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
        }
 
        if (iface->current_mode == NULL) {
-               wpa_printf(MSG_ERROR, "Hardware does not support configured "
-                          "mode");
-               hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
-                              HOSTAPD_LEVEL_WARNING,
-                              "Hardware does not support configured mode "
-                              "(%d) (hw_mode in hostapd.conf)",
-                              (int) iface->conf->hw_mode);
-               return -2;
+               if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
+                   !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
+               {
+                       wpa_printf(MSG_ERROR,
+                                  "Hardware does not support configured mode");
+                       hostapd_logger(iface->bss[0], NULL,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_WARNING,
+                                      "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
+                                      (int) iface->conf->hw_mode);
+                       return -2;
+               }
        }
 
        switch (hostapd_check_chans(iface)) {
index 24f80ad..5b2d7c4 100644 (file)
@@ -295,6 +295,7 @@ enum hostapd_hw_mode {
        HOSTAPD_MODE_IEEE80211G,
        HOSTAPD_MODE_IEEE80211A,
        HOSTAPD_MODE_IEEE80211AD,
+       HOSTAPD_MODE_IEEE80211ANY,
        NUM_HOSTAPD_MODES
 };
 
index 140295c..3c35e79 100644 (file)
@@ -221,6 +221,7 @@ enum qca_wlan_vendor_acs_hw_mode {
        QCA_ACS_MODE_IEEE80211G,
        QCA_ACS_MODE_IEEE80211A,
        QCA_ACS_MODE_IEEE80211AD,
+       QCA_ACS_MODE_IEEE80211ANY,
 };
 
 /**
@@ -230,10 +231,13 @@ enum qca_wlan_vendor_acs_hw_mode {
  *     management offload, a mechanism where the station's firmware
  *     does the exchange with the AP to establish the temporal keys
  *     after roaming, rather than having the user space wpa_supplicant do it.
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
+ *     band selection based on channel selection results.
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
        QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD        = 0,
+       QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
        NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
index f7da636..0324339 100644 (file)
@@ -1212,6 +1212,8 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS_HT_IBSS               0x0000001000000000ULL
 /** Driver supports IBSS with VHT datarates */
 #define WPA_DRIVER_FLAGS_VHT_IBSS              0x0000002000000000ULL
+/** Driver supports automatic band selection */
+#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY   0x0000004000000000ULL
        u64 flags;
 
 #define WPA_DRIVER_SMPS_MODE_STATIC                    0x00000001
@@ -4563,6 +4565,7 @@ union wpa_event_data {
         * @ch_width: Selected Channel width by driver. Driver may choose to
         *      change hostapd configured ACS channel width due driver internal
         *      channel restrictions.
+        * hw_mode: Selected band (used with hw_mode=any)
         */
        struct acs_selected_channels {
                u8 pri_channel;
@@ -4570,6 +4573,7 @@ union wpa_event_data {
                u8 vht_seg0_center_ch;
                u8 vht_seg1_center_ch;
                u16 ch_width;
+               enum hostapd_hw_mode hw_mode;
        } acs_selected_channels;
 };
 
index 26e4984..590731d 100644 (file)
@@ -8365,6 +8365,8 @@ static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
                return QCA_ACS_MODE_IEEE80211A;
        case HOSTAPD_MODE_IEEE80211AD:
                return QCA_ACS_MODE_IEEE80211AD;
+       case HOSTAPD_MODE_IEEE80211ANY:
+               return QCA_ACS_MODE_IEEE80211ANY;
        default:
                return -1;
        }
index ba1e240..e23c57e 100644 (file)
@@ -813,6 +813,9 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
 
        if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
                drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
+
+       if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
+               drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
 }
 
 
index 8cebfb2..7b0f721 100644 (file)
@@ -1491,6 +1491,25 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode)
+{
+       switch (hw_mode) {
+       case QCA_ACS_MODE_IEEE80211B:
+               return HOSTAPD_MODE_IEEE80211B;
+       case QCA_ACS_MODE_IEEE80211G:
+               return HOSTAPD_MODE_IEEE80211G;
+       case QCA_ACS_MODE_IEEE80211A:
+               return HOSTAPD_MODE_IEEE80211A;
+       case QCA_ACS_MODE_IEEE80211AD:
+               return HOSTAPD_MODE_IEEE80211AD;
+       case QCA_ACS_MODE_IEEE80211ANY:
+               return HOSTAPD_MODE_IEEE80211ANY;
+       default:
+               return NUM_HOSTAPD_MODES;
+       }
+}
+
+
 static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
                                   const u8 *data, size_t len)
 {
@@ -1520,14 +1539,28 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
        if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
                event.acs_selected_channels.ch_width =
                        nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+       if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
+               u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+
+               event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode);
+               if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+                   event.acs_selected_channels.hw_mode ==
+                   HOSTAPD_MODE_IEEE80211ANY) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Invalid hw_mode %d in ACS selection event",
+                                  hw_mode);
+                       return;
+               }
+       }
 
        wpa_printf(MSG_INFO,
-                  "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d",
+                  "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %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);
+                  event.acs_selected_channels.vht_seg1_center_ch,
+                  event.acs_selected_channels.hw_mode);
 
        /* Ignore ACS channel list check for backwards compatibility */