X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fofono.c;h=ae419be4e15bcab1c93af6b9968a5b9dae0db49a;hb=091f5fa9229897a52424cd442b325f97d361dbc3;hp=aa9a776bb1b405516ea5718bed158bdabea4d77b;hpb=36757a45b6598fe66aae08320710fb0b276e3c78;p=framework%2Fconnectivity%2Fconnman.git diff --git a/plugins/ofono.c b/plugins/ofono.c index aa9a776..ae419be 100644 --- a/plugins/ofono.c +++ b/plugins/ofono.c @@ -2,7 +2,7 @@ * * 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. * @@ -39,8 +39,9 @@ #include #include #include +#include -#define uninitialized_var(x) x = x +#include "mcc.h" #define OFONO_SERVICE "org.ofono" @@ -50,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" @@ -68,6 +71,8 @@ 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, }; /* @@ -76,13 +81,48 @@ enum ofono_api { * powered -> SubscriberIdentity or Online = True -> gprs, context -> * attached -> netreg -> ready * - * Enabling and disabling modems are steered through the rfkill - * interface. That means when ConnMan toggles the rfkill bit oFono - * will add or remove the modems. + * Depending on the modem type, this plugin will behave differently. * - * ConnMan will always power up (set Powered and Online) the - * modems. No need to power them down because this will be done - * through the rfkill inteface. + * 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; @@ -119,17 +159,18 @@ struct modem_data { 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; @@ -137,6 +178,7 @@ struct modem_data { /* Netreg Interface */ char *name; uint8_t strength; + uint8_t data_strength; /* 1xEVDO signal strength */ connman_bool_t roaming; /* pending calls */ @@ -145,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; @@ -251,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); } @@ -312,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, @@ -471,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) +static int context_set_active(struct modem_data *modem, + connman_bool_t active) { - 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) -{ - 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); @@ -519,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, @@ -617,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); } @@ -751,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; @@ -835,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. * @@ -856,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); @@ -890,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); } @@ -955,12 +1035,32 @@ static void add_network(struct modem_data *modem) 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; @@ -1005,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); } @@ -1020,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; } @@ -1029,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, @@ -1080,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; @@ -1182,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; @@ -1254,10 +1410,49 @@ static void netreg_update_strength(struct modem_data *modem, 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) { @@ -1284,6 +1479,27 @@ static void netreg_update_roaming(struct modem_data *modem, 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) { @@ -1313,6 +1529,8 @@ static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message, 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; } @@ -1338,6 +1556,8 @@ static void netreg_properties_reply(struct modem_data *modem, 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); } @@ -1354,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); @@ -1366,6 +1587,104 @@ static int netreg_get_properties(struct modem_data *modem) netreg_properties_reply, modem); } +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; + + dbus_message_iter_get_basic(&iter, &key); + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); + + if (g_str_equal(key, "Name") == TRUE) + 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); + + add_cdma_network(modem); + + return TRUE; +} + +static void cdma_netreg_properties_reply(struct modem_data *modem, + DBusMessageIter *dict) +{ + DBG("%s", modem->path); + + while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + if (g_str_equal(key, "Name") == 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); + + dbus_message_iter_next(dict); + } + + add_cdma_network(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) { @@ -1395,7 +1714,7 @@ static void cm_update_powered(struct modem_data *modem, if (modem->cm_powered == TRUE) return; - cm_set_powered(modem); + cm_set_powered(modem, TRUE); } static gboolean cm_changed(DBusConnection *connection, DBusMessage *message, @@ -1429,6 +1748,61 @@ static gboolean cm_changed(DBusConnection *connection, DBusMessage *message, 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); @@ -1458,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); @@ -1494,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; - - dbus_message_iter_get_basic(&value, &imsi); + sim_update_imsi(modem, &value); - 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; @@ -1528,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; } @@ -1557,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, @@ -1589,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; @@ -1658,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); - } } } @@ -1744,17 +2201,11 @@ static void add_modem(const char *path, DBusMessageIter *prop) 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) @@ -1766,7 +2217,7 @@ static void modem_power_down(gpointer key, gpointer value, gpointer user_data) if (modem->ignore == TRUE) return; - modem_set_unpowered(modem); + modem_set_powered(modem, FALSE); } static void remove_modem(gpointer data) @@ -1969,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) @@ -1978,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, @@ -2012,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) @@ -2021,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 = { @@ -2033,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; @@ -2043,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) { @@ -2112,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; } @@ -2132,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); @@ -2173,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);