diff --git a/hostapd/Android.mk b/hostapd/Android.mk index edaf6fc8e..84a877819 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -95,6 +95,7 @@ OBJS += src/ap/preauth_auth.c OBJS += src/ap/pmksa_cache_auth.c OBJS += src/ap/ieee802_11_shared.c OBJS += src/ap/beacon.c +OBJS += src/ap/bss_load.c OBJS_d = OBJS_p = LIBS = diff --git a/hostapd/Makefile b/hostapd/Makefile index ac6373e6c..dc5eacdb7 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -59,6 +59,7 @@ OBJS += ../src/ap/preauth_auth.o OBJS += ../src/ap/pmksa_cache_auth.o OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/beacon.o +OBJS += ../src/ap/bss_load.o OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o diff --git a/hostapd/config_file.c b/hostapd/config_file.c index d4ba7cc0c..ddebf1f46 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2489,6 +2489,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, bss->dtim_period); return 1; } + } else if (os_strcmp(buf, "bss_load_update_period") == 0) { + bss->bss_load_update_period = atoi(pos); + if (bss->bss_load_update_period < 0 || + bss->bss_load_update_period > 100) { + wpa_printf(MSG_ERROR, + "Line %d: invalid bss_load_update_period %d", + line, bss->bss_load_update_period); + return 1; + } } else if (os_strcmp(buf, "rts_threshold") == 0) { conf->rts_threshold = atoi(pos); if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index d4e5bf0af..2e6f841bf 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -435,6 +435,11 @@ wmm_ac_vo_acm=0 # associated stations in the BSS. By default, this bridging is allowed. #ap_isolate=1 +# BSS Load update period (in BUs) +# This field is used to enable and configure adding a BSS Load element into +# Beacon and Probe Response frames. +#bss_load_update_period=50 + # Fixed BSS Load value for testing purposes # This field can be used to configure hostapd to add a fixed BSS Load element # into Beacon and Probe Response frames for testing purposes. The format is diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index e37a79659..b693664e4 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -196,6 +196,7 @@ struct hostapd_bss_config { int max_num_sta; /* maximum number of STAs in station table */ int dtim_period; + int bss_load_update_period; int ieee802_1x; /* use IEEE 802.1X */ int eapol_version; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 4cae0d993..22aef9f60 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -34,16 +34,27 @@ static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) { + if (len < 2 + 5) + return eid; + #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->bss_load_test_set) { - if (2 + 5 > len) - return eid; *eid++ = WLAN_EID_BSS_LOAD; *eid++ = 5; os_memcpy(eid, hapd->conf->bss_load_test, 5); eid += 5; + return eid; } #endif /* CONFIG_TESTING_OPTIONS */ + if (hapd->conf->bss_load_update_period) { + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + WPA_PUT_LE16(eid, hapd->num_sta); + eid += 2; + *eid++ = hapd->iface->channel_utilization; + WPA_PUT_LE16(eid, 0); /* no available admission capabity */ + eid += 2; + } return eid; } diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c new file mode 100644 index 000000000..fb6394232 --- /dev/null +++ b/src/ap/bss_load.c @@ -0,0 +1,65 @@ +/* + * BSS Load Element / Channel Utilization + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "bss_load.h" +#include "ap_drv_ops.h" +#include "beacon.h" + + +static void update_channel_utilization(void *eloop_data, void *user_data) +{ + struct hostapd_data *hapd = eloop_data; + unsigned int sec, usec; + int err; + + if (!(hapd->beacon_set_done && hapd->started)) + return; + + err = hostapd_drv_get_survey(hapd, hapd->iface->freq); + if (err) { + wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data"); + return; + } + + ieee802_11_set_beacon(hapd); + + sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; + usec = (hapd->bss_load_update_timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); +} + + +int bss_load_update_init(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + struct hostapd_config *iconf = hapd->iconf; + unsigned int sec, usec; + + if (!conf->bss_load_update_period || !iconf->beacon_int) + return -1; + + hapd->bss_load_update_timeout = conf->bss_load_update_period * + iconf->beacon_int; + sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; + usec = (hapd->bss_load_update_timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); + return 0; +} + + +void bss_load_update_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(update_channel_utilization, hapd, NULL); +} diff --git a/src/ap/bss_load.h b/src/ap/bss_load.h new file mode 100644 index 000000000..ac3c793c9 --- /dev/null +++ b/src/ap/bss_load.h @@ -0,0 +1,17 @@ +/* + * BSS load update + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BSS_LOAD_UPDATE_H +#define BSS_LOAD_UPDATE_H + + +int bss_load_update_init(struct hostapd_data *hapd); +void bss_load_update_deinit(struct hostapd_data *hapd); + + +#endif /* BSS_LOAD_UPDATE_H */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 3bde7205d..9beb32245 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -858,6 +858,42 @@ static void hostapd_update_nf(struct hostapd_iface *iface, } +static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_res) +{ + struct hostapd_channel_data *chan; + struct freq_survey *survey; + u64 divisor, dividend; + + survey = dl_list_first(&survey_res->survey_list, struct freq_survey, + list); + if (!survey || !survey->freq) + return; + + chan = hostapd_get_mode_channel(iface, survey->freq); + if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED) + return; + + wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", + survey->freq, + (unsigned long int) survey->channel_time, + (unsigned long int) survey->channel_time_busy); + + if (survey->channel_time > iface->last_channel_time && + survey->channel_time > survey->channel_time_busy) { + dividend = survey->channel_time_busy - + iface->last_channel_time_busy; + divisor = survey->channel_time - iface->last_channel_time; + + iface->channel_utilization = dividend * 255 / divisor; + wpa_printf(MSG_DEBUG, "Channel Utilization: %d", + iface->channel_utilization); + } + iface->last_channel_time = survey->channel_time; + iface->last_channel_time_busy = survey->channel_time_busy; +} + + static void hostapd_event_get_survey(struct hostapd_data *hapd, struct survey_results *survey_results) { @@ -870,6 +906,11 @@ static void hostapd_event_get_survey(struct hostapd_data *hapd, return; } + if (survey_results->freq_filter) { + hostapd_single_channel_get_survey(iface, survey_results); + return; + } + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, struct freq_survey, list) { chan = hostapd_get_mode_channel(iface, survey->freq); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 764722f96..be7dfd8cc 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -35,6 +35,7 @@ #include "gas_serv.h" #include "dfs.h" #include "ieee802_11.h" +#include "bss_load.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -310,6 +311,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) gas_serv_deinit(hapd); #endif /* CONFIG_INTERWORKING */ + bss_load_update_deinit(hapd); + #ifdef CONFIG_SQLITE bin_clear_free(hapd->tmp_eap_user.identity, hapd->tmp_eap_user.identity_len); @@ -875,6 +878,11 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) } #endif /* CONFIG_INTERWORKING */ + if (conf->bss_load_update_period && bss_load_update_init(hapd)) { + wpa_printf(MSG_ERROR, "BSS Load initialization failed"); + return -1; + } + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { wpa_printf(MSG_ERROR, "VLAN initialization failed."); return -1; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index ca01a688e..50ebe0eff 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -218,6 +218,9 @@ struct hostapd_data { unsigned int cs_c_off_proberesp; int csa_in_progress; + /* BSS Load */ + unsigned int bss_load_update_timeout; + #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; @@ -354,6 +357,11 @@ struct hostapd_iface { /* lowest observed noise floor in dBm */ s8 lowest_nf; + /* channel utilization calculation */ + u64 last_channel_time; + u64 last_channel_time_busy; + u8 channel_utilization; + unsigned int dfs_cac_ms; struct os_reltime dfs_cac_start; diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 8b7df6c77..7d1bb9eae 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -767,6 +767,7 @@ OBJS += src/ap/ieee802_11_shared.c OBJS += src/ap/drv_callbacks.c OBJS += src/ap/ap_drv_ops.c OBJS += src/ap/beacon.c +OBJS += src/ap/bss_load.c OBJS += src/ap/eap_user_db.c ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 7eee73a9d..6556ec48a 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -782,6 +782,7 @@ OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/drv_callbacks.o OBJS += ../src/ap/ap_drv_ops.o OBJS += ../src/ap/beacon.o +OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/eap_user_db.o ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o