Hi Daniel,

On 21/11/2011 17:56, Daniel Wagner wrote:
From: Daniel Wagner<daniel.wag...@bmw-carit.de>

This patch will be splittet into smaller patches later. This is work
in progress.
---
  plugins/ofono.c | 1814 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
  1 files changed, 1804 insertions(+), 10 deletions(-)

diff --git a/plugins/ofono.c b/plugins/ofono.c
index 7d979c4..83f9aef 100644
--- a/plugins/ofono.c
+++ b/plugins/ofono.c
@@ -4,6 +4,7 @@
   *
   *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
   *  Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  Copyright (C) 2011  BWM 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
@@ -34,35 +35,1692 @@
  #include<connman/plugin.h>
  #include<connman/device.h>
  #include<connman/network.h>
+#include<connman/technology.h>
+#include<connman/inet.h>
  #include<connman/dbus.h>
  #include<connman/log.h>

+#define OFONO_SERVICE                  "org.ofono"
+
+#define OFONO_MANAGER_INTERFACE                OFONO_SERVICE ".Manager"
+#define OFONO_MODEM_INTERFACE          OFONO_SERVICE ".Modem"
+#define OFONO_SIM_INTERFACE            OFONO_SERVICE ".SimManager"
+#define OFONO_NETREG_INTERFACE         OFONO_SERVICE ".NetworkRegistration"
+#define OFONO_CM_INTERFACE             OFONO_SERVICE ".ConnectionManager"
+#define OFONO_CONTEXT_INTERFACE                OFONO_SERVICE 
".ConnectionContext"
+
+#define MODEM_ADDED                    "ModemAdded"
+#define MODEM_REMOVED                  "ModemRemoved"
+#define PROPERTY_CHANGED               "PropertyChanged"
+#define CONTEXT_ADDED                  "ContextAdded"
+#define CONTEXT_REMOVED                        "ContextRemoved"
+
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+#define GET_MODEMS                     "GetModems"
+#define GET_CONTEXTS                   "GetContexts"
+
+#define TIMEOUT 40000
+
+enum ofono_api {
+       OFONO_API_SIM =         0x1,
+       OFONO_API_NETREG =      0x2,
+       OFONO_API_CM =          0x4,
+};
+
+/*
+ * The way this plugin works is following:
+ *
+ *   powered ->  SubscriberIdentity or Online = True ->  gprs, context ->
+ *     attached ->  netreg ->  ready
+ */
+
+static connman_bool_t daemon_running;
+
  static DBusConnection *connection;

+static GHashTable *modem_hash;
+static GHashTable *context_hash;
+
+struct network_context {
+       char *path;
+       int index;
+       enum connman_ipconfig_method method;
+       struct connman_ipaddress *address;
+       char *nameservers;
+};
+
+struct modem_data {
+       char *path;
+
+       struct connman_device *device;
+       struct connman_network *network;
+
+       struct network_context *ipv4_context;
+
+       /* Modem Interface */
+       char *serial;*

Do you need this serial field for CDMA modems to publish cellular service?
If yes, we had a talk with Marcel and Denis about this and it seems they prefer using the IMSI to publish the service as it is done for GSM modems. So you can remove this
part.
If not, you can ignore my comment :)

