technology: Add support for regulatory domain notification
[framework/connectivity/connman.git] / src / technology.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <gdbus.h>
27
28 #include "connman.h"
29
30 static DBusConnection *connection;
31
32 static GHashTable *rfkill_table;
33 static GHashTable *device_table;
34 static GSList *technology_list = NULL;
35
36 struct connman_rfkill {
37         unsigned int index;
38         enum connman_service_type type;
39         connman_bool_t softblock;
40         connman_bool_t hardblock;
41 };
42
43 enum connman_technology_state {
44         CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
45         CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
46         CONNMAN_TECHNOLOGY_STATE_AVAILABLE = 2,
47         CONNMAN_TECHNOLOGY_STATE_BLOCKED   = 3,
48         CONNMAN_TECHNOLOGY_STATE_ENABLED   = 4,
49         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 5,
50 };
51
52 struct connman_technology {
53         gint refcount;
54         enum connman_service_type type;
55         enum connman_technology_state state;
56         char *path;
57         GHashTable *rfkill_list;
58         GSList *device_list;
59         gint enabled;
60         gint blocked;
61         char *regdom;
62
63         struct connman_technology_driver *driver;
64         void *driver_data;
65 };
66
67 static GSList *driver_list = NULL;
68
69 static gint compare_priority(gconstpointer a, gconstpointer b)
70 {
71         const struct connman_technology_driver *driver1 = a;
72         const struct connman_technology_driver *driver2 = b;
73
74         return driver2->priority - driver1->priority;
75 }
76
77 /**
78  * connman_technology_driver_register:
79  * @driver: Technology driver definition
80  *
81  * Register a new technology driver
82  *
83  * Returns: %0 on success
84  */
85 int connman_technology_driver_register(struct connman_technology_driver *driver)
86 {
87         GSList *list;
88         struct connman_technology *technology;
89
90         DBG("driver %p name %s", driver, driver->name);
91
92         driver_list = g_slist_insert_sorted(driver_list, driver,
93                                                         compare_priority);
94
95         for (list = technology_list; list; list = list->next) {
96                 technology = list->data;
97
98                 if (technology->driver != NULL)
99                         continue;
100
101                 if (technology->type == driver->type)
102                         technology->driver = driver;
103         }
104
105         return 0;
106 }
107
108 /**
109  * connman_technology_driver_unregister:
110  * @driver: Technology driver definition
111  *
112  * Remove a previously registered technology driver
113  */
114 void connman_technology_driver_unregister(struct connman_technology_driver *driver)
115 {
116         GSList *list;
117         struct connman_technology *technology;
118
119         DBG("driver %p name %s", driver, driver->name);
120
121         for (list = technology_list; list; list = list->next) {
122                 technology = list->data;
123
124                 if (technology->driver == NULL)
125                         continue;
126
127                 if (technology->type == driver->type) {
128                         technology->driver->remove(technology);
129                         technology->driver = NULL;
130                 }
131         }
132
133         driver_list = g_slist_remove(driver_list, driver);
134 }
135
136 void __connman_technology_add_interface(enum connman_service_type type,
137                                 int index, const char *name, const char *ident)
138 {
139         GSList *list;
140
141         switch (type) {
142         case CONNMAN_SERVICE_TYPE_UNKNOWN:
143         case CONNMAN_SERVICE_TYPE_SYSTEM:
144                 return;
145         case CONNMAN_SERVICE_TYPE_ETHERNET:
146         case CONNMAN_SERVICE_TYPE_WIFI:
147         case CONNMAN_SERVICE_TYPE_WIMAX:
148         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
149         case CONNMAN_SERVICE_TYPE_CELLULAR:
150         case CONNMAN_SERVICE_TYPE_GPS:
151         case CONNMAN_SERVICE_TYPE_VPN:
152         case CONNMAN_SERVICE_TYPE_GADGET:
153                 break;
154         }
155
156         connman_info("Create interface %s [ %s ]", name,
157                                 __connman_service_type2string(type));
158
159         for (list = technology_list; list; list = list->next) {
160                 struct connman_technology *technology = list->data;
161
162                 if (technology->type != type)
163                         continue;
164
165                 if (technology->driver == NULL)
166                         continue;
167
168                 if (technology->driver->add_interface)
169                         technology->driver->add_interface(technology,
170                                                         index, name, ident);
171         }
172 }
173
174 void __connman_technology_remove_interface(enum connman_service_type type,
175                                 int index, const char *name, const char *ident)
176 {
177         GSList *list;
178
179         switch (type) {
180         case CONNMAN_SERVICE_TYPE_UNKNOWN:
181         case CONNMAN_SERVICE_TYPE_SYSTEM:
182                 return;
183         case CONNMAN_SERVICE_TYPE_ETHERNET:
184         case CONNMAN_SERVICE_TYPE_WIFI:
185         case CONNMAN_SERVICE_TYPE_WIMAX:
186         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
187         case CONNMAN_SERVICE_TYPE_CELLULAR:
188         case CONNMAN_SERVICE_TYPE_GPS:
189         case CONNMAN_SERVICE_TYPE_VPN:
190         case CONNMAN_SERVICE_TYPE_GADGET:
191                 break;
192         }
193
194         connman_info("Remove interface %s [ %s ]", name,
195                                 __connman_service_type2string(type));
196
197         for (list = technology_list; list; list = list->next) {
198                 struct connman_technology *technology = list->data;
199
200                 if (technology->type != type)
201                         continue;
202
203                 if (technology->driver == NULL)
204                         continue;
205
206                 if (technology->driver->remove_interface)
207                         technology->driver->remove_interface(technology, index);
208         }
209 }
210
211 void connman_technology_tethering_notify(struct connman_technology *technology,
212                                                         connman_bool_t enabled)
213 {
214         DBG("technology %p enabled %u", technology, enabled);
215
216         if (enabled == TRUE)
217                 __connman_tethering_set_enabled();
218         else
219                 __connman_tethering_set_disabled();
220 }
221
222 static int set_tethering(const char *bridge, connman_bool_t enabled)
223 {
224         GSList *list;
225
226         for (list = technology_list; list; list = list->next) {
227                 struct connman_technology *technology = list->data;
228
229                 if (technology->driver == NULL)
230                         continue;
231
232                 if (technology->driver->set_tethering)
233                         technology->driver->set_tethering(technology,
234                                                         bridge, enabled);
235         }
236
237         return 0;
238 }
239
240 int __connman_technology_enable_tethering(const char *bridge)
241 {
242         return set_tethering(bridge, TRUE);
243 }
244
245 int __connman_technology_disable_tethering(const char *bridge)
246 {
247         return set_tethering(bridge, FALSE);
248 }
249
250 void connman_technology_regdom_notify(struct connman_technology *technology,
251                                                         const char *alpha2)
252 {
253         DBG("");
254
255         if (alpha2 == NULL)
256                 connman_error("Failed to set regulatory domain");
257         else
258                 DBG("Regulatory domain set to %s", alpha2);
259
260         g_free(technology->regdom);
261         technology->regdom = g_strdup(alpha2);
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 (technology->driver == NULL)
272                         continue;
273
274                 if (technology->driver->set_regdom)
275                         technology->driver->set_regdom(technology, alpha2);
276         }
277
278         return 0;
279 }
280
281 static void free_rfkill(gpointer data)
282 {
283         struct connman_rfkill *rfkill = data;
284
285         g_free(rfkill);
286 }
287
288 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
289 {
290         GSList *list;
291
292         for (list = technology_list; list; list = list->next) {
293                 struct connman_technology *technology = list->data;
294
295                 if (technology->path == NULL)
296                         continue;
297
298                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
299                                                         &technology->path);
300         }
301 }
302
303 static void technologies_changed(void)
304 {
305         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
306                         CONNMAN_MANAGER_INTERFACE, "Technologies",
307                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
308 }
309
310 static const char *state2string(enum connman_technology_state state)
311 {
312         switch (state) {
313         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
314                 break;
315         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
316                 return "offline";
317         case CONNMAN_TECHNOLOGY_STATE_AVAILABLE:
318                 return "available";
319         case CONNMAN_TECHNOLOGY_STATE_BLOCKED:
320                 return "blocked";
321         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
322                 return "enabled";
323         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
324                 return "connected";
325         }
326
327         return NULL;
328 }
329
330 static void state_changed(struct connman_technology *technology)
331 {
332         const char *str;
333
334         str = state2string(technology->state);
335         if (str == NULL)
336                 return;
337
338         connman_dbus_property_changed_basic(technology->path,
339                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
340                                                 DBUS_TYPE_STRING, &str);
341 }
342
343 static const char *get_name(enum connman_service_type type)
344 {
345         switch (type) {
346         case CONNMAN_SERVICE_TYPE_UNKNOWN:
347         case CONNMAN_SERVICE_TYPE_SYSTEM:
348         case CONNMAN_SERVICE_TYPE_GPS:
349         case CONNMAN_SERVICE_TYPE_VPN:
350         case CONNMAN_SERVICE_TYPE_GADGET:
351                 break;
352         case CONNMAN_SERVICE_TYPE_ETHERNET:
353                 return "Wired";
354         case CONNMAN_SERVICE_TYPE_WIFI:
355                 return "WiFi";
356         case CONNMAN_SERVICE_TYPE_WIMAX:
357                 return "WiMAX";
358         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
359                 return "Bluetooth";
360         case CONNMAN_SERVICE_TYPE_CELLULAR:
361                 return "3G";
362         }
363
364         return NULL;
365 }
366
367 static DBusMessage *get_properties(DBusConnection *conn,
368                                         DBusMessage *message, void *user_data)
369 {
370         struct connman_technology *technology = user_data;
371         DBusMessage *reply;
372         DBusMessageIter array, dict;
373         const char *str;
374
375         reply = dbus_message_new_method_return(message);
376         if (reply == NULL)
377                 return NULL;
378
379         dbus_message_iter_init_append(reply, &array);
380
381         connman_dbus_dict_open(&array, &dict);
382
383         str = state2string(technology->state);
384         if (str != NULL)
385                 connman_dbus_dict_append_basic(&dict, "State",
386                                                 DBUS_TYPE_STRING, &str);
387
388         str = get_name(technology->type);
389         if (str != NULL)
390                 connman_dbus_dict_append_basic(&dict, "Name",
391                                                 DBUS_TYPE_STRING, &str);
392
393         str = __connman_service_type2string(technology->type);
394         if (str != NULL)
395                 connman_dbus_dict_append_basic(&dict, "Type",
396                                                 DBUS_TYPE_STRING, &str);
397
398         connman_dbus_dict_close(&array, &dict);
399
400         return reply;
401 }
402
403 static GDBusMethodTable technology_methods[] = {
404         { "GetProperties", "", "a{sv}", get_properties },
405         { },
406 };
407
408 static GDBusSignalTable technology_signals[] = {
409         { "PropertyChanged", "sv" },
410         { },
411 };
412
413 static struct connman_technology *technology_find(enum connman_service_type type)
414 {
415         GSList *list;
416
417         DBG("type %d", type);
418
419         for (list = technology_list; list; list = list->next) {
420                 struct connman_technology *technology = list->data;
421
422                 if (technology->type == type)
423                         return technology;
424         }
425
426         return NULL;
427 }
428
429 static struct connman_technology *technology_get(enum connman_service_type type)
430 {
431         struct connman_technology *technology;
432         const char *str;
433         GSList *list;
434
435         DBG("type %d", type);
436
437         technology = technology_find(type);
438         if (technology != NULL) {
439                 g_atomic_int_inc(&technology->refcount);
440                 goto done;
441         }
442
443         str = __connman_service_type2string(type);
444         if (str == NULL)
445                 return NULL;
446
447         technology = g_try_new0(struct connman_technology, 1);
448         if (technology == NULL)
449                 return NULL;
450
451         technology->refcount = 1;
452
453         technology->type = type;
454         technology->path = g_strdup_printf("%s/technology/%s",
455                                                         CONNMAN_PATH, str);
456
457         technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
458                                                         NULL, free_rfkill);
459         technology->device_list = NULL;
460
461         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
462
463         if (g_dbus_register_interface(connection, technology->path,
464                                         CONNMAN_TECHNOLOGY_INTERFACE,
465                                         technology_methods, technology_signals,
466                                         NULL, technology, NULL) == FALSE) {
467                 connman_error("Failed to register %s", technology->path);
468                 g_free(technology);
469                 return NULL;
470         }
471
472         technology_list = g_slist_append(technology_list, technology);
473
474         technologies_changed();
475
476         if (technology->driver != NULL)
477                 goto done;
478
479         for (list = driver_list; list; list = list->next) {
480                 struct connman_technology_driver *driver = list->data;
481
482                 DBG("driver %p name %s", driver, driver->name);
483
484                 if (driver->type != technology->type)
485                         continue;
486
487                 if (driver->probe(technology) == 0) {
488                         technology->driver = driver;
489                         break;
490                 }
491         }
492
493 done:
494         DBG("technology %p", technology);
495
496         return technology;
497 }
498
499 static void technology_put(struct connman_technology *technology)
500 {
501         DBG("technology %p", technology);
502
503         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
504                 return;
505
506         if (technology->driver) {
507                 technology->driver->remove(technology);
508                 technology->driver = NULL;
509         }
510
511         technology_list = g_slist_remove(technology_list, technology);
512
513         technologies_changed();
514
515         g_dbus_unregister_interface(connection, technology->path,
516                                                 CONNMAN_TECHNOLOGY_INTERFACE);
517
518         g_slist_free(technology->device_list);
519         g_hash_table_destroy(technology->rfkill_list);
520
521         g_free(technology->path);
522         g_free(technology->regdom);
523         g_free(technology);
524 }
525
526 static void unregister_technology(gpointer data)
527 {
528         struct connman_technology *technology = data;
529
530         technology_put(technology);
531 }
532
533 int __connman_technology_add_device(struct connman_device *device)
534 {
535         struct connman_technology *technology;
536         enum connman_service_type type;
537
538         DBG("device %p", device);
539
540         type = __connman_device_get_service_type(device);
541         __connman_notifier_register(type);
542
543         technology = technology_get(type);
544         if (technology == NULL)
545                 return -ENXIO;
546
547         g_hash_table_insert(device_table, device, technology);
548
549         if (g_atomic_int_get(&technology->blocked))
550                 goto done;
551
552         technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
553
554         state_changed(technology);
555
556 done:
557
558         technology->device_list = g_slist_append(technology->device_list,
559                                                                 device);
560
561         return 0;
562 }
563
564 int __connman_technology_remove_device(struct connman_device *device)
565 {
566         struct connman_technology *technology;
567         enum connman_service_type type;
568
569         DBG("device %p", device);
570
571         type = __connman_device_get_service_type(device);
572         __connman_notifier_unregister(type);
573
574         technology = g_hash_table_lookup(device_table, device);
575         if (technology == NULL)
576                 return -ENXIO;
577
578         technology->device_list = g_slist_remove(technology->device_list,
579                                                                 device);
580         if (technology->device_list == NULL) {
581                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
582                 state_changed(technology);
583         }
584
585         g_hash_table_remove(device_table, device);
586
587         return 0;
588 }
589
590 int __connman_technology_enable_device(struct connman_device *device)
591 {
592         struct connman_technology *technology;
593         enum connman_service_type type;
594
595         DBG("device %p", device);
596
597         technology = g_hash_table_lookup(device_table, device);
598         if (technology == NULL)
599                 return -ENXIO;
600
601         if (g_atomic_int_get(&technology->blocked))
602                 return -ERFKILL;
603
604         type = __connman_device_get_service_type(device);
605         __connman_notifier_enable(type);
606
607         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
608                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
609                 state_changed(technology);
610         }
611
612         return 0;
613 }
614
615 int __connman_technology_disable_device(struct connman_device *device)
616 {
617         struct connman_technology *technology;
618         enum connman_service_type type;
619         GSList *list;
620
621         DBG("device %p", device);
622
623         type = __connman_device_get_service_type(device);
624         __connman_notifier_disable(type);
625
626         technology = g_hash_table_lookup(device_table, device);
627         if (technology == NULL)
628                 return -ENXIO;
629
630         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
631                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
632                 state_changed(technology);
633         }
634
635         for (list = technology->device_list; list; list = list->next) {
636                 struct connman_device *device = list->data;
637
638                 if (__connman_device_get_blocked(device) == FALSE)
639                         return 0;
640         }
641
642         technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
643         state_changed(technology);
644
645         return 0;
646 }
647
648 static void technology_blocked(struct connman_technology *technology,
649                                 connman_bool_t blocked)
650 {
651         GSList *list;
652
653         for (list = technology->device_list; list; list = list->next) {
654                 struct connman_device *device = list->data;
655
656                 __connman_device_set_blocked(device, blocked);
657         }
658 }
659
660 int __connman_technology_add_rfkill(unsigned int index,
661                                         enum connman_service_type type,
662                                                 connman_bool_t softblock,
663                                                 connman_bool_t hardblock)
664 {
665         struct connman_technology *technology;
666         struct connman_rfkill *rfkill;
667         connman_bool_t blocked;
668
669         DBG("index %u type %d soft %u hard %u", index, type,
670                                                         softblock, hardblock);
671
672         technology = technology_get(type);
673         if (technology == NULL)
674                 return -ENXIO;
675
676         rfkill = g_try_new0(struct connman_rfkill, 1);
677         if (rfkill == NULL)
678                 return -ENOMEM;
679
680         rfkill->index = index;
681         rfkill->type = type;
682         rfkill->softblock = softblock;
683         rfkill->hardblock = hardblock;
684
685         g_hash_table_replace(rfkill_table, &rfkill->index, technology);
686
687         g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
688
689         blocked = (softblock || hardblock) ? TRUE : FALSE;
690         if (blocked == FALSE)
691                 return 0;
692
693         if (g_atomic_int_exchange_and_add(&technology->blocked, 1) == 0) {
694                 technology_blocked(technology, TRUE);
695
696                 technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
697                 state_changed(technology);
698         }
699
700         return 0;
701 }
702
703 int __connman_technology_update_rfkill(unsigned int index,
704                                                 connman_bool_t softblock,
705                                                 connman_bool_t hardblock)
706 {
707         struct connman_technology *technology;
708         struct connman_rfkill *rfkill;
709         connman_bool_t blocked, old_blocked;
710
711         DBG("index %u soft %u hard %u", index, softblock, hardblock);
712
713         technology = g_hash_table_lookup(rfkill_table, &index);
714         if (technology == NULL)
715                 return -ENXIO;
716
717         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
718         if (rfkill == NULL)
719                 return -ENXIO;
720
721         old_blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
722         blocked = (softblock || hardblock) ? TRUE : FALSE;
723
724         rfkill->softblock = softblock;
725         rfkill->hardblock = hardblock;
726
727         if (blocked == old_blocked)
728                 return 0;
729
730         if (blocked) {
731                 guint n_blocked;
732
733                 n_blocked =
734                         g_atomic_int_exchange_and_add(&technology->blocked, 1);
735                 if (n_blocked != g_hash_table_size(technology->rfkill_list) - 1)
736                         return 0;
737
738                 technology_blocked(technology, blocked);
739                 technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
740                 state_changed(technology);
741         } else {
742                 if (g_atomic_int_dec_and_test(&technology->blocked) == FALSE)
743                         return 0;
744
745                 technology_blocked(technology, blocked);
746                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
747                 state_changed(technology);
748         }
749
750         return 0;
751 }
752
753 int __connman_technology_remove_rfkill(unsigned int index)
754 {
755         struct connman_technology *technology;
756         struct connman_rfkill *rfkill;
757         connman_bool_t blocked;
758
759         DBG("index %u", index);
760
761         technology = g_hash_table_lookup(rfkill_table, &index);
762         if (technology == NULL)
763                 return -ENXIO;
764
765         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
766         if (rfkill == NULL)
767                 return -ENXIO;
768
769         blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
770
771         g_hash_table_remove(technology->rfkill_list, &index);
772
773         g_hash_table_remove(rfkill_table, &index);
774
775         if (blocked &&
776                 g_atomic_int_dec_and_test(&technology->blocked) == TRUE) {
777                 technology_blocked(technology, FALSE);
778                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
779                 state_changed(technology);
780         }
781
782         return 0;
783 }
784
785 connman_bool_t __connman_technology_get_blocked(enum connman_service_type type)
786 {
787         struct connman_technology *technology;
788
789         technology = technology_find(type);
790         if (technology == NULL)
791                 return FALSE;
792
793         if (g_atomic_int_get(&technology->blocked))
794                 return TRUE;
795
796         return FALSE;
797 }
798
799 int __connman_technology_init(void)
800 {
801         DBG("");
802
803         connection = connman_dbus_get_connection();
804
805         rfkill_table = g_hash_table_new_full(g_int_hash, g_int_equal,
806                                                 NULL, unregister_technology);
807         device_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
808                                                 NULL, unregister_technology);
809
810         return 0;
811 }
812
813 void __connman_technology_cleanup(void)
814 {
815         DBG("");
816
817         g_hash_table_destroy(device_table);
818         g_hash_table_destroy(rfkill_table);
819
820         dbus_connection_unref(connection);
821 }