From: Daniel Wagner <daniel.wagner(a)bmw-carit.de>
---
Hi,
so this is the first very rough version of the new DUN daemon.
There are several things I am sure need to be fixed such as the
names (dun.c used to be gsmdial.c. Though both names do not reall
match IMO).
I will split up this huge patch in smaller ones after the general
approach is accepted.
cheers,
daniel
/me hopes his fire proof underpents will be worth its money...
Makefile.am | 24 ++
bootstrap-configure | 1 +
configure.ac | 4 +
doc/elect-api.txt | 92 +++++++
elect/device.c | 685 +++++++++++++++++++++++++++++++++++++++++++++++++
elect/dun.c | 270 +++++++++++++++++++
elect/elect.conf | 23 ++
elect/elect.h | 102 ++++++++
elect/elect.service | 11 +
elect/main.c | 253 ++++++++++++++++++
elect/manager.c | 115 +++++++++
plugins/bluetooth.h | 1 +
test/elect-connect | 20 ++
test/elect-disconnect | 20 ++
14 files changed, 1621 insertions(+), 0 deletions(-)
create mode 100644 doc/elect-api.txt
create mode 100644 elect/device.c
create mode 100644 elect/dun.c
create mode 100644 elect/elect.conf
create mode 100644 elect/elect.h
create mode 100644 elect/elect.service
create mode 100644 elect/main.c
create mode 100644 elect/manager.c
create mode 100755 test/elect-connect
create mode 100755 test/elect-disconnect
diff --git a/Makefile.am b/Makefile.am
index 337aeb7..657203e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -647,6 +647,30 @@ tools_lookup_provider_name_SOURCES = plugins/mbpi.c plugins/mbpi.h \
tools_lookup_provider_name_LDADD = @GLIB_LIBS@
endif
+if ELECT
+
+sbin_PROGRAMS += elect/electd
+
+elect_electd_SOURCES = $(gdbus_sources) $(gatchat_sources) $(btio_sources) \
+ elect/main.c elect/manager.c elect/device.c elect/dun.c \
+ src/log.c src/dbus.c plugins/bluetooth.c
+
+elect_electd_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
+
+if DATAFILES
+
+dist_dbusconf_DATA += elect/elect.conf
+
+if SYSTEMD
+systemdunitdir += @SYSTEMD_UNITDIR@
+
+systemdunit_DATA += elect/elect.service
+endif
+
+endif
+
+endif
+
noinst_PROGRAMS += gatchat/gsmdial gatchat/test-server gatchat/test-qcdm
gatchat_gsmdial_SOURCES = gatchat/gsmdial.c $(gatchat_sources)
diff --git a/bootstrap-configure b/bootstrap-configure
index db70c66..d80ed8b 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -14,4 +14,5 @@ fi
--localstatedir=/var \
--enable-capng \
--enable-tools \
+ --enable-elect \
--disable-datafiles $*
diff --git a/configure.ac b/configure.ac
index 5e4d34f..f8f3387 100644
--- a/configure.ac
+++ b/configure.ac
@@ -165,6 +165,10 @@ if (test "${enable_tools}" = "yes"); then
fi
AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes")
+AC_ARG_ENABLE(elect, AC_HELP_STRING([--enable-elect],
+ [enable DUN deamon support]), [enable_elect=${enableval}])
+AM_CONDITIONAL(ELECT, test "${enable_elect}" = "yes")
+
AC_ARG_ENABLE(atmodem, AC_HELP_STRING([--disable-atmodem],
[disable ETSI AT modem support]),
[enable_atmodem=${enableval}])
diff --git a/doc/elect-api.txt b/doc/elect-api.txt
new file mode 100644
index 0000000..49a4e25
--- /dev/null
+++ b/doc/elect-api.txt
@@ -0,0 +1,92 @@
+
+Manager hierarchy
+=================
+
+Service org.ofono.elect
+Interface org.ofono.elect.Manager
+Object path /
+
+Methods array{object,dict} GetDevices()
+
+ Get an array of device objects and properties
+ that represent the currently attached devices.
+
+ This method call should only be used once when an
+ application starts up. Further device additions
+ and removal shall be monitored via DeviceAdded and
+ DeviceRemoved signals.
+
+Signals DeviceAdded(object path, dict properties)
+
+ Signal that is sent when a new device is added. It
+ contains the object path of new device and its
+ properties.
+
+ DeviceRemoved(object path)
+
+ Signal that is sent when a device has been removed.
+ The object path is no longer accessible after this
+ signal and only emitted for reference.
+
+
+Device hierarchy
+================
+
+Service org.ofono.elect
+Interface org.ofono.elect.Device
+Object path [variable prefix]/{device0,device1,...}
+
+Methods dict GetProperties()
+
+ Returns properties for the device object. See
+ the properties section for available properties.
+
+ Connect()
+
+ Establish a connection.
+
+ Disconnect()
+
+ Tear down a connection.
+
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties string Name [readonly]
+
+ Friendly name of the device.
+
+ boolean Active [readwrite]
+
+ Holds whether the device is connected.
+
+ dict Settings [readonly]
+
+ Holds all the IP network settings.
+
+ string Interface [readonly, optional]
+
+ Holds the interface of the network interface
+ used by this connection (e.g. "ppp0" "usb0").
+
+ string Method [readonly, optional]
+
+ Holds the IP network config method.
+ "static"- Set IP network statically
+ "dhcp" - Set IP network through DHCP
+
+ string Address [readonly, optional]
+
+ Holds the IP address for this connection.
+
+ string Netmask [readonly, optional]
+
+ Holds the Netmask for this connection.
+
+ array{string} DomainNameServers [readonly, optional]
+
+ Holds the list of domain name servers for this
+ connection.
diff --git a/elect/device.c b/elect/device.c
new file mode 100644
index 0000000..1592cab
--- /dev/null
+++ b/elect/device.c
@@ -0,0 +1,685 @@
+/*
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <netinet/ether.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "plugins/bluetooth.h"
+
+#include "elect.h"
+
+#define STATIC_IP_NETMASK "255.255.255.255"
+
+static DBusConnection *connection;
+
+static GHashTable *device_hash;
+
+struct ipv4_settings {
+ char *interface;
+
+ char *ip;
+ char **nameservers;
+};
+
+struct elect_device {
+ DBusPendingCall *call;
+ DBusMessage *pending;
+
+ char *path;
+ char *address;
+ char *tty;
+
+ char *elect_path;
+ gboolean active;
+ struct ipv4_settings settings;
+ char *name;
+
+ struct elect_dun *dun;
+};
+
+static char *get_ident(const char *path)
+{
+ char *pos;
+
+ if (*path != '/')
+ return NULL;
+
+ pos = strrchr(path, '/');
+ if (pos == NULL)
+ return NULL;
+
+ return pos + 1;
+}
+
+const char *__elect_device_get_path(struct elect_device *device)
+{
+ return device->elect_path;
+}
+
+static void settings_append(struct elect_device *device,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter variant;
+ DBusMessageIter array;
+ char typesig[5];
+ char arraysig[6];
+ const char *method;
+ const char *netmask;
+
+ arraysig[0] = DBUS_TYPE_ARRAY;
+ arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+ arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
+ arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT;
+ arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
+ arraysig[5] = typesig[4] = '\0';
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ arraysig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ typesig, &array);
+
+ if (device->active == FALSE)
+ goto out;
+
+ if (device->settings.interface)
+ ofono_dbus_dict_append(&array, "Interface",
+ DBUS_TYPE_STRING, &device->settings.interface);
+
+ method = "static";
+ ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method);
+
+ if (device->settings.ip)
+ ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
+ &device->settings.ip);
+
+ netmask = STATIC_IP_NETMASK;
+ ofono_dbus_dict_append(&array, "Netmask", DBUS_TYPE_STRING,
+ &netmask);
+
+ if (device->settings.nameservers)
+ ofono_dbus_dict_append_array(&array, "DomainNameServers",
+ DBUS_TYPE_STRING,
+ &device->settings.nameservers);
+
+out:
+ dbus_message_iter_close_container(&variant, &array);
+
+ dbus_message_iter_close_container(iter, &variant);
+}
+
+static void settings_append_dict(struct elect_device *device,
+ DBusMessageIter *dict)
+{
+ DBusMessageIter entry;
+ const char *key = "Settings";
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ settings_append(device, &entry);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+void __elect_device_append_properties(struct elect_device *device,
+ DBusMessageIter *dict)
+{
+ settings_append_dict(device, dict);
+
+ ofono_dbus_dict_append(dict, "Active", DBUS_TYPE_BOOLEAN,
+ &device->active);
+
+ ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING,
+ &device->name);
+}
+
+void __elect_device_foreach(elect_device_foreach_func func, void *userdata)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ DBG("");
+
+ g_hash_table_iter_init(&iter, device_hash);
+
+ while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+ struct elect_device *device = value;
+
+ func(device, userdata);
+ }
+}
+
+static void settings_changed(struct elect_device *device)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+ const char *key = "Settings";
+
+ signal = dbus_message_new_signal(device->elect_path,
+ ELECT_DEVICE_INTERFACE,
+ "PropertyChanged");
+
+ if (signal == NULL)
+ return;
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
+
+ settings_append(device, &iter);
+
+ g_dbus_send_message(connection, signal);
+}
+
+static void connect_cb(const char *interface, const char *ip,
+ const char *peer, const char *dns1, const char *dns2,
+ void *data)
+{
+ struct elect_device *device = data;
+ const char *dns[3] = { dns1, dns2, 0 };
+
+ DBG("%p", device);
+
+ g_free(device->settings.interface);
+ device->settings.interface = g_strdup(interface);
+ if (device->settings.interface == NULL)
+ goto err;
+
+ g_free(device->settings.ip);
+ device->settings.ip = g_strdup(ip);
+ if (device->settings.ip == NULL)
+ goto err;
+
+ g_strfreev(device->settings.nameservers);
+ device->settings.nameservers = g_strdupv((gchar **)dns);
+ if (device->settings.nameservers == NULL)
+ goto err;
+
+ device->active = TRUE;
+
+ settings_changed(device);
+ ofono_dbus_signal_property_changed(connection, device->elect_path,
+ ELECT_DEVICE_INTERFACE, "Active",
+ DBUS_TYPE_BOOLEAN, &device->active);
+
+ return;
+
+err:
+ g_free(device->settings.interface);
+ g_free(device->settings.ip);
+ g_strfreev(device->settings.nameservers);
+ device->settings.interface = NULL;
+ device->settings.ip = NULL;
+ device->settings.nameservers = NULL;
+}
+
+static int bt_disconnect(struct elect_device *device);
+
+static void disconnect_cb(void *data)
+{
+ struct elect_device *device = data;
+
+ DBG("%p", device);
+
+ g_free(device->settings.interface);
+ g_free(device->settings.ip);
+ g_strfreev(device->settings.nameservers);
+ device->settings.interface = NULL;
+ device->settings.ip = NULL;
+ device->settings.nameservers = NULL;
+
+ device->active = FALSE;
+
+ settings_changed(device);
+ ofono_dbus_signal_property_changed(connection, device->elect_path,
+ ELECT_DEVICE_INTERFACE, "Active",
+ DBUS_TYPE_BOOLEAN, &device->active);
+
+ bt_disconnect(device);
+}
+
+static void bt_connect_reply(DBusPendingCall *call, gpointer user_data)
+{
+ struct elect_device *device = user_data;
+ DBusMessage *reply;
+ DBusError derr;
+ char *tty;
+
+ DBG("%p", device);
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ device->call = NULL;
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ DBG("Connection to bt serial returned with error: %s, %s",
+ derr.name, derr.message);
+
+ dbus_error_free(&derr);
+ goto done;
+ }
+
+ dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &tty,
+ DBUS_TYPE_INVALID);
+
+ DBG("%p tty %s", device, tty);
+
+ device->tty = g_strdup(tty);
+
+ device->dun = __elect_dun_create(device->tty, connect_cb,
+ disconnect_cb, device);
+ __elect_dun_connect(device->dun);
+
+done:
+ dbus_message_unref(reply);
+
+ if (device->pending != NULL) {
+ DBusMessage *reply;
+
+ if (device->tty != NULL)
+ reply = dbus_message_new_method_return(device->pending);
+ else
+ reply = __ofono_error_failed(device->pending);
+
+ __ofono_dbus_pending_reply(&device->pending, reply);
+
+ device->pending = NULL;
+ }
+}
+
+static int bt_connect(struct elect_device *device)
+{
+ DBusPendingCall *call;
+ int status;
+ char *profile = "dun";
+
+ DBG("%p", device);
+
+ status = bluetooth_send_with_reply(device->path,
+ BLUEZ_SERIAL_INTERFACE, "Connect",
+ &call, bt_connect_reply,
+ device, NULL, DBUS_TIMEOUT,
+ DBUS_TYPE_STRING, &profile,
+ DBUS_TYPE_INVALID);
+ if (status < 0)
+ return -EINVAL;
+
+ device->call = call;
+
+ return -EINPROGRESS;
+}
+
+static void bt_disconnect_reply(DBusPendingCall *call, gpointer user_data)
+{
+ struct elect_device *device = user_data;
+ DBusMessage *reply;
+
+ DBG("%p", device);
+
+ reply = dbus_pending_call_steal_reply(call);
+ dbus_message_unref(reply);
+
+ device->call = NULL;
+
+ g_free(device->tty);
+ device->tty = NULL;
+
+ __elect_dun_unref(device->dun);
+ device->dun = NULL;
+}
+
+static int bt_disconnect(struct elect_device *device)
+{
+ DBusPendingCall *call;
+ int status;
+
+ DBG("%p", device);
+
+ if (device->tty == NULL)
+ return 0;
+
+ status = bluetooth_send_with_reply(device->path,
+ BLUEZ_SERIAL_INTERFACE, "Disconnect",
+ &call, bt_disconnect_reply,
+ device, NULL, DBUS_TIMEOUT,
+ DBUS_TYPE_STRING, &device->tty,
+ DBUS_TYPE_INVALID);
+ if (status < 0)
+ return -EINVAL;
+
+ device->call = call;
+
+ return -EINPROGRESS;
+}
+
+static int register_device(struct elect_device *device);
+
+static int bt_probe(const char *path, const char *dev_addr,
+ const char *adapter_addr, const char *alias)
+{
+ struct elect_device *device;
+ char buf[256];
+
+ DBG("");
+
+ /* We already have this device in our hash, ignore */
+ if (g_hash_table_lookup(device_hash, path) != NULL)
+ return -EALREADY;
+
+ ofono_info("Using device: %s, devaddr: %s, adapter: %s",
+ path, dev_addr, adapter_addr);
+
+ strcpy(buf, "dun/");
+ bluetooth_create_path(dev_addr, adapter_addr, buf + 4, sizeof(buf) - 4);
+
+ device = g_try_new0(struct elect_device, 1);
+ if (device == 0) {
+ DBG("Failed to create device data structure");
+ return -ENOMEM;
+ }
+
+ DBG("%p", device);
+
+ device->path = g_strdup(path);
+ if (device->path == NULL)
+ goto free;
+
+ device->address = g_strdup(dev_addr);
+ if (device->address == NULL)
+ goto free;
+
+ device->name = g_strdup(alias);
+ if (device->name == NULL)
+ goto free;
+
+ device->elect_path = g_strdup_printf("/device/%s", get_ident(path));
+ if (device->elect_path == NULL)
+ goto free;
+
+ g_hash_table_insert(device_hash, g_strdup(path), device);
+
+ register_device(device);
+
+ return 0;
+
+free:
+ g_free(device->path);
+ g_free(device->address);
+ g_free(device->name);
+ g_free(device->elect_path);
+ g_free(device);
+
+ return -ENOMEM;
+}
+
+static int unregister_device(struct elect_device *device);
+
+static gboolean bt_remove_device(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct elect_device *device = value;
+ const char *path = key;
+ const char *prefix = user_data;
+
+ DBG("%p", device);
+
+ if (device->call != NULL)
+ dbus_pending_call_cancel(device->call);
+
+ if (prefix && g_str_has_prefix(path, prefix) == FALSE)
+ return FALSE;
+
+ unregister_device(device);
+
+ return TRUE;
+}
+
+static void bt_remove(const char *prefix)
+{
+ DBG("%s", prefix);
+
+ if (device_hash == NULL)
+ return;
+
+ g_hash_table_foreach_remove(device_hash, bt_remove_device,
+ (gpointer) prefix);
+}
+
+static void bt_set_alias(const char *path, const char *alias)
+{
+ struct elect_device *device;
+
+ DBG("");
+
+ if (path == NULL || alias == NULL)
+ return;
+
+ device = g_hash_table_lookup(device_hash, path);
+ if (device == NULL)
+ return;
+
+ g_free(device->name);
+ device->name = g_strdup(alias);
+}
+
+static struct bluetooth_profile dun_profile = {
+ .name = "dun_dt",
+ .probe = bt_probe,
+ .remove = bt_remove,
+ .set_alias = bt_set_alias,
+};
+
+static DBusMessage *device_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct elect_device *device = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ OFONO_PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ __elect_device_append_properties(device, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *device_connect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct elect_device *device = data;
+ int err;
+
+ DBG("");
+
+ err = bt_connect(device);
+ if (err != -EINPROGRESS && err < 0)
+ return __ofono_error_failed(msg);
+
+ device->pending = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *device_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct elect_device *device = data;
+
+ DBG("");
+
+ if (device->dun)
+ __elect_dun_disconnect(device->dun);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable device_methods[] = {
+ { "GetProperties", "", "a{sv}", device_get_properties },
+ { "Connect", "", "", device_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", device_disconnect },
+ { }
+};
+
+static GDBusSignalTable device_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static int register_device(struct elect_device *device)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ DBG("%p elect_path %s", device, device->elect_path);
+
+ if (!g_dbus_register_interface(connection, device->elect_path,
+ ELECT_DEVICE_INTERFACE,
+ device_methods, device_signals,
+ NULL, device, NULL)) {
+ ofono_error("Could not register Device %s", device->path);
+ return -EIO;
+ }
+
+ signal = dbus_message_new_signal(ELECT_MANAGER_PATH,
+ ELECT_MANAGER_INTERFACE,
+ "DeviceAdded");
+
+ if (signal == NULL)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &device->elect_path);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ OFONO_PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+ __elect_device_append_properties(device, &dict);
+ dbus_message_iter_close_container(&iter, &dict);
+
+ g_dbus_send_message(connection, signal);
+
+ return 0;
+}
+
+static int unregister_device(struct elect_device *device)
+{
+ DBG("%p elect_path %s", device, device->elect_path);
+
+ g_dbus_unregister_interface(connection, device->elect_path,
+ ELECT_DEVICE_INTERFACE);
+
+ g_dbus_emit_signal(connection, ELECT_MANAGER_PATH,
+ ELECT_MANAGER_INTERFACE, "DeviceRemoved",
+ DBUS_TYPE_OBJECT_PATH, &device->elect_path,
+ DBUS_TYPE_INVALID);
+
+ return 0;
+}
+
+static void destroy_device(gpointer user)
+{
+ struct elect_device *device = user;
+
+ if (device->pending)
+ dbus_message_unref(device->pending);
+
+ if (device->call)
+ dbus_pending_call_cancel(device->call);
+
+ g_free(device->settings.interface);
+ g_free(device->settings.ip);
+ g_strfreev(device->settings.nameservers);
+
+ g_free(device->path);
+ g_free(device->address);
+ g_free(device->tty);
+ g_free(device->elect_path);
+ g_free(device->name);
+
+ g_free(device);
+}
+
+static void device_shutdown(gpointer key, gpointer value, gpointer user_data)
+{
+ struct elect_device *device = value;
+
+ unregister_device(device);
+}
+
+void __elect_device_shutdown(void)
+{
+ g_hash_table_foreach(device_hash, device_shutdown, NULL);
+
+ __elect_exit();
+}
+
+int __elect_device_init(DBusConnection *conn)
+{
+ int err;
+
+ DBG("");
+
+ connection = conn;
+
+ err = bluetooth_register_uuid(DUN_GW_UUID, &dun_profile);
+ if (err < 0)
+ return err;
+
+
+ device_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, destroy_device);
+
+ return 0;
+}
+
+void __elect_device_cleanup(void)
+{
+ DBG("");
+
+ bluetooth_unregister_uuid(DUN_GW_UUID);
+
+ g_hash_table_destroy(device_hash);
+}
diff --git a/elect/dun.c b/elect/dun.c
new file mode 100644
index 0000000..012591e
--- /dev/null
+++ b/elect/dun.c
@@ -0,0 +1,270 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+#include <gatppp.h>
+
+#include "elect.h"
+
+static const char *none_prefix[] = { NULL };
+
+struct elect_dun {
+ char *path;
+
+ GAtPPP *ppp;
+ GAtChat *control;
+ GAtChat *modem;
+
+ elect_dun_connect_cb_t connect_cb;
+ elect_dun_disconnect_cb_t disconnect_cb;
+ void *data;
+};
+
+static void dun_debug(const char *str, void *data)
+{
+ DBG("%s: %s\n", (const char *) data, str);
+}
+
+static void kill_ppp(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct elect_dun *dun = user_data;
+
+ DBG("%s %s\n", dun->path, ok ? "ok" : "failed");
+
+ if (ok == FALSE)
+ return;
+
+ g_at_ppp_unref(dun->ppp);
+ dun->ppp = NULL;
+
+ dun->disconnect_cb(dun->data);
+}
+
+static void ppp_suspend_ath0(gpointer user_data)
+{
+ struct elect_dun *dun = user_data;
+
+ DBG("%s", dun->path);
+
+ g_at_chat_resume(dun->modem);
+ g_at_chat_send(dun->modem, "ATH0", none_prefix, kill_ppp, dun, NULL);
+}
+
+static void resume_ppp(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct elect_dun *dun = user_data;
+
+ DBG("%s %s\n", dun->path, ok ? "ok" : "failed");
+
+ if (ok == FALSE)
+ return;
+
+ g_at_chat_suspend(dun->modem);
+ g_at_ppp_resume(dun->ppp);
+
+ dun->disconnect_cb(dun->data);
+}
+
+static void ppp_suspend_ato0(gpointer user_data)
+{
+ struct elect_dun *dun = user_data;
+
+ DBG("%s", dun->path);
+
+ g_at_chat_resume(dun->modem);
+ g_at_chat_send(dun->modem, "ATO0", none_prefix, resume_ppp, dun, NULL);
+}
+
+static void ppp_connect(const char *iface, const char *local, const char *peer,
+ const char *dns1, const char *dns2,
+ gpointer user_data)
+{
+ struct elect_dun *dun = user_data;
+
+ DBG("Network Device: %s\n", iface);
+ DBG("IP Address: %s\n", local);
+ DBG("Peer IP Address: %s\n", peer);
+ DBG("Primary DNS Server: %s\n", dns1);
+ DBG("Secondary DNS Server: %s\n", dns2);
+
+ if (dun->connect_cb)
+ dun->connect_cb(iface, local, peer, dns1, dns2, dun->data);
+}
+
+static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data)
+{
+ struct elect_dun *dun = user_data;
+
+ DBG("PPP Link down: %d\n", reason);
+
+ g_at_ppp_unref(dun->ppp);
+ dun->ppp = NULL;
+
+ g_at_chat_resume(dun->modem);
+
+ if (dun->disconnect_cb)
+ dun->disconnect_cb(dun->data);
+}
+
+static void connect_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct elect_dun *dun = user_data;
+ GAtIO *io;
+
+ if (!ok) {
+ DBG("Unable to define context\n");
+ return;
+ }
+
+ /* get the data IO channel */
+ io = g_at_chat_get_io(dun->modem);
+
+ /*
+ * shutdown gatchat or else it tries to take all the input
+ * from the modem and does not let PPP get it.
+ */
+ g_at_chat_suspend(dun->modem);
+
+ /* open ppp */
+ dun->ppp = g_at_ppp_new();
+ if (dun->ppp == NULL) {
+ DBG("Unable to create PPP object\n");
+ return;
+ }
+ g_at_ppp_set_debug(dun->ppp, dun_debug, "PPP");
+
+ /* set connect and disconnect callbacks */
+ g_at_ppp_set_connect_function(dun->ppp, ppp_connect, dun);
+ g_at_ppp_set_disconnect_function(dun->ppp, ppp_disconnect, dun);
+
+ /* open the ppp connection */
+ g_at_ppp_open(dun->ppp, io);
+}
+
+static int open_serial(struct elect_dun *dun)
+{
+ GAtSyntax *syntax;
+ GIOChannel *channel;
+
+ channel = g_at_tty_open(dun->path, NULL);
+ if (channel == NULL)
+ return -EIO;
+
+ syntax = g_at_syntax_new_gsm_permissive();
+ dun->control = g_at_chat_new(channel, syntax);
+ g_io_channel_unref(channel);
+ g_at_syntax_unref(syntax);
+
+ if (dun->control == NULL)
+ return -EIO;
+
+ g_at_chat_ref(dun->control);
+ dun->modem = dun->control;
+ g_at_chat_set_debug(dun->control, dun_debug, "Control");
+
+ return 0;
+}
+
+struct elect_dun *__elect_dun_create(const char *path,
+ elect_dun_connect_cb_t connect_cb,
+ elect_dun_disconnect_cb_t disconnect_cb,
+ void *data)
+{
+ struct elect_dun *dun;
+
+ DBG("%s", path);
+
+ dun = g_try_new0(struct elect_dun, 1);
+ if (dun == NULL)
+ return NULL;
+
+ dun->path = g_strdup(path);
+ dun->connect_cb = connect_cb;
+ dun->disconnect_cb = disconnect_cb;
+ dun->data = data;
+
+ return dun;
+}
+
+int __elect_dun_connect(struct elect_dun *dun)
+{
+ int err;
+
+ DBG("%s", dun->path);
+
+ err = open_serial(dun);
+ if (err < 0)
+ return err;
+
+ g_at_chat_send(dun->control, "ATD*99#", none_prefix, connect_cb,
+ dun, NULL);
+
+ return 0;
+}
+
+int __elect_dun_disconnect(struct elect_dun *dun)
+{
+ DBG("%s", dun->path);
+
+ if (dun->ppp == NULL)
+ return -EINVAL;
+
+ /* XXX Which one is the right one? */
+ if (FALSE)
+ g_at_ppp_set_suspend_function(dun->ppp, ppp_suspend_ath0, dun);
+ else
+ g_at_ppp_set_suspend_function(dun->ppp, ppp_suspend_ato0, dun);
+
+ g_at_ppp_suspend(dun->ppp);
+
+ return 0;
+}
+
+void __elect_dun_unref(struct elect_dun *dun)
+{
+ DBG("%s", dun->path);
+
+ if (dun->ppp)
+ g_at_ppp_unref(dun->ppp);
+ if (dun->control)
+ g_at_chat_unref(dun->control);
+
+ g_free(dun->path);
+
+ g_free(dun);
+}
diff --git a/elect/elect.conf b/elect/elect.conf
new file mode 100644
index 0000000..548ffe8
--- /dev/null
+++ b/elect/elect.conf
@@ -0,0 +1,23 @@
+<!-- This configuration file specifies the required security policies
+ for oFono elect (DUN) daemon to work. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration
1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+ <!-- ../system.conf have denied everything, so we just punch some holes -->
+
+ <policy user="root">
+ <allow own="org.ofono.elect"/>
+ <allow send_destination="org.ofono.elect"/>
+ </policy>
+
+ <policy at_console="true">
+ <allow send_destination="org.ofono.elect"/>
+ </policy>
+
+ <policy context="default">
+ <deny send_destination="org.ofono.elect"/>
+ </policy>
+
+</busconfig>
diff --git a/elect/elect.h b/elect/elect.h
new file mode 100644
index 0000000..75d161b
--- /dev/null
+++ b/elect/elect.h
@@ -0,0 +1,102 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+
+#include <ofono/types.h>
+
+void __elect_exit(void);
+
+#include <ofono/log.h>
+
+int __ofono_log_init(const char *program, const char *debug,
+ ofono_bool_t detach);
+void __ofono_log_cleanup(void);
+void __ofono_log_enable(struct ofono_debug_desc *start,
+ struct ofono_debug_desc *stop);
+
+#include <ofono/dbus.h>
+
+#define ELECT_SERVICE "org.ofono.elect"
+#define ELECT_MANAGER_INTERFACE "org.ofono.elect.Manager"
+#define ELECT_DEVICE_INTERFACE "org.ofono.elect.Device"
+#define ELECT_MANAGER_PATH "/"
+
+int __ofono_dbus_init(DBusConnection *conn);
+void __ofono_dbus_cleanup(void);
+
+void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply);
+
+DBusMessage *__ofono_error_invalid_args(DBusMessage *msg);
+DBusMessage *__ofono_error_invalid_format(DBusMessage *msg);
+DBusMessage *__ofono_error_not_implemented(DBusMessage *msg);
+DBusMessage *__ofono_error_failed(DBusMessage *msg);
+DBusMessage *__ofono_error_busy(DBusMessage *msg);
+DBusMessage *__ofono_error_not_found(DBusMessage *msg);
+DBusMessage *__ofono_error_not_active(DBusMessage *msg);
+DBusMessage *__ofono_error_not_supported(DBusMessage *msg);
+DBusMessage *__ofono_error_not_available(DBusMessage *msg);
+DBusMessage *__ofono_error_timed_out(DBusMessage *msg);
+DBusMessage *__ofono_error_sim_not_ready(DBusMessage *msg);
+DBusMessage *__ofono_error_in_use(DBusMessage *msg);
+DBusMessage *__ofono_error_not_attached(DBusMessage *msg);
+DBusMessage *__ofono_error_attach_in_progress(DBusMessage *msg);
+DBusMessage *__ofono_error_not_registered(DBusMessage *msg);
+DBusMessage *__ofono_error_canceled(DBusMessage *msg);
+DBusMessage *__ofono_error_access_denied(DBusMessage *msg);
+DBusMessage *__ofono_error_emergency_active(DBusMessage *msg);
+
+
+int __elect_manager_init(DBusConnection *conn);
+void __elect_manager_cleanup(void);
+
+
+struct elect_device;
+
+int __elect_device_init(DBusConnection *conn);
+void __elect_device_cleanup(void);
+
+typedef void (*elect_device_foreach_func)(struct elect_device *device,
+ void *data);
+void __elect_device_foreach(elect_device_foreach_func cb, void *userdata);
+
+const char *__elect_device_get_path(struct elect_device *device);
+void __elect_device_append_properties(struct elect_device *device,
+ DBusMessageIter *dict);
+void __elect_device_shutdown(void);
+
+
+typedef void (*elect_dun_connect_cb_t)(const char *interface, const char *ip,
+ const char *peer,
+ const char *dns1, const char *dns2,
+ void *data);
+typedef void (*elect_dun_disconnect_cb_t)(void *data);
+
+struct elect_dun *__elect_dun_create(const char *path,
+ elect_dun_connect_cb_t connect_cb,
+ elect_dun_disconnect_cb_t disconnect_cb,
+ void *data);
+int __elect_dun_connect(struct elect_dun *dun);
+int __elect_dun_disconnect(struct elect_dun *dun);
+void __elect_dun_unref(struct elect_dun *dun);
diff --git a/elect/elect.service b/elect/elect.service
new file mode 100644
index 0000000..39d9349
--- /dev/null
+++ b/elect/elect.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=DUN service
+After=syslog.target
+
+[Service]
+Type=dbus
+BusName=org.ofono.elect
+ExecStart=/usr/sbin/electd -n
+
+[Install]
+WantedBy=multi-user.target
diff --git a/elect/main.c b/elect/main.c
new file mode 100644
index 0000000..97be904
--- /dev/null
+++ b/elect/main.c
@@ -0,0 +1,253 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+
+#include <gdbus.h>
+
+#ifdef HAVE_CAPNG
+#include <cap-ng.h>
+#endif
+
+#include "elect.h"
+
+#define SHUTDOWN_GRACE_SECONDS 10
+
+static GMainLoop *event_loop;
+
+void __elect_exit(void)
+{
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+ __elect_exit();
+ return FALSE;
+}
+
+static unsigned int __terminated = 0;
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
+{
+ struct signalfd_siginfo si;
+ ssize_t result;
+ int fd;
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+ return FALSE;
+
+ fd = g_io_channel_unix_get_fd(channel);
+
+ result = read(fd, &si, sizeof(si));
+ if (result != sizeof(si))
+ return FALSE;
+
+ switch (si.ssi_signo) {
+ case SIGINT:
+ case SIGTERM:
+ if (__terminated == 0) {
+ ofono_info("Terminating");
+ g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
+ quit_eventloop, NULL);
+
+ __elect_device_shutdown();
+ }
+
+ __terminated = 1;
+ break;
+ }
+
+ return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+ GIOChannel *channel;
+ guint source;
+ sigset_t mask;
+ int fd;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+ perror("Failed to set signal mask");
+ return 0;
+ }
+
+ fd = signalfd(-1, &mask, 0);
+ if (fd < 0) {
+ perror("Failed to create signal descriptor");
+ return 0;
+ }
+
+ channel = g_io_channel_unix_new(fd);
+
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ source = g_io_add_watch(channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ signal_handler, NULL);
+
+ g_io_channel_unref(channel);
+
+ return source;
+}
+
+static void system_bus_disconnected(DBusConnection *conn, void *user_data)
+{
+ ofono_error("System bus has disconnected!");
+
+ g_main_loop_quit(event_loop);
+}
+
+static gchar *option_debug = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_version = FALSE;
+
+static gboolean parse_debug(const char *key, const char *value,
+ gpointer user_data, GError **error)
+{
+ if (value)
+ option_debug = g_strdup(value);
+ else
+ option_debug = g_strdup("*");
+
+ return TRUE;
+}
+
+static GOptionEntry options[] = {
+ { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug,
+ "Specify debug options to enable", "DEBUG" },
+ { "nodetach", 'n', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &option_detach,
+ "Don't run as daemon in background" },
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+ "Show version information and exit" },
+ { NULL },
+};
+
+int main(int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *err = NULL;
+ DBusConnection *conn;
+ DBusError error;
+ guint signal;
+
+#ifdef HAVE_CAPNG
+ /* Drop capabilities */
+ capng_clear(CAPNG_SELECT_BOTH);
+ capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+ CAP_NET_BIND_SERVICE, CAP_NET_ADMIN,
+ CAP_NET_RAW, CAP_SYS_ADMIN, -1);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+ if (err != NULL) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ return 1;
+ }
+
+ g_printerr("An unknown error occurred\n");
+ return 1;
+ }
+
+ g_option_context_free(context);
+
+ if (option_version == TRUE) {
+ printf("%s\n", VERSION);
+ exit(0);
+ }
+
+ if (option_detach == TRUE) {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ return 1;
+ }
+ }
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ signal = setup_signalfd();
+
+ __ofono_log_init(argv[0], option_debug, option_detach);
+
+ dbus_error_init(&error);
+
+ conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, ELECT_SERVICE, &error);
+ if (conn == NULL) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ ofono_error("Unable to hop onto D-Bus: %s",
+ error.message);
+ dbus_error_free(&error);
+ } else {
+ ofono_error("Unable to hop onto D-Bus");
+ }
+
+ goto cleanup;
+ }
+
+ g_dbus_set_disconnect_function(conn, system_bus_disconnected,
+ NULL, NULL);
+
+ __ofono_dbus_init(conn);
+
+ __elect_device_init(conn);
+ __elect_manager_init(conn);
+
+ g_main_loop_run(event_loop);
+
+ __elect_manager_cleanup();
+ __elect_device_cleanup();
+
+ __ofono_dbus_cleanup();
+ dbus_connection_unref(conn);
+
+cleanup:
+ g_source_remove(signal);
+
+ g_main_loop_unref(event_loop);
+
+ __ofono_log_cleanup();
+
+ return 0;
+}
diff --git a/elect/manager.c b/elect/manager.c
new file mode 100644
index 0000000..c983aea
--- /dev/null
+++ b/elect/manager.c
@@ -0,0 +1,115 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "elect.h"
+
+static DBusConnection *connection;
+
+static void append_device(struct elect_device *device, void *userdata)
+{
+ DBusMessageIter *array = userdata;
+ const char *path = __elect_device_get_path(device);
+ DBusMessageIter entry, dict;
+
+ dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+ NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+ &path);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+ OFONO_PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ __elect_device_append_properties(device, &dict);
+ dbus_message_iter_close_container(&entry, &dict);
+ dbus_message_iter_close_container(array, &entry);
+}
+
+static DBusMessage *manager_get_devices(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_OBJECT_PATH_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING,
+ &array);
+ __elect_device_foreach(append_device, &array);
+ dbus_message_iter_close_container(&iter, &array);
+
+ return reply;
+}
+
+static GDBusMethodTable manager_methods[] = {
+ { "GetDevices", "", "a(oa{sv})",
manager_get_devices },
+ { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+ { "DeviceAdded", "oa{sv}" },
+ { "DeviceRemoved", "o" },
+ { }
+};
+
+int __elect_manager_init(DBusConnection *conn)
+{
+ gboolean ret;
+
+ connection = conn;
+
+ ret = g_dbus_register_interface(connection, ELECT_MANAGER_PATH,
+ ELECT_MANAGER_INTERFACE,
+ manager_methods, manager_signals,
+ NULL, NULL, NULL);
+
+ if (ret == FALSE)
+ return -1;
+
+ return 0;
+}
+
+void __elect_manager_cleanup(void)
+{
+ g_dbus_unregister_interface(connection, ELECT_MANAGER_PATH,
+ ELECT_MANAGER_INTERFACE);
+}
diff --git a/plugins/bluetooth.h b/plugins/bluetooth.h
index daa1873..4fc16ad 100644
--- a/plugins/bluetooth.h
+++ b/plugins/bluetooth.h
@@ -27,6 +27,7 @@
#define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter"
#define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device"
#define BLUEZ_SERVICE_INTERFACE BLUEZ_SERVICE ".Service"
+#define BLUEZ_SERIAL_INTERFACE BLUEZ_SERVICE ".Serial"
#define DBUS_TIMEOUT 15
diff --git a/test/elect-connect b/test/elect-connect
new file mode 100755
index 0000000..09e592e
--- /dev/null
+++ b/test/elect-connect
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+ path = sys.argv[1]
+else:
+ manager = dbus.Interface(bus.get_object('org.ofono.elect', '/'),
+ 'org.ofono.elect.Manager')
+ devices = manager.GetDevices()
+ path = devices[0][0]
+
+print "Connect device %s..." % path
+device = dbus.Interface(bus.get_object('org.ofono.elect', path),
+ 'org.ofono.elect.Device')
+
+device.Connect()
diff --git a/test/elect-disconnect b/test/elect-disconnect
new file mode 100755
index 0000000..76ff74c
--- /dev/null
+++ b/test/elect-disconnect
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+ path = sys.argv[1]
+else:
+ manager = dbus.Interface(bus.get_object('org.ofono.elect', '/'),
+ 'org.ofono.elect.Manager')
+ devices = manager.GetDevices()
+ path = devices[0][0]
+
+print "Disonnect device %s..." % path
+device = dbus.Interface(bus.get_object('org.ofono.elect', path),
+ 'org.ofono.elect.Device')
+
+device.Disconnect()
--
1.7.8.110.g4cb5d1