+       connman_bool_t powered;
+       connman_bool_t online;
+       uint8_t interfaces;
+
+       connman_bool_t set_powered;
+       connman_bool_t set_online;
+
+       /* ConnectionManager Interface */
+       connman_bool_t attached;
+
+       /* ConnectionContext Interface */
+       connman_bool_t active;
+       connman_bool_t set_active;
+
+       /* SimManager Interface */
+       char *imsi;
+
+       /* Netreg Interface */
+       char *name;
+       uint8_t strength;
+
+       /* pending calls */
+       DBusPendingCall *call_set_property;
+       DBusPendingCall *call_get_properties;
+       DBusPendingCall *call_get_contexts;
+};
+
+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 struct network_context *network_context_alloc(const char *path)
+{
+       struct network_context *context;
+
+       context = g_try_new0(struct network_context, 1);
+       if (context == NULL)
+               return NULL;
+
+       context->path = g_strdup(path);
+       context->index = -1;
+       context->method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       context->address = NULL;
+       context->nameservers = NULL;
+
+       return context;
+}
+
+static void network_context_free(struct network_context *context)
+{
+       g_free(context->path);
+       g_free(context->nameservers);
+       connman_ipaddress_free(context->address);
+
+       free(context);
+}
+
+static void set_connected(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       connman_network_set_index(modem->network, modem->ipv4_context->index);
+
+       switch (modem->ipv4_context->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv4_method(modem->network,
+                                               modem->ipv4_context->method);
+               connman_network_set_ipaddress(modem->network,
+                                               modem->ipv4_context->address);
+               connman_network_set_nameservers(modem->network,
+                                       modem->ipv4_context->nameservers);
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               connman_network_set_ipv4_method(modem->network,
+                                               modem->ipv4_context->method);
+               break;
+       }
+
+       connman_network_set_connected(modem->network, TRUE);
+}
+
+static void set_disconnected(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       connman_network_set_connected(modem->network, FALSE);
+}
+
+typedef void (*set_property_cb)(struct modem_data *data,
+                               connman_bool_t success);
+typedef void (*get_properties_cb)(struct modem_data *data,
+                               DBusMessageIter *dict);
+
+struct property_info {
+       struct modem_data *modem;
+       const char *path;
+       const char *interface;
+       const char *property;
+       set_property_cb set_property_cb;
+       get_properties_cb get_properties_cb;
+};
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_info *info = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       connman_bool_t success = TRUE;
+
+       DBG("%s path %s %s.%s", info->modem->path,
+               info->path, info->interface, info->property);
+
+       info->modem->call_set_property = 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: %s %s",
+                               info->path, info->interface, info->property,
+                               error.name, error.message);
+               dbus_error_free(&error);
+               success = FALSE;
+       }
+
+       if (info->set_property_cb != NULL)
+               (*info->set_property_cb)(info->modem, success);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int set_property(struct modem_data *modem,
+                       const char *path, const char *interface,
+                       const char *property, int type, void *value,
+                       set_property_cb notify)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+       struct property_info *info;
+
+       DBG("%s path %s %s.%s", modem->path, path, interface, property);
+
+       if (modem->call_set_property != NULL) {
+               connman_error("Pending SetProperty");
+               return -EBUSY;
+       }
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, path,
+                                       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,
+                       &modem->call_set_property, TIMEOUT) == FALSE) {
+               connman_error("Failed to change property: %s %s.%s",
+                               path, interface, property);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (modem->call_set_property == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       info = g_try_new0(struct property_info, 1);
+       if (info == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       info->modem = modem;
+       info->path = path;
+       info->interface = interface;
+       info->property = property;
+       info->set_property_cb = notify;
+
+       dbus_pending_call_set_notify(modem->call_set_property,
+                                       set_property_reply, info, g_free);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void get_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_info *info = user_data;
+       DBusMessageIter array, dict;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("%s path %s %s", info->modem->path, info->path, info->interface);
+
+       info->modem->call_get_properties = NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("Failed to get properties: %s %s: %s %s",
+                               info->path, info->interface,
+                               error.name, error.message);
+               dbus_error_free(&error);
+
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply,&array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array,&dict);
+
+       if (info->get_properties_cb != NULL)
+               (*info->get_properties_cb)(info->modem,&dict);
+
+done:
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int get_properties(const char *path, const char *interface,
+                               get_properties_cb notify,
+                               struct modem_data *modem)
+{
+       DBusMessage *message;
+       struct property_info *info;
+
+       DBG("%s path %s %s", modem->path, path, interface);
+
+       if (modem->call_get_properties != NULL) {
+               connman_error("Pending GetProperties");
+               return -EBUSY;
+       }
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, path,
+                                       interface, GET_PROPERTIES);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &modem->call_get_properties, TIMEOUT) == FALSE) {
+               connman_error("Failed to call %s.GetProperties()", interface);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (modem->call_get_properties == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       info = g_try_new0(struct property_info, 1);
+       if (info == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       info->modem = modem;
+       info->path = path;
+       info->interface = interface;
+       info->get_properties_cb = notify;
+
+       dbus_pending_call_set_notify(modem->call_get_properties,
+                                       get_properties_reply, info, g_free);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void context_set_active_reply(struct modem_data *modem,
+                                       connman_bool_t success)
+{
+       DBG("%s", modem->path);
+
+       if (success == TRUE) {
+               /*
+                * Don't handle do anything on success here. oFono will send
+                * the change via PropertyChanged singal.
+                */
+               return;
+       }
+
+       /*
+        * Active = True might fail due a timeout. That means oFono
+        * still tries to go online. If we retry to set Active = True,
+        * we just get a InProgress error message. Should we power
+        * cycle the modem in such cases?
+        */
+
+       connman_network_set_error(modem->network,
+                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+}
+
+static int context_set_active(struct modem_data *modem)
+{
+       dbus_bool_t active = TRUE;
+
+       DBG("%s", modem->path);
+
+       return set_property(modem, modem->ipv4_context->path,
+                               OFONO_CONTEXT_INTERFACE,
+                               "Active", DBUS_TYPE_BOOLEAN,
+                               &active,
+                               context_set_active_reply);
+}
+
+static int context_set_inactive(struct modem_data *modem)
+{
+       dbus_bool_t active = FALSE;
+       int err;
+
+       DBG("%s", modem->path);
+
+       err = set_property(modem, modem->ipv4_context->path,
+                               OFONO_CONTEXT_INTERFACE,
+                               "Active", DBUS_TYPE_BOOLEAN,
+                               &active,
+                               NULL);
+       if (err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
+static void modem_set_online_reply(struct modem_data *modem,
+                                       connman_bool_t success)
+{
+       DBG("%s", modem->path);
+
+       if (success == TRUE) {
+               /*
+                * Don't handle do anything on success here. oFono will send
+                * the change via PropertyChanged singal.
+                */
+               return;
+       }
+
+       modem->set_online = FALSE;
+}
+
+static int modem_set_online(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       modem->set_online = TRUE;
+
+       return set_property(modem, modem->path,
+                               OFONO_MODEM_INTERFACE,
+                               "Online", DBUS_TYPE_BOOLEAN,
+                               &modem->set_online,
+                               modem_set_online_reply);
+}
+
+static int modem_set_powered(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       modem->set_powered = TRUE;
+
+       return set_property(modem, modem->path,
+                               OFONO_MODEM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &modem->set_powered,
+                               NULL);
+}
+
+static int modem_set_unpowered(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       modem->set_powered = FALSE;
+
+       return set_property(modem, modem->path,
+                               OFONO_MODEM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &modem->set_powered,
+                               NULL);
+}
+
+static connman_bool_t has_interface(uint8_t interfaces,
+                                       enum ofono_api api)
+{
+       if ((interfaces&  api) == api)
+               return TRUE;
+
+       return FALSE;
+}
+
+static uint8_t extract_interfaces(DBusMessageIter *array)
+{
+       DBusMessageIter entry;
+       uint8_t interfaces = 0;
+
+       dbus_message_iter_recurse(array,&entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *name;
+
+               dbus_message_iter_get_basic(&entry,&name);
+
+               if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_SIM;
+               else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_NETREG;
+               else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_CM;
+
+               dbus_message_iter_next(&entry);
+       }
+
+       return interfaces;
+}
+
+static char *extract_nameservers(DBusMessageIter *array)
+{
+       DBusMessageIter entry;
+       char *nameservers = NULL;
+       char *tmp;
+
+       dbus_message_iter_recurse(array,&entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *nameserver;
+
+               dbus_message_iter_get_basic(&entry,&nameserver);
+
+               if (nameservers == NULL) {
+                       nameservers = g_strdup(nameserver);
+               } else {
+                       tmp = nameservers;
+                       nameservers = g_strdup_printf("%s %s", tmp, nameserver);
+                       g_free(tmp);
+               }
+
+               dbus_message_iter_next(&entry);
+       }
+
+       return nameservers;
+}
+
+static struct network_context *extract_settings(DBusMessageIter *array,
+                                               const char *context_path,
+                                               enum connman_ipconfig_type type)
+{
+       DBusMessageIter dict;
+       char *address = NULL, *netmask = NULL, *gateway = NULL;
+       char *nameservers = NULL;
+       const char *interface = NULL;
+       struct network_context *context = NULL;
+       int index = -1;
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       struct connman_ipaddress *ipaddr = NULL;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return NULL;
+
+       dbus_message_iter_recurse(array,&dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&dict,&entry);
+               dbus_message_iter_get_basic(&entry,&key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry,&value);
+
+               if (g_str_equal(key, "Interface") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&interface);
+
+                       DBG("Interface %s", interface);
+
+                       index = connman_inet_ifindex(interface);
+
+                       DBG("index %d", index);
+               } else if (g_str_equal(key, "Method") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&val);
+
+                       DBG("Method %s", val);
+
+                       if (g_strcmp0(val, "static") == 0) {
+                               method = CONNMAN_IPCONFIG_METHOD_FIXED;
+                       } else if (g_strcmp0(val, "dhcp") == 0) {
+                               method = CONNMAN_IPCONFIG_METHOD_DHCP;
+                               break;
+                       }
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&val);
+
+                       address = g_strdup(val);
+
+                       DBG("Address %s", address);
+               } else if (g_str_equal(key, "Netmask") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&val);
+
+                       netmask = g_strdup(val);
+
+                       DBG("Netmask %s", netmask);
+               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+                       nameservers = extract_nameservers(&value);
+
+                       DBG("Nameservers %s", nameservers);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&val);
+
+                       gateway = g_strdup(val);
+
+                       DBG("Gateway %s", gateway);
+               }
+
+               /* Add IPv6 parsing part */
+
+               dbus_message_iter_next(&dict);
+       }
+
+       context = network_context_alloc(context_path);
+       if (context == NULL)
+               goto out;
+
+       if (method == CONNMAN_IPCONFIG_METHOD_FIXED) {
+               ipaddr = connman_ipaddress_alloc(type);
+               if (ipaddr == NULL) {
+                       network_context_free(context);
+                       context = NULL;
+                       goto out;
+               }
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+                       connman_ipaddress_set_ipv4(ipaddr, address,
+                                                       netmask, gateway);
+               }
+
+               context->nameservers = nameservers;
+       }
+
+       context->address = ipaddr;
+       context->method = method;
+       context->index = index;
+
+out:
+       if (context == NULL || context->nameservers != nameservers)
+               g_free(nameservers);
+
+       g_free(address);
+       g_free(netmask);
+       g_free(gateway);
+
+       return context;
+}
+
+static void create_device(struct modem_data *modem)
+{
+       struct connman_device *device;
+       char *ident;
+
+       DBG("%s", modem->path);
+
+       if (modem->device != NULL) {
+               DBG("Modem is already registered at core, bug?");
+               return;
+       }
+
+       if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
+               ident = modem->imsi;
+       } else {
+               /*
+                * This modem doesn't have a SIM interface for some reason.
+                *
+                * This here is just a workaround for the time
+                * being. We are going to use the serial number in the
+                * hope it is unique enough.
+                */
+               connman_info("Modem %s has no SIM interface", modem->path);
+
+               ident = modem->serial;
+       }
+
+       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_CELLULAR);
+       if (device == NULL)
+               return;
+
+       DBG("device %p", device);
+
+       connman_device_set_ident(device, ident);
+
+       connman_device_set_string(device, "Path", modem->path);
+
+       connman_device_set_data(device, modem);
+
+       if (connman_device_register(device)<  0) {
+               connman_error("Failed to register cellular device");
+               connman_device_unref(device);
+               return;
+       }
+
+       modem->device = device;
+}
+
+static void destroy_device(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       connman_device_set_powered(modem->device, FALSE);
+
+       if (modem->network != NULL) {
+               connman_device_remove_network(modem->device, modem->network);
+               connman_network_unref(modem->network);
+               modem->network = NULL;
+       }
+
+       connman_device_unregister(modem->device);
+       connman_device_unref(modem->device);
+
+       modem->device = NULL;
+}
+
+static void add_network(struct modem_data *modem)
+{
+       const char *group;
+
+       DBG("%s", modem->path);
+
+       if (modem->network != NULL)
+               return;
+
+       modem->network = connman_network_create(modem->ipv4_context->path,
+                                               CONNMAN_NETWORK_TYPE_CELLULAR);
+       if (modem->network == NULL)
+               return;
+
+       DBG("network %p", modem->network);
+
+       connman_network_set_data(modem->network, modem);
+
+       connman_network_set_string(modem->network, "Path",
+                                       modem->ipv4_context->path);
+
+       connman_network_set_index(modem->network, modem->ipv4_context->index);
+
+       if (modem->name != NULL)
+               connman_network_set_name(modem->network, modem->name);
+       else
+               connman_network_set_name(modem->network, "");
+
+       connman_network_set_strength(modem->network, modem->strength);
+
+       group = get_ident(modem->ipv4_context->path);
+       connman_network_set_group(modem->network, group);
+
+       connman_network_set_available(modem->network, TRUE);
+
+       if (connman_device_add_network(modem->device, modem->network)<  0) {
+               connman_network_unref(modem->network);
+               modem->network = NULL;
+               return;
+       }
+}
+
+static int add_cm_context(struct modem_data *modem, const char *context_path,
+                               DBusMessageIter *dict)
+{
+       const char *context_type;
+       struct network_context *ipv4_context = NULL;
+       connman_bool_t active = FALSE;
+
+       DBG("%s context path %s", modem->path, context_path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict,&entry);
+               dbus_message_iter_get_basic(&entry,&key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry,&value);
+
+               if (g_str_equal(key, "Type") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&context_type);
+
+                       DBG("%s context %s type %s", modem->path,
+                               context_path, context_type);
+               } else if (g_str_equal(key, "Settings") == TRUE) {
+                       ipv4_context = extract_settings(&value, context_path,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+               } else if (g_str_equal(key, "Active") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&active);
+
+                       DBG("%s Active %d", modem->path, active);
+               }
+
+               dbus_message_iter_next(dict);
+       }
+
+       if (ipv4_context == NULL)
+               return -EINVAL;
+
+       if (g_strcmp0(context_type, "internet") != 0) {
+               network_context_free(ipv4_context);
+               return -EINVAL;
+       }
+
+       if (modem->ipv4_context != NULL) {
+               /*
+                * We have already assigned a context to this modem
+                * and we do only support one Internet context.
+                */
+               network_context_free(ipv4_context);
+               return -EALREADY;
+       }
+
+       modem->ipv4_context = ipv4_context;
+       modem->active = active;
+
+       g_hash_table_replace(context_hash, g_strdup(context_path), modem);
+
+       return 0;
+}
+
+static void remove_cm_context(struct modem_data *modem,
+                               const char *context_path)
+{
+       if (modem->ipv4_context == NULL)
+               return;
+
+       g_hash_table_remove(context_hash, context_path);
+
+       network_context_free(modem->ipv4_context);
+       modem->ipv4_context = NULL;
+}
+
+static gboolean context_changed(DBusConnection *connection,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       const char *context_path = dbus_message_get_path(message);
+       struct modem_data *modem = NULL;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("context_path %s", context_path);
+
+       modem = g_hash_table_lookup(context_hash, context_path);
+       if (modem == 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);
+
+       /*
+        * oFono 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, "Settings") == TRUE) {
+               struct network_context *ipv4_context;
+
+               DBG("%s Settings", modem->path);
+
+               ipv4_context = extract_settings(&value, context_path,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               if (ipv4_context == NULL)
+                       return TRUE;
+
+               if (modem->ipv4_context != NULL)
+                       network_context_free(modem->ipv4_context);
+
+               modem->ipv4_context = ipv4_context;
+       } else if (g_str_equal(key, "Active") == TRUE) {
+               dbus_message_iter_get_basic(&value,&modem->active);
+
+               DBG("%s Active %d", modem->path, modem->active);
+
+               if (modem->active == TRUE)
+                       set_connected(modem);
+               else
+                       set_disconnected(modem);
+       }
+
+       return TRUE;
+}
+
+static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       DBusMessageIter array, dict, entry, value;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("%s", modem->path);
+
+       modem->call_get_contexts = NULL;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply,&array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array,&dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               const char *context_path;
+
+               dbus_message_iter_recurse(&dict,&entry);
+               dbus_message_iter_get_basic(&entry,&context_path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry,&value);
+
+               if (add_cm_context(modem, context_path,&value) == 0)
+                       break;
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int cm_get_contexts(struct modem_data *modem)
+{
+       DBusMessage *message;
+
+       DBG("%s", modem->path);
+
+       if (modem->call_get_contexts != NULL)
+               return -EBUSY;
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
+                                       OFONO_CM_INTERFACE, GET_CONTEXTS);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &modem->call_get_contexts, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetContexts()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (modem->call_get_contexts == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(modem->call_get_contexts,
+                                       cm_get_contexts_reply,
+                                       modem, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static gboolean cm_context_added(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       char *context_path;
+       struct modem_data *modem;
+       DBusMessageIter iter, properties;
+
+       DBG("%s", path);
+
+       modem = g_hash_table_lookup(modem_hash, context_path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message,&iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter,&context_path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter,&properties);
+
+       if (add_cm_context(modem, context_path,&properties) != 0)
+               return TRUE;
+
+       return TRUE;
+}
+
+static gboolean cm_context_removed(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       const char *context_path;
+       struct modem_data *modem;
+       DBusMessageIter iter;
+
+       DBG("context path %s", path);
+
+       if (dbus_message_iter_init(message,&iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter,&context_path);
+
+       modem = g_hash_table_lookup(context_hash, context_path);
+       if (modem == NULL)
+               return TRUE;
+
+       remove_cm_context(modem, context_path);
+
+       return TRUE;
+}
+
+static gboolean netreg_changed(DBusConnection *connection, DBusMessage 
*message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == 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);
+
+       if (g_str_equal(key, "Name") == TRUE) {
+               char *name;
+
+               dbus_message_iter_get_basic(&value,&name);
+
+               DBG("%s Name %s", modem->path, name);
+
+               g_free(modem->name);
+               modem->name = g_strdup(name);
+
+               if (modem->network == NULL)
+                       return TRUE;
+
+               connman_network_set_name(modem->network, modem->name);
+               connman_network_update(modem->network);
+       } else if (g_str_equal(key, "Strength") == TRUE) {
+               dbus_message_iter_get_basic(&value,&modem->strength);
+
+               DBG("%s Strength %d", modem->path, modem->strength);
+
+               if (modem->network == NULL)
+                       return TRUE;
+
+               connman_network_set_strength(modem->network, modem->strength);
+               connman_network_update(modem->network);
+       }
+
+       return TRUE;
+}
+
+static void netreg_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict,&entry);
+               dbus_message_iter_get_basic(&entry,&key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry,&value);
+
+               if (g_str_equal(key, "Name") == TRUE) {
+                       char *name;
+
+                       dbus_message_iter_get_basic(&value,&name);
+
+                       DBG("%s Name %s", modem->path, name);
+
+                       g_free(modem->name);
+                       modem->name = g_strdup(name);
+
+                       if (modem->network != NULL) {
+                               connman_network_set_name(modem->network,
+                                                               modem->name);
+                               connman_network_update(modem->network);
+                       }
+               } else if (g_str_equal(key, "Strength") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&modem->strength);
+
+                       DBG("%s Strength %d", modem->path,
+                               modem->strength);
+
+                       if (modem->network != NULL) {
+                               connman_network_set_strength(modem->network,
+                                                       modem->strength);
+                               connman_network_update(modem->network);
+                       }
+               }
+
+               dbus_message_iter_next(dict);
+       }
+
+
+       if (modem->ipv4_context == NULL) {
+               /*
+                * netgreg_get_properties() was issued after we got
+                * cm_get_contexts_reply() where we create the
+                * ipv4_context. Though before we got the
+                * netreg_properties_reply the context was removed
+                * again. Therefore we have to skip the network
+                * creation.
+                */
+               return;
+       }
+
+       add_network(modem);
+
+       if (modem->active == TRUE)
+               set_connected(modem);
+}
+
+static int netreg_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_NETREG_INTERFACE,
+                       netreg_properties_reply, modem);
+}
+
+static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == 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);
+
+       if (g_str_equal(key, "Attached") == TRUE) {
+               dbus_message_iter_get_basic(&value,&modem->attached);
+
+               DBG("%s Attached %d", modem->path, modem->attached);
+
+               if (modem->attached == TRUE&&  modem->set_online == TRUE)
+                       netreg_get_properties(modem);
+       }
+
+       return TRUE;
+}
+
+static void cm_properties_reply(struct modem_data *modem, DBusMessageIter 
*dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict,&entry);
+               dbus_message_iter_get_basic(&entry,&key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry,&value);
+
+               if (g_str_equal(key, "Attached") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&modem->attached);
+
+                       DBG("%s Attached %d", modem->path,
+                               modem->attached);
+
+                       if (modem->attached == TRUE&&
+                                       modem->set_online == FALSE) {
+                               netreg_get_properties(modem);
+                       }
+                       return;
+               }
+
+               dbus_message_iter_next(dict);
+       }
+}
+
+static int cm_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_CM_INTERFACE,
+                               cm_properties_reply, modem);
+}
+
+static void update_sim_imsi(struct modem_data *modem,
+                               const char *imsi)
+{
+       DBG("%s imsi %s", modem->path, imsi);
+
+       if (g_strcmp0(modem->imsi, imsi) == 0)
+               return;
+
+       g_free(modem->imsi);
+       modem->imsi = g_strdup(imsi);
+}
+
+static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == 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);
+
+       if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
+               char *imsi;
+
+               dbus_message_iter_get_basic(&value,&imsi);
+
+               update_sim_imsi(modem, imsi);
+
+               if (modem->online == FALSE)
+                       modem_set_online(modem);
+               else if (modem->device == NULL)
+                       create_device(modem);
+       }
+
+       return TRUE;
+}
+
+static void sim_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict,&entry);
+               dbus_message_iter_get_basic(&entry,&key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry,&value);
+
+               if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
+                       char *imsi;
+
+                       dbus_message_iter_get_basic(&value,&imsi);
+
+                       update_sim_imsi(modem, imsi);
+
+                       if (modem->online == FALSE) {
+                               modem_set_online(modem);
+                               break;
+                       }
+
+                       if (modem->device == NULL)
+                               create_device(modem);
+
+                       if (has_interface(modem->interfaces,
+                                               OFONO_API_CM) == TRUE) {
+                               cm_get_properties(modem);
+                               cm_get_contexts(modem);
+                       }
+                       return;
+               }
+
+               dbus_message_iter_next(dict);
+       }
+}
+
+static int sim_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_SIM_INTERFACE,
+                       sim_properties_reply, modem);
+}
+
+static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == 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);
+
+       if (g_str_equal(key, "Powered") == TRUE) {
+               dbus_message_iter_get_basic(&value,&modem->powered);
+
+               DBG("%s Powered %d", modem->path, modem->powered);
+
+               if (modem->powered == FALSE)
+                       modem_set_powered(modem);
+       } else if (g_str_equal(key, "Online") == TRUE) {
+               dbus_message_iter_get_basic(&value,&modem->online);
+
+               DBG("%s Online %d", modem->path, modem->online);
+
+               if (modem->online == TRUE&&
+                               has_interface(modem->interfaces,
+                                               OFONO_API_CM) == TRUE) {
+                       cm_get_properties(modem);
+                       cm_get_contexts(modem);
+               }
+       } else if (g_str_equal(key, "Interfaces") == TRUE) {
+               modem->interfaces = extract_interfaces(&value);
+
+               DBG("%s Interfaces 0x%02x", modem->path,
+                       modem->interfaces);
+
+               if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
+                       if (modem->imsi == NULL&&
+                                       modem->set_powered == FALSE) {
+                               /*
+                                * Only use do GetProperties() when
+                                * device has not been powered up.
+                                */
+                               sim_get_properties(modem);
+                               return TRUE;
+                       }
+               }
+
+               if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
+                       if (modem->device == NULL) {
+                               create_device(modem);
+                               cm_get_properties(modem);
+                               cm_get_contexts(modem);
+                       }
+               } else {
+                       if (modem->ipv4_context != NULL) {
+                               remove_cm_context(modem,
+                                               modem->ipv4_context->path);
+                       }
+
+                       if (modem->device != NULL)
+                               destroy_device(modem);
+               }
+       } else if (g_str_equal(key, "Serial") == TRUE) {
+               char *serial;
+
+               dbus_message_iter_get_basic(&value,&serial);
+
+               g_free(modem->serial);
+               modem->serial = g_strdup(serial);
+
+               DBG("%s Serial %s", modem->path, modem->serial);
+       }
+
+       return TRUE;
+}
+
+static void add_modem(const char *path, DBusMessageIter *prop)
+{
+       struct modem_data *modem;
+
+       DBG("%s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem != NULL) {
+               /*
+                * When oFono powers up we ask for the modems and oFono is
+                * reporting with modem_added signal the modems. Only
+                * handle them once.
+                */
+               return;
+       }
+
+       modem = g_try_new0(struct modem_data, 1);
+       if (modem == NULL)
+               return;
+
+       modem->path = g_strdup(path);
+
+       g_hash_table_insert(modem_hash, g_strdup(path), modem);
+
+       while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(prop,&entry);
+               dbus_message_iter_get_basic(&entry,&key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry,&value);
+
+               if (g_str_equal(key, "Powered") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&modem->powered);
+
+                       DBG("%s Powered %d", modem->path, modem->powered);
+               } else if (g_str_equal(key, "Online") == TRUE) {
+                       dbus_message_iter_get_basic(&value,&modem->online);
+
+                       DBG("%s Online %d", modem->path, modem->online);
+               } else if (g_str_equal(key, "Interfaces") == TRUE) {
+                       modem->interfaces = extract_interfaces(&value);
+
+                       DBG("%s Interfaces 0x%02x", modem->path,
+                               modem->interfaces);
+               } else if (g_str_equal(key, "Serial") == TRUE) {
+                       char *serial;
+
+                       dbus_message_iter_get_basic(&value,&serial);
+                       modem->serial = g_strdup(serial);
+
+                       DBG("%s Serial %s", modem->path, modem->serial);
+               }
+
+               dbus_message_iter_next(prop);
+       }
+
+       if (modem->powered == FALSE)
+               modem_set_powered(modem);
+       else if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE)
+               sim_get_properties(modem);
+}
+
+static void modem_power_down(gpointer key, gpointer value, gpointer user_data)
+{
+       struct modem_data *modem = value;
+
+       DBG("%s", modem->path);
+
+       modem_set_unpowered(modem);
+}
+
+static void remove_modem(gpointer data)
+{
+       struct modem_data *modem = data;
+
+       DBG("%s", modem->path);
+
+       if (modem->call_get_properties != NULL)
+               dbus_pending_call_cancel(modem->call_get_properties);
+
+       if (modem->call_set_property != NULL)
+               dbus_pending_call_cancel(modem->call_set_property);
+
+       if (modem->call_get_contexts != NULL)
+               dbus_pending_call_cancel(modem->call_get_contexts);
+
+       if (modem->device != NULL)
+               destroy_device(modem);
+
+       if (modem->ipv4_context != NULL)
+               remove_cm_context(modem, modem->ipv4_context->path);
+
+       g_free(modem->serial);
+       g_free(modem->name);
+       g_free(modem->imsi);
+       g_free(modem->path);
+
+       g_free(modem);
+}
+
+static gboolean modem_added(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter, properties;
+       const char *path;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message,&iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter,&path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter,&properties);
+
+       add_modem(path,&properties);
+
+       return TRUE;
+}
+
+static gboolean modem_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter;
+       const char *path;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message,&iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter,&path);
+
+       g_hash_table_remove(modem_hash, path);
+
+       return TRUE;
+}
+
+static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       DBusMessageIter array, dict;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply,&array) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&array,&dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter value, properties;
+               const char *path;
+
+               dbus_message_iter_recurse(&dict,&value);
+               dbus_message_iter_get_basic(&value,&path);
+
+               dbus_message_iter_next(&value);
+               dbus_message_iter_recurse(&value,&properties);
+
+               add_modem(path,&properties);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int manager_get_modems(void)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, "/",
+                                       OFONO_MANAGER_INTERFACE, GET_MODEMS);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                       &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetModems()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, manager_get_modems_reply,
+                                       NULL, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void ofono_connect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       daemon_running = TRUE;
+
+       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, remove_modem);
+       if (modem_hash == NULL)
+               return;
+
+       context_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, NULL);
+       if (context_hash == NULL) {
+               g_hash_table_destroy(modem_hash);
+               return;
+       }
+
+       manager_get_modems();
+}
+
+static void ofono_disconnect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       daemon_running = FALSE;
+
+       if (modem_hash == NULL || context_hash == NULL)
+               return;
+
+       g_hash_table_destroy(modem_hash);
+       modem_hash = NULL;
+
+       g_hash_table_destroy(context_hash);
+       context_hash = NULL;
+}
+
  static int network_probe(struct connman_network *network)
  {
-       DBG("network %p", network);
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);

        return 0;
  }

  static void network_remove(struct connman_network *network)
  {
-       DBG("network %p", network);
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
  }

  static int network_connect(struct connman_network *network)
  {
-       DBG("network %p", network);
+       struct modem_data *modem = connman_network_get_data(network);

-       return 0;
+       DBG("%s network %p", modem->path, network);
+
+       return context_set_active(modem);
  }

  static int network_disconnect(struct connman_network *network)
  {
-       DBG("network %p", network);
+       struct modem_data *modem = connman_network_get_data(network);

-       return 0;
+       DBG("%s network %p", modem->path, network);
+
+       return context_set_inactive(modem);
  }

  static struct connman_network_driver network_driver = {
@@ -76,26 +1734,43 @@ static struct connman_network_driver network_driver = {

  static int modem_probe(struct connman_device *device)
  {
-       DBG("device %p", device);
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);

        return 0;
  }

  static void modem_remove(struct connman_device *device)
  {
-       DBG("device %p", device);
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
  }

+/*
+ * Enabling and disabling modems are steered through the rfkill
+ * interface. That means when ConnMan toggles the rfkill bit oFono
+ * will add or remove the modems.
+ *
+ * ConnMan will always power up (set Powered and Online) the
+ * modems. No need to power them down because this will be done
+ * through the rfkill inteface.
+ */
  static int modem_enable(struct connman_device *device)
  {
-       DBG("device %p", device);
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);

        return 0;
  }

  static int modem_disable(struct connman_device *device)
  {
-       DBG("device %p", device);
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);

        return 0;
  }
