packaging: Bump to 1.17
[platform/upstream/ofono.git] / dundee / device.c
1 /*
2  *  oFono - Open Source Telephony
3  *
4  *  Copyright (C) 2008-2012  Intel Corporation. All rights reserved.
5  *  Copyright (C) 2012  BMW Car IT GmbH. 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 <errno.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <netinet/ether.h>
30
31 #include <glib.h>
32 #include <gdbus.h>
33 #include <gatchat.h>
34 #include <gatppp.h>
35
36 #include "dundee.h"
37
38 #define PPP_TIMEOUT 15
39
40 static int next_device_id = 0;
41 static GHashTable *device_hash;
42
43 static const char *none_prefix[] = { NULL };
44
45 struct ipv4_settings {
46         char *interface;
47         char *ip;
48         char **nameservers;
49 };
50
51 struct dundee_device {
52         char *path;
53         struct dundee_device_driver *driver;
54         gboolean registered;
55
56         GAtPPP *ppp;
57         GAtChat *chat;
58
59         char *name;
60         gboolean active;
61         struct ipv4_settings settings;
62
63         DBusMessage *pending;
64         guint connect_timeout;
65         void *data;
66 };
67
68 const char *__dundee_device_get_path(struct dundee_device *device)
69 {
70         return device->path;
71 }
72
73 static void settings_append(struct dundee_device *device,
74                                         DBusMessageIter *iter)
75 {
76         DBusMessageIter variant;
77         DBusMessageIter array;
78         char typesig[5];
79         char arraysig[6];
80
81         arraysig[0] = DBUS_TYPE_ARRAY;
82         arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
83         arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
84         arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT;
85         arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
86         arraysig[5] = typesig[4] = '\0';
87
88         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
89                                                 arraysig, &variant);
90
91         dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
92                                                 typesig, &array);
93
94         if (device->active == FALSE)
95                 goto out;
96
97         if (device->settings.interface)
98                 ofono_dbus_dict_append(&array, "Interface",
99                                 DBUS_TYPE_STRING, &device->settings.interface);
100
101         if (device->settings.ip)
102                 ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
103                                         &device->settings.ip);
104
105         if (device->settings.nameservers)
106                 ofono_dbus_dict_append_array(&array, "DomainNameServers",
107                                                 DBUS_TYPE_STRING,
108                                                 &device->settings.nameservers);
109
110 out:
111         dbus_message_iter_close_container(&variant, &array);
112
113         dbus_message_iter_close_container(iter, &variant);
114 }
115
116 static void settings_append_dict(struct dundee_device *device,
117                                 DBusMessageIter *dict)
118 {
119         DBusMessageIter entry;
120         const char *key = "Settings";
121
122         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
123                                                 NULL, &entry);
124
125         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
126
127         settings_append(device, &entry);
128
129         dbus_message_iter_close_container(dict, &entry);
130 }
131
132 void __dundee_device_append_properties(struct dundee_device *device,
133                                         DBusMessageIter *dict)
134 {
135         settings_append_dict(device, dict);
136
137         ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING,
138                                 &device->name);
139
140         ofono_dbus_dict_append(dict, "Active", DBUS_TYPE_BOOLEAN,
141                                 &device->active);
142 }
143
144 void __dundee_device_foreach(dundee_device_foreach_func func, void *userdata)
145 {
146         GHashTableIter iter;
147         gpointer key, value;
148
149         DBG("");
150
151         g_hash_table_iter_init(&iter, device_hash);
152
153         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
154                 struct dundee_device *device = value;
155
156                 func(device, userdata);
157         }
158 }
159
160 static void settings_changed(struct dundee_device *device)
161 {
162         DBusConnection *conn = ofono_dbus_get_connection();
163         DBusMessage *signal;
164         DBusMessageIter iter;
165         const char *key = "Settings";
166
167         signal = dbus_message_new_signal(device->path,
168                                         DUNDEE_DEVICE_INTERFACE,
169                                         "PropertyChanged");
170
171         if (signal == NULL)
172                 return;
173         dbus_message_iter_init_append(signal, &iter);
174         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
175
176         settings_append(device, &iter);
177
178         g_dbus_send_message(conn, signal);
179 }
180
181 static DBusMessage *device_get_properties(DBusConnection *conn,
182                                         DBusMessage *msg, void *data)
183 {
184         struct dundee_device *device = data;
185         DBusMessage *reply;
186         DBusMessageIter iter;
187         DBusMessageIter dict;
188
189         reply = dbus_message_new_method_return(msg);
190         if (reply == NULL)
191                 return NULL;
192
193         dbus_message_iter_init_append(reply, &iter);
194
195         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
196                                         OFONO_PROPERTIES_ARRAY_SIGNATURE,
197                                         &dict);
198
199         __dundee_device_append_properties(device, &dict);
200
201         dbus_message_iter_close_container(&iter, &dict);
202
203         return reply;
204 }
205
206
207 static void debug(const char *str, void *data)
208 {
209         DBG("%s: %s\n", (const char *) data, str);
210 }
211
212 static void ppp_connect(const char *iface, const char *local, const char *peer,
213                         const char *dns1, const char *dns2,
214                         gpointer user_data)
215 {
216         DBusConnection *conn = ofono_dbus_get_connection();
217         struct dundee_device *device = user_data;
218         const char *dns[3] = { dns1, dns2, 0 };
219
220         DBG("%p", device);
221         DBG("Network Device: %s\n", iface);
222         DBG("IP Address: %s\n", local);
223         DBG("Peer IP Address: %s\n", peer);
224         DBG("Primary DNS Server: %s\n", dns1);
225         DBG("Secondary DNS Server: %s\n", dns2);
226
227         if (device->connect_timeout > 0) {
228                 g_source_remove(device->connect_timeout);
229                 device->connect_timeout = 0;
230         }
231
232         g_free(device->settings.interface);
233         device->settings.interface = g_strdup(iface);
234         if (device->settings.interface == NULL)
235                 goto err;
236
237         g_free(device->settings.ip);
238         device->settings.ip = g_strdup(local);
239         if (device->settings.ip == NULL)
240                 goto err;
241
242         g_strfreev(device->settings.nameservers);
243         device->settings.nameservers = g_strdupv((gchar **)dns);
244         if (device->settings.nameservers == NULL)
245                 goto err;
246
247         __ofono_dbus_pending_reply(&device->pending,
248                         dbus_message_new_method_return(device->pending));
249         device->pending = NULL;
250
251         device->active = TRUE;
252
253         settings_changed(device);
254         ofono_dbus_signal_property_changed(conn, device->path,
255                                         DUNDEE_DEVICE_INTERFACE, "Active",
256                                         DBUS_TYPE_BOOLEAN, &device->active);
257
258         return;
259
260 err:
261         g_free(device->settings.interface);
262         g_free(device->settings.ip);
263         g_strfreev(device->settings.nameservers);
264         device->settings.interface = NULL;
265         device->settings.ip = NULL;
266         device->settings.nameservers = NULL;
267
268         __ofono_dbus_pending_reply(&device->pending,
269                                 __dundee_error_failed(device->pending));
270         device->pending = NULL;
271 }
272
273 void dundee_device_disconnect(const struct dundee_error *error,
274                                                 struct dundee_device *device)
275 {
276         if (device == NULL)
277                 return;
278
279         DBG("%s", device->path);
280
281         g_at_chat_unref(device->chat);
282         device->chat = NULL;
283
284         if (device->pending == NULL)
285                 return;
286
287         if (error->type != DUNDEE_ERROR_TYPE_NO_ERROR) {
288                 __ofono_dbus_pending_reply(&device->pending,
289                                         __dundee_error_failed(device->pending));
290                 goto out;
291         }
292
293         __ofono_dbus_pending_reply(&device->pending,
294                 dbus_message_new_method_return(device->pending));
295
296 out:
297         device->pending = NULL;
298 }
299
300 static void disconnect_callback(const struct dundee_error *error, void *data)
301 {
302         struct dundee_device *device = data;
303         dundee_device_disconnect(error, device);
304 }
305
306 static gboolean ppp_connect_timeout(gpointer user_data)
307 {
308         struct dundee_device *device = user_data;
309
310         if (device->pending != NULL) {
311                 __ofono_dbus_pending_reply(&device->pending,
312                                 __dundee_error_timed_out(device->pending));
313                 device->pending = NULL;
314         }
315
316         device->driver->disconnect(device, disconnect_callback, device);
317
318         device->connect_timeout = 0;
319
320         return FALSE;
321 }
322
323 static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data)
324 {
325         DBusConnection *conn = ofono_dbus_get_connection();
326         struct dundee_device *device = user_data;
327
328         DBG("%p", device);
329         DBG("PPP Link down: %d\n", reason);
330
331         g_at_ppp_unref(device->ppp);
332         device->ppp = NULL;
333
334         g_at_chat_resume(device->chat);
335
336         g_free(device->settings.interface);
337         g_free(device->settings.ip);
338         g_strfreev(device->settings.nameservers);
339         device->settings.interface = NULL;
340         device->settings.ip = NULL;
341         device->settings.nameservers = NULL;
342
343         device->active = FALSE;
344
345         settings_changed(device);
346         ofono_dbus_signal_property_changed(conn, device->path,
347                                         DUNDEE_DEVICE_INTERFACE, "Active",
348                                         DBUS_TYPE_BOOLEAN, &device->active);
349
350         device->driver->disconnect(device, disconnect_callback, device);
351 }
352
353 static void dial_cb(gboolean ok, GAtResult *result, gpointer user_data)
354 {
355         struct dundee_device *device = user_data;
356         GAtIO *io;
357
358         if (!ok) {
359                 DBG("Unable to define context\n");
360                 goto err;
361         }
362
363         /* get the data IO channel */
364         io = g_at_chat_get_io(device->chat);
365
366         /*
367          * shutdown gatchat or else it tries to take all the input
368          * from the modem and does not let PPP get it.
369          */
370         g_at_chat_suspend(device->chat);
371
372         /* open ppp */
373         device->ppp = g_at_ppp_new();
374         if (device->ppp == NULL) {
375                 DBG("Unable to create PPP object\n");
376                 goto err;
377         }
378         g_at_ppp_set_debug(device->ppp, debug, "PPP");
379
380         device->connect_timeout = g_timeout_add_seconds(PPP_TIMEOUT,
381                                                 ppp_connect_timeout, device);
382
383         /* set connect and disconnect callbacks */
384         g_at_ppp_set_connect_function(device->ppp, ppp_connect, device);
385         g_at_ppp_set_disconnect_function(device->ppp, ppp_disconnect, device);
386
387         /* open the ppp connection */
388         g_at_ppp_open(device->ppp, io);
389
390         return;
391
392 err:
393         __ofono_dbus_pending_reply(&device->pending,
394                                 __dundee_error_failed(device->pending));
395         device->pending = NULL;
396
397         device->driver->disconnect(device, disconnect_callback, device);
398 }
399
400 static int device_dial_setup(struct dundee_device *device, int fd)
401 {
402         GAtSyntax *syntax;
403         GIOChannel *io;
404
405         io = g_io_channel_unix_new(fd);
406         if (io == NULL)
407                 return -EIO;
408
409         syntax = g_at_syntax_new_gsm_permissive();
410         device->chat = g_at_chat_new(io, syntax);
411         g_io_channel_unref(io);
412         g_at_syntax_unref(syntax);
413
414         if (device->chat == NULL)
415                 return -EIO;
416
417         g_at_chat_set_debug(device->chat, debug, "Control");
418
419         g_at_chat_send(device->chat, "ATD*99#", none_prefix, dial_cb,
420                         device, NULL);
421
422         return 0;
423 }
424
425 static void connect_callback(const struct dundee_error *error,
426                                 int fd, void *data)
427 {
428         struct dundee_device *device = data;
429         int err;
430
431         DBG("%p", device);
432
433         if (error->type != DUNDEE_ERROR_TYPE_NO_ERROR)
434                 goto err;
435
436         err = device_dial_setup(device, fd);
437         if (err < 0)
438                 goto err;
439
440         return;
441
442 err:
443         __ofono_dbus_pending_reply(&device->pending,
444                                 __dundee_error_failed(device->pending));
445         device->pending = NULL;
446 }
447
448 static DBusMessage *set_property_active(struct dundee_device *device,
449                                         DBusMessage *msg,
450                                         DBusMessageIter *var)
451 {
452         ofono_bool_t active;
453
454         DBG("%p path %s", device, device->path);
455
456         if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN)
457                 return __dundee_error_invalid_args(msg);
458
459         if (device->pending)
460                 return __dundee_error_in_progress(msg);
461
462         dbus_message_iter_get_basic(var, &active);
463
464         device->pending = dbus_message_ref(msg);
465
466         if (active)
467                 device->driver->connect(device, connect_callback, device);
468         else if (device->ppp)
469                 g_at_ppp_shutdown(device->ppp);
470
471         return NULL;
472 }
473
474 static DBusMessage *device_set_property(DBusConnection *conn,
475                                         DBusMessage *msg, void *data)
476 {
477         struct dundee_device *device = data;
478         DBusMessageIter iter, var;
479         const char *name;
480
481         if (dbus_message_iter_init(msg, &iter) == FALSE)
482                 return __dundee_error_invalid_args(msg);
483
484         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
485                 return __dundee_error_invalid_args(msg);
486
487         dbus_message_iter_get_basic(&iter, &name);
488         dbus_message_iter_next(&iter);
489
490         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
491                 return __dundee_error_invalid_args(msg);
492
493         dbus_message_iter_recurse(&iter, &var);
494
495         if (g_str_equal(name, "Active"))
496                 return set_property_active(device, msg, &var);
497
498         return __dundee_error_invalid_args(msg);
499 }
500
501 static const GDBusMethodTable device_methods[] = {
502         { GDBUS_METHOD("GetProperties",
503                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
504                         device_get_properties) },
505         { GDBUS_ASYNC_METHOD("SetProperty",
506                         GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
507                         NULL, device_set_property) },
508         { }
509 };
510
511 static const GDBusSignalTable device_signals[] = {
512         { GDBUS_SIGNAL("PropertyChanged",
513                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
514         { }
515 };
516
517 static int register_device(struct dundee_device *device)
518 {
519         DBusConnection *conn = ofono_dbus_get_connection();
520         DBusMessage *signal;
521         DBusMessageIter iter;
522         DBusMessageIter dict;
523
524         DBG("%p path %s", device, device->path);
525
526         if (!g_dbus_register_interface(conn, device->path,
527                                         DUNDEE_DEVICE_INTERFACE,
528                                         device_methods, device_signals,
529                                         NULL, device, NULL)) {
530                 ofono_error("Could not register Device %s", device->path);
531                 return -EIO;
532         }
533
534         signal = dbus_message_new_signal(DUNDEE_MANAGER_PATH,
535                                                 DUNDEE_MANAGER_INTERFACE,
536                                                 "DeviceAdded");
537
538         if (signal == NULL)
539                 return -ENOMEM;
540
541         dbus_message_iter_init_append(signal, &iter);
542
543         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
544                                         &device->path);
545         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
546                                         OFONO_PROPERTIES_ARRAY_SIGNATURE,
547                                         &dict);
548         __dundee_device_append_properties(device, &dict);
549         dbus_message_iter_close_container(&iter, &dict);
550
551         g_dbus_send_message(conn, signal);
552
553         return 0;
554 }
555
556 static int unregister_device(struct dundee_device *device)
557 {
558         DBusConnection *conn = ofono_dbus_get_connection();
559
560         DBG("%p path %s", device, device->path);
561
562         g_dbus_unregister_interface(conn, device->path,
563                                         DUNDEE_DEVICE_INTERFACE);
564
565         g_dbus_emit_signal(conn, DUNDEE_MANAGER_PATH,
566                                 DUNDEE_MANAGER_INTERFACE, "DeviceRemoved",
567                                 DBUS_TYPE_OBJECT_PATH, &device->path,
568                                 DBUS_TYPE_INVALID);
569
570         return 0;
571 }
572
573 static void destroy_device(gpointer user)
574 {
575         struct dundee_device *device = user;
576
577         if (device->chat != NULL)
578                 g_at_chat_unref(device->chat);
579
580         if (device->ppp != NULL)
581                 g_at_ppp_unref(device->ppp);
582
583         if (device->pending)
584                 dbus_message_unref(device->pending);
585
586         g_free(device->settings.interface);
587         g_free(device->settings.ip);
588         g_strfreev(device->settings.nameservers);
589
590         g_free(device->path);
591         g_free(device->name);
592
593         g_free(device);
594 }
595
596 struct dundee_device *dundee_device_create(struct dundee_device_driver *d)
597 {
598         struct dundee_device *device;
599
600         device = g_try_new0(struct dundee_device, 1);
601         if (device == NULL)
602                 return NULL;
603
604         device->driver = d;
605
606         device->path = g_strdup_printf("/device%d", next_device_id);
607         if (device->path == NULL) {
608                 g_free(device);
609                 return NULL;
610         }
611
612         next_device_id += 1;
613
614         return device;
615 }
616
617 int dundee_device_register(struct dundee_device *device)
618 {
619         int err;
620
621         err = register_device(device);
622         if (err < 0)
623                 return err;
624
625         device->registered = TRUE;
626
627         g_hash_table_insert(device_hash, g_strdup(device->path), device);
628
629         return 0;
630 }
631
632 void dundee_device_unregister(struct dundee_device *device)
633 {
634         DBG("%p", device);
635
636         unregister_device(device);
637
638         device->registered = FALSE;
639
640         g_hash_table_remove(device_hash, device->path);
641 }
642
643 void dundee_device_set_data(struct dundee_device *device, void *data)
644 {
645         device->data = data;
646 }
647
648 void *dundee_device_get_data(struct dundee_device *device)
649 {
650         return device->data;
651 }
652
653 int dundee_device_set_name(struct dundee_device *device, const char *name)
654 {
655         DBusConnection *conn = ofono_dbus_get_connection();
656
657         DBG("%p name %s", device, name);
658
659         g_free(device->name);
660         device->name = g_strdup(name);
661
662         if (device->registered == FALSE)
663                 return 0;
664
665         ofono_dbus_signal_property_changed(conn, device->path,
666                                         DUNDEE_DEVICE_INTERFACE, "Name",
667                                         DBUS_TYPE_STRING, &device->name);
668
669         return 0;
670 }
671
672 static void device_shutdown(gpointer key, gpointer value, gpointer user_data)
673 {
674         struct dundee_device *device = value;
675
676         unregister_device(device);
677 }
678
679 void __dundee_device_shutdown(void)
680 {
681         g_hash_table_foreach(device_hash, device_shutdown, NULL);
682
683         __dundee_exit();
684 }
685
686 int __dundee_device_init(void)
687 {
688         DBG("");
689
690         device_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
691                                                 g_free, destroy_device);
692
693         return 0;
694 }
695
696 void __dundee_device_cleanup(void)
697 {
698         DBG("");
699
700         g_hash_table_destroy(device_hash);
701 }