X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fofono.c;h=f0bd3c5d4574b7e87bb29746aca50d6ca2863c87;hb=d8c91830a27ce9ad75a6d7fe54068d7e697cbdf9;hp=6652077299b0e595e183b225b3d5b3fd1f9d3daf;hpb=c3d4dfaee40b0c8817c38117ae08a47c4ec7f1f2;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/ofono.c b/plugins/ofono.c old mode 100644 new mode 100755 index 6652077..f0bd3c5 --- a/plugins/ofono.c +++ b/plugins/ofono.c @@ -2,8 +2,9 @@ * * Connection Manager * - * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2011-2014 BMW Car IT GmbH. * * 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 @@ -29,16 +30,16 @@ #include #include +#include #define CONNMAN_API_SUBJECT_TO_CHANGE #include #include #include -#include -#include #include -#include +#include #include +#include #include "mcc.h" @@ -46,633 +47,800 @@ #define OFONO_MANAGER_INTERFACE OFONO_SERVICE ".Manager" #define OFONO_MODEM_INTERFACE OFONO_SERVICE ".Modem" -#define OFONO_GPRS_INTERFACE OFONO_SERVICE ".ConnectionManager" -#define OFONO_CONTEXT_INTERFACE OFONO_SERVICE ".ConnectionContext" #define OFONO_SIM_INTERFACE OFONO_SERVICE ".SimManager" -#define OFONO_REGISTRATION_INTERFACE OFONO_SERVICE ".NetworkRegistration" +#define OFONO_NETREG_INTERFACE OFONO_SERVICE ".NetworkRegistration" +#define OFONO_CM_INTERFACE OFONO_SERVICE ".ConnectionManager" +#define OFONO_CONTEXT_INTERFACE OFONO_SERVICE ".ConnectionContext" +#define OFONO_CDMA_CM_INTERFACE OFONO_SERVICE ".cdma.ConnectionManager" +#define OFONO_CDMA_NETREG_INTERFACE OFONO_SERVICE ".cdma.NetworkRegistration" +#define MODEM_ADDED "ModemAdded" +#define MODEM_REMOVED "ModemRemoved" #define PROPERTY_CHANGED "PropertyChanged" -#define GET_PROPERTIES "GetProperties" -#define SET_PROPERTY "SetProperty" #define CONTEXT_ADDED "ContextAdded" #define CONTEXT_REMOVED "ContextRemoved" -#define ADD_CONTEXT "AddContext" -#define GET_MODEMS "GetModems" -#define MODEM_ADDED "ModemAdded" -#define MODEM_REMOVED "ModemRemoved" +#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, + OFONO_API_CDMA_NETREG = 0x8, + OFONO_API_CDMA_CM = 0x10, +}; + +/* + * The way this plugin works is following: + * + * powered -> SubscriberIdentity or Online = True -> gprs, context -> + * attached -> netreg -> ready + * + * Depending on the modem type, this plugin will behave differently. + * + * GSM working flow: + * + * When a new modem appears, the plugin always powers it up. This + * allows the plugin to create a connman_device. The core will call + * modem_enable() if the technology is enabled. modem_enable() will + * then set the modem online. If the technology is disabled then + * modem_disable() will just set the modem offline. The modem is + * always kept powered all the time. + * + * After setting the modem online the plugin waits for the + * ConnectionManager and ConnectionContext to appear. When the context + * signals that it is attached and the NetworkRegistration interface + * appears, a new Service will be created and registered at the core. + * + * When asked to connect to the network (network_connect()) the plugin + * will set the Active property on the context. If this operation is + * successful the modem is connected to the network. oFono will inform + * the plugin about IP configuration through the updating the context's + * properties. + * + * CDMA working flow: + * + * When a new modem appears, the plugin always powers it up. This + * allows the plugin to create connman_device either using IMSI either + * using modem Serial if the modem got a SIM interface or not. + * + * As for GSM, the core will call modem_enable() if the technology + * is enabled. modem_enable() will then set the modem online. + * If the technology is disabled then modem_disable() will just set the + * modem offline. The modem is always kept powered all the time. + * + * After setting the modem online the plugin waits for CdmaConnectionManager + * interface to appear. Then, once CdmaNetworkRegistration appears, a new + * Service will be created and registered at the core. + * + * When asked to connect to the network (network_connect()) the plugin + * will power up the CdmaConnectionManager interface. + * If the operation is successful the modem is connected to the network. + * oFono will inform the plugin about IP configuration through the + * updating CdmaConnectionManager settings properties. + */ + static DBusConnection *connection; -static GHashTable *modem_hash = NULL; +static GHashTable *modem_hash; +static GHashTable *context_hash; -static GHashTable *network_hash; +struct network_context { + char *path; + int index; + struct connman_network *network; + + enum connman_ipconfig_method ipv4_method; + struct connman_ipaddress *ipv4_address; + char *ipv4_nameservers; + + enum connman_ipconfig_method ipv6_method; + struct connman_ipaddress *ipv6_address; + char *ipv6_nameservers; + + int refcount; + + bool active; + bool valid_apn; /* APN is 'valid' if length > 0 */ +}; struct modem_data { char *path; + struct connman_device *device; - gboolean has_sim; - gboolean has_reg; - gboolean has_gprs; - gboolean available; - gboolean pending_online; - dbus_bool_t requested_online; - dbus_bool_t online; - - /* org.ofono.ConnectionManager properties */ - dbus_bool_t powered; - dbus_bool_t attached; - dbus_bool_t roaming_allowed; - connman_bool_t registered; - connman_bool_t roaming; - uint8_t strength, has_strength; - char *operator; -}; + GSList *context_list; -struct network_info { - struct connman_network *network; + /* Modem Interface */ + char *serial; + bool powered; + bool online; + uint8_t interfaces; + bool ignore; - enum connman_ipconfig_method ipv4_method; - struct connman_ipaddress ipv4_address; + bool set_powered; - enum connman_ipconfig_method ipv6_method; - struct connman_ipaddress ipv6_address; + /* CDMA ConnectionManager Interface */ + bool cdma_cm_powered; + + /* ConnectionManager Interface */ + bool attached; + bool cm_powered; + + /* SimManager Interface */ + char *imsi; + + /* Netreg Interface */ + char *name; + uint8_t strength; + uint8_t data_strength; /* 1xEVDO signal strength */ + bool registered; + bool roaming; + + /* pending calls */ + DBusPendingCall *call_set_property; + DBusPendingCall *call_get_properties; + DBusPendingCall *call_get_contexts; }; -static int modem_probe(struct connman_device *device) +static const char *api2string(enum ofono_api api) +{ + switch (api) { + case OFONO_API_SIM: + return "sim"; + case OFONO_API_NETREG: + return "netreg"; + case OFONO_API_CM: + return "cm"; + case OFONO_API_CDMA_NETREG: + return "cdma-netreg"; + case OFONO_API_CDMA_CM: + return "cmda-cm"; + } + + return "unknown"; +} + +static char *get_ident(const char *path) { - DBG("device %p", device); + char *pos; - return 0; + if (*path != '/') + return NULL; + + pos = strrchr(path, '/'); + if (!pos) + return NULL; + + return pos + 1; } -static void modem_remove(struct connman_device *device) +static struct network_context *get_context_with_path(GSList *context_list, + const gchar *path) { - DBG("device %p", device); + GSList *list; + + DBG("path %s", path); + + for (list = context_list; list; list = list->next) { + struct network_context *context = list->data; + + if (g_strcmp0(context->path, path) == 0) + return context; + } + + return NULL; } -static int call_ofono(const char *path, - const char *interface, const char *method, - DBusPendingCallNotifyFunction notify, void *user_data, - DBusFreeFunction free_function, - int type, ...) +static struct network_context *get_context_with_network(GSList *context_list, + const struct connman_network *network) { - DBusMessage *message; - DBusPendingCall *call; - dbus_bool_t ok; - va_list va; + GSList *list; + + DBG("network %p", network); - DBG("path %s %s.%s", path, interface, method); + for (list = context_list; list; list = list->next) { + struct network_context *context = list->data; - if (path == NULL) - return -EINVAL; + if (context->network == network) + return context; + } - message = dbus_message_new_method_call(OFONO_SERVICE, path, - interface, method); - if (message == NULL) - return -ENOMEM; + return NULL; +} + +static struct network_context *network_context_alloc(const char *path) +{ + struct network_context *context; - dbus_message_set_auto_start(message, FALSE); + context = g_try_new0(struct network_context, 1); + if (!context) + return NULL; - va_start(va, type); - ok = dbus_message_append_args_valist(message, type, va); - va_end(va); + context->path = g_strdup(path); + context->index = -1; - if (!ok) - return -ENOMEM; + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + context->ipv4_address = NULL; + context->ipv4_nameservers = NULL; - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to call %s.%s", interface, method); - dbus_message_unref(message); - return -EINVAL; + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + context->ipv6_address = NULL; + context->ipv6_nameservers = NULL; + + context->refcount = 1; + + return context; +} + +static void network_context_ref(struct network_context *context) +{ + DBG("%p ref %d", context, context->refcount + 1); + + __sync_fetch_and_add(&context->refcount, 1); +} + +static void network_context_unref(struct network_context *context) +{ + DBG("%p ref %d", context, context->refcount - 1); + + if (__sync_fetch_and_sub(&context->refcount, 1) != 1) + return; + + g_free(context->path); + + connman_ipaddress_free(context->ipv4_address); + g_free(context->ipv4_nameservers); + + connman_ipaddress_free(context->ipv6_address); + g_free(context->ipv6_nameservers); + + g_free(context); +} + +static void set_connected(struct modem_data *modem, + struct network_context *context) +{ + struct connman_service *service; + bool setip = false; + enum connman_ipconfig_method method; + char *nameservers; + int index; + + DBG("%s", modem->path); + + index = context->index; + + method = context->ipv4_method; + if (index < 0 || (!context->ipv4_address && + method == CONNMAN_IPCONFIG_METHOD_FIXED)) { + connman_error("Invalid index and/or address"); + return; } - if (call == NULL) { - connman_error("D-Bus connection not available"); - dbus_message_unref(message); - return -EINVAL; + service = connman_service_lookup_from_network(context->network); + if (!service) + return; + + connman_service_create_ip4config(service, index); + connman_network_set_ipv4_method(context->network, method); + + if (method == CONNMAN_IPCONFIG_METHOD_FIXED || + method == CONNMAN_IPCONFIG_METHOD_DHCP) { + setip = true; } - dbus_pending_call_set_notify(call, notify, user_data, free_function); + if (method == CONNMAN_IPCONFIG_METHOD_FIXED) { + connman_network_set_ipaddress(context->network, + context->ipv4_address); + } - dbus_message_unref(message); + method = context->ipv6_method; + connman_service_create_ip6config(service, index); + connman_network_set_ipv6_method(context->network, method); - return -EINPROGRESS; + if (method == CONNMAN_IPCONFIG_METHOD_AUTO) { + setip = true; + } + + /* Set the nameservers */ + if (context->ipv4_nameservers && + context->ipv6_nameservers) { + nameservers = g_strdup_printf("%s %s", + context->ipv4_nameservers, + context->ipv6_nameservers); + connman_network_set_nameservers(context->network, nameservers); + g_free(nameservers); + } else if (context->ipv4_nameservers) { + connman_network_set_nameservers(context->network, + context->ipv4_nameservers); + } else if (context->ipv6_nameservers) { + connman_network_set_nameservers(context->network, + context->ipv6_nameservers); + } + + if (setip) { + connman_network_set_index(context->network, index); + connman_network_set_connected(context->network, true); + } +} + +static void set_disconnected(struct network_context *context) +{ + DBG("%s", context->path); + + if (context->network) + connman_network_set_connected(context->network, false); + + g_free(context->ipv4_nameservers); + context->ipv4_nameservers = NULL; + if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv4_method = + CONNMAN_IPCONFIG_METHOD_UNKNOWN; + + g_free(context->ipv6_nameservers); + context->ipv6_nameservers = NULL; + if (context->ipv6_method != CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv6_method = + CONNMAN_IPCONFIG_METHOD_UNKNOWN; +} + +typedef void (*set_property_cb)(struct modem_data *data, + struct network_context *context, bool success); +typedef void (*get_properties_cb)(struct modem_data *data, + DBusMessageIter *dict); + +struct property_info { + struct modem_data *modem; + struct network_context *context; + const char *path; + const char *interface; + const char *property; + set_property_cb set_property_cb; + get_properties_cb get_properties_cb; +}; + +static void free_property_info(void * memory) +{ + struct property_info * info = memory; + + if (info->context) + network_context_unref(info->context); + + g_free(info); } static void set_property_reply(DBusPendingCall *call, void *user_data) { + struct property_info *info = user_data; DBusMessage *reply; DBusError error; - char const *name = user_data; + bool success = true; - DBG(""); + 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("SetProperty(%s) %s %s", name, + 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) + (*info->set_property_cb)(info->modem, info->context, + success); + dbus_message_unref(reply); dbus_pending_call_unref(call); } -static int set_property(const char *path, const char *interface, +static int set_property(struct modem_data *modem, + struct network_context *context, + const char *path, const char *interface, const char *property, int type, void *value, - DBusPendingCallNotifyFunction notify, void *user_data, - DBusFreeFunction free_function) + set_property_cb notify) { DBusMessage *message; DBusMessageIter iter; - DBusPendingCall *call; - - DBG("path %s %s.%s", path, interface, property); + struct property_info *info; - g_assert(notify == NULL ? free_function == NULL : 1); + DBG("%s path %s %s.%s", modem->path, path, interface, property); - if (path == NULL) - return -EINVAL; + if (modem->call_set_property) { + DBG("Cancel pending SetProperty"); + dbus_pending_call_cancel(modem->call_set_property); + dbus_pending_call_unref(modem->call_set_property); + modem->call_set_property = NULL; + } message = dbus_message_new_method_call(OFONO_SERVICE, path, interface, SET_PROPERTY); - if (message == NULL) + if (!message) return -ENOMEM; - dbus_message_set_auto_start(message, FALSE); - dbus_message_iter_init_append(message, &iter); connman_dbus_property_append_basic(&iter, property, type, value); - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to change \"%s\" property on %s", - property, interface); + if (!dbus_connection_send_with_reply(connection, message, + &modem->call_set_property, TIMEOUT)) { + connman_error("Failed to change property: %s %s.%s", + path, interface, property); dbus_message_unref(message); return -EINVAL; } - if (call == NULL) { + if (!modem->call_set_property) { connman_error("D-Bus connection not available"); dbus_message_unref(message); return -EINVAL; } - if (notify == NULL) { - notify = set_property_reply; - user_data = (void *)property; - free_function = NULL; + info = g_try_new0(struct property_info, 1); + if (!info) { + dbus_message_unref(message); + return -ENOMEM; } - dbus_pending_call_set_notify(call, notify, user_data, free_function); + info->modem = modem; + info->context = context; + info->path = path; + info->interface = interface; + info->property = property; + info->set_property_cb = notify; + + if (info->context) + network_context_ref(info->context); + + dbus_pending_call_set_notify(modem->call_set_property, + set_property_reply, info, free_property_info); dbus_message_unref(message); return -EINPROGRESS; } -static void update_modem_online(struct modem_data *modem, - connman_bool_t online) +static void get_properties_reply(DBusPendingCall *call, void *user_data) { - DBG("modem %p path %s online %d", modem, modem->path, online); + struct property_info *info = user_data; + DBusMessageIter array, dict; + DBusMessage *reply; + DBusError error; - modem->online = online; - modem->requested_online = online; - modem->pending_online = FALSE; + DBG("%s path %s %s", info->modem->path, info->path, info->interface); - if (modem->device) - connman_device_set_powered(modem->device, online); -} + info->modem->call_get_properties = NULL; -static void remove_device_networks(struct connman_device *device) -{ - GHashTableIter iter; - gpointer key, value; - GSList *info_list = NULL; - GSList *list; + dbus_error_init(&error); - if (network_hash == NULL) - return; + reply = dbus_pending_call_steal_reply(call); - g_hash_table_iter_init(&iter, network_hash); + 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); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - struct network_info *info = value; + goto done; + } - if (connman_network_get_device(info->network) != device) - continue; + if (!dbus_message_iter_init(reply, &array)) + goto done; - info_list = g_slist_append(info_list, info); - } + if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) + goto done; - for (list = info_list; list != NULL; list = list->next) { - struct network_info *info = list->data; + dbus_message_iter_recurse(&array, &dict); - connman_device_remove_network(device, info->network); - } + if (info->get_properties_cb) + (*info->get_properties_cb)(info->modem, &dict); + +done: - g_slist_free(info_list); + dbus_message_unref(reply); + + dbus_pending_call_unref(call); } -static void set_online_reply(DBusPendingCall *call, void *user_data) +static int get_properties(const char *path, const char *interface, + get_properties_cb notify, + struct modem_data *modem) { - struct modem_data *modem; - DBusMessage *reply; - DBusError error; - gboolean result; - - DBG("path %s", (char *)user_data); + DBusMessage *message; + struct property_info *info; - if (modem_hash == NULL) - return; + DBG("%s path %s %s", modem->path, path, interface); - modem = g_hash_table_lookup(modem_hash, user_data); - if (modem == NULL) - return; + if (modem->call_get_properties) { + connman_error("Pending GetProperties"); + return -EBUSY; + } - reply = dbus_pending_call_steal_reply(call); + message = dbus_message_new_method_call(OFONO_SERVICE, path, + interface, GET_PROPERTIES); + if (!message) + return -ENOMEM; - dbus_error_init(&error); + if (!dbus_connection_send_with_reply(connection, message, + &modem->call_get_properties, TIMEOUT)) { + connman_error("Failed to call %s.GetProperties()", interface); + dbus_message_unref(message); + return -EINVAL; + } - if (dbus_set_error_from_message(&error, reply)) { - connman_error("SetProperty(Online) %s %s", - error.name, error.message); - dbus_error_free(&error); + if (!modem->call_get_properties) { + connman_error("D-Bus connection not available"); + dbus_message_unref(message); + return -EINVAL; + } - result = modem->online; - } else - result = modem->requested_online; + info = g_try_new0(struct property_info, 1); + if (!info) { + dbus_message_unref(message); + return -ENOMEM; + } - if (modem->pending_online) - update_modem_online(modem, result); + info->modem = modem; + info->path = path; + info->interface = interface; + info->get_properties_cb = notify; - if (result == FALSE) - remove_device_networks(modem->device); + dbus_pending_call_set_notify(modem->call_get_properties, + get_properties_reply, info, g_free); - dbus_message_unref(reply); + dbus_message_unref(message); - dbus_pending_call_unref(call); + return -EINPROGRESS; } -static int modem_change_online(char const *path, dbus_bool_t online) +static void context_set_active_reply(struct modem_data *modem, + struct network_context *context, bool success) { - struct modem_data *modem = g_hash_table_lookup(modem_hash, path); - - if (modem == NULL) - return -ENODEV; + DBG("%s", context->path); - if (modem->online == online) - return -EALREADY; + if (success) { + /* + * Don't handle do anything on success here. oFono will send + * the change via PropertyChanged signal. + */ + return; + } - modem->requested_online = online; + /* + * 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? + */ + + if (!context->network) { + /* + * In the case where we power down the device + * we don't wait for the reply, therefore the network + * might already be gone. + */ + return; + } - return set_property(path, OFONO_MODEM_INTERFACE, "Online", - DBUS_TYPE_BOOLEAN, &online, - set_online_reply, - (void *)g_strdup(path), g_free); + connman_network_set_error(context->network, + CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); } -static int modem_enable(struct connman_device *device) +static int context_set_active(struct modem_data *modem, + struct network_context *context, + dbus_bool_t active) { - const char *path = connman_device_get_string(device, "Path"); + int err; + + DBG("%s active %d", modem->path, active); + + err = set_property(modem, context, context->path, + OFONO_CONTEXT_INTERFACE, + "Active", DBUS_TYPE_BOOLEAN, + &active, + context_set_active_reply); - DBG("device %p, path, %s", device, path); + if (!active && err == -EINPROGRESS) + return 0; - return modem_change_online(path, TRUE); + return err; } -static int modem_disable(struct connman_device *device) +static void cdma_cm_set_powered_reply(struct modem_data *modem, + struct network_context *context, bool success) { - const char *path = connman_device_get_string(device, "Path"); + DBG("%s", context->path); - DBG("device %p path %s", device, path); + if (success) { + /* + * Don't handle do anything on success here. oFono will send + * the change via PropertyChanged signal. + */ + return; + } - return modem_change_online(path, FALSE); -} + /* + * Powered = True might fail due a timeout. That means oFono + * still tries to go online. If we retry to set Powered = True, + * we just get a InProgress error message. Should we power + * cycle the modem in such cases? + */ + + if (!context->network) { + /* + * In the case where we power down the device + * we don't wait for the reply, therefore the network + * might already be gone. + */ + return; + } -static struct connman_device_driver modem_driver = { - .name = "modem", - .type = CONNMAN_DEVICE_TYPE_CELLULAR, - .probe = modem_probe, - .remove = modem_remove, - .enable = modem_enable, - .disable = modem_disable, -}; + connman_network_set_error(context->network, + CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); +} -static void modem_remove_device(struct modem_data *modem) +static int cdma_cm_set_powered(struct modem_data *modem, dbus_bool_t powered) { - DBG("modem %p path %s device %p", modem, modem->path, modem->device); + int err; + struct network_context *context = NULL; - if (modem->device == NULL) - return; + if (!modem->context_list) + return -1; - remove_device_networks(modem->device); + DBG("%s powered %d", modem->path, powered); - connman_device_unregister(modem->device); - connman_device_unref(modem->device); + /* In case of CDMA, there is only one context */ + context = modem->context_list->data; + err = set_property(modem, context, modem->path, + OFONO_CDMA_CM_INTERFACE, + "Powered", DBUS_TYPE_BOOLEAN, + &powered, + cdma_cm_set_powered_reply); - modem->device = NULL; + if (!powered && err == -EINPROGRESS) + return 0; + + return err; } -static void remove_modem(gpointer data) +static int modem_set_online(struct modem_data *modem, dbus_bool_t online) { - struct modem_data *modem = data; - - modem_remove_device(modem); - - g_free(modem->path); - g_free(modem->operator); + DBG("%s online %d", modem->path, online); - g_free(modem); + return set_property(modem, NULL, modem->path, + OFONO_MODEM_INTERFACE, + "Online", DBUS_TYPE_BOOLEAN, + &online, + NULL); } -static void remove_network(gpointer data) +static int cm_set_powered(struct modem_data *modem, dbus_bool_t powered) { - struct network_info *info = data; - struct connman_device *device; + int err; - device = connman_network_get_device(info->network); - if (device != NULL) - connman_device_remove_network(device, info->network); + DBG("%s powered %d", modem->path, powered); - connman_network_unref(info->network); + err = set_property(modem, NULL, modem->path, + OFONO_CM_INTERFACE, + "Powered", DBUS_TYPE_BOOLEAN, + &powered, + NULL); - g_free(info); -} - -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_service(struct connman_network *network) -{ - const char *path; - char *group; - - DBG(""); - - path = connman_network_get_string(network, "Path"); - - group = get_ident(path); - - connman_network_set_group(network, group); -} - -static int network_probe(struct connman_network *network) -{ - return 0; -} - -static gboolean pending_network_is_available(struct connman_network *network) -{ - /* Modem or network may be removed */ - if (network == NULL || connman_network_get_device(network) == NULL) { - DBG("Modem or network was removed"); - return FALSE; - } - - return TRUE; -} - -static void set_connected(struct network_info *info, - connman_bool_t connected) -{ - gboolean setip = FALSE; - - DBG("network %p connected %d", info->network, connected); - - switch (info->ipv4_method) { - case CONNMAN_IPCONFIG_METHOD_UNKNOWN: - case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_MANUAL: - case CONNMAN_IPCONFIG_METHOD_AUTO: - break; - - case CONNMAN_IPCONFIG_METHOD_FIXED: - connman_network_set_ipv4_method(info->network, - info->ipv4_method); - connman_network_set_ipaddress(info->network, - &info->ipv4_address); - setip = TRUE; - break; - - case CONNMAN_IPCONFIG_METHOD_DHCP: - connman_network_set_ipv4_method(info->network, - info->ipv4_method); - setip = TRUE; - break; - } - - switch (info->ipv6_method) { - case CONNMAN_IPCONFIG_METHOD_UNKNOWN: - case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_MANUAL: - case CONNMAN_IPCONFIG_METHOD_DHCP: - case CONNMAN_IPCONFIG_METHOD_AUTO: - break; - - case CONNMAN_IPCONFIG_METHOD_FIXED: - connman_network_set_ipv6_method(info->network, - info->ipv6_method); - connman_network_set_ipaddress(info->network, - &info->ipv6_address); - setip = TRUE; - break; - } - - if (setip == TRUE) - connman_network_set_connected(info->network, connected); -} - -static void set_active_reply(DBusPendingCall *call, void *user_data) -{ - char const *path = user_data; - DBusMessage *reply; - DBusError error; - struct network_info *info; - - info = g_hash_table_lookup(network_hash, path); - - reply = dbus_pending_call_steal_reply(call); - - if (info == NULL) - goto done; - - DBG("path %s network %p", path, info->network); - - if (!pending_network_is_available(info->network)) - goto done; - - dbus_error_init(&error); - - if (dbus_set_error_from_message(&error, reply)) { - connman_error("SetProperty(Active) %s %s", - error.name, error.message); + if (!powered && err == -EINPROGRESS) + return 0; - if (connman_network_get_index(info->network) < 0) - connman_network_set_error(info->network, - CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); - - dbus_error_free(&error); - } else if (connman_network_get_index(info->network) >= 0) - set_connected(info, TRUE); - -done: - dbus_message_unref(reply); - - dbus_pending_call_unref(call); -} - -static int set_network_active(struct connman_network *network) -{ - dbus_bool_t value = TRUE; - const char *path = connman_network_get_string(network, "Path"); - - DBG("network %p, path %s", network, path); - - return set_property(path, OFONO_CONTEXT_INTERFACE, - "Active", DBUS_TYPE_BOOLEAN, &value, - set_active_reply, g_strdup(path), g_free); + return err; } -static int set_network_inactive(struct connman_network *network) +static int modem_set_powered(struct modem_data *modem, dbus_bool_t powered) { int err; - dbus_bool_t value = FALSE; - const char *path = connman_network_get_string(network, "Path"); - DBG("network %p, path %s", network, path); + DBG("%s powered %d", modem->path, powered); - err = set_property(path, OFONO_CONTEXT_INTERFACE, - "Active", DBUS_TYPE_BOOLEAN, &value, - NULL, NULL, NULL); + modem->set_powered = powered; - if (err == -EINPROGRESS) - err = 0; + err = set_property(modem, NULL, modem->path, + OFONO_MODEM_INTERFACE, + "Powered", DBUS_TYPE_BOOLEAN, + &powered, + NULL); + + if (!powered && err == -EINPROGRESS) + return 0; return err; } -static int network_connect(struct connman_network *network) +static bool has_interface(uint8_t interfaces, + enum ofono_api api) { - struct connman_device *device; - struct modem_data *modem; - - DBG("network %p", network); - - device = connman_network_get_device(network); - if (device == NULL) - return -ENODEV; - - modem = connman_device_get_data(device); - if (modem == NULL) - return -ENODEV; + if ((interfaces & api) == api) + return true; - if (modem->registered == FALSE) - return -ENOLINK; - - if (modem->powered == FALSE) - return -ENOLINK; - - if (modem->roaming_allowed == FALSE && modem->roaming == TRUE) - return -ENOLINK; - - return set_network_active(network); + return false; } -static int network_disconnect(struct connman_network *network) +static uint8_t extract_interfaces(DBusMessageIter *array) { - DBG("network %p", network); + DBusMessageIter entry; + uint8_t interfaces = 0; - if (connman_network_get_index(network) < 0) - return -ENOTCONN; + dbus_message_iter_recurse(array, &entry); - connman_network_set_associating(network, FALSE); + while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { + const char *name; - return set_network_inactive(network); -} + dbus_message_iter_get_basic(&entry, &name); -static void network_remove(struct connman_network *network) -{ - char const *path = connman_network_get_string(network, "Path"); + if (g_str_equal(name, OFONO_SIM_INTERFACE)) + interfaces |= OFONO_API_SIM; + else if (g_str_equal(name, OFONO_NETREG_INTERFACE)) + interfaces |= OFONO_API_NETREG; + else if (g_str_equal(name, OFONO_CM_INTERFACE)) + interfaces |= OFONO_API_CM; + else if (g_str_equal(name, OFONO_CDMA_CM_INTERFACE)) + interfaces |= OFONO_API_CDMA_CM; + else if (g_str_equal(name, OFONO_CDMA_NETREG_INTERFACE)) + interfaces |= OFONO_API_CDMA_NETREG; - DBG("network %p path %s", network, path); + dbus_message_iter_next(&entry); + } - g_hash_table_remove(network_hash, path); + return interfaces; } -static struct connman_network_driver network_driver = { - .name = "network", - .type = CONNMAN_NETWORK_TYPE_CELLULAR, - .probe = network_probe, - .remove = network_remove, - .connect = network_connect, - .disconnect = network_disconnect, -}; - -static void get_dns(DBusMessageIter *array, struct network_info *info) +static char *extract_nameservers(DBusMessageIter *array) { DBusMessageIter entry; - gchar *nameservers = NULL, *nameservers_old = NULL; - - DBG(""); - + char *nameservers = NULL; + char *tmp; dbus_message_iter_recurse(array, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { - const char *dns; + const char *nameserver; - dbus_message_iter_get_basic(&entry, &dns); + dbus_message_iter_get_basic(&entry, &nameserver); - DBG("dns %s", dns); - - if (nameservers == NULL) { - - nameservers = g_strdup(dns); + if (!nameservers) { + nameservers = g_strdup(nameserver); } else { - - nameservers_old = nameservers; - nameservers = g_strdup_printf("%s %s", - nameservers_old, dns); - g_free(nameservers_old); + tmp = nameservers; + nameservers = g_strdup_printf("%s %s", tmp, nameserver); + g_free(tmp); } dbus_message_iter_next(&entry); } - connman_network_set_nameservers(info->network, nameservers); - - g_free(nameservers); + return nameservers; } -static void update_ipv4_settings(DBusMessageIter *array, - struct network_info *info) +static void extract_ipv4_settings(DBusMessageIter *array, + struct network_context *context) { DBusMessageIter dict; char *address = NULL, *netmask = NULL, *gateway = NULL; + char *nameservers = NULL; const char *interface = NULL; + int index = -1; - DBG("network %p", info->network); + connman_ipaddress_free(context->ipv4_address); + context->ipv4_address = NULL; if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return; @@ -686,99 +854,101 @@ static void update_ipv4_settings(DBusMessageIter *array, dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); - DBG("key %s", key); - dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Interface") == TRUE) { - int index; - + if (g_str_equal(key, "Interface")) { dbus_message_iter_get_basic(&value, &interface); - DBG("interface %s", interface); + DBG("Interface %s", interface); index = connman_inet_ifindex(interface); - if (index >= 0) { - connman_network_set_index(info->network, index); - } else { - connman_error("Can not find interface %s", - interface); - break; - } - } else if (g_str_equal(key, "Method") == TRUE) { - const char *method; - dbus_message_iter_get_basic(&value, &method); + DBG("index %d", index); + } else if (g_str_equal(key, "Method")) { + dbus_message_iter_get_basic(&value, &val); - if (g_strcmp0(method, "static") == 0) { + DBG("Method %s", val); - info->ipv4_method = - CONNMAN_IPCONFIG_METHOD_FIXED; - } else if (g_strcmp0(method, "dhcp") == 0) { + if (g_strcmp0(val, "static") == 0) + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED; + else if (g_strcmp0(val, "dhcp") == 0) + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_DHCP; - info->ipv4_method = - CONNMAN_IPCONFIG_METHOD_DHCP; - break; - } - } else if (g_str_equal(key, "Address") == TRUE) { + } else if (g_str_equal(key, "Address")) { dbus_message_iter_get_basic(&value, &val); address = g_strdup(val); - DBG("address %s", address); - } else if (g_str_equal(key, "Netmask") == TRUE) { + DBG("Address %s", address); + } else if (g_str_equal(key, "Netmask")) { dbus_message_iter_get_basic(&value, &val); netmask = g_strdup(val); - DBG("netmask %s", netmask); - } else if (g_str_equal(key, "DomainNameServers") == TRUE) { + DBG("Netmask %s", netmask); + } else if (g_str_equal(key, "DomainNameServers")) { + nameservers = extract_nameservers(&value); - get_dns(&value, info); - } else if (g_str_equal(key, "Gateway") == TRUE) { + DBG("Nameservers %s", nameservers); + } else if (g_str_equal(key, "Gateway")) { dbus_message_iter_get_basic(&value, &val); gateway = g_strdup(val); - DBG("gateway %s", gateway); + DBG("Gateway %s", gateway); } dbus_message_iter_next(&dict); } + if (index < 0) + goto out; + + context->index = index; - if (info->ipv4_method == CONNMAN_IPCONFIG_METHOD_FIXED) { - connman_ipaddress_set_ipv4(&info->ipv4_address, address, - netmask, gateway); + if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED) + goto out; + + context->ipv4_address = connman_ipaddress_alloc(AF_INET); + if (!context->ipv4_address) { + context->index = -1; + goto out; } - /* deactive, oFono send NULL inteface before deactive signal */ - if (interface == NULL) - connman_network_set_index(info->network, -1); + connman_ipaddress_set_ipv4(context->ipv4_address, address, + netmask, gateway); + + g_free(context->ipv4_nameservers); + context->ipv4_nameservers = nameservers; + +out: + if (context->ipv4_nameservers != nameservers) + g_free(nameservers); g_free(address); g_free(netmask); g_free(gateway); } -static void update_ipv6_settings(DBusMessageIter *array, - struct network_info *info) +static void extract_ipv6_settings(DBusMessageIter *array, + struct network_context *context) { DBusMessageIter dict; char *address = NULL, *gateway = NULL; - unsigned char prefix_length; + unsigned char prefix_length = 0; + char *nameservers = NULL; const char *interface = NULL; + int index = -1; - DBG("network %p", info->network); + connman_ipaddress_free(context->ipv6_address); + context->ipv6_address = NULL; if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(array, &dict); - info->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED; - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key, *val; @@ -786,592 +956,494 @@ static void update_ipv6_settings(DBusMessageIter *array, dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); - DBG("key %s", key); - dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Interface") == TRUE) { - int index; - + if (g_str_equal(key, "Interface")) { dbus_message_iter_get_basic(&value, &interface); - DBG("interface %s", interface); + DBG("Interface %s", interface); index = connman_inet_ifindex(interface); - if (index >= 0) { - connman_network_set_index(info->network, index); - } else { - connman_error("Can not find interface %s", - interface); - break; - } - } else if (g_str_equal(key, "Address") == TRUE) { + + DBG("index %d", index); + } else if (g_str_equal(key, "Address")) { dbus_message_iter_get_basic(&value, &val); address = g_strdup(val); - DBG("address %s", address); - } else if (g_str_equal(key, "PrefixLength") == TRUE) { + DBG("Address %s", address); + } else if (g_str_equal(key, "PrefixLength")) { dbus_message_iter_get_basic(&value, &prefix_length); DBG("prefix length %d", prefix_length); - } else if (g_str_equal(key, "DomainNameServers") == TRUE) { + } else if (g_str_equal(key, "DomainNameServers")) { + nameservers = extract_nameservers(&value); - get_dns(&value, info); - } else if (g_str_equal(key, "Gateway") == TRUE) { + DBG("Nameservers %s", nameservers); + } else if (g_str_equal(key, "Gateway")) { dbus_message_iter_get_basic(&value, &val); gateway = g_strdup(val); - DBG("gateway %s", gateway); + DBG("Gateway %s", gateway); } dbus_message_iter_next(&dict); } - connman_ipaddress_set_ipv6(&info->ipv6_address, address, - prefix_length, gateway); + if (index < 0) + goto out; - /* deactive, oFono send NULL inteface before deactive signal */ - if (interface == NULL) - connman_network_set_index(info->network, -1); - - g_free(address); - g_free(gateway); -} + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_AUTO; -static int add_network(struct connman_device *device, - const char *path, DBusMessageIter *dict) -{ - struct modem_data *modem = connman_device_get_data(device); - struct connman_network *network; - struct network_info *info; - char *ident; - dbus_bool_t active = FALSE; + context->ipv6_address = + connman_ipaddress_alloc(AF_INET6); + if (!context->ipv6_address) + goto out; - DBG("modem %p device %p path %s", modem, device, path); + context->index = index; + connman_ipaddress_set_ipv6(context->ipv6_address, address, + prefix_length, gateway); - ident = get_ident(path); + g_free(context->ipv6_nameservers); + context->ipv6_nameservers = nameservers; - network = connman_device_get_network(device, ident); - if (network != NULL) - return -EALREADY; +out: + if (context->ipv6_nameservers != nameservers) + g_free(nameservers); - info = g_hash_table_lookup(network_hash, path); - if (info != NULL) { - DBG("path %p already exists with device %p", path, - connman_network_get_device(info->network)); - if (connman_network_get_device(info->network)) - return -EALREADY; - g_hash_table_remove(network_hash, path); - } + g_free(address); + g_free(gateway); +} - network = connman_network_create(ident, CONNMAN_NETWORK_TYPE_CELLULAR); - if (network == NULL) - return -ENOMEM; +static bool ready_to_create_device(struct modem_data *modem) +{ + /* + * There are three different modem types which behave slightly + * different: + * - GSM modems will expose the SIM interface then the + * CM interface. + * - CDMA modems will expose CM first and sometime later + * a unique serial number. + * + * This functions tests if we have the necessary information gathered + * before we are able to create a device. + */ - info = g_try_new0(struct network_info, 1); - if (info == NULL) { - connman_network_unref(network); - return -ENOMEM; - } + if (modem->device) + return false; - connman_ipaddress_clear(&info->ipv4_address); - connman_ipaddress_clear(&info->ipv6_address); - info->network = network; + if (modem->imsi || modem->serial) + return true; - connman_network_set_string(network, "Path", path); + return false; +} - create_service(network); +static void create_device(struct modem_data *modem) +{ + struct connman_device *device; + char *ident = NULL; - g_hash_table_insert(network_hash, g_strdup(path), info); + DBG("%s", modem->path); - connman_network_set_available(network, TRUE); - connman_network_set_index(network, -1); + if (modem->imsi) + ident = modem->imsi; + else if (modem->serial) + ident = modem->serial; - if (modem->operator) - connman_network_set_name(network, modem->operator); + if (!connman_dbus_validate_ident(ident)) + ident = connman_dbus_encode_string(ident); else - connman_network_set_name(network, ""); + ident = g_strdup(ident); - if (modem->has_strength) - connman_network_set_strength(network, modem->strength); + device = connman_device_create("ofono", CONNMAN_DEVICE_TYPE_CELLULAR); + if (!device) + goto out; - connman_network_set_roaming(network, modem->roaming); + DBG("device %p", device); - while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *key; + connman_device_set_ident(device, ident); - dbus_message_iter_recurse(dict, &entry); - dbus_message_iter_get_basic(&entry, &key); + connman_device_set_string(device, "Path", modem->path); - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); + connman_device_set_data(device, modem); - if (g_str_equal(key, "Type")) { - const char *type; + if (connman_device_register(device) < 0) { + connman_error("Failed to register cellular device"); + connman_device_unref(device); + goto out; + } - dbus_message_iter_get_basic(&value, &type); - if (g_strcmp0(type, "internet") != 0) { - DBG("path %p type %s", path, type); - g_hash_table_remove(network_hash, path); - return -EIO; - } - } else if (g_str_equal(key, "Settings")) - update_ipv4_settings(&value, info); - else if (g_str_equal(key, "IPv6.Settings")) - update_ipv6_settings(&value, info); - else if (g_str_equal(key, "Active") == TRUE) - dbus_message_iter_get_basic(&value, &active); + modem->device = device; - dbus_message_iter_next(dict); - } + connman_device_set_powered(modem->device, modem->online); +out: + g_free(ident); +} - if (connman_device_add_network(device, network) != 0) { - g_hash_table_remove(network_hash, path); - return -EIO; - } +static void destroy_device(struct modem_data *modem) +{ + DBG("%s", modem->path); - /* Connect only if requested to do so */ - if (active && connman_network_get_connecting(network) == TRUE) - set_connected(info, active); + connman_device_set_powered(modem->device, false); - return 0; + connman_device_unregister(modem->device); + connman_device_unref(modem->device); + + modem->device = NULL; } -static void check_networks_reply(DBusPendingCall *call, void *user_data) +static void add_network(struct modem_data *modem, + struct network_context *context) { - char *path = user_data; - struct modem_data *modem; - DBusMessage *reply; - DBusMessageIter array, entry, value, properties; + const char *group; - DBG("path %s", path); + DBG("%s", modem->path); - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return; - if (modem->device == NULL) + if (context->network) return; - reply = dbus_pending_call_steal_reply(call); + context->network = connman_network_create(context->path, + CONNMAN_NETWORK_TYPE_CELLULAR); + if (!context->network) + return; - if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) - goto done; + DBG("network %p", context->network); - dbus_message_iter_init(reply, &array); + connman_network_set_data(context->network, modem); - dbus_message_iter_recurse(&array, &entry); + connman_network_set_string(context->network, "Path", + context->path); - while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) { - char const *network_path; + if (modem->name) + connman_network_set_name(context->network, modem->name); + else + connman_network_set_name(context->network, ""); - dbus_message_iter_recurse(&entry, &value); - dbus_message_iter_get_basic(&value, &network_path); + connman_network_set_strength(context->network, modem->strength); - dbus_message_iter_next(&value); - dbus_message_iter_recurse(&value, &properties); + group = get_ident(context->path); + connman_network_set_group(context->network, group); - add_network(modem->device, network_path, &properties); + connman_network_set_bool(context->network, "Roaming", + modem->roaming); - dbus_message_iter_next(&entry); + if (connman_device_add_network(modem->device, context->network) < 0) { + connman_network_unref(context->network); + context->network = NULL; + return; } - -done: - dbus_message_unref(reply); - - dbus_pending_call_unref(call); } -static void check_networks(struct modem_data *modem) +static void remove_network(struct modem_data *modem, + struct network_context *context) { - char const *path = modem->path; + DBG("%s", modem->path); + + if (!context || !context->network) + return; - DBG("modem %p path %s", modem, path); + DBG("network %p", context->network); - call_ofono(path, OFONO_GPRS_INTERFACE, "GetContexts", - check_networks_reply, g_strdup(path), g_free, - DBUS_TYPE_INVALID); + if (modem->device) + connman_device_remove_network(modem->device, context->network); + connman_network_unref(context->network); + context->network = NULL; } -static void modem_clear_network_errors(struct modem_data *modem) +static int set_context_ipconfig(struct network_context *context, + const char *protocol) { - struct connman_device *device = modem->device; - GHashTableIter i; - gpointer value; + DBG("context %p protocol %s", context, protocol); - if (device == NULL) - return; + if (!context || !protocol) + return -EINVAL; - g_hash_table_iter_init(&i, network_hash); + if (g_str_equal(protocol, "ip")) { + if (context->ipv4_method == CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; - while (g_hash_table_iter_next(&i, NULL, &value)) { - struct network_info *info = value; + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_OFF; - if (connman_network_get_device(info->network) == device) - connman_network_clear_error(info->network); - } -} + connman_ipaddress_free(context->ipv6_address); + context->ipv6_address = NULL; -static void modem_operator_name_changed(struct modem_data *modem, - char const *name) -{ - struct connman_device *device = modem->device; - GHashTableIter i; - gpointer value; + } else if (g_str_equal(protocol, "ipv6")) { + if (context->ipv6_method == CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; - if (device == NULL) - return; + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_OFF; - if (modem->operator != NULL) - g_free(modem->operator); - modem->operator = g_strdup(name); + connman_ipaddress_free(context->ipv4_address); + context->ipv4_address = NULL; - for (g_hash_table_iter_init(&i, network_hash); - g_hash_table_iter_next(&i, NULL, &value);) { - struct network_info *info = value; + } else if (g_str_equal(protocol, "dual")) { + if (context->ipv4_method == CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; - if (connman_network_get_device(info->network) == device) { - connman_network_set_name(info->network, name); - connman_network_update(info->network); - } + if (context->ipv6_method == CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; } + + DBG("ipv4 method %d ipv6 method %d", context->ipv4_method, + context->ipv6_method); + + return 0; } -static void modem_strength_changed(struct modem_data *modem, uint8_t strength) +static int add_cm_context(struct modem_data *modem, const char *context_path, + DBusMessageIter *dict) { - struct connman_device *device = modem->device; - GHashTableIter i; - gpointer value; + const char *context_type = NULL; + struct network_context *context = NULL; + dbus_bool_t active = FALSE; + const char *ip_protocol = NULL; - modem->strength = strength; - modem->has_strength = TRUE; + DBG("%s context path %s", modem->path, context_path); - if (device == NULL) - return; + context = network_context_alloc(context_path); + if (!context) + return -ENOMEM; - for (g_hash_table_iter_init(&i, network_hash); - g_hash_table_iter_next(&i, NULL, &value);) { - struct network_info *info = value; + while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; - if (connman_network_get_device(info->network) == device) { - connman_network_set_strength(info->network, strength); - connman_network_update(info->network); - } - } -} + dbus_message_iter_recurse(dict, &entry); + dbus_message_iter_get_basic(&entry, &key); -static void modem_roaming_changed(struct modem_data *modem, - char const *status) -{ - struct connman_device *device = modem->device; - connman_bool_t roaming = FALSE; - connman_bool_t registered = FALSE; - connman_bool_t was_roaming = modem->roaming; - GHashTableIter i; - gpointer value; + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(status, "roaming")) - roaming = TRUE; - else if (g_str_equal(status, "registered")) - registered = TRUE; + if (g_str_equal(key, "Type")) { + dbus_message_iter_get_basic(&value, &context_type); - registered = registered || roaming; + DBG("%s context %s type %s", modem->path, + context_path, context_type); + } else if (g_str_equal(key, "Settings")) { + DBG("%s Settings", modem->path); - if (modem->roaming == roaming && modem->registered == registered) - return; + extract_ipv4_settings(&value, context); + } else if (g_str_equal(key, "IPv6.Settings")) { + DBG("%s IPv6.Settings", modem->path); - modem->registered = registered; - modem->roaming = roaming; + extract_ipv6_settings(&value, context); + } else if (g_str_equal(key, "Active")) { + dbus_message_iter_get_basic(&value, &active); - if (roaming == was_roaming) - return; + DBG("%s Active %d", modem->path, active); + } else if (g_str_equal(key, "AccessPointName")) { + const char *apn; - if (device == NULL) - return; + dbus_message_iter_get_basic(&value, &apn); + if (apn && strlen(apn) > 0) + context->valid_apn = true; + else + context->valid_apn = false; - g_hash_table_iter_init(&i, network_hash); + DBG("%s AccessPointName '%s'", modem->path, apn); + } else if (g_str_equal(key, "Protocol") && + dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING ) { - while (g_hash_table_iter_next(&i, NULL, &value)) { - struct network_info *info = value; + dbus_message_iter_get_basic(&value, &ip_protocol); - if (connman_network_get_device(info->network) == device) { - connman_network_set_roaming(info->network, roaming); - connman_network_update(info->network); + DBG("%s Protocol %s", modem->path, ip_protocol); } + + dbus_message_iter_next(dict); } -} -static void modem_registration_removed(struct modem_data *modem) -{ - modem->registered = FALSE; - modem->roaming = FALSE; -} + if (g_strcmp0(context_type, "internet") != 0) { + network_context_unref(context); + return -EINVAL; + } -static void modem_registration_changed(struct modem_data *modem, - DBusMessageIter *entry) -{ - DBusMessageIter iter; - const char *key; - int type; - connman_uint8_t strength; - char const *name, *status, *mcc_s; + if (ip_protocol) + set_context_ipconfig(context, ip_protocol); - dbus_message_iter_get_basic(entry, &key); + context->active = active; - DBG("key %s", key); + modem->context_list = g_slist_prepend(modem->context_list, context); + g_hash_table_replace(context_hash, g_strdup(context_path), modem); - dbus_message_iter_next(entry); + if (context->valid_apn && modem->attached && + has_interface(modem->interfaces, OFONO_API_NETREG)) + add_network(modem, context); - dbus_message_iter_recurse(entry, &iter); + return 0; +} - type = dbus_message_iter_get_arg_type(&iter); - if (type != DBUS_TYPE_BYTE && type != DBUS_TYPE_STRING) +static void remove_cm_context(struct modem_data *modem, + struct network_context *context) +{ + if (!modem->context_list) + return; + if (!context) return; - if (g_str_equal(key, "Name") && type == DBUS_TYPE_STRING) { - dbus_message_iter_get_basic(&iter, &name); - modem_operator_name_changed(modem, name); - } else if (g_str_equal(key, "Strength") && type == DBUS_TYPE_BYTE) { - dbus_message_iter_get_basic(&iter, &strength); - modem_strength_changed(modem, strength); - } else if (g_str_equal(key, "Status") && type == DBUS_TYPE_STRING) { - dbus_message_iter_get_basic(&iter, &status); - modem_roaming_changed(modem, status); - } else if (g_str_equal(key, "MobileCountryCode") && - type == DBUS_TYPE_STRING) { - int mcc; - char *alpha2; - - dbus_message_iter_get_basic(&iter, &mcc_s); - - mcc = atoi(mcc_s); - if (mcc > 799) - return; + g_hash_table_remove(context_hash, context->path); - alpha2 = mcc_country_codes[mcc - 200]; - connman_technology_set_regdom(alpha2); - } + if (context->network) + remove_network(modem, context); + modem->context_list = g_slist_remove(modem->context_list, context); + network_context_unref(context); + context = NULL; } -static gboolean reg_changed(DBusConnection *connection, - DBusMessage *message, void *user_data) +static void remove_all_contexts(struct modem_data *modem) { - const char *path = dbus_message_get_path(message); - struct modem_data *modem; - DBusMessageIter iter; + GSList *list = NULL; - DBG("path %s", path); + DBG(""); - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return TRUE; + if (modem->context_list == NULL) + return; - if (dbus_message_iter_init(message, &iter)) - modem_registration_changed(modem, &iter); + list = modem->context_list; + while (list) { + struct network_context *context = list->data; - return TRUE; -} - -static void check_registration_reply(DBusPendingCall *call, void *user_data) -{ - char const *path = user_data; - struct modem_data *modem; - DBusMessage *reply; - DBusMessageIter array, dict, entry; - - DBG("path %s", path); - - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return; - - reply = dbus_pending_call_steal_reply(call); - - if (dbus_message_iter_init(reply, &array) == FALSE) - goto done; - - if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) - goto done; + remove_cm_context(modem, context); - dbus_message_iter_recurse(&array, &dict); - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - dbus_message_iter_recurse(&dict, &entry); - modem_registration_changed(modem, &entry); - dbus_message_iter_next(&dict); + list = modem->context_list; } - -done: - dbus_message_unref(reply); - - dbus_pending_call_unref(call); -} - -static void check_registration(struct modem_data *modem) -{ - char const *path = modem->path; - - DBG("modem %p path %s", modem, path); - - call_ofono(path, OFONO_REGISTRATION_INTERFACE, GET_PROPERTIES, - check_registration_reply, g_strdup(path), g_free, - DBUS_TYPE_INVALID); + g_slist_free(modem->context_list); + modem->context_list = NULL; } -static void modem_gprs_changed(struct modem_data *modem, - DBusMessageIter *entry) +static void remove_all_networks(struct modem_data *modem) { - DBusMessageIter iter; - const char *key; - int type; - dbus_bool_t value; - - dbus_message_iter_get_basic(entry, &key); - - DBG("key %s", key); - - dbus_message_iter_next(entry); - - dbus_message_iter_recurse(entry, &iter); - - type = dbus_message_iter_get_arg_type(&iter); - - if (type != DBUS_TYPE_BOOLEAN) - return; - - dbus_message_iter_get_basic(&iter, &value); - - if (g_str_equal(key, "Attached") == TRUE) { - DBG("Attached %d", value); - - modem->attached = value; - - if (value) - modem_clear_network_errors(modem); - } else if (g_str_equal(key, "Powered") == TRUE) { - DBG("Powered %d", value); + GSList *list; - modem->powered = value; - } else if (g_str_equal(key, "RoamingAllowed") == TRUE) { - DBG("RoamingAllowed %d", value); + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - modem->roaming_allowed = value; + remove_network(modem, context); } } -static void check_gprs_reply(DBusPendingCall *call, void *user_data) +static gboolean context_changed(DBusConnection *conn, + DBusMessage *message, + void *user_data) { - char const *path = user_data; - struct modem_data *modem; - DBusMessage *reply; - DBusMessageIter array, dict, entry; + struct network_context *context = NULL; + const char *context_path = dbus_message_get_path(message); + struct modem_data *modem = NULL; + DBusMessageIter iter, value; + const char *key; - DBG("path %s", path); + DBG("context_path %s", context_path); - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return; + modem = g_hash_table_lookup(context_hash, context_path); + if (!modem) + return TRUE; - reply = dbus_pending_call_steal_reply(call); + context = get_context_with_path(modem->context_list, context_path); + if (!context) + return TRUE; - if (dbus_message_iter_init(reply, &array) == FALSE) - goto done; + if (!dbus_message_iter_init(message, &iter)) + return TRUE; - if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) - goto done; + dbus_message_iter_get_basic(&iter, &key); - dbus_message_iter_recurse(&array, &dict); - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - dbus_message_iter_recurse(&dict, &entry); - modem_gprs_changed(modem, &entry); - dbus_message_iter_next(&dict); - } + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); -done: - dbus_message_unref(reply); + /* + * 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")) { + DBG("%s Settings", modem->path); - dbus_pending_call_unref(call); + extract_ipv4_settings(&value, context); + } else if (g_str_equal(key, "IPv6.Settings")) { + DBG("%s IPv6.Settings", modem->path); - check_networks(modem); -} + extract_ipv6_settings(&value, context); + } else if (g_str_equal(key, "Active")) { + dbus_bool_t active; -static void check_gprs(struct modem_data *modem) -{ - char const *path = modem->path; + dbus_message_iter_get_basic(&value, &active); + context->active = active; - DBG("modem %p path %s", modem, path); + DBG("%s Active %d", modem->path, context->active); - call_ofono(path, OFONO_GPRS_INTERFACE, GET_PROPERTIES, - check_gprs_reply, g_strdup(path), g_free, - DBUS_TYPE_INVALID); -} + if (context->active) + set_connected(modem, context); + else + set_disconnected(context); + } else if (g_str_equal(key, "AccessPointName")) { + const char *apn; -static void add_device(const char *path, const char *imsi) -{ - struct modem_data *modem; - struct connman_device *device; + dbus_message_iter_get_basic(&value, &apn); - DBG("path %s imsi %s", path, imsi); + DBG("%s AccessPointName %s", modem->path, apn); - if (path == NULL) - return; + if (apn && strlen(apn) > 0) { + context->valid_apn = true; - if (imsi == NULL) - return; + if (context->network) + return TRUE; - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return; + if (!modem->attached) + return TRUE; - if (modem->device) { - if (!g_strcmp0(imsi, connman_device_get_ident(modem->device))) - return; + if (!has_interface(modem->interfaces, + OFONO_API_NETREG)) + return TRUE; - modem_remove_device(modem); - } + add_network(modem, context); - if (strlen(imsi) == 0) - return; + if (context->active) + set_connected(modem, context); + } else { + context->valid_apn = false; - device = connman_device_create(imsi, CONNMAN_DEVICE_TYPE_CELLULAR); - if (device == NULL) - return; + if (!context->network) + return TRUE; - connman_device_set_ident(device, imsi); + remove_network(modem, context); + } - connman_device_set_string(device, "Path", path); + } else if (g_str_equal(key, "Protocol") && + dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING ) { + const char *ip_protocol; - connman_device_set_data(device, modem); + dbus_message_iter_get_basic(&value, &ip_protocol); - if (connman_device_register(device) < 0) { - connman_device_unref(device); - return; + set_context_ipconfig(context, ip_protocol); } - modem->device = device; - - if (modem->has_reg) - check_registration(modem); - - if (modem->has_gprs) - check_gprs(modem); + return TRUE; } -static void sim_properties_reply(DBusPendingCall *call, void *user_data) +static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data) { - const char *path = user_data; - const char *imsi = NULL; + struct modem_data *modem = user_data; + DBusMessageIter array, dict, entry, value; DBusMessage *reply; - DBusMessageIter array, dict; + DBusError error; - DBG("path %s", path); + DBG("%s", modem->path); + + modem->call_get_contexts = NULL; reply = dbus_pending_call_steal_reply(call); - if (dbus_message_iter_init(reply, &array) == FALSE) + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply)) { + connman_error("%s", error.message); + dbus_error_free(&error); + goto done; + } + + if (!dbus_message_iter_init(reply, &array)) goto done; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) @@ -1379,20 +1451,17 @@ static void sim_properties_reply(DBusPendingCall *call, void *user_data) dbus_message_iter_recurse(&array, &dict); - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *key; + 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, &key); + dbus_message_iter_get_basic(&entry, &context_path); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "SubscriberIdentity")) { - dbus_message_iter_get_basic(&value, &imsi); - add_device(path, imsi); - } + if (add_cm_context(modem, context_path, &value)) + break; dbus_message_iter_next(&dict); } @@ -1403,214 +1472,259 @@ done: dbus_pending_call_unref(call); } -static void get_imsi(const char *path) +static int cm_get_contexts(struct modem_data *modem) { - DBG("path %s", path); + DBusMessage *message; - call_ofono(path, OFONO_SIM_INTERFACE, GET_PROPERTIES, - sim_properties_reply, g_strdup(path), g_free, - DBUS_TYPE_INVALID); -} + DBG("%s", modem->path); -static int gprs_change_powered(const char *path, dbus_bool_t powered) -{ - DBG("path %s powered %d", path, powered); + if (modem->call_get_contexts) + return -EBUSY; - return set_property(path, OFONO_GPRS_INTERFACE, "Powered", - DBUS_TYPE_BOOLEAN, &powered, - NULL, NULL, NULL); -} + message = dbus_message_new_method_call(OFONO_SERVICE, modem->path, + OFONO_CM_INTERFACE, GET_CONTEXTS); + if (!message) + return -ENOMEM; -static int modem_change_powered(const char *path, dbus_bool_t powered) -{ - DBG("path %s powered %d", path, powered); + if (!dbus_connection_send_with_reply(connection, message, + &modem->call_get_contexts, TIMEOUT)) { + connman_error("Failed to call GetContexts()"); + dbus_message_unref(message); + return -EINVAL; + } - return set_property(path, OFONO_MODEM_INTERFACE, "Powered", - DBUS_TYPE_BOOLEAN, &powered, - NULL, NULL, NULL); -} + if (!modem->call_get_contexts) { + 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); -static gboolean modem_has_interface(DBusMessageIter *array, - char const *interface) -{ - DBusMessageIter entry; + dbus_message_unref(message); - dbus_message_iter_recurse(array, &entry); + return -EINPROGRESS; +} - while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { - const char *element; +static gboolean cm_context_added(DBusConnection *conn, + DBusMessage *message, + void *user_data) +{ + const char *path = dbus_message_get_path(message); + char *context_path; + struct modem_data *modem; + DBusMessageIter iter, properties, dict; - dbus_message_iter_get_basic(&entry, &element); + DBG("%s", path); - if (g_strcmp0(interface, element) == 0) - return TRUE; + modem = g_hash_table_lookup(modem_hash, path); + if (!modem) + return TRUE; - dbus_message_iter_next(&entry); - } + if (!dbus_message_iter_init(message, &iter)) + return TRUE; - return FALSE; -} + dbus_message_iter_get_basic(&iter, &context_path); -static gboolean modem_has_sim(DBusMessageIter *array) -{ - return modem_has_interface(array, OFONO_SIM_INTERFACE); -} + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &properties); -static gboolean modem_has_reg(DBusMessageIter *array) -{ - return modem_has_interface(array, OFONO_REGISTRATION_INTERFACE); -} + /* Sometimes, we get an array instead of dict */ + if (dbus_message_iter_get_arg_type(&properties) == DBUS_TYPE_ARRAY) { + /* Must recurse again */ + dbus_message_iter_recurse(&properties, &dict); + if (add_cm_context(modem, context_path, &dict) != 0) + return TRUE; + } + if (add_cm_context(modem, context_path, &properties) != 0) + return TRUE; -static gboolean modem_has_gprs(DBusMessageIter *array) -{ - return modem_has_interface(array, OFONO_GPRS_INTERFACE); + return TRUE; } -static void add_modem(const char *path, DBusMessageIter *prop) +static gboolean cm_context_removed(DBusConnection *conn, + DBusMessage *message, + void *user_data) { + const char *path = dbus_message_get_path(message); + const char *context_path; struct modem_data *modem; - dbus_bool_t powered = FALSE; - dbus_bool_t online = FALSE; - dbus_bool_t locked = FALSE; - gboolean has_sim = FALSE; - gboolean has_reg = FALSE; - gboolean has_gprs = FALSE; + struct network_context *context; + DBusMessageIter iter; - modem = g_hash_table_lookup(modem_hash, path); + DBG("context path %s", path); - if (modem != NULL) - return; + if (!dbus_message_iter_init(message, &iter)) + return TRUE; - modem = g_try_new0(struct modem_data, 1); - if (modem == NULL) - return; + dbus_message_iter_get_basic(&iter, &context_path); - modem->path = g_strdup(path); - modem->device = NULL; - modem->available = TRUE; + modem = g_hash_table_lookup(context_hash, context_path); + if (!modem) + return TRUE; - g_hash_table_insert(modem_hash, g_strdup(path), modem); + context = get_context_with_path(modem->context_list, context_path); + remove_cm_context(modem, context); - while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *key; + return TRUE; +} - dbus_message_iter_recurse(prop, &entry); - dbus_message_iter_get_basic(&entry, &key); +static void netreg_update_name(struct modem_data *modem, + DBusMessageIter* value) +{ + char *name; + GSList *list; - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); + dbus_message_iter_get_basic(value, &name); - if (g_str_equal(key, "Powered") == TRUE) - dbus_message_iter_get_basic(&value, &powered); - else if (g_str_equal(key, "Lockdown") == TRUE) - dbus_message_iter_get_basic(&value, &locked); - else if (g_str_equal(key, "Interfaces") == TRUE) { - has_sim = modem_has_sim(&value); - has_reg = modem_has_reg(&value); - has_gprs = modem_has_gprs(&value); - } + DBG("%s Name %s", modem->path, name); - dbus_message_iter_next(prop); - } + g_free(modem->name); + modem->name = g_strdup(name); - if (locked) + if (!modem->context_list) return; - if (!powered) - modem_change_powered(path, TRUE); - - modem->has_sim = has_sim; - modem->has_reg = has_reg; - modem->has_gprs = has_gprs; - - update_modem_online(modem, online); + /* For all the context */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - if (has_sim) - get_imsi(path); + if (context->network) { + connman_network_set_name(context->network, modem->name); + connman_network_update(context->network); + } + } } -static void manager_modems_reply(DBusPendingCall *call, void *user_data) +static void netreg_update_strength(struct modem_data *modem, + DBusMessageIter *value) { - DBusMessage *reply; - DBusError error; - DBusMessageIter array, dict; + GSList *list; - DBG(""); + dbus_message_iter_get_basic(value, &modem->strength); - reply = dbus_pending_call_steal_reply(call); + DBG("%s Strength %d", modem->path, modem->strength); - if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) - goto done; + if (!modem->context_list) + return; - dbus_error_init(&error); + /* + * GSM: + * We don't have 2 signal notifications we always report the strength + * signal. data_strength is always equal to 0. + * + * CDMA: + * In the case we have a data_strength signal (from 1xEVDO network) + * we don't need to update the value with strength signal (from 1xCDMA) + * because the modem is registered to 1xEVDO network for data call. + * In case we have no data_strength signal (not registered to 1xEVDO + * network), we must report the strength signal (registered to 1xCDMA + * network e.g slow mode). + */ + if (modem->data_strength != 0) + return; - if (dbus_set_error_from_message(&error, reply)) { - connman_error("ModemManager.GetModems() %s %s", - error.name, error.message); - dbus_error_free(&error); - goto done; + /* For all the context */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; + + if (context->network) { + connman_network_set_strength(context->network, + modem->strength); + connman_network_update(context->network); + } } +} - if (dbus_message_iter_init(reply, &array) == FALSE) - goto done; +/* Retrieve 1xEVDO Data Strength signal */ +static void netreg_update_datastrength(struct modem_data *modem, + DBusMessageIter *value) +{ + GSList *list; - dbus_message_iter_recurse(&array, &dict); + dbus_message_iter_get_basic(value, &modem->data_strength); - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) { - DBusMessageIter value, properties; - const char *modem_path; + DBG("%s Data Strength %d", modem->path, modem->data_strength); - dbus_message_iter_recurse(&dict, &value); - dbus_message_iter_get_basic(&value, &modem_path); + if (!modem->context_list) + return; - dbus_message_iter_next(&value); - dbus_message_iter_recurse(&value, &properties); + /* + * CDMA modem is not registered to 1xEVDO network, let + * update_signal_strength() reporting the value on the Strength signal + * notification. + */ + if (modem->data_strength == 0) + return; - /* Add modem */ - add_modem(modem_path, &properties); + /* For all the context */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - dbus_message_iter_next(&dict); + if (context->network) { + connman_network_set_strength(context->network, + modem->data_strength); + connman_network_update(context->network); + } } - -done: - dbus_message_unref(reply); - - dbus_pending_call_unref(call); } -static void ofono_connect(DBusConnection *connection, void *user_data) +static void netreg_update_status(struct modem_data *modem, + DBusMessageIter *value) { - DBG("connection %p", connection); + char *status; + bool roaming; + GSList *list; - modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, remove_modem); + dbus_message_iter_get_basic(value, &status); + + roaming = g_str_equal(status, "roaming"); + modem->registered = roaming || g_str_equal(status, "registered"); + + if (roaming == modem->roaming) + return; + + modem->roaming = roaming; + + if (!modem->context_list) + return; - network_hash = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, remove_network); + /* For all the context */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - call_ofono("/", OFONO_MANAGER_INTERFACE, GET_MODEMS, - manager_modems_reply, NULL, NULL, - DBUS_TYPE_INVALID); + if (context->network) { + connman_network_set_bool(context->network, + "Roaming", modem->roaming); + connman_network_update(context->network); + } + } } -static void ofono_disconnect(DBusConnection *connection, void *user_data) +static void netreg_update_regdom(struct modem_data *modem, + DBusMessageIter *value) { - DBG("connection %p", connection); + char *mobile_country_code; + char *alpha2; + int mcc; - if (modem_hash != NULL) { - g_hash_table_destroy(modem_hash); - modem_hash = NULL; - } + dbus_message_iter_get_basic(value, &mobile_country_code); - if (network_hash != NULL) { - g_hash_table_destroy(network_hash); - network_hash = NULL; - } + DBG("%s MobileContryCode %s", modem->path, mobile_country_code); + + + mcc = atoi(mobile_country_code); + if (mcc > 799 || mcc < 200) + return; + + alpha2 = mcc_country_codes[mcc - 200]; + if (alpha2) + connman_technology_set_regdom(alpha2); } -static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, +static gboolean netreg_changed(DBusConnection *conn, DBusMessage *message, void *user_data) { const char *path = dbus_message_get_path(message); @@ -1618,13 +1732,14 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, DBusMessageIter iter, value; const char *key; - DBG("path %s", path); - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) + if (!modem) return TRUE; - if (dbus_message_iter_init(message, &iter) == FALSE) + if (modem->ignore) + return TRUE; + + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &key); @@ -1632,347 +1747,1180 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); - if (g_str_equal(key, "Powered") == TRUE) { - dbus_bool_t powered; + if (g_str_equal(key, "Name")) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength")) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "Status")) + netreg_update_status(modem, &value); + else if (g_str_equal(key, "MobileCountryCode")) + netreg_update_regdom(modem, &value); - dbus_message_iter_get_basic(&value, &powered); - if (powered == TRUE) - return TRUE; + return TRUE; +} - modem->has_sim = FALSE; - modem->has_reg = FALSE; - modem->has_gprs = FALSE; +static void netreg_properties_reply(struct modem_data *modem, + DBusMessageIter *dict) +{ + GSList *list = NULL; - modem_remove_device(modem); - } else if (g_str_equal(key, "Online") == TRUE) { + 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")) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength")) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "Status")) + netreg_update_status(modem, &value); + else if (g_str_equal(key, "MobileCountryCode")) + netreg_update_regdom(modem, &value); + + dbus_message_iter_next(dict); + } + + if (!modem->context_list) { + /* + * netgreg_get_properties() was issued after we got + * cm_get_contexts_reply() where we create the + * context. Though before we got the + * netreg_properties_reply the context was removed + * again. Therefore we have to skip the network + * creation. + */ + return; + } + /* Check for all contexts if they are valids and/or actives */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; + + if (context->valid_apn) + add_network(modem, context); + if (context->active) + set_connected(modem, context); + } +} + +static int netreg_get_properties(struct modem_data *modem) +{ + return get_properties(modem->path, OFONO_NETREG_INTERFACE, + netreg_properties_reply, modem); +} + +static void add_cdma_network(struct modem_data *modem) +{ + struct network_context *context = NULL; + /* Be sure that device is created before adding CDMA network */ + if (!modem->device) + return; + + /* + * CDMA modems don't need contexts for data call, however the current + * add_network() logic needs one, so we create one to proceed. + */ + if (!modem->context_list) { + context = network_context_alloc(modem->path); + modem->context_list = g_slist_prepend(modem->context_list, + context); + } else + context = modem->context_list->data; + + if (!modem->name) + modem->name = g_strdup("CDMA Network"); + + add_network(modem, context); + + if (modem->cdma_cm_powered) + set_connected(modem, context); +} + +static gboolean cdma_netreg_changed(DBusConnection *conn, + DBusMessage *message, + void *user_data) +{ + const char *path = dbus_message_get_path(message); + struct modem_data *modem; + DBusMessageIter iter, value; + const char *key; + + DBG(""); + + modem = g_hash_table_lookup(modem_hash, path); + if (!modem) + return TRUE; + + if (modem->ignore) + return TRUE; + + if (!dbus_message_iter_init(message, &iter)) + 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")) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength")) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "DataStrength")) + netreg_update_datastrength(modem, &value); + else if (g_str_equal(key, "Status")) + netreg_update_status(modem, &value); + + if (modem->registered) + add_cdma_network(modem); + else + remove_all_networks(modem); + + return TRUE; +} + +static void cdma_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")) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength")) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "DataStrength")) + netreg_update_datastrength(modem, &value); + else if (g_str_equal(key, "Status")) + netreg_update_status(modem, &value); + + dbus_message_iter_next(dict); + } + + if (modem->registered) + add_cdma_network(modem); + else + remove_all_networks(modem); +} + +static int cdma_netreg_get_properties(struct modem_data *modem) +{ + return get_properties(modem->path, OFONO_CDMA_NETREG_INTERFACE, + cdma_netreg_properties_reply, modem); +} + +static void cm_update_attached(struct modem_data *modem, + DBusMessageIter *value) +{ + dbus_bool_t attached; + + dbus_message_iter_get_basic(value, &attached); + modem->attached = attached; + + DBG("%s Attached %d", modem->path, modem->attached); + + if (!modem->attached) { + remove_all_networks(modem); + return; + } + + if (!has_interface(modem->interfaces, OFONO_API_NETREG)) + return; + + netreg_get_properties(modem); +} + +static void cm_update_powered(struct modem_data *modem, + DBusMessageIter *value) +{ + dbus_bool_t cm_powered; + + dbus_message_iter_get_basic(value, &cm_powered); + modem->cm_powered = cm_powered; + + DBG("%s ConnnectionManager Powered %d", modem->path, + modem->cm_powered); + + if (modem->cm_powered) + return; + + cm_set_powered(modem, TRUE); +} + +static gboolean cm_changed(DBusConnection *conn, 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) + return TRUE; + + if (modem->ignore) + return TRUE; + + if (!dbus_message_iter_init(message, &iter)) + 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")) + cm_update_attached(modem, &value); + else if (g_str_equal(key, "Powered")) + cm_update_powered(modem, &value); + + return TRUE; +} + +static void cdma_cm_update_powered(struct modem_data *modem, + DBusMessageIter *value) +{ + struct network_context *context = NULL; + dbus_bool_t cdma_cm_powered; + + dbus_message_iter_get_basic(value, &cdma_cm_powered); + modem->cdma_cm_powered = cdma_cm_powered; + + DBG("%s CDMA cm Powered %d", modem->path, modem->cdma_cm_powered); + + if (!modem->context_list) + return; + + /* In case of CDMA, there is only one context */ + context = modem->context_list->data; + if (modem->cdma_cm_powered) + set_connected(modem, context); + else + set_disconnected(context); +} + +static void cdma_cm_update_settings(struct modem_data *modem, + DBusMessageIter *value) +{ + DBG("%s Settings", modem->path); + + extract_ipv4_settings(value, modem->context_list->data); +} + +static gboolean cdma_cm_changed(DBusConnection *conn, + 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) + return TRUE; + + if (modem->online && !modem->context_list) + cdma_netreg_get_properties(modem); + + if (!dbus_message_iter_init(message, &iter)) + 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")) + cdma_cm_update_powered(modem, &value); + if (g_str_equal(key, "Settings")) + cdma_cm_update_settings(modem, &value); + + 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")) + cm_update_attached(modem, &value); + else if (g_str_equal(key, "Powered")) + cm_update_powered(modem, &value); + + 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 cdma_cm_properties_reply(struct modem_data *modem, + DBusMessageIter *dict) +{ + DBG("%s", modem->path); + + if (modem->online) + cdma_netreg_get_properties(modem); + + 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, "Powered")) + cdma_cm_update_powered(modem, &value); + if (g_str_equal(key, "Settings")) + cdma_cm_update_settings(modem, &value); + + dbus_message_iter_next(dict); + } +} + +static int cdma_cm_get_properties(struct modem_data *modem) +{ + return get_properties(modem->path, OFONO_CDMA_CM_INTERFACE, + cdma_cm_properties_reply, modem); +} + +static void sim_update_imsi(struct modem_data *modem, + DBusMessageIter *value) +{ + char *imsi; + + dbus_message_iter_get_basic(value, &imsi); + + DBG("%s imsi %s", modem->path, imsi); + + g_free(modem->imsi); + modem->imsi = g_strdup(imsi); +} + +static gboolean sim_changed(DBusConnection *conn, 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) + return TRUE; + + if (modem->ignore) + return TRUE; + + if (!dbus_message_iter_init(message, &iter)) + 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")) { + sim_update_imsi(modem, &value); + + if (!ready_to_create_device(modem)) + return TRUE; + + /* + * This is a GSM modem. Create the device and + * register it at the core. Enabling (setting + * it online is done through the + * modem_enable() callback. + */ + 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")) { + sim_update_imsi(modem, &value); + + if (!ready_to_create_device(modem)) + return; + + /* + * This is a GSM modem. Create the device and + * register it at the core. Enabling (setting + * it online is done through the + * modem_enable() callback. + */ + create_device(modem); + + if (!modem->online) + return; + + /* + * The modem is already online and we have the CM interface. + * There will be no interface update and therefore our + * state machine will not go to next step. We have to + * trigger it from here. + */ + if (has_interface(modem->interfaces, OFONO_API_CM)) { + 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 bool api_added(uint8_t old_iface, uint8_t new_iface, + enum ofono_api api) +{ + if (!has_interface(old_iface, api) && + has_interface(new_iface, api)) { + DBG("%s added", api2string(api)); + return true; + } + + return false; +} + +static bool api_removed(uint8_t old_iface, uint8_t new_iface, + enum ofono_api api) +{ + if (has_interface(old_iface, api) && + !has_interface(new_iface, api)) { + DBG("%s removed", api2string(api)); + return true; + } + + return false; +} + +static void modem_update_interfaces(struct modem_data *modem, + uint8_t old_ifaces, + uint8_t new_ifaces) +{ + DBG("%s", modem->path); + + if (api_added(old_ifaces, new_ifaces, OFONO_API_SIM)) { + if (!modem->imsi && + !modem->set_powered) { + /* + * Only use do GetProperties() when + * device has not been powered up. + */ + sim_get_properties(modem); + } + } + + if (api_added(old_ifaces, new_ifaces, OFONO_API_CM)) { + if (modem->device) { + cm_get_properties(modem); + cm_get_contexts(modem); + } + } + + if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_CM)) { + if (ready_to_create_device(modem)) { + create_device(modem); + if (modem->registered) + add_cdma_network(modem); + } + + if (modem->device) + cdma_cm_get_properties(modem); + } + + if (api_added(old_ifaces, new_ifaces, OFONO_API_NETREG)) { + if (modem->attached) + netreg_get_properties(modem); + } + + if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG)) + cdma_netreg_get_properties(modem); + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_CM)) { + if (modem->call_get_contexts) { + DBG("cancelling pending GetContexts call"); + dbus_pending_call_cancel(modem->call_get_contexts); + dbus_pending_call_unref(modem->call_get_contexts); + modem->call_get_contexts = NULL; + } + remove_all_contexts(modem); + } + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_CM)) + remove_all_contexts(modem); + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_NETREG)) + remove_all_networks(modem); + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG)) + remove_all_networks(modem); +} + +static gboolean modem_changed(DBusConnection *conn, 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) + return TRUE; + + if (modem->ignore) + return TRUE; + + if (!dbus_message_iter_init(message, &iter)) + 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")) { + dbus_bool_t powered; + + dbus_message_iter_get_basic(&value, &powered); + modem->powered = powered; + + DBG("%s Powered %d", modem->path, modem->powered); + + /* Set the powered according to the value */ + modem_set_powered(modem, powered); + } else if (g_str_equal(key, "Online")) { dbus_bool_t online; dbus_message_iter_get_basic(&value, &online); + modem->online = online; + + DBG("%s Online %d", modem->path, modem->online); + + if (!modem->device) + return TRUE; + + connman_device_set_powered(modem->device, modem->online); + } else if (g_str_equal(key, "Interfaces")) { + uint8_t interfaces; + + interfaces = extract_interfaces(&value); + + if (interfaces == modem->interfaces) + return TRUE; + + DBG("%s Interfaces 0x%02x", modem->path, interfaces); + + modem_update_interfaces(modem, modem->interfaces, interfaces); + + modem->interfaces = interfaces; + } else if (g_str_equal(key, "Serial")) { + 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); + + if (has_interface(modem->interfaces, + OFONO_API_CDMA_CM)) { + if (ready_to_create_device(modem)) { + create_device(modem); + if (modem->registered) + add_cdma_network(modem); + } + } + } - update_modem_online(modem, online); - } else if (g_str_equal(key, "Lockdown") == TRUE) { - dbus_bool_t locked; + 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) { + /* + * 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) + 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")) { + dbus_bool_t powered; + + dbus_message_iter_get_basic(&value, &powered); + modem->powered = powered; + + DBG("%s Powered %d", modem->path, modem->powered); + } else if (g_str_equal(key, "Online")) { + dbus_bool_t online; + + dbus_message_iter_get_basic(&value, &online); + modem->online = online; + + DBG("%s Online %d", modem->path, modem->online); + } else if (g_str_equal(key, "Interfaces")) { + modem->interfaces = extract_interfaces(&value); + + DBG("%s Interfaces 0x%02x", modem->path, + modem->interfaces); + } else if (g_str_equal(key, "Serial")) { + char *serial; + + dbus_message_iter_get_basic(&value, &serial); + modem->serial = g_strdup(serial); + + DBG("%s Serial %s", modem->path, modem->serial); + } else if (g_str_equal(key, "Type")) { + char *type; + + dbus_message_iter_get_basic(&value, &type); + + DBG("%s Type %s", modem->path, type); + if (g_strcmp0(type, "hardware") != 0) { + DBG("%s Ignore this modem", modem->path); + modem->ignore = true; + } + } + + dbus_message_iter_next(prop); + } + + if (modem->ignore) + return; + + if (!modem->powered) { + modem_set_powered(modem, TRUE); + return; + } + + modem_update_interfaces(modem, 0, modem->interfaces); +} + +static void modem_power_down(gpointer key, gpointer value, gpointer user_data) +{ + struct modem_data *modem = value; + + DBG("%s", modem->path); + + if (modem->ignore) + return; + + modem_set_powered(modem, FALSE); +} + +static void remove_modem(gpointer data) +{ + struct modem_data *modem = data; + + DBG("%s", modem->path); + + if (modem->call_set_property) { + dbus_pending_call_cancel(modem->call_set_property); + dbus_pending_call_unref(modem->call_set_property); + modem->call_set_property = NULL; + } + + if (modem->call_get_properties) { + dbus_pending_call_cancel(modem->call_get_properties); + dbus_pending_call_unref(modem->call_get_properties); + modem->call_get_properties = NULL; + } + + if (modem->call_get_contexts) { + dbus_pending_call_cancel(modem->call_get_contexts); + dbus_pending_call_unref(modem->call_get_contexts); + modem->call_get_contexts = NULL; + } + + /* Must remove the contexts before the device */ + if (modem->context_list) + remove_all_contexts(modem); + + if (modem->device) + destroy_device(modem); + + g_free(modem->serial); + g_free(modem->name); + g_free(modem->imsi); + g_free(modem->path); + + g_free(modem); +} + +static gboolean modem_added(DBusConnection *conn, + DBusMessage *message, void *user_data) +{ + DBusMessageIter iter, properties; + const char *path; + + DBG(""); + + if (!dbus_message_iter_init(message, &iter)) + 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 *conn, + DBusMessage *message, void *user_data) +{ + DBusMessageIter iter; + const char *path; + + DBG(""); + + if (!dbus_message_iter_init(message, &iter)) + return TRUE; + + dbus_message_iter_get_basic(&iter, &path); + + g_hash_table_remove(modem_hash, path); + + return TRUE; +} - dbus_message_iter_get_basic(&value, &locked); +static void manager_get_modems_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply; + DBusError error; + DBusMessageIter array, dict; - if (!locked) - modem_change_powered(path, TRUE); + DBG(""); - } else if (g_str_equal(key, "Interfaces") == TRUE) { - gboolean has_sim = modem_has_sim(&value); - gboolean has_reg = modem_has_reg(&value); - gboolean had_reg = modem->has_reg; - gboolean has_gprs = modem_has_gprs(&value); - gboolean had_gprs = modem->has_gprs; + reply = dbus_pending_call_steal_reply(call); - modem->has_sim = has_sim; - modem->has_reg = has_reg; - modem->has_gprs = has_gprs; + dbus_error_init(&error); - if (modem->device == NULL) { - if (has_sim) - get_imsi(modem->path); - } else if (!has_sim) { - modem_remove_device(modem); - } else { - if (has_reg && !had_reg) - check_registration(modem); - else if (had_reg && !has_reg) - modem_registration_removed(modem); - - if (has_gprs && !had_gprs) { - gprs_change_powered(modem->path, TRUE); - check_gprs(modem); - } - } + if (dbus_set_error_from_message(&error, reply)) { + connman_error("%s", error.message); + dbus_error_free(&error); + goto done; } - return TRUE; -} + if (!dbus_message_iter_init(reply, &array)) + goto done; -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; + dbus_message_iter_recurse(&array, &dict); - DBG("path %s", path); + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) { + DBusMessageIter value, properties; + const char *path; - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return TRUE; + dbus_message_iter_recurse(&dict, &value); + dbus_message_iter_get_basic(&value, &path); - if (dbus_message_iter_init(message, &iter) == FALSE) - return TRUE; + dbus_message_iter_next(&value); + dbus_message_iter_recurse(&value, &properties); - dbus_message_iter_get_basic(&iter, &key); + add_modem(path, &properties); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &value); + dbus_message_iter_next(&dict); + } - if (g_str_equal(key, "SubscriberIdentity") == TRUE) { - char *imsi; +done: + dbus_message_unref(reply); - dbus_message_iter_get_basic(&value, &imsi); + dbus_pending_call_unref(call); +} - add_device(path, imsi); - } else if (g_str_equal(key, "Present") == TRUE) { - dbus_bool_t present; +static int manager_get_modems(void) +{ + DBusMessage *message; + DBusPendingCall *call; - dbus_message_iter_get_basic(&value, &present); + DBG(""); - if (present) - return TRUE; + message = dbus_message_new_method_call(OFONO_SERVICE, "/", + OFONO_MANAGER_INTERFACE, GET_MODEMS); + if (!message) + return -ENOMEM; - if (modem->device != NULL) - modem_remove_device(modem); + if (!dbus_connection_send_with_reply(connection, message, + &call, TIMEOUT)) { + connman_error("Failed to call GetModems()"); + dbus_message_unref(message); + return -EINVAL; + } - modem->has_gprs = FALSE; - modem->has_reg = FALSE; + if (!call) { + connman_error("D-Bus connection not available"); + dbus_message_unref(message); + return -EINVAL; } - return TRUE; + dbus_pending_call_set_notify(call, manager_get_modems_reply, + NULL, NULL); + + dbus_message_unref(message); + + return -EINPROGRESS; } -static gboolean gprs_changed(DBusConnection *connection, DBusMessage *message, - void *user_data) +static void ofono_connect(DBusConnection *conn, void *user_data) { - const char *path = dbus_message_get_path(message); - struct modem_data *modem; - DBusMessageIter iter; - - DBG("path %s", path); + DBG(""); - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return TRUE; + modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, remove_modem); + if (!modem_hash) + return; - if (dbus_message_iter_init(message, &iter)) - modem_gprs_changed(modem, &iter); + context_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + if (!context_hash) { + g_hash_table_destroy(modem_hash); + return; + } - return TRUE; + manager_get_modems(); } -static gboolean context_added(DBusConnection *connection, - DBusMessage *message, void *user_data) +static void ofono_disconnect(DBusConnection *conn, void *user_data) { - const char *path = dbus_message_get_path(message); - const char *network_path; - struct modem_data *modem; - DBusMessageIter iter, properties; + DBG(""); - DBG("path %s", path); + if (!modem_hash || !context_hash) + return; - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL || modem->device == NULL) - return TRUE; + g_hash_table_destroy(modem_hash); + modem_hash = NULL; - if (dbus_message_iter_init(message, &iter) == FALSE) - return TRUE; + g_hash_table_destroy(context_hash); + context_hash = NULL; +} - dbus_message_iter_get_basic(&iter, &network_path); +static int network_probe(struct connman_network *network) +{ + struct modem_data *modem = connman_network_get_data(network); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &properties); + DBG("%s network %p", modem->path, network); - add_network(modem->device, network_path, &properties); + return 0; +} - return TRUE; +static void network_remove(struct connman_network *network) +{ + struct modem_data *modem = connman_network_get_data(network); + + DBG("%s network %p", modem->path, network); } -static gboolean context_removed(DBusConnection *connection, - DBusMessage *message, void *user_data) +static int network_connect(struct connman_network *network) { - const char *path = dbus_message_get_path(message); - const char *network_path; - struct modem_data *modem; - DBusMessageIter iter; + struct network_context *context; + struct modem_data *modem = connman_network_get_data(network); - DBG("path %s", path); + DBG("%s network %p", modem->path, network); - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL || modem->device == NULL) - return TRUE; + if (!g_hash_table_lookup(modem_hash, modem->path)) + return -ENODEV; - if (dbus_message_iter_init(message, &iter) == FALSE) - return TRUE; + context = get_context_with_network(modem->context_list, network); + if (!context) + return -ENODEV; - dbus_message_iter_get_basic(&iter, &network_path); + if (has_interface(modem->interfaces, OFONO_API_CM)) + return context_set_active(modem, context, TRUE); + else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM)) + return cdma_cm_set_powered(modem, TRUE); - g_hash_table_remove(network_hash, network_path); - return TRUE; + connman_error("Connection manager interface not available"); + + return -ENOSYS; } -static gboolean modem_added(DBusConnection *connection, - DBusMessage *message, void *user_data) +static int network_disconnect(struct connman_network *network) { - DBusMessageIter iter, properties; - const char *modem_path; + struct network_context *context; + struct modem_data *modem = connman_network_get_data(network); - DBG(""); + DBG("%s network %p", modem->path, network); - if (dbus_message_iter_init(message, &iter) == FALSE) - return TRUE; + if (!g_hash_table_lookup(modem_hash, modem->path)) + return -ENODEV; - dbus_message_iter_get_basic(&iter, &modem_path); + context = get_context_with_network(modem->context_list, network); + if (!context) + return -ENODEV; - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &properties); + if (has_interface(modem->interfaces, OFONO_API_CM)) + return context_set_active(modem, context, FALSE); + else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM)) + return cdma_cm_set_powered(modem, FALSE); - add_modem(modem_path, &properties); + connman_error("Connection manager interface not available"); - return TRUE; + return -ENOSYS; } -static gboolean modem_removed(DBusConnection *connection, - DBusMessage *message, void *user_data) -{ - DBusMessageIter iter; - const char *modem_path; +static struct connman_network_driver network_driver = { + .name = "cellular", + .type = CONNMAN_NETWORK_TYPE_CELLULAR, + .probe = network_probe, + .remove = network_remove, + .connect = network_connect, + .disconnect = network_disconnect, +}; - DBG(""); +static int modem_probe(struct connman_device *device) +{ + struct modem_data *modem = connman_device_get_data(device); - if (dbus_message_iter_init(message, &iter) == FALSE) - return TRUE; + DBG("%s device %p", modem->path, device); - dbus_message_iter_get_basic(&iter, &modem_path); + return 0; +} - g_hash_table_remove(modem_hash, modem_path); +static void modem_remove(struct connman_device *device) +{ + struct modem_data *modem = connman_device_get_data(device); - return TRUE; + DBG("%s device %p", modem->path, device); } -static gboolean context_changed(DBusConnection *connection, - DBusMessage *message, void *user_data) +static int modem_enable(struct connman_device *device) { - const char *path = dbus_message_get_path(message); - struct network_info *info; - DBusMessageIter iter, value; - const char *key; - - DBG("path %s", path); + struct modem_data *modem = connman_device_get_data(device); - info = g_hash_table_lookup(network_hash, path); - if (info == NULL) - return TRUE; + DBG("%s device %p", modem->path, device); - if (!pending_network_is_available(info->network)) { - g_hash_table_remove(network_hash, path); - return TRUE; - } + if (modem->online) + return 0; - if (dbus_message_iter_init(message, &iter) == FALSE) - return TRUE; + return modem_set_online(modem, TRUE); +} - dbus_message_iter_get_basic(&iter, &key); +static int modem_disable(struct connman_device *device) +{ + struct modem_data *modem = connman_device_get_data(device); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &value); + DBG("%s device %p", modem->path, device); - DBG("key %s", key); + if (!modem->online) + return 0; - if (g_str_equal(key, "Settings") == TRUE) - update_ipv4_settings(&value, info); - else if (g_str_equal(key, "IPv6.Settings")) - update_ipv6_settings(&value, info); - else if (g_str_equal(key, "Active") == TRUE) { - dbus_bool_t active; + return modem_set_online(modem, FALSE); +} - dbus_message_iter_get_basic(&value, &active); +static struct connman_device_driver modem_driver = { + .name = "modem", + .type = CONNMAN_DEVICE_TYPE_CELLULAR, + .probe = modem_probe, + .remove = modem_remove, + .enable = modem_enable, + .disable = modem_disable, +}; - if (active == FALSE) - set_connected(info, active); - else if (connman_network_get_connecting(info->network) == TRUE) - /* Connect only if requested to do so */ - set_connected(info, active); - } +static int tech_probe(struct connman_technology *technology) +{ + return 0; +} - return TRUE; +static void tech_remove(struct connman_technology *technology) +{ } +static struct connman_technology_driver tech_driver = { + .name = "cellular", + .type = CONNMAN_SERVICE_TYPE_CELLULAR, + .probe = tech_probe, + .remove = tech_remove, +}; + static guint watch; -static guint reg_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 gprs_watch; static guint context_added_watch; static guint context_removed_watch; -static guint modem_watch; -static guint modem_added_watch; -static guint modem_removed_watch; +static guint netreg_watch; static guint context_watch; +static guint cdma_cm_watch; +static guint cdma_netreg_watch; static int ofono_init(void) { int err; + DBG(""); + connection = connman_dbus_get_connection(); - if (connection == NULL) + if (!connection) return -EIO; - watch = g_dbus_add_service_watch(connection, OFONO_SERVICE, - ofono_connect, ofono_disconnect, NULL, NULL); + watch = g_dbus_add_service_watch(connection, + OFONO_SERVICE, ofono_connect, + ofono_disconnect, NULL, NULL); + + modem_added_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, + NULL, OFONO_MANAGER_INTERFACE, + MODEM_ADDED, + modem_added, + NULL, NULL); + + modem_removed_watch = g_dbus_add_signal_watch(connection, + OFONO_SERVICE, NULL, + OFONO_MANAGER_INTERFACE, + MODEM_REMOVED, + modem_removed, + NULL, NULL); + + modem_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, + OFONO_MODEM_INTERFACE, + PROPERTY_CHANGED, + modem_changed, + NULL, NULL); - reg_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_REGISTRATION_INTERFACE, + cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, + OFONO_CM_INTERFACE, PROPERTY_CHANGED, - reg_changed, + cm_changed, NULL, NULL); - gprs_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_GPRS_INTERFACE, + sim_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, + OFONO_SIM_INTERFACE, PROPERTY_CHANGED, - gprs_changed, + sim_changed, NULL, NULL); - context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_GPRS_INTERFACE, + context_added_watch = g_dbus_add_signal_watch(connection, + OFONO_SERVICE, NULL, + OFONO_CM_INTERFACE, CONTEXT_ADDED, - context_added, + cm_context_added, NULL, NULL); - context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_GPRS_INTERFACE, + context_removed_watch = g_dbus_add_signal_watch(connection, + OFONO_SERVICE, NULL, + OFONO_CM_INTERFACE, CONTEXT_REMOVED, - context_removed, + cm_context_removed, NULL, NULL); - modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_MODEM_INTERFACE, + context_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, + NULL, OFONO_CONTEXT_INTERFACE, PROPERTY_CHANGED, - modem_changed, + context_changed, NULL, NULL); - sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_SIM_INTERFACE, + netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, + OFONO_NETREG_INTERFACE, PROPERTY_CHANGED, - sim_changed, - NULL, NULL); - - modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_MANAGER_INTERFACE, - MODEM_ADDED, - modem_added, + netreg_changed, NULL, NULL); - modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_MANAGER_INTERFACE, - MODEM_REMOVED, - modem_removed, + cdma_cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, + NULL, OFONO_CDMA_CM_INTERFACE, + PROPERTY_CHANGED, + cdma_cm_changed, NULL, NULL); - context_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_CONTEXT_INTERFACE, + cdma_netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, + NULL, OFONO_CDMA_NETREG_INTERFACE, PROPERTY_CHANGED, - context_changed, + cdma_netreg_changed, NULL, NULL); - if (watch == 0 || gprs_watch == 0 || context_added_watch == 0 || - context_removed_watch == 0 || modem_watch == 0 || - reg_watch == 0 || sim_watch == 0 || - modem_added_watch == 0 || modem_removed_watch == 0 || - context_watch == 0) { + + 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 || + cdma_cm_watch == 0 || cdma_netreg_watch == 0) { err = -EIO; goto remove; } @@ -1987,20 +2935,28 @@ static int ofono_init(void) goto remove; } + err = connman_technology_driver_register(&tech_driver); + if (err < 0) { + connman_device_driver_unregister(&modem_driver); + connman_network_driver_unregister(&network_driver); + goto remove; + } + return 0; remove: - g_dbus_remove_watch(connection, watch); - g_dbus_remove_watch(connection, sim_watch); - g_dbus_remove_watch(connection, reg_watch); - g_dbus_remove_watch(connection, gprs_watch); - g_dbus_remove_watch(connection, context_added_watch); + g_dbus_remove_watch(connection, cdma_netreg_watch); + g_dbus_remove_watch(connection, cdma_cm_watch); + 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, context_watch); - + g_dbus_remove_watch(connection, modem_added_watch); + g_dbus_remove_watch(connection, watch); dbus_connection_unref(connection); return err; @@ -2008,22 +2964,44 @@ remove: static void ofono_exit(void) { - g_dbus_remove_watch(connection, watch); - g_dbus_remove_watch(connection, sim_watch); - g_dbus_remove_watch(connection, reg_watch); - g_dbus_remove_watch(connection, gprs_watch); - g_dbus_remove_watch(connection, context_added_watch); - g_dbus_remove_watch(connection, context_removed_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, context_watch); + DBG(""); + + if (modem_hash) { + /* + * We should probably 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; + } - ofono_disconnect(connection, NULL); + if (context_hash) { + g_hash_table_destroy(context_hash); + context_hash = NULL; + } + connman_technology_driver_unregister(&tech_driver); connman_device_driver_unregister(&modem_driver); connman_network_driver_unregister(&network_driver); + g_dbus_remove_watch(connection, cdma_netreg_watch); + g_dbus_remove_watch(connection, cdma_cm_watch); + 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); }