@@ -109,6 +1784,17 @@ static struct connman_device_driver modem_driver = {
        .disable        = modem_disable,
  };

+static guint watch;
+static guint modem_added_watch;
+static guint modem_removed_watch;
+static guint modem_watch;
+static guint cm_watch;
+static guint sim_watch;
+static guint context_added_watch;
+static guint context_removed_watch;
+static guint netreg_watch;
+static guint context_watch;
+
  static int ofono_init(void)
  {
        int err;
@@ -119,6 +1805,74 @@ static int ofono_init(void)
        if (connection == NULL)
                return -EIO;

+       watch = g_dbus_add_service_watch(connection,
+                                       OFONO_SERVICE, ofono_connect,
+                                       ofono_disconnect, NULL, NULL);
+
+       modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_MANAGER_INTERFACE,
+                                               MODEM_ADDED,
+                                               modem_added,
+                                               NULL, NULL);
+
+       modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_MANAGER_INTERFACE,
+                                               MODEM_REMOVED,
+                                               modem_removed,
+                                               NULL, NULL);
+
+       modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_MODEM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               modem_changed,
+                                               NULL, NULL);
+
+       cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               cm_changed,
+                                               NULL, NULL);
+
+       sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_SIM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               sim_changed,
+                                               NULL, NULL);
+
+       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               CONTEXT_ADDED,
+                                               cm_context_added,
+                                               NULL, NULL);
+
+       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               CONTEXT_REMOVED,
+                                               cm_context_removed,
+                                               NULL, NULL);
+
+       context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CONTEXT_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               context_changed,
+                                               NULL, NULL);
+
+       netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_NETREG_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               netreg_changed,
+                                               NULL, NULL);
+
+
+       if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
+                       modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
+                       context_added_watch == 0 ||
+                       context_removed_watch == 0 ||
+                       context_watch == 0 || netreg_watch == 0) {
+               err = -EIO;
+               goto remove;
+       }
+
        err = connman_network_driver_register(&network_driver);
        if (err<  0)
                goto remove;
