4d8eb03dfcda6036134b3c19168f0b4e34094caa
[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 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_ENABLED   = 2,
48         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 3,
49 };
50
51 struct connman_technology {
52         gint refcount;
53         enum connman_service_type type;
54         enum connman_technology_state state;
55         char *path;
56         GHashTable *rfkill_list;
57         GSList *device_list;
58         gint enabled;
59         gint blocked;
60         char *regdom;
61
62         connman_bool_t tethering;
63         char *tethering_ident;
64         char *tethering_passphrase;
65
66         connman_bool_t enable_persistent; /* Save the tech state */
67
68         struct connman_technology_driver *driver;
69         void *driver_data;
70
71         DBusMessage *pending_reply;
72         guint pending_timeout;
73 };
74
75 static GSList *driver_list = NULL;
76
77 static gint compare_priority(gconstpointer a, gconstpointer b)
78 {
79         const struct connman_technology_driver *driver1 = a;
80         const struct connman_technology_driver *driver2 = b;
81
82         return driver2->priority - driver1->priority;
83 }
84
85 /**
86  * connman_technology_driver_register:
87  * @driver: Technology driver definition
88  *
89  * Register a new technology driver
90  *
91  * Returns: %0 on success
92  */
93 int connman_technology_driver_register(struct connman_technology_driver *driver)
94 {
95         GSList *list;
96         struct connman_technology *technology;
97
98         DBG("driver %p name %s", driver, driver->name);
99
100         driver_list = g_slist_insert_sorted(driver_list, driver,
101                                                         compare_priority);
102
103         for (list = technology_list; list; list = list->next) {
104                 technology = list->data;
105
106                 if (technology->driver != NULL)
107                         continue;
108
109                 if (technology->type == driver->type)
110                         technology->driver = driver;
111         }
112
113         return 0;
114 }
115
116 /**
117  * connman_technology_driver_unregister:
118  * @driver: Technology driver definition
119  *
120  * Remove a previously registered technology driver
121  */
122 void connman_technology_driver_unregister(struct connman_technology_driver *driver)
123 {
124         GSList *list;
125         struct connman_technology *technology;
126
127         DBG("driver %p name %s", driver, driver->name);
128
129         for (list = technology_list; list; list = list->next) {
130                 technology = list->data;
131
132                 if (technology->driver == NULL)
133                         continue;
134
135                 if (technology->type == driver->type) {
136                         technology->driver->remove(technology);
137                         technology->driver = NULL;
138                 }
139         }
140
141         driver_list = g_slist_remove(driver_list, driver);
142 }
143
144 static void tethering_changed(struct connman_technology *technology)
145 {
146         connman_bool_t tethering = technology->tethering;
147
148         connman_dbus_property_changed_basic(technology->path,
149                                 CONNMAN_TECHNOLOGY_INTERFACE, "Tethering",
150                                                 DBUS_TYPE_BOOLEAN, &tethering);
151 }
152
153 void connman_technology_tethering_notify(struct connman_technology *technology,
154                                                         connman_bool_t enabled)
155 {
156         DBG("technology %p enabled %u", technology, enabled);
157
158         if (technology->tethering == enabled)
159                 return;
160
161         technology->tethering = enabled;
162
163         tethering_changed(technology);
164
165         if (enabled == TRUE)
166                 __connman_tethering_set_enabled();
167         else
168                 __connman_tethering_set_disabled();
169 }
170
171 static int set_tethering(struct connman_technology *technology,
172                                 const char *bridge, connman_bool_t enabled)
173 {
174         const char *ident, *passphrase;
175
176         ident = technology->tethering_ident;
177         passphrase = technology->tethering_passphrase;
178
179         if (technology->driver == NULL ||
180                         technology->driver->set_tethering == NULL)
181                 return -EOPNOTSUPP;
182
183         if (technology->type == CONNMAN_SERVICE_TYPE_WIFI &&
184             (ident == NULL || passphrase == NULL))
185                 return -EINVAL;
186
187         return technology->driver->set_tethering(technology, ident, passphrase,
188                                                         bridge, enabled);
189 }
190
191 void connman_technology_regdom_notify(struct connman_technology *technology,
192                                                         const char *alpha2)
193 {
194         DBG("");
195
196         if (alpha2 == NULL)
197                 connman_error("Failed to set regulatory domain");
198         else
199                 DBG("Regulatory domain set to %s", alpha2);
200
201         g_free(technology->regdom);
202         technology->regdom = g_strdup(alpha2);
203 }
204
205 int connman_technology_set_regdom(const char *alpha2)
206 {
207         GSList *list;
208
209         for (list = technology_list; list; list = list->next) {
210                 struct connman_technology *technology = list->data;
211
212                 if (technology->driver == NULL)
213                         continue;
214
215                 if (technology->driver->set_regdom)
216                         technology->driver->set_regdom(technology, alpha2);
217         }
218
219         return 0;
220 }
221
222 static void free_rfkill(gpointer data)
223 {
224         struct connman_rfkill *rfkill = data;
225
226         g_free(rfkill);
227 }
228
229 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
230 {
231         GSList *list;
232
233         for (list = technology_list; list; list = list->next) {
234                 struct connman_technology *technology = list->data;
235
236                 if (technology->path == NULL)
237                         continue;
238
239                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
240                                                         &technology->path);
241         }
242 }
243
244 static void technologies_changed(void)
245 {
246         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
247                         CONNMAN_MANAGER_INTERFACE, "Technologies",
248                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
249 }
250
251 static const char *state2string(enum connman_technology_state state)
252 {
253         switch (state) {
254         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
255                 break;
256         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
257                 return "offline";
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->pending_reply = NULL;
483         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
484
485         __connman_storage_load_technology(technology);
486
487         if (g_dbus_register_interface(connection, technology->path,
488                                         CONNMAN_TECHNOLOGY_INTERFACE,
489                                         technology_methods, technology_signals,
490                                         NULL, technology, NULL) == FALSE) {
491                 connman_error("Failed to register %s", technology->path);
492                 g_free(technology);
493                 return NULL;
494         }
495
496         technology_list = g_slist_append(technology_list, technology);
497
498         technologies_changed();
499
500         if (technology->driver != NULL)
501                 goto done;
502
503         for (list = driver_list; list; list = list->next) {
504                 struct connman_technology_driver *driver = list->data;
505
506                 DBG("driver %p name %s", driver, driver->name);
507
508                 if (driver->type != technology->type)
509                         continue;
510
511                 if (driver->probe(technology) == 0) {
512                         technology->driver = driver;
513                         break;
514                 }
515         }
516
517 done:
518         DBG("technology %p", technology);
519
520         return technology;
521 }
522
523 static void technology_put(struct connman_technology *technology)
524 {
525         DBG("technology %p", technology);
526
527         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
528                 return;
529
530         if (technology->driver) {
531                 technology->driver->remove(technology);
532                 technology->driver = NULL;
533         }
534
535         technology_list = g_slist_remove(technology_list, technology);
536
537         technologies_changed();
538
539         g_dbus_unregister_interface(connection, technology->path,
540                                                 CONNMAN_TECHNOLOGY_INTERFACE);
541
542         g_slist_free(technology->device_list);
543         g_hash_table_destroy(technology->rfkill_list);
544
545         g_free(technology->path);
546         g_free(technology->regdom);
547         g_free(technology);
548 }
549
550 void __connman_technology_add_interface(enum connman_service_type type,
551                                 int index, const char *name, const char *ident)
552 {
553         struct connman_technology *technology;
554
555         switch (type) {
556         case CONNMAN_SERVICE_TYPE_UNKNOWN:
557         case CONNMAN_SERVICE_TYPE_SYSTEM:
558                 return;
559         case CONNMAN_SERVICE_TYPE_ETHERNET:
560         case CONNMAN_SERVICE_TYPE_WIFI:
561         case CONNMAN_SERVICE_TYPE_WIMAX:
562         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
563         case CONNMAN_SERVICE_TYPE_CELLULAR:
564         case CONNMAN_SERVICE_TYPE_GPS:
565         case CONNMAN_SERVICE_TYPE_VPN:
566         case CONNMAN_SERVICE_TYPE_GADGET:
567                 break;
568         }
569
570         connman_info("Create interface %s [ %s ]", name,
571                                 __connman_service_type2string(type));
572
573         technology = technology_get(type);
574
575         if (technology == NULL || technology->driver == NULL
576                         || technology->driver->add_interface == NULL)
577                 return;
578
579         technology->driver->add_interface(technology,
580                                         index, name, ident);
581 }
582
583 void __connman_technology_remove_interface(enum connman_service_type type,
584                                 int index, const char *name, const char *ident)
585 {
586         struct connman_technology *technology;
587
588         switch (type) {
589         case CONNMAN_SERVICE_TYPE_UNKNOWN:
590         case CONNMAN_SERVICE_TYPE_SYSTEM:
591                 return;
592         case CONNMAN_SERVICE_TYPE_ETHERNET:
593         case CONNMAN_SERVICE_TYPE_WIFI:
594         case CONNMAN_SERVICE_TYPE_WIMAX:
595         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
596         case CONNMAN_SERVICE_TYPE_CELLULAR:
597         case CONNMAN_SERVICE_TYPE_GPS:
598         case CONNMAN_SERVICE_TYPE_VPN:
599         case CONNMAN_SERVICE_TYPE_GADGET:
600                 break;
601         }
602
603         connman_info("Remove interface %s [ %s ]", name,
604                                 __connman_service_type2string(type));
605
606         technology = technology_find(type);
607
608         if (technology == NULL || technology->driver == NULL)
609                 return;
610
611         if (technology->driver->remove_interface)
612                 technology->driver->remove_interface(technology, index);
613
614         technology_put(technology);
615 }
616
617 int __connman_technology_add_device(struct connman_device *device)
618 {
619         struct connman_technology *technology;
620         enum connman_service_type type;
621
622         DBG("device %p", device);
623
624         type = __connman_device_get_service_type(device);
625         __connman_notifier_register(type);
626
627         technology = technology_get(type);
628         if (technology == NULL)
629                 return -ENXIO;
630
631         if (g_atomic_int_get(&technology->blocked))
632                 goto done;
633
634 done:
635
636         technology->device_list = g_slist_append(technology->device_list,
637                                                                 device);
638
639         return 0;
640 }
641
642 int __connman_technology_remove_device(struct connman_device *device)
643 {
644         struct connman_technology *technology;
645         enum connman_service_type type;
646
647         DBG("device %p", device);
648
649         type = __connman_device_get_service_type(device);
650         __connman_notifier_unregister(type);
651
652         technology = technology_find(type);
653         if (technology == NULL)
654                 return -ENXIO;
655
656         technology->device_list = g_slist_remove(technology->device_list,
657                                                                 device);
658         if (technology->device_list == NULL) {
659                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
660                 state_changed(technology);
661         }
662
663         return 0;
664 }
665
666 static gboolean technology_pending_reply(gpointer user_data)
667 {
668         struct connman_technology *technology = user_data;
669         DBusMessage *reply;
670
671         /* Power request timedout, send ETIMEDOUT. */
672         if (technology->pending_reply != NULL) {
673                 reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
674                 if (reply != NULL)
675                         g_dbus_send_message(connection, reply);
676
677                 dbus_message_unref(technology->pending_reply);
678                 technology->pending_reply = NULL;
679                 technology->pending_timeout = 0;
680         }
681
682         return FALSE;
683 }
684
685 int __connman_technology_enabled(enum connman_service_type type)
686 {
687         struct connman_technology *technology;
688
689         technology = technology_find(type);
690         if (technology == NULL)
691                 return -ENXIO;
692
693         if (g_atomic_int_get(&technology->blocked))
694                 return -ERFKILL;
695
696         __connman_notifier_enable(type);
697
698         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
699                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
700                 state_changed(technology);
701         }
702
703         if (technology->pending_reply != NULL) {
704                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
705                 dbus_message_unref(technology->pending_reply);
706                 technology->pending_reply = NULL;
707                 technology->pending_timeout = 0;
708         }
709
710         return 0;
711 }
712
713 int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg)
714 {
715         struct connman_technology *technology;
716         GSList *list;
717         int err = 0;
718         int ret = -ENODEV;
719         DBusMessage *reply;
720
721         DBG("type %d enable", type);
722
723         technology = technology_find(type);
724         if (technology == NULL) {
725                 err = -ENXIO;
726                 goto done;
727         }
728
729         if (technology->pending_reply != NULL) {
730                 err = -EBUSY;
731                 goto done;
732         }
733
734         if (msg != NULL) {
735                 technology->pending_reply = dbus_message_ref(msg);
736                 /*
737                  * This is a bit of a trick. When msg is not NULL it means
738                  * thats technology_enable was invoked from the manager API. Hence we save
739                  * the state here.
740                  */
741                 technology->enable_persistent = TRUE;
742                 __connman_storage_save_technology(technology);
743         }
744
745         for (list = technology->device_list; list; list = list->next) {
746                 struct connman_device *device = list->data;
747
748                 err = __connman_device_enable_persistent(device);
749                 /*
750                  * err = 0 : Device was enabled right away.
751                  * If atleast one device gets enabled, we consider
752                  * the technology to be enabled.
753                  */
754                 if (err == 0)
755                         ret = 0;
756         }
757
758 done:
759         if (ret == 0)
760                 return ret;
761
762         if (msg != NULL) {
763                 if (err == -EINPROGRESS)
764                         technology->pending_timeout = g_timeout_add_seconds(10,
765                                         technology_pending_reply, technology);
766                 else {
767                         reply = __connman_error_failed(msg, -err);
768                         if (reply != NULL)
769                                 g_dbus_send_message(connection, reply);
770                 }
771         }
772
773         return err;
774 }
775
776 int __connman_technology_disabled(enum connman_service_type type)
777 {
778         struct connman_technology *technology;
779         GSList *list;
780
781         technology = technology_find(type);
782         if (technology == NULL)
783                 return -ENXIO;
784
785         if (technology->pending_reply != NULL) {
786                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
787                 dbus_message_unref(technology->pending_reply);
788                 technology->pending_reply = NULL;
789         }
790
791         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
792                 __connman_notifier_disable(type);
793                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
794                 state_changed(technology);
795         }
796
797         for (list = technology->device_list; list; list = list->next) {
798                 struct connman_device *device = list->data;
799
800                 if (__connman_device_get_blocked(device) == FALSE)
801                         return 0;
802         }
803
804         return 0;
805 }
806
807 int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg)
808 {
809         struct connman_technology *technology;
810         GSList *list;
811         int err = 0;
812         int ret = -ENODEV;
813         DBusMessage *reply;
814
815         DBG("type %d disable", type);
816
817         technology = technology_find(type);
818         if (technology == NULL) {
819                 err = -ENXIO;
820                 goto done;
821         }
822
823         if (technology->pending_reply != NULL) {
824                 err = -EBUSY;
825                 goto done;
826         }
827
828         if (msg != NULL) {
829                 technology->pending_reply = dbus_message_ref(msg);
830                 technology->enable_persistent = FALSE;
831                 __connman_storage_save_technology(technology);
832         }
833
834         for (list = technology->device_list; list; list = list->next) {
835                 struct connman_device *device = list->data;
836
837                 err = __connman_device_disable_persistent(device);
838                 if (err == 0)
839                         ret = 0;
840         }
841
842 done:
843         if (ret == 0)
844                 return ret;
845
846         if (msg != NULL) {
847                 if (err == -EINPROGRESS)
848                         technology->pending_timeout = g_timeout_add_seconds(10,
849                                         technology_pending_reply, technology);
850                 else {
851                         reply = __connman_error_failed(msg, -err);
852                         if (reply != NULL)
853                                 g_dbus_send_message(connection, reply);
854                 }
855         }
856
857         return err;
858 }
859
860 int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
861 {
862         GSList *list;
863         int err = -EINVAL;
864
865         DBG("offlinemode %s", offlinemode ? "On" : "Off");
866
867         /* Traverse technology list, enable/disable each technology. */
868         for (list = technology_list; list; list = list->next) {
869                 struct connman_technology *technology = list->data;
870
871                 if (offlinemode)
872                         err = __connman_technology_disable(technology->type, NULL);
873                 if (!offlinemode && technology->enable_persistent)
874                         err = __connman_technology_enable(technology->type, NULL);
875         }
876
877         if (err == 0 || err == -EINPROGRESS) {
878                 __connman_profile_set_offlinemode(offlinemode);
879                 __connman_profile_save_default();
880
881                 __connman_notifier_offlinemode(offlinemode);
882         }
883
884         return err;
885 }
886
887 static void technology_blocked(struct connman_technology *technology,
888                                 connman_bool_t blocked)
889 {
890         GSList *list;
891
892         for (list = technology->device_list; list; list = list->next) {
893                 struct connman_device *device = list->data;
894
895                 __connman_device_set_blocked(device, blocked);
896         }
897 }
898
899 int __connman_technology_add_rfkill(unsigned int index,
900                                         enum connman_service_type type,
901                                                 connman_bool_t softblock,
902                                                 connman_bool_t hardblock)
903 {
904         struct connman_technology *technology;
905         struct connman_rfkill *rfkill;
906         connman_bool_t blocked;
907
908         DBG("index %u type %d soft %u hard %u", index, type,
909                                                         softblock, hardblock);
910
911         technology = technology_get(type);
912         if (technology == NULL)
913                 return -ENXIO;
914
915         rfkill = g_try_new0(struct connman_rfkill, 1);
916         if (rfkill == NULL)
917                 return -ENOMEM;
918
919         rfkill->index = index;
920         rfkill->type = type;
921         rfkill->softblock = softblock;
922         rfkill->hardblock = hardblock;
923
924         g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
925
926         blocked = (softblock || hardblock) ? TRUE : FALSE;
927         if (blocked == FALSE)
928                 return 0;
929
930         if (g_atomic_int_exchange_and_add(&technology->blocked, 1) == 0) {
931                 technology_blocked(technology, TRUE);
932
933                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
934                 state_changed(technology);
935         }
936
937         return 0;
938 }
939
940 int __connman_technology_update_rfkill(unsigned int index,
941                                         enum connman_service_type type,
942                                                 connman_bool_t softblock,
943                                                 connman_bool_t hardblock)
944 {
945         struct connman_technology *technology;
946         struct connman_rfkill *rfkill;
947         connman_bool_t blocked, old_blocked;
948
949         DBG("index %u soft %u hard %u", index, softblock, hardblock);
950
951         technology = technology_find(type);
952         if (technology == NULL)
953                 return -ENXIO;
954
955         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
956         if (rfkill == NULL)
957                 return -ENXIO;
958
959         old_blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
960         blocked = (softblock || hardblock) ? TRUE : FALSE;
961
962         rfkill->softblock = softblock;
963         rfkill->hardblock = hardblock;
964
965         if (blocked == old_blocked)
966                 return 0;
967
968         if (blocked) {
969                 guint n_blocked;
970
971                 n_blocked =
972                         g_atomic_int_exchange_and_add(&technology->blocked, 1);
973                 if (n_blocked != g_hash_table_size(technology->rfkill_list) - 1)
974                         return 0;
975
976                 technology_blocked(technology, blocked);
977                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
978                 state_changed(technology);
979         } else {
980                 if (g_atomic_int_dec_and_test(&technology->blocked) == FALSE)
981                         return 0;
982
983                 technology_blocked(technology, blocked);
984                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
985                 state_changed(technology);
986         }
987
988         return 0;
989 }
990
991 int __connman_technology_remove_rfkill(unsigned int index,
992                                         enum connman_service_type type)
993 {
994         struct connman_technology *technology;
995         struct connman_rfkill *rfkill;
996         connman_bool_t blocked;
997
998         DBG("index %u", index);
999
1000         technology = technology_find(type);
1001         if (technology == NULL)
1002                 return -ENXIO;
1003
1004         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
1005         if (rfkill == NULL)
1006                 return -ENXIO;
1007
1008         blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
1009
1010         g_hash_table_remove(technology->rfkill_list, &index);
1011
1012         if (blocked &&
1013                 g_atomic_int_dec_and_test(&technology->blocked) == TRUE) {
1014                 technology_blocked(technology, FALSE);
1015                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
1016                 state_changed(technology);
1017         }
1018
1019         return 0;
1020 }
1021
1022 connman_bool_t __connman_technology_get_blocked(enum connman_service_type type)
1023 {
1024         struct connman_technology *technology;
1025
1026         technology = technology_find(type);
1027         if (technology == NULL)
1028                 return FALSE;
1029
1030         if (g_atomic_int_get(&technology->blocked))
1031                 return TRUE;
1032
1033         return FALSE;
1034 }
1035
1036 static int technology_load(struct connman_technology *technology)
1037 {
1038         const char *ident = __connman_profile_active_ident();
1039         GKeyFile *keyfile;
1040         gchar *identifier;
1041         GError *error = NULL;
1042         connman_bool_t enable;
1043
1044         DBG("technology %p", technology);
1045
1046         keyfile = __connman_storage_open_profile(ident);
1047         if (keyfile == NULL)
1048                 return 0;
1049
1050         identifier = g_strdup_printf("%s", get_name(technology->type));
1051         if (identifier == NULL)
1052                 goto done;
1053
1054         enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
1055         if (error == NULL)
1056                 technology->enable_persistent = enable;
1057         else
1058                 technology->enable_persistent = FALSE;
1059
1060         g_clear_error(&error);
1061 done:
1062         g_free(identifier);
1063
1064         __connman_storage_close_profile(ident, keyfile, FALSE);
1065
1066         return 0;
1067 }
1068
1069 static int technology_save(struct connman_technology *technology)
1070 {
1071         const char *ident = __connman_profile_active_ident();
1072         GKeyFile *keyfile;
1073         gchar *identifier;
1074
1075         DBG("technology %p", technology);
1076
1077         keyfile = __connman_storage_open_profile(ident);
1078         if (keyfile == NULL)
1079                 return 0;
1080
1081         identifier = g_strdup_printf("%s", get_name(technology->type));
1082         if (identifier == NULL)
1083                 goto done;
1084
1085         g_key_file_set_boolean(keyfile, identifier, "Enable",
1086                                 technology->enable_persistent);
1087
1088 done:
1089         g_free(identifier);
1090
1091         __connman_storage_close_profile(ident, keyfile, TRUE);
1092
1093         return 0;
1094 }
1095
1096 static struct connman_storage tech_storage = {
1097         .name           = "technology",
1098         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1099         .tech_load      = technology_load,
1100         .tech_save      = technology_save,
1101 };
1102
1103 int __connman_technology_init(void)
1104 {
1105         DBG("");
1106
1107         connection = connman_dbus_get_connection();
1108
1109         return connman_storage_register(&tech_storage);
1110 }
1111
1112 void __connman_technology_cleanup(void)
1113 {
1114         DBG("");
1115
1116         dbus_connection_unref(connection);
1117
1118         connman_storage_unregister(&tech_storage);
1119 }