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