diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 0c1f401b2..9800ab295 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -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); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 390c75361..1c1e43d1d 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -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 diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 80e4c2e7c..6ecd094ac 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -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) { diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 96744c4f3..069d1ae53 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -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)) { diff --git a/src/common/defs.h b/src/common/defs.h index 24f80ad4a..5b2d7c42b 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -295,6 +295,7 @@ enum hostapd_hw_mode { HOSTAPD_MODE_IEEE80211G, HOSTAPD_MODE_IEEE80211A, HOSTAPD_MODE_IEEE80211AD, + HOSTAPD_MODE_IEEE80211ANY, NUM_HOSTAPD_MODES }; diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 140295cfa..3c35e796f 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -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 */ }; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index f7da63669..032433992 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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; }; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 26e4984d6..590731d23 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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; } diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index ba1e24021..e23c57edd 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -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; } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 8cebfb256..7b0f721e6 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -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 */