diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 0ec170131..2beb34c05 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2915,6 +2915,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } conf->local_pwr_constraint = val; + } else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) { + conf->spectrum_mgmt_required = atoi(pos); } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration " "item '%s'", line, buf); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 63232d750..3733a32c4 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -115,6 +115,13 @@ ssid=test # Valid values are 0..255. #local_pwr_constraint=3 +# Set Spectrum Management subfield in the Capability Information field. +# This config option forces the Spectrum Management bit to be set. When this +# option is not set, the value of the Spectrum Management bit depends on whether +# DFS or TPC is required by regulatory authorities. This can be used only with +# ieee80211d=1 and local_pwr_constraint configured. +#spectrum_mgmt_required=1 + # 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) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index a9e8bfac1..f5014a5cc 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -837,6 +837,12 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config) return -1; } + if (full_config && conf->spectrum_mgmt_required && + conf->local_pwr_constraint == -1) { + wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements"); + return -1; + } + for (i = 0; i < conf->num_bss; i++) { if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) return -1; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 26a0ef6c9..eda193b33 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -526,6 +526,9 @@ struct hostapd_config { */ int local_pwr_constraint; + /* Control Spectrum Management bit */ + int spectrum_mgmt_required; + struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; /* diff --git a/src/ap/beacon.c b/src/ap/beacon.c index d84af9534..33320bd48 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -27,6 +27,7 @@ #include "ap_drv_ops.h" #include "beacon.h" #include "hs20.h" +#include "dfs.h" #ifdef NEED_AP_MLME @@ -105,12 +106,43 @@ static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; + u8 local_pwr_constraint = 0; + int dfs; - if (hapd->iconf->local_pwr_constraint == -1 || - hapd->iface->current_mode == NULL || + if (hapd->iface->current_mode == NULL || hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) return eid; + /* + * There is no DFS support and power constraint was not directly + * requested by config option. + */ + if (!hapd->iconf->ieee80211h && + hapd->iconf->local_pwr_constraint == -1) + return eid; + + /* Check if DFS is required by regulatory. */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) { + wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", + dfs); + dfs = 0; + } + + if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1) + return eid; + + /* + * ieee80211h (DFS) is enabled so Power Constraint element shall + * be added when running on DFS channel whenever local_pwr_constraint + * is configured or not. In order to meet regulations when TPC is not + * implemented using a transmit power that is below the legal maximum + * (including any mitigation factor) should help. In this case, + * indicate 3 dB below maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = 3; + /* * A STA that is not an AP shall use a transmit power less than or * equal to the local maximum transmit power level for the channel. @@ -126,7 +158,10 @@ static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid) /* Length */ *pos++ = 1; /* Local Power Constraint */ - *pos++ = hapd->iconf->local_pwr_constraint; + if (local_pwr_constraint) + *pos++ = local_pwr_constraint; + else + *pos++ = hapd->iconf->local_pwr_constraint; return pos; } diff --git a/src/ap/dfs.c b/src/ap/dfs.c index ec691db3a..47e5002a1 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -801,3 +801,23 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); return 0; } + + +int hostapd_is_dfs_required(struct hostapd_iface *iface) +{ + int n_chans, start_chan_idx; + + if (!iface->current_mode) + return -1; + + /* Get start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface); + if (start_chan_idx == -1) + return -1; + + /* Get number of used channels, depend on width */ + n_chans = dfs_get_used_n_chans(iface); + + /* Check if any of configured channels require DFS */ + return dfs_check_chans_radar(iface, start_chan_idx, n_chans); +} diff --git a/src/ap/dfs.h b/src/ap/dfs.h index 859ff7915..a619c55c2 100644 --- a/src/ap/dfs.h +++ b/src/ap/dfs.h @@ -21,5 +21,6 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); +int hostapd_is_dfs_required(struct hostapd_iface *iface); #endif /* DFS_H */ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 9251ac3a5..f3e0df313 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -38,6 +38,7 @@ #include "ap_drv_ops.h" #include "wnm_ap.h" #include "ieee802_11.h" +#include "dfs.h" u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) @@ -137,6 +138,15 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, { int capab = WLAN_CAPABILITY_ESS; int privacy; + int dfs; + + /* Check if any of configured channels require DFS */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) { + wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", + dfs); + dfs = 0; + } if (hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE) @@ -174,6 +184,17 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, hapd->iface->num_sta_no_short_slot_time == 0) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + /* + * Currently, Spectrum Management capability bit is set when directly + * requested in configuration by spectrum_mgmt_required or when AP is + * running on DFS channel. + * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit + */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + (hapd->iconf->spectrum_mgmt_required || dfs)) + capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; + return capab; }