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