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