device: Support already powered devices
[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 && err != -EALREADY) {
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 && err != -EALREADY) {
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         int err;
622
623         DBG("driver %p powered %d", device, powered);
624
625         if (device->powered == powered) {
626                 device->powered_pending = powered;
627                 return -EALREADY;
628         }
629
630         if (powered == TRUE)
631                 err = __connman_device_enable(device);
632         else
633                 err = __connman_device_disable(device);
634
635         if (err < 0 && err != -EINPROGRESS)
636                 return err;
637
638         device->powered = powered;
639         device->powered_pending = powered;
640
641         if (device->powered == TRUE)
642                 __connman_technology_enable_device(device);
643         else
644                 __connman_technology_disable_device(device);
645
646         if (device->offlinemode == TRUE && powered == TRUE)
647                 return connman_device_set_powered(device, FALSE);
648
649         if (powered == FALSE)
650                 return 0;
651
652         reset_scan_trigger(device);
653
654         if (device->driver && device->driver->scan)
655                 device->driver->scan(device);
656
657         return 0;
658 }
659
660 int __connman_device_set_blocked(struct connman_device *device,
661                                                 connman_bool_t blocked)
662 {
663         connman_bool_t powered;
664
665         DBG("device %p blocked %d", device, blocked);
666
667         device->blocked = blocked;
668
669         if (device->offlinemode == TRUE)
670                 return 0;
671
672         connman_info("%s {rfkill} blocked %d", device->interface, blocked);
673
674         if (blocked == FALSE)
675                 powered = device->powered_persistent;
676         else
677                 powered = FALSE;
678
679         return set_powered(device, powered);
680 }
681
682 connman_bool_t __connman_device_get_blocked(struct connman_device *device)
683 {
684         return device->blocked;
685 }
686
687 int __connman_device_scan(struct connman_device *device)
688 {
689         if (!device->driver || !device->driver->scan)
690                 return -EOPNOTSUPP;
691
692         if (device->powered == FALSE)
693                 return -ENOLINK;
694
695         reset_scan_trigger(device);
696
697         return device->driver->scan(device);
698 }
699
700 int __connman_device_enable_persistent(struct connman_device *device)
701 {
702         int err;
703
704         DBG("device %p", device);
705
706         device->powered_persistent = TRUE;
707
708         __connman_storage_save_device(device);
709
710         err = __connman_device_enable(device);
711         if (err == 0 || err == -EINPROGRESS) {
712                 device->offlinemode = FALSE;
713                 if (__connman_profile_get_offlinemode() == TRUE) {
714                         __connman_profile_set_offlinemode(FALSE, FALSE);
715
716                         __connman_profile_save_default();
717                 }
718         }
719
720         return err;
721 }
722
723 int __connman_device_disable_persistent(struct connman_device *device)
724 {
725         DBG("device %p", device);
726
727         device->powered_persistent = FALSE;
728
729         __connman_storage_save_device(device);
730
731         return __connman_device_disable(device);
732 }
733
734 int __connman_device_disconnect(struct connman_device *device)
735 {
736         GHashTableIter iter;
737         gpointer key, value;
738
739         DBG("device %p", device);
740
741         connman_device_set_disconnected(device, TRUE);
742
743         g_hash_table_iter_init(&iter, device->networks);
744
745         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
746                 struct connman_network *network = value;
747
748                 if (__connman_network_get_connecting(network) == TRUE) {
749                         /*
750                          * Skip network in the process of connecting.
751                          * This is a workaround for WiFi networks serviced
752                          * by the supplicant plugin that hold a reference
753                          * to the network.  If we disconnect the network
754                          * here then the referenced object will not be
755                          * registered and usage (like launching DHCP client)
756                          * will fail.  There is nothing to be gained by
757                          * removing the network here anyway.
758                          */
759                         connman_warn("Skipping disconnect of %s",
760                                 connman_network_get_identifier(network));
761                         continue;
762                 }
763
764                 __connman_network_disconnect(network);
765         }
766
767         return 0;
768 }
769
770 static void mark_network_unavailable(gpointer key, gpointer value,
771                                                         gpointer user_data)
772 {
773         struct connman_network *network = value;
774
775         if (connman_network_get_connected(network) == TRUE)
776                 return;
777
778         connman_network_set_available(network, FALSE);
779 }
780
781 static gboolean remove_unavailable_network(gpointer key, gpointer value,
782                                                         gpointer user_data)
783 {
784         struct connman_network *network = value;
785
786         if (connman_network_get_connected(network) == TRUE)
787                 return FALSE;
788
789         if (connman_network_get_available(network) == TRUE)
790                 return FALSE;
791
792         return TRUE;
793 }
794
795 void __connman_device_cleanup_networks(struct connman_device *device)
796 {
797         g_hash_table_foreach_remove(device->networks,
798                                         remove_unavailable_network, NULL);
799 }
800
801 /**
802  * connman_device_set_scanning:
803  * @device: device structure
804  * @scanning: scanning state
805  *
806  * Change scanning state of device
807  */
808 int connman_device_set_scanning(struct connman_device *device,
809                                                 connman_bool_t scanning)
810 {
811         DBG("device %p scanning %d", device, scanning);
812
813         if (!device->driver || !device->driver->scan)
814                 return -EINVAL;
815
816         if (device->scanning == scanning)
817                 return -EALREADY;
818
819         device->scanning = scanning;
820
821         if (scanning == TRUE) {
822                 reset_scan_trigger(device);
823
824                 g_hash_table_foreach(device->networks,
825                                         mark_network_unavailable, NULL);
826
827                 return 0;
828         }
829
830         __connman_device_cleanup_networks(device);
831
832         if (device->connections > 0)
833                 return 0;
834
835         if (device->disconnected == TRUE)
836                 return 0;
837
838         __connman_service_auto_connect();
839
840         return 0;
841 }
842
843 /**
844  * connman_device_set_disconnected:
845  * @device: device structure
846  * @disconnected: disconnected state
847  *
848  * Change disconnected state of device (only for device with networks)
849  */
850 int connman_device_set_disconnected(struct connman_device *device,
851                                                 connman_bool_t disconnected)
852 {
853         DBG("device %p disconnected %d", device, disconnected);
854
855         if (device->disconnected == disconnected)
856                 return -EALREADY;
857
858         device->disconnected = disconnected;
859
860         if (disconnected == TRUE)
861                 force_scan_trigger(device);
862
863         return 0;
864 }
865
866 /**
867  * connman_device_get_disconnected:
868  * @device: device structure
869  *
870  * Get device disconnected state
871  */
872 connman_bool_t connman_device_get_disconnected(struct connman_device *device)
873 {
874         return device->disconnected;
875 }
876
877 /**
878  * connman_device_set_string:
879  * @device: device structure
880  * @key: unique identifier
881  * @value: string value
882  *
883  * Set string value for specific key
884  */
885 int connman_device_set_string(struct connman_device *device,
886                                         const char *key, const char *value)
887 {
888         DBG("device %p key %s value %s", device, key, value);
889
890         if (g_str_equal(key, "Address") == TRUE) {
891                 g_free(device->address);
892                 device->address = g_strdup(value);
893         } else if (g_str_equal(key, "Name") == TRUE) {
894                 g_free(device->name);
895                 device->name = g_strdup(value);
896         } else if (g_str_equal(key, "Node") == TRUE) {
897                 g_free(device->node);
898                 device->node = g_strdup(value);
899         }
900
901         return connman_element_set_string(&device->element, key, value);
902 }
903
904 /**
905  * connman_device_get_string:
906  * @device: device structure
907  * @key: unique identifier
908  *
909  * Get string value for specific key
910  */
911 const char *connman_device_get_string(struct connman_device *device,
912                                                         const char *key)
913 {
914         DBG("device %p key %s", device, key);
915
916         if (g_str_equal(key, "Address") == TRUE)
917                 return device->address;
918         else if (g_str_equal(key, "Name") == TRUE)
919                 return device->name;
920         else if (g_str_equal(key, "Node") == TRUE)
921                 return device->node;
922         else if (g_str_equal(key, "Interface") == TRUE)
923                 return device->interface;
924
925         return connman_element_get_string(&device->element, key);
926 }
927
928 static void set_offlinemode(struct connman_element *element, gpointer user_data)
929 {
930         struct connman_device *device = element->device;
931         connman_bool_t offlinemode = GPOINTER_TO_UINT(user_data);
932         connman_bool_t powered;
933
934         DBG("element %p name %s", element, element->name);
935
936         if (device == NULL)
937                 return;
938
939         device->offlinemode = offlinemode;
940
941         if (device->blocked == TRUE)
942                 return;
943
944         powered = (offlinemode == TRUE) ? FALSE : TRUE;
945
946         if (device->powered == powered)
947                 return;
948
949         if (device->powered_persistent == FALSE)
950                 powered = FALSE;
951
952         set_powered(device, powered);
953 }
954
955 int __connman_device_set_offlinemode(connman_bool_t offlinemode)
956 {
957         DBG("offlinmode %d", offlinemode);
958
959         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
960                         set_offlinemode, GUINT_TO_POINTER(offlinemode));
961
962         __connman_notifier_offlinemode(offlinemode);
963
964         return 0;
965 }
966
967 void __connman_device_increase_connections(struct connman_device *device)
968 {
969         if (device == NULL)
970                 return;
971
972         device->connections++;
973 }
974
975 void __connman_device_decrease_connections(struct connman_device *device)
976 {
977         if (device == NULL)
978                 return;
979
980         device->connections--;
981 }
982
983 /**
984  * connman_device_add_network:
985  * @device: device structure
986  * @network: network structure
987  *
988  * Add new network to the device
989  */
990 int connman_device_add_network(struct connman_device *device,
991                                         struct connman_network *network)
992 {
993         const char *identifier = connman_network_get_identifier(network);
994         int err;
995
996         DBG("device %p network %p", device, network);
997
998         if (identifier == NULL)
999                 return -EINVAL;
1000
1001         __connman_network_set_device(network, device);
1002
1003         err = connman_element_register((struct connman_element *) network,
1004                                                         &device->element);
1005         if (err < 0) {
1006                 __connman_network_set_device(network, NULL);
1007                 return err;
1008         }
1009
1010         g_hash_table_insert(device->networks, g_strdup(identifier),
1011                                                                 network);
1012
1013         return 0;
1014 }
1015
1016 /**
1017  * connman_device_get_network:
1018  * @device: device structure
1019  * @identifier: network identifier
1020  *
1021  * Get network for given identifier
1022  */
1023 struct connman_network *connman_device_get_network(struct connman_device *device,
1024                                                         const char *identifier)
1025 {
1026         DBG("device %p identifier %s", device, identifier);
1027
1028         return g_hash_table_lookup(device->networks, identifier);
1029 }
1030
1031 /**
1032  * connman_device_remove_network:
1033  * @device: device structure
1034  * @identifier: network identifier
1035  *
1036  * Remove network for given identifier
1037  */
1038 int connman_device_remove_network(struct connman_device *device,
1039                                                         const char *identifier)
1040 {
1041         DBG("device %p identifier %s", device, identifier);
1042
1043         g_hash_table_remove(device->networks, identifier);
1044
1045         return 0;
1046 }
1047
1048 void connman_device_remove_all_networks(struct connman_device *device)
1049 {
1050         g_hash_table_remove_all(device->networks);
1051 }
1052
1053 void __connman_device_set_network(struct connman_device *device,
1054                                         struct connman_network *network)
1055 {
1056         const char *name;
1057
1058         if (device == NULL)
1059                 return;
1060
1061         if (device->network == network)
1062                 return;
1063
1064         if (device->network != NULL)
1065                 connman_network_unref(device->network);
1066
1067         if (network != NULL) {
1068                 name = connman_network_get_string(network,
1069                                                 CONNMAN_PROPERTY_ID_NAME);
1070                 g_free(device->last_network);
1071                 device->last_network = g_strdup(name);
1072
1073                 device->network = connman_network_ref(network);
1074         } else {
1075                 g_free(device->last_network);
1076                 device->last_network = NULL;
1077
1078                 device->network = NULL;
1079         }
1080 }
1081
1082 void __connman_device_set_reconnect(struct connman_device *device,
1083                                                 connman_bool_t reconnect)
1084 {
1085         device->reconnect = reconnect;
1086 }
1087
1088 connman_bool_t  __connman_device_get_reconnect(
1089                                 struct connman_device *device)
1090 {
1091         return device->reconnect;
1092 }
1093
1094 /**
1095  * connman_device_register:
1096  * @device: device structure
1097  *
1098  * Register device with the system
1099  */
1100 int connman_device_register(struct connman_device *device)
1101 {
1102         __connman_storage_load_device(device);
1103
1104         device->offlinemode = __connman_profile_get_offlinemode();
1105
1106         return connman_element_register(&device->element, NULL);
1107 }
1108
1109 /**
1110  * connman_device_unregister:
1111  * @device: device structure
1112  *
1113  * Unregister device with the system
1114  */
1115 void connman_device_unregister(struct connman_device *device)
1116 {
1117         __connman_storage_save_device(device);
1118
1119         connman_element_unregister(&device->element);
1120 }
1121
1122 /**
1123  * connman_device_get_data:
1124  * @device: device structure
1125  *
1126  * Get private device data pointer
1127  */
1128 void *connman_device_get_data(struct connman_device *device)
1129 {
1130         return device->driver_data;
1131 }
1132
1133 /**
1134  * connman_device_set_data:
1135  * @device: device structure
1136  * @data: data pointer
1137  *
1138  * Set private device data pointer
1139  */
1140 void connman_device_set_data(struct connman_device *device, void *data)
1141 {
1142         device->driver_data = data;
1143 }
1144
1145 static gboolean match_driver(struct connman_device *device,
1146                                         struct connman_device_driver *driver)
1147 {
1148         if (device->type == driver->type ||
1149                         driver->type == CONNMAN_DEVICE_TYPE_UNKNOWN)
1150                 return TRUE;
1151
1152         return FALSE;
1153 }
1154
1155 static int device_probe(struct connman_element *element)
1156 {
1157         struct connman_device *device = element->device;
1158         GSList *list;
1159
1160         DBG("element %p name %s", element, element->name);
1161
1162         if (device == NULL)
1163                 return -ENODEV;
1164
1165         if (device->driver != NULL)
1166                 return -EALREADY;
1167
1168         for (list = driver_list; list; list = list->next) {
1169                 struct connman_device_driver *driver = list->data;
1170
1171                 if (match_driver(device, driver) == FALSE)
1172                         continue;
1173
1174                 DBG("driver %p name %s", driver, driver->name);
1175
1176                 if (driver->probe(device) == 0) {
1177                         device->driver = driver;
1178                         break;
1179                 }
1180         }
1181
1182         if (device->driver == NULL)
1183                 return -ENODEV;
1184
1185         return setup_device(device);
1186 }
1187
1188 static void device_remove(struct connman_element *element)
1189 {
1190         struct connman_device *device = element->device;
1191
1192         DBG("element %p name %s", element, element->name);
1193
1194         if (device == NULL)
1195                 return;
1196
1197         if (device->driver == NULL)
1198                 return;
1199
1200         remove_device(device);
1201 }
1202
1203 static struct connman_driver device_driver = {
1204         .name           = "device",
1205         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
1206         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
1207         .probe          = device_probe,
1208         .remove         = device_remove,
1209 };
1210
1211 static int device_load(struct connman_device *device)
1212 {
1213         const char *ident = __connman_profile_active_ident();
1214         GKeyFile *keyfile;
1215         GError *error = NULL;
1216         gchar *identifier;
1217         connman_bool_t powered;
1218         int val;
1219
1220         DBG("device %p", device);
1221
1222         keyfile = __connman_storage_open_profile(ident);
1223         if (keyfile == NULL)
1224                 return 0;
1225
1226         identifier = g_strdup_printf("device_%s", device->element.name);
1227         if (identifier == NULL)
1228                 goto done;
1229
1230         powered = g_key_file_get_boolean(keyfile, identifier,
1231                                                 "Powered", &error);
1232         if (error == NULL)
1233                 device->powered_persistent = powered;
1234         g_clear_error(&error);
1235
1236         val = g_key_file_get_integer(keyfile, identifier,
1237                                                 "ScanInterval", &error);
1238         if (error == NULL)
1239                 device->scan_interval = val;
1240         g_clear_error(&error);
1241
1242 done:
1243         g_free(identifier);
1244
1245         __connman_storage_close_profile(ident, keyfile, FALSE);
1246
1247         return 0;
1248 }
1249
1250 static int device_save(struct connman_device *device)
1251 {
1252         const char *ident = __connman_profile_active_ident();
1253         GKeyFile *keyfile;
1254         gchar *identifier;
1255
1256         DBG("device %p", device);
1257
1258         keyfile = __connman_storage_open_profile(ident);
1259         if (keyfile == NULL)
1260                 return 0;
1261
1262         identifier = g_strdup_printf("device_%s", device->element.name);
1263         if (identifier == NULL)
1264                 goto done;
1265
1266         g_key_file_set_boolean(keyfile, identifier,
1267                                         "Powered", device->powered_persistent);
1268
1269         g_key_file_set_integer(keyfile, identifier,
1270                                         "ScanInterval", device->scan_interval);
1271
1272 done:
1273         g_free(identifier);
1274
1275         __connman_storage_close_profile(ident, keyfile, TRUE);
1276
1277         return 0;
1278 }
1279
1280 static struct connman_storage device_storage = {
1281         .name           = "device",
1282         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1283         .device_load    = device_load,
1284         .device_save    = device_save,
1285 };
1286
1287 int __connman_device_init(void)
1288 {
1289         DBG("");
1290
1291         if (connman_storage_register(&device_storage) < 0)
1292                 connman_error("Failed to register device storage");
1293
1294         return connman_driver_register(&device_driver);
1295 }
1296
1297 void __connman_device_cleanup(void)
1298 {
1299         DBG("");
1300
1301         connman_driver_unregister(&device_driver);
1302
1303         connman_storage_unregister(&device_storage);
1304 }