device: Check for a device driver pointer before de-referencing it
[framework/connectivity/connman.git] / src / device.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 "connman.h"
30
31 struct connman_device {
32         struct connman_element element;
33         enum connman_device_type type;
34         connman_bool_t offlinemode;
35         connman_bool_t blocked;
36         connman_bool_t powered;
37         connman_bool_t powered_pending;
38         connman_bool_t powered_persistent;
39         connman_bool_t scanning;
40         connman_bool_t disconnected;
41         connman_bool_t reconnect;
42         connman_uint16_t scan_interval;
43         char *name;
44         char *node;
45         char *address;
46         char *interface;
47         char *ident;
48         int phyindex;
49         unsigned int connections;
50         guint scan_timeout;
51
52         struct connman_device_driver *driver;
53         void *driver_data;
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 void clear_scan_trigger(struct connman_device *device)
78 {
79         if (device->scan_timeout > 0) {
80                 g_source_remove(device->scan_timeout);
81                 device->scan_timeout = 0;
82         }
83 }
84
85 static void reset_scan_trigger(struct connman_device *device)
86 {
87         clear_scan_trigger(device);
88
89         if (device->scan_interval > 0) {
90                 guint interval = device->scan_interval;
91                 device->scan_timeout = g_timeout_add_seconds(interval,
92                                         device_scan_trigger, device);
93         }
94 }
95
96 static void force_scan_trigger(struct connman_device *device)
97 {
98         clear_scan_trigger(device);
99
100         device->scan_timeout = g_timeout_add_seconds(5,
101                                         device_scan_trigger, device);
102 }
103
104 void connman_device_schedule_scan(struct connman_device *device)
105 {
106         reset_scan_trigger(device);
107 }
108
109 static const char *type2description(enum connman_device_type type)
110 {
111         switch (type) {
112         case CONNMAN_DEVICE_TYPE_UNKNOWN:
113         case CONNMAN_DEVICE_TYPE_VENDOR:
114                 break;
115         case CONNMAN_DEVICE_TYPE_ETHERNET:
116                 return "Ethernet";
117         case CONNMAN_DEVICE_TYPE_WIFI:
118                 return "Wireless";
119         case CONNMAN_DEVICE_TYPE_WIMAX:
120                 return "WiMAX";
121         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
122                 return "Bluetooth";
123         case CONNMAN_DEVICE_TYPE_GPS:
124                 return "GPS";
125         case CONNMAN_DEVICE_TYPE_CELLULAR:
126                 return "Cellular";
127         }
128
129         return NULL;
130 }
131
132 static const char *type2string(enum connman_device_type type)
133 {
134         switch (type) {
135         case CONNMAN_DEVICE_TYPE_UNKNOWN:
136         case CONNMAN_DEVICE_TYPE_VENDOR:
137                 break;
138         case CONNMAN_DEVICE_TYPE_ETHERNET:
139                 return "ethernet";
140         case CONNMAN_DEVICE_TYPE_WIFI:
141                 return "wifi";
142         case CONNMAN_DEVICE_TYPE_WIMAX:
143                 return "wimax";
144         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
145                 return "bluetooth";
146         case CONNMAN_DEVICE_TYPE_GPS:
147                 return "gps";
148         case CONNMAN_DEVICE_TYPE_CELLULAR:
149                 return "cellular";
150         }
151
152         return NULL;
153 }
154
155 enum connman_service_type __connman_device_get_service_type(struct connman_device *device)
156 {
157         enum connman_device_type type = connman_device_get_type(device);
158
159         switch (type) {
160         case CONNMAN_DEVICE_TYPE_UNKNOWN:
161         case CONNMAN_DEVICE_TYPE_VENDOR:
162         case CONNMAN_DEVICE_TYPE_GPS:
163                 break;
164         case CONNMAN_DEVICE_TYPE_ETHERNET:
165                 return CONNMAN_SERVICE_TYPE_ETHERNET;
166         case CONNMAN_DEVICE_TYPE_WIFI:
167                 return CONNMAN_SERVICE_TYPE_WIFI;
168         case CONNMAN_DEVICE_TYPE_WIMAX:
169                 return CONNMAN_SERVICE_TYPE_WIMAX;
170         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
171                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
172         case CONNMAN_DEVICE_TYPE_CELLULAR:
173                 return CONNMAN_SERVICE_TYPE_CELLULAR;
174         }
175
176         return CONNMAN_SERVICE_TYPE_UNKNOWN;
177 }
178
179 int __connman_device_enable(struct connman_device *device)
180 {
181         int err;
182
183         DBG("device %p %d", device, device->blocked);
184
185         if (!device->driver || !device->driver->enable)
186                 return -EOPNOTSUPP;
187
188         if (device->powered_pending == TRUE)
189                 return -EALREADY;
190
191         if (device->blocked == TRUE)
192                 return -ENOLINK;
193
194         err = device->driver->enable(device);
195         if (err < 0) {
196                 if (err == -EINPROGRESS) {
197                         device->powered_pending = TRUE;
198                         device->offlinemode = FALSE;
199                         if (__connman_profile_get_offlinemode() == TRUE)
200                                 __connman_profile_set_offlinemode(FALSE, FALSE);
201                 }
202                 return err;
203         }
204
205         device->powered_pending = TRUE;
206         device->powered = TRUE;
207         device->offlinemode = FALSE;
208         if (__connman_profile_get_offlinemode() == TRUE)
209                 __connman_profile_set_offlinemode(FALSE, FALSE);
210
211         __connman_technology_enable_device(device);
212
213         return 0;
214 }
215
216 int __connman_device_disable(struct connman_device *device)
217 {
218         int err;
219
220         DBG("device %p", device);
221
222         if (!device->driver || !device->driver->disable)
223                 return -EOPNOTSUPP;
224
225         if (device->powered == FALSE)
226                 return -ENOLINK;
227
228         if (device->powered_pending == FALSE)
229                 return -EALREADY;
230
231         device->reconnect = FALSE;
232
233         clear_scan_trigger(device);
234
235         g_hash_table_remove_all(device->networks);
236
237         err = device->driver->disable(device);
238         if (err < 0) {
239                 if (err == -EINPROGRESS)
240                         device->powered_pending = FALSE;
241                 return err;
242         }
243
244         device->powered_pending = FALSE;
245         device->powered = FALSE;
246
247         __connman_technology_disable_device(device);
248
249         return 0;
250 }
251
252 static int set_powered(struct connman_device *device, connman_bool_t powered)
253 {
254         DBG("device %p powered %d", device, powered);
255
256         if (powered == TRUE)
257                 return __connman_device_enable(device);
258         else
259                 return __connman_device_disable(device);
260 }
261
262 static int setup_device(struct connman_device *device)
263 {
264         DBG("device %p", device);
265
266         __connman_technology_add_device(device);
267
268         if (device->offlinemode == FALSE &&
269                                 device->powered_persistent == TRUE)
270                 __connman_device_enable(device);
271
272         return 0;
273 }
274
275 static void probe_driver(struct connman_element *element, gpointer user_data)
276 {
277         struct connman_device_driver *driver = user_data;
278
279         DBG("element %p name %s", element, element->name);
280
281         if (element->device == NULL)
282                 return;
283
284         if (element->device->driver != NULL)
285                 return;
286
287         if (driver->type != element->device->type)
288                 return;
289
290         if (driver->probe(element->device) < 0)
291                 return;
292
293         element->device->driver = driver;
294
295         __connman_element_set_driver(element);
296
297         setup_device(element->device);
298 }
299
300 static void remove_device(struct connman_device *device)
301 {
302         DBG("device %p", device);
303
304         __connman_device_disable(device);
305
306         __connman_technology_remove_device(device);
307
308         if (device->driver->remove)
309                 device->driver->remove(device);
310
311         device->driver = NULL;
312 }
313
314 static void remove_driver(struct connman_element *element, gpointer user_data)
315 {
316         struct connman_device_driver *driver = user_data;
317
318         DBG("element %p name %s", element, element->name);
319
320         if (element->device == NULL)
321                 return;
322
323         if (element->device->driver == driver)
324                 remove_device(element->device);
325 }
326
327 connman_bool_t __connman_device_has_driver(struct connman_device *device)
328 {
329         if (device == NULL || device->driver == NULL)
330                 return FALSE;
331
332         return TRUE;
333 }
334
335 static GSList *driver_list = NULL;
336
337 static gint compare_priority(gconstpointer a, gconstpointer b)
338 {
339         const struct connman_device_driver *driver1 = a;
340         const struct connman_device_driver *driver2 = b;
341
342         return driver2->priority - driver1->priority;
343 }
344
345 /**
346  * connman_device_driver_register:
347  * @driver: device driver definition
348  *
349  * Register a new device driver
350  *
351  * Returns: %0 on success
352  */
353 int connman_device_driver_register(struct connman_device_driver *driver)
354 {
355         DBG("driver %p name %s", driver, driver->name);
356
357         driver_list = g_slist_insert_sorted(driver_list, driver,
358                                                         compare_priority);
359
360         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
361                                                 probe_driver, driver);
362
363         return 0;
364 }
365
366 /**
367  * connman_device_driver_unregister:
368  * @driver: device driver definition
369  *
370  * Remove a previously registered device driver
371  */
372 void connman_device_driver_unregister(struct connman_device_driver *driver)
373 {
374         DBG("driver %p name %s", driver, driver->name);
375
376         driver_list = g_slist_remove(driver_list, driver);
377
378         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
379                                                 remove_driver, driver);
380 }
381
382 static void unregister_network(gpointer data)
383 {
384         struct connman_network *network = data;
385
386         DBG("network %p", network);
387
388         connman_element_unregister((struct connman_element *) network);
389
390         __connman_network_set_device(network, NULL);
391
392         connman_network_unref(network);
393 }
394
395 static void device_destruct(struct connman_element *element)
396 {
397         struct connman_device *device = element->device;
398
399         DBG("element %p name %s", element, element->name);
400
401         clear_scan_trigger(device);
402
403         g_free(device->ident);
404         g_free(device->node);
405         g_free(device->name);
406         g_free(device->address);
407         g_free(device->interface);
408
409         g_free(device->last_network);
410
411         g_hash_table_destroy(device->networks);
412         device->networks = NULL;
413 }
414
415 /**
416  * connman_device_create:
417  * @node: device node name (for example an address)
418  * @type: device type
419  *
420  * Allocate a new device of given #type and assign the #node name to it.
421  *
422  * Returns: a newly-allocated #connman_device structure
423  */
424 struct connman_device *connman_device_create(const char *node,
425                                                 enum connman_device_type type)
426 {
427         struct connman_device *device;
428         const char *str;
429         enum connman_service_type service_type;
430
431         DBG("node %s type %d", node, type);
432
433         device = g_try_new0(struct connman_device, 1);
434         if (device == NULL)
435                 return NULL;
436
437         DBG("device %p", device);
438
439         __connman_element_initialize(&device->element);
440
441         device->element.name = g_strdup(node);
442         device->element.type = CONNMAN_ELEMENT_TYPE_DEVICE;
443
444         device->element.device = device;
445         device->element.destruct = device_destruct;
446
447         str = type2string(type);
448         if (str != NULL)
449                 connman_element_set_string(&device->element,
450                                         CONNMAN_PROPERTY_ID_TYPE, str);
451
452         device->element.ipv4.method = CONNMAN_IPCONFIG_METHOD_DHCP;
453
454         device->type = type;
455         device->name = g_strdup(type2description(device->type));
456
457         device->powered_persistent = TRUE;
458
459         device->phyindex = -1;
460
461         service_type = __connman_device_get_service_type(device);
462         device->blocked = __connman_technology_get_blocked(service_type);
463
464         switch (type) {
465         case CONNMAN_DEVICE_TYPE_UNKNOWN:
466         case CONNMAN_DEVICE_TYPE_VENDOR:
467                 device->scan_interval = 0;
468                 break;
469         case CONNMAN_DEVICE_TYPE_ETHERNET:
470         case CONNMAN_DEVICE_TYPE_WIFI:
471                 device->scan_interval = 300;
472                 break;
473         case CONNMAN_DEVICE_TYPE_WIMAX:
474                 device->scan_interval = 0;
475                 break;
476         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
477                 device->scan_interval = 0;
478                 break;
479         case CONNMAN_DEVICE_TYPE_GPS:
480                 device->scan_interval = 0;
481                 break;
482         case CONNMAN_DEVICE_TYPE_CELLULAR:
483                 device->scan_interval = 0;
484                 break;
485         }
486
487         device->networks = g_hash_table_new_full(g_str_hash, g_str_equal,
488                                                 g_free, unregister_network);
489
490         return device;
491 }
492
493 /**
494  * connman_device_ref:
495  * @device: device structure
496  *
497  * Increase reference counter of device
498  */
499 struct connman_device *connman_device_ref(struct connman_device *device)
500 {
501         if (connman_element_ref(&device->element) == NULL)
502                 return NULL;
503
504         return device;
505 }
506
507 /**
508  * connman_device_unref:
509  * @device: device structure
510  *
511  * Decrease reference counter of device
512  */
513 void connman_device_unref(struct connman_device *device)
514 {
515         connman_element_unref(&device->element);
516 }
517
518 const char *__connman_device_get_type(struct connman_device *device)
519 {
520         return type2string(device->type);
521 }
522
523 /**
524  * connman_device_get_type:
525  * @device: device structure
526  *
527  * Get type of device
528  */
529 enum connman_device_type connman_device_get_type(struct connman_device *device)
530 {
531         return device->type;
532 }
533
534 /**
535  * connman_device_set_index:
536  * @device: device structure
537  * @index: index number
538  *
539  * Set index number of device
540  */
541 void connman_device_set_index(struct connman_device *device, int index)
542 {
543         device->element.index = index;
544 }
545
546 /**
547  * connman_device_get_index:
548  * @device: device structure
549  *
550  * Get index number of device
551  */
552 int connman_device_get_index(struct connman_device *device)
553 {
554         return device->element.index;
555 }
556
557 int __connman_device_get_phyindex(struct connman_device *device)
558 {
559         return device->phyindex;
560 }
561
562 void __connman_device_set_phyindex(struct connman_device *device,
563                                                         int phyindex)
564 {
565         device->phyindex = phyindex;
566 }
567
568 /**
569  * connman_device_set_interface:
570  * @device: device structure
571  * @interface: interface name
572  *
573  * Set interface name of device
574  */
575 void connman_device_set_interface(struct connman_device *device,
576                                                 const char *interface)
577 {
578         g_free(device->element.devname);
579         device->element.devname = g_strdup(interface);
580
581         g_free(device->interface);
582         device->interface = g_strdup(interface);
583
584         if (device->name == NULL) {
585                 const char *str = type2description(device->type);
586                 if (str != NULL && device->interface != NULL)
587                         device->name = g_strdup_printf("%s (%s)", str,
588                                                         device->interface);
589         }
590 }
591
592 /**
593  * connman_device_set_ident:
594  * @device: device structure
595  * @ident: unique identifier
596  *
597  * Set unique identifier of device
598  */
599 void connman_device_set_ident(struct connman_device *device,
600                                                         const char *ident)
601 {
602         g_free(device->ident);
603         device->ident = g_strdup(ident);
604 }
605
606 const char *connman_device_get_ident(struct connman_device *device)
607 {
608         return device->ident;
609 }
610
611 /**
612  * connman_device_set_powered:
613  * @device: device structure
614  * @powered: powered state
615  *
616  * Change power state of device
617  */
618 int connman_device_set_powered(struct connman_device *device,
619                                                 connman_bool_t powered)
620 {
621         DBG("driver %p powered %d", device, powered);
622
623         if (device->powered == powered) {
624                 device->powered_pending = powered;
625                 return -EALREADY;
626         }
627
628         if (powered == TRUE)
629                 __connman_device_enable(device);
630         else
631                 __connman_device_disable(device);
632
633         device->powered = powered;
634         device->powered_pending = powered;
635
636         if (device->powered == TRUE)
637                 __connman_technology_enable_device(device);
638         else
639                 __connman_technology_disable_device(device);
640
641         if (device->offlinemode == TRUE && powered == TRUE)
642                 return connman_device_set_powered(device, FALSE);
643
644         if (powered == FALSE)
645                 return 0;
646
647         reset_scan_trigger(device);
648
649         if (device->driver && device->driver->scan)
650                 device->driver->scan(device);
651
652         return 0;
653 }
654
655 int __connman_device_set_blocked(struct connman_device *device,
656                                                 connman_bool_t blocked)
657 {
658         connman_bool_t powered;
659
660         DBG("device %p blocked %d", device, blocked);
661
662         device->blocked = blocked;
663
664         if (device->offlinemode == TRUE)
665                 return 0;
666
667         connman_info("%s {rfkill} blocked %d", device->interface, blocked);
668
669         if (blocked == FALSE)
670                 powered = device->powered_persistent;
671         else
672                 powered = FALSE;
673
674         return set_powered(device, powered);
675 }
676
677 connman_bool_t __connman_device_get_blocked(struct connman_device *device)
678 {
679         return device->blocked;
680 }
681
682 int __connman_device_scan(struct connman_device *device)
683 {
684         if (!device->driver || !device->driver->scan)
685                 return -EOPNOTSUPP;
686
687         if (device->powered == FALSE)
688                 return -ENOLINK;
689
690         reset_scan_trigger(device);
691
692         return device->driver->scan(device);
693 }
694
695 int __connman_device_enable_persistent(struct connman_device *device)
696 {
697         int err;
698
699         DBG("device %p", device);
700
701         device->powered_persistent = TRUE;
702
703         __connman_storage_save_device(device);
704
705         err = __connman_device_enable(device);
706         if (err == 0 || err == -EINPROGRESS) {
707                 device->offlinemode = FALSE;
708                 if (__connman_profile_get_offlinemode() == TRUE) {
709                         __connman_profile_set_offlinemode(FALSE, FALSE);
710
711                         __connman_profile_save_default();
712                 }
713         }
714
715         return err;
716 }
717
718 int __connman_device_disable_persistent(struct connman_device *device)
719 {
720         DBG("device %p", device);
721
722         device->powered_persistent = FALSE;
723
724         __connman_storage_save_device(device);
725
726         return __connman_device_disable(device);
727 }
728
729 int __connman_device_disconnect(struct connman_device *device)
730 {
731         GHashTableIter iter;
732         gpointer key, value;
733
734         DBG("device %p", device);
735
736         connman_device_set_disconnected(device, TRUE);
737
738         g_hash_table_iter_init(&iter, device->networks);
739
740         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
741                 struct connman_network *network = value;
742
743                 if (__connman_network_get_connecting(network) == TRUE) {
744                         /*
745                          * Skip network in the process of connecting.
746                          * This is a workaround for WiFi networks serviced
747                          * by the supplicant plugin that hold a reference
748                          * to the network.  If we disconnect the network
749                          * here then the referenced object will not be
750                          * registered and usage (like launching DHCP client)
751                          * will fail.  There is nothing to be gained by
752                          * removing the network here anyway.
753                          */
754                         connman_warn("Skipping disconnect of %s",
755                                 connman_network_get_identifier(network));
756                         continue;
757                 }
758
759                 __connman_network_disconnect(network);
760         }
761
762         return 0;
763 }
764
765 static void mark_network_unavailable(gpointer key, gpointer value,
766                                                         gpointer user_data)
767 {
768         struct connman_network *network = value;
769
770         if (connman_network_get_connected(network) == TRUE)
771                 return;
772
773         connman_network_set_available(network, FALSE);
774 }
775
776 static gboolean remove_unavailable_network(gpointer key, gpointer value,
777                                                         gpointer user_data)
778 {
779         struct connman_network *network = value;
780
781         if (connman_network_get_connected(network) == TRUE)
782                 return FALSE;
783
784         if (connman_network_get_available(network) == TRUE)
785                 return FALSE;
786
787         return TRUE;
788 }
789
790 void __connman_device_cleanup_networks(struct connman_device *device)
791 {
792         g_hash_table_foreach_remove(device->networks,
793                                         remove_unavailable_network, NULL);
794 }
795
796 /**
797  * connman_device_set_scanning:
798  * @device: device structure
799  * @scanning: scanning state
800  *
801  * Change scanning state of device
802  */
803 int connman_device_set_scanning(struct connman_device *device,
804                                                 connman_bool_t scanning)
805 {
806         DBG("device %p scanning %d", device, scanning);
807
808         if (!device->driver || !device->driver->scan)
809                 return -EINVAL;
810
811         if (device->scanning == scanning)
812                 return -EALREADY;
813
814         device->scanning = scanning;
815
816         if (scanning == TRUE) {
817                 reset_scan_trigger(device);
818
819                 g_hash_table_foreach(device->networks,
820                                         mark_network_unavailable, NULL);
821
822                 return 0;
823         }
824
825         __connman_device_cleanup_networks(device);
826
827         if (device->connections > 0)
828                 return 0;
829
830         if (device->disconnected == TRUE)
831                 return 0;
832
833         __connman_service_auto_connect();
834
835         return 0;
836 }
837
838 /**
839  * connman_device_set_disconnected:
840  * @device: device structure
841  * @disconnected: disconnected state
842  *
843  * Change disconnected state of device (only for device with networks)
844  */
845 int connman_device_set_disconnected(struct connman_device *device,
846                                                 connman_bool_t disconnected)
847 {
848         DBG("device %p disconnected %d", device, disconnected);
849
850         if (device->disconnected == disconnected)
851                 return -EALREADY;
852
853         device->disconnected = disconnected;
854
855         if (disconnected == TRUE)
856                 force_scan_trigger(device);
857
858         return 0;
859 }
860
861 /**
862  * connman_device_get_disconnected:
863  * @device: device structure
864  *
865  * Get device disconnected state
866  */
867 connman_bool_t connman_device_get_disconnected(struct connman_device *device)
868 {
869         return device->disconnected;
870 }
871
872 /**
873  * connman_device_set_string:
874  * @device: device structure
875  * @key: unique identifier
876  * @value: string value
877  *
878  * Set string value for specific key
879  */
880 int connman_device_set_string(struct connman_device *device,
881                                         const char *key, const char *value)
882 {
883         DBG("device %p key %s value %s", device, key, value);
884
885         if (g_str_equal(key, "Address") == TRUE) {
886                 g_free(device->address);
887                 device->address = g_strdup(value);
888         } else if (g_str_equal(key, "Name") == TRUE) {
889                 g_free(device->name);
890                 device->name = g_strdup(value);
891         } else if (g_str_equal(key, "Node") == TRUE) {
892                 g_free(device->node);
893                 device->node = g_strdup(value);
894         }
895
896         return connman_element_set_string(&device->element, key, value);
897 }
898
899 /**
900  * connman_device_get_string:
901  * @device: device structure
902  * @key: unique identifier
903  *
904  * Get string value for specific key
905  */
906 const char *connman_device_get_string(struct connman_device *device,
907                                                         const char *key)
908 {
909         DBG("device %p key %s", device, key);
910
911         if (g_str_equal(key, "Address") == TRUE)
912                 return device->address;
913         else if (g_str_equal(key, "Name") == TRUE)
914                 return device->name;
915         else if (g_str_equal(key, "Node") == TRUE)
916                 return device->node;
917         else if (g_str_equal(key, "Interface") == TRUE)
918                 return device->interface;
919
920         return connman_element_get_string(&device->element, key);
921 }
922
923 static void set_offlinemode(struct connman_element *element, gpointer user_data)
924 {
925         struct connman_device *device = element->device;
926         connman_bool_t offlinemode = GPOINTER_TO_UINT(user_data);
927         connman_bool_t powered;
928
929         DBG("element %p name %s", element, element->name);
930
931         if (device == NULL)
932                 return;
933
934         device->offlinemode = offlinemode;
935
936         if (device->blocked == TRUE)
937                 return;
938
939         powered = (offlinemode == TRUE) ? FALSE : TRUE;
940
941         if (device->powered == powered)
942                 return;
943
944         if (device->powered_persistent == FALSE)
945                 powered = FALSE;
946
947         set_powered(device, powered);
948 }
949
950 int __connman_device_set_offlinemode(connman_bool_t offlinemode)
951 {
952         DBG("offlinmode %d", offlinemode);
953
954         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
955                         set_offlinemode, GUINT_TO_POINTER(offlinemode));
956
957         __connman_notifier_offlinemode(offlinemode);
958
959         return 0;
960 }
961
962 void __connman_device_increase_connections(struct connman_device *device)
963 {
964         if (device == NULL)
965                 return;
966
967         device->connections++;
968 }
969
970 void __connman_device_decrease_connections(struct connman_device *device)
971 {
972         if (device == NULL)
973                 return;
974
975         device->connections--;
976 }
977
978 /**
979  * connman_device_add_network:
980  * @device: device structure
981  * @network: network structure
982  *
983  * Add new network to the device
984  */
985 int connman_device_add_network(struct connman_device *device,
986                                         struct connman_network *network)
987 {
988         const char *identifier = connman_network_get_identifier(network);
989         int err;
990
991         DBG("device %p network %p", device, network);
992
993         if (identifier == NULL)
994                 return -EINVAL;
995
996         __connman_network_set_device(network, device);
997
998         err = connman_element_register((struct connman_element *) network,
999                                                         &device->element);
1000         if (err < 0) {
1001                 __connman_network_set_device(network, NULL);
1002                 return err;
1003         }
1004
1005         g_hash_table_insert(device->networks, g_strdup(identifier),
1006                                                                 network);
1007
1008         return 0;
1009 }
1010
1011 /**
1012  * connman_device_get_network:
1013  * @device: device structure
1014  * @identifier: network identifier
1015  *
1016  * Get network for given identifier
1017  */
1018 struct connman_network *connman_device_get_network(struct connman_device *device,
1019                                                         const char *identifier)
1020 {
1021         DBG("device %p identifier %s", device, identifier);
1022
1023         return g_hash_table_lookup(device->networks, identifier);
1024 }
1025
1026 /**
1027  * connman_device_remove_network:
1028  * @device: device structure
1029  * @identifier: network identifier
1030  *
1031  * Remove network for given identifier
1032  */
1033 int connman_device_remove_network(struct connman_device *device,
1034                                                         const char *identifier)
1035 {
1036         DBG("device %p identifier %s", device, identifier);
1037
1038         g_hash_table_remove(device->networks, identifier);
1039
1040         return 0;
1041 }
1042
1043 void connman_device_remove_all_networks(struct connman_device *device)
1044 {
1045         g_hash_table_remove_all(device->networks);
1046 }
1047
1048 void __connman_device_set_network(struct connman_device *device,
1049                                         struct connman_network *network)
1050 {
1051         const char *name;
1052
1053         if (device == NULL)
1054                 return;
1055
1056         if (device->network == network)
1057                 return;
1058
1059         if (device->network != NULL)
1060                 connman_network_unref(device->network);
1061
1062         if (network != NULL) {
1063                 name = connman_network_get_string(network,
1064                                                 CONNMAN_PROPERTY_ID_NAME);
1065                 g_free(device->last_network);
1066                 device->last_network = g_strdup(name);
1067
1068                 device->network = connman_network_ref(network);
1069         } else {
1070                 g_free(device->last_network);
1071                 device->last_network = NULL;
1072
1073                 device->network = NULL;
1074         }
1075 }
1076
1077 void __connman_device_set_reconnect(struct connman_device *device,
1078                                                 connman_bool_t reconnect)
1079 {
1080         device->reconnect = reconnect;
1081 }
1082
1083 connman_bool_t  __connman_device_get_reconnect(
1084                                 struct connman_device *device)
1085 {
1086         return device->reconnect;
1087 }
1088
1089 /**
1090  * connman_device_register:
1091  * @device: device structure
1092  *
1093  * Register device with the system
1094  */
1095 int connman_device_register(struct connman_device *device)
1096 {
1097         __connman_storage_load_device(device);
1098
1099         device->offlinemode = __connman_profile_get_offlinemode();
1100
1101         return connman_element_register(&device->element, NULL);
1102 }
1103
1104 /**
1105  * connman_device_unregister:
1106  * @device: device structure
1107  *
1108  * Unregister device with the system
1109  */
1110 void connman_device_unregister(struct connman_device *device)
1111 {
1112         __connman_storage_save_device(device);
1113
1114         connman_element_unregister(&device->element);
1115 }
1116
1117 /**
1118  * connman_device_get_data:
1119  * @device: device structure
1120  *
1121  * Get private device data pointer
1122  */
1123 void *connman_device_get_data(struct connman_device *device)
1124 {
1125         return device->driver_data;
1126 }
1127
1128 /**
1129  * connman_device_set_data:
1130  * @device: device structure
1131  * @data: data pointer
1132  *
1133  * Set private device data pointer
1134  */
1135 void connman_device_set_data(struct connman_device *device, void *data)
1136 {
1137         device->driver_data = data;
1138 }
1139
1140 static gboolean match_driver(struct connman_device *device,
1141                                         struct connman_device_driver *driver)
1142 {
1143         if (device->type == driver->type ||
1144                         driver->type == CONNMAN_DEVICE_TYPE_UNKNOWN)
1145                 return TRUE;
1146
1147         return FALSE;
1148 }
1149
1150 static int device_probe(struct connman_element *element)
1151 {
1152         struct connman_device *device = element->device;
1153         GSList *list;
1154
1155         DBG("element %p name %s", element, element->name);
1156
1157         if (device == NULL)
1158                 return -ENODEV;
1159
1160         if (device->driver != NULL)
1161                 return -EALREADY;
1162
1163         for (list = driver_list; list; list = list->next) {
1164                 struct connman_device_driver *driver = list->data;
1165
1166                 if (match_driver(device, driver) == FALSE)
1167                         continue;
1168
1169                 DBG("driver %p name %s", driver, driver->name);
1170
1171                 if (driver->probe(device) == 0) {
1172                         device->driver = driver;
1173                         break;
1174                 }
1175         }
1176
1177         if (device->driver == NULL)
1178                 return -ENODEV;
1179
1180         return setup_device(device);
1181 }
1182
1183 static void device_remove(struct connman_element *element)
1184 {
1185         struct connman_device *device = element->device;
1186
1187         DBG("element %p name %s", element, element->name);
1188
1189         if (device == NULL)
1190                 return;
1191
1192         if (device->driver == NULL)
1193                 return;
1194
1195         remove_device(device);
1196 }
1197
1198 static struct connman_driver device_driver = {
1199         .name           = "device",
1200         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
1201         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
1202         .probe          = device_probe,
1203         .remove         = device_remove,
1204 };
1205
1206 static int device_load(struct connman_device *device)
1207 {
1208         const char *ident = __connman_profile_active_ident();
1209         GKeyFile *keyfile;
1210         GError *error = NULL;
1211         gchar *identifier;
1212         connman_bool_t powered;
1213         int val;
1214
1215         DBG("device %p", device);
1216
1217         keyfile = __connman_storage_open_profile(ident);
1218         if (keyfile == NULL)
1219                 return 0;
1220
1221         identifier = g_strdup_printf("device_%s", device->element.name);
1222         if (identifier == NULL)
1223                 goto done;
1224
1225         powered = g_key_file_get_boolean(keyfile, identifier,
1226                                                 "Powered", &error);
1227         if (error == NULL)
1228                 device->powered_persistent = powered;
1229         g_clear_error(&error);
1230
1231         val = g_key_file_get_integer(keyfile, identifier,
1232                                                 "ScanInterval", &error);
1233         if (error == NULL)
1234                 device->scan_interval = val;
1235         g_clear_error(&error);
1236
1237 done:
1238         g_free(identifier);
1239
1240         __connman_storage_close_profile(ident, keyfile, FALSE);
1241
1242         return 0;
1243 }
1244
1245 static int device_save(struct connman_device *device)
1246 {
1247         const char *ident = __connman_profile_active_ident();
1248         GKeyFile *keyfile;
1249         gchar *identifier;
1250
1251         DBG("device %p", device);
1252
1253         keyfile = __connman_storage_open_profile(ident);
1254         if (keyfile == NULL)
1255                 return 0;
1256
1257         identifier = g_strdup_printf("device_%s", device->element.name);
1258         if (identifier == NULL)
1259                 goto done;
1260
1261         g_key_file_set_boolean(keyfile, identifier,
1262                                         "Powered", device->powered_persistent);
1263
1264         g_key_file_set_integer(keyfile, identifier,
1265                                         "ScanInterval", device->scan_interval);
1266
1267 done:
1268         g_free(identifier);
1269
1270         __connman_storage_close_profile(ident, keyfile, TRUE);
1271
1272         return 0;
1273 }
1274
1275 static struct connman_storage device_storage = {
1276         .name           = "device",
1277         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1278         .device_load    = device_load,
1279         .device_save    = device_save,
1280 };
1281
1282 int __connman_device_init(void)
1283 {
1284         DBG("");
1285
1286         if (connman_storage_register(&device_storage) < 0)
1287                 connman_error("Failed to register device storage");
1288
1289         return connman_driver_register(&device_driver);
1290 }
1291
1292 void __connman_device_cleanup(void)
1293 {
1294         DBG("");
1295
1296         connman_driver_unregister(&device_driver);
1297
1298         connman_storage_unregister(&device_storage);
1299 }