diff options
Diffstat (limited to 'contrib/wpa/src/ap/hw_features.c')
-rw-r--r-- | contrib/wpa/src/ap/hw_features.c | 373 |
1 files changed, 308 insertions, 65 deletions
diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c index c1f19e26b550..4b66b02f4e16 100644 --- a/contrib/wpa/src/ap/hw_features.c +++ b/contrib/wpa/src/ap/hw_features.c @@ -224,16 +224,27 @@ int hostapd_prepare_rates(struct hostapd_iface *iface, } -#ifdef CONFIG_IEEE80211N static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) { - int pri_chan, sec_chan; + int pri_freq, sec_freq; + struct hostapd_channel_data *p_chan, *s_chan; - pri_chan = iface->conf->channel; - sec_chan = pri_chan + iface->conf->secondary_channel * 4; + pri_freq = iface->freq; + sec_freq = pri_freq + iface->conf->secondary_channel * 20; + + if (!iface->current_mode) + return 0; + + p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL, + iface->hw_features, + iface->num_hw_features); - return allowed_ht40_channel_pair(iface->current_mode, pri_chan, - sec_chan); + s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL, + iface->hw_features, + iface->num_hw_features); + + return allowed_ht40_channel_pair(iface->current_mode->mode, + p_chan, s_chan); } @@ -241,9 +252,11 @@ static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) { if (iface->conf->secondary_channel > 0) { iface->conf->channel += 4; + iface->freq += 20; iface->conf->secondary_channel = -1; } else { iface->conf->channel -= 4; + iface->freq -= 20; iface->conf->secondary_channel = 1; } } @@ -252,13 +265,23 @@ static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, struct wpa_scan_results *scan_res) { - int pri_chan, sec_chan; + unsigned int pri_freq, sec_freq; int res; + struct hostapd_channel_data *pri_chan, *sec_chan; - pri_chan = iface->conf->channel; - sec_chan = pri_chan + iface->conf->secondary_channel * 4; + pri_freq = iface->freq; + sec_freq = pri_freq + iface->conf->secondary_channel * 20; + + if (!iface->current_mode) + return 0; + pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, + NULL, iface->hw_features, + iface->num_hw_features); + sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, + NULL, iface->hw_features, + iface->num_hw_features); - res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan); + res = check_40mhz_5g(scan_res, pri_chan, sec_chan); if (res == 2) { if (iface->conf->no_pri_sec_switch) { @@ -290,7 +313,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) { struct wpa_scan_results *scan_res; int oper40; - int res; + int res = 0; /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ @@ -326,7 +349,24 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) } } - res = ieee80211n_allowed_ht40_channel_pair(iface); +#ifdef CONFIG_IEEE80211AX + if (iface->conf->secondary_channel && + iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && + iface->conf->ieee80211ax) { + struct he_capabilities *he_cap; + + he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP]; + if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & + HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) { + wpa_printf(MSG_DEBUG, + "HE: 40 MHz channel width is not supported in 2.4 GHz; clear secondary channel configuration"); + iface->conf->secondary_channel = 0; + } + } +#endif /* CONFIG_IEEE80211AX */ + + if (iface->conf->secondary_channel) + res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0); @@ -352,7 +392,7 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, if (iface->current_mode == NULL) return; - pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + pri_freq = iface->freq; if (iface->conf->secondary_channel > 0) sec_freq = pri_freq + 20; else @@ -397,7 +437,7 @@ static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, if (iface->current_mode == NULL) return; - pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + pri_freq = iface->freq; if (iface->conf->secondary_channel > 0) { affected_start = pri_freq - 10; affected_end = pri_freq + 30; @@ -537,26 +577,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 0; } - switch (conf & HT_CAP_INFO_SMPS_MASK) { - case HT_CAP_INFO_SMPS_STATIC: - if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) { - wpa_printf(MSG_ERROR, - "Driver does not support configured HT capability [SMPS-STATIC]"); - return 0; - } - break; - case HT_CAP_INFO_SMPS_DYNAMIC: - if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) { - wpa_printf(MSG_ERROR, - "Driver does not support configured HT capability [SMPS-DYNAMIC]"); - return 0; - } - break; - case HT_CAP_INFO_SMPS_DISABLED: - default: - break; - } - if ((conf & HT_CAP_INFO_GREEN_FIELD) && !(hw & HT_CAP_INFO_GREEN_FIELD)) { wpa_printf(MSG_ERROR, "Driver does not support configured " @@ -663,13 +683,13 @@ static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface) } #endif /* CONFIG_IEEE80211AX */ -#endif /* CONFIG_IEEE80211N */ - int hostapd_check_ht_capab(struct hostapd_iface *iface) { -#ifdef CONFIG_IEEE80211N int ret; + + if (is_6ghz_freq(iface->freq)) + return 0; if (!iface->conf->ieee80211n) return 0; @@ -698,21 +718,92 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) return ret; if (!ieee80211n_allowed_ht40_channel_pair(iface)) return -1; -#endif /* CONFIG_IEEE80211N */ return 0; } +int hostapd_check_edmg_capab(struct hostapd_iface *iface) +{ + struct hostapd_hw_modes *mode = iface->hw_features; + struct ieee80211_edmg_config edmg; + + if (!iface->conf->enable_edmg) + return 0; + + hostapd_encode_edmg_chan(iface->conf->enable_edmg, + iface->conf->edmg_channel, + iface->conf->channel, + &edmg); + + if (mode->edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg)) + return 0; + + wpa_printf(MSG_WARNING, "Requested EDMG configuration is not valid"); + wpa_printf(MSG_INFO, "EDMG capab: channels 0x%x, bw_config %d", + mode->edmg.channels, mode->edmg.bw_config); + wpa_printf(MSG_INFO, + "Requested EDMG configuration: channels 0x%x, bw_config %d", + edmg.channels, edmg.bw_config); + return -1; +} + + +int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface) +{ +#ifdef CONFIG_IEEE80211AX + struct he_capabilities *he_cap; + u16 hw; + + if (!iface->current_mode || !is_6ghz_freq(iface->freq)) + return 0; + + he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP]; + hw = he_cap->he_6ghz_capa; + if (iface->conf->he_6ghz_max_mpdu > + ((hw & HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK) >> + HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Max MPDU length"); + return -1; + } + + if (iface->conf->he_6ghz_max_ampdu_len_exp > + ((hw & HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK) >> + HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Max AMPDU Length Exponent"); + return -1; + } + + if (iface->conf->he_6ghz_rx_ant_pat && + !(hw & HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Rx Antenna Pattern"); + return -1; + } + + if (iface->conf->he_6ghz_tx_ant_pat && + !(hw & HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Tx Antenna Pattern"); + return -1; + } +#endif /* CONFIG_IEEE80211AX */ + return 0; +} + + static int hostapd_is_usable_chan(struct hostapd_iface *iface, - int channel, int primary) + int frequency, int primary) { struct hostapd_channel_data *chan; if (!iface->current_mode) return 0; - chan = hw_get_channel_chan(iface->current_mode, channel, NULL); + chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL, + iface->hw_features, iface->num_hw_features); if (!chan) return 0; @@ -721,8 +812,8 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, return 1; wpa_printf(MSG_INFO, - "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s", - channel, primary ? "primary" : "secondary", + "Frequency %d (%s) not allowed for AP mode, flags: 0x%x%s%s", + frequency, primary ? "primary" : "secondary", chan->flag, chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); @@ -730,37 +821,125 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, } -static int hostapd_is_usable_chans(struct hostapd_iface *iface) +static int hostapd_is_usable_edmg(struct hostapd_iface *iface) { - int secondary_chan; + int i, contiguous = 0; + int num_of_enabled = 0; + int max_contiguous = 0; + struct ieee80211_edmg_config edmg; struct hostapd_channel_data *pri_chan; - pri_chan = hw_get_channel_chan(iface->current_mode, - iface->conf->channel, NULL); + if (!iface->conf->enable_edmg) + return 1; + + if (!iface->current_mode) + return 0; + pri_chan = hw_get_channel_freq(iface->current_mode->mode, + iface->freq, NULL, + iface->hw_features, + iface->num_hw_features); if (!pri_chan) return 0; + hostapd_encode_edmg_chan(iface->conf->enable_edmg, + iface->conf->edmg_channel, + pri_chan->chan, + &edmg); + if (!(edmg.channels & BIT(pri_chan->chan - 1))) + return 0; + + /* 60 GHz channels 1..6 */ + for (i = 0; i < 6; i++) { + int freq = 56160 + 2160 * (i + 1); - if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) + if (edmg.channels & BIT(i)) { + contiguous++; + num_of_enabled++; + } else { + contiguous = 0; + continue; + } + + /* P802.11ay defines that the total number of subfields + * set to one does not exceed 4. + */ + if (num_of_enabled > 4) + return 0; + + if (!hostapd_is_usable_chan(iface, freq, 1)) + return 0; + + if (contiguous > max_contiguous) + max_contiguous = contiguous; + } + + /* Check if the EDMG configuration is valid under the limitations + * of P802.11ay. + */ + /* check bw_config against contiguous EDMG channels */ + switch (edmg.bw_config) { + case EDMG_BW_CONFIG_4: + if (!max_contiguous) + return 0; + break; + case EDMG_BW_CONFIG_5: + if (max_contiguous < 2) + return 0; + break; + default: + return 0; + } + + return 1; +} + + +static int hostapd_is_usable_chans(struct hostapd_iface *iface) +{ + int secondary_freq; + struct hostapd_channel_data *pri_chan; + + if (!iface->current_mode) + return 0; + pri_chan = hw_get_channel_freq(iface->current_mode->mode, + iface->freq, NULL, + iface->hw_features, + iface->num_hw_features); + if (!pri_chan) { + wpa_printf(MSG_ERROR, "Primary frequency not present"); + return 0; + } + if (!hostapd_is_usable_chan(iface, pri_chan->freq, 1)) { + wpa_printf(MSG_ERROR, "Primary frequency not allowed"); + return 0; + } + if (!hostapd_is_usable_edmg(iface)) return 0; if (!iface->conf->secondary_channel) return 1; + if (hostapd_is_usable_chan(iface, iface->freq + + iface->conf->secondary_channel * 20, 0)) { + if (iface->conf->secondary_channel == 1 && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) + return 1; + if (iface->conf->secondary_channel == -1 && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) + return 1; + } if (!iface->conf->ht40_plus_minus_allowed) - return hostapd_is_usable_chan( - iface, iface->conf->channel + - iface->conf->secondary_channel * 4, 0); + return 0; /* Both HT40+ and HT40- are set, pick a valid secondary channel */ - secondary_chan = iface->conf->channel + 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + secondary_freq = iface->freq + 20; + if (hostapd_is_usable_chan(iface, secondary_freq, 0) && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { iface->conf->secondary_channel = 1; return 1; } - secondary_chan = iface->conf->channel - 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + secondary_freq = iface->freq - 20; + if (hostapd_is_usable_chan(iface, secondary_freq, 0) && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { iface->conf->secondary_channel = -1; return 1; @@ -770,10 +949,43 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) } +static void hostapd_determine_mode(struct hostapd_iface *iface) +{ + int i; + enum hostapd_hw_mode target_mode; + + if (iface->current_mode || + iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY) + return; + + if (iface->freq < 4000) + target_mode = HOSTAPD_MODE_IEEE80211G; + else if (iface->freq > 50000) + target_mode = HOSTAPD_MODE_IEEE80211AD; + else + target_mode = HOSTAPD_MODE_IEEE80211A; + + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode; + + mode = &iface->hw_features[i]; + if (mode->mode == target_mode) { + iface->current_mode = mode; + iface->conf->hw_mode = mode->mode; + break; + } + } + + if (!iface->current_mode) + wpa_printf(MSG_ERROR, "ACS: Cannot decide mode"); +} + + static enum hostapd_chan_status hostapd_check_chans(struct hostapd_iface *iface) { - if (iface->conf->channel) { + if (iface->freq) { + hostapd_determine_mode(iface); if (hostapd_is_usable_chans(iface)) return HOSTAPD_CHAN_VALID; else @@ -807,9 +1019,9 @@ static void hostapd_notify_bad_chans(struct hostapd_iface *iface) hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, - "Configured channel (%d) not found from the " - "channel list of current mode (%d) %s", + "Configured channel (%d) or frequency (%d) (secondary_channel=%d) not found from the channel list of the current mode (%d) %s", iface->conf->channel, + iface->freq, iface->conf->secondary_channel, iface->current_mode->mode, hostapd_hw_mode_txt(iface->current_mode->mode)); hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, @@ -829,9 +1041,7 @@ int hostapd_acs_completed(struct hostapd_iface *iface, int err) case HOSTAPD_CHAN_VALID: wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_COMPLETED "freq=%d channel=%d", - hostapd_hw_get_freq(iface->bss[0], - iface->conf->channel), - iface->conf->channel); + iface->freq, iface->conf->channel); break; case HOSTAPD_CHAN_ACS: wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available"); @@ -889,16 +1099,28 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) iface->current_mode = NULL; for (i = 0; i < iface->num_hw_features; i++) { struct hostapd_hw_modes *mode = &iface->hw_features[i]; + int chan; + if (mode->mode == iface->conf->hw_mode) { + if (iface->freq > 0 && + !hw_mode_get_channel(mode, iface->freq, &chan)) + continue; + iface->current_mode = mode; break; } } if (iface->current_mode == NULL) { - if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || - !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) - { + if ((iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && + (iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) { + wpa_printf(MSG_DEBUG, + "Using offloaded hw_mode=any ACS"); + } else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && + iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211ANY) { + wpa_printf(MSG_DEBUG, + "Using internal ACS for hw_mode=any"); + } else { wpa_printf(MSG_ERROR, "Hardware does not support configured mode"); hostapd_logger(iface->bss[0], NULL, @@ -952,7 +1174,9 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) struct hostapd_hw_modes *mode; if (hapd->iface->current_mode) { - channel = hw_get_chan(hapd->iface->current_mode, freq); + channel = hw_get_chan(hapd->iface->current_mode->mode, freq, + hapd->iface->hw_features, + hapd->iface->num_hw_features); if (channel) return channel; } @@ -963,9 +1187,28 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) return 0; for (i = 0; i < hapd->iface->num_hw_features; i++) { mode = &hapd->iface->hw_features[i]; - channel = hw_get_chan(mode, freq); + channel = hw_get_chan(mode->mode, freq, + hapd->iface->hw_features, + hapd->iface->num_hw_features); if (channel) return channel; } return 0; } + + +int hostapd_hw_skip_mode(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode) +{ + int i; + + if (iface->current_mode) + return mode != iface->current_mode; + if (mode->mode != HOSTAPD_MODE_IEEE80211B) + return 0; + for (i = 0; i < iface->num_hw_features; i++) { + if (iface->hw_features[i].mode == HOSTAPD_MODE_IEEE80211G) + return 1; + } + return 0; +} |