Add support for technology states
[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 *device_table;
33 static GSList *technology_list = NULL;
34
35 enum connman_technology_state {
36         CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
37         CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
38         CONNMAN_TECHNOLOGY_STATE_AVAILABLE = 2,
39         CONNMAN_TECHNOLOGY_STATE_ENABLED   = 3,
40         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 4,
41 };
42
43 struct connman_technology {
44         gint refcount;
45         enum connman_service_type type;
46         enum connman_technology_state state;
47         char *path;
48         GSList *device_list;
49 };
50
51 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
52 {
53         GSList *list;
54
55         for (list = technology_list; list; list = list->next) {
56                 struct connman_technology *technology = list->data;
57
58                 if (technology->path == NULL)
59                         continue;
60
61                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
62                                                         &technology->path);
63         }
64 }
65
66 static void technologies_changed(void)
67 {
68         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
69                         CONNMAN_MANAGER_INTERFACE, "Technologies",
70                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
71 }
72
73 static void device_list(DBusMessageIter *iter, void *user_data)
74 {
75         struct connman_technology *technology = user_data;
76         GSList *list;
77
78         for (list = technology->device_list; list; list = list->next) {
79                 struct connman_device *device = list->data;
80                 const char *path;
81
82                 path = connman_device_get_path(device);
83                 if (path == NULL)
84                         continue;
85
86                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
87                                                                         &path);
88         }
89 }
90
91 static void devices_changed(struct connman_technology *technology)
92 {
93         connman_dbus_property_changed_array(technology->path,
94                         CONNMAN_TECHNOLOGY_INTERFACE, "Devices",
95                         DBUS_TYPE_OBJECT_PATH, device_list, technology);
96 }
97
98 static const char *state2string(enum connman_technology_state state)
99 {
100         switch (state) {
101         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
102                 break;
103         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
104                 return "offline";
105         case CONNMAN_TECHNOLOGY_STATE_AVAILABLE:
106                 return "available";
107         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
108                 return "enabled";
109         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
110                 return "connected";
111         }
112
113         return NULL;
114 }
115
116 static void state_changed(struct connman_technology *technology)
117 {
118         const char *str;
119
120         str = state2string(technology->state);
121         if (str == NULL)
122                 return;
123
124         connman_dbus_property_changed_basic(technology->path,
125                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
126                                                 DBUS_TYPE_STRING, &str);
127 }
128
129 static const char *get_name(enum connman_service_type type)
130 {
131         switch (type) {
132         case CONNMAN_SERVICE_TYPE_UNKNOWN:
133         case CONNMAN_SERVICE_TYPE_SYSTEM:
134         case CONNMAN_SERVICE_TYPE_GPS:
135         case CONNMAN_SERVICE_TYPE_VPN:
136                 break;
137         case CONNMAN_SERVICE_TYPE_ETHERNET:
138                 return "Wired";
139         case CONNMAN_SERVICE_TYPE_WIFI:
140                 return "WiFi";
141         case CONNMAN_SERVICE_TYPE_WIMAX:
142                 return "WiMAX";
143         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
144                 return "Bluetooth";
145         case CONNMAN_SERVICE_TYPE_CELLULAR:
146                 return "3G";
147         }
148
149         return NULL;
150 }
151
152 static DBusMessage *get_properties(DBusConnection *conn,
153                                         DBusMessage *message, void *user_data)
154 {
155         struct connman_technology *technology = user_data;
156         DBusMessage *reply;
157         DBusMessageIter array, dict;
158         const char *str;
159
160         reply = dbus_message_new_method_return(message);
161         if (reply == NULL)
162                 return NULL;
163
164         dbus_message_iter_init_append(reply, &array);
165
166         connman_dbus_dict_open(&array, &dict);
167
168         str = state2string(technology->state);
169         if (str != NULL)
170                 connman_dbus_dict_append_basic(&dict, "State",
171                                                 DBUS_TYPE_STRING, &str);
172
173         str = get_name(technology->type);
174         if (str != NULL)
175                 connman_dbus_dict_append_basic(&dict, "Name",
176                                                 DBUS_TYPE_STRING, &str);
177
178         str = __connman_service_type2string(technology->type);
179         if (str != NULL)
180                 connman_dbus_dict_append_basic(&dict, "Type",
181                                                 DBUS_TYPE_STRING, &str);
182
183         connman_dbus_dict_append_array(&dict, "Devices",
184                         DBUS_TYPE_OBJECT_PATH, device_list, technology);
185
186         connman_dbus_dict_close(&array, &dict);
187
188         return reply;
189 }
190
191 static GDBusMethodTable technology_methods[] = {
192         { "GetProperties", "", "a{sv}", get_properties },
193         { },
194 };
195
196 static GDBusSignalTable technology_signals[] = {
197         { "PropertyChanged", "sv" },
198         { },
199 };
200
201 static struct connman_technology *technology_find(enum connman_service_type type)
202 {
203         GSList *list;
204
205         DBG("type %d", type);
206
207         for (list = technology_list; list; list = list->next) {
208                 struct connman_technology *technology = list->data;
209
210                 if (technology->type == type)
211                         return technology;
212         }
213
214         return NULL;
215 }
216
217 static struct connman_technology *technology_get(enum connman_service_type type)
218 {
219         struct connman_technology *technology;
220         const char *str;
221
222         DBG("type %d", type);
223
224         technology = technology_find(type);
225         if (technology != NULL) {
226                 g_atomic_int_inc(&technology->refcount);
227                 goto done;
228         }
229
230         str = __connman_service_type2string(type);
231         if (str == NULL)
232                 return NULL;
233
234         technology = g_try_new0(struct connman_technology, 1);
235         if (technology == NULL)
236                 return NULL;
237
238         technology->refcount = 1;
239
240         technology->type = type;
241         technology->path = g_strdup_printf("%s/technology/%s",
242                                                         CONNMAN_PATH, str);
243
244         if (g_dbus_register_interface(connection, technology->path,
245                                         CONNMAN_TECHNOLOGY_INTERFACE,
246                                         technology_methods, technology_signals,
247                                         NULL, technology, NULL) == FALSE) {
248                 connman_error("Failed to register %s", technology->path);
249                 g_free(technology);
250                 return NULL;
251         }
252
253         technology_list = g_slist_append(technology_list, technology);
254
255         technologies_changed();
256
257 done:
258         DBG("technology %p", technology);
259
260         return technology;
261 }
262
263 static void technology_put(struct connman_technology *technology)
264 {
265         DBG("technology %p", technology);
266
267         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
268                 return;
269
270         technology_list = g_slist_remove(technology_list, technology);
271
272         technologies_changed();
273
274         g_dbus_unregister_interface(connection, technology->path,
275                                                 CONNMAN_TECHNOLOGY_INTERFACE);
276
277         g_free(technology->path);
278         g_free(technology);
279 }
280
281 static void unregister_device(gpointer data)
282 {
283         struct connman_technology *technology = data;
284
285         DBG("technology %p", technology);
286
287         technology_put(technology);
288 }
289
290 int __connman_technology_add_device(struct connman_device *device)
291 {
292         struct connman_technology *technology;
293         enum connman_service_type type;
294
295         DBG("device %p", device);
296
297         type = __connman_device_get_service_type(device);
298
299         technology = technology_get(type);
300         if (technology == NULL)
301                 return -ENXIO;
302
303         g_hash_table_insert(device_table, device, technology);
304
305         if (technology->device_list == NULL) {
306                 technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
307                 state_changed(technology);
308         }
309
310         technology->device_list = g_slist_append(technology->device_list,
311                                                                 device);
312         devices_changed(technology);
313
314         return 0;
315 }
316
317 int __connman_technology_remove_device(struct connman_device *device)
318 {
319         struct connman_technology *technology;
320
321         DBG("device %p", device);
322
323         technology = g_hash_table_lookup(device_table, device);
324         if (technology == NULL)
325                 return -ENXIO;
326
327         technology->device_list = g_slist_remove(technology->device_list,
328                                                                 device);
329         devices_changed(technology);
330
331         if (technology->device_list == NULL) {
332                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
333                 state_changed(technology);
334         }
335
336         g_hash_table_remove(device_table, device);
337
338         return 0;
339 }
340
341 int __connman_technology_init(void)
342 {
343         DBG("");
344
345         connection = connman_dbus_get_connection();
346
347         device_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
348                                                 NULL, unregister_device);
349
350         return 0;
351 }
352
353 void __connman_technology_cleanup(void)
354 {
355         DBG("");
356
357         g_hash_table_destroy(device_table);
358
359         dbus_connection_unref(connection);
360 }