#include <config.h>
#endif
+#include <string.h>
#include <errno.h>
#include <gdbus.h>
#define DUNDEE_SERVICE "org.ofono.dundee"
#define DUNDEE_MANAGER_INTERFACE DUNDEE_SERVICE ".Manager"
+#define DUNDEE_DEVICE_INTERFACE DUNDEE_SERVICE ".Device"
#define DEVICE_ADDED "DeviceAdded"
#define DEVICE_REMOVED "DeviceRemoved"
+#define PROPERTY_CHANGED "PropertyChanged"
+#define GET_PROPERTIES "GetProperties"
+#define SET_PROPERTY "SetProperty"
#define GET_DEVICES "GetDevices"
#define TIMEOUT 40000
char *path;
char *name;
+ struct connman_device *device;
+ struct connman_network *network;
+
connman_bool_t active;
int index;
enum connman_ipconfig_method method;
struct connman_ipaddress *address;
char *nameservers;
+
+ DBusPendingCall *call;
};
+static char *get_ident(const char *path)
+{
+ char *pos;
+
+ if (*path != '/')
+ return NULL;
+
+ pos = strrchr(path, '/');
+ if (pos == NULL)
+ return NULL;
+
+ return pos + 1;
+}
+
+static void create_device(struct dundee_data *info)
+{
+ struct connman_device *device;
+ char *ident;
+
+ DBG("%s", info->path);
+
+ ident = g_strdup(get_ident(info->path));
+ device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
+ if (device == NULL)
+ goto out;
+
+ DBG("device %p", device);
+
+ connman_device_set_ident(device, ident);
+
+ connman_device_set_string(device, "Path", info->path);
+
+ connman_device_set_data(device, info);
+
+ if (connman_device_register(device) < 0) {
+ connman_error("Failed to register DUN device");
+ connman_device_unref(device);
+ goto out;
+ }
+
+ info->device = device;
+
+out:
+ g_free(ident);
+}
+
+static void destroy_device(struct dundee_data *info)
+{
+ connman_device_set_powered(info->device, FALSE);
+
+ if (info->call != NULL)
+ dbus_pending_call_cancel(info->call);
+
+ if (info->network != NULL) {
+ connman_device_remove_network(info->device, info->network);
+ connman_network_unref(info->network);
+ info->network = NULL;
+ }
+
+ connman_device_unregister(info->device);
+ connman_device_unref(info->device);
+
+ info->device = NULL;
+}
+
static void device_destroy(gpointer data)
{
struct dundee_data *info = data;
+ if (info->device != NULL)
+ destroy_device(info);
+
g_free(info->path);
g_free(info->name);
g_free(info);
}
+static void create_network(struct dundee_data *info)
+{
+ struct connman_network *network;
+ const char *group;
+
+ DBG("%s", info->path);
+
+ network = connman_network_create(info->path,
+ CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
+ if (network == NULL)
+ return;
+
+ DBG("network %p", network);
+
+ connman_network_set_data(network, info);
+
+ connman_network_set_string(network, "Path",
+ info->path);
+
+ connman_network_set_name(network, info->name);
+
+ group = get_ident(info->path);
+ connman_network_set_group(network, group);
+
+ connman_network_set_available(network, TRUE);
+
+ if (connman_device_add_network(info->device, network) < 0) {
+ connman_network_unref(network);
+ return;
+ }
+
+ info->network = network;
+}
+
+static void set_connected(struct dundee_data *info)
+{
+ DBG("%s", info->path);
+
+ connman_inet_ifup(info->index);
+
+ connman_network_set_index(info->network, info->index);
+ connman_network_set_ipv4_method(info->network,
+ CONNMAN_IPCONFIG_METHOD_FIXED);
+ connman_network_set_ipaddress(info->network, info->address);
+ connman_network_set_nameservers(info->network, info->nameservers);
+
+ connman_network_set_connected(info->network, TRUE);
+}
+
+static void set_disconnected(struct dundee_data *info)
+{
+ DBG("%s", info->path);
+
+ connman_network_set_connected(info->network, FALSE);
+ connman_inet_ifdown(info->index);
+}
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+ struct dundee_data *info = user_data;
+ DBusMessage *reply;
+ DBusError error;
+
+ DBG("%s", info->path);
+
+ info->call = NULL;
+
+ dbus_error_init(&error);
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ if (dbus_set_error_from_message(&error, reply)) {
+ connman_error("Failed to change property: %s %s %s",
+ info->path, error.name, error.message);
+ dbus_error_free(&error);
+
+ connman_network_set_error(info->network,
+ CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+ }
+
+ dbus_message_unref(reply);
+
+ dbus_pending_call_unref(call);
+}
+
+static int set_property(struct dundee_data *info,
+ const char *property, int type, void *value)
+{
+ DBusMessage *message;
+ DBusMessageIter iter;
+
+ DBG("%s %s", info->path, property);
+
+ message = dbus_message_new_method_call(DUNDEE_SERVICE, info->path,
+ DUNDEE_DEVICE_INTERFACE, SET_PROPERTY);
+ if (message == NULL)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(message, &iter);
+ connman_dbus_property_append_basic(&iter, property, type, value);
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &info->call, TIMEOUT) == FALSE) {
+ connman_error("Failed to change property: %s %s",
+ info->path, property);
+ dbus_message_unref(message);
+ return -EINVAL;
+ }
+
+ if (info->call == NULL) {
+ connman_error("D-Bus connection not available");
+ dbus_message_unref(message);
+ return -EINVAL;
+ }
+
+ dbus_pending_call_set_notify(info->call, set_property_reply,
+ info, NULL);
+
+ dbus_message_unref(message);
+
+ return -EINPROGRESS;
+}
+
+static int device_set_active(struct dundee_data *info)
+{
+ dbus_bool_t active = TRUE;
+
+ DBG("%s", info->path);
+
+ return set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+ &active);
+}
+
+static int device_set_inactive(struct dundee_data *info)
+{
+ dbus_bool_t active = FALSE;
+ int err;
+
+ DBG("%s", info->path);
+
+ err = set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+ &active);
+ if (err == -EINPROGRESS)
+ return 0;
+
+ return err;
+}
+
static int network_probe(struct connman_network *network)
{
DBG("network %p", network);
static int network_connect(struct connman_network *network)
{
+ struct dundee_data *info = connman_network_get_data(network);
+
DBG("network %p", network);
- return 0;
+ return device_set_active(info);
}
static int network_disconnect(struct connman_network *network)
{
+ struct dundee_data *info = connman_network_get_data(network);
+
DBG("network %p", network);
- return 0;
+ return device_set_inactive(info);
}
static struct connman_network_driver network_driver = {
static int dundee_probe(struct connman_device *device)
{
+ GHashTableIter iter;
+ gpointer key, value;
+
DBG("device %p", device);
- return 0;
+ if (dundee_devices == NULL)
+ return -ENOTSUP;
+
+ g_hash_table_iter_init(&iter, dundee_devices);
+
+ while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+ struct dundee_data *info = value;
+
+ if (device == info->device)
+ return 0;
+ }
+
+ return -ENOTSUP;
}
static void dundee_remove(struct connman_device *device)
g_free(gateway);
}
+static gboolean device_changed(DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ const char *path = dbus_message_get_path(message);
+ struct dundee_data *info = NULL;
+ DBusMessageIter iter, value;
+ const char *key;
+ const char *signature = DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING;
+
+ if (dbus_message_has_signature(message, signature) == FALSE) {
+ connman_error("dundee signature does not match");
+ return TRUE;
+ }
+
+ info = g_hash_table_lookup(dundee_devices, path);
+ if (info == NULL)
+ return TRUE;
+
+ if (dbus_message_iter_init(message, &iter) == FALSE)
+ return TRUE;
+
+ dbus_message_iter_get_basic(&iter, &key);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &value);
+
+ /*
+ * Dundee guarantees the ordering of Settings and
+ * Active. Settings will always be send before Active = True.
+ * That means we don't have to order here.
+ */
+ if (g_str_equal(key, "Active") == TRUE) {
+ dbus_message_iter_get_basic(&value, &info->active);
+
+ DBG("%s Active %d", info->path, info->active);
+
+ if (info->active == TRUE)
+ set_connected(info);
+ else
+ set_disconnected(info);
+ } else if (g_str_equal(key, "Settings") == TRUE) {
+ DBG("%s Settings", info->path);
+
+ extract_settings(&value, info);
+ } else if (g_str_equal(key, "Name") == TRUE) {
+ char *name;
+
+ dbus_message_iter_get_basic(&value, &name);
+
+ g_free(info->name);
+ info->name = g_strdup(name);
+
+ DBG("%s Name %s", info->path, info->name);
+
+ connman_network_set_name(info->network, info->name);
+ connman_network_update(info->network);
+ }
+
+ return TRUE;
+}
+
static void add_device(const char *path, DBusMessageIter *properties)
{
struct dundee_data *info;
}
g_hash_table_insert(dundee_devices, g_strdup(path), info);
+
+ create_device(info);
+ create_network(info);
+
+ if (info->active == TRUE)
+ set_connected(info);
}
static gboolean device_added(DBusConnection *connection, DBusMessage *message,
static guint watch;
static guint added_watch;
static guint removed_watch;
+static guint device_watch;
static int dundee_init(void)
{
DEVICE_REMOVED, device_removed,
NULL, NULL);
- if (watch == 0 || added_watch == 0 || removed_watch == 0) {
+ device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+ DUNDEE_DEVICE_INTERFACE,
+ PROPERTY_CHANGED,
+ device_changed,
+ NULL, NULL);
+
+
+ if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
+ device_watch == 0) {
err = -EIO;
goto remove;
}
g_dbus_remove_watch(connection, watch);
g_dbus_remove_watch(connection, added_watch);
g_dbus_remove_watch(connection, removed_watch);
+ g_dbus_remove_watch(connection, device_watch);
dbus_connection_unref(connection);
g_dbus_remove_watch(connection, watch);
g_dbus_remove_watch(connection, added_watch);
g_dbus_remove_watch(connection, removed_watch);
+ g_dbus_remove_watch(connection, device_watch);
connman_device_driver_unregister(&dundee_driver);
connman_network_driver_unregister(&network_driver);