f52d84808cf0a759c66da9276a866a494d7d6488
[framework/connectivity/connman.git] / plugins / wifi.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <linux/if_arp.h>
32 #include <linux/wireless.h>
33
34 #include <gdbus.h>
35
36 #define CONNMAN_API_SUBJECT_TO_CHANGE
37 #include <connman/plugin.h>
38 #include <connman/driver.h>
39 #include <connman/dbus.h>
40 #include <connman/log.h>
41
42 #include "inet.h"
43 #include "supplicant.h"
44
45 #define CLEANUP_TIMEOUT   8     /* in seconds */
46 #define INACTIVE_TIMEOUT  12    /* in seconds */
47
48 struct wifi_data {
49         GSList *current;
50         GSList *pending;
51         guint cleanup_timer;
52         guint inactive_timer;
53         gchar *identifier;
54         gboolean connected;
55 };
56
57 static int network_probe(struct connman_element *element)
58 {
59         DBG("element %p name %s", element, element->name);
60
61         return 0;
62 }
63
64 static void network_remove(struct connman_element *element)
65 {
66         DBG("element %p name %s", element, element->name);
67 }
68
69 static int network_enable(struct connman_element *element)
70 {
71         struct connman_device *device = (struct connman_device *) element->parent;
72         char *name, *security = NULL, *passphrase = NULL;
73         unsigned char *ssid;
74         int ssid_len;
75
76         DBG("element %p name %s", element, element->name);
77
78         if (connman_element_get_static_property(element,
79                                                 "Name", &name) == FALSE)
80                 return -EIO;
81
82         if (connman_element_get_static_array_property(element,
83                                 "WiFi.SSID", &ssid, &ssid_len) == FALSE)
84                 return -EIO;
85
86         if (device != NULL) {
87                 struct wifi_data *data = connman_device_get_data(device);
88
89                 if (data != NULL) {
90                         if (data->connected == TRUE)
91                                 return -EBUSY;
92
93                         g_free(data->identifier);
94                         data->identifier = g_strdup(name);
95                 }
96         }
97
98         connman_element_get_value(element,
99                         CONNMAN_PROPERTY_ID_WIFI_SECURITY, &security);
100
101         connman_element_get_value(element,
102                         CONNMAN_PROPERTY_ID_WIFI_PASSPHRASE, &passphrase);
103
104         DBG("name %s security %s passhprase %s",
105                                         name, security, passphrase);
106
107         if (__supplicant_connect(element, ssid, ssid_len,
108                                                 security, passphrase) < 0)
109                 connman_error("Failed to initiate connect");
110
111         return 0;
112 }
113
114 static int network_disable(struct connman_element *element)
115 {
116         DBG("element %p name %s", element, element->name);
117
118         connman_element_unregister_children(element);
119
120         __supplicant_disconnect(element);
121
122         return 0;
123 }
124
125 static struct connman_driver network_driver = {
126         .name           = "wifi-network",
127         .type           = CONNMAN_ELEMENT_TYPE_NETWORK,
128         .subtype        = CONNMAN_ELEMENT_SUBTYPE_WIFI,
129         .probe          = network_probe,
130         .remove         = network_remove,
131         .enable         = network_enable,
132         .disable        = network_disable,
133 };
134
135 static struct connman_element *find_current_element(struct wifi_data *data,
136                                                         const char *identifier)
137 {
138         GSList *list;
139
140         for (list = data->current; list; list = list->next) {
141                 struct connman_element *element = list->data;
142
143                 if (connman_element_match_static_property(element,
144                                                 "Name", &identifier) == TRUE)
145                         return element;
146         }
147
148         return NULL;
149 }
150
151 static struct connman_element *find_pending_element(struct wifi_data *data,
152                                                         const char *identifier)
153 {
154         GSList *list;
155
156         for (list = data->pending; list; list = list->next) {
157                 struct connman_element *element = list->data;
158
159                 if (connman_element_match_static_property(element,
160                                                 "Name", &identifier) == TRUE)
161                         return element;
162         }
163
164         return NULL;
165 }
166
167 static gboolean inactive_scan(gpointer user_data)
168 {
169         struct connman_device *device = user_data;
170         struct wifi_data *data = connman_device_get_data(device);
171
172         DBG("device %p", device);
173
174         __supplicant_scan(device);
175
176         data->inactive_timer = 0;
177
178         return FALSE;
179 }
180
181 static void connect_known_networks(struct connman_device *device)
182 {
183         struct wifi_data *data = connman_device_get_data(device);
184         GSList *list;
185
186         DBG("device %p", device);
187
188         if (data->inactive_timer > 0) {
189                 g_source_remove(data->inactive_timer);
190                 data->inactive_timer = 0;
191         }
192
193         for (list = data->current; list; list = list->next) {
194                 struct connman_element *element = list->data;
195
196                 if (element->policy == CONNMAN_ELEMENT_POLICY_AUTO &&
197                                                 element->remember == TRUE &&
198                                                 element->available == TRUE) {
199                         if (network_enable(element) == 0)
200                                 return;
201                 }
202         }
203
204         data->inactive_timer = g_timeout_add_seconds(INACTIVE_TIMEOUT,
205                                                         inactive_scan, device);
206 }
207
208 static void state_change(struct connman_device *device,
209                                                 enum supplicant_state state)
210 {
211         struct wifi_data *data = connman_device_get_data(device);
212         struct connman_element *element;
213
214         DBG("device %p state %d", device, state);
215
216         if (state == STATE_SCANNING)
217                 connman_device_set_scanning(device, TRUE);
218         else
219                 connman_device_set_scanning(device, FALSE);
220
221         if (data == NULL)
222                 return;
223
224         DBG("identifier %s", data->identifier);
225
226         if (data->identifier == NULL)
227                 goto reconnect;
228
229         element = find_current_element(data, data->identifier);
230         if (element == NULL)
231                 goto reconnect;
232
233         if (state == STATE_COMPLETED && data->connected == FALSE) {
234                 struct connman_element *dhcp;
235
236                 data->connected = TRUE;
237                 connman_element_set_enabled(element, TRUE);
238
239                 dhcp = connman_element_create(NULL);
240
241                 dhcp->type = CONNMAN_ELEMENT_TYPE_DHCP;
242                 dhcp->index = element->index;
243
244                 if (connman_element_register(dhcp, element) < 0)
245                         connman_element_unref(dhcp);
246         } else if (state == STATE_INACTIVE || state == STATE_DISCONNECTED) {
247                 data->connected = FALSE;
248                 connman_element_set_enabled(element, FALSE);
249
250                 connman_element_unregister_children(element);
251         }
252
253 reconnect:
254         if (state == STATE_INACTIVE) {
255                 data->connected = FALSE;
256                 connect_known_networks(device);
257         }
258 }
259
260 static gboolean cleanup_pending(gpointer user_data)
261 {
262         struct wifi_data *data = user_data;
263         GSList *list;
264
265         DBG("");
266
267         for (list = data->pending; list; list = list->next) {
268                 struct connman_element *element = list->data;
269
270                 DBG("element %p name %s", element, element->name);
271
272                 connman_element_unregister(element);
273                 connman_element_unref(element);
274         }
275
276         g_slist_free(data->pending);
277         data->pending = NULL;
278
279         data->cleanup_timer = 0;
280
281         return FALSE;
282 }
283
284 static void clear_results(struct connman_device *device)
285 {
286         struct wifi_data *data = connman_device_get_data(device);
287
288         DBG("pending %d", g_slist_length(data->pending));
289         DBG("current %d", g_slist_length(data->current));
290
291         if (data->cleanup_timer > 0) {
292                 g_source_remove(data->cleanup_timer);
293                 cleanup_pending(data);
294         }
295
296         data->pending = data->current;
297         data->current = NULL;
298
299         data->cleanup_timer = g_timeout_add_seconds(CLEANUP_TIMEOUT,
300                                                         cleanup_pending, data);
301 }
302
303 static void scan_result(struct connman_device *device,
304                                         struct supplicant_network *network)
305 {
306         struct wifi_data *data = connman_device_get_data(device);
307         struct connman_element *element;
308         gchar *temp;
309         unsigned int i;
310
311         DBG("device %p identifier %s", device, network->identifier);
312
313         if (data == NULL)
314                 return;
315
316         if (network->identifier == NULL)
317                 return;
318
319         if (network->identifier[0] == '\0')
320                 return;
321
322         temp = g_strdup(network->identifier);
323
324         for (i = 0; i < strlen(temp); i++) {
325                 char tmp = temp[i];
326                 if ((tmp < '0' || tmp > '9') && (tmp < 'A' || tmp > 'Z') &&
327                                                 (tmp < 'a' || tmp > 'z'))
328                         temp[i] = '_';
329         }
330
331         element = find_pending_element(data, network->identifier);
332         if (element == NULL) {
333                 const char *mode;
334
335                 element = connman_element_create(temp);
336
337                 element->type    = CONNMAN_ELEMENT_TYPE_NETWORK;
338                 element->subtype = CONNMAN_ELEMENT_SUBTYPE_WIFI;
339                 element->index   = connman_device_get_index(device);
340
341                 connman_element_add_static_property(element, "Name",
342                                 DBUS_TYPE_STRING, &network->identifier);
343
344                 connman_element_add_static_array_property(element, "WiFi.SSID",
345                         DBUS_TYPE_BYTE, &network->ssid, network->ssid_len);
346
347                 mode = (network->adhoc == TRUE) ? "adhoc" : "managed";
348                 connman_element_add_static_property(element, "WiFi.Mode",
349                                                 DBUS_TYPE_STRING, &mode);
350
351                 if (element->wifi.security == NULL) {
352                         const char *security;
353
354                         if (network->has_rsn == TRUE)
355                                 security = "wpa2";
356                         else if (network->has_wpa == TRUE)
357                                 security = "wpa";
358                         else if (network->has_wep == TRUE)
359                                 security = "wep";
360                         else
361                                 security = "none";
362
363                         element->wifi.security = g_strdup(security);
364                 }
365
366                 element->strength = network->quality;
367
368                 connman_element_add_static_property(element, "Strength",
369                                         DBUS_TYPE_BYTE, &element->strength);
370
371                 DBG("%s (%s %s) strength %d", network->identifier, mode,
372                                 element->wifi.security, element->strength);
373
374                 if (connman_element_register(element,
375                                 (struct connman_element *) device) < 0) {
376                         connman_element_unref(element);
377                         goto done;
378                 }
379         } else {
380                 data->pending = g_slist_remove(data->pending, element);
381
382                 if (element->strength != network->quality) {
383                         element->strength = network->quality;
384
385                         connman_element_set_static_property(element, "Strength",
386                                         DBUS_TYPE_BYTE, &element->strength);
387
388                         connman_element_update(element);
389                 }
390         }
391
392         data->current = g_slist_append(data->current, element);
393
394         element->available = TRUE;
395
396 done:
397         g_free(temp);
398 }
399
400 static struct supplicant_callback wifi_callback = {
401         .state_change   = state_change,
402         .clear_results  = clear_results,
403         .scan_result    = scan_result,
404 };
405
406 static int wifi_probe(struct connman_device *device)
407 {
408         struct wifi_data *data;
409
410         DBG("device %p", device);
411
412         data = g_try_new0(struct wifi_data, 1);
413         if (data == NULL)
414                 return -ENOMEM;
415
416         data->connected = FALSE;
417
418         connman_device_set_data(device, data);
419
420         return 0;
421 }
422
423 static void wifi_remove(struct connman_device *device)
424 {
425         struct wifi_data *data = connman_device_get_data(device);
426
427         DBG("device %p", device);
428
429         connman_device_set_data(device, NULL);
430
431         g_free(data->identifier);
432         g_free(data);
433 }
434
435 static int wifi_enable(struct connman_device *device)
436 {
437         int err;
438
439         DBG("device %p", device);
440
441         err = __supplicant_start(device, &wifi_callback);
442         if (err < 0)
443                 return err;
444
445         connman_device_set_powered(device, TRUE);
446
447         __supplicant_scan(device);
448
449         return 0;
450 }
451
452 static int wifi_disable(struct connman_device *device)
453 {
454         struct wifi_data *data = connman_device_get_data(device);
455         GSList *list;
456
457         DBG("device %p", device);
458
459         if (data->cleanup_timer > 0) {
460                 g_source_remove(data->cleanup_timer);
461                 cleanup_pending(data);
462         }
463
464         if (data->inactive_timer > 0) {
465                 g_source_remove(data->inactive_timer);
466                 data->inactive_timer = 0;
467         }
468
469         for (list = data->current; list; list = list->next) {
470                 struct connman_element *network = list->data;
471
472                 if (network->enabled == TRUE)
473                         __supplicant_disconnect(network);
474
475                 connman_element_unref(network);
476         }
477
478         g_slist_free(data->current);
479         data->current = NULL;
480
481         connman_element_unregister_children((struct connman_element *) device);
482
483         __supplicant_stop(device);
484
485         connman_device_set_powered(device, FALSE);
486
487         return 0;
488 }
489
490 static int wifi_scan(struct connman_device *device)
491 {
492         DBG("device %p", device);
493
494         __supplicant_scan(device);
495
496         return 0;
497 }
498
499 static struct connman_device_driver wifi_driver = {
500         .name           = "wifi",
501         .type           = CONNMAN_DEVICE_TYPE_WIFI,
502         .probe          = wifi_probe,
503         .remove         = wifi_remove,
504         .enable         = wifi_enable,
505         .disable        = wifi_disable,
506         .scan           = wifi_scan,
507 };
508
509 static void supplicant_connect(DBusConnection *connection, void *user_data)
510 {
511         DBG("connection %p", connection);
512
513         __supplicant_init(connection);
514
515         connman_device_driver_register(&wifi_driver);
516 }
517
518 static void supplicant_disconnect(DBusConnection *connection, void *user_data)
519 {
520         DBG("connection %p", connection);
521
522         connman_device_driver_unregister(&wifi_driver);
523
524         __supplicant_exit();
525 }
526
527 static DBusConnection *connection;
528 static guint watch;
529
530 static int wifi_init(void)
531 {
532         int err;
533
534         connection = connman_dbus_get_connection();
535         if (connection == NULL)
536                 return -EIO;
537
538         err = connman_driver_register(&network_driver);
539         if (err < 0) {
540                 dbus_connection_unref(connection);
541                 return err;
542         }
543
544         watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
545                         supplicant_connect, supplicant_disconnect, NULL, NULL);
546
547         if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
548                 supplicant_connect(connection, NULL);
549         else
550                 __supplicant_activate(connection);
551
552         return 0;
553 }
554
555 static void wifi_exit(void)
556 {
557         connman_driver_unregister(&network_driver);
558
559         if (watch > 0)
560                 g_dbus_remove_watch(connection, watch);
561
562         supplicant_disconnect(connection, NULL);
563
564         dbus_connection_unref(connection);
565 }
566
567 CONNMAN_PLUGIN_DEFINE(wifi, "WiFi interface plugin", VERSION,
568                                                         wifi_init, wifi_exit)