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