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