2 * oFono - Open Source Telephony
4 * Copyright (C) 2008-2012 Intel Corporation. All rights reserved.
5 * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved.
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.
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.
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
29 #include <netinet/ether.h>
38 #define PPP_TIMEOUT 15
40 static int next_device_id = 0;
41 static GHashTable *device_hash;
43 static const char *none_prefix[] = { NULL };
45 struct ipv4_settings {
51 struct dundee_device {
53 struct dundee_device_driver *driver;
61 struct ipv4_settings settings;
64 guint connect_timeout;
68 const char *__dundee_device_get_path(struct dundee_device *device)
73 static void settings_append(struct dundee_device *device,
74 DBusMessageIter *iter)
76 DBusMessageIter variant;
77 DBusMessageIter array;
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';
88 dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
91 dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
94 if (device->active == FALSE)
97 if (device->settings.interface)
98 ofono_dbus_dict_append(&array, "Interface",
99 DBUS_TYPE_STRING, &device->settings.interface);
101 if (device->settings.ip)
102 ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
103 &device->settings.ip);
105 if (device->settings.nameservers)
106 ofono_dbus_dict_append_array(&array, "DomainNameServers",
108 &device->settings.nameservers);
111 dbus_message_iter_close_container(&variant, &array);
113 dbus_message_iter_close_container(iter, &variant);
116 static void settings_append_dict(struct dundee_device *device,
117 DBusMessageIter *dict)
119 DBusMessageIter entry;
120 const char *key = "Settings";
122 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
125 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
127 settings_append(device, &entry);
129 dbus_message_iter_close_container(dict, &entry);
132 void __dundee_device_append_properties(struct dundee_device *device,
133 DBusMessageIter *dict)
135 settings_append_dict(device, dict);
137 ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING,
140 ofono_dbus_dict_append(dict, "Active", DBUS_TYPE_BOOLEAN,
144 void __dundee_device_foreach(dundee_device_foreach_func func, void *userdata)
151 g_hash_table_iter_init(&iter, device_hash);
153 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
154 struct dundee_device *device = value;
156 func(device, userdata);
160 static void settings_changed(struct dundee_device *device)
162 DBusConnection *conn = ofono_dbus_get_connection();
164 DBusMessageIter iter;
165 const char *key = "Settings";
167 signal = dbus_message_new_signal(device->path,
168 DUNDEE_DEVICE_INTERFACE,
173 dbus_message_iter_init_append(signal, &iter);
174 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
176 settings_append(device, &iter);
178 g_dbus_send_message(conn, signal);
181 static DBusMessage *device_get_properties(DBusConnection *conn,
182 DBusMessage *msg, void *data)
184 struct dundee_device *device = data;
186 DBusMessageIter iter;
187 DBusMessageIter dict;
189 reply = dbus_message_new_method_return(msg);
193 dbus_message_iter_init_append(reply, &iter);
195 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
196 OFONO_PROPERTIES_ARRAY_SIGNATURE,
199 __dundee_device_append_properties(device, &dict);
201 dbus_message_iter_close_container(&iter, &dict);
207 static void debug(const char *str, void *data)
209 DBG("%s: %s\n", (const char *) data, str);
212 static void ppp_connect(const char *iface, const char *local, const char *peer,
213 const char *dns1, const char *dns2,
216 DBusConnection *conn = ofono_dbus_get_connection();
217 struct dundee_device *device = user_data;
218 const char *dns[3] = { dns1, dns2, 0 };
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);
227 if (device->connect_timeout > 0) {
228 g_source_remove(device->connect_timeout);
229 device->connect_timeout = 0;
232 g_free(device->settings.interface);
233 device->settings.interface = g_strdup(iface);
234 if (device->settings.interface == NULL)
237 g_free(device->settings.ip);
238 device->settings.ip = g_strdup(local);
239 if (device->settings.ip == NULL)
242 g_strfreev(device->settings.nameservers);
243 device->settings.nameservers = g_strdupv((gchar **)dns);
244 if (device->settings.nameservers == NULL)
247 __ofono_dbus_pending_reply(&device->pending,
248 dbus_message_new_method_return(device->pending));
249 device->pending = NULL;
251 device->active = TRUE;
253 settings_changed(device);
254 ofono_dbus_signal_property_changed(conn, device->path,
255 DUNDEE_DEVICE_INTERFACE, "Active",
256 DBUS_TYPE_BOOLEAN, &device->active);
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;
268 __ofono_dbus_pending_reply(&device->pending,
269 __dundee_error_failed(device->pending));
270 device->pending = NULL;
273 void dundee_device_disconnect(const struct dundee_error *error,
274 struct dundee_device *device)
279 DBG("%s", device->path);
281 g_at_chat_unref(device->chat);
284 if (device->pending == NULL)
287 if (error->type != DUNDEE_ERROR_TYPE_NO_ERROR) {
288 __ofono_dbus_pending_reply(&device->pending,
289 __dundee_error_failed(device->pending));
293 __ofono_dbus_pending_reply(&device->pending,
294 dbus_message_new_method_return(device->pending));
297 device->pending = NULL;
300 static void disconnect_callback(const struct dundee_error *error, void *data)
302 struct dundee_device *device = data;
303 dundee_device_disconnect(error, device);
306 static gboolean ppp_connect_timeout(gpointer user_data)
308 struct dundee_device *device = user_data;
310 if (device->pending != NULL) {
311 __ofono_dbus_pending_reply(&device->pending,
312 __dundee_error_timed_out(device->pending));
313 device->pending = NULL;
316 device->driver->disconnect(device, disconnect_callback, device);
318 device->connect_timeout = 0;
323 static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data)
325 DBusConnection *conn = ofono_dbus_get_connection();
326 struct dundee_device *device = user_data;
329 DBG("PPP Link down: %d\n", reason);
331 g_at_ppp_unref(device->ppp);
334 g_at_chat_resume(device->chat);
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;
343 device->active = FALSE;
345 settings_changed(device);
346 ofono_dbus_signal_property_changed(conn, device->path,
347 DUNDEE_DEVICE_INTERFACE, "Active",
348 DBUS_TYPE_BOOLEAN, &device->active);
350 device->driver->disconnect(device, disconnect_callback, device);
353 static void dial_cb(gboolean ok, GAtResult *result, gpointer user_data)
355 struct dundee_device *device = user_data;
359 DBG("Unable to define context\n");
363 /* get the data IO channel */
364 io = g_at_chat_get_io(device->chat);
367 * shutdown gatchat or else it tries to take all the input
368 * from the modem and does not let PPP get it.
370 g_at_chat_suspend(device->chat);
373 device->ppp = g_at_ppp_new();
374 if (device->ppp == NULL) {
375 DBG("Unable to create PPP object\n");
378 g_at_ppp_set_debug(device->ppp, debug, "PPP");
380 device->connect_timeout = g_timeout_add_seconds(PPP_TIMEOUT,
381 ppp_connect_timeout, device);
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);
387 /* open the ppp connection */
388 g_at_ppp_open(device->ppp, io);
393 __ofono_dbus_pending_reply(&device->pending,
394 __dundee_error_failed(device->pending));
395 device->pending = NULL;
397 device->driver->disconnect(device, disconnect_callback, device);
400 static int device_dial_setup(struct dundee_device *device, int fd)
405 io = g_io_channel_unix_new(fd);
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);
414 if (device->chat == NULL)
417 g_at_chat_set_debug(device->chat, debug, "Control");
419 g_at_chat_send(device->chat, "ATD*99#", none_prefix, dial_cb,
425 static void connect_callback(const struct dundee_error *error,
428 struct dundee_device *device = data;
433 if (error->type != DUNDEE_ERROR_TYPE_NO_ERROR)
436 err = device_dial_setup(device, fd);
443 __ofono_dbus_pending_reply(&device->pending,
444 __dundee_error_failed(device->pending));
445 device->pending = NULL;
448 static DBusMessage *set_property_active(struct dundee_device *device,
450 DBusMessageIter *var)
454 DBG("%p path %s", device, device->path);
456 if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN)
457 return __dundee_error_invalid_args(msg);
460 return __dundee_error_in_progress(msg);
462 dbus_message_iter_get_basic(var, &active);
464 device->pending = dbus_message_ref(msg);
467 device->driver->connect(device, connect_callback, device);
468 else if (device->ppp)
469 g_at_ppp_shutdown(device->ppp);
474 static DBusMessage *device_set_property(DBusConnection *conn,
475 DBusMessage *msg, void *data)
477 struct dundee_device *device = data;
478 DBusMessageIter iter, var;
481 if (dbus_message_iter_init(msg, &iter) == FALSE)
482 return __dundee_error_invalid_args(msg);
484 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
485 return __dundee_error_invalid_args(msg);
487 dbus_message_iter_get_basic(&iter, &name);
488 dbus_message_iter_next(&iter);
490 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
491 return __dundee_error_invalid_args(msg);
493 dbus_message_iter_recurse(&iter, &var);
495 if (g_str_equal(name, "Active"))
496 return set_property_active(device, msg, &var);
498 return __dundee_error_invalid_args(msg);
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) },
511 static const GDBusSignalTable device_signals[] = {
512 { GDBUS_SIGNAL("PropertyChanged",
513 GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
517 static int register_device(struct dundee_device *device)
519 DBusConnection *conn = ofono_dbus_get_connection();
521 DBusMessageIter iter;
522 DBusMessageIter dict;
524 DBG("%p path %s", device, device->path);
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);
534 signal = dbus_message_new_signal(DUNDEE_MANAGER_PATH,
535 DUNDEE_MANAGER_INTERFACE,
541 dbus_message_iter_init_append(signal, &iter);
543 dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
545 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
546 OFONO_PROPERTIES_ARRAY_SIGNATURE,
548 __dundee_device_append_properties(device, &dict);
549 dbus_message_iter_close_container(&iter, &dict);
551 g_dbus_send_message(conn, signal);
556 static int unregister_device(struct dundee_device *device)
558 DBusConnection *conn = ofono_dbus_get_connection();
560 DBG("%p path %s", device, device->path);
562 g_dbus_unregister_interface(conn, device->path,
563 DUNDEE_DEVICE_INTERFACE);
565 g_dbus_emit_signal(conn, DUNDEE_MANAGER_PATH,
566 DUNDEE_MANAGER_INTERFACE, "DeviceRemoved",
567 DBUS_TYPE_OBJECT_PATH, &device->path,
573 static void destroy_device(gpointer user)
575 struct dundee_device *device = user;
577 if (device->chat != NULL)
578 g_at_chat_unref(device->chat);
580 if (device->ppp != NULL)
581 g_at_ppp_unref(device->ppp);
584 dbus_message_unref(device->pending);
586 g_free(device->settings.interface);
587 g_free(device->settings.ip);
588 g_strfreev(device->settings.nameservers);
590 g_free(device->path);
591 g_free(device->name);
596 struct dundee_device *dundee_device_create(struct dundee_device_driver *d)
598 struct dundee_device *device;
600 device = g_try_new0(struct dundee_device, 1);
606 device->path = g_strdup_printf("/device%d", next_device_id);
607 if (device->path == NULL) {
617 int dundee_device_register(struct dundee_device *device)
621 err = register_device(device);
625 device->registered = TRUE;
627 g_hash_table_insert(device_hash, g_strdup(device->path), device);
632 void dundee_device_unregister(struct dundee_device *device)
636 unregister_device(device);
638 device->registered = FALSE;
640 g_hash_table_remove(device_hash, device->path);
643 void dundee_device_set_data(struct dundee_device *device, void *data)
648 void *dundee_device_get_data(struct dundee_device *device)
653 int dundee_device_set_name(struct dundee_device *device, const char *name)
655 DBusConnection *conn = ofono_dbus_get_connection();
657 DBG("%p name %s", device, name);
659 g_free(device->name);
660 device->name = g_strdup(name);
662 if (device->registered == FALSE)
665 ofono_dbus_signal_property_changed(conn, device->path,
666 DUNDEE_DEVICE_INTERFACE, "Name",
667 DBUS_TYPE_STRING, &device->name);
672 static void device_shutdown(gpointer key, gpointer value, gpointer user_data)
674 struct dundee_device *device = value;
676 unregister_device(device);
679 void __dundee_device_shutdown(void)
681 g_hash_table_foreach(device_hash, device_shutdown, NULL);
686 int __dundee_device_init(void)
690 device_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
691 g_free, destroy_device);
696 void __dundee_device_cleanup(void)
700 g_hash_table_destroy(device_hash);