We are abandoning these changes as it's hard to assess the value of factoring noise floor measurements into a background scan initiation decision. Please drop this patch from consideration. On Tue, Apr 9, 2019 at 4:56 PM <npoojary@xxxxxxxxxxxx> wrote: > > From: Neeraj Poojary <npoojary@xxxxxxxxxxxx> > > The signal strength number tells only half of the picture with > respect to signal quality. The other half is the prevailing > noise on the channel. The two together indicate how easily the > interface is able to pull data out of the background noise. > > This change takes noise into account, by calculating a CQM > threshold based both on the RSSI and noise. This noise value is > available in the CQM events as well as polled data. Since most > background scan algorithms will probably want to do this the same > way, I have added this as routines in bgscan.c that can be > utilized by individual background scan mechanisms. > > Additionally, I've modified bgscan_simple to use this mechanism, > and scheduled noisefloor updates to happen at the same rate as > background scans (the noisefloor should be a slowly changing > parameter). > > This patch also includes changes to the background scan procedure > to make background scans low priority and resume aborted scans from > frequencies that haven't been scanned yet. > > Signed-off-by: Neeraj Poojary <npoojary@xxxxxxxxxxxx> > --- > src/drivers/driver.h | 2 + > wpa_supplicant/bgscan.c | 85 ++++++++++++ > wpa_supplicant/bgscan_i.h | 66 ++++++++++ > wpa_supplicant/bgscan_simple.c | 227 +++++++++++++++++++++++++++++---- > wpa_supplicant/scan.c | 1 + > 5 files changed, 355 insertions(+), 26 deletions(-) > create mode 100644 wpa_supplicant/bgscan_i.h > > diff --git a/src/drivers/driver.h b/src/drivers/driver.h > index 4ac9f16a0..b4ac6dbdd 100644 > --- a/src/drivers/driver.h > +++ b/src/drivers/driver.h > @@ -314,11 +314,13 @@ struct wpa_scan_res { > * @res: Array of pointers to allocated variable length scan result entries > * @num: Number of entries in the scan result array > * @fetch_time: Time when the results were fetched from the driver > + * @aborted: Whether the scan was aborted > */ > struct wpa_scan_results { > struct wpa_scan_res **res; > size_t num; > struct os_reltime fetch_time; > + int aborted; > }; > > /** > diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c > index 1ea640114..544f62512 100644 > --- a/wpa_supplicant/bgscan.c > +++ b/wpa_supplicant/bgscan.c > @@ -9,9 +9,12 @@ > #include "includes.h" > > #include "common.h" > +#include "eloop.h" > #include "wpa_supplicant_i.h" > #include "config_ssid.h" > +#include "driver_i.h" > #include "bgscan.h" > +#include "bgscan_i.h" > > > static const struct bgscan_ops * bgscan_modules[] = { > @@ -107,3 +110,85 @@ void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, > current_noise, > current_txrate); > } > + > +static void bgscan_apply_signal_monitor(void *eloop_ctx, void *timeout_ctx) > +{ > + struct bgscan_signal_monitor_state *sm_state = eloop_ctx; > + > + wpa_drv_signal_monitor(sm_state->wpa_s, sm_state->calc_threshold, > + sm_state->hysteresis); > +} > + > + > +void bgscan_update_signal_monitor(struct bgscan_signal_monitor_state *sm_state, > + int current_signal, int current_noise) > +{ > + int threshold = current_noise + sm_state->headroom; > + > + if (current_noise >= 0) > + return; > + > + if (threshold >= sm_state->calc_threshold - > + BGSCAN_NOISEFLOOR_TOLERANCE && > + threshold <= sm_state->calc_threshold + > + BGSCAN_NOISEFLOOR_TOLERANCE) > + return; > + > + wpa_printf(MSG_DEBUG, "%s: noisefloor update: %d -> %d", > + __func__, sm_state->calc_threshold - sm_state->headroom, > + current_noise); > + > + sm_state->calc_threshold = threshold; > + > + /* > + * Schedule a noisefloor adjustment. Do this as a timeout callback, > + * so it is implicitly throttled. > + */ > + eloop_cancel_timeout(bgscan_apply_signal_monitor, sm_state, NULL); > + eloop_register_timeout(BGSCAN_NOISEFLOOR_UPDATE_DELAY, 0, > + bgscan_apply_signal_monitor, sm_state, NULL); > +} > + > +int bgscan_poll_signal_monitor(struct bgscan_signal_monitor_state *sm_state, > + struct wpa_signal_info *siginfo_ret) > +{ > + struct wpa_signal_info siginfo; > + int ret; > + > + ret = wpa_drv_signal_poll(sm_state->wpa_s, &siginfo); > + if (ret != 0) > + return ret; > + > + wpa_printf(MSG_DEBUG, "%s: bgscan poll noisefloor: %d ", > + __func__, siginfo.current_noise); > + > + bgscan_update_signal_monitor(sm_state, siginfo.current_signal, > + siginfo.current_noise); > + > + if (siginfo_ret != 0) > + memcpy(siginfo_ret, &siginfo, sizeof(siginfo)); > + > + return 0; > +} > + > +void bgscan_init_signal_monitor(struct bgscan_signal_monitor_state *sm_state, > + struct wpa_supplicant *wpa_s, > + int signal_threshold, > + int hysteresis) { > + > + sm_state->wpa_s = wpa_s; > + sm_state->calc_threshold = signal_threshold; > + sm_state->hysteresis = hysteresis; > + sm_state->headroom = signal_threshold - BGSCAN_DEFAULT_NOISE_FLOOR; > + > + if (wpa_drv_signal_monitor(wpa_s, signal_threshold, hysteresis) < 0) > + wpa_printf(MSG_ERROR, "bgscan simple: Failed to enable " > + "signal strength monitoring"); > +} > + > +void bgscan_deinit_signal_monitor(struct bgscan_signal_monitor_state *sm_state) > +{ > + wpa_drv_signal_monitor(sm_state->wpa_s, 0, 0); > + eloop_cancel_timeout(bgscan_apply_signal_monitor, sm_state, NULL); > +} > + > diff --git a/wpa_supplicant/bgscan_i.h b/wpa_supplicant/bgscan_i.h > new file mode 100644 > index 000000000..42b14df20 > --- /dev/null > +++ b/wpa_supplicant/bgscan_i.h > @@ -0,0 +1,66 @@ > +/* > + * WPA Supplicant - background scan and roaming interface > + * Copyright (c) 2009-2010, Jouni Malinen <j@xxxxx> > + * > + * This software may be distributed under the terms of the BSD license. > + * See README for more details. > + */ > + > +#ifndef BGSCAN_I_H > +#define BGSCAN_I_H > + > +/* > + * The signal monitoring code is an optional facility for bgscan algorithms > + * that want to track both signal strength and noise floor (e.g. so they can > + * make decisions based on received signal strength relative to noise floor). > + * > + * calc_threshold: Signal strength threshold for generating CQM events. > + * When signal strength passes above or below this value CQM events > + * are generated. rssi_threshold is initialized from user-specified > + * options to the algorithm and then recalculated based on the current > + * noise floor. > + * headroom: The the threshold for signal above the noisefloor for > + * generating CQM events. headroom is calculated at initialization > + * from the user-specified signal strength and then used to calculate > + * calc_threshold using the current noise floor. > + * hysteresis: Hysterisis value passed into the driver CQM to indicate > + * how large a delta in received signal (in dBm) from the last CQM > + * event should trigger another CQM event. > + */ > +struct bgscan_signal_monitor_state { > + struct wpa_supplicant *wpa_s; > + int calc_threshold; > + int headroom; > + int hysteresis; > +}; > + > +void bgscan_init_signal_monitor(struct bgscan_signal_monitor_state *sm_state, > + struct wpa_supplicant *wpa_s, > + int signal_threshold, > + int hysteresis); > +void bgscan_deinit_signal_monitor(struct bgscan_signal_monitor_state *sm_state); > +void bgscan_update_signal_monitor(struct bgscan_signal_monitor_state *sm_state, > + int current_signal, int current_noise); > +int bgscan_poll_signal_monitor(struct bgscan_signal_monitor_state *sm_state, > + struct wpa_signal_info *siginfo_ret); > + > + > +/* > + * The time (secs) to delay updates to the CQM monitoring parameters. This is > + * done to collapse rapid changes into a single request. > + */ > +#define BGSCAN_NOISEFLOOR_UPDATE_DELAY 10 > + > +/* > + * The starting/default noise floor for the channel (dBm). This also > + * serves as the reference noise floor for user-specified signal strength > + * values in bgscan algorithms that use these facilities. > + */ > +#define BGSCAN_DEFAULT_NOISE_FLOOR -95 > + > +/* > + * Range [+/-] for determining if the noise floor has changed enough for > + * us to adjust the RSSI threshold. > + */ > +#define BGSCAN_NOISEFLOOR_TOLERANCE 1 > +#endif /* BGSCAN_I_H */ > diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c > index 41a26df0d..ae7927aba 100644 > --- a/wpa_supplicant/bgscan_simple.c > +++ b/wpa_supplicant/bgscan_simple.c > @@ -16,6 +16,7 @@ > #include "driver_i.h" > #include "scan.h" > #include "bgscan.h" > +#include "bgscan_i.h" > > struct bgscan_simple_data { > struct wpa_supplicant *wpa_s; > @@ -27,9 +28,56 @@ struct bgscan_simple_data { > int short_interval; /* use if signal < threshold */ > int long_interval; /* use if signal > threshold */ > struct os_reltime last_bgscan; > + int *supp_freqs; > + int n_supp_freqs; > + int *scan_freqs; > + int freq_idx; > + struct bgscan_signal_monitor_state signal_monitor; > }; > > > +static int * bgscan_simple_get_freqs(struct bgscan_simple_data *data) > +{ > + int *freqs = data->scan_freqs; > + int i, j; > + > + if (data->supp_freqs == NULL) > + return NULL; > + > + if (freqs == NULL) > + return NULL; > + > + j = 0; > + for (i = data->freq_idx; i < data->n_supp_freqs; i++) > + freqs[j++] = data->supp_freqs[i]; > + for (i = 0; i < data->freq_idx; i++) > + freqs[j++] = data->supp_freqs[i]; > + freqs[j] = 0; /* NB: terminator expected elsewhere */ > + > + return freqs; > +} > + > + > +#define BGSCAN_FREQ_LIST_MAX_STRLEN 1000 > +static void log_freqs(const char *tag, const int freqs[]) > +{ > + char msg[BGSCAN_FREQ_LIST_MAX_STRLEN]; > + int i, pos; > + > + msg[0] = '\0'; > + for (i = 0, pos = 0; freqs[i] != 0 && pos < BGSCAN_FREQ_LIST_MAX_STRLEN; i++) { > + int ret; > + ret = os_snprintf(&msg[pos], BGSCAN_FREQ_LIST_MAX_STRLEN - pos, " %d", > + freqs[i]); > + if (ret < 0 || ret >= BGSCAN_FREQ_LIST_MAX_STRLEN - pos) > + break; > + pos += ret; > + } > + msg[pos] = '\0'; > + wpa_printf(MSG_DEBUG, "bgscan simple: %s frequencies:%s", tag, msg); > +} > + > + > static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) > { > struct bgscan_simple_data *data = eloop_ctx; > @@ -40,19 +88,26 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) > params.num_ssids = 1; > params.ssids[0].ssid = data->ssid->ssid; > params.ssids[0].ssid_len = data->ssid->ssid_len; > - params.freqs = data->ssid->scan_freq; > > - /* > - * A more advanced bgscan module would learn about most like channels > - * over time and request scans only for some channels (probing others > - * every now and then) to reduce effect on the data connection. > - */ > + if (data->ssid->scan_freq == NULL) > + params.freqs = bgscan_simple_get_freqs(data); > + else > + params.freqs = data->ssid->scan_freq; > > - wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan"); > + params.low_priority = 1; > + > + wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan " > + "with reason %s", > + timeout_ctx ? (char *)timeout_ctx : "None"); > + if (params.freqs != NULL) > + log_freqs("Scanning", params.freqs); > if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { > wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan"); > + eloop_cancel_timeout(bgscan_simple_timeout, data, > + ELOOP_ALL_CTX); > eloop_register_timeout(data->scan_interval, 0, > - bgscan_simple_timeout, data, NULL); > + bgscan_simple_timeout, data, > + "failed-scan"); > } else { > if (data->scan_interval == data->short_interval) { > data->short_scan_count++; > @@ -100,6 +155,70 @@ static int bgscan_simple_get_params(struct bgscan_simple_data *data, > } > > > +static int in_array(const int *array, int v, int arraylen) > +{ > + int i; > + > + if (array == NULL) > + return 0; > + > + for (i = 0; i < arraylen; i++) > + if (array[i] == v) > + return 1; > + return 0; > +} > + > + > +static void bgscan_simple_setup_freqs(struct wpa_supplicant *wpa_s, > + struct bgscan_simple_data *data) > +{ > + struct hostapd_hw_modes *modes; > + const struct hostapd_hw_modes *infra; > + u16 num_modes, flags; > + int i, j, *freqs; > + size_t count; > + u8 dfs_domain; > + > + data->supp_freqs = NULL; > + data->freq_idx = 0; > + > + modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, &flags, &dfs_domain); > + if (!modes) > + return; > + > + count = 0; > + freqs = NULL; > + for (i = 0; i < num_modes; i++) { > + for (j = 0; j < modes[i].num_channels; j++) { > + int freq, *n; > + > + if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED) > + continue; > + freq = modes[i].channels[j].freq; > + if (in_array(freqs, freq, count)) /* NB: de-dup list */ > + continue; > + n = os_realloc(freqs, (count + 2) * sizeof(int)); > + if (n != NULL) { > + freqs = n; > + freqs[count++] = freq; > + freqs[count] = 0; > + } > + } > + os_free(modes[i].channels); > + os_free(modes[i].rates); > + } > + os_free(modes); > + > + if (freqs != NULL) { > + data->supp_freqs = freqs; > + data->n_supp_freqs = count; > + data->scan_freqs = os_malloc((count + 1) * sizeof(int)); > + > + log_freqs("Supported", freqs); > + } > +} > + > + > static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, > const char *params, > const struct wpa_ssid *ssid) > @@ -125,23 +244,25 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, > data->signal_threshold, data->short_interval, > data->long_interval); > > - if (data->signal_threshold && > - wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) { > - wpa_printf(MSG_ERROR, "bgscan simple: Failed to enable " > - "signal strength monitoring"); > - } > - > data->scan_interval = data->short_interval; > data->max_short_scans = data->long_interval / data->short_interval + 1; > if (data->signal_threshold) { > - /* Poll for signal info to set initial scan interval */ > struct wpa_signal_info siginfo; > - if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 && > + > + bgscan_init_signal_monitor(&data->signal_monitor, wpa_s, > + data->signal_threshold, 4); > + > + /* Poll for signal info to set initial scan interval */ > + if (bgscan_poll_signal_monitor(&data->signal_monitor, > + &siginfo) == 0 && > siginfo.current_signal >= data->signal_threshold) > data->scan_interval = data->long_interval; > } > wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d", > data->scan_interval); > + > + bgscan_simple_setup_freqs(wpa_s, data); > + > eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, > data, NULL); > > @@ -160,23 +281,70 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, > static void bgscan_simple_deinit(void *priv) > { > struct bgscan_simple_data *data = priv; > - eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); > + eloop_cancel_timeout(bgscan_simple_timeout, data, ELOOP_ALL_CTX); > if (data->signal_threshold) > - wpa_drv_signal_monitor(data->wpa_s, 0, 0); > + bgscan_deinit_signal_monitor(&data->signal_monitor); > + os_free(data->supp_freqs); > + os_free(data->scan_freqs); > os_free(data); > } > > > +static int find_freq_index(const struct bgscan_simple_data *data, int freq) > +{ > + int ix; > + > + for (ix = data->freq_idx; ix < data->n_supp_freqs; ix++) > + if (freq == data->supp_freqs[ix]) > + return ix; > + for (ix = 0; ix < data->freq_idx; ix++) > + if (freq == data->supp_freqs[ix]) > + return ix; > + return -1; > +} > + > static int bgscan_simple_notify_scan(void *priv, > struct wpa_scan_results *scan_res) > { > struct bgscan_simple_data *data = priv; > + char *bgscan_reason = NULL; > > wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification"); > > - eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); > + if (scan_res->aborted && data->supp_freqs != NULL) { > + int last_freq, i, idx; > + /* > + * Scan was aborted; advance the rotor past known > + * channels visited. This does not take into account > + * channels that were visited but had no scan results. > + * This should be ok as we always supply a complete > + * frequency list when we scan. > + * > + * NB: can't depend on scan results order matching our > + * channel list as the upper layers sort results > + */ > + last_freq = 0; > + for (i = 0; i < scan_res->num; i++) { > + if (scan_res->res[i]->freq == last_freq) > + continue; > + last_freq = scan_res->res[i]->freq; > + idx = find_freq_index(data, last_freq) + 1; > + if (idx != -1) > + data->freq_idx = (idx + 1) % data->n_supp_freqs; > + } > + } else > + data->freq_idx = 0; > + wpa_printf(MSG_DEBUG, "bgscan simple: freq_idx %d", data->freq_idx); > + > + if (data->signal_threshold) > + bgscan_poll_signal_monitor(&data->signal_monitor, NULL); > + if (data->scan_interval == data->short_interval) > + bgscan_reason = "short-interval-scan"; > + else > + bgscan_reason = "long-interval-scan"; > + eloop_cancel_timeout(bgscan_simple_timeout, data, ELOOP_ALL_CTX); > eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, > - data, NULL); > + data, bgscan_reason); > > /* > * A more advanced bgscan could process scan results internally, select > @@ -213,6 +381,10 @@ static void bgscan_simple_notify_signal_change(void *priv, int above, > "(above=%d current_signal=%d current_noise=%d " > "current_txrate=%d))", above, current_signal, > current_noise, current_txrate); > + > + bgscan_update_signal_monitor(&data->signal_monitor, current_signal, > + current_noise); > + > if (data->scan_interval == data->long_interval && !above) { > wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " > "bgscan interval"); > @@ -234,18 +406,20 @@ static void bgscan_simple_notify_signal_change(void *priv, int above, > * scan is too far in the future. > */ > eloop_cancel_timeout(bgscan_simple_timeout, data, > - NULL); > + ELOOP_ALL_CTX); > eloop_register_timeout(data->scan_interval, 0, > bgscan_simple_timeout, data, > - NULL); > + "signal-below-short-interval-scan"); > } > } else if (data->scan_interval == data->short_interval && above) { > wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan " > "interval"); > data->scan_interval = data->long_interval; > - eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); > + eloop_cancel_timeout(bgscan_simple_timeout, data, > + ELOOP_ALL_CTX); > eloop_register_timeout(data->scan_interval, 0, > - bgscan_simple_timeout, data, NULL); > + bgscan_simple_timeout, data, > + "signal-above-long-interval-scan"); > } else if (!above) { > /* > * Signal dropped further 4 dB. Request a new scan if we have > @@ -258,9 +432,10 @@ static void bgscan_simple_notify_signal_change(void *priv, int above, > > if (scan) { > wpa_printf(MSG_DEBUG, "bgscan simple: Trigger immediate scan"); > - eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); > + eloop_cancel_timeout(bgscan_simple_timeout, data, > + ELOOP_ALL_CTX); > eloop_register_timeout(0, 0, bgscan_simple_timeout, data, > - NULL); > + "signal-below-immediate-scan"); > } > } > > diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c > index ee39e0c92..517a87cc6 100644 > --- a/wpa_supplicant/scan.c > +++ b/wpa_supplicant/scan.c > @@ -2399,6 +2399,7 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, > wpa_s->ignore_post_flush_scan_res = 0; > return scan_res; > } > + scan_res->aborted = (info && info->aborted); > > wpa_bss_update_start(wpa_s); > for (i = 0; i < scan_res->num; i++) > -- > 2.20.1 > _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap