Verify argument type before processing
[connman] / src / device.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 <errno.h>
27 #include <gdbus.h>
28
29 #include "connman.h"
30
31 struct connman_device {
32         struct connman_element element;
33         enum connman_device_type type;
34         enum connman_device_mode mode;
35         enum connman_device_policy policy;
36         connman_bool_t powered;
37         connman_bool_t carrier;
38         connman_bool_t scanning;
39         connman_uint8_t priority;
40         char *name;
41         char *node;
42         char *interface;
43         unsigned int connections;
44
45         struct connman_device_driver *driver;
46         void *driver_data;
47
48         connman_bool_t registered;
49
50         GHashTable *networks;
51 };
52
53 static const char *type2description(enum connman_device_type type)
54 {
55         switch (type) {
56         case CONNMAN_DEVICE_TYPE_UNKNOWN:
57         case CONNMAN_DEVICE_TYPE_VENDOR:
58                 break;
59         case CONNMAN_DEVICE_TYPE_ETHERNET:
60                 return "Ethernet";
61         case CONNMAN_DEVICE_TYPE_WIFI:
62                 return "Wireless";
63         case CONNMAN_DEVICE_TYPE_WIMAX:
64                 return "WiMAX";
65         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
66                 return "Bluetooth";
67         case CONNMAN_DEVICE_TYPE_HSO:
68         case CONNMAN_DEVICE_TYPE_NOZOMI:
69         case CONNMAN_DEVICE_TYPE_HUAWEI:
70         case CONNMAN_DEVICE_TYPE_NOVATEL:
71                 return "Cellular";
72         }
73
74         return NULL;
75 }
76
77 static const char *type2string(enum connman_device_type type)
78 {
79         switch (type) {
80         case CONNMAN_DEVICE_TYPE_UNKNOWN:
81         case CONNMAN_DEVICE_TYPE_VENDOR:
82                 break;
83         case CONNMAN_DEVICE_TYPE_ETHERNET:
84                 return "ethernet";
85         case CONNMAN_DEVICE_TYPE_WIFI:
86                 return "wifi";
87         case CONNMAN_DEVICE_TYPE_WIMAX:
88                 return "wimax";
89         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
90                 return "bluetooth";
91         case CONNMAN_DEVICE_TYPE_HSO:
92         case CONNMAN_DEVICE_TYPE_HUAWEI:
93         case CONNMAN_DEVICE_TYPE_NOZOMI:
94         case CONNMAN_DEVICE_TYPE_NOVATEL:
95                 return "cellular";
96         }
97
98         return NULL;
99 }
100
101 static const char *policy2string(enum connman_device_policy policy)
102 {
103         switch (policy) {
104         case CONNMAN_DEVICE_POLICY_UNKNOWN:
105                 break;
106         case CONNMAN_DEVICE_POLICY_IGNORE:
107                 return "ignore";
108         case CONNMAN_DEVICE_POLICY_OFF:
109                 return "off";
110         case CONNMAN_DEVICE_POLICY_AUTO:
111                 return "auto";
112         case CONNMAN_DEVICE_POLICY_MANUAL:
113                 return "manual";
114         }
115
116         return NULL;
117 }
118
119 static enum connman_device_policy string2policy(const char *policy)
120 {
121         if (g_str_equal(policy, "ignore") == TRUE)
122                 return CONNMAN_DEVICE_POLICY_IGNORE;
123         else if (g_str_equal(policy, "off") == TRUE)
124                 return CONNMAN_DEVICE_POLICY_OFF;
125         else if (g_str_equal(policy, "auto") == TRUE)
126                 return CONNMAN_DEVICE_POLICY_AUTO;
127         else if (g_str_equal(policy, "manual") == TRUE)
128                 return CONNMAN_DEVICE_POLICY_MANUAL;
129         else
130                 return CONNMAN_DEVICE_POLICY_UNKNOWN;
131 }
132
133 static int set_powered(struct connman_device *device, connman_bool_t powered)
134 {
135         struct connman_device_driver *driver = device->driver;
136         int err;
137
138         DBG("device %p powered %d", device, powered);
139
140         if (!driver)
141                 return -EINVAL;
142
143         if (powered == TRUE) {
144                 if (driver->enable)
145                         err = driver->enable(device);
146                 else
147                         err = -EINVAL;
148         } else {
149                 g_hash_table_remove_all(device->networks);
150
151                 if (driver->disable)
152                         err = driver->disable(device);
153                 else
154                         err = -EINVAL;
155         }
156
157         return err;
158 }
159
160 static int set_policy(DBusConnection *connection,
161                                 struct connman_device *device,
162                                         enum connman_device_policy policy)
163 {
164         DBusMessage *signal;
165         DBusMessageIter entry, value;
166         const char *str, *key = "Policy";
167         int err = 0;
168
169         DBG("device %p policy %d", device, policy);
170
171         if (device->policy == policy)
172                 return 0;
173
174         switch (policy) {
175         case CONNMAN_DEVICE_POLICY_UNKNOWN:
176                 return -EINVAL;
177         case CONNMAN_DEVICE_POLICY_IGNORE:
178                 break;
179         case CONNMAN_DEVICE_POLICY_OFF:
180                 if (device->powered == TRUE)
181                         err = set_powered(device, FALSE);
182                 break;
183         case CONNMAN_DEVICE_POLICY_AUTO:
184         case CONNMAN_DEVICE_POLICY_MANUAL:
185                 if (device->powered == FALSE)
186                         err = set_powered(device, TRUE);
187                 break;
188         }
189
190         if (err < 0)
191                 return err;
192
193         device->policy = policy;
194
195         signal = dbus_message_new_signal(device->element.path,
196                                 CONNMAN_DEVICE_INTERFACE, "PropertyChanged");
197         if (signal == NULL)
198                 return 0;
199
200         dbus_message_iter_init_append(signal, &entry);
201
202         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
203
204         str = policy2string(policy);
205
206         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
207                                         DBUS_TYPE_STRING_AS_STRING, &value);
208         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
209         dbus_message_iter_close_container(&entry, &value);
210
211         g_dbus_send_message(connection, signal);
212
213         return 0;
214 }
215
216 static void append_networks(struct connman_device *device,
217                                                 DBusMessageIter *entry)
218 {
219         DBusMessageIter value, iter;
220         const char *key = "Networks";
221
222         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
223
224         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
225                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
226                                                                 &value);
227
228         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
229                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
230         __connman_element_list((struct connman_element *) device,
231                                         CONNMAN_ELEMENT_TYPE_NETWORK, &iter);
232         dbus_message_iter_close_container(&value, &iter);
233
234         dbus_message_iter_close_container(entry, &value);
235 }
236
237 static DBusMessage *get_properties(DBusConnection *conn,
238                                         DBusMessage *msg, void *data)
239 {
240         struct connman_device *device = data;
241         DBusMessage *reply;
242         DBusMessageIter array, dict, entry;
243         const char *str;
244
245         DBG("conn %p", conn);
246
247         if (__connman_security_check_privilege(msg,
248                                         CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
249                 return __connman_error_permission_denied(msg);
250
251         reply = dbus_message_new_method_return(msg);
252         if (reply == NULL)
253                 return NULL;
254
255         dbus_message_iter_init_append(reply, &array);
256
257         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
258                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
259                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
260                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
261
262         str = type2description(device->type);
263         if (str != NULL && device->interface != NULL) {
264                 char *name = g_strdup_printf("%s (%s)", str, device->interface);
265                 if (name != NULL)
266                         connman_dbus_dict_append_variant(&dict, "Name",
267                                                 DBUS_TYPE_STRING, &name);
268                 g_free(name);
269         }
270
271         str = type2string(device->type);
272         if (str != NULL)
273                 connman_dbus_dict_append_variant(&dict, "Type",
274                                                 DBUS_TYPE_STRING, &str);
275
276         if (device->interface != NULL)
277                 connman_dbus_dict_append_variant(&dict, "Interface",
278                                         DBUS_TYPE_STRING, &device->interface);
279
280         str = policy2string(device->policy);
281         if (str != NULL)
282                 connman_dbus_dict_append_variant(&dict, "Policy",
283                                                 DBUS_TYPE_STRING, &str);
284
285         if (device->priority > 0)
286                 connman_dbus_dict_append_variant(&dict, "Priority",
287                                         DBUS_TYPE_BYTE, &device->priority);
288
289         connman_dbus_dict_append_variant(&dict, "Powered",
290                                         DBUS_TYPE_BOOLEAN, &device->powered);
291
292         if (device->driver && device->driver->scan)
293                 connman_dbus_dict_append_variant(&dict, "Scanning",
294                                         DBUS_TYPE_BOOLEAN, &device->scanning);
295
296         switch (device->mode) {
297         case CONNMAN_DEVICE_MODE_UNKNOWN:
298         case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
299                 break;
300         case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
301         case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
302                 dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
303                                                                 NULL, &entry);
304                 append_networks(device, &entry);
305                 dbus_message_iter_close_container(&dict, &entry);
306                 break;
307         }
308
309         dbus_message_iter_close_container(&array, &dict);
310
311         return reply;
312 }
313
314 static DBusMessage *set_property(DBusConnection *conn,
315                                         DBusMessage *msg, void *data)
316 {
317         struct connman_device *device = data;
318         DBusMessageIter iter, value;
319         const char *name;
320         int type;
321
322         DBG("conn %p", conn);
323
324         if (dbus_message_iter_init(msg, &iter) == FALSE)
325                 return __connman_error_invalid_arguments(msg);
326
327         dbus_message_iter_get_basic(&iter, &name);
328         dbus_message_iter_next(&iter);
329         dbus_message_iter_recurse(&iter, &value);
330
331         if (__connman_security_check_privilege(msg,
332                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
333                 return __connman_error_permission_denied(msg);
334
335         type = dbus_message_iter_get_arg_type(&value);
336
337         if (g_str_equal(name, "Powered") == TRUE) {
338                 connman_bool_t powered;
339                 int err;
340
341                 if (type != DBUS_TYPE_BOOLEAN)
342                         return __connman_error_invalid_arguments(msg);
343
344                 dbus_message_iter_get_basic(&value, &powered);
345
346                 if (device->powered == powered)
347                         return __connman_error_invalid_arguments(msg);
348
349                 err = set_powered(device, powered);
350                 if (err < 0 && err != -EINPROGRESS)
351                         return __connman_error_failed(msg);
352         } else if (g_str_equal(name, "Policy") == TRUE) {
353                 enum connman_device_policy policy;
354                 const char *str;
355                 int err;
356
357                 if (type != DBUS_TYPE_STRING)
358                         return __connman_error_invalid_arguments(msg);
359
360                 dbus_message_iter_get_basic(&value, &str);
361                 policy = string2policy(str);
362                 if (policy == CONNMAN_DEVICE_POLICY_UNKNOWN)
363                         return __connman_error_invalid_arguments(msg);
364
365                 err = set_policy(conn, device, policy);
366                 if (err < 0)
367                         return __connman_error_failed(msg);
368         } else if (g_str_equal(name, "Priority") == TRUE) {
369                 connman_uint8_t priority;
370
371                 if (type != DBUS_TYPE_BYTE)
372                         return __connman_error_invalid_arguments(msg);
373
374                 dbus_message_iter_get_basic(&value, &priority);
375
376                 device->priority = priority;
377         }
378
379         __connman_storage_save_device(device);
380
381         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
382 }
383
384 static DBusMessage *create_network(DBusConnection *conn,
385                                         DBusMessage *msg, void *data)
386 {
387         DBG("conn %p", conn);
388
389         if (__connman_security_check_privilege(msg,
390                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
391                 return __connman_error_permission_denied(msg);
392
393         return __connman_error_invalid_arguments(msg);
394 }
395
396 static DBusMessage *remove_network(DBusConnection *conn,
397                                         DBusMessage *msg, void *data)
398 {
399         DBG("conn %p", conn);
400
401         if (__connman_security_check_privilege(msg,
402                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
403                 return __connman_error_permission_denied(msg);
404
405         return __connman_error_invalid_arguments(msg);
406 }
407
408 static DBusMessage *propose_scan(DBusConnection *conn,
409                                         DBusMessage *msg, void *data)
410 {
411         struct connman_device *device = data;
412         int err;
413
414         DBG("conn %p", conn);
415
416         switch (device->mode) {
417         case CONNMAN_DEVICE_MODE_UNKNOWN:
418         case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
419                 return __connman_error_not_supported(msg);
420         case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
421         case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
422                 break;
423         }
424
425         if (!device->driver || !device->driver->scan)
426                 return __connman_error_not_supported(msg);
427
428         if (device->powered == FALSE)
429                 return __connman_error_failed(msg);
430
431         err = device->driver->scan(device);
432         if (err < 0)
433                 return __connman_error_failed(msg);
434
435         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
436 }
437
438 static GDBusMethodTable device_methods[] = {
439         { "GetProperties", "",      "a{sv}", get_properties },
440         { "SetProperty",   "sv",    "",      set_property   },
441         { "CreateNetwork", "a{sv}", "o",     create_network },
442         { "RemoveNetwork", "o",     "",      remove_network },
443         { "ProposeScan",   "",      "",      propose_scan   },
444         { },
445 };
446
447 static GDBusSignalTable device_signals[] = {
448         { "PropertyChanged", "sv" },
449         { },
450 };
451
452 static DBusConnection *connection;
453
454 static void append_devices(DBusMessageIter *entry)
455 {
456         DBusMessageIter value, iter;
457         const char *key = "Devices";
458
459         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
460
461         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
462                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
463                                                                 &value);
464
465         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
466                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
467         __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, &iter);
468         dbus_message_iter_close_container(&value, &iter);
469
470         dbus_message_iter_close_container(entry, &value);
471 }
472
473 static void emit_devices_signal(void)
474 {
475         DBusMessage *signal;
476         DBusMessageIter entry;
477
478         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
479                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
480         if (signal == NULL)
481                 return;
482
483         dbus_message_iter_init_append(signal, &entry);
484
485         append_devices(&entry);
486
487         g_dbus_send_message(connection, signal);
488 }
489
490 static int register_interface(struct connman_element *element)
491 {
492         struct connman_device *device = element->device;
493
494         DBG("element %p name %s", element, element->name);
495
496         if (g_dbus_register_interface(connection, element->path,
497                                         CONNMAN_DEVICE_INTERFACE,
498                                         device_methods, device_signals,
499                                         NULL, device, NULL) == FALSE) {
500                 connman_error("Failed to register %s device", element->path);
501                 return -EIO;
502         }
503
504         device->registered = TRUE;
505
506         emit_devices_signal();
507
508         return 0;
509 }
510
511 static void unregister_interface(struct connman_element *element)
512 {
513         struct connman_device *device = element->device;
514
515         DBG("element %p name %s", element, element->name);
516
517         device->registered = FALSE;
518
519         emit_devices_signal();
520
521         g_dbus_unregister_interface(connection, element->path,
522                                                 CONNMAN_DEVICE_INTERFACE);
523 }
524
525 static void device_enable(struct connman_device *device)
526 {
527         DBG("device %p", device);
528
529         if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE ||
530                                 device->policy == CONNMAN_DEVICE_POLICY_OFF)
531                 return;
532
533         if (device->powered == TRUE)
534                 return;
535
536         if (device->driver->enable)
537                 device->driver->enable(device);
538 }
539
540 static void device_disable(struct connman_device *device)
541 {
542         DBG("device %p", device);
543
544         if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE)
545                 return;
546
547         if (device->powered == FALSE)
548                 return;
549
550         g_hash_table_remove_all(device->networks);
551
552         if (device->driver->disable)
553                 device->driver->disable(device);
554 }
555
556 static int setup_device(struct connman_device *device)
557 {
558         int err;
559
560         DBG("device %p", device);
561
562         err = register_interface(&device->element);
563         if (err < 0) {
564                 if (device->driver->remove)
565                         device->driver->remove(device);
566                 device->driver = NULL;
567                 return err;
568         }
569
570         device_enable(device);
571
572         return 0;
573 }
574
575 static void probe_driver(struct connman_element *element, gpointer user_data)
576 {
577         struct connman_device_driver *driver = user_data;
578
579         DBG("element %p name %s", element, element->name);
580
581         if (element->device == NULL)
582                 return;
583
584         if (element->device->driver != NULL)
585                 return;
586
587         if (driver->probe(element->device) < 0)
588                 return;
589
590         element->device->driver = driver;
591
592         setup_device(element->device);
593 }
594
595 static void remove_device(struct connman_device *device)
596 {
597         DBG("device %p", device);
598
599         device_disable(device);
600
601         unregister_interface(&device->element);
602
603         if (device->driver->remove)
604                 device->driver->remove(device);
605
606         device->driver = NULL;
607 }
608
609 static void remove_driver(struct connman_element *element, gpointer user_data)
610 {
611         struct connman_device_driver *driver = user_data;
612
613         DBG("element %p name %s", element, element->name);
614
615         if (element->device == NULL)
616                 return;
617
618         if (element->device->driver == driver)
619                 remove_device(element->device);
620 }
621
622 connman_bool_t __connman_device_has_driver(struct connman_device *device)
623 {
624         if (device == NULL || device->driver == NULL)
625                 return FALSE;
626
627         return device->registered;
628 }
629
630 static GSList *driver_list = NULL;
631
632 static gint compare_priority(gconstpointer a, gconstpointer b)
633 {
634         const struct connman_device_driver *driver1 = a;
635         const struct connman_device_driver *driver2 = b;
636
637         return driver2->priority - driver1->priority;
638 }
639
640 /**
641  * connman_device_driver_register:
642  * @driver: device driver definition
643  *
644  * Register a new device driver
645  *
646  * Returns: %0 on success
647  */
648 int connman_device_driver_register(struct connman_device_driver *driver)
649 {
650         DBG("driver %p name %s", driver, driver->name);
651
652         driver_list = g_slist_insert_sorted(driver_list, driver,
653                                                         compare_priority);
654
655         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
656                                                 probe_driver, driver);
657
658         return 0;
659 }
660
661 /**
662  * connman_device_driver_unregister:
663  * @driver: device driver definition
664  *
665  * Remove a previously registered device driver
666  */
667 void connman_device_driver_unregister(struct connman_device_driver *driver)
668 {
669         DBG("driver %p name %s", driver, driver->name);
670
671         driver_list = g_slist_remove(driver_list, driver);
672
673         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
674                                                 remove_driver, driver);
675 }
676
677 static void unregister_network(gpointer data)
678 {
679         struct connman_network *network = data;
680
681         DBG("network %p", network);
682
683         connman_element_unregister((struct connman_element *) network);
684
685         connman_network_unref(network);
686 }
687
688 static void device_destruct(struct connman_element *element)
689 {
690         struct connman_device *device = element->device;
691
692         DBG("element %p name %s", element, element->name);
693
694         g_free(device->node);
695         g_free(device->name);
696         g_free(device->interface);
697
698         g_hash_table_destroy(device->networks);
699         device->networks = NULL;
700 }
701
702 /**
703  * connman_device_create:
704  * @node: device node name (for example an address)
705  * @type: device type
706  *
707  * Allocate a new device of given #type and assign the #node name to it.
708  *
709  * Returns: a newly-allocated #connman_device structure
710  */
711 struct connman_device *connman_device_create(const char *node,
712                                                 enum connman_device_type type)
713 {
714         struct connman_device *device;
715         const char *str;
716
717         DBG("node %s type %d", node, type);
718
719         device = g_try_new0(struct connman_device, 1);
720         if (device == NULL)
721                 return NULL;
722
723         DBG("device %p", device);
724
725         __connman_element_initialize(&device->element);
726
727         device->element.name = g_strdup(node);
728         device->element.type = CONNMAN_ELEMENT_TYPE_DEVICE;
729
730         device->element.device = device;
731         device->element.destruct = device_destruct;
732
733         str = type2string(type);
734         if (str != NULL)
735                 connman_element_add_static_property(&device->element,
736                                         "Type", DBUS_TYPE_STRING, &str);
737
738         device->type   = type;
739         device->mode   = CONNMAN_DEVICE_MODE_UNKNOWN;
740         device->policy = CONNMAN_DEVICE_POLICY_AUTO;
741
742         switch (type) {
743         case CONNMAN_DEVICE_TYPE_UNKNOWN:
744         case CONNMAN_DEVICE_TYPE_VENDOR:
745                 device->priority = 0;
746                 break;
747         case CONNMAN_DEVICE_TYPE_ETHERNET:
748         case CONNMAN_DEVICE_TYPE_WIFI:
749                 device->priority = 100;
750                 break;
751         case CONNMAN_DEVICE_TYPE_WIMAX:
752                 device->priority = 20;
753                 break;
754         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
755                 device->priority = 50;
756                 break;
757         case CONNMAN_DEVICE_TYPE_HSO:
758         case CONNMAN_DEVICE_TYPE_NOZOMI:
759         case CONNMAN_DEVICE_TYPE_HUAWEI:
760         case CONNMAN_DEVICE_TYPE_NOVATEL:
761                 device->priority = 60;
762                 break;
763         }
764
765         device->networks = g_hash_table_new_full(g_str_hash, g_str_equal,
766                                                 g_free, unregister_network);
767
768         return device;
769 }
770
771 /**
772  * connman_device_ref:
773  * @device: device structure
774  *
775  * Increase reference counter of device
776  */
777 struct connman_device *connman_device_ref(struct connman_device *device)
778 {
779         if (connman_element_ref(&device->element) == NULL)
780                 return NULL;
781
782         return device;
783 }
784
785 /**
786  * connman_device_unref:
787  * @device: device structure
788  *
789  * Decrease reference counter of device
790  */
791 void connman_device_unref(struct connman_device *device)
792 {
793         connman_element_unref(&device->element);
794 }
795
796 /**
797  * connman_device_get_name:
798  * @device: device structure
799  *
800  * Get unique name of device
801  */
802 const char *connman_device_get_name(struct connman_device *device)
803 {
804         return device->element.name;
805 }
806
807 /**
808  * connman_device_get_path:
809  * @device: device structure
810  *
811  * Get path name of device
812  */
813 const char *connman_device_get_path(struct connman_device *device)
814 {
815         return device->element.path;
816 }
817
818 /**
819  * connman_device_set_index:
820  * @device: device structure
821  * @index: index number
822  *
823  * Set index number of device
824  */
825 void connman_device_set_index(struct connman_device *device, int index)
826 {
827         device->element.index = index;
828 }
829
830 /**
831  * connman_device_get_index:
832  * @device: device structure
833  *
834  * Get index number of device
835  */
836 int connman_device_get_index(struct connman_device *device)
837 {
838         return device->element.index;
839 }
840
841 /**
842  * connman_device_set_interface:
843  * @device: device structure
844  * @interface: interface name
845  *
846  * Set interface name of device
847  */
848 void connman_device_set_interface(struct connman_device *device,
849                                                         const char *interface)
850 {
851         g_free(device->element.devname);
852         device->element.devname = g_strdup(interface);
853
854         g_free(device->interface);
855         device->interface = g_strdup(interface);
856 }
857
858 /**
859  * connman_device_get_interface:
860  * @device: device structure
861  *
862  * Get interface name of device
863  */
864 const char *connman_device_get_interface(struct connman_device *device)
865 {
866         return device->interface;
867 }
868
869 /**
870  * connman_device_set_policy:
871  * @device: device structure
872  * @policy: power and connection policy
873  *
874  * Change power and connection policy of device
875  */
876 void connman_device_set_policy(struct connman_device *device,
877                                         enum connman_device_policy policy)
878 {
879         device->policy = policy;
880 }
881
882 /**
883  * connman_device_set_mode:
884  * @device: device structure
885  * @mode: network mode
886  *
887  * Change network mode of device
888  */
889 void connman_device_set_mode(struct connman_device *device,
890                                                 enum connman_device_mode mode)
891 {
892         device->mode = mode;
893 }
894
895 /**
896  * connman_device_set_powered:
897  * @device: device structure
898  * @powered: powered state
899  *
900  * Change power state of device
901  */
902 int connman_device_set_powered(struct connman_device *device,
903                                                 connman_bool_t powered)
904 {
905         DBusMessage *signal;
906         DBusMessageIter entry, value;
907         const char *key = "Powered";
908
909         DBG("driver %p powered %d", device, powered);
910
911         if (device->powered == powered)
912                 return -EALREADY;
913
914         device->powered = powered;
915
916         signal = dbus_message_new_signal(device->element.path,
917                                 CONNMAN_DEVICE_INTERFACE, "PropertyChanged");
918         if (signal == NULL)
919                 return 0;
920
921         dbus_message_iter_init_append(signal, &entry);
922
923         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
924
925         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
926                                         DBUS_TYPE_BOOLEAN_AS_STRING, &value);
927         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &powered);
928         dbus_message_iter_close_container(&entry, &value);
929
930         g_dbus_send_message(connection, signal);
931
932         if (powered == FALSE)
933                 return 0;
934
935         if (device->policy != CONNMAN_DEVICE_POLICY_AUTO)
936                 return 0;
937
938         if (device->driver->scan)
939                 device->driver->scan(device);
940
941         return 0;
942 }
943
944 /**
945  * connman_device_set_carrier:
946  * @device: device structure
947  * @carrier: carrier state
948  *
949  * Change carrier state of device (only for device without scanning)
950  */
951 int connman_device_set_carrier(struct connman_device *device,
952                                                 connman_bool_t carrier)
953 {
954         DBG("driver %p carrier %d", device, carrier);
955
956         switch (device->mode) {
957         case CONNMAN_DEVICE_MODE_UNKNOWN:
958         case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
959         case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
960                 return -EINVAL;
961         case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
962                 break;
963         }
964
965         if (device->carrier == carrier)
966                 return -EALREADY;
967
968         device->carrier = carrier;
969
970         if (carrier == TRUE) {
971                 struct connman_element *element;
972
973                 element = connman_element_create(NULL);
974                 if (element != NULL) {
975                         element->type  = CONNMAN_ELEMENT_TYPE_DHCP;
976                         element->index = device->element.index;
977
978                         if (connman_element_register(element,
979                                                         &device->element) < 0)
980                                 connman_element_unref(element);
981                 }
982         } else
983                 connman_element_unregister_children(&device->element);
984
985         return 0;
986 }
987
988 static void connect_known_network(struct connman_device *device)
989 {
990         struct connman_network *network = NULL;
991         GHashTableIter iter;
992         gpointer key, value;
993         unsigned int count = 0;
994
995         DBG("device %p", device);
996
997         g_hash_table_iter_init(&iter, device->networks);
998
999         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1000                 connman_uint8_t old_priority, new_priority;
1001                 connman_uint8_t old_strength, new_strength;
1002
1003                 count++;
1004
1005                 if (connman_network_get_remember(value) == FALSE)
1006                         continue;
1007
1008                 if (network == NULL) {
1009                         network = value;
1010                         continue;
1011                 }
1012
1013                 old_priority = connman_network_get_uint8(network, "Priority");
1014                 new_priority = connman_network_get_uint8(value, "Priority");
1015
1016                 if (new_priority != old_priority) {
1017                         if (new_priority > old_priority)
1018                                 network = value;
1019                         continue;
1020                 }
1021
1022                 old_strength = connman_network_get_uint8(network, "Strength");
1023                 new_strength = connman_network_get_uint8(value, "Strength");
1024
1025                 if (new_strength > old_strength)
1026                         network = value;
1027         }
1028
1029         if (network != NULL) {
1030                 int err;
1031
1032                 err = connman_network_connect(network);
1033                 if (err == 0 || err == -EINPROGRESS)
1034                         return;
1035         }
1036
1037         if (count > 0)
1038                 return;
1039
1040         if (device->driver && device->driver->scan)
1041                 device->driver->scan(device);
1042 }
1043
1044 static void mark_network_unavailable(gpointer key, gpointer value,
1045                                                         gpointer user_data)
1046 {
1047         struct connman_network *network = value;
1048
1049         if (connman_network_get_remember(network) == TRUE)
1050                 return;
1051
1052         connman_network_set_available(network, FALSE);
1053 }
1054
1055 static gboolean remove_unavailable_network(gpointer key, gpointer value,
1056                                                         gpointer user_data)
1057 {
1058         struct connman_network *network = value;
1059
1060         if (connman_network_get_remember(network) == TRUE)
1061                 return FALSE;
1062
1063         if (connman_network_get_available(network) == TRUE)
1064                 return FALSE;
1065
1066         return TRUE;
1067 }
1068
1069 /**
1070  * connman_device_set_scanning:
1071  * @device: device structure
1072  * @scanning: scanning state
1073  *
1074  * Change scanning state of device
1075  */
1076 int connman_device_set_scanning(struct connman_device *device,
1077                                                 connman_bool_t scanning)
1078 {
1079         DBusMessage *signal;
1080         DBusMessageIter entry, value;
1081         const char *key = "Scanning";
1082
1083         DBG("driver %p scanning %d", device, scanning);
1084
1085         if (!device->driver || !device->driver->scan)
1086                 return -EINVAL;
1087
1088         if (device->scanning == scanning)
1089                 return -EALREADY;
1090
1091         device->scanning = scanning;
1092
1093         signal = dbus_message_new_signal(device->element.path,
1094                                 CONNMAN_DEVICE_INTERFACE, "PropertyChanged");
1095         if (signal == NULL)
1096                 return 0;
1097
1098         dbus_message_iter_init_append(signal, &entry);
1099
1100         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
1101
1102         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
1103                                         DBUS_TYPE_BOOLEAN_AS_STRING, &value);
1104         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &scanning);
1105         dbus_message_iter_close_container(&entry, &value);
1106
1107         g_dbus_send_message(connection, signal);
1108
1109         if (scanning == TRUE) {
1110                 g_hash_table_foreach(device->networks,
1111                                         mark_network_unavailable, NULL);
1112                 return 0;
1113         }
1114
1115         g_hash_table_foreach_remove(device->networks,
1116                                         remove_unavailable_network, NULL);
1117
1118         if (device->connections > 0)
1119                 return 0;
1120
1121         if (device->policy != CONNMAN_DEVICE_POLICY_AUTO)
1122                 return 0;
1123
1124         connect_known_network(device);
1125
1126         return 0;
1127 }
1128
1129 /**
1130  * connman_device_set_string:
1131  * @device: device structure
1132  * @key: unique identifier
1133  * @value: string value
1134  *
1135  * Set string value for specific key
1136  */
1137 int connman_device_set_string(struct connman_device *device,
1138                                         const char *key, const char *value)
1139 {
1140         DBG("device %p key %s value %s", device, key, value);
1141
1142         if (g_str_equal(key, "Name") == TRUE) {
1143                 g_free(device->name);
1144                 device->name = g_strdup(value);
1145         } else if (g_str_equal(key, "Node") == TRUE) {
1146                 g_free(device->node);
1147                 device->node = g_strdup(value);
1148         }
1149
1150         return 0;
1151 }
1152
1153 /**
1154  * connman_device_get_string:
1155  * @device: device structure
1156  * @key: unique identifier
1157  *
1158  * Get string value for specific key
1159  */
1160 const char *connman_device_get_string(struct connman_device *device,
1161                                                         const char *key)
1162 {
1163         DBG("device %p key %s", device, key);
1164
1165         if (g_str_equal(key, "Name") == TRUE)
1166                 return device->name;
1167         else if (g_str_equal(key, "Node") == TRUE)
1168                 return device->node;
1169
1170         return NULL;
1171 }
1172
1173 static void set_offlinemode(struct connman_element *element, gpointer user_data)
1174 {
1175         struct connman_device *device = element->device;
1176         connman_bool_t offlinemode = GPOINTER_TO_UINT(user_data);
1177         connman_bool_t powered;
1178
1179         DBG("element %p name %s", element, element->name);
1180
1181         if (device == NULL)
1182                 return;
1183
1184         powered = (offlinemode == TRUE) ? FALSE : TRUE;
1185
1186         if (device->powered == powered)
1187                 return;
1188
1189         set_powered(device, powered);
1190 }
1191
1192 int __connman_device_set_offlinemode(connman_bool_t offlinemode)
1193 {
1194         DBG("offlinmode %d", offlinemode);
1195
1196         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
1197                         set_offlinemode, GUINT_TO_POINTER(offlinemode));
1198
1199         return 0;
1200 }
1201
1202 void __connman_device_increase_connections(struct connman_device *device)
1203 {
1204         device->connections++;
1205 }
1206
1207 void __connman_device_decrease_connections(struct connman_device *device)
1208 {
1209         device->connections--;
1210 }
1211
1212 /**
1213  * connman_device_add_network:
1214  * @device: device structure
1215  * @network: network structure
1216  *
1217  * Add new network to the device
1218  */
1219 int connman_device_add_network(struct connman_device *device,
1220                                         struct connman_network *network)
1221 {
1222         const char *identifier = connman_network_get_identifier(network);
1223         int err;
1224
1225         DBG("device %p network %p", device, network);
1226
1227         switch (device->mode) {
1228         case CONNMAN_DEVICE_MODE_UNKNOWN:
1229         case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
1230                 return -EINVAL;
1231         case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
1232         case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
1233                 break;
1234         }
1235
1236         __connman_network_set_device(network, device);
1237
1238         __connman_storage_load_network(network);
1239
1240         err = connman_element_register((struct connman_element *) network,
1241                                                         &device->element);
1242         if (err < 0) {
1243                 __connman_network_set_device(network, NULL);
1244                 return err;
1245         }
1246
1247         g_hash_table_insert(device->networks, g_strdup(identifier),
1248                                                                 network);
1249
1250         return 0;
1251 }
1252
1253 /**
1254  * connman_device_get_network:
1255  * @device: device structure
1256  * @identifier: network identifier
1257  *
1258  * Get network for given identifier
1259  */
1260 struct connman_network *connman_device_get_network(struct connman_device *device,
1261                                                         const char *identifier)
1262 {
1263         DBG("device %p identifier %s", device, identifier);
1264
1265         return g_hash_table_lookup(device->networks, identifier);
1266 }
1267
1268 /**
1269  * connman_device_remove_network:
1270  * @device: device structure
1271  * @identifier: network identifier
1272  *
1273  * Remove network for given identifier
1274  */
1275 int connman_device_remove_network(struct connman_device *device,
1276                                                         const char *identifier)
1277 {
1278         DBG("device %p identifier %s", device, identifier);
1279
1280         g_hash_table_remove(device->networks, identifier);
1281
1282         return 0;
1283 }
1284
1285 /**
1286  * connman_device_register:
1287  * @device: device structure
1288  *
1289  * Register device with the system
1290  */
1291 int connman_device_register(struct connman_device *device)
1292 {
1293         __connman_storage_load_device(device);
1294
1295         switch (device->mode) {
1296         case CONNMAN_DEVICE_MODE_UNKNOWN:
1297         case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
1298                 break;
1299         case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
1300         case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
1301                 __connman_storage_init_network(device);
1302                 break;
1303         }
1304
1305         return connman_element_register(&device->element, NULL);
1306 }
1307
1308 /**
1309  * connman_device_unregister:
1310  * @device: device structure
1311  *
1312  * Unregister device with the system
1313  */
1314 void connman_device_unregister(struct connman_device *device)
1315 {
1316         __connman_storage_save_device(device);
1317
1318         connman_element_unregister(&device->element);
1319 }
1320
1321 /**
1322  * connman_device_get_data:
1323  * @device: device structure
1324  *
1325  * Get private device data pointer
1326  */
1327 void *connman_device_get_data(struct connman_device *device)
1328 {
1329         return device->driver_data;
1330 }
1331
1332 /**
1333  * connman_device_set_data:
1334  * @device: device structure
1335  * @data: data pointer
1336  *
1337  * Set private device data pointer
1338  */
1339 void connman_device_set_data(struct connman_device *device, void *data)
1340 {
1341         device->driver_data = data;
1342 }
1343
1344 static gboolean match_driver(struct connman_device *device,
1345                                         struct connman_device_driver *driver)
1346 {
1347         if (device->type == driver->type ||
1348                         driver->type == CONNMAN_DEVICE_TYPE_UNKNOWN)
1349                 return TRUE;
1350
1351         return FALSE;
1352 }
1353
1354 static int device_probe(struct connman_element *element)
1355 {
1356         struct connman_device *device = element->device;
1357         GSList *list;
1358
1359         DBG("element %p name %s", element, element->name);
1360
1361         if (device == NULL)
1362                 return -ENODEV;
1363
1364         if (device->driver != NULL)
1365                 return -EALREADY;
1366
1367         for (list = driver_list; list; list = list->next) {
1368                 struct connman_device_driver *driver = list->data;
1369
1370                 if (match_driver(device, driver) == FALSE)
1371                         continue;
1372
1373                 DBG("driver %p name %s", driver, driver->name);
1374
1375                 if (driver->probe(device) == 0) {
1376                         device->driver = driver;
1377                         break;
1378                 }
1379         }
1380
1381         if (device->driver == NULL)
1382                 return -ENODEV;
1383
1384         return setup_device(device);
1385 }
1386
1387 static void device_remove(struct connman_element *element)
1388 {
1389         struct connman_device *device = element->device;
1390
1391         DBG("element %p name %s", element, element->name);
1392
1393         if (device == NULL)
1394                 return;
1395
1396         if (device->driver == NULL)
1397                 return;
1398
1399         remove_device(device);
1400 }
1401
1402 static struct connman_driver device_driver = {
1403         .name           = "device",
1404         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
1405         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
1406         .probe          = device_probe,
1407         .remove         = device_remove,
1408 };
1409
1410 static int device_load(struct connman_device *device)
1411 {
1412         GKeyFile *keyfile;
1413         gchar *pathname, *data = NULL;
1414         gsize length;
1415         char *str;
1416         int val;
1417
1418         DBG("device %p", device);
1419
1420         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR,
1421                                                         device->element.name);
1422         if (pathname == NULL)
1423                 return -ENOMEM;
1424
1425         keyfile = g_key_file_new();
1426
1427         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
1428                 g_free(pathname);
1429                 return -ENOENT;
1430         }
1431
1432         g_free(pathname);
1433
1434         if (g_key_file_load_from_data(keyfile, data, length,
1435                                                         0, NULL) == FALSE) {
1436                 g_free(data);
1437                 return -EILSEQ;
1438         }
1439
1440         g_free(data);
1441
1442         str = g_key_file_get_string(keyfile, "Configuration", "Policy", NULL);
1443         if (str != NULL) {
1444                 device->policy = string2policy(str);
1445                 g_free(str);
1446         }
1447
1448         val = g_key_file_get_integer(keyfile, "Configuration",
1449                                                         "Priority", NULL);
1450         if (val > 0)
1451                 device->priority = val;
1452
1453         g_key_file_free(keyfile);
1454
1455         return 0;
1456 }
1457
1458 static int device_save(struct connman_device *device)
1459 {
1460         GKeyFile *keyfile;
1461         gchar *pathname, *data = NULL;
1462         gsize length;
1463         const char *str;
1464
1465         DBG("device %p", device);
1466
1467         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR,
1468                                                         device->element.name);
1469         if (pathname == NULL)
1470                 return -ENOMEM;
1471
1472         keyfile = g_key_file_new();
1473
1474         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
1475                 goto update;
1476
1477         if (length > 0) {
1478                 if (g_key_file_load_from_data(keyfile, data, length,
1479                                                         0, NULL) == FALSE)
1480                         goto done;
1481         }
1482
1483         g_free(data);
1484
1485 update:
1486         str = policy2string(device->policy);
1487         if (str != NULL)
1488                 g_key_file_set_string(keyfile, "Configuration", "Policy", str);
1489
1490         if (device->priority > 0)
1491                 g_key_file_set_integer(keyfile, "Configuration",
1492                                                 "Priority", device->priority);
1493
1494         data = g_key_file_to_data(keyfile, &length, NULL);
1495
1496         g_file_set_contents(pathname, data, length, NULL);
1497
1498 done:
1499         g_free(data);
1500
1501         g_key_file_free(keyfile);
1502
1503         g_free(pathname);
1504
1505         return 0;
1506 }
1507
1508 static struct connman_storage device_storage = {
1509         .name           = "device",
1510         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1511         .device_load    = device_load,
1512         .device_save    = device_save,
1513 };
1514
1515 int __connman_device_init(void)
1516 {
1517         DBG("");
1518
1519         connection = connman_dbus_get_connection();
1520
1521         if (connman_storage_register(&device_storage) < 0)
1522                 connman_error("Failed to register device storage");
1523
1524         return connman_driver_register(&device_driver);
1525 }
1526
1527 void __connman_device_cleanup(void)
1528 {
1529         DBG("");
1530
1531         connman_driver_unregister(&device_driver);
1532
1533         connman_storage_unregister(&device_storage);
1534
1535         dbus_connection_unref(connection);
1536 }