8b06a77e110dc57c2f563add43648665b8f8c1bd
[platform/upstream/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 };
40
41 enum connman_technology_state {
42         CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
43         CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
44         CONNMAN_TECHNOLOGY_STATE_AVAILABLE = 2,
45         CONNMAN_TECHNOLOGY_STATE_ENABLED   = 3,
46         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 4,
47 };
48
49 struct connman_technology {
50         gint refcount;
51         enum connman_service_type type;
52         enum connman_technology_state state;
53         char *path;
54         GHashTable *rfkill_list;
55         GSList *device_list;
56         gint enabled;
57 };
58
59 static void free_rfkill(gpointer data)
60 {
61         struct connman_rfkill *rfkill = data;
62
63         g_free(rfkill);
64 }
65
66 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
67 {
68         GSList *list;
69
70         for (list = technology_list; list; list = list->next) {
71                 struct connman_technology *technology = list->data;
72
73                 if (technology->path == NULL)
74                         continue;
75
76                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
77                                                         &technology->path);
78         }
79 }
80
81 static void technologies_changed(void)
82 {
83         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
84                         CONNMAN_MANAGER_INTERFACE, "Technologies",
85                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
86 }
87
88 static void device_list(DBusMessageIter *iter, void *user_data)
89 {
90         struct connman_technology *technology = user_data;
91         GSList *list;
92
93         for (list = technology->device_list; list; list = list->next) {
94                 struct connman_device *device = list->data;
95                 const char *path;
96
97                 path = connman_device_get_path(device);
98                 if (path == NULL)
99                         continue;
100
101                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
102                                                                         &path);
103         }
104 }
105
106 static void devices_changed(struct connman_technology *technology)
107 {
108         connman_dbus_property_changed_array(technology->path,
109                         CONNMAN_TECHNOLOGY_INTERFACE, "Devices",
110                         DBUS_TYPE_OBJECT_PATH, device_list, technology);
111 }
112
113 static const char *state2string(enum connman_technology_state state)
114 {
115         switch (state) {
116         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
117                 break;
118         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
119                 return "offline";
120         case CONNMAN_TECHNOLOGY_STATE_AVAILABLE:
121                 return "available";
122         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
123                 return "enabled";
124         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
125                 return "connected";
126         }
127
128         return NULL;
129 }
130
131 static void state_changed(struct connman_technology *technology)
132 {
133         const char *str;
134
135         str = state2string(technology->state);
136         if (str == NULL)
137                 return;
138
139         connman_dbus_property_changed_basic(technology->path,
140                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
141                                                 DBUS_TYPE_STRING, &str);
142 }
143
144 static const char *get_name(enum connman_service_type type)
145 {
146         switch (type) {
147         case CONNMAN_SERVICE_TYPE_UNKNOWN:
148         case CONNMAN_SERVICE_TYPE_SYSTEM:
149         case CONNMAN_SERVICE_TYPE_GPS:
150         case CONNMAN_SERVICE_TYPE_VPN:
151                 break;
152         case CONNMAN_SERVICE_TYPE_ETHERNET:
153                 return "Wired";
154         case CONNMAN_SERVICE_TYPE_WIFI:
155                 return "WiFi";
156         case CONNMAN_SERVICE_TYPE_WIMAX:
157                 return "WiMAX";
158         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
159                 return "Bluetooth";
160         case CONNMAN_SERVICE_TYPE_CELLULAR:
161                 return "3G";
162         }
163
164         return NULL;
165 }
166
167 static DBusMessage *get_properties(DBusConnection *conn,
168                                         DBusMessage *message, void *user_data)
169 {
170         struct connman_technology *technology = user_data;
171         DBusMessage *reply;
172         DBusMessageIter array, dict;
173         const char *str;
174
175         reply = dbus_message_new_method_return(message);
176         if (reply == NULL)
177                 return NULL;
178
179         dbus_message_iter_init_append(reply, &array);
180
181         connman_dbus_dict_open(&array, &dict);
182
183         str = state2string(technology->state);
184         if (str != NULL)
185                 connman_dbus_dict_append_basic(&dict, "State",
186                                                 DBUS_TYPE_STRING, &str);
187
188         str = get_name(technology->type);
189         if (str != NULL)
190                 connman_dbus_dict_append_basic(&dict, "Name",
191                                                 DBUS_TYPE_STRING, &str);
192
193         str = __connman_service_type2string(technology->type);
194         if (str != NULL)
195                 connman_dbus_dict_append_basic(&dict, "Type",
196                                                 DBUS_TYPE_STRING, &str);
197
198         connman_dbus_dict_append_array(&dict, "Devices",
199                         DBUS_TYPE_OBJECT_PATH, device_list, technology);
200
201         connman_dbus_dict_close(&array, &dict);
202
203         return reply;
204 }
205
206 static GDBusMethodTable technology_methods[] = {
207         { "GetProperties", "", "a{sv}", get_properties },
208         { },
209 };
210
211 static GDBusSignalTable technology_signals[] = {
212         { "PropertyChanged", "sv" },
213         { },
214 };
215
216 static struct connman_technology *technology_find(enum connman_service_type type)
217 {
218         GSList *list;
219
220         DBG("type %d", type);
221
222         for (list = technology_list; list; list = list->next) {
223                 struct connman_technology *technology = list->data;
224
225                 if (technology->type == type)
226                         return technology;
227         }
228
229         return NULL;
230 }
231
232 static struct connman_technology *technology_get(enum connman_service_type type)
233 {
234         struct connman_technology *technology;
235         const char *str;
236
237         DBG("type %d", type);
238
239         technology = technology_find(type);
240         if (technology != NULL) {
241                 g_atomic_int_inc(&technology->refcount);
242                 goto done;
243         }
244
245         str = __connman_service_type2string(type);
246         if (str == NULL)
247                 return NULL;
248
249         technology = g_try_new0(struct connman_technology, 1);
250         if (technology == NULL)
251                 return NULL;
252
253         technology->refcount = 1;
254
255         technology->type = type;
256         technology->path = g_strdup_printf("%s/technology/%s",
257                                                         CONNMAN_PATH, str);
258
259         technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
260                                                         NULL, free_rfkill);
261         technology->device_list = NULL;
262
263         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
264
265         if (g_dbus_register_interface(connection, technology->path,
266                                         CONNMAN_TECHNOLOGY_INTERFACE,
267                                         technology_methods, technology_signals,
268                                         NULL, technology, NULL) == FALSE) {
269                 connman_error("Failed to register %s", technology->path);
270                 g_free(technology);
271                 return NULL;
272         }
273
274         technology_list = g_slist_append(technology_list, technology);
275
276         technologies_changed();
277
278 done:
279         DBG("technology %p", technology);
280
281         return technology;
282 }
283
284 static void technology_put(struct connman_technology *technology)
285 {
286         DBG("technology %p", technology);
287
288         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
289                 return;
290
291         technology_list = g_slist_remove(technology_list, technology);
292
293         technologies_changed();
294
295         g_dbus_unregister_interface(connection, technology->path,
296                                                 CONNMAN_TECHNOLOGY_INTERFACE);
297
298         g_slist_free(technology->device_list);
299         g_hash_table_destroy(technology->rfkill_list);
300
301         g_free(technology->path);
302         g_free(technology);
303 }
304
305 static void unregister_technology(gpointer data)
306 {
307         struct connman_technology *technology = data;
308
309         technology_put(technology);
310 }
311
312 int __connman_technology_add_device(struct connman_device *device)
313 {
314         struct connman_technology *technology;
315         enum connman_service_type type;
316
317         DBG("device %p", device);
318
319         type = __connman_device_get_service_type(device);
320         __connman_notifier_register(type);
321
322         technology = technology_get(type);
323         if (technology == NULL)
324                 return -ENXIO;
325
326         g_hash_table_insert(device_table, device, technology);
327
328         if (technology->device_list == NULL) {
329                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
330                 state_changed(technology);
331         }
332
333         technology->device_list = g_slist_append(technology->device_list,
334                                                                 device);
335         devices_changed(technology);
336
337         return 0;
338 }
339
340 int __connman_technology_remove_device(struct connman_device *device)
341 {
342         struct connman_technology *technology;
343         enum connman_service_type type;
344
345         DBG("device %p", device);
346
347         type = __connman_device_get_service_type(device);
348         __connman_notifier_unregister(type);
349
350         technology = g_hash_table_lookup(device_table, device);
351         if (technology == NULL)
352                 return -ENXIO;
353
354         technology->device_list = g_slist_remove(technology->device_list,
355                                                                 device);
356         devices_changed(technology);
357
358         if (technology->device_list == NULL) {
359                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
360                 state_changed(technology);
361         }
362
363         g_hash_table_remove(device_table, device);
364
365         return 0;
366 }
367
368 int __connman_technology_enable_device(struct connman_device *device)
369 {
370         struct connman_technology *technology;
371         enum connman_service_type type;
372
373         DBG("device %p", device);
374
375         type = __connman_device_get_service_type(device);
376         __connman_notifier_enable(type);
377
378         technology = g_hash_table_lookup(device_table, device);
379         if (technology == NULL)
380                 return -ENXIO;
381
382         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
383                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
384                 state_changed(technology);
385         }
386
387         return 0;
388 }
389
390 int __connman_technology_disable_device(struct connman_device *device)
391 {
392         struct connman_technology *technology;
393         enum connman_service_type type;
394
395         DBG("device %p", device);
396
397         type = __connman_device_get_service_type(device);
398         __connman_notifier_disable(type);
399
400         technology = g_hash_table_lookup(device_table, device);
401         if (technology == NULL)
402                 return -ENXIO;
403
404         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
405                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
406                 state_changed(technology);
407         }
408
409         return 0;
410 }
411
412 int __connman_technology_add_rfkill(unsigned int index,
413                                         enum connman_service_type type,
414                                                 connman_bool_t softblock,
415                                                 connman_bool_t hardblock)
416 {
417         struct connman_technology *technology;
418         struct connman_rfkill *rfkill;
419
420         DBG("index %u type %d soft %u hard %u", index, type,
421                                                         softblock, hardblock);
422
423         rfkill = g_try_new0(struct connman_rfkill, 1);
424         if (rfkill == NULL)
425                 return -ENOMEM;
426
427         rfkill->index = index;
428         rfkill->type = type;
429
430         technology = technology_get(type);
431         if (technology == NULL) {
432                 g_free(rfkill);
433                 return -ENXIO;
434         }
435
436         g_hash_table_replace(rfkill_table, &index, technology);
437
438         g_hash_table_replace(technology->rfkill_list, &index, rfkill);
439
440         return 0;
441 }
442
443 int __connman_technology_update_rfkill(unsigned int index,
444                                                 connman_bool_t softblock,
445                                                 connman_bool_t hardblock)
446 {
447         struct connman_technology *technology;
448
449         DBG("index %u soft %u hard %u", index, softblock, hardblock);
450
451         technology = g_hash_table_lookup(rfkill_table, &index);
452         if (technology == NULL)
453                 return -ENXIO;
454
455         return 0;
456 }
457
458 int __connman_technology_remove_rfkill(unsigned int index)
459 {
460         struct connman_technology *technology;
461
462         DBG("index %u", index);
463
464         technology = g_hash_table_lookup(rfkill_table, &index);
465         if (technology == NULL)
466                 return -ENXIO;
467
468         g_hash_table_remove(technology->rfkill_list, &index);
469
470         g_hash_table_remove(rfkill_table, &index);
471
472         return 0;
473 }
474
475 int __connman_technology_init(void)
476 {
477         DBG("");
478
479         connection = connman_dbus_get_connection();
480
481         rfkill_table = g_hash_table_new_full(g_int_hash, g_int_equal,
482                                                 NULL, unregister_technology);
483         device_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
484                                                 NULL, unregister_technology);
485
486         return 0;
487 }
488
489 void __connman_technology_cleanup(void)
490 {
491         DBG("");
492
493         g_hash_table_destroy(device_table);
494         g_hash_table_destroy(rfkill_table);
495
496         dbus_connection_unref(connection);
497 }