technology: Link hard rfkill with actual device's state
[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         connman_bool_t persistent;
654         int err;
655
656         if (powered == TRUE) {
657                 err = technology_enable(technology, FALSE);
658                 persistent = TRUE;
659         } else {
660                 err = technology_disable(technology, FALSE);
661                 persistent = FALSE;
662         }
663
664         if (err != -EBUSY) {
665                 technology->enable_persistent = persistent;
666                 technology_save(technology);
667         }
668
669         if (err == -EINPROGRESS) {
670                 technology->pending_reply = dbus_message_ref(msg);
671                 technology->pending_timeout = g_timeout_add_seconds(10,
672                                         technology_pending_reply, technology);
673         } else if (err == -EALREADY) {
674                 if (powered == TRUE)
675                         reply = __connman_error_already_enabled(msg);
676                 else
677                         reply = __connman_error_already_disabled(msg);
678         } else if (err < 0)
679                 reply = __connman_error_failed(msg, -err);
680         else
681                 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
682
683         return reply;
684 }
685
686 static DBusMessage *set_property(DBusConnection *conn,
687                                         DBusMessage *msg, void *data)
688 {
689         struct connman_technology *technology = data;
690         DBusMessageIter iter, value;
691         const char *name;
692         int type;
693
694         DBG("conn %p", conn);
695
696         if (dbus_message_iter_init(msg, &iter) == FALSE)
697                 return __connman_error_invalid_arguments(msg);
698
699         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
700                 return __connman_error_invalid_arguments(msg);
701
702         dbus_message_iter_get_basic(&iter, &name);
703         dbus_message_iter_next(&iter);
704
705         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
706                 return __connman_error_invalid_arguments(msg);
707
708         dbus_message_iter_recurse(&iter, &value);
709
710         type = dbus_message_iter_get_arg_type(&value);
711
712         DBG("property %s", name);
713
714         if (g_str_equal(name, "Tethering") == TRUE) {
715                 int err;
716                 connman_bool_t tethering;
717
718                 if (type != DBUS_TYPE_BOOLEAN)
719                         return __connman_error_invalid_arguments(msg);
720
721                 dbus_message_iter_get_basic(&value, &tethering);
722
723                 if (technology->tethering == tethering) {
724                         if (tethering == FALSE)
725                                 return __connman_error_already_disabled(msg);
726                         else
727                                 return __connman_error_already_enabled(msg);
728                 }
729
730                 err = set_tethering(technology, tethering);
731                 if (err < 0)
732                         return __connman_error_failed(msg, -err);
733
734         } else if (g_str_equal(name, "TetheringIdentifier") == TRUE) {
735                 const char *str;
736
737                 dbus_message_iter_get_basic(&value, &str);
738
739                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
740                         return __connman_error_not_supported(msg);
741
742                 if (strlen(str) < 1 || strlen(str) > 32)
743                         return __connman_error_invalid_arguments(msg);
744
745                 if (g_strcmp0(technology->tethering_ident, str) != 0) {
746                         g_free(technology->tethering_ident);
747                         technology->tethering_ident = g_strdup(str);
748                         technology_save(technology);
749
750                         connman_dbus_property_changed_basic(technology->path,
751                                                 CONNMAN_TECHNOLOGY_INTERFACE,
752                                                 "TetheringIdentifier",
753                                                 DBUS_TYPE_STRING,
754                                                 &technology->tethering_ident);
755                 }
756         } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) {
757                 const char *str;
758
759                 dbus_message_iter_get_basic(&value, &str);
760
761                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
762                         return __connman_error_not_supported(msg);
763
764                 if (strlen(str) < 8 || strlen(str) > 63)
765                         return __connman_error_passphrase_required(msg);
766
767                 if (g_strcmp0(technology->tethering_passphrase, str) != 0) {
768                         g_free(technology->tethering_passphrase);
769                         technology->tethering_passphrase = g_strdup(str);
770                         technology_save(technology);
771
772                         connman_dbus_property_changed_basic(technology->path,
773                                         CONNMAN_TECHNOLOGY_INTERFACE,
774                                         "TetheringPassphrase",
775                                         DBUS_TYPE_STRING,
776                                         &technology->tethering_passphrase);
777                 }
778         } else if (g_str_equal(name, "Powered") == TRUE) {
779                 connman_bool_t enable;
780
781                 if (type != DBUS_TYPE_BOOLEAN)
782                         return __connman_error_invalid_arguments(msg);
783
784                 dbus_message_iter_get_basic(&value, &enable);
785
786                 return set_powered(technology, msg, enable);
787         } else
788                 return __connman_error_invalid_property(msg);
789
790         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
791 }
792
793 static struct connman_technology *technology_find(enum connman_service_type type)
794 {
795         GSList *list;
796
797         DBG("type %d", type);
798
799         for (list = technology_list; list; list = list->next) {
800                 struct connman_technology *technology = list->data;
801
802                 if (technology->type == type)
803                         return technology;
804         }
805
806         return NULL;
807 }
808
809 static void reply_scan_pending(struct connman_technology *technology, int err)
810 {
811         DBusMessage *reply;
812
813         DBG("technology %p err %d", technology, err);
814
815         while (technology->scan_pending != NULL) {
816                 DBusMessage *msg = technology->scan_pending->data;
817
818                 DBG("reply to %s", dbus_message_get_sender(msg));
819
820                 if (err == 0)
821                         reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
822                 else
823                         reply = __connman_error_failed(msg, -err);
824                 g_dbus_send_message(connection, reply);
825                 dbus_message_unref(msg);
826
827                 technology->scan_pending =
828                         g_slist_delete_link(technology->scan_pending,
829                                         technology->scan_pending);
830         }
831 }
832
833 void __connman_technology_scan_started(struct connman_device *device)
834 {
835         DBG("device %p", device);
836 }
837
838 void __connman_technology_scan_stopped(struct connman_device *device)
839 {
840         int count = 0;
841         struct connman_technology *technology;
842         enum connman_service_type type;
843         GSList *list;
844
845         type = __connman_device_get_service_type(device);
846         technology = technology_find(type);
847
848         DBG("technology %p device %p", technology, device);
849
850         if (technology == NULL)
851                 return;
852
853         for (list = technology->device_list; list != NULL; list = list->next) {
854                 struct connman_device *other_device = list->data;
855
856                 if (device == other_device)
857                         continue;
858
859                 if (__connman_device_get_service_type(other_device) != type)
860                         continue;
861
862                 if (connman_device_get_scanning(other_device) == TRUE)
863                         count += 1;
864         }
865
866         if (count == 0)
867                 reply_scan_pending(technology, 0);
868 }
869
870 void __connman_technology_notify_regdom_by_device(struct connman_device *device,
871                                                 int result, const char *alpha2)
872 {
873         struct connman_technology *technology;
874         enum connman_service_type type;
875
876         type = __connman_device_get_service_type(device);
877         technology = technology_find(type);
878
879         if (technology == NULL)
880                 return;
881
882         if (result < 0) {
883                 if (technology->driver != NULL &&
884                                 technology->driver->set_regdom != NULL) {
885                         technology->driver->set_regdom(technology, alpha2);
886                         return;
887                 }
888
889                 alpha2 = NULL;
890         }
891
892         connman_technology_regdom_notify(technology, alpha2);
893 }
894
895 static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data)
896 {
897         struct connman_technology *technology = data;
898         int err;
899
900         DBG ("technology %p request from %s", technology,
901                         dbus_message_get_sender(msg));
902
903         dbus_message_ref(msg);
904         technology->scan_pending =
905                 g_slist_prepend(technology->scan_pending, msg);
906
907         err = __connman_device_request_scan(technology->type);
908         if (err < 0)
909                 reply_scan_pending(technology, err);
910
911         return NULL;
912 }
913
914 static const GDBusMethodTable technology_methods[] = {
915         { GDBUS_DEPRECATED_METHOD("GetProperties",
916                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
917                         get_properties) },
918         { GDBUS_ASYNC_METHOD("SetProperty",
919                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
920                         NULL, set_property) },
921         { GDBUS_ASYNC_METHOD("Scan", NULL, NULL, scan) },
922         { },
923 };
924
925 static const GDBusSignalTable technology_signals[] = {
926         { GDBUS_SIGNAL("PropertyChanged",
927                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
928         { },
929 };
930
931 static struct connman_technology *technology_get(enum connman_service_type type)
932 {
933         struct connman_technology *technology;
934         struct connman_technology_driver *driver = NULL;
935         const char *str;
936         GSList *list;
937         int err;
938
939         DBG("type %d", type);
940
941         str = __connman_service_type2string(type);
942         if (str == NULL)
943                 return NULL;
944
945         technology = technology_find(type);
946         if (technology != NULL) {
947                 __sync_fetch_and_add(&technology->refcount, 1);
948                 return technology;
949         }
950
951         /* First check if we have a driver for this technology type */
952         for (list = driver_list; list; list = list->next) {
953                 driver = list->data;
954
955                 if (driver->type == type)
956                         break;
957                 else
958                         driver = NULL;
959         }
960
961         if (driver == NULL) {
962                 DBG("No matching driver found for %s.",
963                                 __connman_service_type2string(type));
964                 return NULL;
965         }
966
967         technology = g_try_new0(struct connman_technology, 1);
968         if (technology == NULL)
969                 return NULL;
970
971         technology->refcount = 1;
972
973         if (type == CONNMAN_SERVICE_TYPE_ETHERNET)
974                 technology->hardblocked = FALSE;
975         else
976                 technology->hardblocked = TRUE;
977
978         technology->type = type;
979         technology->path = g_strdup_printf("%s/technology/%s",
980                                                         CONNMAN_PATH, str);
981
982         technology->device_list = NULL;
983
984         technology->pending_reply = NULL;
985
986         technology_load(technology);
987
988         if (g_dbus_register_interface(connection, technology->path,
989                                         CONNMAN_TECHNOLOGY_INTERFACE,
990                                         technology_methods, technology_signals,
991                                         NULL, technology, NULL) == FALSE) {
992                 connman_error("Failed to register %s", technology->path);
993                 g_free(technology);
994                 return NULL;
995         }
996
997         technology_list = g_slist_prepend(technology_list, technology);
998
999         technology_added_signal(technology);
1000
1001         technology->driver = driver;
1002         err = driver->probe(technology);
1003         if (err != 0)
1004                 DBG("Driver probe failed for technology %p", technology);
1005
1006         DBG("technology %p", technology);
1007
1008         return technology;
1009 }
1010
1011 static void technology_put(struct connman_technology *technology)
1012 {
1013         DBG("technology %p", technology);
1014
1015         if (__sync_sub_and_fetch(&technology->refcount, 1) > 0)
1016                 return;
1017
1018         reply_scan_pending(technology, -EINTR);
1019
1020         if (technology->driver) {
1021                 technology->driver->remove(technology);
1022                 technology->driver = NULL;
1023         }
1024
1025         technology_list = g_slist_remove(technology_list, technology);
1026
1027         technology_removed_signal(technology);
1028
1029         g_dbus_unregister_interface(connection, technology->path,
1030                                                 CONNMAN_TECHNOLOGY_INTERFACE);
1031
1032         g_slist_free(technology->device_list);
1033
1034         g_free(technology->path);
1035         g_free(technology->regdom);
1036         g_free(technology->tethering_ident);
1037         g_free(technology->tethering_passphrase);
1038         g_free(technology);
1039 }
1040
1041 void __connman_technology_add_interface(enum connman_service_type type,
1042                                 int index, const char *name, const char *ident)
1043 {
1044         struct connman_technology *technology;
1045
1046         switch (type) {
1047         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1048         case CONNMAN_SERVICE_TYPE_SYSTEM:
1049                 return;
1050         case CONNMAN_SERVICE_TYPE_ETHERNET:
1051         case CONNMAN_SERVICE_TYPE_WIFI:
1052         case CONNMAN_SERVICE_TYPE_WIMAX:
1053         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
1054         case CONNMAN_SERVICE_TYPE_CELLULAR:
1055         case CONNMAN_SERVICE_TYPE_GPS:
1056         case CONNMAN_SERVICE_TYPE_VPN:
1057         case CONNMAN_SERVICE_TYPE_GADGET:
1058                 break;
1059         }
1060
1061         connman_info("Adding interface %s [ %s ]", name,
1062                                 __connman_service_type2string(type));
1063
1064         technology = technology_find(type);
1065
1066         if (technology == NULL || technology->driver == NULL
1067                         || technology->driver->add_interface == NULL)
1068                 return;
1069
1070         technology->driver->add_interface(technology,
1071                                         index, name, ident);
1072 }
1073
1074 void __connman_technology_remove_interface(enum connman_service_type type,
1075                                 int index, const char *name, const char *ident)
1076 {
1077         struct connman_technology *technology;
1078
1079         switch (type) {
1080         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1081         case CONNMAN_SERVICE_TYPE_SYSTEM:
1082                 return;
1083         case CONNMAN_SERVICE_TYPE_ETHERNET:
1084         case CONNMAN_SERVICE_TYPE_WIFI:
1085         case CONNMAN_SERVICE_TYPE_WIMAX:
1086         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
1087         case CONNMAN_SERVICE_TYPE_CELLULAR:
1088         case CONNMAN_SERVICE_TYPE_GPS:
1089         case CONNMAN_SERVICE_TYPE_VPN:
1090         case CONNMAN_SERVICE_TYPE_GADGET:
1091                 break;
1092         }
1093
1094         connman_info("Remove interface %s [ %s ]", name,
1095                                 __connman_service_type2string(type));
1096
1097         technology = technology_find(type);
1098
1099         if (technology == NULL || technology->driver == NULL)
1100                 return;
1101
1102         if (technology->driver->remove_interface)
1103                 technology->driver->remove_interface(technology, index);
1104 }
1105
1106 int __connman_technology_add_device(struct connman_device *device)
1107 {
1108         struct connman_technology *technology;
1109         enum connman_service_type type;
1110
1111         DBG("device %p", device);
1112
1113         type = __connman_device_get_service_type(device);
1114
1115         technology = technology_get(type);
1116         if (technology == NULL) {
1117                 /*
1118                  * Since no driver can be found for this device at the moment we
1119                  * add it to the techless device list.
1120                 */
1121                 techless_device_list = g_slist_prepend(techless_device_list,
1122                                                                 device);
1123
1124                 return -ENXIO;
1125         }
1126
1127         if (technology->enable_persistent &&
1128                                         global_offlinemode == FALSE &&
1129                                         technology->hardblocked == FALSE) {
1130                 int err = __connman_device_enable(device);
1131                 /*
1132                  * connman_technology_add_device() calls __connman_device_enable()
1133                  * but since the device is already enabled, the calls does not
1134                  * propagate through to connman_technology_enabled via
1135                  * connman_device_set_powered.
1136                  */
1137                 if (err == -EALREADY)
1138                         __connman_technology_enabled(type);
1139         }
1140         /* if technology persistent state is offline or hardblocked */
1141         if (technology->enable_persistent == FALSE ||
1142                                         technology->hardblocked == TRUE)
1143                 __connman_device_disable(device);
1144
1145         technology->device_list = g_slist_prepend(technology->device_list,
1146                                                                 device);
1147
1148         return 0;
1149 }
1150
1151 int __connman_technology_remove_device(struct connman_device *device)
1152 {
1153         struct connman_technology *technology;
1154         enum connman_service_type type;
1155
1156         DBG("device %p", device);
1157
1158         type = __connman_device_get_service_type(device);
1159
1160         technology = technology_find(type);
1161         if (technology == NULL) {
1162                 techless_device_list = g_slist_remove(techless_device_list,
1163                                                                 device);
1164                 return -ENXIO;
1165         }
1166
1167         technology->device_list = g_slist_remove(technology->device_list,
1168                                                                 device);
1169         technology_put(technology);
1170
1171         return 0;
1172 }
1173
1174 static void powered_changed(struct connman_technology *technology)
1175 {
1176         connman_bool_t powered;
1177
1178         __sync_synchronize();
1179         if (technology->enabled >0)
1180                 powered = TRUE;
1181         else
1182                 powered = FALSE;
1183
1184         connman_dbus_property_changed_basic(technology->path,
1185                         CONNMAN_TECHNOLOGY_INTERFACE, "Powered",
1186                         DBUS_TYPE_BOOLEAN, &powered);
1187 }
1188
1189 int __connman_technology_enabled(enum connman_service_type type)
1190 {
1191         struct connman_technology *technology;
1192
1193         technology = technology_find(type);
1194         if (technology == NULL)
1195                 return -ENXIO;
1196
1197         if (__sync_fetch_and_add(&technology->enabled, 1) != 0)
1198                 return -EALREADY;
1199
1200         powered_changed(technology);
1201
1202         if (technology->pending_reply != NULL) {
1203                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
1204                 dbus_message_unref(technology->pending_reply);
1205                 g_source_remove(technology->pending_timeout);
1206                 technology->pending_reply = NULL;
1207                 technology->pending_timeout = 0;
1208         }
1209
1210         return 0;
1211 }
1212
1213 int __connman_technology_disabled(enum connman_service_type type)
1214 {
1215         struct connman_technology *technology;
1216
1217         technology = technology_find(type);
1218         if (technology == NULL)
1219                 return -ENXIO;
1220
1221         if (__sync_fetch_and_sub(&technology->enabled, 1) != 1)
1222                 return -EINPROGRESS;
1223
1224         if (technology->pending_reply != NULL) {
1225                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
1226                 dbus_message_unref(technology->pending_reply);
1227                 g_source_remove(technology->pending_timeout);
1228                 technology->pending_reply = NULL;
1229                 technology->pending_timeout = 0;
1230         }
1231
1232         powered_changed(technology);
1233
1234         return 0;
1235 }
1236
1237 int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
1238 {
1239         GSList *list;
1240         int err = -EINVAL;
1241
1242         if (global_offlinemode == offlinemode)
1243                 return 0;
1244
1245         DBG("offlinemode %s", offlinemode ? "On" : "Off");
1246
1247         /*
1248          * This is a bit tricky. When you set offlinemode, there is no
1249          * way to differentiate between attempting offline mode and
1250          * resuming offlinemode from last saved profile. We need that
1251          * information in rfkill_update, otherwise it falls back on the
1252          * technology's persistent state. Hence we set the offline mode here
1253          * but save it & call the notifier only if its successful.
1254          */
1255
1256         global_offlinemode = offlinemode;
1257
1258         /* Traverse technology list, enable/disable each technology. */
1259         for (list = technology_list; list; list = list->next) {
1260                 struct connman_technology *technology = list->data;
1261
1262                 if (offlinemode)
1263                         err = technology_disable(technology, FALSE);
1264
1265                 if (!offlinemode && technology->enable_persistent)
1266                         err = technology_enable(technology, FALSE);
1267         }
1268
1269         if (err == 0 || err == -EINPROGRESS || err == -EALREADY) {
1270                 connman_technology_save_offlinemode();
1271                 __connman_notifier_offlinemode(offlinemode);
1272         } else
1273                 global_offlinemode = connman_technology_load_offlinemode();
1274
1275         return err;
1276 }
1277
1278 void __connman_technology_set_connected(enum connman_service_type type,
1279                 connman_bool_t connected)
1280 {
1281         struct connman_technology *technology;
1282
1283         technology = technology_find(type);
1284         if (technology == NULL)
1285                 return;
1286
1287         DBG("technology %p connected %d", technology, connected);
1288
1289         technology->connected = connected;
1290
1291         connman_dbus_property_changed_basic(technology->path,
1292                         CONNMAN_TECHNOLOGY_INTERFACE, "Connected",
1293                         DBUS_TYPE_BOOLEAN, &connected);
1294 }
1295
1296 static void technology_apply_hardblock_change(struct connman_technology *technology,
1297                                                 connman_bool_t hardblock)
1298 {
1299         gboolean apply = TRUE;
1300         GList *start, *list;
1301
1302         if (technology->hardblocked == hardblock)
1303                 return;
1304
1305         start = g_hash_table_get_values(rfkill_list);
1306         for (list = start; list != NULL; list = list->next) {
1307                 struct connman_rfkill *rfkill = list->data;
1308
1309                 if (rfkill->type != technology->type)
1310                         continue;
1311
1312                 if (rfkill->hardblock != hardblock)
1313                         apply = FALSE;
1314         }
1315
1316         g_list_free(start);
1317
1318         if (apply == FALSE)
1319                 return;
1320
1321         technology->hardblocked = hardblock;
1322
1323         if (hardblock == TRUE) {
1324                 DBG("%s is switched off.", get_name(technology->type));
1325                 technology_disable(technology, TRUE);
1326         } else
1327                 technology_enable(technology, TRUE);
1328
1329 }
1330
1331 int __connman_technology_add_rfkill(unsigned int index,
1332                                         enum connman_service_type type,
1333                                                 connman_bool_t softblock,
1334                                                 connman_bool_t hardblock)
1335 {
1336         struct connman_technology *technology;
1337         struct connman_rfkill *rfkill;
1338
1339         DBG("index %u type %d soft %u hard %u", index, type,
1340                                                         softblock, hardblock);
1341
1342         rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index));
1343         if (rfkill != NULL)
1344                 goto done;
1345
1346         rfkill = g_try_new0(struct connman_rfkill, 1);
1347         if (rfkill == NULL)
1348                 return -ENOMEM;
1349
1350         rfkill->index = index;
1351         rfkill->type = type;
1352         rfkill->softblock = softblock;
1353         rfkill->hardblock = hardblock;
1354
1355         g_hash_table_insert(rfkill_list, GINT_TO_POINTER(index), rfkill);
1356
1357 done:
1358         technology = technology_get(type);
1359         /* If there is no driver for this type, ignore it. */
1360         if (technology == NULL)
1361                 return -ENXIO;
1362
1363         technology_apply_hardblock_change(technology, hardblock);
1364
1365         /*
1366          * If Offline mode is on, we softblock the device if it isnt already.
1367          * If Offline mode is off, we rely on the persistent state of tech.
1368          */
1369         if (global_offlinemode) {
1370                 if (!softblock)
1371                         return __connman_rfkill_block(type, TRUE);
1372         } else {
1373                 if (technology->enable_persistent && softblock)
1374                         return __connman_rfkill_block(type, FALSE);
1375                 /* if technology persistent state is offline */
1376                 if (!technology->enable_persistent && !softblock)
1377                         return __connman_rfkill_block(type, TRUE);
1378         }
1379
1380         return 0;
1381 }
1382
1383 int __connman_technology_update_rfkill(unsigned int index,
1384                                         enum connman_service_type type,
1385                                                 connman_bool_t softblock,
1386                                                 connman_bool_t hardblock)
1387 {
1388         struct connman_technology *technology;
1389         struct connman_rfkill *rfkill;
1390
1391         DBG("index %u soft %u hard %u", index, softblock, hardblock);
1392
1393         rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index));
1394         if (rfkill == NULL)
1395                 return -ENXIO;
1396
1397         if (rfkill->softblock == softblock &&
1398                 rfkill->hardblock == hardblock)
1399                 return 0;
1400
1401         rfkill->softblock = softblock;
1402         rfkill->hardblock = hardblock;
1403
1404         technology = technology_find(type);
1405         /* If there is no driver for this type, ignore it. */
1406         if (technology == NULL)
1407                 return -ENXIO;
1408
1409         technology_apply_hardblock_change(technology, hardblock);
1410
1411         if (!global_offlinemode) {
1412                 if (technology->enable_persistent && softblock)
1413                         return __connman_rfkill_block(type, FALSE);
1414                 if (!technology->enable_persistent && !softblock)
1415                         return __connman_rfkill_block(type, TRUE);
1416         }
1417
1418         return 0;
1419 }
1420
1421 int __connman_technology_remove_rfkill(unsigned int index,
1422                                         enum connman_service_type type)
1423 {
1424         struct connman_technology *technology;
1425         struct connman_rfkill *rfkill;
1426
1427         DBG("index %u", index);
1428
1429         rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index));
1430         if (rfkill == NULL)
1431                 return -ENXIO;
1432
1433         g_hash_table_remove(rfkill_list, GINT_TO_POINTER(index));
1434
1435         technology = technology_find(type);
1436         if (technology == NULL)
1437                 return -ENXIO;
1438
1439         technology_put(technology);
1440
1441         return 0;
1442 }
1443
1444 int __connman_technology_init(void)
1445 {
1446         DBG("");
1447
1448         connection = connman_dbus_get_connection();
1449
1450         rfkill_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
1451                                                         NULL, free_rfkill);
1452
1453         global_offlinemode = connman_technology_load_offlinemode();
1454
1455         /* This will create settings file if it is missing */
1456         connman_technology_save_offlinemode();
1457
1458         return 0;
1459 }
1460
1461 void __connman_technology_cleanup(void)
1462 {
1463         DBG("");
1464
1465         g_hash_table_destroy(rfkill_list);
1466
1467         dbus_connection_unref(connection);
1468 }