]>
Commit | Line | Data |
---|---|---|
60b94c98 JM |
1 | /* |
2 | * WPA Supplicant - background scan and roaming module: simple | |
e2f74005 | 3 | * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> |
60b94c98 JM |
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" | |
e2f74005 | 22 | #include "driver_i.h" |
9ba9fa07 | 23 | #include "scan.h" |
60b94c98 JM |
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; | |
e2f74005 JM |
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; | |
60b94c98 JM |
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); | |
e2f74005 JM |
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; | |
60b94c98 | 85 | } |
e2f74005 JM |
86 | pos++; |
87 | data->long_interval = atoi(pos); | |
88 | ||
89 | return 0; | |
60b94c98 JM |
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; | |
e2f74005 JM |
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; | |
60b94c98 JM |
125 | eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, |
126 | data, NULL); | |
1e6ef645 JM |
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 | ||
60b94c98 JM |
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); | |
e2f74005 JM |
144 | if (data->signal_threshold) |
145 | wpa_drv_signal_monitor(data->wpa_s, 0, 0); | |
60b94c98 JM |
146 | os_free(data); |
147 | } | |
148 | ||
149 | ||
c2594c36 JM |
150 | static int bgscan_simple_notify_scan(void *priv, |
151 | struct wpa_scan_results *scan_res) | |
60b94c98 JM |
152 | { |
153 | struct bgscan_simple_data *data = priv; | |
154 | ||
155 | wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification"); | |
156 | ||
157 | eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); | |
158 | eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, | |
159 | data, NULL); | |
160 | ||
161 | /* | |
162 | * A more advanced bgscan could process scan results internally, select | |
163 | * the BSS and request roam if needed. This sample uses the existing | |
164 | * BSS/ESS selection routine. Change this to return 1 if selection is | |
165 | * done inside the bgscan module. | |
166 | */ | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | ||
172 | static void bgscan_simple_notify_beacon_loss(void *priv) | |
173 | { | |
174 | wpa_printf(MSG_DEBUG, "bgscan simple: beacon loss"); | |
175 | /* TODO: speed up background scanning */ | |
176 | } | |
177 | ||
178 | ||
60a972a6 | 179 | static void bgscan_simple_notify_signal_change(void *priv, int above, |
174fa789 PS |
180 | int current_signal, |
181 | int current_noise, | |
182 | int current_txrate) | |
60b94c98 | 183 | { |
e2f74005 | 184 | struct bgscan_simple_data *data = priv; |
1e6ef645 JM |
185 | int scan = 0; |
186 | struct os_time now; | |
e2f74005 JM |
187 | |
188 | if (data->short_interval == data->long_interval || | |
189 | data->signal_threshold == 0) | |
190 | return; | |
191 | ||
192 | wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed " | |
174fa789 PS |
193 | "(above=%d current_signal=%d current_noise=%d " |
194 | "current_txrate=%d))", above, current_signal, | |
195 | current_noise, current_txrate); | |
e2f74005 | 196 | if (data->scan_interval == data->long_interval && !above) { |
1e6ef645 JM |
197 | wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " |
198 | "bgscan interval"); | |
e2f74005 | 199 | data->scan_interval = data->short_interval; |
1e6ef645 JM |
200 | os_get_time(&now); |
201 | if (now.sec > data->last_bgscan.sec + 1) | |
202 | scan = 1; | |
e2f74005 JM |
203 | } else if (data->scan_interval == data->short_interval && above) { |
204 | wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan " | |
205 | "interval"); | |
206 | data->scan_interval = data->long_interval; | |
207 | eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); | |
208 | eloop_register_timeout(data->scan_interval, 0, | |
209 | bgscan_simple_timeout, data, NULL); | |
210 | } else if (!above) { | |
e2f74005 JM |
211 | /* |
212 | * Signal dropped further 4 dB. Request a new scan if we have | |
213 | * not yet scanned in a while. | |
214 | */ | |
215 | os_get_time(&now); | |
1e6ef645 JM |
216 | if (now.sec > data->last_bgscan.sec + 10) |
217 | scan = 1; | |
218 | } | |
219 | ||
220 | if (scan) { | |
221 | wpa_printf(MSG_DEBUG, "bgscan simple: Trigger immediate scan"); | |
222 | eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); | |
223 | eloop_register_timeout(0, 0, bgscan_simple_timeout, data, | |
224 | NULL); | |
e2f74005 | 225 | } |
60b94c98 JM |
226 | } |
227 | ||
228 | ||
229 | const struct bgscan_ops bgscan_simple_ops = { | |
230 | .name = "simple", | |
231 | .init = bgscan_simple_init, | |
232 | .deinit = bgscan_simple_deinit, | |
233 | .notify_scan = bgscan_simple_notify_scan, | |
234 | .notify_beacon_loss = bgscan_simple_notify_beacon_loss, | |
235 | .notify_signal_change = bgscan_simple_notify_signal_change, | |
236 | }; |