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