technology: When disabling technology, disble tethering too
[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 static connman_bool_t global_offlinemode;
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_ENABLED   = 2,
50         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 3,
51 };
52
53 struct connman_technology {
54         gint refcount;
55         enum connman_service_type type;
56         enum connman_technology_state state;
57         char *path;
58         GHashTable *rfkill_list;
59         GSList *device_list;
60         gint enabled;
61         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                                 connman_bool_t enabled)
174 {
175         const char *ident, *passphrase, *bridge;
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         bridge = __connman_tethering_get_bridge();
185         if (bridge == NULL)
186                 return -EOPNOTSUPP;
187
188         if (technology->type == CONNMAN_SERVICE_TYPE_WIFI &&
189             (ident == NULL || passphrase == NULL))
190                 return -EINVAL;
191
192         return technology->driver->set_tethering(technology, ident, passphrase,
193                                                         bridge, enabled);
194 }
195
196 void connman_technology_regdom_notify(struct connman_technology *technology,
197                                                         const char *alpha2)
198 {
199         DBG("");
200
201         if (alpha2 == NULL)
202                 connman_error("Failed to set regulatory domain");
203         else
204                 DBG("Regulatory domain set to %s", alpha2);
205
206         g_free(technology->regdom);
207         technology->regdom = g_strdup(alpha2);
208 }
209
210 int connman_technology_set_regdom(const char *alpha2)
211 {
212         GSList *list;
213
214         for (list = technology_list; list; list = list->next) {
215                 struct connman_technology *technology = list->data;
216
217                 if (technology->driver == NULL)
218                         continue;
219
220                 if (technology->driver->set_regdom)
221                         technology->driver->set_regdom(technology, alpha2);
222         }
223
224         return 0;
225 }
226
227 static void free_rfkill(gpointer data)
228 {
229         struct connman_rfkill *rfkill = data;
230
231         g_free(rfkill);
232 }
233
234 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
235 {
236         GSList *list;
237
238         for (list = technology_list; list; list = list->next) {
239                 struct connman_technology *technology = list->data;
240
241                 if (technology->path == NULL)
242                         continue;
243
244                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
245                                                         &technology->path);
246         }
247 }
248
249 static void technologies_changed(void)
250 {
251         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
252                         CONNMAN_MANAGER_INTERFACE, "Technologies",
253                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
254 }
255
256 static const char *state2string(enum connman_technology_state state)
257 {
258         switch (state) {
259         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
260                 break;
261         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
262                 return "offline";
263         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
264                 return "enabled";
265         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
266                 return "connected";
267         }
268
269         return NULL;
270 }
271
272 static void state_changed(struct connman_technology *technology)
273 {
274         const char *str;
275
276         str = state2string(technology->state);
277         if (str == NULL)
278                 return;
279
280         connman_dbus_property_changed_basic(technology->path,
281                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
282                                                 DBUS_TYPE_STRING, &str);
283 }
284
285 static const char *get_name(enum connman_service_type type)
286 {
287         switch (type) {
288         case CONNMAN_SERVICE_TYPE_UNKNOWN:
289         case CONNMAN_SERVICE_TYPE_SYSTEM:
290         case CONNMAN_SERVICE_TYPE_GPS:
291         case CONNMAN_SERVICE_TYPE_VPN:
292         case CONNMAN_SERVICE_TYPE_GADGET:
293                 break;
294         case CONNMAN_SERVICE_TYPE_ETHERNET:
295                 return "Wired";
296         case CONNMAN_SERVICE_TYPE_WIFI:
297                 return "WiFi";
298         case CONNMAN_SERVICE_TYPE_WIMAX:
299                 return "WiMAX";
300         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
301                 return "Bluetooth";
302         case CONNMAN_SERVICE_TYPE_CELLULAR:
303                 return "3G";
304         }
305
306         return NULL;
307 }
308
309 static void load_state(struct connman_technology *technology)
310 {
311         GKeyFile *keyfile;
312         gchar *identifier;
313         GError *error = NULL;
314         connman_bool_t enable;
315
316         DBG("technology %p", technology);
317
318         keyfile = __connman_storage_load_global();
319         /* Fallback on disabling technology if file not found. */
320         if (keyfile == NULL) {
321                 technology->enable_persistent = FALSE;
322                 return;
323         }
324
325         identifier = g_strdup_printf("%s", get_name(technology->type));
326         if (identifier == NULL)
327                 goto done;
328
329         enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
330         if (error == NULL)
331                 technology->enable_persistent = enable;
332         else {
333                 technology->enable_persistent = FALSE;
334                 g_clear_error(&error);
335         }
336 done:
337         g_free(identifier);
338
339         g_key_file_free(keyfile);
340
341         return;
342 }
343
344 static void save_state(struct connman_technology *technology)
345 {
346         GKeyFile *keyfile;
347         gchar *identifier;
348
349         DBG("technology %p", technology);
350
351         keyfile = __connman_storage_load_global();
352         if (keyfile == NULL)
353                 keyfile = g_key_file_new();
354
355         identifier = g_strdup_printf("%s", get_name(technology->type));
356         if (identifier == NULL)
357                 goto done;
358
359         g_key_file_set_boolean(keyfile, identifier, "Enable",
360                                 technology->enable_persistent);
361
362 done:
363         g_free(identifier);
364
365         __connman_storage_save_global(keyfile);
366
367         g_key_file_free(keyfile);
368
369         return;
370 }
371
372 connman_bool_t __connman_technology_get_offlinemode(void)
373 {
374         return global_offlinemode;
375 }
376
377 static void connman_technology_save_offlinemode()
378 {
379         GKeyFile *keyfile;
380
381         keyfile = __connman_storage_load_global();
382         if (keyfile == NULL)
383                 keyfile = g_key_file_new();
384
385         g_key_file_set_boolean(keyfile, "global",
386                                         "OfflineMode", global_offlinemode);
387
388         __connman_storage_save_global(keyfile);
389
390         g_key_file_free(keyfile);
391
392         return;
393 }
394
395 static connman_bool_t connman_technology_load_offlinemode()
396 {
397         GKeyFile *keyfile;
398         GError *error = NULL;
399         connman_bool_t offlinemode;
400
401         /* If there is a error, we enable offlinemode */
402         keyfile = __connman_storage_load_global();
403         if (keyfile == NULL)
404                 return TRUE;
405
406         offlinemode = g_key_file_get_boolean(keyfile, "global",
407                                                 "OfflineMode", &error);
408         if (error != NULL) {
409                 offlinemode = TRUE;
410                 g_clear_error(&error);
411         }
412
413         g_key_file_free(keyfile);
414
415         return offlinemode;
416 }
417
418 static DBusMessage *get_properties(DBusConnection *conn,
419                                         DBusMessage *message, void *user_data)
420 {
421         struct connman_technology *technology = user_data;
422         DBusMessage *reply;
423         DBusMessageIter array, dict;
424         const char *str;
425
426         reply = dbus_message_new_method_return(message);
427         if (reply == NULL)
428                 return NULL;
429
430         dbus_message_iter_init_append(reply, &array);
431
432         connman_dbus_dict_open(&array, &dict);
433
434         str = state2string(technology->state);
435         if (str != NULL)
436                 connman_dbus_dict_append_basic(&dict, "State",
437                                                 DBUS_TYPE_STRING, &str);
438
439         str = get_name(technology->type);
440         if (str != NULL)
441                 connman_dbus_dict_append_basic(&dict, "Name",
442                                                 DBUS_TYPE_STRING, &str);
443
444         str = __connman_service_type2string(technology->type);
445         if (str != NULL)
446                 connman_dbus_dict_append_basic(&dict, "Type",
447                                                 DBUS_TYPE_STRING, &str);
448
449         connman_dbus_dict_append_basic(&dict, "Tethering",
450                                         DBUS_TYPE_BOOLEAN,
451                                         &technology->tethering);
452
453         if (technology->tethering_ident != NULL)
454                 connman_dbus_dict_append_basic(&dict, "TetheringIdentifier",
455                                                 DBUS_TYPE_STRING,
456                                                 &technology->tethering_ident);
457
458         if (technology->tethering_passphrase != NULL)
459                 connman_dbus_dict_append_basic(&dict, "TetheringPassphrase",
460                                                 DBUS_TYPE_STRING,
461                                                 &technology->tethering_passphrase);
462
463         connman_dbus_dict_close(&array, &dict);
464
465         return reply;
466 }
467
468 static DBusMessage *set_property(DBusConnection *conn,
469                                         DBusMessage *msg, void *data)
470 {
471         struct connman_technology *technology = data;
472         DBusMessageIter iter, value;
473         const char *name;
474         int type;
475
476         DBG("conn %p", conn);
477
478         if (dbus_message_iter_init(msg, &iter) == FALSE)
479                 return __connman_error_invalid_arguments(msg);
480
481         dbus_message_iter_get_basic(&iter, &name);
482         dbus_message_iter_next(&iter);
483         dbus_message_iter_recurse(&iter, &value);
484
485         type = dbus_message_iter_get_arg_type(&value);
486
487         DBG("property %s", name);
488
489         if (g_str_equal(name, "Tethering") == TRUE) {
490                 int err;
491                 connman_bool_t tethering;
492
493                 if (type != DBUS_TYPE_BOOLEAN)
494                         return __connman_error_invalid_arguments(msg);
495
496                 dbus_message_iter_get_basic(&value, &tethering);
497
498                 if (technology->tethering == tethering)
499                         return __connman_error_in_progress(msg);
500
501                 err = set_tethering(technology, tethering);
502                 if (err < 0)
503                         return __connman_error_failed(msg, -err);
504
505         } else if (g_str_equal(name, "TetheringIdentifier") == TRUE) {
506                 const char *str;
507
508                 dbus_message_iter_get_basic(&value, &str);
509
510                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
511                         return __connman_error_not_supported(msg);
512
513                 technology->tethering_ident = g_strdup(str);
514         } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) {
515                 const char *str;
516
517                 dbus_message_iter_get_basic(&value, &str);
518
519                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
520                         return __connman_error_not_supported(msg);
521
522                 if (strlen(str) < 8)
523                         return __connman_error_invalid_arguments(msg);
524
525                 technology->tethering_passphrase = g_strdup(str);
526         } else
527                 return __connman_error_invalid_property(msg);
528
529         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
530 }
531
532 static GDBusMethodTable technology_methods[] = {
533         { "GetProperties", "",   "a{sv}", get_properties },
534         { "SetProperty",   "sv", "",      set_property   },
535         { },
536 };
537
538 static GDBusSignalTable technology_signals[] = {
539         { "PropertyChanged", "sv" },
540         { },
541 };
542
543 static struct connman_technology *technology_find(enum connman_service_type type)
544 {
545         GSList *list;
546
547         DBG("type %d", type);
548
549         for (list = technology_list; list; list = list->next) {
550                 struct connman_technology *technology = list->data;
551
552                 if (technology->type == type)
553                         return technology;
554         }
555
556         return NULL;
557 }
558
559 static struct connman_technology *technology_get(enum connman_service_type type)
560 {
561         struct connman_technology *technology;
562         const char *str;
563         GSList *list;
564
565         DBG("type %d", type);
566
567         technology = technology_find(type);
568         if (technology != NULL) {
569                 g_atomic_int_inc(&technology->refcount);
570                 goto done;
571         }
572
573         str = __connman_service_type2string(type);
574         if (str == NULL)
575                 return NULL;
576
577         technology = g_try_new0(struct connman_technology, 1);
578         if (technology == NULL)
579                 return NULL;
580
581         technology->refcount = 1;
582
583         technology->type = type;
584         technology->path = g_strdup_printf("%s/technology/%s",
585                                                         CONNMAN_PATH, str);
586
587         technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
588                                                         NULL, free_rfkill);
589         technology->device_list = NULL;
590
591         technology->pending_reply = NULL;
592         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
593
594         load_state(technology);
595
596         if (g_dbus_register_interface(connection, technology->path,
597                                         CONNMAN_TECHNOLOGY_INTERFACE,
598                                         technology_methods, technology_signals,
599                                         NULL, technology, NULL) == FALSE) {
600                 connman_error("Failed to register %s", technology->path);
601                 g_free(technology);
602                 return NULL;
603         }
604
605         technology_list = g_slist_append(technology_list, technology);
606
607         technologies_changed();
608
609         if (technology->driver != NULL)
610                 goto done;
611
612         for (list = driver_list; list; list = list->next) {
613                 struct connman_technology_driver *driver = list->data;
614
615                 DBG("driver %p name %s", driver, driver->name);
616
617                 if (driver->type != technology->type)
618                         continue;
619
620                 if (driver->probe(technology) == 0) {
621                         technology->driver = driver;
622                         break;
623                 }
624         }
625
626 done:
627         DBG("technology %p", technology);
628
629         return technology;
630 }
631
632 static void technology_put(struct connman_technology *technology)
633 {
634         DBG("technology %p", technology);
635
636         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
637                 return;
638
639         if (technology->driver) {
640                 technology->driver->remove(technology);
641                 technology->driver = NULL;
642         }
643
644         technology_list = g_slist_remove(technology_list, technology);
645
646         technologies_changed();
647
648         g_dbus_unregister_interface(connection, technology->path,
649                                                 CONNMAN_TECHNOLOGY_INTERFACE);
650
651         g_slist_free(technology->device_list);
652         g_hash_table_destroy(technology->rfkill_list);
653
654         g_free(technology->path);
655         g_free(technology->regdom);
656         g_free(technology);
657 }
658
659 void __connman_technology_add_interface(enum connman_service_type type,
660                                 int index, const char *name, const char *ident)
661 {
662         struct connman_technology *technology;
663
664         switch (type) {
665         case CONNMAN_SERVICE_TYPE_UNKNOWN:
666         case CONNMAN_SERVICE_TYPE_SYSTEM:
667                 return;
668         case CONNMAN_SERVICE_TYPE_ETHERNET:
669         case CONNMAN_SERVICE_TYPE_WIFI:
670         case CONNMAN_SERVICE_TYPE_WIMAX:
671         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
672         case CONNMAN_SERVICE_TYPE_CELLULAR:
673         case CONNMAN_SERVICE_TYPE_GPS:
674         case CONNMAN_SERVICE_TYPE_VPN:
675         case CONNMAN_SERVICE_TYPE_GADGET:
676                 break;
677         }
678
679         connman_info("Create interface %s [ %s ]", name,
680                                 __connman_service_type2string(type));
681
682         technology = technology_get(type);
683
684         if (technology == NULL || technology->driver == NULL
685                         || technology->driver->add_interface == NULL)
686                 return;
687
688         technology->driver->add_interface(technology,
689                                         index, name, ident);
690 }
691
692 void __connman_technology_remove_interface(enum connman_service_type type,
693                                 int index, const char *name, const char *ident)
694 {
695         struct connman_technology *technology;
696
697         switch (type) {
698         case CONNMAN_SERVICE_TYPE_UNKNOWN:
699         case CONNMAN_SERVICE_TYPE_SYSTEM:
700                 return;
701         case CONNMAN_SERVICE_TYPE_ETHERNET:
702         case CONNMAN_SERVICE_TYPE_WIFI:
703         case CONNMAN_SERVICE_TYPE_WIMAX:
704         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
705         case CONNMAN_SERVICE_TYPE_CELLULAR:
706         case CONNMAN_SERVICE_TYPE_GPS:
707         case CONNMAN_SERVICE_TYPE_VPN:
708         case CONNMAN_SERVICE_TYPE_GADGET:
709                 break;
710         }
711
712         connman_info("Remove interface %s [ %s ]", name,
713                                 __connman_service_type2string(type));
714
715         technology = technology_find(type);
716
717         if (technology == NULL || technology->driver == NULL)
718                 return;
719
720         if (technology->driver->remove_interface)
721                 technology->driver->remove_interface(technology, index);
722
723         technology_put(technology);
724 }
725
726 int __connman_technology_add_device(struct connman_device *device)
727 {
728         struct connman_technology *technology;
729         enum connman_service_type type;
730
731         DBG("device %p", device);
732
733         type = __connman_device_get_service_type(device);
734         __connman_notifier_register(type);
735
736         technology = technology_get(type);
737         if (technology == NULL)
738                 return -ENXIO;
739
740         if (technology->enable_persistent && !global_offlinemode)
741                 __connman_device_enable(device);
742         /* if technology persistent state is offline */
743         if (!technology->enable_persistent)
744                 __connman_device_disable(device);
745
746         technology->device_list = g_slist_append(technology->device_list,
747                                                                 device);
748
749         return 0;
750 }
751
752 int __connman_technology_remove_device(struct connman_device *device)
753 {
754         struct connman_technology *technology;
755         enum connman_service_type type;
756
757         DBG("device %p", device);
758
759         type = __connman_device_get_service_type(device);
760         __connman_notifier_unregister(type);
761
762         technology = technology_find(type);
763         if (technology == NULL)
764                 return -ENXIO;
765
766         technology->device_list = g_slist_remove(technology->device_list,
767                                                                 device);
768         if (technology->device_list == NULL) {
769                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
770                 state_changed(technology);
771         }
772
773         return 0;
774 }
775
776 static gboolean technology_pending_reply(gpointer user_data)
777 {
778         struct connman_technology *technology = user_data;
779         DBusMessage *reply;
780
781         /* Power request timedout, send ETIMEDOUT. */
782         if (technology->pending_reply != NULL) {
783                 reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
784                 if (reply != NULL)
785                         g_dbus_send_message(connection, reply);
786
787                 dbus_message_unref(technology->pending_reply);
788                 technology->pending_reply = NULL;
789                 technology->pending_timeout = 0;
790         }
791
792         return FALSE;
793 }
794
795 int __connman_technology_enabled(enum connman_service_type type)
796 {
797         struct connman_technology *technology;
798
799         technology = technology_find(type);
800         if (technology == NULL)
801                 return -ENXIO;
802
803         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
804                 __connman_notifier_enable(type);
805                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
806                 state_changed(technology);
807         }
808
809         if (technology->pending_reply != NULL) {
810                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
811                 dbus_message_unref(technology->pending_reply);
812                 technology->pending_reply = NULL;
813                 technology->pending_timeout = 0;
814         }
815
816         return 0;
817 }
818
819 int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg)
820 {
821         struct connman_technology *technology;
822         GSList *list;
823         int err = 0;
824         int ret = -ENODEV;
825         DBusMessage *reply;
826
827         DBG("type %d enable", type);
828
829         technology = technology_find(type);
830         if (technology == NULL) {
831                 err = -ENXIO;
832                 goto done;
833         }
834
835         if (technology->pending_reply != NULL) {
836                 err = -EBUSY;
837                 goto done;
838         }
839
840         if (msg != NULL) {
841                 technology->pending_reply = dbus_message_ref(msg);
842                 /*
843                  * This is a bit of a trick. When msg is not NULL it means
844                  * thats technology_enable was invoked from the manager API. Hence we save
845                  * the state here.
846                  */
847                 technology->enable_persistent = TRUE;
848                 save_state(technology);
849         }
850
851         __connman_rfkill_block(technology->type, FALSE);
852
853         for (list = technology->device_list; list; list = list->next) {
854                 struct connman_device *device = list->data;
855
856                 err = __connman_device_enable(device);
857                 /*
858                  * err = 0 : Device was enabled right away.
859                  * If atleast one device gets enabled, we consider
860                  * the technology to be enabled.
861                  */
862                 if (err == 0)
863                         ret = 0;
864         }
865
866 done:
867         if (ret == 0)
868                 return ret;
869
870         if (msg != NULL) {
871                 if (err == -EINPROGRESS)
872                         technology->pending_timeout = g_timeout_add_seconds(10,
873                                         technology_pending_reply, technology);
874                 else {
875                         reply = __connman_error_failed(msg, -err);
876                         if (reply != NULL)
877                                 g_dbus_send_message(connection, reply);
878                 }
879         }
880
881         return err;
882 }
883
884 int __connman_technology_disabled(enum connman_service_type type)
885 {
886         struct connman_technology *technology;
887
888         technology = technology_find(type);
889         if (technology == NULL)
890                 return -ENXIO;
891
892         if (technology->pending_reply != NULL) {
893                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
894                 dbus_message_unref(technology->pending_reply);
895                 technology->pending_reply = NULL;
896         }
897
898         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
899                 __connman_notifier_disable(type);
900                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
901                 state_changed(technology);
902         }
903
904         return 0;
905 }
906
907 int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg)
908 {
909         struct connman_technology *technology;
910         GSList *list;
911         int err = 0;
912         int ret = -ENODEV;
913         DBusMessage *reply;
914
915         DBG("type %d disable", type);
916
917         technology = technology_find(type);
918         if (technology == NULL) {
919                 err = -ENXIO;
920                 goto done;
921         }
922
923         if (technology->pending_reply != NULL) {
924                 err = -EBUSY;
925                 goto done;
926         }
927
928         if (technology->tethering == TRUE)
929                 set_tethering(technology, FALSE);
930
931         if (msg != NULL) {
932                 technology->pending_reply = dbus_message_ref(msg);
933                 technology->enable_persistent = FALSE;
934                 save_state(technology);
935         }
936
937         __connman_rfkill_block(technology->type, TRUE);
938
939         for (list = technology->device_list; list; list = list->next) {
940                 struct connman_device *device = list->data;
941
942                 err = __connman_device_disable(device);
943                 if (err == 0)
944                         ret = 0;
945         }
946
947 done:
948         if (ret == 0)
949                 return ret;
950
951         if (msg != NULL) {
952                 if (err == -EINPROGRESS)
953                         technology->pending_timeout = g_timeout_add_seconds(10,
954                                         technology_pending_reply, technology);
955                 else {
956                         reply = __connman_error_failed(msg, -err);
957                         if (reply != NULL)
958                                 g_dbus_send_message(connection, reply);
959                 }
960         }
961
962         return err;
963 }
964
965 int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
966 {
967         GSList *list;
968         int err = -EINVAL;
969
970         if (global_offlinemode == offlinemode)
971                 return 0;
972
973         DBG("offlinemode %s", offlinemode ? "On" : "Off");
974
975         /*
976          * This is a bit tricky. When you set offlinemode, there is no
977          * way to differentiate between attempting offline mode and
978          * resuming offlinemode from last saved profile. We need that
979          * information in rfkill_update, otherwise it falls back on the
980          * technology's persistent state. Hence we set the offline mode here
981          * but save it & call the notifier only if its successful.
982          */
983
984         global_offlinemode = offlinemode;
985
986         /* Traverse technology list, enable/disable each technology. */
987         for (list = technology_list; list; list = list->next) {
988                 struct connman_technology *technology = list->data;
989
990                 if (offlinemode)
991                         err = __connman_technology_disable(technology->type, NULL);
992
993                 if (!offlinemode && technology->enable_persistent)
994                         err = __connman_technology_enable(technology->type, NULL);
995         }
996
997         if (err == 0 || err == -EINPROGRESS || err == -EALREADY) {
998                 connman_technology_save_offlinemode();
999                 __connman_notifier_offlinemode(offlinemode);
1000         } else
1001                 global_offlinemode = connman_technology_load_offlinemode();
1002
1003         return err;
1004 }
1005
1006 int __connman_technology_add_rfkill(unsigned int index,
1007                                         enum connman_service_type type,
1008                                                 connman_bool_t softblock,
1009                                                 connman_bool_t hardblock)
1010 {
1011         struct connman_technology *technology;
1012         struct connman_rfkill *rfkill;
1013
1014         DBG("index %u type %d soft %u hard %u", index, type,
1015                                                         softblock, hardblock);
1016
1017         technology = technology_get(type);
1018         if (technology == NULL)
1019                 return -ENXIO;
1020
1021         rfkill = g_try_new0(struct connman_rfkill, 1);
1022         if (rfkill == NULL)
1023                 return -ENOMEM;
1024
1025         rfkill->index = index;
1026         rfkill->type = type;
1027         rfkill->softblock = softblock;
1028         rfkill->hardblock = hardblock;
1029
1030         g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
1031
1032         if (hardblock) {
1033                 DBG("%s is switched off.", get_name(type));
1034                 return 0;
1035         }
1036
1037         /*
1038          * If Offline mode is on, we softblock the device if it isnt already.
1039          * If Offline mode is off, we rely on the persistent state of tech.
1040          */
1041         if (global_offlinemode) {
1042                 if (!softblock)
1043                         return __connman_rfkill_block(type, TRUE);
1044         } else {
1045                 if (technology->enable_persistent && softblock)
1046                         return __connman_rfkill_block(type, FALSE);
1047                 /* if technology persistent state is offline */
1048                 if (!technology->enable_persistent && !softblock)
1049                         return __connman_rfkill_block(type, TRUE);
1050         }
1051
1052         return 0;
1053 }
1054
1055 int __connman_technology_update_rfkill(unsigned int index,
1056                                         enum connman_service_type type,
1057                                                 connman_bool_t softblock,
1058                                                 connman_bool_t hardblock)
1059 {
1060         struct connman_technology *technology;
1061         struct connman_rfkill *rfkill;
1062
1063         DBG("index %u soft %u hard %u", index, softblock, hardblock);
1064
1065         technology = technology_find(type);
1066         if (technology == NULL)
1067                 return -ENXIO;
1068
1069         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
1070         if (rfkill == NULL)
1071                 return -ENXIO;
1072
1073         if (rfkill->softblock == softblock &&
1074                 rfkill->hardblock == hardblock)
1075                 return 0;
1076
1077         rfkill->softblock = softblock;
1078         rfkill->hardblock = hardblock;
1079
1080         if (hardblock) {
1081                 DBG("%s is switched off.", get_name(type));
1082                 return 0;
1083         }
1084
1085         if (!global_offlinemode) {
1086                 if (technology->enable_persistent && softblock)
1087                         return __connman_rfkill_block(type, FALSE);
1088                 if (!technology->enable_persistent && !softblock)
1089                         return __connman_rfkill_block(type, TRUE);
1090         }
1091
1092         return 0;
1093 }
1094
1095 int __connman_technology_remove_rfkill(unsigned int index,
1096                                         enum connman_service_type type)
1097 {
1098         struct connman_technology *technology;
1099         struct connman_rfkill *rfkill;
1100
1101         DBG("index %u", index);
1102
1103         technology = technology_find(type);
1104         if (technology == NULL)
1105                 return -ENXIO;
1106
1107         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
1108         if (rfkill == NULL)
1109                 return -ENXIO;
1110
1111         g_hash_table_remove(technology->rfkill_list, &index);
1112
1113         technology_put(technology);
1114
1115         return 0;
1116 }
1117
1118 int __connman_technology_init(void)
1119 {
1120         DBG("");
1121
1122         connection = connman_dbus_get_connection();
1123
1124         global_offlinemode = connman_technology_load_offlinemode();
1125
1126         return 0;
1127 }
1128
1129 void __connman_technology_cleanup(void)
1130 {
1131         DBG("");
1132
1133         dbus_connection_unref(connection);
1134 }