1/* 2 * WPA Supplicant - background scan and roaming module: simple 3 * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9#include "includes.h" 10 11#include "common.h" 12#include "eloop.h" 13#include "drivers/driver.h" 14#include "config_ssid.h" 15#include "wpa_supplicant_i.h" 16#include "driver_i.h" 17#include "scan.h" 18#include "bgscan.h" 19 20struct bgscan_simple_data { 21 struct wpa_supplicant *wpa_s; 22 const struct wpa_ssid *ssid; 23 int scan_interval; 24 int signal_threshold; 25 int short_scan_count; /* counter for scans using short scan interval */ 26 int max_short_scans; /* maximum times we short-scan before back-off */ 27 int short_interval; /* use if signal < threshold */ 28 int long_interval; /* use if signal > threshold */ 29 struct os_reltime last_bgscan; 30}; 31 32 33static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) 34{ 35 struct bgscan_simple_data *data = eloop_ctx; 36 struct wpa_supplicant *wpa_s = data->wpa_s; 37 struct wpa_driver_scan_params params; 38 39 os_memset(¶ms, 0, sizeof(params)); 40 params.num_ssids = 1; 41 params.ssids[0].ssid = data->ssid->ssid; 42 params.ssids[0].ssid_len = data->ssid->ssid_len; 43 params.freqs = data->ssid->scan_freq; 44 45 /* 46 * A more advanced bgscan module would learn about most like channels 47 * over time and request scans only for some channels (probing others 48 * every now and then) to reduce effect on the data connection. 49 */ 50 51 wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan"); 52 if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { 53 wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan"); 54 eloop_register_timeout(data->scan_interval, 0, 55 bgscan_simple_timeout, data, NULL); 56 } else { 57 if (data->scan_interval == data->short_interval) { 58 data->short_scan_count++; 59 /* 60 * Spend at most the duration of a long scan interval 61 * scanning at the short scan interval. After that, 62 * revert to the long scan interval. 63 */ 64 if (data->short_scan_count > data->max_short_scans) { 65 data->scan_interval = data->long_interval; 66 wpa_printf(MSG_DEBUG, "bgscan simple: Backing " 67 "off to long scan interval"); 68 } 69 } else if (data->short_scan_count > 0) { 70 /* 71 * If we lasted a long scan interval without any 72 * CQM triggers, decrease the short-scan count, 73 * which allows 1 more short-scan interval to 74 * occur in the future when CQM triggers. 75 */ 76 data->short_scan_count--; 77 } 78 os_get_reltime(&data->last_bgscan); 79 } 80} 81 82 83static int bgscan_simple_get_params(struct bgscan_simple_data *data, 84 const char *params) 85{ 86 const char *pos; 87 88 if (params == NULL) 89 return 0; 90 91 data->short_interval = atoi(params); 92 93 pos = os_strchr(params, ':'); 94 if (pos == NULL) 95 return 0; 96 pos++; 97 data->signal_threshold = atoi(pos); 98 pos = os_strchr(pos, ':'); 99 if (pos == NULL) { 100 wpa_printf(MSG_ERROR, "bgscan simple: Missing scan interval " 101 "for high signal"); 102 return -1; 103 } 104 pos++; 105 data->long_interval = atoi(pos); 106 107 return 0; 108} 109 110 111static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, 112 const char *params, 113 const struct wpa_ssid *ssid) 114{ 115 struct bgscan_simple_data *data; 116 117 data = os_zalloc(sizeof(*data)); 118 if (data == NULL) 119 return NULL; 120 data->wpa_s = wpa_s; 121 data->ssid = ssid; 122 if (bgscan_simple_get_params(data, params) < 0) { 123 os_free(data); 124 return NULL; 125 } 126 if (data->short_interval <= 0) 127 data->short_interval = 30; 128 if (data->long_interval <= 0) 129 data->long_interval = 30; 130 131 wpa_printf(MSG_DEBUG, "bgscan simple: Signal strength threshold %d " 132 "Short bgscan interval %d Long bgscan interval %d", 133 data->signal_threshold, data->short_interval, 134 data->long_interval); 135 136 if (data->signal_threshold && 137 wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) { 138 wpa_printf(MSG_ERROR, "bgscan simple: Failed to enable " 139 "signal strength monitoring"); 140 } 141 142 data->scan_interval = data->short_interval; 143 data->max_short_scans = data->long_interval / data->short_interval + 1; 144 if (data->signal_threshold) { 145 /* Poll for signal info to set initial scan interval */ 146 struct wpa_signal_info siginfo; 147 if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 && 148 siginfo.current_signal >= data->signal_threshold) 149 data->scan_interval = data->long_interval; 150 } 151 wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d", 152 data->scan_interval); 153 eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, 154 data, NULL); 155 156 /* 157 * This function is called immediately after an association, so it is 158 * reasonable to assume that a scan was completed recently. This makes 159 * us skip an immediate new scan in cases where the current signal 160 * level is below the bgscan threshold. 161 */ 162 os_get_reltime(&data->last_bgscan); 163 164 return data; 165} 166 167 168static void bgscan_simple_deinit(void *priv) 169{ 170 struct bgscan_simple_data *data = priv; 171 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 172 if (data->signal_threshold) 173 wpa_drv_signal_monitor(data->wpa_s, 0, 0); 174 os_free(data); 175} 176 177 178static int bgscan_simple_notify_scan(void *priv, 179 struct wpa_scan_results *scan_res) 180{ 181 struct bgscan_simple_data *data = priv; 182 183 wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification"); 184 185 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 186 eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, 187 data, NULL); 188 189 /* 190 * A more advanced bgscan could process scan results internally, select 191 * the BSS and request roam if needed. This sample uses the existing 192 * BSS/ESS selection routine. Change this to return 1 if selection is 193 * done inside the bgscan module. 194 */ 195 196 return 0; 197} 198 199 200static void bgscan_simple_notify_beacon_loss(void *priv) 201{ 202 wpa_printf(MSG_DEBUG, "bgscan simple: beacon loss"); 203 /* TODO: speed up background scanning */ 204} 205 206 207static void bgscan_simple_notify_signal_change(void *priv, int above, 208 int current_signal, 209 int current_noise, 210 int current_txrate) 211{ 212 struct bgscan_simple_data *data = priv; 213 int scan = 0; 214 struct os_reltime now; 215 216 if (data->short_interval == data->long_interval || 217 data->signal_threshold == 0) 218 return; 219 220 wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed " 221 "(above=%d current_signal=%d current_noise=%d " 222 "current_txrate=%d))", above, current_signal, 223 current_noise, current_txrate); 224 if (data->scan_interval == data->long_interval && !above) { 225 wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " 226 "bgscan interval"); 227 data->scan_interval = data->short_interval; 228 os_get_reltime(&now); 229 if (now.sec > data->last_bgscan.sec + 1 && 230 data->short_scan_count <= data->max_short_scans) 231 /* 232 * If we haven't just previously (<1 second ago) 233 * performed a scan, and we haven't depleted our 234 * budget for short-scans, perform a scan 235 * immediately. 236 */ 237 scan = 1; 238 else if (data->last_bgscan.sec + data->long_interval > 239 now.sec + data->scan_interval) { 240 /* 241 * Restart scan interval timer if currently scheduled 242 * scan is too far in the future. 243 */ 244 eloop_cancel_timeout(bgscan_simple_timeout, data, 245 NULL); 246 eloop_register_timeout(data->scan_interval, 0, 247 bgscan_simple_timeout, data, 248 NULL); 249 } 250 } else if (data->scan_interval == data->short_interval && above) { 251 wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan " 252 "interval"); 253 data->scan_interval = data->long_interval; 254 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 255 eloop_register_timeout(data->scan_interval, 0, 256 bgscan_simple_timeout, data, NULL); 257 } else if (!above) { 258 /* 259 * Signal dropped further 4 dB. Request a new scan if we have 260 * not yet scanned in a while. 261 */ 262 os_get_reltime(&now); 263 if (now.sec > data->last_bgscan.sec + 10) 264 scan = 1; 265 } 266 267 if (scan) { 268 wpa_printf(MSG_DEBUG, "bgscan simple: Trigger immediate scan"); 269 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 270 eloop_register_timeout(0, 0, bgscan_simple_timeout, data, 271 NULL); 272 } 273} 274 275 276const struct bgscan_ops bgscan_simple_ops = { 277 .name = "simple", 278 .init = bgscan_simple_init, 279 .deinit = bgscan_simple_deinit, 280 .notify_scan = bgscan_simple_notify_scan, 281 .notify_beacon_loss = bgscan_simple_notify_beacon_loss, 282 .notify_signal_change = bgscan_simple_notify_signal_change, 283}; 284