@@ -132,6 +1886,16 @@ static int ofono_init(void)
        return 0;

  remove:
+       g_dbus_remove_watch(connection, netreg_watch);
+       g_dbus_remove_watch(connection, context_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, sim_watch);
+       g_dbus_remove_watch(connection, cm_watch);
+       g_dbus_remove_watch(connection, modem_watch);
+       g_dbus_remove_watch(connection, modem_removed_watch);
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, watch);
        dbus_connection_unref(connection);

        return err;
@@ -141,9 +1905,39 @@ static void ofono_exit(void)
  {
        DBG("");

+       if (modem_hash != NULL) {
+               /*
+                * We should propably wait for the SetProperty() reply
+                * message, because ...
+                */
+               g_hash_table_foreach(modem_hash, modem_power_down, NULL);
+
+               /*
+                * ... here we will cancel the call.
+                */
+               g_hash_table_destroy(modem_hash);
+               modem_hash = NULL;
+       }
+
+       if (context_hash != NULL) {
+               g_hash_table_destroy(context_hash);
+               context_hash = NULL;
+       }
+
        connman_device_driver_unregister(&modem_driver);
        connman_network_driver_unregister(&network_driver);

+       g_dbus_remove_watch(connection, netreg_watch);
+       g_dbus_remove_watch(connection, context_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, sim_watch);
+       g_dbus_remove_watch(connection, cm_watch);
+       g_dbus_remove_watch(connection, modem_watch);
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, modem_removed_watch);
+       g_dbus_remove_watch(connection, watch);
+
        dbus_connection_unref(connection);
  }


Kind regards,
Guillaume

_______________________________________________
connman mailing list
connman@connman.net
http://lists.connman.net/listinfo/connman

Reply via email to