X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fofono.c;h=ae419be4e15bcab1c93af6b9968a5b9dae0db49a;hb=091f5fa9229897a52424cd442b325f97d361dbc3;hp=0e26d40e60bf26707548ea6e9d7bb2ca6d8a1675;hpb=4ccccee21fae749ce7bdcdc154ba79c45317e93f;p=framework%2Fconnectivity%2Fconnman.git diff --git a/plugins/ofono.c b/plugins/ofono.c index 0e26d40..ae419be 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-2012 Intel Corporation. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2011 BWM Car IT GmbH. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -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; @@ -100,19 +156,21 @@ struct modem_data { connman_bool_t powered; connman_bool_t online; uint8_t interfaces; + connman_bool_t ignore; connman_bool_t set_powered; - connman_bool_t set_online; + + /* CDMA ConnectionManager Interface */ + connman_bool_t cdma_cm_powered; /* ConnectionManager Interface */ connman_bool_t attached; connman_bool_t cm_powered; - connman_bool_t set_cm_powered; - /* ConnectionContext Interface */ connman_bool_t active; connman_bool_t set_active; + connman_bool_t valid_apn; /* APN is 'valid' if length > 0 */ /* SimManager Interface */ char *imsi; @@ -120,6 +178,8 @@ struct modem_data { /* Netreg Interface */ char *name; uint8_t strength; + uint8_t data_strength; /* 1xEVDO signal strength */ + connman_bool_t roaming; /* pending calls */ DBusPendingCall *call_set_property; @@ -127,6 +187,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; @@ -233,6 +311,9 @@ static void set_disconnected(struct modem_data *modem) { DBG("%s", modem->path); + if (modem->network == NULL) + return; + connman_network_set_connected(modem->network, FALSE); } @@ -294,8 +375,10 @@ 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; + DBG("Cancel pending SetProperty"); + + dbus_pending_call_cancel(modem->call_set_property); + modem->call_set_property = NULL; } message = dbus_message_new_method_call(OFONO_SERVICE, path, @@ -453,42 +536,39 @@ static void context_set_active_reply(struct modem_data *modem, * cycle the modem in such cases? */ + if (modem->network == NULL) { + /* + * 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(modem->network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); } -static int context_set_active(struct modem_data *modem) -{ - dbus_bool_t active = TRUE; - - DBG("%s", modem->path); - - return set_property(modem, modem->context->path, - OFONO_CONTEXT_INTERFACE, - "Active", DBUS_TYPE_BOOLEAN, - &active, - context_set_active_reply); -} - -static int context_set_inactive(struct modem_data *modem) +static int context_set_active(struct modem_data *modem, + connman_bool_t active) { - dbus_bool_t active = FALSE; int err; - DBG("%s", modem->path); + DBG("%s active %d", modem->path, active); err = set_property(modem, modem->context->path, OFONO_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &active, - NULL); - if (err == -EINPROGRESS) + context_set_active_reply); + + if (active == FALSE && err == -EINPROGRESS) return 0; return err; } -static void modem_set_online_reply(struct modem_data *modem, +static void cdma_cm_set_powered_reply(struct modem_data *modem, connman_bool_t success) { DBG("%s", modem->path); @@ -501,75 +581,90 @@ static void modem_set_online_reply(struct modem_data *modem, 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 (modem->network == NULL) { /* - * 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(modem->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, connman_bool_t powered) { - DBG("%s", modem->path); + int err; - modem->set_cm_powered = TRUE; + DBG("%s powered %d", modem->path, powered); - return set_property(modem, modem->path, - OFONO_CM_INTERFACE, + err = set_property(modem, 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 == FALSE && err == -EINPROGRESS) + return 0; + + return err; } -static int modem_set_powered(struct modem_data *modem) +static int modem_set_online(struct modem_data *modem, connman_bool_t online) { - DBG("%s", modem->path); - - modem->set_powered = TRUE; + DBG("%s online %d", modem->path, online); return set_property(modem, modem->path, OFONO_MODEM_INTERFACE, + "Online", DBUS_TYPE_BOOLEAN, + &online, + NULL); +} + +static int cm_set_powered(struct modem_data *modem, connman_bool_t powered) +{ + int err; + + DBG("%s powered %d", modem->path, powered); + + err = set_property(modem, modem->path, + OFONO_CM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, - &modem->set_powered, + &powered, NULL); + + if (powered == FALSE && err == -EINPROGRESS) + return 0; + + return err; } -static int modem_set_unpowered(struct modem_data *modem) +static int modem_set_powered(struct modem_data *modem, connman_bool_t powered) { - DBG("%s", modem->path); + int err; - modem->set_powered = FALSE; + DBG("%s powered %d", modem->path, powered); - return set_property(modem, modem->path, + modem->set_powered = powered; + + err = set_property(modem, modem->path, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, - &modem->set_powered, + &powered, NULL); + + if (powered == FALSE && err == -EINPROGRESS) + return 0; + + return err; } static connman_bool_t has_interface(uint8_t interfaces, @@ -599,6 +694,10 @@ static uint8_t extract_interfaces(DBusMessageIter *array) interfaces |= OFONO_API_NETREG; else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE) interfaces |= OFONO_API_CM; + else if (g_str_equal(name, OFONO_CDMA_CM_INTERFACE) == TRUE) + interfaces |= OFONO_API_CDMA_CM; + else if (g_str_equal(name, OFONO_CDMA_NETREG_INTERFACE) == TRUE) + interfaces |= OFONO_API_CDMA_NETREG; dbus_message_iter_next(&entry); } @@ -733,7 +832,7 @@ 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; @@ -817,8 +916,6 @@ static connman_bool_t ready_to_create_device(struct modem_data *modem) * 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. * @@ -838,7 +935,7 @@ static connman_bool_t ready_to_create_device(struct modem_data *modem) static void create_device(struct modem_data *modem) { struct connman_device *device; - char *uninitialized_var(ident); + char *ident = NULL; DBG("%s", modem->path); @@ -872,6 +969,7 @@ static void create_device(struct modem_data *modem) modem->device = device; + connman_device_set_powered(modem->device, modem->online); out: g_free(ident); } @@ -929,17 +1027,40 @@ static void add_network(struct modem_data *modem) connman_network_set_available(modem->network, TRUE); + connman_network_set_bool(modem->network, "Roaming", + modem->roaming); + if (connman_device_add_network(modem->device, modem->network) < 0) { connman_network_unref(modem->network); modem->network = NULL; return; } + + /* + * Create the ipconfig layer before trying to connect. Withouth + * the ipconfig layer the core is not ready to process errors. + */ + connman_network_set_index(modem->network, -1); +} + +static void remove_network(struct modem_data *modem) +{ + DBG("%s", modem->path); + + if (modem->network == NULL) + return; + + DBG("network %p", modem->network); + + connman_device_remove_network(modem->device, modem->network); + connman_network_unref(modem->network); + modem->network = NULL; } 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; @@ -984,8 +1105,17 @@ static int add_cm_context(struct modem_data *modem, const char *context_path, dbus_message_iter_get_basic(&value, &active); DBG("%s Active %d", modem->path, active); - } + } else if (g_str_equal(key, "AccessPointName") == TRUE) { + const char *apn; + dbus_message_iter_get_basic(&value, &apn); + if (apn != NULL && strlen(apn) > 0) + modem->valid_apn = TRUE; + else + modem->valid_apn = FALSE; + + DBG("%s AccessPointName '%s'", modem->path, apn); + } dbus_message_iter_next(dict); } @@ -999,6 +1129,12 @@ static int add_cm_context(struct modem_data *modem, const char *context_path, g_hash_table_replace(context_hash, g_strdup(context_path), modem); + if (modem->valid_apn == TRUE && modem->attached == TRUE && + has_interface(modem->interfaces, + OFONO_API_NETREG) == TRUE) { + add_network(modem); + } + return 0; } @@ -1008,10 +1144,18 @@ static void remove_cm_context(struct modem_data *modem, if (modem->context == NULL) return; + if (modem->network != NULL) + remove_network(modem); + g_hash_table_remove(context_hash, context_path); network_context_free(modem->context); modem->context = NULL; + + modem->valid_apn = FALSE; + + if (modem->network != NULL) + remove_network(modem); } static gboolean context_changed(DBusConnection *connection, @@ -1059,6 +1203,39 @@ static gboolean context_changed(DBusConnection *connection, set_connected(modem); else set_disconnected(modem); + } else if (g_str_equal(key, "AccessPointName") == TRUE) { + const char *apn; + + dbus_message_iter_get_basic(&value, &apn); + + DBG("%s AccessPointName %s", modem->path, apn); + + if (apn != NULL && strlen(apn) > 0) { + modem->valid_apn = TRUE; + + if (modem->network != NULL) + return TRUE; + + if (modem->attached == FALSE) + return TRUE; + + if (has_interface(modem->interfaces, + OFONO_API_NETREG) == FALSE) { + return TRUE; + } + + add_network(modem); + + if (modem->active == TRUE) + set_connected(modem); + } else { + modem->valid_apn = FALSE; + + if (modem->network == NULL) + return TRUE; + + remove_network(modem); + } } return TRUE; @@ -1161,7 +1338,7 @@ static gboolean cm_context_added(DBusConnection *connection, DBG("%s", path); - modem = g_hash_table_lookup(modem_hash, context_path); + modem = g_hash_table_lookup(modem_hash, path); if (modem == NULL) return TRUE; @@ -1204,6 +1381,125 @@ static gboolean cm_context_removed(DBusConnection *connection, return TRUE; } +static void netreg_update_name(struct modem_data *modem, + DBusMessageIter* value) +{ + char *name; + + dbus_message_iter_get_basic(value, &name); + + DBG("%s Name %s", modem->path, name); + + g_free(modem->name); + modem->name = g_strdup(name); + + if (modem->network == NULL) + return; + + connman_network_set_name(modem->network, modem->name); + connman_network_update(modem->network); +} + +static void netreg_update_strength(struct modem_data *modem, + DBusMessageIter *value) +{ + dbus_message_iter_get_basic(value, &modem->strength); + + DBG("%s Strength %d", modem->path, modem->strength); + + if (modem->network == NULL) + return; + + /* + * 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; + + connman_network_set_strength(modem->network, modem->strength); + connman_network_update(modem->network); +} + +/* Retrieve 1xEVDO Data Strength signal */ +static void netreg_update_datastrength(struct modem_data *modem, + DBusMessageIter *value) +{ + dbus_message_iter_get_basic(value, &modem->data_strength); + + DBG("%s Data Strength %d", modem->path, modem->data_strength); + + if (modem->network == NULL) + 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; + + connman_network_set_strength(modem->network, modem->data_strength); + connman_network_update(modem->network); +} + +static void netreg_update_roaming(struct modem_data *modem, + DBusMessageIter *value) +{ + char *status; + connman_bool_t roaming; + + dbus_message_iter_get_basic(value, &status); + + if (g_str_equal(status, "roaming") == TRUE) + roaming = TRUE; + else + roaming = FALSE; + + if (roaming == modem->roaming) + return; + + modem->roaming = roaming; + + if (modem->network == NULL) + return; + + connman_network_set_bool(modem->network, + "Roaming", modem->roaming); + connman_network_update(modem->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 != NULL) + connman_technology_set_regdom(alpha2); +} + static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -1216,6 +1512,9 @@ static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message, if (modem == NULL) return TRUE; + if (modem->ignore == TRUE) + return TRUE; + if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; @@ -1224,32 +1523,14 @@ static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message, dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); - if (g_str_equal(key, "Name") == TRUE) { - char *name; - - dbus_message_iter_get_basic(&value, &name); - - DBG("%s Name %s", modem->path, name); - - g_free(modem->name); - modem->name = g_strdup(name); - - if (modem->network == NULL) - return TRUE; - - connman_network_set_name(modem->network, modem->name); - connman_network_update(modem->network); - } else if (g_str_equal(key, "Strength") == TRUE) { - dbus_message_iter_get_basic(&value, &modem->strength); - - DBG("%s Strength %d", modem->path, modem->strength); - - if (modem->network == NULL) - return TRUE; - - connman_network_set_strength(modem->network, modem->strength); - connman_network_update(modem->network); - } + if (g_str_equal(key, "Name") == TRUE) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength") == TRUE) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "Status") == TRUE) + netreg_update_roaming(modem, &value); + else if (g_str_equal(key, "MobileCountryCode") == TRUE) + netreg_update_regdom(modem, &value); return TRUE; } @@ -1269,33 +1550,14 @@ 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") == TRUE) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength") == TRUE) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "Status") == TRUE) + netreg_update_roaming(modem, &value); + else if (g_str_equal(key, "MobileCountryCode") == TRUE) + netreg_update_regdom(modem, &value); dbus_message_iter_next(dict); } @@ -1312,7 +1574,8 @@ static void netreg_properties_reply(struct modem_data *modem, return; } - add_network(modem); + if (modem->valid_apn == TRUE) + add_network(modem); if (modem->active == TRUE) set_connected(modem); @@ -1324,18 +1587,46 @@ 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) +{ + /* Be sure that device is created before adding CDMA network */ + if (modem->device == NULL) + 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 == NULL) + modem->context = network_context_alloc(modem->path); + + if (modem->name == NULL) + modem->name = g_strdup("CDMA Network"); + + add_network(modem); + + if (modem->cdma_cm_powered == TRUE) + set_connected(modem); +} + +static gboolean cdma_netreg_changed(DBusConnection *connection, + DBusMessage *message, + void *user_data) { const char *path = dbus_message_get_path(message); struct modem_data *modem; DBusMessageIter iter, value; const char *key; + DBG(""); + modem = g_hash_table_lookup(modem_hash, path); if (modem == NULL) return TRUE; + if (modem->ignore == TRUE) + return TRUE; + if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; @@ -1344,31 +1635,22 @@ 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); - - DBG("%s Attached %d", modem->path, modem->attached); + if (g_str_equal(key, "Name") == TRUE) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength") == TRUE) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "DataStrength") == TRUE) + netreg_update_datastrength(modem, &value); + else if (g_str_equal(key, "Status") == TRUE) + netreg_update_roaming(modem, &value); - 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); - - DBG("%s ConnnectionManager Powered %d", modem->path, - modem->cm_powered); - - if (modem->cm_powered == FALSE) - cm_set_powered(modem); - } + add_cdma_network(modem); return TRUE; } -static void cm_properties_reply(struct modem_data *modem, DBusMessageIter *dict) +static void cdma_netreg_properties_reply(struct modem_data *modem, + DBusMessageIter *dict) { DBG("%s", modem->path); @@ -1382,27 +1664,163 @@ 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, "Name") == TRUE) + netreg_update_name(modem, &value); + else if (g_str_equal(key, "Strength") == TRUE) + netreg_update_strength(modem, &value); + else if (g_str_equal(key, "DataStrength") == TRUE) + netreg_update_datastrength(modem, &value); + else if (g_str_equal(key, "Status") == TRUE) + netreg_update_roaming(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); + add_cdma_network(modem); +} - DBG("%s ConnnectionManager Powered %d", modem->path, - modem->cm_powered); +static int cdma_netreg_get_properties(struct modem_data *modem) +{ + return get_properties(modem->path, OFONO_CDMA_NETREG_INTERFACE, + cdma_netreg_properties_reply, modem); +} - if (modem->cm_powered == FALSE) - cm_set_powered(modem); - } +static void cm_update_attached(struct modem_data *modem, + DBusMessageIter *value) +{ + dbus_message_iter_get_basic(value, &modem->attached); + + DBG("%s Attached %d", modem->path, modem->attached); + + if (modem->attached == FALSE) + return; + + if (has_interface(modem->interfaces, + OFONO_API_NETREG) == FALSE) { + return; + } + + netreg_get_properties(modem); +} + +static void cm_update_powered(struct modem_data *modem, + DBusMessageIter *value) +{ + dbus_message_iter_get_basic(value, &modem->cm_powered); + + DBG("%s ConnnectionManager Powered %d", modem->path, + modem->cm_powered); + + if (modem->cm_powered == TRUE) + return; + + cm_set_powered(modem, TRUE); +} + +static gboolean cm_changed(DBusConnection *connection, DBusMessage *message, + void *user_data) +{ + const char *path = dbus_message_get_path(message); + struct modem_data *modem; + DBusMessageIter iter, value; + const char *key; + + modem = g_hash_table_lookup(modem_hash, path); + if (modem == NULL) + return TRUE; + + if (modem->ignore == TRUE) + return TRUE; + + if (dbus_message_iter_init(message, &iter) == FALSE) + return TRUE; + + dbus_message_iter_get_basic(&iter, &key); + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); + + if (g_str_equal(key, "Attached") == TRUE) + cm_update_attached(modem, &value); + else if (g_str_equal(key, "Powered") == TRUE) + cm_update_powered(modem, &value); + + return TRUE; +} + +static void cdma_cm_update_powered(struct modem_data *modem, + DBusMessageIter *value) +{ + dbus_message_iter_get_basic(value, &modem->cdma_cm_powered); + + DBG("%s CDMA cm Powered %d", modem->path, modem->cdma_cm_powered); + + if (modem->network == NULL) + return; + + if (modem->cdma_cm_powered == TRUE) + set_connected(modem); + else + set_disconnected(modem); +} + +static void cdma_cm_update_settings(struct modem_data *modem, + DBusMessageIter *value) +{ + DBG("%s Settings", modem->path); + + extract_ipv4_settings(value, modem->context); +} + +static gboolean cdma_cm_changed(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + const char *path = dbus_message_get_path(message); + struct modem_data *modem; + DBusMessageIter iter, value; + const char *key; + + modem = g_hash_table_lookup(modem_hash, path); + if (modem == NULL) + return TRUE; + + if (modem->online == TRUE && modem->network == NULL) + cdma_netreg_get_properties(modem); + + if (dbus_message_iter_init(message, &iter) == FALSE) + return TRUE; + + dbus_message_iter_get_basic(&iter, &key); + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); + + if (g_str_equal(key, "Powered") == TRUE) + cdma_cm_update_powered(modem, &value); + if (g_str_equal(key, "Settings") == TRUE) + 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") == TRUE) + cm_update_attached(modem, &value); + else if (g_str_equal(key, "Powered") == TRUE) + cm_update_powered(modem, &value); dbus_message_iter_next(dict); } @@ -1414,13 +1832,47 @@ static int cm_get_properties(struct modem_data *modem) cm_properties_reply, modem); } -static void update_sim_imsi(struct modem_data *modem, - const char *imsi) +static void cdma_cm_properties_reply(struct modem_data *modem, + DBusMessageIter *dict) { - DBG("%s imsi %s", modem->path, imsi); + DBG("%s", modem->path); - if (g_strcmp0(modem->imsi, imsi) == 0) - return; + if (modem->online == TRUE) + 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") == TRUE) + cdma_cm_update_powered(modem, &value); + if (g_str_equal(key, "Settings") == TRUE) + 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); @@ -1438,6 +1890,9 @@ static gboolean sim_changed(DBusConnection *connection, DBusMessage *message, if (modem == NULL) return TRUE; + if (modem->ignore == TRUE) + return TRUE; + if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; @@ -1447,19 +1902,18 @@ static gboolean sim_changed(DBusConnection *connection, DBusMessage *message, dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "SubscriberIdentity") == TRUE) { - char *imsi; + sim_update_imsi(modem, &value); - dbus_message_iter_get_basic(&value, &imsi); - - update_sim_imsi(modem, imsi); + if (ready_to_create_device(modem) == FALSE) + 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; @@ -1481,24 +1935,31 @@ static void sim_properties_reply(struct modem_data *modem, dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "SubscriberIdentity") == TRUE) { - char *imsi; + sim_update_imsi(modem, &value); - dbus_message_iter_get_basic(&value, &imsi); + if (ready_to_create_device(modem) == FALSE) + 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 == FALSE) + 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) == TRUE) { - if (ready_to_create_device(modem) == TRUE) - create_device(modem); - if (modem->device != NULL) { - cm_get_properties(modem); - cm_get_contexts(modem); - } + cm_get_properties(modem); + cm_get_contexts(modem); } return; } @@ -1510,7 +1971,89 @@ 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 connman_bool_t api_added(uint8_t old_iface, uint8_t new_iface, + enum ofono_api api) +{ + if (has_interface(old_iface, api) == FALSE && + has_interface(new_iface, api) == TRUE) { + DBG("%s added", api2string(api)); + return TRUE; + } + + return FALSE; +} + +static connman_bool_t api_removed(uint8_t old_iface, uint8_t new_iface, + enum ofono_api api) +{ + if (has_interface(old_iface, api) == TRUE && + has_interface(new_iface, api) == FALSE) { + 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) == TRUE) { + if (modem->imsi == NULL && + modem->set_powered == FALSE) { + /* + * 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) == TRUE) { + if (modem->device != NULL) { + cm_get_properties(modem); + cm_get_contexts(modem); + } + } + + if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_CM) == TRUE) { + if (ready_to_create_device(modem) == TRUE) + create_device(modem); + + if (modem->device != NULL) + cdma_cm_get_properties(modem); + } + + if (api_added(old_ifaces, new_ifaces, OFONO_API_NETREG) == TRUE) { + if (modem->attached == TRUE) + netreg_get_properties(modem); + } + + if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG) == TRUE) { + cdma_netreg_get_properties(modem); + } + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_CM) == TRUE) { + remove_cm_context(modem, modem->context->path); + } + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_CM) == TRUE) { + remove_cm_context(modem, modem->context->path); + } + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_NETREG) == TRUE) { + remove_network(modem); + } + + if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG == TRUE)) { + remove_network(modem); + } } static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, @@ -1525,6 +2068,9 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, if (modem == NULL) return TRUE; + if (modem->ignore == TRUE) + return TRUE; + if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; @@ -1539,65 +2085,29 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, DBG("%s Powered %d", modem->path, modem->powered); if (modem->powered == FALSE) - modem_set_powered(modem); + modem_set_powered(modem, TRUE); } else if (g_str_equal(key, "Online") == TRUE) { dbus_message_iter_get_basic(&value, &modem->online); DBG("%s Online %d", modem->path, modem->online); - if (modem->online == FALSE) + if (modem->device == NULL) 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); - } + connman_device_set_powered(modem->device, modem->online); } 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; - } - } + 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); - } + 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") == TRUE) { char *serial; @@ -1608,13 +2118,10 @@ 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 (has_interface(modem->interfaces, + OFONO_API_CDMA_CM) == TRUE) { if (ready_to_create_device(modem) == TRUE) create_device(modem); - if (modem->device != NULL) { - cm_get_properties(modem); - cm_get_contexts(modem); - } } } @@ -1675,23 +2182,30 @@ static void add_modem(const char *path, DBusMessageIter *prop) modem->serial = g_strdup(serial); DBG("%s Serial %s", modem->path, modem->serial); + } else if (g_str_equal(key, "Type") == TRUE) { + 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 == TRUE) + return; + 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); - } + 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 +2214,10 @@ static void modem_power_down(gpointer key, gpointer value, gpointer user_data) DBG("%s", modem->path); - modem_set_unpowered(modem); + if (modem->ignore == TRUE) + return; + + modem_set_powered(modem, FALSE); } static void remove_modem(gpointer data) @@ -1903,7 +2420,14 @@ static int network_connect(struct connman_network *network) DBG("%s network %p", modem->path, network); - return context_set_active(modem); + if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) + return context_set_active(modem, TRUE); + else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE) + return cdma_cm_set_powered(modem, TRUE); + + connman_error("Connection manager interface not available"); + + return -ENOSYS; } static int network_disconnect(struct connman_network *network) @@ -1912,11 +2436,18 @@ static int network_disconnect(struct connman_network *network) DBG("%s network %p", modem->path, network); - return context_set_inactive(modem); + if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) + return context_set_active(modem, FALSE); + else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE) + 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 +2477,10 @@ static int modem_enable(struct connman_device *device) DBG("%s device %p", modem->path, device); - return 0; + if (modem->online == TRUE) + return 0; + + return modem_set_online(modem, TRUE); } static int modem_disable(struct connman_device *device) @@ -1955,7 +2489,10 @@ static int modem_disable(struct connman_device *device) DBG("%s device %p", modem->path, device); - return 0; + if (modem->online == FALSE) + return 0; + + return modem_set_online(modem, FALSE); } static struct connman_device_driver modem_driver = { @@ -1967,6 +2504,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 +2530,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) { @@ -2046,12 +2601,25 @@ static int ofono_init(void) netreg_changed, NULL, NULL); + cdma_cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL, + OFONO_CDMA_CM_INTERFACE, + PROPERTY_CHANGED, + cdma_cm_changed, + NULL, NULL); + + cdma_netreg_watch = g_dbus_add_signal_watch(connection, NULL, 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 +2634,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); @@ -2107,9 +2684,12 @@ static void ofono_exit(void) 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);