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