technology: Add bridge name to the tethering hook
[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
62         struct connman_technology_driver *driver;
63         void *driver_data;
64 };
65
66 static GSList *driver_list = NULL;
67
68 static gint compare_priority(gconstpointer a, gconstpointer b)
69 {
70         const struct connman_technology_driver *driver1 = a;
71         const struct connman_technology_driver *driver2 = b;
72
73         return driver2->priority - driver1->priority;
74 }
75
76 /**
77  * connman_technology_driver_register:
78  * @driver: Technology driver definition
79  *
80  * Register a new technology driver
81  *
82  * Returns: %0 on success
83  */
84 int connman_technology_driver_register(struct connman_technology_driver *driver)
85 {
86         DBG("driver %p name %s", driver, driver->name);
87
88         driver_list = g_slist_insert_sorted(driver_list, driver,
89                                                         compare_priority);
90
91         return 0;
92 }
93
94 /**
95  * connman_technology_driver_unregister:
96  * @driver: Technology driver definition
97  *
98  * Remove a previously registered technology driver
99  */
100 void connman_technology_driver_unregister(struct connman_technology_driver *driver)
101 {
102         DBG("driver %p name %s", driver, driver->name);
103
104         driver_list = g_slist_remove(driver_list, driver);
105 }
106
107 void __connman_technology_add_interface(enum connman_service_type type,
108                                 int index, const char *name, const char *ident)
109 {
110         GSList *list;
111
112         switch (type) {
113         case CONNMAN_SERVICE_TYPE_UNKNOWN:
114         case CONNMAN_SERVICE_TYPE_SYSTEM:
115                 return;
116         case CONNMAN_SERVICE_TYPE_ETHERNET:
117         case CONNMAN_SERVICE_TYPE_WIFI:
118         case CONNMAN_SERVICE_TYPE_WIMAX:
119         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
120         case CONNMAN_SERVICE_TYPE_CELLULAR:
121         case CONNMAN_SERVICE_TYPE_GPS:
122         case CONNMAN_SERVICE_TYPE_VPN:
123                 break;
124         }
125
126         connman_info("Create interface %s [ %s ]", name,
127                                 __connman_service_type2string(type));
128
129         for (list = technology_list; list; list = list->next) {
130                 struct connman_technology *technology = list->data;
131
132                 if (technology->type != type)
133                         continue;
134
135                 if (technology->driver == NULL)
136                         continue;
137
138                 if (technology->driver->add_interface)
139                         technology->driver->add_interface(technology,
140                                                         index, name, ident);
141         }
142 }
143
144 void __connman_technology_remove_interface(enum connman_service_type type,
145                                 int index, const char *name, const char *ident)
146 {
147         GSList *list;
148
149         switch (type) {
150         case CONNMAN_SERVICE_TYPE_UNKNOWN:
151         case CONNMAN_SERVICE_TYPE_SYSTEM:
152                 return;
153         case CONNMAN_SERVICE_TYPE_ETHERNET:
154         case CONNMAN_SERVICE_TYPE_WIFI:
155         case CONNMAN_SERVICE_TYPE_WIMAX:
156         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
157         case CONNMAN_SERVICE_TYPE_CELLULAR:
158         case CONNMAN_SERVICE_TYPE_GPS:
159         case CONNMAN_SERVICE_TYPE_VPN:
160                 break;
161         }
162
163         connman_info("Remove interface %s [ %s ]", name,
164                                 __connman_service_type2string(type));
165
166         for (list = technology_list; list; list = list->next) {
167                 struct connman_technology *technology = list->data;
168
169                 if (technology->type != type)
170                         continue;
171
172                 if (technology->driver == NULL)
173                         continue;
174
175                 if (technology->driver->remove_interface)
176                         technology->driver->remove_interface(technology, index);
177         }
178 }
179
180 static int set_tethering(const char *bridge, connman_bool_t enabled)
181 {
182         GSList *list;
183
184         for (list = technology_list; list; list = list->next) {
185                 struct connman_technology *technology = list->data;
186
187                 if (technology->driver == NULL)
188                         continue;
189
190                 if (technology->driver->set_tethering)
191                         technology->driver->set_tethering(technology, bridge, enabled);
192         }
193
194         return 0;
195 }
196
197 int __connman_technology_enable_tethering(const char *bridge)
198 {
199         return set_tethering(bridge, TRUE);
200 }
201
202 int __connman_technology_disable_tethering(const char *bridge)
203 {
204         return set_tethering(bridge, FALSE);
205 }
206
207 static void free_rfkill(gpointer data)
208 {
209         struct connman_rfkill *rfkill = data;
210
211         g_free(rfkill);
212 }
213
214 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
215 {
216         GSList *list;
217
218         for (list = technology_list; list; list = list->next) {
219                 struct connman_technology *technology = list->data;
220
221                 if (technology->path == NULL)
222                         continue;
223
224                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
225                                                         &technology->path);
226         }
227 }
228
229 static void technologies_changed(void)
230 {
231         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
232                         CONNMAN_MANAGER_INTERFACE, "Technologies",
233                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
234 }
235
236 static const char *state2string(enum connman_technology_state state)
237 {
238         switch (state) {
239         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
240                 break;
241         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
242                 return "offline";
243         case CONNMAN_TECHNOLOGY_STATE_AVAILABLE:
244                 return "available";
245         case CONNMAN_TECHNOLOGY_STATE_BLOCKED:
246                 return "blocked";
247         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
248                 return "enabled";
249         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
250                 return "connected";
251         }
252
253         return NULL;
254 }
255
256 static void state_changed(struct connman_technology *technology)
257 {
258         const char *str;
259
260         str = state2string(technology->state);
261         if (str == NULL)
262                 return;
263
264         connman_dbus_property_changed_basic(technology->path,
265                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
266                                                 DBUS_TYPE_STRING, &str);
267 }
268
269 static const char *get_name(enum connman_service_type type)
270 {
271         switch (type) {
272         case CONNMAN_SERVICE_TYPE_UNKNOWN:
273         case CONNMAN_SERVICE_TYPE_SYSTEM:
274         case CONNMAN_SERVICE_TYPE_GPS:
275         case CONNMAN_SERVICE_TYPE_VPN:
276                 break;
277         case CONNMAN_SERVICE_TYPE_ETHERNET:
278                 return "Wired";
279         case CONNMAN_SERVICE_TYPE_WIFI:
280                 return "WiFi";
281         case CONNMAN_SERVICE_TYPE_WIMAX:
282                 return "WiMAX";
283         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
284                 return "Bluetooth";
285         case CONNMAN_SERVICE_TYPE_CELLULAR:
286                 return "3G";
287         }
288
289         return NULL;
290 }
291
292 static DBusMessage *get_properties(DBusConnection *conn,
293                                         DBusMessage *message, void *user_data)
294 {
295         struct connman_technology *technology = user_data;
296         DBusMessage *reply;
297         DBusMessageIter array, dict;
298         const char *str;
299
300         reply = dbus_message_new_method_return(message);
301         if (reply == NULL)
302                 return NULL;
303
304         dbus_message_iter_init_append(reply, &array);
305
306         connman_dbus_dict_open(&array, &dict);
307
308         str = state2string(technology->state);
309         if (str != NULL)
310                 connman_dbus_dict_append_basic(&dict, "State",
311                                                 DBUS_TYPE_STRING, &str);
312
313         str = get_name(technology->type);
314         if (str != NULL)
315                 connman_dbus_dict_append_basic(&dict, "Name",
316                                                 DBUS_TYPE_STRING, &str);
317
318         str = __connman_service_type2string(technology->type);
319         if (str != NULL)
320                 connman_dbus_dict_append_basic(&dict, "Type",
321                                                 DBUS_TYPE_STRING, &str);
322
323         connman_dbus_dict_close(&array, &dict);
324
325         return reply;
326 }
327
328 static GDBusMethodTable technology_methods[] = {
329         { "GetProperties", "", "a{sv}", get_properties },
330         { },
331 };
332
333 static GDBusSignalTable technology_signals[] = {
334         { "PropertyChanged", "sv" },
335         { },
336 };
337
338 static struct connman_technology *technology_find(enum connman_service_type type)
339 {
340         GSList *list;
341
342         DBG("type %d", type);
343
344         for (list = technology_list; list; list = list->next) {
345                 struct connman_technology *technology = list->data;
346
347                 if (technology->type == type)
348                         return technology;
349         }
350
351         return NULL;
352 }
353
354 static struct connman_technology *technology_get(enum connman_service_type type)
355 {
356         struct connman_technology *technology;
357         const char *str;
358         GSList *list;
359
360         DBG("type %d", type);
361
362         technology = technology_find(type);
363         if (technology != NULL) {
364                 g_atomic_int_inc(&technology->refcount);
365                 goto done;
366         }
367
368         str = __connman_service_type2string(type);
369         if (str == NULL)
370                 return NULL;
371
372         technology = g_try_new0(struct connman_technology, 1);
373         if (technology == NULL)
374                 return NULL;
375
376         technology->refcount = 1;
377
378         technology->type = type;
379         technology->path = g_strdup_printf("%s/technology/%s",
380                                                         CONNMAN_PATH, str);
381
382         technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
383                                                         NULL, free_rfkill);
384         technology->device_list = NULL;
385
386         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
387
388         if (g_dbus_register_interface(connection, technology->path,
389                                         CONNMAN_TECHNOLOGY_INTERFACE,
390                                         technology_methods, technology_signals,
391                                         NULL, technology, NULL) == FALSE) {
392                 connman_error("Failed to register %s", technology->path);
393                 g_free(technology);
394                 return NULL;
395         }
396
397         technology_list = g_slist_append(technology_list, technology);
398
399         technologies_changed();
400
401         if (technology->driver != NULL)
402                 goto done;
403
404         for (list = driver_list; list; list = list->next) {
405                 struct connman_technology_driver *driver = list->data;
406
407                 DBG("driver %p name %s", driver, driver->name);
408
409                 if (driver->type != technology->type)
410                         continue;
411
412                 if (driver->probe(technology) == 0) {
413                         technology->driver = driver;
414                         break;
415                 }
416         }
417
418 done:
419         DBG("technology %p", technology);
420
421         return technology;
422 }
423
424 static void technology_put(struct connman_technology *technology)
425 {
426         DBG("technology %p", technology);
427
428         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
429                 return;
430
431         if (technology->driver) {
432                 technology->driver->remove(technology);
433                 technology->driver = NULL;
434         }
435
436         technology_list = g_slist_remove(technology_list, technology);
437
438         technologies_changed();
439
440         g_dbus_unregister_interface(connection, technology->path,
441                                                 CONNMAN_TECHNOLOGY_INTERFACE);
442
443         g_slist_free(technology->device_list);
444         g_hash_table_destroy(technology->rfkill_list);
445
446         g_free(technology->path);
447         g_free(technology);
448 }
449
450 static void unregister_technology(gpointer data)
451 {
452         struct connman_technology *technology = data;
453
454         technology_put(technology);
455 }
456
457 int __connman_technology_add_device(struct connman_device *device)
458 {
459         struct connman_technology *technology;
460         enum connman_service_type type;
461
462         DBG("device %p", device);
463
464         type = __connman_device_get_service_type(device);
465         __connman_notifier_register(type);
466
467         technology = technology_get(type);
468         if (technology == NULL)
469                 return -ENXIO;
470
471         g_hash_table_insert(device_table, device, technology);
472
473         if (g_atomic_int_get(&technology->blocked))
474                 goto done;
475
476         technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
477
478         state_changed(technology);
479
480 done:
481
482         technology->device_list = g_slist_append(technology->device_list,
483                                                                 device);
484
485         return 0;
486 }
487
488 int __connman_technology_remove_device(struct connman_device *device)
489 {
490         struct connman_technology *technology;
491         enum connman_service_type type;
492
493         DBG("device %p", device);
494
495         type = __connman_device_get_service_type(device);
496         __connman_notifier_unregister(type);
497
498         technology = g_hash_table_lookup(device_table, device);
499         if (technology == NULL)
500                 return -ENXIO;
501
502         technology->device_list = g_slist_remove(technology->device_list,
503                                                                 device);
504         if (technology->device_list == NULL) {
505                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
506                 state_changed(technology);
507         }
508
509         g_hash_table_remove(device_table, device);
510
511         return 0;
512 }
513
514 int __connman_technology_enable_device(struct connman_device *device)
515 {
516         struct connman_technology *technology;
517         enum connman_service_type type;
518
519         DBG("device %p", device);
520
521         technology = g_hash_table_lookup(device_table, device);
522         if (technology == NULL)
523                 return -ENXIO;
524
525         if (g_atomic_int_get(&technology->blocked))
526                 return -ERFKILL;
527
528         type = __connman_device_get_service_type(device);
529         __connman_notifier_enable(type);
530
531         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
532                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
533                 state_changed(technology);
534         }
535
536         return 0;
537 }
538
539 int __connman_technology_disable_device(struct connman_device *device)
540 {
541         struct connman_technology *technology;
542         enum connman_service_type type;
543         GSList *list;
544
545         DBG("device %p", device);
546
547         type = __connman_device_get_service_type(device);
548         __connman_notifier_disable(type);
549
550         technology = g_hash_table_lookup(device_table, device);
551         if (technology == NULL)
552                 return -ENXIO;
553
554         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
555                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
556                 state_changed(technology);
557         }
558
559         for (list = technology->device_list; list; list = list->next) {
560                 struct connman_device *device = list->data;
561
562                 if (__connman_device_get_blocked(device) == FALSE)
563                         return 0;
564         }
565
566         technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
567         state_changed(technology);
568
569         return 0;
570 }
571
572 static void technology_blocked(struct connman_technology *technology,
573                                 connman_bool_t blocked)
574 {
575         GSList *list;
576
577         for (list = technology->device_list; list; list = list->next) {
578                 struct connman_device *device = list->data;
579
580                 __connman_device_set_blocked(device, blocked);
581         }
582 }
583
584 int __connman_technology_add_rfkill(unsigned int index,
585                                         enum connman_service_type type,
586                                                 connman_bool_t softblock,
587                                                 connman_bool_t hardblock)
588 {
589         struct connman_technology *technology;
590         struct connman_rfkill *rfkill;
591         connman_bool_t blocked;
592
593         DBG("index %u type %d soft %u hard %u", index, type,
594                                                         softblock, hardblock);
595
596         technology = technology_get(type);
597         if (technology == NULL)
598                 return -ENXIO;
599
600         rfkill = g_try_new0(struct connman_rfkill, 1);
601         if (rfkill == NULL)
602                 return -ENOMEM;
603
604         rfkill->index = index;
605         rfkill->type = type;
606         rfkill->softblock = softblock;
607         rfkill->hardblock = hardblock;
608
609         g_hash_table_replace(rfkill_table, &rfkill->index, technology);
610
611         g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
612
613         blocked = (softblock || hardblock) ? TRUE : FALSE;
614         if (blocked == FALSE)
615                 return 0;
616
617         if (g_atomic_int_exchange_and_add(&technology->blocked, 1) == 0) {
618                 technology_blocked(technology, TRUE);
619
620                 technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
621                 state_changed(technology);
622         }
623
624         return 0;
625 }
626
627 int __connman_technology_update_rfkill(unsigned int index,
628                                                 connman_bool_t softblock,
629                                                 connman_bool_t hardblock)
630 {
631         struct connman_technology *technology;
632         struct connman_rfkill *rfkill;
633         connman_bool_t blocked, old_blocked;
634
635         DBG("index %u soft %u hard %u", index, softblock, hardblock);
636
637         technology = g_hash_table_lookup(rfkill_table, &index);
638         if (technology == NULL)
639                 return -ENXIO;
640
641         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
642         if (rfkill == NULL)
643                 return -ENXIO;
644
645         old_blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
646         blocked = (softblock || hardblock) ? TRUE : FALSE;
647
648         rfkill->softblock = softblock;
649         rfkill->hardblock = hardblock;
650
651         if (blocked == old_blocked)
652                 return 0;
653
654         if (blocked) {
655                 guint n_blocked;
656
657                 n_blocked =
658                         g_atomic_int_exchange_and_add(&technology->blocked, 1);
659                 if (n_blocked != g_hash_table_size(technology->rfkill_list) - 1)
660                         return 0;
661
662                 technology_blocked(technology, blocked);
663                 technology->state = CONNMAN_TECHNOLOGY_STATE_BLOCKED;
664                 state_changed(technology);
665         } else {
666                 if (g_atomic_int_dec_and_test(&technology->blocked) == FALSE)
667                         return 0;
668
669                 technology_blocked(technology, blocked);
670                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
671                 state_changed(technology);
672         }
673
674         return 0;
675 }
676
677 int __connman_technology_remove_rfkill(unsigned int index)
678 {
679         struct connman_technology *technology;
680
681         DBG("index %u", index);
682
683         technology = g_hash_table_lookup(rfkill_table, &index);
684         if (technology == NULL)
685                 return -ENXIO;
686
687         g_hash_table_remove(technology->rfkill_list, &index);
688
689         g_hash_table_remove(rfkill_table, &index);
690
691         if (g_atomic_int_dec_and_test(&technology->blocked) == TRUE) {
692                 technology_blocked(technology, FALSE);
693                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
694                 state_changed(technology);
695         }
696
697         return 0;
698 }
699
700 connman_bool_t __connman_technology_get_blocked(enum connman_service_type type)
701 {
702         struct connman_technology *technology;
703
704         technology = technology_get(type);
705         if (technology == NULL)
706                 return FALSE;
707
708         if (g_atomic_int_get(&technology->blocked))
709                 return TRUE;
710
711         return FALSE;
712 }
713
714 int __connman_technology_init(void)
715 {
716         DBG("");
717
718         connection = connman_dbus_get_connection();
719
720         rfkill_table = g_hash_table_new_full(g_int_hash, g_int_equal,
721                                                 NULL, unregister_technology);
722         device_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
723                                                 NULL, unregister_technology);
724
725         return 0;
726 }
727
728 void __connman_technology_cleanup(void)
729 {
730         DBG("");
731
732         g_hash_table_destroy(device_table);
733         g_hash_table_destroy(rfkill_table);
734
735         dbus_connection_unref(connection);
736 }