19a096d784874a4b3946ac00ade8cc026cbfd5de
[platform/upstream/connman.git] / src / technology.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <string.h>
28
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 static DBusConnection *connection;
34
35 static GSList *technology_list = NULL;
36
37 struct connman_rfkill {
38         unsigned int index;
39         enum connman_service_type type;
40         connman_bool_t softblock;
41         connman_bool_t hardblock;
42 };
43
44 enum connman_technology_state {
45         CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
46         CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
47         CONNMAN_TECHNOLOGY_STATE_AVAILABLE = 2,
48         CONNMAN_TECHNOLOGY_STATE_ENABLED   = 3,
49         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 4,
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         char *regdom;
62
63         connman_bool_t tethering;
64         char *tethering_ident;
65         char *tethering_passphrase;
66
67         connman_bool_t enable_persistent; /* Save the tech state */
68
69         struct connman_technology_driver *driver;
70         void *driver_data;
71
72         DBusMessage *pending_reply;
73         guint pending_timeout;
74 };
75
76 static GSList *driver_list = NULL;
77
78 static gint compare_priority(gconstpointer a, gconstpointer b)
79 {
80         const struct connman_technology_driver *driver1 = a;
81         const struct connman_technology_driver *driver2 = b;
82
83         return driver2->priority - driver1->priority;
84 }
85
86 /**
87  * connman_technology_driver_register:
88  * @driver: Technology driver definition
89  *
90  * Register a new technology driver
91  *
92  * Returns: %0 on success
93  */
94 int connman_technology_driver_register(struct connman_technology_driver *driver)
95 {
96         GSList *list;
97         struct connman_technology *technology;
98
99         DBG("driver %p name %s", driver, driver->name);
100
101         driver_list = g_slist_insert_sorted(driver_list, driver,
102                                                         compare_priority);
103
104         for (list = technology_list; list; list = list->next) {
105                 technology = list->data;
106
107                 if (technology->driver != NULL)
108                         continue;
109
110                 if (technology->type == driver->type)
111                         technology->driver = driver;
112         }
113
114         return 0;
115 }
116
117 /**
118  * connman_technology_driver_unregister:
119  * @driver: Technology driver definition
120  *
121  * Remove a previously registered technology driver
122  */
123 void connman_technology_driver_unregister(struct connman_technology_driver *driver)
124 {
125         GSList *list;
126         struct connman_technology *technology;
127
128         DBG("driver %p name %s", driver, driver->name);
129
130         for (list = technology_list; list; list = list->next) {
131                 technology = list->data;
132
133                 if (technology->driver == NULL)
134                         continue;
135
136                 if (technology->type == driver->type) {
137                         technology->driver->remove(technology);
138                         technology->driver = NULL;
139                 }
140         }
141
142         driver_list = g_slist_remove(driver_list, driver);
143 }
144
145 static void tethering_changed(struct connman_technology *technology)
146 {
147         connman_bool_t tethering = technology->tethering;
148
149         connman_dbus_property_changed_basic(technology->path,
150                                 CONNMAN_TECHNOLOGY_INTERFACE, "Tethering",
151                                                 DBUS_TYPE_BOOLEAN, &tethering);
152 }
153
154 void connman_technology_tethering_notify(struct connman_technology *technology,
155                                                         connman_bool_t enabled)
156 {
157         DBG("technology %p enabled %u", technology, enabled);
158
159         if (technology->tethering == enabled)
160                 return;
161
162         technology->tethering = enabled;
163
164         tethering_changed(technology);
165
166         if (enabled == TRUE)
167                 __connman_tethering_set_enabled();
168         else
169                 __connman_tethering_set_disabled();
170 }
171
172 static int set_tethering(struct connman_technology *technology,
173                                 const char *bridge, connman_bool_t enabled)
174 {
175         const char *ident, *passphrase;
176
177         ident = technology->tethering_ident;
178         passphrase = technology->tethering_passphrase;
179
180         if (technology->driver == NULL ||
181                         technology->driver->set_tethering == NULL)
182                 return -EOPNOTSUPP;
183
184         if (technology->type == CONNMAN_SERVICE_TYPE_WIFI &&
185             (ident == NULL || passphrase == NULL))
186                 return -EINVAL;
187
188         return technology->driver->set_tethering(technology, ident, passphrase,
189                                                         bridge, enabled);
190 }
191
192 void connman_technology_regdom_notify(struct connman_technology *technology,
193                                                         const char *alpha2)
194 {
195         DBG("");
196
197         if (alpha2 == NULL)
198                 connman_error("Failed to set regulatory domain");
199         else
200                 DBG("Regulatory domain set to %s", alpha2);
201
202         g_free(technology->regdom);
203         technology->regdom = g_strdup(alpha2);
204 }
205
206 int connman_technology_set_regdom(const char *alpha2)
207 {
208         GSList *list;
209
210         for (list = technology_list; list; list = list->next) {
211                 struct connman_technology *technology = list->data;
212
213                 if (technology->driver == NULL)
214                         continue;
215
216                 if (technology->driver->set_regdom)
217                         technology->driver->set_regdom(technology, alpha2);
218         }
219
220         return 0;
221 }
222
223 static void free_rfkill(gpointer data)
224 {
225         struct connman_rfkill *rfkill = data;
226
227         g_free(rfkill);
228 }
229
230 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
231 {
232         GSList *list;
233
234         for (list = technology_list; list; list = list->next) {
235                 struct connman_technology *technology = list->data;
236
237                 if (technology->path == NULL)
238                         continue;
239
240                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
241                                                         &technology->path);
242         }
243 }
244
245 static void technologies_changed(void)
246 {
247         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
248                         CONNMAN_MANAGER_INTERFACE, "Technologies",
249                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
250 }
251
252 static const char *state2string(enum connman_technology_state state)
253 {
254         switch (state) {
255         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
256                 break;
257         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
258                 return "offline";
259         case CONNMAN_TECHNOLOGY_STATE_AVAILABLE:
260                 return "available";
261         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
262                 return "enabled";
263         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
264                 return "connected";
265         }
266
267         return NULL;
268 }
269
270 static void state_changed(struct connman_technology *technology)
271 {
272         const char *str;
273
274         str = state2string(technology->state);
275         if (str == NULL)
276                 return;
277
278         connman_dbus_property_changed_basic(technology->path,
279                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
280                                                 DBUS_TYPE_STRING, &str);
281 }
282
283 static const char *get_name(enum connman_service_type type)
284 {
285         switch (type) {
286         case CONNMAN_SERVICE_TYPE_UNKNOWN:
287         case CONNMAN_SERVICE_TYPE_SYSTEM:
288         case CONNMAN_SERVICE_TYPE_GPS:
289         case CONNMAN_SERVICE_TYPE_VPN:
290         case CONNMAN_SERVICE_TYPE_GADGET:
291                 break;
292         case CONNMAN_SERVICE_TYPE_ETHERNET:
293                 return "Wired";
294         case CONNMAN_SERVICE_TYPE_WIFI:
295                 return "WiFi";
296         case CONNMAN_SERVICE_TYPE_WIMAX:
297                 return "WiMAX";
298         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
299                 return "Bluetooth";
300         case CONNMAN_SERVICE_TYPE_CELLULAR:
301                 return "3G";
302         }
303
304         return NULL;
305 }
306
307 static DBusMessage *get_properties(DBusConnection *conn,
308                                         DBusMessage *message, void *user_data)
309 {
310         struct connman_technology *technology = user_data;
311         DBusMessage *reply;
312         DBusMessageIter array, dict;
313         const char *str;
314
315         reply = dbus_message_new_method_return(message);
316         if (reply == NULL)
317                 return NULL;
318
319         dbus_message_iter_init_append(reply, &array);
320
321         connman_dbus_dict_open(&array, &dict);
322
323         str = state2string(technology->state);
324         if (str != NULL)
325                 connman_dbus_dict_append_basic(&dict, "State",
326                                                 DBUS_TYPE_STRING, &str);
327
328         str = get_name(technology->type);
329         if (str != NULL)
330                 connman_dbus_dict_append_basic(&dict, "Name",
331                                                 DBUS_TYPE_STRING, &str);
332
333         str = __connman_service_type2string(technology->type);
334         if (str != NULL)
335                 connman_dbus_dict_append_basic(&dict, "Type",
336                                                 DBUS_TYPE_STRING, &str);
337
338         connman_dbus_dict_append_basic(&dict, "Tethering",
339                                         DBUS_TYPE_BOOLEAN,
340                                         &technology->tethering);
341
342         if (technology->tethering_ident != NULL)
343                 connman_dbus_dict_append_basic(&dict, "TetheringIdentifier",
344                                                 DBUS_TYPE_STRING,
345                                                 &technology->tethering_ident);
346
347         if (technology->tethering_passphrase != NULL)
348                 connman_dbus_dict_append_basic(&dict, "TetheringPassphrase",
349                                                 DBUS_TYPE_STRING,
350                                                 &technology->tethering_passphrase);
351
352         connman_dbus_dict_close(&array, &dict);
353
354         return reply;
355 }
356
357 static DBusMessage *set_property(DBusConnection *conn,
358                                         DBusMessage *msg, void *data)
359 {
360         struct connman_technology *technology = data;
361         DBusMessageIter iter, value;
362         const char *name;
363         int type;
364
365         DBG("conn %p", conn);
366
367         if (dbus_message_iter_init(msg, &iter) == FALSE)
368                 return __connman_error_invalid_arguments(msg);
369
370         dbus_message_iter_get_basic(&iter, &name);
371         dbus_message_iter_next(&iter);
372         dbus_message_iter_recurse(&iter, &value);
373
374         type = dbus_message_iter_get_arg_type(&value);
375
376         DBG("property %s", name);
377
378         if (g_str_equal(name, "Tethering") == TRUE) {
379                 int err;
380                 connman_bool_t tethering;
381                 const char *bridge;
382
383                 if (type != DBUS_TYPE_BOOLEAN)
384                         return __connman_error_invalid_arguments(msg);
385
386                 dbus_message_iter_get_basic(&value, &tethering);
387
388                 if (technology->tethering == tethering)
389                         return __connman_error_in_progress(msg);
390
391                 bridge = __connman_tethering_get_bridge();
392                 if (bridge == NULL)
393                         return __connman_error_not_supported(msg);
394
395                 err = set_tethering(technology, bridge, tethering);
396                 if (err < 0)
397                         return __connman_error_failed(msg, -err);
398
399         } else if (g_str_equal(name, "TetheringIdentifier") == TRUE) {
400                 const char *str;
401
402                 dbus_message_iter_get_basic(&value, &str);
403
404                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
405                         return __connman_error_not_supported(msg);
406
407                 technology->tethering_ident = g_strdup(str);
408         } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) {
409                 const char *str;
410
411                 dbus_message_iter_get_basic(&value, &str);
412
413                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
414                         return __connman_error_not_supported(msg);
415
416                 if (strlen(str) < 8)
417                         return __connman_error_invalid_arguments(msg);
418
419                 technology->tethering_passphrase = g_strdup(str);
420         } else
421                 return __connman_error_invalid_property(msg);
422
423         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
424 }
425
426 static GDBusMethodTable technology_methods[] = {
427         { "GetProperties", "",   "a{sv}", get_properties },
428         { "SetProperty",   "sv", "",      set_property   },
429         { },
430 };
431
432 static GDBusSignalTable technology_signals[] = {
433         { "PropertyChanged", "sv" },
434         { },
435 };
436
437 static struct connman_technology *technology_find(enum connman_service_type type)
438 {
439         GSList *list;
440
441         DBG("type %d", type);
442
443         for (list = technology_list; list; list = list->next) {
444                 struct connman_technology *technology = list->data;
445
446                 if (technology->type == type)
447                         return technology;
448         }
449
450         return NULL;
451 }
452
453 static struct connman_technology *technology_get(enum connman_service_type type)
454 {
455         struct connman_technology *technology;
456         const char *str;
457         GSList *list;
458
459         DBG("type %d", type);
460
461         technology = technology_find(type);
462         if (technology != NULL) {
463                 g_atomic_int_inc(&technology->refcount);
464                 goto done;
465         }
466
467         str = __connman_service_type2string(type);
468         if (str == NULL)
469                 return NULL;
470
471         technology = g_try_new0(struct connman_technology, 1);
472         if (technology == NULL)
473                 return NULL;
474
475         technology->refcount = 1;
476
477         technology->type = type;
478         technology->path = g_strdup_printf("%s/technology/%s",
479                                                         CONNMAN_PATH, str);
480
481         technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
482                                                         NULL, free_rfkill);
483         technology->device_list = NULL;
484
485         technology->pending_reply = NULL;
486         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
487
488         __connman_storage_load_technology(technology);
489
490         if (g_dbus_register_interface(connection, technology->path,
491                                         CONNMAN_TECHNOLOGY_INTERFACE,
492                                         technology_methods, technology_signals,
493                                         NULL, technology, NULL) == FALSE) {
494                 connman_error("Failed to register %s", technology->path);
495                 g_free(technology);
496                 return NULL;
497         }
498
499         technology_list = g_slist_append(technology_list, technology);
500
501         technologies_changed();
502
503         if (technology->driver != NULL)
504                 goto done;
505
506         for (list = driver_list; list; list = list->next) {
507                 struct connman_technology_driver *driver = list->data;
508
509                 DBG("driver %p name %s", driver, driver->name);
510
511                 if (driver->type != technology->type)
512                         continue;
513
514                 if (driver->probe(technology) == 0) {
515                         technology->driver = driver;
516                         break;
517                 }
518         }
519
520 done:
521         DBG("technology %p", technology);
522
523         return technology;
524 }
525
526 static void technology_put(struct connman_technology *technology)
527 {
528         DBG("technology %p", technology);
529
530         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
531                 return;
532
533         if (technology->driver) {
534                 technology->driver->remove(technology);
535                 technology->driver = NULL;
536         }
537
538         technology_list = g_slist_remove(technology_list, technology);
539
540         technologies_changed();
541
542         g_dbus_unregister_interface(connection, technology->path,
543                                                 CONNMAN_TECHNOLOGY_INTERFACE);
544
545         g_slist_free(technology->device_list);
546         g_hash_table_destroy(technology->rfkill_list);
547
548         g_free(technology->path);
549         g_free(technology->regdom);
550         g_free(technology);
551 }
552
553 void __connman_technology_add_interface(enum connman_service_type type,
554                                 int index, const char *name, const char *ident)
555 {
556         struct connman_technology *technology;
557
558         switch (type) {
559         case CONNMAN_SERVICE_TYPE_UNKNOWN:
560         case CONNMAN_SERVICE_TYPE_SYSTEM:
561                 return;
562         case CONNMAN_SERVICE_TYPE_ETHERNET:
563         case CONNMAN_SERVICE_TYPE_WIFI:
564         case CONNMAN_SERVICE_TYPE_WIMAX:
565         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
566         case CONNMAN_SERVICE_TYPE_CELLULAR:
567         case CONNMAN_SERVICE_TYPE_GPS:
568         case CONNMAN_SERVICE_TYPE_VPN:
569         case CONNMAN_SERVICE_TYPE_GADGET:
570                 break;
571         }
572
573         connman_info("Create interface %s [ %s ]", name,
574                                 __connman_service_type2string(type));
575
576         technology = technology_get(type);
577
578         if (technology == NULL || technology->driver == NULL
579                         || technology->driver->add_interface == NULL)
580                 return;
581
582         technology->driver->add_interface(technology,
583                                         index, name, ident);
584 }
585
586 void __connman_technology_remove_interface(enum connman_service_type type,
587                                 int index, const char *name, const char *ident)
588 {
589         struct connman_technology *technology;
590
591         switch (type) {
592         case CONNMAN_SERVICE_TYPE_UNKNOWN:
593         case CONNMAN_SERVICE_TYPE_SYSTEM:
594                 return;
595         case CONNMAN_SERVICE_TYPE_ETHERNET:
596         case CONNMAN_SERVICE_TYPE_WIFI:
597         case CONNMAN_SERVICE_TYPE_WIMAX:
598         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
599         case CONNMAN_SERVICE_TYPE_CELLULAR:
600         case CONNMAN_SERVICE_TYPE_GPS:
601         case CONNMAN_SERVICE_TYPE_VPN:
602         case CONNMAN_SERVICE_TYPE_GADGET:
603                 break;
604         }
605
606         connman_info("Remove interface %s [ %s ]", name,
607                                 __connman_service_type2string(type));
608
609         technology = technology_find(type);
610
611         if (technology == NULL || technology->driver == NULL)
612                 return;
613
614         if (technology->driver->remove_interface)
615                 technology->driver->remove_interface(technology, index);
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         if (g_atomic_int_get(&technology->blocked))
635                 goto done;
636
637         technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
638
639         state_changed(technology);
640
641 done:
642
643         technology->device_list = g_slist_append(technology->device_list,
644                                                                 device);
645
646         return 0;
647 }
648
649 int __connman_technology_remove_device(struct connman_device *device)
650 {
651         struct connman_technology *technology;
652         enum connman_service_type type;
653
654         DBG("device %p", device);
655
656         type = __connman_device_get_service_type(device);
657         __connman_notifier_unregister(type);
658
659         technology = technology_find(type);
660         if (technology == NULL)
661                 return -ENXIO;
662
663         technology->device_list = g_slist_remove(technology->device_list,
664                                                                 device);
665         if (technology->device_list == NULL) {
666                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
667                 state_changed(technology);
668         }
669
670         return 0;
671 }
672
673 static gboolean technology_pending_reply(gpointer user_data)
674 {
675         struct connman_technology *technology = user_data;
676         DBusMessage *reply;
677
678         /* Power request timedout, send ETIMEDOUT. */
679         if (technology->pending_reply != NULL) {
680                 reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
681                 if (reply != NULL)
682                         g_dbus_send_message(connection, reply);
683
684                 dbus_message_unref(technology->pending_reply);
685                 technology->pending_reply = NULL;
686                 technology->pending_timeout = 0;
687         }
688
689         return FALSE;
690 }
691
692 int __connman_technology_enabled(enum connman_service_type type)
693 {
694         struct connman_technology *technology;
695
696         technology = technology_find(type);
697         if (technology == NULL)
698                 return -ENXIO;
699
700         if (g_atomic_int_get(&technology->blocked))
701                 return -ERFKILL;
702
703         __connman_notifier_enable(type);
704
705         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
706                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
707                 state_changed(technology);
708         }
709
710         if (technology->pending_reply != NULL) {
711                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
712                 dbus_message_unref(technology->pending_reply);
713                 technology->pending_reply = NULL;
714                 technology->pending_timeout = 0;
715         }
716
717         return 0;
718 }
719
720 int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg)
721 {
722         struct connman_technology *technology;
723         GSList *list;
724         int err = 0;
725         int ret = -ENODEV;
726         DBusMessage *reply;
727
728         DBG("type %d enable", type);
729
730         technology = technology_find(type);
731         if (technology == NULL) {
732                 err = -ENXIO;
733                 goto done;
734         }
735
736         if (technology->pending_reply != NULL) {
737                 err = -EBUSY;
738                 goto done;
739         }
740
741         if (msg != NULL) {
742                 technology->pending_reply = dbus_message_ref(msg);
743                 /*
744                  * This is a bit of a trick. When msg is not NULL it means
745                  * thats technology_enable was invoked from the manager API. Hence we save
746                  * the state here.
747                  */
748                 technology->enable_persistent = TRUE;
749                 __connman_storage_save_technology(technology);
750         }
751
752         for (list = technology->device_list; list; list = list->next) {
753                 struct connman_device *device = list->data;
754
755                 err = __connman_device_enable_persistent(device);
756                 /*
757                  * err = 0 : Device was enabled right away.
758                  * If atleast one device gets enabled, we consider
759                  * the technology to be enabled.
760                  */
761                 if (err == 0)
762                         ret = 0;
763         }
764
765 done:
766         if (ret == 0)
767                 return ret;
768
769         if (msg != NULL) {
770                 if (err == -EINPROGRESS)
771                         technology->pending_timeout = g_timeout_add_seconds(10,
772                                         technology_pending_reply, technology);
773                 else {
774                         reply = __connman_error_failed(msg, -err);
775                         if (reply != NULL)
776                                 g_dbus_send_message(connection, reply);
777                 }
778         }
779
780         return err;
781 }
782
783 int __connman_technology_disabled(enum connman_service_type type)
784 {
785         struct connman_technology *technology;
786         GSList *list;
787
788         technology = technology_find(type);
789         if (technology == NULL)
790                 return -ENXIO;
791
792         if (technology->pending_reply != NULL) {
793                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
794                 dbus_message_unref(technology->pending_reply);
795                 technology->pending_reply = NULL;
796         }
797
798         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
799                 __connman_notifier_disable(type);
800
801                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
802                 state_changed(technology);
803         }
804
805         for (list = technology->device_list; list; list = list->next) {
806                 struct connman_device *device = list->data;
807
808                 if (__connman_device_get_blocked(device) == FALSE)
809                         return 0;
810         }
811
812         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
813         state_changed(technology);
814
815         return 0;
816 }
817
818 int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg)
819 {
820         struct connman_technology *technology;
821         GSList *list;
822         int err = 0;
823         int ret = -ENODEV;
824         DBusMessage *reply;
825
826         DBG("type %d disable", type);
827
828         technology = technology_find(type);
829         if (technology == NULL) {
830                 err = -ENXIO;
831                 goto done;
832         }
833
834         if (technology->pending_reply != NULL) {
835                 err = -EBUSY;
836                 goto done;
837         }
838
839         if (msg != NULL) {
840                 technology->pending_reply = dbus_message_ref(msg);
841                 technology->enable_persistent = FALSE;
842                 __connman_storage_save_technology(technology);
843         }
844
845         for (list = technology->device_list; list; list = list->next) {
846                 struct connman_device *device = list->data;
847
848                 err = __connman_device_disable_persistent(device);
849                 if (err == 0)
850                         ret = 0;
851         }
852
853 done:
854         if (ret == 0)
855                 return ret;
856
857         if (msg != NULL) {
858                 if (err == -EINPROGRESS)
859                         technology->pending_timeout = g_timeout_add_seconds(10,
860                                         technology_pending_reply, technology);
861                 else {
862                         reply = __connman_error_failed(msg, -err);
863                         if (reply != NULL)
864                                 g_dbus_send_message(connection, reply);
865                 }
866         }
867
868         return err;
869 }
870
871 int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
872 {
873         GSList *list;
874         int err = -EINVAL;
875
876         DBG("offlinemode %s", offlinemode ? "On" : "Off");
877
878         /* Traverse technology list, enable/disable each technology. */
879         for (list = technology_list; list; list = list->next) {
880                 struct connman_technology *technology = list->data;
881
882                 if (offlinemode)
883                         err = __connman_technology_disable(technology->type, NULL);
884                 if (!offlinemode && technology->enable_persistent)
885                         err = __connman_technology_enable(technology->type, NULL);
886         }
887
888         if (err == 0 || err == -EINPROGRESS) {
889                 __connman_profile_set_offlinemode(offlinemode);
890                 __connman_profile_save_default();
891
892                 __connman_notifier_offlinemode(offlinemode);
893         }
894
895         return err;
896 }
897
898 static void technology_blocked(struct connman_technology *technology,
899                                 connman_bool_t blocked)
900 {
901         GSList *list;
902
903         for (list = technology->device_list; list; list = list->next) {
904                 struct connman_device *device = list->data;
905
906                 __connman_device_set_blocked(device, blocked);
907         }
908 }
909
910 int __connman_technology_add_rfkill(unsigned int index,
911                                         enum connman_service_type type,
912                                                 connman_bool_t softblock,
913                                                 connman_bool_t hardblock)
914 {
915         struct connman_technology *technology;
916         struct connman_rfkill *rfkill;
917         connman_bool_t blocked;
918
919         DBG("index %u type %d soft %u hard %u", index, type,
920                                                         softblock, hardblock);
921
922         technology = technology_get(type);
923         if (technology == NULL)
924                 return -ENXIO;
925
926         rfkill = g_try_new0(struct connman_rfkill, 1);
927         if (rfkill == NULL)
928                 return -ENOMEM;
929
930         rfkill->index = index;
931         rfkill->type = type;
932         rfkill->softblock = softblock;
933         rfkill->hardblock = hardblock;
934
935         g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
936
937         blocked = (softblock || hardblock) ? TRUE : FALSE;
938         if (blocked == FALSE)
939                 return 0;
940
941         if (g_atomic_int_exchange_and_add(&technology->blocked, 1) == 0) {
942                 technology_blocked(technology, TRUE);
943
944                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
945                 state_changed(technology);
946         }
947
948         return 0;
949 }
950
951 int __connman_technology_update_rfkill(unsigned int index,
952                                         enum connman_service_type type,
953                                                 connman_bool_t softblock,
954                                                 connman_bool_t hardblock)
955 {
956         struct connman_technology *technology;
957         struct connman_rfkill *rfkill;
958         connman_bool_t blocked, old_blocked;
959
960         DBG("index %u soft %u hard %u", index, softblock, hardblock);
961
962         technology = technology_find(type);
963         if (technology == NULL)
964                 return -ENXIO;
965
966         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
967         if (rfkill == NULL)
968                 return -ENXIO;
969
970         old_blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
971         blocked = (softblock || hardblock) ? TRUE : FALSE;
972
973         rfkill->softblock = softblock;
974         rfkill->hardblock = hardblock;
975
976         if (blocked == old_blocked)
977                 return 0;
978
979         if (blocked) {
980                 guint n_blocked;
981
982                 n_blocked =
983                         g_atomic_int_exchange_and_add(&technology->blocked, 1);
984                 if (n_blocked != g_hash_table_size(technology->rfkill_list) - 1)
985                         return 0;
986
987                 technology_blocked(technology, blocked);
988                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
989                 state_changed(technology);
990         } else {
991                 if (g_atomic_int_dec_and_test(&technology->blocked) == FALSE)
992                         return 0;
993
994                 technology_blocked(technology, blocked);
995                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
996                 state_changed(technology);
997         }
998
999         return 0;
1000 }
1001
1002 int __connman_technology_remove_rfkill(unsigned int index,
1003                                         enum connman_service_type type)
1004 {
1005         struct connman_technology *technology;
1006         struct connman_rfkill *rfkill;
1007         connman_bool_t blocked;
1008
1009         DBG("index %u", index);
1010
1011         technology = technology_find(type);
1012         if (technology == NULL)
1013                 return -ENXIO;
1014
1015         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
1016         if (rfkill == NULL)
1017                 return -ENXIO;
1018
1019         blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
1020
1021         g_hash_table_remove(technology->rfkill_list, &index);
1022
1023         if (blocked &&
1024                 g_atomic_int_dec_and_test(&technology->blocked) == TRUE) {
1025                 technology_blocked(technology, FALSE);
1026                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
1027                 state_changed(technology);
1028         }
1029
1030         return 0;
1031 }
1032
1033 connman_bool_t __connman_technology_get_blocked(enum connman_service_type type)
1034 {
1035         struct connman_technology *technology;
1036
1037         technology = technology_find(type);
1038         if (technology == NULL)
1039                 return FALSE;
1040
1041         if (g_atomic_int_get(&technology->blocked))
1042                 return TRUE;
1043
1044         return FALSE;
1045 }
1046
1047 static int technology_load(struct connman_technology *technology)
1048 {
1049         const char *ident = __connman_profile_active_ident();
1050         GKeyFile *keyfile;
1051         gchar *identifier;
1052         GError *error = NULL;
1053         connman_bool_t enable;
1054
1055         DBG("technology %p", technology);
1056
1057         keyfile = __connman_storage_open_profile(ident);
1058         if (keyfile == NULL)
1059                 return 0;
1060
1061         identifier = g_strdup_printf("%s", get_name(technology->type));
1062         if (identifier == NULL)
1063                 goto done;
1064
1065         enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
1066         if (error == NULL)
1067                 technology->enable_persistent = enable;
1068         else
1069                 technology->enable_persistent = FALSE;
1070
1071         g_clear_error(&error);
1072 done:
1073         g_free(identifier);
1074
1075         __connman_storage_close_profile(ident, keyfile, FALSE);
1076
1077         return 0;
1078 }
1079
1080 static int technology_save(struct connman_technology *technology)
1081 {
1082         const char *ident = __connman_profile_active_ident();
1083         GKeyFile *keyfile;
1084         gchar *identifier;
1085
1086         DBG("technology %p", technology);
1087
1088         keyfile = __connman_storage_open_profile(ident);
1089         if (keyfile == NULL)
1090                 return 0;
1091
1092         identifier = g_strdup_printf("%s", get_name(technology->type));
1093         if (identifier == NULL)
1094                 goto done;
1095
1096         g_key_file_set_boolean(keyfile, identifier, "Enable",
1097                                 technology->enable_persistent);
1098
1099 done:
1100         g_free(identifier);
1101
1102         __connman_storage_close_profile(ident, keyfile, TRUE);
1103
1104         return 0;
1105 }
1106
1107 static struct connman_storage tech_storage = {
1108         .name           = "technology",
1109         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1110         .tech_load      = technology_load,
1111         .tech_save      = technology_save,
1112 };
1113
1114 int __connman_technology_init(void)
1115 {
1116         DBG("");
1117
1118         connection = connman_dbus_get_connection();
1119
1120         return connman_storage_register(&tech_storage);
1121 }
1122
1123 void __connman_technology_cleanup(void)
1124 {
1125         DBG("");
1126
1127         dbus_connection_unref(connection);
1128
1129         connman_storage_unregister(&tech_storage);
1130 }