Disable technology when removing device
[platform/upstream/connman.git] / src / technology.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 <gdbus.h>
27
28 #include "connman.h"
29
30 static DBusConnection *connection;
31
32 static GHashTable *rfkill_table;
33 static GHashTable *device_table;
34 static GSList *technology_list = NULL;
35
36 struct connman_rfkill {
37         unsigned int index;
38         enum connman_service_type type;
39 };
40
41 enum connman_technology_state {
42         CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
43         CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
44         CONNMAN_TECHNOLOGY_STATE_AVAILABLE = 2,
45         CONNMAN_TECHNOLOGY_STATE_BLOCKED   = 3,
46         CONNMAN_TECHNOLOGY_STATE_ENABLED   = 4,
47         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 5,
48 };
49
50 struct connman_technology {
51         gint refcount;
52         enum connman_service_type type;
53         enum connman_technology_state state;
54         char *path;
55         GHashTable *rfkill_list;
56         GSList *device_list;
57         gint enabled;
58
59         struct connman_technology_driver *driver;
60         void *driver_data;
61 };
62
63 static GSList *driver_list = NULL;
64
65 static gint compare_priority(gconstpointer a, gconstpointer b)
66 {
67         const struct connman_technology_driver *driver1 = a;
68         const struct connman_technology_driver *driver2 = b;
69
70         return driver2->priority - driver1->priority;
71 }
72
73 /**
74  * connman_technology_driver_register:
75  * @driver: Technology driver definition
76  *
77  * Register a new technology driver
78  *
79  * Returns: %0 on success
80  */
81 int connman_technology_driver_register(struct connman_technology_driver *driver)
82 {
83         DBG("driver %p name %s", driver, driver->name);
84
85         driver_list = g_slist_insert_sorted(driver_list, driver,
86                                                         compare_priority);
87
88         return 0;
89 }
90
91 /**
92  * connman_technology_driver_unregister:
93  * @driver: Technology driver definition
94  *
95  * Remove a previously registered technology driver
96  */
97 void connman_technology_driver_unregister(struct connman_technology_driver *driver)
98 {
99         DBG("driver %p name %s", driver, driver->name);
100
101         driver_list = g_slist_remove(driver_list, driver);
102 }
103
104 void __connman_technology_add_interface(enum connman_service_type type,
105                                 int index, const char *name, const char *ident)
106 {
107         GSList *list;
108
109         switch (type) {
110         case CONNMAN_SERVICE_TYPE_UNKNOWN:
111         case CONNMAN_SERVICE_TYPE_SYSTEM:
112                 return;
113         case CONNMAN_SERVICE_TYPE_ETHERNET:
114         case CONNMAN_SERVICE_TYPE_WIFI:
115         case CONNMAN_SERVICE_TYPE_WIMAX:
116         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
117         case CONNMAN_SERVICE_TYPE_CELLULAR:
118         case CONNMAN_SERVICE_TYPE_GPS:
119         case CONNMAN_SERVICE_TYPE_VPN:
120                 break;
121         }
122
123         connman_info("Create interface %s [ %s ]", name,
124                                 __connman_service_type2string(type));
125
126         for (list = technology_list; list; list = list->next) {
127                 struct connman_technology *technology = list->data;
128
129                 if (technology->type != type)
130                         continue;
131
132                 if (technology->driver == NULL)
133                         continue;
134
135                 if (technology->driver->add_interface)
136                         technology->driver->add_interface(technology,
137                                                         index, name, ident);
138         }
139 }
140
141 void __connman_technology_remove_interface(enum connman_service_type type,
142                                 int index, const char *name, const char *ident)
143 {
144         GSList *list;
145
146         switch (type) {
147         case CONNMAN_SERVICE_TYPE_UNKNOWN:
148         case CONNMAN_SERVICE_TYPE_SYSTEM:
149                 return;
150         case CONNMAN_SERVICE_TYPE_ETHERNET:
151         case CONNMAN_SERVICE_TYPE_WIFI:
152         case CONNMAN_SERVICE_TYPE_WIMAX:
153         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
154         case CONNMAN_SERVICE_TYPE_CELLULAR:
155         case CONNMAN_SERVICE_TYPE_GPS:
156         case CONNMAN_SERVICE_TYPE_VPN:
157                 break;
158         }
159
160         connman_info("Remove interface %s [ %s ]", name,
161                                 __connman_service_type2string(type));
162
163         for (list = technology_list; list; list = list->next) {
164                 struct connman_technology *technology = list->data;
165
166                 if (technology->type != type)
167                         continue;
168
169                 if (technology->driver == NULL)
170                         continue;
171
172                 if (technology->driver->remove_interface)
173                         technology->driver->remove_interface(technology, index);
174         }
175 }
176
177 static int set_tethering(connman_bool_t enabled)
178 {
179         GSList *list;
180
181         for (list = technology_list; list; list = list->next) {
182                 struct connman_technology *technology = list->data;
183
184                 if (technology->driver == NULL)
185                         continue;
186
187                 if (technology->driver->set_tethering)
188                         technology->driver->set_tethering(technology, enabled);
189         }
190
191         return 0;
192 }
193
194 int __connman_technology_enable_tethering(void)
195 {
196         return set_tethering(TRUE);
197 }
198
199 int __connman_technology_disable_tethering(void)
200 {
201         return set_tethering(FALSE);
202 }
203
204 static void free_rfkill(gpointer data)
205 {
206         struct connman_rfkill *rfkill = data;
207
208         g_free(rfkill);
209 }
210
211 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
212 {
213         GSList *list;
214
215         for (list = technology_list; list; list = list->next) {
216                 struct connman_technology *technology = list->data;
217
218                 if (technology->path == NULL)
219                         continue;
220
221                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
222                                                         &technology->path);
223         }
224 }
225
226 static void technologies_changed(void)
227 {
228         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
229                         CONNMAN_MANAGER_INTERFACE, "Technologies",
230                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
231 }
232
233 static void device_list(DBusMessageIter *iter, void *user_data)
234 {
235         struct connman_technology *technology = user_data;
236         GSList *list;
237
238         for (list = technology->device_list; list; list = list->next) {
239                 struct connman_device *device = list->data;
240                 const char *path;
241
242                 path = connman_device_get_path(device);
243                 if (path == NULL)
244                         continue;
245
246                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
247                                                                         &path);
248         }
249 }
250
251 static void devices_changed(struct connman_technology *technology)
252 {
253         connman_dbus_property_changed_array(technology->path,
254                         CONNMAN_TECHNOLOGY_INTERFACE, "Devices",
255                         DBUS_TYPE_OBJECT_PATH, device_list, technology);
256 }
257
258 static const char *state2string(enum connman_technology_state state)
259 {
260         switch (state) {
261         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
262                 break;
263         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
264                 return "offline";
265         case CONNMAN_TECHNOLOGY_STATE_AVAILABLE:
266                 return "available";
267         case CONNMAN_TECHNOLOGY_STATE_BLOCKED:
268                 return "blocked";
269         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
270                 return "enabled";
271         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
272                 return "connected";
273         }
274
275         return NULL;
276 }
277
278 static void state_changed(struct connman_technology *technology)
279 {
280         const char *str;
281
282         str = state2string(technology->state);
283         if (str == NULL)
284                 return;
285
286         connman_dbus_property_changed_basic(technology->path,
287                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
288                                                 DBUS_TYPE_STRING, &str);
289 }
290
291 static const char *get_name(enum connman_service_type type)
292 {
293         switch (type) {
294         case CONNMAN_SERVICE_TYPE_UNKNOWN:
295         case CONNMAN_SERVICE_TYPE_SYSTEM:
296         case CONNMAN_SERVICE_TYPE_GPS:
297         case CONNMAN_SERVICE_TYPE_VPN:
298                 break;
299         case CONNMAN_SERVICE_TYPE_ETHERNET:
300                 return "Wired";
301         case CONNMAN_SERVICE_TYPE_WIFI:
302                 return "WiFi";
303         case CONNMAN_SERVICE_TYPE_WIMAX:
304                 return "WiMAX";
305         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
306                 return "Bluetooth";
307         case CONNMAN_SERVICE_TYPE_CELLULAR:
308                 return "3G";
309         }
310
311         return NULL;
312 }
313
314 static DBusMessage *get_properties(DBusConnection *conn,
315                                         DBusMessage *message, void *user_data)
316 {
317         struct connman_technology *technology = user_data;
318         DBusMessage *reply;
319         DBusMessageIter array, dict;
320         const char *str;
321
322         reply = dbus_message_new_method_return(message);
323         if (reply == NULL)
324                 return NULL;
325
326         dbus_message_iter_init_append(reply, &array);
327
328         connman_dbus_dict_open(&array, &dict);
329
330         str = state2string(technology->state);
331         if (str != NULL)
332                 connman_dbus_dict_append_basic(&dict, "State",
333                                                 DBUS_TYPE_STRING, &str);
334
335         str = get_name(technology->type);
336         if (str != NULL)
337                 connman_dbus_dict_append_basic(&dict, "Name",
338                                                 DBUS_TYPE_STRING, &str);
339
340         str = __connman_service_type2string(technology->type);
341         if (str != NULL)
342                 connman_dbus_dict_append_basic(&dict, "Type",
343                                                 DBUS_TYPE_STRING, &str);
344
345         connman_dbus_dict_append_array(&dict, "Devices",
346                         DBUS_TYPE_OBJECT_PATH, device_list, technology);
347
348         connman_dbus_dict_close(&array, &dict);
349
350         return reply;
351 }
352
353 static GDBusMethodTable technology_methods[] = {
354         { "GetProperties", "", "a{sv}", get_properties },
355         { },
356 };
357
358 static GDBusSignalTable technology_signals[] = {
359         { "PropertyChanged", "sv" },
360         { },
361 };
362
363 static struct connman_technology *technology_find(enum connman_service_type type)
364 {
365         GSList *list;
366
367         DBG("type %d", type);
368
369         for (list = technology_list; list; list = list->next) {
370                 struct connman_technology *technology = list->data;
371
372                 if (technology->type == type)
373                         return technology;
374         }
375
376         return NULL;
377 }
378
379 static struct connman_technology *technology_get(enum connman_service_type type)
380 {
381         struct connman_technology *technology;
382         const char *str;
383         GSList *list;
384
385         DBG("type %d", type);
386
387         technology = technology_find(type);
388         if (technology != NULL) {
389                 g_atomic_int_inc(&technology->refcount);
390                 goto done;
391         }
392
393         str = __connman_service_type2string(type);
394         if (str == NULL)
395                 return NULL;
396
397         technology = g_try_new0(struct connman_technology, 1);
398         if (technology == NULL)
399                 return NULL;
400
401         technology->refcount = 1;
402
403         technology->type = type;
404         technology->path = g_strdup_printf("%s/technology/%s",
405                                                         CONNMAN_PATH, str);
406
407         technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
408                                                         NULL, free_rfkill);
409         technology->device_list = NULL;
410
411         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
412
413         if (g_dbus_register_interface(connection, technology->path,
414                                         CONNMAN_TECHNOLOGY_INTERFACE,
415                                         technology_methods, technology_signals,
416                                         NULL, technology, NULL) == FALSE) {
417                 connman_error("Failed to register %s", technology->path);
418                 g_free(technology);
419                 return NULL;
420         }
421
422         technology_list = g_slist_append(technology_list, technology);
423
424         technologies_changed();
425
426         if (technology->driver != NULL)
427                 goto done;
428
429         for (list = driver_list; list; list = list->next) {
430                 struct connman_technology_driver *driver = list->data;
431
432                 DBG("driver %p name %s", driver, driver->name);
433
434                 if (driver->type != technology->type)
435                         continue;
436
437                 if (driver->probe(technology) == 0) {
438                         technology->driver = driver;
439                         break;
440                 }
441         }
442
443 done:
444         DBG("technology %p", technology);
445
446         return technology;
447 }
448
449 static void technology_put(struct connman_technology *technology)
450 {
451         DBG("technology %p", technology);
452
453         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
454                 return;
455
456         if (technology->driver) {
457                 technology->driver->remove(technology);
458                 technology->driver = NULL;
459         }
460
461         technology_list = g_slist_remove(technology_list, technology);
462
463         technologies_changed();
464
465         g_dbus_unregister_interface(connection, technology->path,
466                                                 CONNMAN_TECHNOLOGY_INTERFACE);
467
468         g_slist_free(technology->device_list);
469         g_hash_table_destroy(technology->rfkill_list);
470
471         g_free(technology->path);
472         g_free(technology);
473 }
474
475 static void unregister_technology(gpointer data)
476 {
477         struct connman_technology *technology = data;
478
479         technology_put(technology);
480 }
481
482 int __connman_technology_add_device(struct connman_device *device)
483 {
484         struct connman_technology *technology;
485         enum connman_service_type type;
486
487         DBG("device %p", device);
488
489         type = __connman_device_get_service_type(device);
490         __connman_notifier_register(type);
491
492         technology = technology_get(type);
493         if (technology == NULL)
494                 return -ENXIO;
495
496         g_hash_table_insert(device_table, device, technology);
497
498         if (technology->device_list == NULL) {
499                 if (__connman_device_get_blocked(device) == TRUE)
500                         technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
501                 else
502                         technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
503
504                 state_changed(technology);
505         } else {
506                 if (technology->state == CONNMAN_TECHNOLOGY_STATE_BLOCKED &&
507                                 __connman_device_get_blocked(device) == FALSE) {
508                         technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
509                         state_changed(technology);
510                 }
511         }
512
513         technology->device_list = g_slist_append(technology->device_list,
514                                                                 device);
515         devices_changed(technology);
516
517         return 0;
518 }
519
520 int __connman_technology_remove_device(struct connman_device *device)
521 {
522         struct connman_technology *technology;
523         enum connman_service_type type;
524
525         DBG("device %p", device);
526
527         type = __connman_device_get_service_type(device);
528         __connman_notifier_unregister(type);
529
530         technology = g_hash_table_lookup(device_table, device);
531         if (technology == NULL)
532                 return -ENXIO;
533
534         technology->device_list = g_slist_remove(technology->device_list,
535                                                                 device);
536         devices_changed(technology);
537
538         if (technology->device_list == NULL) {
539                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
540                 state_changed(technology);
541         }
542
543         g_hash_table_remove(device_table, device);
544
545         return 0;
546 }
547
548 int __connman_technology_enable_device(struct connman_device *device)
549 {
550         struct connman_technology *technology;
551         enum connman_service_type type;
552
553         DBG("device %p", device);
554
555         type = __connman_device_get_service_type(device);
556         __connman_notifier_enable(type);
557
558         technology = g_hash_table_lookup(device_table, device);
559         if (technology == NULL)
560                 return -ENXIO;
561
562         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
563                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
564                 state_changed(technology);
565         }
566
567         return 0;
568 }
569
570 int __connman_technology_disable_device(struct connman_device *device)
571 {
572         struct connman_technology *technology;
573         enum connman_service_type type;
574         GSList *list;
575
576         DBG("device %p", device);
577
578         type = __connman_device_get_service_type(device);
579         __connman_notifier_disable(type);
580
581         technology = g_hash_table_lookup(device_table, device);
582         if (technology == NULL)
583                 return -ENXIO;
584
585         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
586                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
587                 state_changed(technology);
588         }
589
590         for (list = technology->device_list; list; list = list->next) {
591                 struct connman_device *device = list->data;
592
593                 if (__connman_device_get_blocked(device) == FALSE)
594                         return 0;
595         }
596
597         technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
598         state_changed(technology);
599
600         return 0;
601 }
602
603 int __connman_technology_add_rfkill(unsigned int index,
604                                         enum connman_service_type type,
605                                                 connman_bool_t softblock,
606                                                 connman_bool_t hardblock)
607 {
608         struct connman_technology *technology;
609         struct connman_rfkill *rfkill;
610
611         DBG("index %u type %d soft %u hard %u", index, type,
612                                                         softblock, hardblock);
613
614         rfkill = g_try_new0(struct connman_rfkill, 1);
615         if (rfkill == NULL)
616                 return -ENOMEM;
617
618         rfkill->index = index;
619         rfkill->type = type;
620
621         technology = technology_get(type);
622         if (technology == NULL) {
623                 g_free(rfkill);
624                 return -ENXIO;
625         }
626
627         g_hash_table_replace(rfkill_table, &index, technology);
628
629         g_hash_table_replace(technology->rfkill_list, &index, rfkill);
630
631         return 0;
632 }
633
634 int __connman_technology_update_rfkill(unsigned int index,
635                                                 connman_bool_t softblock,
636                                                 connman_bool_t hardblock)
637 {
638         struct connman_technology *technology;
639
640         DBG("index %u soft %u hard %u", index, softblock, hardblock);
641
642         technology = g_hash_table_lookup(rfkill_table, &index);
643         if (technology == NULL)
644                 return -ENXIO;
645
646         return 0;
647 }
648
649 int __connman_technology_remove_rfkill(unsigned int index)
650 {
651         struct connman_technology *technology;
652
653         DBG("index %u", index);
654
655         technology = g_hash_table_lookup(rfkill_table, &index);
656         if (technology == NULL)
657                 return -ENXIO;
658
659         g_hash_table_remove(technology->rfkill_list, &index);
660
661         g_hash_table_remove(rfkill_table, &index);
662
663         return 0;
664 }
665
666 int __connman_technology_init(void)
667 {
668         DBG("");
669
670         connection = connman_dbus_get_connection();
671
672         rfkill_table = g_hash_table_new_full(g_int_hash, g_int_equal,
673                                                 NULL, unregister_technology);
674         device_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
675                                                 NULL, unregister_technology);
676
677         return 0;
678 }
679
680 void __connman_technology_cleanup(void)
681 {
682         DBG("");
683
684         g_hash_table_destroy(device_table);
685         g_hash_table_destroy(rfkill_table);
686
687         dbus_connection_unref(connection);
688 }