From c4d71c25057465e18941b57765b45f5626b9e314 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 10 Jul 2010 15:55:48 -0700 Subject: [PATCH] bgscan: Add starting point for more advanced bgscan module: learn This is based on the bgscan "simple" module and this initial commit does not add any new functionality. --- wpa_supplicant/Makefile | 6 + wpa_supplicant/bgscan.c | 6 + wpa_supplicant/bgscan_learn.c | 220 ++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 wpa_supplicant/bgscan_learn.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 1d2562372..514f5c651 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1184,6 +1184,12 @@ OBJS += bgscan_simple.o NEED_BGSCAN=y endif +ifdef CONFIG_BGSCAN_LEARN +CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.o +NEED_BGSCAN=y +endif + ifdef NEED_BGSCAN CFLAGS += -DCONFIG_BGSCAN OBJS += bgscan.o diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c index a0d98dbfb..e5fdfc4f7 100644 --- a/wpa_supplicant/bgscan.c +++ b/wpa_supplicant/bgscan.c @@ -22,11 +22,17 @@ #ifdef CONFIG_BGSCAN_SIMPLE extern const struct bgscan_ops bgscan_simple_ops; #endif /* CONFIG_BGSCAN_SIMPLE */ +#ifdef CONFIG_BGSCAN_LEARN +extern const struct bgscan_ops bgscan_learn_ops; +#endif /* CONFIG_BGSCAN_LEARN */ static const struct bgscan_ops * bgscan_modules[] = { #ifdef CONFIG_BGSCAN_SIMPLE &bgscan_simple_ops, #endif /* CONFIG_BGSCAN_SIMPLE */ +#ifdef CONFIG_BGSCAN_LEARN + &bgscan_learn_ops, +#endif /* CONFIG_BGSCAN_LEARN */ NULL }; diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c new file mode 100644 index 000000000..12448565c --- /dev/null +++ b/wpa_supplicant/bgscan_learn.c @@ -0,0 +1,220 @@ +/* + * WPA Supplicant - background scan and roaming module: learn + * Copyright (c) 2009-2010, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "drivers/driver.h" +#include "config_ssid.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "scan.h" +#include "bgscan.h" + +struct bgscan_learn_data { + struct wpa_supplicant *wpa_s; + const struct wpa_ssid *ssid; + int scan_interval; + int signal_threshold; + int short_interval; /* use if signal < threshold */ + int long_interval; /* use if signal > threshold */ + struct os_time last_bgscan; +}; + + +static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct bgscan_learn_data *data = eloop_ctx; + struct wpa_supplicant *wpa_s = data->wpa_s; + struct wpa_driver_scan_params params; + + os_memset(¶ms, 0, sizeof(params)); + 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. + */ + + wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan"); + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { + wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan"); + eloop_register_timeout(data->scan_interval, 0, + bgscan_learn_timeout, data, NULL); + } else + os_get_time(&data->last_bgscan); +} + + +static int bgscan_learn_get_params(struct bgscan_learn_data *data, + const char *params) +{ + const char *pos; + + if (params == NULL) + return 0; + + data->short_interval = atoi(params); + + pos = os_strchr(params, ':'); + if (pos == NULL) + return 0; + pos++; + data->signal_threshold = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval " + "for high signal"); + return -1; + } + pos++; + data->long_interval = atoi(pos); + + return 0; +} + + +static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, + const char *params, + const struct wpa_ssid *ssid) +{ + struct bgscan_learn_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->wpa_s = wpa_s; + data->ssid = ssid; + if (bgscan_learn_get_params(data, params) < 0) { + os_free(data); + return NULL; + } + if (data->short_interval <= 0) + data->short_interval = 30; + if (data->long_interval <= 0) + data->long_interval = 30; + + wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d " + "Short bgscan interval %d Long bgscan interval %d", + 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 learn: Failed to enable " + "signal strength monitoring"); + } + + data->scan_interval = data->short_interval; + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, + data, NULL); + return data; +} + + +static void bgscan_learn_deinit(void *priv) +{ + struct bgscan_learn_data *data = priv; + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + if (data->signal_threshold) + wpa_drv_signal_monitor(data->wpa_s, 0, 0); + os_free(data); +} + + +static int bgscan_learn_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct bgscan_learn_data *data = priv; + + wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification"); + + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, + data, NULL); + + /* + * A more advanced bgscan could process scan results internally, select + * the BSS and request roam if needed. This sample uses the existing + * BSS/ESS selection routine. Change this to return 1 if selection is + * done inside the bgscan module. + */ + + return 0; +} + + +static void bgscan_learn_notify_beacon_loss(void *priv) +{ + wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss"); + /* TODO: speed up background scanning */ +} + + +static void bgscan_learn_notify_signal_change(void *priv, int above) +{ + struct bgscan_learn_data *data = priv; + + if (data->short_interval == data->long_interval || + data->signal_threshold == 0) + return; + + wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed " + "(above=%d)", above); + if (data->scan_interval == data->long_interval && !above) { + wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan " + "and start using short bgscan interval"); + data->scan_interval = data->short_interval; + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(0, 0, bgscan_learn_timeout, data, + NULL); + } else if (data->scan_interval == data->short_interval && above) { + wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan " + "interval"); + data->scan_interval = data->long_interval; + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, + bgscan_learn_timeout, data, NULL); + } else if (!above) { + struct os_time now; + /* + * Signal dropped further 4 dB. Request a new scan if we have + * not yet scanned in a while. + */ + os_get_time(&now); + if (now.sec > data->last_bgscan.sec + 10) { + wpa_printf(MSG_DEBUG, "bgscan learn: Trigger " + "immediate scan"); + eloop_cancel_timeout(bgscan_learn_timeout, data, + NULL); + eloop_register_timeout(0, 0, bgscan_learn_timeout, + data, NULL); + } + } +} + + +const struct bgscan_ops bgscan_learn_ops = { + .name = "learn", + .init = bgscan_learn_init, + .deinit = bgscan_learn_deinit, + .notify_scan = bgscan_learn_notify_scan, + .notify_beacon_loss = bgscan_learn_notify_beacon_loss, + .notify_signal_change = bgscan_learn_notify_signal_change, +};