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