Add carrier detection callbacks and driver data functions
[connman] / src / iface.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007  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 <string.h>
27 #include <arpa/inet.h>
28
29 #include <glib.h>
30 #include <gdbus.h>
31
32 #include <hal/libhal.h>
33
34 #include "connman.h"
35
36 static GSList *drivers = NULL;
37
38 int connman_iface_register(struct connman_iface_driver *driver)
39 {
40         DBG("driver %p", driver);
41
42         drivers = g_slist_append(drivers, driver);
43
44         return 0;
45 }
46
47 void connman_iface_unregister(struct connman_iface_driver *driver)
48 {
49         DBG("driver %p", driver);
50
51         drivers = g_slist_remove(drivers, driver);
52 }
53
54 static GSList *interfaces = NULL;
55
56 struct connman_iface *__connman_iface_find(int index)
57 {
58         GSList *list;
59
60         for (list = interfaces; list; list = list->next) {
61                 struct connman_iface *iface = list->data;
62
63                 if (iface->index == index)
64                         return iface;
65         }
66
67         return NULL;
68 }
69
70 void __connman_iface_list(DBusMessageIter *iter)
71 {
72         GSList *list;
73
74         DBG("");
75
76         for (list = interfaces; list; list = list->next) {
77                 struct connman_iface *iface = list->data;
78
79                 dbus_message_iter_append_basic(iter,
80                                 DBUS_TYPE_OBJECT_PATH, &iface->path);
81         }
82 }
83
84 int connman_iface_update(struct connman_iface *iface,
85                                         enum connman_iface_state state)
86 {
87         switch (state) {
88         case CONNMAN_IFACE_STATE_ACTIVE:
89                 if (iface->type == CONNMAN_IFACE_TYPE_80211) {
90                         if (iface->driver->scan)
91                                 iface->driver->scan(iface);
92
93                         if (iface->driver->connect)
94                                 iface->driver->connect(iface, NULL);
95                 }
96                 break;
97
98         case CONNMAN_IFACE_STATE_CONNECTED:
99                 __connman_dhcp_request(iface);
100                 break;
101
102         default:
103                 break;
104         }
105
106         iface->state = state;
107
108         return 0;
109 }
110
111 void connman_iface_indicate_carrier(struct connman_iface *iface, int carrier)
112 {
113         DBG("iface %p carrier %d", iface, carrier);
114 }
115
116 static DBusMessage *enable_iface(DBusConnection *conn,
117                                         DBusMessage *msg, void *data)
118 {
119         struct connman_iface *iface = data;
120         struct connman_iface_driver *driver = iface->driver;
121         DBusMessage *reply;
122
123         DBG("conn %p", conn);
124
125         reply = dbus_message_new_method_return(msg);
126         if (reply == NULL)
127                 return NULL;
128
129         if (driver->activate)
130                 driver->activate(iface);
131
132         dbus_message_append_args(reply, DBUS_TYPE_INVALID);
133
134         return reply;
135 }
136
137 static GDBusMethodTable iface_methods[] = {
138         { "Enable", "", "", enable_iface },
139         { },
140 };
141
142 static dbus_bool_t get_type(DBusConnection *conn,
143                                         DBusMessageIter *iter, void *data)
144 {
145         struct connman_iface *iface = data;
146         const char *type;
147
148         DBG("iface %p", iface);
149
150         switch (iface->type) {
151         case CONNMAN_IFACE_TYPE_80203:
152                 type = "80203";
153                 break;
154         case CONNMAN_IFACE_TYPE_80211:
155                 type = "80211";
156                 break;
157         case CONNMAN_IFACE_TYPE_WIMAX:
158                 type = "wimax";
159                 break;
160         case CONNMAN_IFACE_TYPE_BLUETOOTH:
161                 type = "bluetooth";
162                 break;
163         default:
164                 type = "unknown";
165                 break;
166         }
167
168         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
169
170         return TRUE;
171 }
172
173 static GDBusPropertyTable iface_properties[] = {
174         { "Type", "s", get_type },
175         { },
176 };
177
178 static void device_free(void *data)
179 {
180         struct connman_iface *iface = data;
181
182         DBG("iface %p", iface);
183
184         if (iface->driver && iface->driver->remove)
185                 iface->driver->remove(iface);
186
187         g_free(iface->path);
188         g_free(iface->udi);
189         g_free(iface->sysfs);
190         g_free(iface);
191 }
192
193 static int probe_device(LibHalContext *ctx,
194                         struct connman_iface_driver *driver, const char *udi)
195 {
196         DBusConnection *conn;
197         struct connman_iface *iface;
198         char *temp, *sysfs;
199         int err;
200
201         DBG("ctx %p driver %p udi %s", ctx, driver, udi);
202
203         if (!driver->probe)
204                 return -1;
205
206         iface = g_try_new0(struct connman_iface, 1);
207         if (iface == NULL)
208                 return -1;
209
210         temp = g_path_get_basename(udi);
211         iface->path = g_strdup_printf("%s/%s", CONNMAN_IFACE_BASEPATH, temp);
212         g_free(temp);
213
214         iface->udi = g_strdup(udi);
215
216         DBG("path %s", iface->path);
217
218         sysfs = libhal_device_get_property_string(ctx, udi,
219                                                 "linux.sysfs_path", NULL);
220         if (sysfs != NULL)
221                 iface->sysfs = g_strdup(sysfs);
222
223         iface->index = -1;
224
225         if (g_str_has_prefix(driver->capability, "net") == TRUE)
226                 iface->index = libhal_device_get_property_int(ctx, udi,
227                                                 "net.linux.ifindex", NULL);
228
229         iface->type = CONNMAN_IFACE_TYPE_UNKNOWN;
230         iface->flags = 0;
231         iface->state = CONNMAN_IFACE_STATE_UNKNOWN;
232
233         DBG("iface %p", iface);
234
235         err = driver->probe(iface);
236         if (err < 0) {
237                 device_free(iface);
238                 return -1;
239         }
240
241         iface->driver = driver;
242
243         conn = libhal_ctx_get_dbus_connection(ctx);
244
245         g_dbus_register_object(conn, iface->path, iface, device_free);
246
247         interfaces = g_slist_append(interfaces, iface);
248
249         if ((iface->flags & CONNMAN_IFACE_FLAG_IPV4) &&
250                                                 driver->get_ipv4) {
251                 driver->get_ipv4(iface, &iface->ipv4);
252
253                 DBG("address %s", inet_ntoa(iface->ipv4.address));
254         }
255
256         g_dbus_register_interface(conn, iface->path,
257                                         CONNMAN_IFACE_INTERFACE,
258                                         iface_methods, NULL, iface_properties);
259
260         g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
261                                         CONNMAN_MANAGER_INTERFACE,
262                                         "InterfaceAdded",
263                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
264                                         DBUS_TYPE_INVALID);
265
266         return 0;
267 }
268
269 static void device_added(LibHalContext *ctx, const char *udi)
270 {
271         GSList *list;
272
273         DBG("ctx %p udi %s", ctx, udi);
274
275         for (list = drivers; list; list = list->next) {
276                 struct connman_iface_driver *driver = list->data;
277
278                 if (driver->capability == NULL)
279                         continue;
280
281                 if (libhal_device_query_capability(ctx, udi,
282                                         driver->capability, NULL) == TRUE) {
283                         if (probe_device(ctx, driver, udi) == 0)
284                                 break;
285                 }
286         }
287 }
288
289 static void device_removed(LibHalContext *ctx, const char *udi)
290 {
291         DBusConnection *conn;
292         GSList *list;
293
294         DBG("ctx %p udi %s", ctx, udi);
295
296         conn = libhal_ctx_get_dbus_connection(ctx);
297
298         for (list = interfaces; list; list = list->next) {
299                 struct connman_iface *iface = list->data;
300
301                 if (strcmp(udi, iface->udi) == 0) {
302                         g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
303                                         CONNMAN_MANAGER_INTERFACE,
304                                         "InterfaceRemoved",
305                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
306                                         DBUS_TYPE_INVALID);
307                         interfaces = g_slist_remove(interfaces, iface);
308                         g_dbus_unregister_interface(conn, iface->path,
309                                                 CONNMAN_IFACE_INTERFACE);
310                         g_dbus_unregister_object(conn, iface->path);
311                         break;
312                 }
313         }
314 }
315
316 static void probe_driver(LibHalContext *ctx,
317                                 struct connman_iface_driver *driver)
318 {
319         char **list;
320         int num;
321
322         DBG("ctx %p driver %p", ctx, driver);
323
324         list = libhal_find_device_by_capability(ctx,
325                                         driver->capability, &num, NULL);
326         if (list) {
327                 char **tmp = list;
328
329                 while (*tmp) {
330                         probe_device(ctx, driver, *tmp);
331                         tmp++;
332                 }
333
334                 libhal_free_string_array(list);
335         }
336 }
337
338 static void find_devices(LibHalContext *ctx)
339 {
340         GSList *list;
341
342         DBG("ctx %p", ctx);
343
344         for (list = drivers; list; list = list->next) {
345                 struct connman_iface_driver *driver = list->data;
346
347                 DBG("driver %p", driver);
348
349                 if (driver->capability == NULL)
350                         continue;
351
352                 probe_driver(ctx, driver);
353         }
354 }
355
356 static LibHalContext *hal_ctx = NULL;
357
358 static void hal_init(void *data)
359 {
360         DBusConnection *conn = data;
361
362         DBG("conn %p", conn);
363
364         if (hal_ctx != NULL)
365                 return;
366
367         hal_ctx = libhal_ctx_new();
368         if (hal_ctx == NULL)
369                 return;
370
371         if (libhal_ctx_set_dbus_connection(hal_ctx, conn) == FALSE) {
372                 libhal_ctx_free(hal_ctx);
373                 return;
374         }
375
376         if (libhal_ctx_init(hal_ctx, NULL) == FALSE) {
377                 libhal_ctx_free(hal_ctx);
378                 return ;
379         }
380
381         libhal_ctx_set_device_added(hal_ctx, device_added);
382         libhal_ctx_set_device_removed(hal_ctx, device_removed);
383
384         //libhal_ctx_set_device_new_capability(hal_ctx, new_capability);
385         //libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability);
386
387         find_devices(hal_ctx);
388 }
389
390 static void hal_cleanup(void *data)
391 {
392         DBusConnection *conn = data;
393         GSList *list;
394
395         DBG("conn %p", conn);
396
397         if (hal_ctx == NULL)
398                 return;
399
400         for (list = interfaces; list; list = list->next) {
401                 struct connman_iface *iface = list->data;
402
403                 DBG("path %s", iface->path);
404
405                 g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
406                                         CONNMAN_MANAGER_INTERFACE,
407                                         "InterfaceRemoved",
408                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
409                                         DBUS_TYPE_INVALID);
410
411                 g_dbus_unregister_interface(conn, iface->path,
412                                                 CONNMAN_IFACE_INTERFACE);
413
414                 g_dbus_unregister_object(conn, iface->path);
415         }
416
417         g_slist_free(interfaces);
418
419         interfaces = NULL;
420
421         libhal_ctx_shutdown(hal_ctx, NULL);
422
423         libhal_ctx_free(hal_ctx);
424
425         hal_ctx = NULL;
426 }
427
428 static DBusConnection *connection = NULL;
429 static guint hal_watch = 0;
430
431 int __connman_iface_init(DBusConnection *conn)
432 {
433         DBG("conn %p", conn);
434
435         connection = dbus_connection_ref(conn);
436         if (connection == NULL)
437                 return -1;
438
439         hal_init(connection);
440
441         hal_watch = g_dbus_add_watch(connection, "org.freedesktop.Hal",
442                                 hal_init, hal_cleanup, connection, NULL);
443
444         return 0;
445 }
446
447 void __connman_iface_cleanup(void)
448 {
449         DBG("conn %p", connection);
450
451         g_dbus_remove_watch(connection, hal_watch);
452
453         hal_cleanup(connection);
454
455         dbus_connection_unref(connection);
456 }