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