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