X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fofono.c;h=36873d5a0c82551d97c29ae2f341e07f197d42e3;hb=refs%2Ftags%2Fupstream%2F1.38;hp=0e26d40e60bf26707548ea6e9d7bb2ca6d8a1675;hpb=4ccccee21fae749ce7bdcdc154ba79c45317e93f;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/ofono.c b/plugins/ofono.c index 0e26d40..36873d5 100644 --- 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 @@ -38,8 +39,9 @@ #include #include #include +#include -#define uninitialized_var(x) x = x +#include "mcc.h" #define OFONO_SERVICE "org.ofono" @@ -49,6 +51,8 @@ #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" @@ -67,8 +71,60 @@ 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; @@ -77,6 +133,7 @@ static GHashTable *context_hash; struct network_context { char *path; int index; + struct connman_network *network; enum connman_ipconfig_method ipv4_method; struct connman_ipaddress *ipv4_address; @@ -85,34 +142,35 @@ struct network_context { 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; - struct connman_network *network; - struct network_context *context; + GSList *context_list; /* Modem Interface */ char *serial; - connman_bool_t powered; - connman_bool_t online; + bool powered; + bool online; uint8_t interfaces; + bool ignore; - connman_bool_t set_powered; - connman_bool_t set_online; - - /* ConnectionManager Interface */ - connman_bool_t attached; - connman_bool_t cm_powered; + bool set_powered; - connman_bool_t set_cm_powered; + /* CDMA ConnectionManager Interface */ + bool cdma_cm_powered; - /* ConnectionContext Interface */ - connman_bool_t active; - connman_bool_t set_active; + /* ConnectionManager Interface */ + bool attached; + bool cm_powered; /* SimManager Interface */ char *imsi; @@ -120,6 +178,9 @@ struct modem_data { /* Netreg Interface */ char *name; uint8_t strength; + uint8_t data_strength; /* 1xEVDO signal strength */ + bool registered; + bool roaming; /* pending calls */ DBusPendingCall *call_set_property; @@ -127,6 +188,24 @@ struct modem_data { DBusPendingCall *call_get_contexts; }; +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) { char *pos; @@ -135,18 +214,52 @@ static char *get_ident(const char *path) return NULL; pos = strrchr(path, '/'); - if (pos == NULL) + if (!pos) return NULL; return pos + 1; } +static struct network_context *get_context_with_path(GSList *context_list, + const gchar *path) +{ + 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 struct network_context *get_context_with_network(GSList *context_list, + const struct connman_network *network) +{ + GSList *list; + + DBG("network %p", network); + + for (list = context_list; list; list = list->next) { + struct network_context *context = list->data; + + if (context->network == network) + return context; + } + + return NULL; +} + static struct network_context *network_context_alloc(const char *path) { struct network_context *context; context = g_try_new0(struct network_context, 1); - if (context == NULL) + if (!context) return NULL; context->path = g_strdup(path); @@ -160,11 +273,25 @@ static struct network_context *network_context_alloc(const char *path) context->ipv6_address = NULL; context->ipv6_nameservers = NULL; + context->refcount = 1; + return context; } -static void network_context_free(struct network_context *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); @@ -173,76 +300,104 @@ static void network_context_free(struct network_context *context) connman_ipaddress_free(context->ipv6_address); g_free(context->ipv6_nameservers); - free(context); + g_free(context); } -static void set_connected(struct modem_data *modem) +static void set_connected(struct modem_data *modem, + struct network_context *context) { - connman_bool_t setip = FALSE; + struct connman_service *service; + bool setip = false; + enum connman_ipconfig_method method; + char *nameservers; + int index; DBG("%s", modem->path); - connman_network_set_index(modem->network, modem->context->index); + 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; + } + + service = connman_service_lookup_from_network(context->network); + if (!service) + return; - switch (modem->context->ipv4_method) { - case CONNMAN_IPCONFIG_METHOD_UNKNOWN: - case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_MANUAL: - case CONNMAN_IPCONFIG_METHOD_AUTO: - break; + connman_service_create_ip4config(service, index); + connman_network_set_ipv4_method(context->network, method); - case CONNMAN_IPCONFIG_METHOD_FIXED: - connman_network_set_ipv4_method(modem->network, - modem->context->ipv4_method); - connman_network_set_ipaddress(modem->network, - modem->context->ipv4_address); - connman_network_set_nameservers(modem->network, - modem->context->ipv4_nameservers); - setip = TRUE; - break; + if (method == CONNMAN_IPCONFIG_METHOD_FIXED || + method == CONNMAN_IPCONFIG_METHOD_DHCP) { + setip = true; + } - case CONNMAN_IPCONFIG_METHOD_DHCP: - connman_network_set_ipv4_method(modem->network, - modem->context->ipv4_method); - setip = TRUE; - break; + if (method == CONNMAN_IPCONFIG_METHOD_FIXED) { + connman_network_set_ipaddress(context->network, + context->ipv4_address); } - switch (modem->context->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; + method = context->ipv6_method; + connman_service_create_ip6config(service, index); + connman_network_set_ipv6_method(context->network, method); + + if (method == CONNMAN_IPCONFIG_METHOD_AUTO) { + setip = true; + } - case CONNMAN_IPCONFIG_METHOD_FIXED: - connman_network_set_ipv6_method(modem->network, - modem->context->ipv6_method); - connman_network_set_ipaddress(modem->network, - modem->context->ipv6_address); - setip = TRUE; - break; + /* 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 == TRUE) - connman_network_set_connected(modem->network, TRUE); + if (setip) { + connman_network_set_index(context->network, index); + connman_network_set_connected(context->network, true); + } } -static void set_disconnected(struct modem_data *modem) +static void set_disconnected(struct network_context *context) { - DBG("%s", modem->path); + 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; - connman_network_set_connected(modem->network, FALSE); + 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, - connman_bool_t success); + 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; @@ -250,12 +405,22 @@ struct property_info { 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; - connman_bool_t success = TRUE; + bool success = true; DBG("%s path %s %s.%s", info->modem->path, info->path, info->interface, info->property); @@ -271,11 +436,12 @@ static void set_property_reply(DBusPendingCall *call, void *user_data) info->path, info->interface, info->property, error.name, error.message); dbus_error_free(&error); - success = FALSE; + success = false; } - if (info->set_property_cb != NULL) - (*info->set_property_cb)(info->modem, success); + if (info->set_property_cb) + (*info->set_property_cb)(info->modem, info->context, + success); dbus_message_unref(reply); @@ -283,6 +449,7 @@ static void set_property_reply(DBusPendingCall *call, void *user_data) } 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, set_property_cb notify) @@ -293,47 +460,53 @@ static int set_property(struct modem_data *modem, DBG("%s path %s %s.%s", modem->path, path, interface, property); - if (modem->call_set_property != NULL) { - connman_error("Pending SetProperty"); - return -EBUSY; + 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_iter_init_append(message, &iter); connman_dbus_property_append_basic(&iter, property, type, value); - if (dbus_connection_send_with_reply(connection, message, - &modem->call_set_property, TIMEOUT) == FALSE) { + 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 (modem->call_set_property == NULL) { + if (!modem->call_set_property) { connman_error("D-Bus connection not available"); dbus_message_unref(message); return -EINVAL; } info = g_try_new0(struct property_info, 1); - if (info == NULL) { + if (!info) { dbus_message_unref(message); return -ENOMEM; } 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, g_free); + set_property_reply, info, free_property_info); dbus_message_unref(message); @@ -364,7 +537,7 @@ static void get_properties_reply(DBusPendingCall *call, void *user_data) goto done; } - if (dbus_message_iter_init(reply, &array) == FALSE) + if (!dbus_message_iter_init(reply, &array)) goto done; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) @@ -372,7 +545,7 @@ static void get_properties_reply(DBusPendingCall *call, void *user_data) dbus_message_iter_recurse(&array, &dict); - if (info->get_properties_cb != NULL) + if (info->get_properties_cb) (*info->get_properties_cb)(info->modem, &dict); done: @@ -391,31 +564,31 @@ static int get_properties(const char *path, const char *interface, DBG("%s path %s %s", modem->path, path, interface); - if (modem->call_get_properties != NULL) { + if (modem->call_get_properties) { connman_error("Pending GetProperties"); return -EBUSY; } message = dbus_message_new_method_call(OFONO_SERVICE, path, interface, GET_PROPERTIES); - if (message == NULL) + if (!message) return -ENOMEM; - if (dbus_connection_send_with_reply(connection, message, - &modem->call_get_properties, TIMEOUT) == FALSE) { + 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 (modem->call_get_properties == NULL) { + if (!modem->call_get_properties) { connman_error("D-Bus connection not available"); dbus_message_unref(message); return -EINVAL; } info = g_try_new0(struct property_info, 1); - if (info == NULL) { + if (!info) { dbus_message_unref(message); return -ENOMEM; } @@ -434,14 +607,14 @@ static int get_properties(const char *path, const char *interface, } static void context_set_active_reply(struct modem_data *modem, - connman_bool_t success) + struct network_context *context, bool success) { - DBG("%s", modem->path); + DBG("%s", context->path); - if (success == TRUE) { + if (success) { /* * Don't handle do anything on success here. oFono will send - * the change via PropertyChanged singal. + * the change via PropertyChanged signal. */ return; } @@ -453,132 +626,152 @@ static void context_set_active_reply(struct modem_data *modem, * cycle the modem in such cases? */ - connman_network_set_error(modem->network, + 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; + } + + connman_network_set_error(context->network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); } -static int context_set_active(struct modem_data *modem) +static int context_set_active(struct modem_data *modem, + struct network_context *context, + dbus_bool_t active) { - dbus_bool_t active = TRUE; + int err; - DBG("%s", modem->path); + DBG("%s active %d", modem->path, active); - return set_property(modem, modem->context->path, + err = set_property(modem, context, context->path, OFONO_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &active, context_set_active_reply); -} - -static int context_set_inactive(struct modem_data *modem) -{ - dbus_bool_t active = FALSE; - int err; - - DBG("%s", modem->path); - err = set_property(modem, modem->context->path, - OFONO_CONTEXT_INTERFACE, - "Active", DBUS_TYPE_BOOLEAN, - &active, - NULL); - if (err == -EINPROGRESS) + if (!active && err == -EINPROGRESS) return 0; return err; } -static void modem_set_online_reply(struct modem_data *modem, - connman_bool_t success) +static void cdma_cm_set_powered_reply(struct modem_data *modem, + struct network_context *context, bool success) { - DBG("%s", modem->path); + DBG("%s", context->path); - if (success == TRUE) { + if (success) { /* * Don't handle do anything on success here. oFono will send - * the change via PropertyChanged singal. + * the change via PropertyChanged signal. */ return; } - modem->set_online = FALSE; -} - -static int modem_set_online(struct modem_data *modem) -{ - DBG("%s", modem->path); - - modem->set_online = TRUE; - - return set_property(modem, modem->path, - OFONO_MODEM_INTERFACE, - "Online", DBUS_TYPE_BOOLEAN, - &modem->set_online, - modem_set_online_reply); -} - -static void cm_set_powered_reply(struct modem_data *modem, - connman_bool_t success) -{ - DBG("%s", modem->path); + /* + * 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 (success == TRUE) { + if (!context->network) { /* - * Don't handle do anything on success here. oFono will send - * the change via PropertyChanged singal. + * In the case where we power down the device + * we don't wait for the reply, therefore the network + * might already be gone. */ return; } - modem->set_cm_powered = FALSE; + connman_network_set_error(context->network, + CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); } -static int cm_set_powered(struct modem_data *modem) +static int cdma_cm_set_powered(struct modem_data *modem, dbus_bool_t powered) { - DBG("%s", modem->path); + int err; + struct network_context *context = NULL; - modem->set_cm_powered = TRUE; + if (!modem->context_list) + return -1; - return set_property(modem, modem->path, - OFONO_CM_INTERFACE, + DBG("%s powered %d", modem->path, powered); + + /* 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, - &modem->set_cm_powered, - cm_set_powered_reply); + &powered, + cdma_cm_set_powered_reply); + + if (!powered && err == -EINPROGRESS) + return 0; + + return err; } -static int modem_set_powered(struct modem_data *modem) +static int modem_set_online(struct modem_data *modem, dbus_bool_t online) { - DBG("%s", modem->path); - - modem->set_powered = TRUE; + DBG("%s online %d", modem->path, online); - return set_property(modem, modem->path, + return set_property(modem, NULL, modem->path, OFONO_MODEM_INTERFACE, + "Online", DBUS_TYPE_BOOLEAN, + &online, + NULL); +} + +static int cm_set_powered(struct modem_data *modem, dbus_bool_t powered) +{ + int err; + + DBG("%s powered %d", modem->path, powered); + + err = set_property(modem, NULL, modem->path, + OFONO_CM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, - &modem->set_powered, + &powered, NULL); + + if (!powered && err == -EINPROGRESS) + return 0; + + return err; } -static int modem_set_unpowered(struct modem_data *modem) +static int modem_set_powered(struct modem_data *modem, dbus_bool_t powered) { - DBG("%s", modem->path); + int err; + + DBG("%s powered %d", modem->path, powered); - modem->set_powered = FALSE; + modem->set_powered = powered; - return set_property(modem, modem->path, + err = set_property(modem, NULL, modem->path, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, - &modem->set_powered, + &powered, NULL); + + if (!powered && err == -EINPROGRESS) + return 0; + + return err; } -static connman_bool_t has_interface(uint8_t interfaces, +static bool has_interface(uint8_t interfaces, enum ofono_api api) { if ((interfaces & api) == api) - return TRUE; + return true; - return FALSE; + return false; } static uint8_t extract_interfaces(DBusMessageIter *array) @@ -593,12 +786,16 @@ static uint8_t extract_interfaces(DBusMessageIter *array) dbus_message_iter_get_basic(&entry, &name); - if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE) + if (g_str_equal(name, OFONO_SIM_INTERFACE)) interfaces |= OFONO_API_SIM; - else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE) + else if (g_str_equal(name, OFONO_NETREG_INTERFACE)) interfaces |= OFONO_API_NETREG; - else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE) + 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; dbus_message_iter_next(&entry); } @@ -619,7 +816,7 @@ static char *extract_nameservers(DBusMessageIter *array) dbus_message_iter_get_basic(&entry, &nameserver); - if (nameservers == NULL) { + if (!nameservers) { nameservers = g_strdup(nameserver); } else { tmp = nameservers; @@ -642,6 +839,9 @@ static void extract_ipv4_settings(DBusMessageIter *array, const char *interface = NULL; int index = -1; + connman_ipaddress_free(context->ipv4_address); + context->ipv4_address = NULL; + if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return; @@ -657,7 +857,7 @@ static void extract_ipv4_settings(DBusMessageIter *array, dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Interface") == TRUE) { + if (g_str_equal(key, "Interface")) { dbus_message_iter_get_basic(&value, &interface); DBG("Interface %s", interface); @@ -665,34 +865,33 @@ static void extract_ipv4_settings(DBusMessageIter *array, index = connman_inet_ifindex(interface); DBG("index %d", index); - } else if (g_str_equal(key, "Method") == TRUE) { + } else if (g_str_equal(key, "Method")) { dbus_message_iter_get_basic(&value, &val); DBG("Method %s", val); - if (g_strcmp0(val, "static") == 0) { + if (g_strcmp0(val, "static") == 0) context->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED; - } else if (g_strcmp0(val, "dhcp") == 0) { + else if (g_strcmp0(val, "dhcp") == 0) context->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) { + } 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) { + } else if (g_str_equal(key, "DomainNameServers")) { nameservers = extract_nameservers(&value); DBG("Nameservers %s", nameservers); - } else if (g_str_equal(key, "Gateway") == TRUE) { + } else if (g_str_equal(key, "Gateway")) { dbus_message_iter_get_basic(&value, &val); gateway = g_strdup(val); @@ -706,17 +905,21 @@ static void extract_ipv4_settings(DBusMessageIter *array, if (index < 0) goto out; + context->index = index; + if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED) goto out; context->ipv4_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4); - if (context->ipv4_address == NULL) + if (!context->ipv4_address) { + context->index = -1; goto out; + } - context->index = index; connman_ipaddress_set_ipv4(context->ipv4_address, address, netmask, gateway); + g_free(context->ipv4_nameservers); context->ipv4_nameservers = nameservers; out: @@ -733,11 +936,14 @@ static void extract_ipv6_settings(DBusMessageIter *array, { 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; + connman_ipaddress_free(context->ipv6_address); + context->ipv6_address = NULL; + if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return; @@ -753,7 +959,7 @@ static void extract_ipv6_settings(DBusMessageIter *array, dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Interface") == TRUE) { + if (g_str_equal(key, "Interface")) { dbus_message_iter_get_basic(&value, &interface); DBG("Interface %s", interface); @@ -761,21 +967,21 @@ static void extract_ipv6_settings(DBusMessageIter *array, index = connman_inet_ifindex(interface); DBG("index %d", index); - } 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, "PrefixLength") == TRUE) { + } 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); DBG("Nameservers %s", nameservers); - } else if (g_str_equal(key, "Gateway") == TRUE) { + } else if (g_str_equal(key, "Gateway")) { dbus_message_iter_get_basic(&value, &val); gateway = g_strdup(val); @@ -789,17 +995,18 @@ static void extract_ipv6_settings(DBusMessageIter *array, if (index < 0) goto out; - context->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED; + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_AUTO; context->ipv6_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6); - if (context->ipv6_address == NULL) + if (!context->ipv6_address) goto out; context->index = index; connman_ipaddress_set_ipv6(context->ipv6_address, address, prefix_length, gateway); + g_free(context->ipv6_nameservers); context->ipv6_nameservers = nameservers; out: @@ -810,15 +1017,13 @@ out: g_free(gateway); } -static connman_bool_t ready_to_create_device(struct modem_data *modem) +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. - * - DUN modems will expose first a unique serial number (BDADDR) - * and then the CM interface. * - CDMA modems will expose CM first and sometime later * a unique serial number. * @@ -826,34 +1031,34 @@ static connman_bool_t ready_to_create_device(struct modem_data *modem) * before we are able to create a device. */ - if (modem->device != NULL) - return FALSE; + if (modem->device) + return false; - if (modem->imsi != NULL || modem->serial != NULL) - return TRUE; + if (modem->imsi || modem->serial) + return true; - return FALSE; + return false; } static void create_device(struct modem_data *modem) { struct connman_device *device; - char *uninitialized_var(ident); + char *ident = NULL; DBG("%s", modem->path); - if (modem->imsi != NULL) + if (modem->imsi) ident = modem->imsi; - else if (modem->serial != NULL) + else if (modem->serial) ident = modem->serial; - if (connman_dbus_validate_ident(ident) == FALSE) + if (!connman_dbus_validate_ident(ident)) ident = connman_dbus_encode_string(ident); else ident = g_strdup(ident); - device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_CELLULAR); - if (device == NULL) + device = connman_device_create("ofono", CONNMAN_DEVICE_TYPE_CELLULAR); + if (!device) goto out; DBG("device %p", device); @@ -872,6 +1077,7 @@ static void create_device(struct modem_data *modem) modem->device = device; + connman_device_set_powered(modem->device, modem->online); out: g_free(ident); } @@ -880,13 +1086,7 @@ static void destroy_device(struct modem_data *modem) { DBG("%s", modem->path); - connman_device_set_powered(modem->device, FALSE); - - if (modem->network != NULL) { - connman_device_remove_network(modem->device, modem->network); - connman_network_unref(modem->network); - modem->network = NULL; - } + connman_device_set_powered(modem->device, false); connman_device_unregister(modem->device); connman_device_unref(modem->device); @@ -894,67 +1094,116 @@ static void destroy_device(struct modem_data *modem) modem->device = NULL; } -static void add_network(struct modem_data *modem) +static void add_network(struct modem_data *modem, + struct network_context *context) { const char *group; DBG("%s", modem->path); - if (modem->network != NULL) + if (context->network) return; - modem->network = connman_network_create(modem->context->path, - CONNMAN_NETWORK_TYPE_CELLULAR); - if (modem->network == NULL) + context->network = connman_network_create(context->path, + CONNMAN_NETWORK_TYPE_CELLULAR); + if (!context->network) return; - DBG("network %p", modem->network); + DBG("network %p", context->network); - connman_network_set_data(modem->network, modem); + connman_network_set_data(context->network, modem); - connman_network_set_string(modem->network, "Path", - modem->context->path); + connman_network_set_string(context->network, "Path", + context->path); - connman_network_set_index(modem->network, modem->context->index); - - if (modem->name != NULL) - connman_network_set_name(modem->network, modem->name); + if (modem->name) + connman_network_set_name(context->network, modem->name); else - connman_network_set_name(modem->network, ""); + connman_network_set_name(context->network, ""); + + connman_network_set_strength(context->network, modem->strength); - connman_network_set_strength(modem->network, modem->strength); + group = get_ident(context->path); + connman_network_set_group(context->network, group); - group = get_ident(modem->context->path); - connman_network_set_group(modem->network, group); + connman_network_set_bool(context->network, "Roaming", + modem->roaming); + + if (connman_device_add_network(modem->device, context->network) < 0) { + connman_network_unref(context->network); + context->network = NULL; + return; + } +} - connman_network_set_available(modem->network, TRUE); +static void remove_network(struct modem_data *modem, + struct network_context *context) +{ + DBG("%s", modem->path); - if (connman_device_add_network(modem->device, modem->network) < 0) { - connman_network_unref(modem->network); - modem->network = NULL; + if (!context || !context->network) return; + + DBG("network %p", context->network); + + if (modem->device) + connman_device_remove_network(modem->device, context->network); + connman_network_unref(context->network); + context->network = NULL; +} + +static int set_context_ipconfig(struct network_context *context, + const char *protocol) +{ + DBG("context %p protocol %s", context, protocol); + + if (!context || !protocol) + return -EINVAL; + + if (g_str_equal(protocol, "ip")) { + if (context->ipv4_method == CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_OFF; + + connman_ipaddress_free(context->ipv6_address); + context->ipv6_address = NULL; + + } else if (g_str_equal(protocol, "ipv6")) { + if (context->ipv6_method == CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_OFF; + + connman_ipaddress_free(context->ipv4_address); + context->ipv4_address = NULL; + + } else if (g_str_equal(protocol, "dual")) { + if (context->ipv4_method == CONNMAN_IPCONFIG_METHOD_OFF) + context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + + 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 int add_cm_context(struct modem_data *modem, const char *context_path, DBusMessageIter *dict) { - const char *context_type; + const char *context_type = NULL; struct network_context *context = NULL; - connman_bool_t active = FALSE; + dbus_bool_t active = FALSE; + const char *ip_protocol = NULL; DBG("%s context path %s", modem->path, context_path); - if (modem->context != NULL) { - /* - * We have already assigned a context to this modem - * and we do only support one Internet context. - */ - return -EALREADY; - } - context = network_context_alloc(context_path); - if (context == NULL) + if (!context) return -ENOMEM; while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { @@ -967,57 +1216,119 @@ static int add_cm_context(struct modem_data *modem, const char *context_path, dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Type") == TRUE) { + if (g_str_equal(key, "Type")) { dbus_message_iter_get_basic(&value, &context_type); DBG("%s context %s type %s", modem->path, context_path, context_type); - } else if (g_str_equal(key, "Settings") == TRUE) { + } else if (g_str_equal(key, "Settings")) { DBG("%s Settings", modem->path); extract_ipv4_settings(&value, context); - } else if (g_str_equal(key, "IPv6.Settings") == TRUE) { + } else if (g_str_equal(key, "IPv6.Settings")) { DBG("%s IPv6.Settings", modem->path); extract_ipv6_settings(&value, context); - } else if (g_str_equal(key, "Active") == TRUE) { + } else if (g_str_equal(key, "Active")) { dbus_message_iter_get_basic(&value, &active); DBG("%s Active %d", modem->path, active); + } else if (g_str_equal(key, "AccessPointName")) { + const char *apn; + + dbus_message_iter_get_basic(&value, &apn); + if (apn && strlen(apn) > 0) + context->valid_apn = true; + else + context->valid_apn = false; + + DBG("%s AccessPointName '%s'", modem->path, apn); + } else if (g_str_equal(key, "Protocol") && + dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING ) { + + dbus_message_iter_get_basic(&value, &ip_protocol); + + DBG("%s Protocol %s", modem->path, ip_protocol); } dbus_message_iter_next(dict); } if (g_strcmp0(context_type, "internet") != 0) { - network_context_free(context); + network_context_unref(context); return -EINVAL; } - modem->context = context; - modem->active = active; + if (ip_protocol) + set_context_ipconfig(context, ip_protocol); + + context->active = active; + modem->context_list = g_slist_prepend(modem->context_list, context); g_hash_table_replace(context_hash, g_strdup(context_path), modem); + if (context->valid_apn && modem->attached && + has_interface(modem->interfaces, OFONO_API_NETREG)) + add_network(modem, context); + return 0; } static void remove_cm_context(struct modem_data *modem, - const char *context_path) + struct network_context *context) +{ + if (!modem->context_list) + return; + if (!context) + return; + + g_hash_table_remove(context_hash, context->path); + + if (context->network) + remove_network(modem, context); + modem->context_list = g_slist_remove(modem->context_list, context); + + network_context_unref(context); + context = NULL; +} + +static void remove_all_contexts(struct modem_data *modem) { - if (modem->context == NULL) + GSList *list = NULL; + + DBG(""); + + if (modem->context_list == NULL) return; - g_hash_table_remove(context_hash, context_path); + list = modem->context_list; + while (list) { + struct network_context *context = list->data; + + remove_cm_context(modem, context); + + list = modem->context_list; + } + g_slist_free(modem->context_list); + modem->context_list = NULL; +} + +static void remove_all_networks(struct modem_data *modem) +{ + GSList *list; + + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - network_context_free(modem->context); - modem->context = NULL; + remove_network(modem, context); + } } -static gboolean context_changed(DBusConnection *connection, +static gboolean context_changed(DBusConnection *conn, DBusMessage *message, void *user_data) { + struct network_context *context = NULL; const char *context_path = dbus_message_get_path(message); struct modem_data *modem = NULL; DBusMessageIter iter, value; @@ -1026,10 +1337,14 @@ static gboolean context_changed(DBusConnection *connection, DBG("context_path %s", context_path); modem = g_hash_table_lookup(context_hash, context_path); - if (modem == NULL) + if (!modem) + return TRUE; + + context = get_context_with_path(modem->context_list, context_path); + if (!context) return TRUE; - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &key); @@ -1042,23 +1357,66 @@ static gboolean context_changed(DBusConnection *connection, * Active. Settings will always be send before Active = True. * That means we don't have to order here. */ - if (g_str_equal(key, "Settings") == TRUE) { + if (g_str_equal(key, "Settings")) { DBG("%s Settings", modem->path); - extract_ipv4_settings(&value, modem->context); - } else if (g_str_equal(key, "IPv6.Settings") == TRUE) { + extract_ipv4_settings(&value, context); + } else if (g_str_equal(key, "IPv6.Settings")) { DBG("%s IPv6.Settings", modem->path); - extract_ipv6_settings(&value, modem->context); - } else if (g_str_equal(key, "Active") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->active); + extract_ipv6_settings(&value, context); + } else if (g_str_equal(key, "Active")) { + dbus_bool_t active; - DBG("%s Active %d", modem->path, modem->active); + dbus_message_iter_get_basic(&value, &active); + context->active = active; - if (modem->active == TRUE) - set_connected(modem); + DBG("%s Active %d", modem->path, context->active); + + if (context->active) + set_connected(modem, context); else - set_disconnected(modem); + set_disconnected(context); + } else if (g_str_equal(key, "AccessPointName")) { + const char *apn; + + dbus_message_iter_get_basic(&value, &apn); + + DBG("%s AccessPointName %s", modem->path, apn); + + if (apn && strlen(apn) > 0) { + context->valid_apn = true; + + if (context->network) + return TRUE; + + if (!modem->attached) + return TRUE; + + if (!has_interface(modem->interfaces, + OFONO_API_NETREG)) + return TRUE; + + add_network(modem, context); + + if (context->active) + set_connected(modem, context); + } else { + context->valid_apn = false; + + if (!context->network) + return TRUE; + + remove_network(modem, context); + } + + } else if (g_str_equal(key, "Protocol") && + dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING ) { + const char *ip_protocol; + + dbus_message_iter_get_basic(&value, &ip_protocol); + + set_context_ipconfig(context, ip_protocol); } return TRUE; @@ -1079,13 +1437,13 @@ static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data) dbus_error_init(&error); - if (dbus_set_error_from_message(&error, reply) == TRUE) { + 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) == FALSE) + if (!dbus_message_iter_init(reply, &array)) goto done; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) @@ -1102,7 +1460,7 @@ static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data) dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (add_cm_context(modem, context_path, &value) == 0) + if (add_cm_context(modem, context_path, &value)) break; dbus_message_iter_next(&dict); @@ -1120,22 +1478,22 @@ static int cm_get_contexts(struct modem_data *modem) DBG("%s", modem->path); - if (modem->call_get_contexts != NULL) + if (modem->call_get_contexts) return -EBUSY; message = dbus_message_new_method_call(OFONO_SERVICE, modem->path, OFONO_CM_INTERFACE, GET_CONTEXTS); - if (message == NULL) + if (!message) return -ENOMEM; - if (dbus_connection_send_with_reply(connection, message, - &modem->call_get_contexts, TIMEOUT) == FALSE) { + 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; } - if (modem->call_get_contexts == NULL) { + if (!modem->call_get_contexts) { connman_error("D-Bus connection not available"); dbus_message_unref(message); return -EINVAL; @@ -1150,22 +1508,22 @@ static int cm_get_contexts(struct modem_data *modem) return -EINPROGRESS; } -static gboolean cm_context_added(DBusConnection *connection, +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; + DBusMessageIter iter, properties, dict; DBG("%s", path); - modem = g_hash_table_lookup(modem_hash, context_path); - if (modem == NULL) + modem = g_hash_table_lookup(modem_hash, path); + if (!modem) return TRUE; - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &context_path); @@ -1173,83 +1531,230 @@ static gboolean cm_context_added(DBusConnection *connection, dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &properties); + /* 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; return TRUE; } -static gboolean cm_context_removed(DBusConnection *connection, +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; + struct network_context *context; DBusMessageIter iter; DBG("context path %s", path); - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &context_path); modem = g_hash_table_lookup(context_hash, context_path); - if (modem == NULL) + if (!modem) return TRUE; - remove_cm_context(modem, context_path); + context = get_context_with_path(modem->context_list, context_path); + remove_cm_context(modem, context); return TRUE; } -static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message, - void *user_data) +static void netreg_update_name(struct modem_data *modem, + DBusMessageIter* value) { - const char *path = dbus_message_get_path(message); - struct modem_data *modem; - DBusMessageIter iter, value; - const char *key; + char *name; + GSList *list; - modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) - return TRUE; + dbus_message_iter_get_basic(value, &name); - if (dbus_message_iter_init(message, &iter) == FALSE) - return TRUE; + DBG("%s Name %s", modem->path, name); - dbus_message_iter_get_basic(&iter, &key); + g_free(modem->name); + modem->name = g_strdup(name); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &value); + if (!modem->context_list) + return; + + /* For all the context */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - if (g_str_equal(key, "Name") == TRUE) { - char *name; + if (context->network) { + connman_network_set_name(context->network, modem->name); + connman_network_update(context->network); + } + } +} - dbus_message_iter_get_basic(&value, &name); +static void netreg_update_strength(struct modem_data *modem, + DBusMessageIter *value) +{ + GSList *list; - DBG("%s Name %s", modem->path, name); + dbus_message_iter_get_basic(value, &modem->strength); - g_free(modem->name); - modem->name = g_strdup(name); + DBG("%s Strength %d", modem->path, modem->strength); - if (modem->network == NULL) - return TRUE; + if (!modem->context_list) + return; - connman_network_set_name(modem->network, modem->name); - connman_network_update(modem->network); - } else if (g_str_equal(key, "Strength") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->strength); + /* + * 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; - DBG("%s Strength %d", modem->path, modem->strength); + /* For all the context */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - if (modem->network == NULL) - return TRUE; + if (context->network) { + connman_network_set_strength(context->network, + modem->strength); + connman_network_update(context->network); + } + } +} + +/* Retrieve 1xEVDO Data Strength signal */ +static void netreg_update_datastrength(struct modem_data *modem, + DBusMessageIter *value) +{ + GSList *list; + + dbus_message_iter_get_basic(value, &modem->data_strength); + + DBG("%s Data Strength %d", modem->path, modem->data_strength); + + if (!modem->context_list) + return; + + /* + * 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; + + /* 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->data_strength); + connman_network_update(context->network); + } + } +} + +static void netreg_update_status(struct modem_data *modem, + DBusMessageIter *value) +{ + char *status; + bool roaming; + GSList *list; + + 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; + + /* For all the context */ + for (list = modem->context_list; list; list = list->next) { + struct network_context *context = list->data; - connman_network_set_strength(modem->network, modem->strength); - connman_network_update(modem->network); + if (context->network) { + connman_network_set_bool(context->network, + "Roaming", modem->roaming); + connman_network_update(context->network); + } } +} + +static void netreg_update_regdom(struct modem_data *modem, + DBusMessageIter *value) +{ + char *mobile_country_code; + char *alpha2; + int mcc; + + dbus_message_iter_get_basic(value, &mobile_country_code); + + 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 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; + + 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, "Status")) + netreg_update_status(modem, &value); + else if (g_str_equal(key, "MobileCountryCode")) + netreg_update_regdom(modem, &value); return TRUE; } @@ -1257,6 +1762,8 @@ static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message, static void netreg_properties_reply(struct modem_data *modem, DBusMessageIter *dict) { + GSList *list = NULL; + DBG("%s", modem->path); while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { @@ -1269,38 +1776,19 @@ static void netreg_properties_reply(struct modem_data *modem, dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Name") == TRUE) { - char *name; - - dbus_message_iter_get_basic(&value, &name); - - DBG("%s Name %s", modem->path, name); - - g_free(modem->name); - modem->name = g_strdup(name); - - if (modem->network != NULL) { - connman_network_set_name(modem->network, - modem->name); - connman_network_update(modem->network); - } - } else if (g_str_equal(key, "Strength") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->strength); - - DBG("%s Strength %d", modem->path, - modem->strength); - - if (modem->network != NULL) { - connman_network_set_strength(modem->network, - modem->strength); - connman_network_update(modem->network); - } - } + 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 == NULL) { + if (!modem->context_list) { /* * netgreg_get_properties() was issued after we got * cm_get_contexts_reply() where we create the @@ -1311,11 +1799,15 @@ static void netreg_properties_reply(struct modem_data *modem, */ return; } - - add_network(modem); - - if (modem->active == TRUE) - set_connected(modem); + /* 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) @@ -1324,19 +1816,52 @@ static int netreg_get_properties(struct modem_data *modem) netreg_properties_reply, modem); } -static gboolean cm_changed(DBusConnection *connection, DBusMessage *message, - void *user_data) +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 == NULL) + if (!modem) + return TRUE; + + if (modem->ignore) return TRUE; - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &key); @@ -1344,27 +1869,189 @@ static gboolean cm_changed(DBusConnection *connection, DBusMessage *message, dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); - if (g_str_equal(key, "Attached") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->attached); + 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); - DBG("%s Attached %d", modem->path, modem->attached); + return TRUE; +} - if (modem->attached == TRUE) { - if (has_interface(modem->interfaces, - OFONO_API_NETREG) == TRUE) { - netreg_get_properties(modem); - } - } - } else if (g_str_equal(key, "Powered") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->cm_powered); +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 ConnnectionManager Powered %d", modem->path, - modem->cm_powered); + DBG("%s Attached %d", modem->path, modem->attached); - if (modem->cm_powered == FALSE) - cm_set_powered(modem); + 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; } @@ -1382,51 +2069,68 @@ static void cm_properties_reply(struct modem_data *modem, DBusMessageIter *dict) dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Attached") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->attached); + if (g_str_equal(key, "Attached")) + cm_update_attached(modem, &value); + else if (g_str_equal(key, "Powered")) + cm_update_powered(modem, &value); - DBG("%s Attached %d", modem->path, - modem->attached); + dbus_message_iter_next(dict); + } +} - if (modem->attached == TRUE) { - if (has_interface(modem->interfaces, - OFONO_API_NETREG) == TRUE) { - netreg_get_properties(modem); - } - } - } else if (g_str_equal(key, "Powered") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->cm_powered); +static int cm_get_properties(struct modem_data *modem) +{ + return get_properties(modem->path, OFONO_CM_INTERFACE, + cm_properties_reply, modem); +} - DBG("%s ConnnectionManager Powered %d", modem->path, - modem->cm_powered); +static void cdma_cm_properties_reply(struct modem_data *modem, + DBusMessageIter *dict) +{ + DBG("%s", modem->path); - if (modem->cm_powered == FALSE) - cm_set_powered(modem); - } + 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 cm_get_properties(struct modem_data *modem) +static int cdma_cm_get_properties(struct modem_data *modem) { - return get_properties(modem->path, OFONO_CM_INTERFACE, - cm_properties_reply, modem); + return get_properties(modem->path, OFONO_CDMA_CM_INTERFACE, + cdma_cm_properties_reply, modem); } -static void update_sim_imsi(struct modem_data *modem, - const char *imsi) +static void sim_update_imsi(struct modem_data *modem, + DBusMessageIter *value) { - DBG("%s imsi %s", modem->path, imsi); + char *imsi; - if (g_strcmp0(modem->imsi, imsi) == 0) - return; + 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 *connection, DBusMessage *message, +static gboolean sim_changed(DBusConnection *conn, DBusMessage *message, void *user_data) { const char *path = dbus_message_get_path(message); @@ -1435,10 +2139,13 @@ static gboolean sim_changed(DBusConnection *connection, DBusMessage *message, const char *key; modem = g_hash_table_lookup(modem_hash, path); - if (modem == NULL) + if (!modem) + return TRUE; + + if (modem->ignore) return TRUE; - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &key); @@ -1446,20 +2153,19 @@ static gboolean sim_changed(DBusConnection *connection, DBusMessage *message, dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); - if (g_str_equal(key, "SubscriberIdentity") == TRUE) { - char *imsi; - - dbus_message_iter_get_basic(&value, &imsi); + if (g_str_equal(key, "SubscriberIdentity")) { + sim_update_imsi(modem, &value); - update_sim_imsi(modem, imsi); + if (!ready_to_create_device(modem)) + return TRUE; - if (modem->online == FALSE) { - modem_set_online(modem); - } else if (has_interface(modem->interfaces, - OFONO_API_CM) == TRUE) { - if (ready_to_create_device(modem) == TRUE) - create_device(modem); - } + /* + * 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; @@ -1480,25 +2186,32 @@ static void sim_properties_reply(struct modem_data *modem, dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "SubscriberIdentity") == TRUE) { - char *imsi; + if (g_str_equal(key, "SubscriberIdentity")) { + sim_update_imsi(modem, &value); - dbus_message_iter_get_basic(&value, &imsi); + if (!ready_to_create_device(modem)) + return; - update_sim_imsi(modem, imsi); + /* + * 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 == FALSE) { - modem_set_online(modem); - break; - } + if (!modem->online) + return; - if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) { - if (ready_to_create_device(modem) == TRUE) - create_device(modem); - if (modem->device != NULL) { - cm_get_properties(modem); - cm_get_contexts(modem); - } + /* + * 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; } @@ -1510,10 +2223,97 @@ static void sim_properties_reply(struct modem_data *modem, static int sim_get_properties(struct modem_data *modem) { return get_properties(modem->path, OFONO_SIM_INTERFACE, - sim_properties_reply, modem); + sim_properties_reply, modem); } -static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, +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); @@ -1522,10 +2322,13 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, const char *key; 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); @@ -1533,72 +2336,42 @@ 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_message_iter_get_basic(&value, &modem->powered); + 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); - if (modem->powered == FALSE) - modem_set_powered(modem); - } else if (g_str_equal(key, "Online") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->online); + /* 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->online == FALSE) + if (!modem->device) return TRUE; - if (has_interface(modem->interfaces, OFONO_API_CM) == FALSE) - return TRUE; - if (ready_to_create_device(modem) == TRUE) - create_device(modem); - if (modem->device != NULL) { - cm_get_properties(modem); - cm_get_contexts(modem); - } - } else if (g_str_equal(key, "Interfaces") == TRUE) { - modem->interfaces = extract_interfaces(&value); - - DBG("%s Interfaces 0x%02x", modem->path, - modem->interfaces); - - if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) { - if (modem->imsi == NULL && - modem->set_powered == FALSE) { - /* - * Only use do GetProperties() when - * device has not been powered up. - */ - sim_get_properties(modem); - return TRUE; - } - } + connman_device_set_powered(modem->device, modem->online); + } else if (g_str_equal(key, "Interfaces")) { + uint8_t interfaces; - if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) { - if (ready_to_create_device(modem) == TRUE) - create_device(modem); - if (modem->device != NULL) { - cm_get_properties(modem); - cm_get_contexts(modem); - return TRUE; - } - } else { - if (modem->context != NULL) { - remove_cm_context(modem, - modem->context->path); - } - - if (modem->device != NULL) - destroy_device(modem); + interfaces = extract_interfaces(&value); + if (interfaces == modem->interfaces) return TRUE; - } - if (has_interface(modem->interfaces, OFONO_API_NETREG) == TRUE) { - if (modem->attached == TRUE) - netreg_get_properties(modem); - } - } else if (g_str_equal(key, "Serial") == 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); @@ -1608,12 +2381,12 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, DBG("%s Serial %s", modem->path, modem->serial); - if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) { - if (ready_to_create_device(modem) == TRUE) + if (has_interface(modem->interfaces, + OFONO_API_CDMA_CM)) { + if (ready_to_create_device(modem)) { create_device(modem); - if (modem->device != NULL) { - cm_get_properties(modem); - cm_get_contexts(modem); + if (modem->registered) + add_cdma_network(modem); } } } @@ -1628,7 +2401,7 @@ static void add_modem(const char *path, DBusMessageIter *prop) DBG("%s", path); modem = g_hash_table_lookup(modem_hash, path); - if (modem != NULL) { + if (modem) { /* * When oFono powers up we ask for the modems and oFono is * reporting with modem_added signal the modems. Only @@ -1638,7 +2411,7 @@ static void add_modem(const char *path, DBusMessageIter *prop) } modem = g_try_new0(struct modem_data, 1); - if (modem == NULL) + if (!modem) return; modem->path = g_strdup(path); @@ -1655,43 +2428,56 @@ static void add_modem(const char *path, DBusMessageIter *prop) dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Powered") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->powered); + 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") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->online); + } 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") == TRUE) { + } 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") == TRUE) { + } 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->powered == FALSE) { - modem_set_powered(modem); - } else if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) { - sim_get_properties(modem); - } else if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) { - if (ready_to_create_device(modem) == TRUE) - create_device(modem); - if (modem->device != NULL) { - cm_get_properties(modem); - cm_get_contexts(modem); - } + 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) @@ -1700,7 +2486,10 @@ static void modem_power_down(gpointer key, gpointer value, gpointer user_data) DBG("%s", modem->path); - modem_set_unpowered(modem); + if (modem->ignore) + return; + + modem_set_powered(modem, FALSE); } static void remove_modem(gpointer data) @@ -1709,20 +2498,30 @@ static void remove_modem(gpointer data) DBG("%s", modem->path); - if (modem->call_set_property != NULL) + 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 != 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 != 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; + } - if (modem->device != NULL) - destroy_device(modem); + /* Must remove the contexts before the device */ + if (modem->context_list) + remove_all_contexts(modem); - if (modem->context != NULL) - remove_cm_context(modem, modem->context->path); + if (modem->device) + destroy_device(modem); g_free(modem->serial); g_free(modem->name); @@ -1732,7 +2531,7 @@ static void remove_modem(gpointer data) g_free(modem); } -static gboolean modem_added(DBusConnection *connection, +static gboolean modem_added(DBusConnection *conn, DBusMessage *message, void *user_data) { DBusMessageIter iter, properties; @@ -1740,7 +2539,7 @@ static gboolean modem_added(DBusConnection *connection, DBG(""); - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &path); @@ -1753,7 +2552,7 @@ static gboolean modem_added(DBusConnection *connection, return TRUE; } -static gboolean modem_removed(DBusConnection *connection, +static gboolean modem_removed(DBusConnection *conn, DBusMessage *message, void *user_data) { DBusMessageIter iter; @@ -1761,7 +2560,7 @@ static gboolean modem_removed(DBusConnection *connection, DBG(""); - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &path); @@ -1783,13 +2582,13 @@ static void manager_get_modems_reply(DBusPendingCall *call, void *user_data) dbus_error_init(&error); - if (dbus_set_error_from_message(&error, reply) == TRUE) { + 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) == FALSE) + if (!dbus_message_iter_init(reply, &array)) goto done; dbus_message_iter_recurse(&array, &dict); @@ -1824,17 +2623,17 @@ static int manager_get_modems(void) message = dbus_message_new_method_call(OFONO_SERVICE, "/", OFONO_MANAGER_INTERFACE, GET_MODEMS); - if (message == NULL) + if (!message) return -ENOMEM; - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { + if (!dbus_connection_send_with_reply(connection, message, + &call, TIMEOUT)) { connman_error("Failed to call GetModems()"); dbus_message_unref(message); return -EINVAL; } - if (call == NULL) { + if (!call) { connman_error("D-Bus connection not available"); dbus_message_unref(message); return -EINVAL; @@ -1854,12 +2653,12 @@ static void ofono_connect(DBusConnection *conn, void *user_data) modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, remove_modem); - if (modem_hash == NULL) + if (!modem_hash) return; context_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - if (context_hash == NULL) { + if (!context_hash) { g_hash_table_destroy(modem_hash); return; } @@ -1871,7 +2670,7 @@ static void ofono_disconnect(DBusConnection *conn, void *user_data) { DBG(""); - if (modem_hash == NULL || context_hash == NULL) + if (!modem_hash || !context_hash) return; g_hash_table_destroy(modem_hash); @@ -1899,24 +2698,54 @@ static void network_remove(struct connman_network *network) static int network_connect(struct connman_network *network) { + struct network_context *context; struct modem_data *modem = connman_network_get_data(network); DBG("%s network %p", modem->path, network); - return context_set_active(modem); + if (!g_hash_table_lookup(modem_hash, modem->path)) + return -ENODEV; + + context = get_context_with_network(modem->context_list, network); + if (!context) + return -ENODEV; + + 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); + + connman_error("Connection manager interface not available"); + + return -ENOSYS; } static int network_disconnect(struct connman_network *network) { + struct network_context *context; struct modem_data *modem = connman_network_get_data(network); DBG("%s network %p", modem->path, network); - return context_set_inactive(modem); + if (!g_hash_table_lookup(modem_hash, modem->path)) + return -ENODEV; + + context = get_context_with_network(modem->context_list, network); + if (!context) + return -ENODEV; + + 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); + + connman_error("Connection manager interface not available"); + + return -ENOSYS; } static struct connman_network_driver network_driver = { - .name = "network", + .name = "cellular", .type = CONNMAN_NETWORK_TYPE_CELLULAR, .probe = network_probe, .remove = network_remove, @@ -1946,7 +2775,10 @@ static int modem_enable(struct connman_device *device) DBG("%s device %p", modem->path, device); - return 0; + if (modem->online) + return 0; + + return modem_set_online(modem, TRUE); } static int modem_disable(struct connman_device *device) @@ -1955,7 +2787,10 @@ static int modem_disable(struct connman_device *device) DBG("%s device %p", modem->path, device); - return 0; + if (!modem->online) + return 0; + + return modem_set_online(modem, FALSE); } static struct connman_device_driver modem_driver = { @@ -1967,6 +2802,22 @@ static struct connman_device_driver modem_driver = { .disable = modem_disable, }; +static int tech_probe(struct connman_technology *technology) +{ + return 0; +} + +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 modem_added_watch; static guint modem_removed_watch; @@ -1977,6 +2828,8 @@ static guint context_added_watch; static guint context_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) { @@ -1985,73 +2838,89 @@ static int ofono_init(void) 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); - modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_MANAGER_INTERFACE, + 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, 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, NULL, NULL, + modem_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, OFONO_MODEM_INTERFACE, PROPERTY_CHANGED, modem_changed, NULL, NULL); - cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL, + cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, OFONO_CM_INTERFACE, PROPERTY_CHANGED, cm_changed, NULL, NULL); - sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL, + sim_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, OFONO_SIM_INTERFACE, PROPERTY_CHANGED, sim_changed, NULL, NULL); - context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL, + context_added_watch = g_dbus_add_signal_watch(connection, + OFONO_SERVICE, NULL, OFONO_CM_INTERFACE, CONTEXT_ADDED, cm_context_added, NULL, NULL); - context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL, + context_removed_watch = g_dbus_add_signal_watch(connection, + OFONO_SERVICE, NULL, OFONO_CM_INTERFACE, CONTEXT_REMOVED, cm_context_removed, NULL, NULL); - context_watch = g_dbus_add_signal_watch(connection, NULL, NULL, - OFONO_CONTEXT_INTERFACE, + context_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, + NULL, OFONO_CONTEXT_INTERFACE, PROPERTY_CHANGED, context_changed, NULL, NULL); - netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL, + netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, OFONO_NETREG_INTERFACE, PROPERTY_CHANGED, netreg_changed, NULL, NULL); + cdma_cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, + NULL, OFONO_CDMA_CM_INTERFACE, + PROPERTY_CHANGED, + cdma_cm_changed, + NULL, NULL); + + cdma_netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, + NULL, OFONO_CDMA_NETREG_INTERFACE, + PROPERTY_CHANGED, + cdma_netreg_changed, + NULL, NULL); + if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 || modem_watch == 0 || cm_watch == 0 || sim_watch == 0 || context_added_watch == 0 || context_removed_watch == 0 || - context_watch == 0 || netreg_watch == 0) { + context_watch == 0 || netreg_watch == 0 || + cdma_cm_watch == 0 || cdma_netreg_watch == 0) { err = -EIO; goto remove; } @@ -2066,9 +2935,18 @@ 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, 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); @@ -2088,9 +2966,9 @@ static void ofono_exit(void) { DBG(""); - if (modem_hash != NULL) { + if (modem_hash) { /* - * We should propably wait for the SetProperty() reply + * We should probably wait for the SetProperty() reply * message, because ... */ g_hash_table_foreach(modem_hash, modem_power_down, NULL); @@ -2102,14 +2980,17 @@ static void ofono_exit(void) modem_hash = NULL; } - if (context_hash != 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);