4 Copyright (C) 2009 Javier S. Pedro
6 @author Javier S. Pedro <javispedro@javispedro.com>
8 This file is part of libicd-network-wpa.
10 This program is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation; either version 2 of the License, or (at your
13 option) any later version.
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU General Public License along
21 with this program; if not, write to the Free Software Foundation, Inc.,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include <network_api.h>
29 #include <osso-ic-dbus.h>
33 #include "dbus-handler.h"
39 /** time in seconds a found network is kept cached by icd2 */
40 #define ICD_SEARCH_LIFETIME (5 * 60)
41 /** time in seconds after which a new network scan is to be triggered */
42 #define ICD_SEARCH_INTERVAL (2 * 60)
44 /** time (in milliseconds) to wait after a wpa_supplicant disconnected event
45 * before calling it a day and shutting down the interface */
46 #define ICD_DISCONNECTED_TIMEOUT (60 * 1000)
48 /** icd flags to use */
49 #define ICD_WPA_FLAGS (ICD_NW_ATTR_AUTOCONNECT | ICD_NW_ATTR_IAPNAME)
52 /** equivalent to signal level 0 */
53 #define RSSI_MIN (-85)
54 /** equivalent to signal level max */
55 #define RSSI_MAX (-30)
56 /* guaranteed to be random, chosen by fair dice roll :) */
59 static icd_nw_watch_pid_fn icd_watch_fn;
60 static gpointer icd_watch_token;
61 static icd_nw_close_fn icd_close_fn;
62 static icd_nw_status_change_fn icd_status_change_fn;
63 static icd_nw_renew_fn icd_renew_fn;
65 static gchar * cur_network_id = NULL;
67 /** @see ICD_DISCONNECTED_TIMEOUT */
68 static guint connect_timeout = 0;
70 static inline gint rssi_to_signal(gint rssi)
72 gint signal = rssi - RSSI_MIN + ICD_NW_LEVEL_NONE;
73 signal *= ICD_NW_LEVEL_10;
74 signal /= (RSSI_MAX - RSSI_MIN);
76 if (signal < ICD_NW_LEVEL_NONE) return ICD_NW_LEVEL_NONE;
77 else if (signal > ICD_NW_LEVEL_10) return ICD_NW_LEVEL_10;
81 static gboolean disconnected_timeout_cb(gpointer data)
83 // If connect_timeout is 0, we have been disabled.
84 if (!connect_timeout) return FALSE;
86 // Disconnected for too long
87 DLOG_DEBUG("Disconnected for way too long");
88 icd_close(ICD_NW_ERROR, ICD_DBUS_ERROR_NETWORK_ERROR);
93 /** Supplicant callback while fully connected */
94 static void icd_supp_cb(enum supp_status status, const char * error_str,
97 DLOG_DEBUG("%s: %d", __func__, status);
100 case SUPP_STATUS_ERROR:
101 DLOG_WARN_L("Unexpected supplicant error");
104 case SUPP_STATUS_KILLED:
106 icd_close(ICD_NW_ERROR, ICD_DBUS_ERROR_SYSTEM_ERROR);
109 case SUPP_STATUS_DISCONNECTED:
110 if (!connect_timeout) {
111 DLOG_DEBUG("Disconnected, starting timeout");
112 connect_timeout = g_timeout_add(
113 ICD_DISCONNECTED_TIMEOUT,
114 disconnected_timeout_cb,
119 case SUPP_STATUS_CONNECTED:
120 if (connect_timeout) {
121 DLOG_DEBUG("Connected, removing timeout");
122 g_source_remove(connect_timeout);
128 // TODO Disable PSM while roaming (dis/asociating)?
131 struct pre_down_data {
132 icd_nw_link_pre_down_cb_fn link_pre_down_cb;
133 gpointer link_pre_down_cb_token;
136 /** Supplicant callback in link_pre_down mode */
137 static void icd_down_supp_cb(enum supp_status status, const char * error_str,
140 struct pre_down_data *data = (struct pre_down_data *) user_data;
141 DLOG_DEBUG("%s: %d", __func__, status);
143 if (status == SUPP_STATUS_KILLED) {
144 // Everything was fine
145 supp_set_callback(NULL, NULL);
146 data->link_pre_down_cb(ICD_NW_SUCCESS,
147 data->link_pre_down_cb_token);
152 icd_pre_down (const gchar *network_type,
153 const guint network_attrs,
154 const gchar *network_id,
155 const gchar *interface_name,
156 icd_nw_link_pre_down_cb_fn link_pre_down_cb,
157 const gpointer link_pre_down_cb_token,
160 DLOG_DEBUG(__func__);
163 struct pre_down_data *data = g_new(struct pre_down_data, 1);
164 data->link_pre_down_cb = link_pre_down_cb;
165 data->link_pre_down_cb_token = link_pre_down_cb_token;
167 if (supp_is_active()) {
168 supp_set_callback(icd_down_supp_cb, data);
170 // Kill the supplicant,
172 // but wait for the "child exit" event.
174 // Supplicant is already dead, no need to wait.
175 supp_set_callback(NULL, NULL);
176 supp_disable(); // Clears status info
178 link_pre_down_cb(ICD_NW_SUCCESS, link_pre_down_cb_token);
182 struct post_up_data {
183 icd_nw_link_post_up_cb_fn link_post_up_cb;
184 gpointer link_post_up_cb_token;
187 /** Supplicant callback in link_post_up status */
188 static void icd_up_supp_cb(enum supp_status status, const char * error_str,
191 struct post_up_data *data = (struct post_up_data *) user_data;
192 DLOG_DEBUG("%s: %d", __func__, status);
195 case SUPP_STATUS_CONNECTED:
196 data->link_post_up_cb(ICD_NW_SUCCESS_NEXT_LAYER,
198 data->link_post_up_cb_token, NULL);
200 supp_set_callback(icd_supp_cb, NULL);
203 case SUPP_STATUS_DISCONNECTED:
204 error_str = ICD_DBUS_ERROR_WLAN_AUTH_FAILED;
207 case SUPP_STATUS_ERROR:
210 // kill the supplicant before everything crashes.
211 supp_set_callback(NULL, NULL);
215 case SUPP_STATUS_KILLED:
216 data->link_post_up_cb(ICD_NW_ERROR,
218 data->link_post_up_cb_token, NULL);
226 static void icd_post_up(const gchar *network_type,
227 const guint network_attrs,
228 const gchar *network_id,
229 const gchar *interface_name,
230 icd_nw_link_post_up_cb_fn link_post_up_cb,
231 const gpointer link_post_up_cb_token,
234 DLOG_DEBUG(__func__);
236 struct post_up_data *data = g_new(struct post_up_data, 1);
237 data->link_post_up_cb = link_post_up_cb;
238 data->link_post_up_cb_token = link_post_up_cb_token;
240 supp_set_callback(icd_up_supp_cb, data);
242 if (supp_enable() != 0)
244 icd_up_supp_cb(-1, ICD_DBUS_ERROR_SYSTEM_ERROR, data);
247 supp_set_interface(interface_name);
248 supp_set_network_id(network_id);
251 static void icd_link_down(const gchar *network_type,
252 const guint network_attrs,
253 const gchar *network_id,
254 const gchar *interface_name,
255 icd_nw_link_down_cb_fn link_down_cb,
256 const gpointer link_down_cb_token,
259 DLOG_DEBUG(__func__);
261 networks_disconnect(network_id);
263 g_free(cur_network_id);
264 cur_network_id = NULL;
266 // TODO: Maybe wait for wlancond->disconnect callback?
268 link_down_cb(ICD_NW_SUCCESS, link_down_cb_token);
271 struct link_up_data {
272 icd_nw_link_up_cb_fn link_up_cb;
273 gpointer link_up_cb_token;
276 static void icd_link_up_done(int status, const char *error, gpointer user_data)
278 DLOG_DEBUG("%s: %d", __func__, status);
280 struct link_up_data *data = (struct link_up_data *) user_data;
283 g_free(cur_network_id);
284 cur_network_id = NULL;
285 data->link_up_cb(ICD_NW_ERROR,
287 WPA_IFACE, data->link_up_cb_token,
291 data->link_up_cb(ICD_NW_SUCCESS_NEXT_LAYER,
293 WPA_IFACE, data->link_up_cb_token,
300 static void icd_link_up(const gchar *network_type,
301 const guint network_attrs,
302 const gchar *network_id,
303 icd_nw_link_up_cb_fn link_up_cb,
304 const gpointer link_up_cb_token,
307 DLOG_DEBUG("%s: %s", __func__, network_id);
309 struct link_up_data *data = g_new(struct link_up_data, 1);
310 data->link_up_cb = link_up_cb;
311 data->link_up_cb_token = link_up_cb_token;
313 if (cur_network_id) {
314 DLOG_WARN_L("Double link up");
316 data->link_up_cb(ICD_NW_TOO_MANY_CONNECTIONS,
318 WPA_IFACE, data->link_up_cb_token,
323 cur_network_id = g_strdup(network_id);
325 networks_connect(network_id, icd_link_up_done, data);
329 icd_nw_link_stats_cb_fn cb;
333 static void icd_link_stats_done
334 (int status, const char * strdata, int rssi, gpointer user_data)
336 DLOG_DEBUG("%s: %d", __func__, status);
338 struct stats_data * data = (struct stats_data *) user_data;
340 gint signal = rssi_to_signal(rssi);
342 DLOG_DEBUG("status: rssi=%d, signal=%d", rssi, signal);
344 data->cb(data->token,
355 // IPv[46] module will fill time_active, rx & tx values.
361 static void icd_link_stats(const gchar *network_type,
362 const guint network_attrs,
363 const gchar *network_id,
365 icd_nw_link_stats_cb_fn cb,
366 const gpointer link_stats_cb_token)
368 DLOG_DEBUG("%s", __func__);
370 struct stats_data *data = g_new(struct stats_data, 1);
372 data->token = link_stats_cb_token;
374 networks_status(icd_link_stats_done, data);
378 icd_nw_search_cb_fn search_cb;
379 gpointer search_cb_token;
382 static void icd_send_search_result(int status, const char * id,
383 const char * ssid, const char * ap, int rssi, gpointer user_data)
385 DLOG_DEBUG("%s: %d", __func__, status);
387 struct search_data * data = (struct search_data *) user_data;
391 case SEARCH_CONTINUE:
392 signal = rssi_to_signal(rssi);
394 data->search_cb(ICD_NW_SEARCH_CONTINUE,
402 data->search_cb_token);
404 case SEARCH_FINISHED:
405 data->search_cb(ICD_NW_SEARCH_COMPLETE,
413 data->search_cb_token);
421 static void icd_start_search(const gchar *network_type,
423 icd_nw_search_cb_fn search_cb,
424 const gpointer search_cb_token,
427 DLOG_DEBUG(__func__);
429 struct search_data *data = g_new(struct search_data, 1);
430 data->search_cb = search_cb;
431 data->search_cb_token = search_cb_token;
433 networks_search_start(icd_send_search_result, data);
436 static void icd_stop_search(gpointer *private)
438 DLOG_DEBUG(__func__);
440 // Never seen this called.
442 networks_search_stop();
445 static void icd_child_exit(const pid_t pid,
446 const gint exit_value,
449 DLOG_DEBUG(__func__);
451 /*// Supplicant crashed/exited!
452 if (killed_supp_cb) {
453 // We have killed it, notify auth layer is now down.
455 g_free(killed_supp_cb);
458 DLOG_WARN("Supplicant exited, but we didn't expect it");
459 icd_close(ICD_NW_ERROR, ICD_DBUS_ERROR_SYSTEM_ERROR);
461 supp_handle_killed();
464 static void icd_network_destruct(gpointer *private)
466 DLOG_DEBUG(__func__);
468 // Fortunately, icd kills the supplicant for us.
470 close_dbus_connection();
474 gboolean icd_nw_init (
475 struct icd_nw_api *network_api,
476 icd_nw_watch_pid_fn watch_fn,
477 gpointer watch_fn_token,
478 icd_nw_close_fn close_fn,
479 icd_nw_status_change_fn status_change_fn,
480 icd_nw_renew_fn renew_fn )
482 network_api->version = ICD_NW_MODULE_VERSION;
483 network_api->private = NULL;
485 network_api->link_pre_down = icd_pre_down;
486 network_api->link_post_up = icd_post_up;
488 network_api->link_down = icd_link_down;
489 network_api->link_up = icd_link_up;
490 network_api->link_stats = icd_link_stats;
492 network_api->search_lifetime = ICD_SEARCH_LIFETIME;
493 network_api->search_interval = ICD_SEARCH_INTERVAL;
495 network_api->start_search = icd_start_search;
496 network_api->stop_search = icd_stop_search;
498 network_api->child_exit = icd_child_exit;
500 network_api->network_destruct = icd_network_destruct;
502 icd_watch_fn = watch_fn;
503 icd_watch_token = watch_fn_token;
504 icd_close_fn = close_fn;
505 icd_status_change_fn = status_change_fn;
506 icd_renew_fn = renew_fn;
508 if (networks_initialize() != 0) {
509 DLOG_ERR_L("Network list failed!");
513 if (setup_dbus_connection(NULL, init_dbus_handlers) != 0) {
514 DLOG_ERR_L("D-BUS connection setup failed!");
521 void icd_watch_pid(const pid_t pid)
523 icd_watch_fn(pid, icd_watch_token);
526 void icd_close(enum icd_nw_status status,
527 const gchar *err_str)
529 icd_close_fn(status, err_str, WPA_NETWORK_TYPE, ICD_WPA_FLAGS, cur_network_id);