1 /* 2 * WPA Supplicant - background scan and roaming module: simple 3 * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15 #include "includes.h" 16 17 #include "common.h" 18 #include "eloop.h" 19 #include "drivers/driver.h" 20 #include "config_ssid.h" 21 #include "wpa_supplicant_i.h" 22 #include "driver_i.h" 23 #include "scan.h" 24 #include "bgscan.h" 25 26 struct bgscan_simple_data { 27 struct wpa_supplicant *wpa_s; 28 const struct wpa_ssid *ssid; 29 int scan_interval; 30 int signal_threshold; 31 int short_interval; /* use if signal < threshold */ 32 int long_interval; /* use if signal > threshold */ 33 struct os_time last_bgscan; 34 }; 35 36 37 static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) 38 { 39 struct bgscan_simple_data *data = eloop_ctx; 40 struct wpa_supplicant *wpa_s = data->wpa_s; 41 struct wpa_driver_scan_params params; 42 43 os_memset(¶ms, 0, sizeof(params)); 44 params.num_ssids = 1; 45 params.ssids[0].ssid = data->ssid->ssid; 46 params.ssids[0].ssid_len = data->ssid->ssid_len; 47 params.freqs = data->ssid->scan_freq; 48 49 /* 50 * A more advanced bgscan module would learn about most like channels 51 * over time and request scans only for some channels (probing others 52 * every now and then) to reduce effect on the data connection. 53 */ 54 55 wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan"); 56 if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { 57 wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan"); 58 eloop_register_timeout(data->scan_interval, 0, 59 bgscan_simple_timeout, data, NULL); 60 } else 61 os_get_time(&data->last_bgscan); 62 } 63 64 65 static int bgscan_simple_get_params(struct bgscan_simple_data *data, 66 const char *params) 67 { 68 const char *pos; 69 70 if (params == NULL) 71 return 0; 72 73 data->short_interval = atoi(params); 74 75 pos = os_strchr(params, ':'); 76 if (pos == NULL) 77 return 0; 78 pos++; 79 data->signal_threshold = atoi(pos); 80 pos = os_strchr(pos, ':'); 81 if (pos == NULL) { 82 wpa_printf(MSG_ERROR, "bgscan simple: Missing scan interval " 83 "for high signal"); 84 return -1; 85 } 86 pos++; 87 data->long_interval = atoi(pos); 88 89 return 0; 90 } 91 92 93 static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, 94 const char *params, 95 const struct wpa_ssid *ssid) 96 { 97 struct bgscan_simple_data *data; 98 99 data = os_zalloc(sizeof(*data)); 100 if (data == NULL) 101 return NULL; 102 data->wpa_s = wpa_s; 103 data->ssid = ssid; 104 if (bgscan_simple_get_params(data, params) < 0) { 105 os_free(data); 106 return NULL; 107 } 108 if (data->short_interval <= 0) 109 data->short_interval = 30; 110 if (data->long_interval <= 0) 111 data->long_interval = 30; 112 113 wpa_printf(MSG_DEBUG, "bgscan simple: Signal strength threshold %d " 114 "Short bgscan interval %d Long bgscan interval %d", 115 data->signal_threshold, data->short_interval, 116 data->long_interval); 117 118 if (data->signal_threshold && 119 wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) { 120 wpa_printf(MSG_ERROR, "bgscan simple: Failed to enable " 121 "signal strength monitoring"); 122 } 123 124 data->scan_interval = data->short_interval; 125 eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, 126 data, NULL); 127 128 /* 129 * This function is called immediately after an association, so it is 130 * reasonable to assume that a scan was completed recently. This makes 131 * us skip an immediate new scan in cases where the current signal 132 * level is below the bgscan threshold. 133 */ 134 os_get_time(&data->last_bgscan); 135 136 return data; 137 } 138 139 140 static void bgscan_simple_deinit(void *priv) 141 { 142 struct bgscan_simple_data *data = priv; 143 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 144 if (data->signal_threshold) 145 wpa_drv_signal_monitor(data->wpa_s, 0, 0); 146 os_free(data); 147 } 148 149 150 static int bgscan_simple_notify_scan(void *priv) 151 { 152 struct bgscan_simple_data *data = priv; 153 154 wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification"); 155 156 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 157 eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, 158 data, NULL); 159 160 /* 161 * A more advanced bgscan could process scan results internally, select 162 * the BSS and request roam if needed. This sample uses the existing 163 * BSS/ESS selection routine. Change this to return 1 if selection is 164 * done inside the bgscan module. 165 */ 166 167 return 0; 168 } 169 170 171 static void bgscan_simple_notify_beacon_loss(void *priv) 172 { 173 wpa_printf(MSG_DEBUG, "bgscan simple: beacon loss"); 174 /* TODO: speed up background scanning */ 175 } 176 177 178 static void bgscan_simple_notify_signal_change(void *priv, int above) 179 { 180 struct bgscan_simple_data *data = priv; 181 int scan = 0; 182 struct os_time now; 183 184 if (data->short_interval == data->long_interval || 185 data->signal_threshold == 0) 186 return; 187 188 wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed " 189 "(above=%d)", above); 190 if (data->scan_interval == data->long_interval && !above) { 191 wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " 192 "bgscan interval"); 193 data->scan_interval = data->short_interval; 194 os_get_time(&now); 195 if (now.sec > data->last_bgscan.sec + 1) 196 scan = 1; 197 } else if (data->scan_interval == data->short_interval && above) { 198 wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan " 199 "interval"); 200 data->scan_interval = data->long_interval; 201 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 202 eloop_register_timeout(data->scan_interval, 0, 203 bgscan_simple_timeout, data, NULL); 204 } else if (!above) { 205 /* 206 * Signal dropped further 4 dB. Request a new scan if we have 207 * not yet scanned in a while. 208 */ 209 os_get_time(&now); 210 if (now.sec > data->last_bgscan.sec + 10) 211 scan = 1; 212 } 213 214 if (scan) { 215 wpa_printf(MSG_DEBUG, "bgscan simple: Trigger immediate scan"); 216 eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); 217 eloop_register_timeout(0, 0, bgscan_simple_timeout, data, 218 NULL); 219 } 220 } 221 222 223 const struct bgscan_ops bgscan_simple_ops = { 224 .name = "simple", 225 .init = bgscan_simple_init, 226 .deinit = bgscan_simple_deinit, 227 .notify_scan = bgscan_simple_notify_scan, 228 .notify_beacon_loss = bgscan_simple_notify_beacon_loss, 229 .notify_signal_change = bgscan_simple_notify_signal_change, 230 }; 231