| /* |
| * |
| * Connection Manager |
| * |
| * 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 |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| |
| #include <gdbus.h> |
| #include <string.h> |
| #include <stdint.h> |
| |
| #define CONNMAN_API_SUBJECT_TO_CHANGE |
| #include <connman/plugin.h> |
| #include <connman/device.h> |
| #include <connman/network.h> |
| #include <connman/inet.h> |
| #include <connman/dbus.h> |
| #include <connman/log.h> |
| #include <connman/technology.h> |
| |
| #include "mcc.h" |
| |
| #define OFONO_SERVICE "org.ofono" |
| |
| #define OFONO_MANAGER_INTERFACE OFONO_SERVICE ".Manager" |
| #define OFONO_MODEM_INTERFACE OFONO_SERVICE ".Modem" |
| #define OFONO_SIM_INTERFACE OFONO_SERVICE ".SimManager" |
| #define OFONO_NETREG_INTERFACE OFONO_SERVICE ".NetworkRegistration" |
| #define OFONO_CM_INTERFACE OFONO_SERVICE ".ConnectionManager" |
| #define OFONO_CONTEXT_INTERFACE OFONO_SERVICE ".ConnectionContext" |
| #define OFONO_CDMA_CM_INTERFACE OFONO_SERVICE ".cdma.ConnectionManager" |
| #define OFONO_CDMA_NETREG_INTERFACE OFONO_SERVICE ".cdma.NetworkRegistration" |
| |
| #define MODEM_ADDED "ModemAdded" |
| #define MODEM_REMOVED "ModemRemoved" |
| #define PROPERTY_CHANGED "PropertyChanged" |
| #define CONTEXT_ADDED "ContextAdded" |
| #define CONTEXT_REMOVED "ContextRemoved" |
| |
| #define GET_PROPERTIES "GetProperties" |
| #define SET_PROPERTY "SetProperty" |
| #define GET_MODEMS "GetModems" |
| #define GET_CONTEXTS "GetContexts" |
| |
| #define TIMEOUT 40000 |
| |
| enum ofono_api { |
| OFONO_API_SIM = 0x1, |
| OFONO_API_NETREG = 0x2, |
| OFONO_API_CM = 0x4, |
| 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; |
| static GHashTable *context_hash; |
| |
| struct network_context { |
| char *path; |
| int index; |
| |
| enum connman_ipconfig_method ipv4_method; |
| struct connman_ipaddress *ipv4_address; |
| char *ipv4_nameservers; |
| |
| enum connman_ipconfig_method ipv6_method; |
| struct connman_ipaddress *ipv6_address; |
| char *ipv6_nameservers; |
| }; |
| |
| struct modem_data { |
| char *path; |
| |
| struct connman_device *device; |
| struct connman_network *network; |
| |
| struct network_context *context; |
| |
| /* Modem Interface */ |
| char *serial; |
| bool powered; |
| bool online; |
| uint8_t interfaces; |
| bool ignore; |
| |
| bool set_powered; |
| |
| /* CDMA ConnectionManager Interface */ |
| bool cdma_cm_powered; |
| |
| /* ConnectionManager Interface */ |
| bool attached; |
| bool cm_powered; |
| |
| /* ConnectionContext Interface */ |
| bool active; |
| bool valid_apn; /* APN is 'valid' if length > 0 */ |
| |
| /* SimManager Interface */ |
| char *imsi; |
| |
| /* Netreg Interface */ |
| char *name; |
| uint8_t strength; |
| uint8_t data_strength; /* 1xEVDO signal strength */ |
| bool registered; |
| bool roaming; |
| |
| /* pending calls */ |
| DBusPendingCall *call_set_property; |
| DBusPendingCall *call_get_properties; |
| DBusPendingCall *call_get_contexts; |
| }; |
| |
| static 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; |
| |
| if (*path != '/') |
| return NULL; |
| |
| pos = strrchr(path, '/'); |
| if (!pos) |
| return NULL; |
| |
| return pos + 1; |
| } |
| |
| static struct network_context *network_context_alloc(const char *path) |
| { |
| struct network_context *context; |
| |
| context = g_try_new0(struct network_context, 1); |
| if (!context) |
| return NULL; |
| |
| context->path = g_strdup(path); |
| context->index = -1; |
| |
| context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; |
| context->ipv4_address = NULL; |
| context->ipv4_nameservers = NULL; |
| |
| context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; |
| context->ipv6_address = NULL; |
| context->ipv6_nameservers = NULL; |
| |
| return context; |
| } |
| |
| static void network_context_free(struct network_context *context) |
| { |
| g_free(context->path); |
| |
| connman_ipaddress_free(context->ipv4_address); |
| g_free(context->ipv4_nameservers); |
| |
| connman_ipaddress_free(context->ipv6_address); |
| g_free(context->ipv6_nameservers); |
| |
| free(context); |
| } |
| |
| static void set_connected(struct modem_data *modem) |
| { |
| struct connman_service *service; |
| bool setip = false; |
| enum connman_ipconfig_method method; |
| char *nameservers; |
| int index; |
| |
| DBG("%s", modem->path); |
| |
| index = modem->context->index; |
| |
| method = modem->context->ipv4_method; |
| if (index < 0 || (!modem->context->ipv4_address && |
| method == CONNMAN_IPCONFIG_METHOD_FIXED)) { |
| connman_error("Invalid index and/or address"); |
| return; |
| } |
| |
| service = connman_service_lookup_from_network(modem->network); |
| if (!service) |
| return; |
| |
| connman_service_create_ip4config(service, index); |
| connman_network_set_ipv4_method(modem->network, method); |
| |
| if (method == CONNMAN_IPCONFIG_METHOD_FIXED || |
| method == CONNMAN_IPCONFIG_METHOD_DHCP) { |
| setip = true; |
| } |
| |
| if (method == CONNMAN_IPCONFIG_METHOD_FIXED) { |
| connman_network_set_ipaddress(modem->network, |
| modem->context->ipv4_address); |
| } |
| |
| method = modem->context->ipv6_method; |
| connman_service_create_ip6config(service, index); |
| connman_network_set_ipv6_method(modem->network, method); |
| |
| if (method == CONNMAN_IPCONFIG_METHOD_AUTO) { |
| setip = true; |
| } |
| |
| /* Set the nameservers */ |
| if (modem->context->ipv4_nameservers && |
| modem->context->ipv6_nameservers) { |
| nameservers = g_strdup_printf("%s %s", |
| modem->context->ipv4_nameservers, |
| modem->context->ipv6_nameservers); |
| connman_network_set_nameservers(modem->network, nameservers); |
| g_free(nameservers); |
| } else if (modem->context->ipv4_nameservers) { |
| connman_network_set_nameservers(modem->network, |
| modem->context->ipv4_nameservers); |
| } else if (modem->context->ipv6_nameservers) { |
| connman_network_set_nameservers(modem->network, |
| modem->context->ipv6_nameservers); |
| } |
| |
| if (setip) { |
| connman_network_set_index(modem->network, index); |
| connman_network_set_connected(modem->network, true); |
| } |
| } |
| |
| static void set_disconnected(struct modem_data *modem) |
| { |
| DBG("%s", modem->path); |
| |
| if (modem->network) |
| connman_network_set_connected(modem->network, false); |
| |
| if (modem->context) { |
| g_free(modem->context->ipv4_nameservers); |
| modem->context->ipv4_nameservers = NULL; |
| if (modem->context->ipv4_method != CONNMAN_IPCONFIG_METHOD_OFF) |
| modem->context->ipv4_method = |
| CONNMAN_IPCONFIG_METHOD_UNKNOWN; |
| |
| g_free(modem->context->ipv6_nameservers); |
| modem->context->ipv6_nameservers = NULL; |
| if (modem->context->ipv6_method != CONNMAN_IPCONFIG_METHOD_OFF) |
| modem->context->ipv6_method = |
| CONNMAN_IPCONFIG_METHOD_UNKNOWN; |
| } |
| } |
| |
| typedef void (*set_property_cb)(struct modem_data *data, |
| bool success); |
| typedef void (*get_properties_cb)(struct modem_data *data, |
| DBusMessageIter *dict); |
| |
| struct property_info { |
| struct modem_data *modem; |
| const char *path; |
| const char *interface; |
| const char *property; |
| set_property_cb set_property_cb; |
| get_properties_cb get_properties_cb; |
| }; |
| |
| static void set_property_reply(DBusPendingCall *call, void *user_data) |
| { |
| struct property_info *info = user_data; |
| DBusMessage *reply; |
| DBusError error; |
| bool success = true; |
| |
| DBG("%s path %s %s.%s", info->modem->path, |
| info->path, info->interface, info->property); |
| |
| info->modem->call_set_property = NULL; |
| |
| dbus_error_init(&error); |
| |
| reply = dbus_pending_call_steal_reply(call); |
| |
| if (dbus_set_error_from_message(&error, reply)) { |
| connman_error("Failed to change property: %s %s.%s: %s %s", |
| info->path, info->interface, info->property, |
| error.name, error.message); |
| dbus_error_free(&error); |
| success = false; |
| } |
| |
| if (info->set_property_cb) |
| (*info->set_property_cb)(info->modem, success); |
| |
| dbus_message_unref(reply); |
| |
| dbus_pending_call_unref(call); |
| } |
| |
| static int set_property(struct modem_data *modem, |
| const char *path, const char *interface, |
| const char *property, int type, void *value, |
| set_property_cb notify) |
| { |
| DBusMessage *message; |
| DBusMessageIter iter; |
| struct property_info *info; |
| |
| DBG("%s path %s %s.%s", modem->path, path, interface, property); |
| |
| if (modem->call_set_property) { |
| 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) |
| 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)) { |
| connman_error("Failed to change property: %s %s.%s", |
| path, interface, property); |
| dbus_message_unref(message); |
| return -EINVAL; |
| } |
| |
| 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) { |
| dbus_message_unref(message); |
| return -ENOMEM; |
| } |
| |
| info->modem = modem; |
| info->path = path; |
| info->interface = interface; |
| info->property = property; |
| info->set_property_cb = notify; |
| |
| dbus_pending_call_set_notify(modem->call_set_property, |
| set_property_reply, info, g_free); |
| |
| dbus_message_unref(message); |
| |
| return -EINPROGRESS; |
| } |
| |
| static void get_properties_reply(DBusPendingCall *call, void *user_data) |
| { |
| struct property_info *info = user_data; |
| DBusMessageIter array, dict; |
| DBusMessage *reply; |
| DBusError error; |
| |
| DBG("%s path %s %s", info->modem->path, info->path, info->interface); |
| |
| info->modem->call_get_properties = NULL; |
| |
| dbus_error_init(&error); |
| |
| reply = dbus_pending_call_steal_reply(call); |
| |
| if (dbus_set_error_from_message(&error, reply)) { |
| connman_error("Failed to get properties: %s %s: %s %s", |
| info->path, info->interface, |
| error.name, error.message); |
| dbus_error_free(&error); |
| |
| goto done; |
| } |
| |
| if (!dbus_message_iter_init(reply, &array)) |
| goto done; |
| |
| if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) |
| goto done; |
| |
| dbus_message_iter_recurse(&array, &dict); |
| |
| if (info->get_properties_cb) |
| (*info->get_properties_cb)(info->modem, &dict); |
| |
| done: |
| |
| dbus_message_unref(reply); |
| |
| dbus_pending_call_unref(call); |
| } |
| |
| static int get_properties(const char *path, const char *interface, |
| get_properties_cb notify, |
| struct modem_data *modem) |
| { |
| DBusMessage *message; |
| struct property_info *info; |
| |
| DBG("%s path %s %s", modem->path, path, interface); |
| |
| if (modem->call_get_properties) { |
| connman_error("Pending GetProperties"); |
| return -EBUSY; |
| } |
| |
| message = dbus_message_new_method_call(OFONO_SERVICE, path, |
| interface, GET_PROPERTIES); |
| if (!message) |
| return -ENOMEM; |
| |
| 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) { |
| connman_error("D-Bus connection not available"); |
| dbus_message_unref(message); |
| return -EINVAL; |
| } |
| |
| info = g_try_new0(struct property_info, 1); |
| if (!info) { |
| dbus_message_unref(message); |
| return -ENOMEM; |
| } |
| |
| info->modem = modem; |
| info->path = path; |
| info->interface = interface; |
| info->get_properties_cb = notify; |
| |
| dbus_pending_call_set_notify(modem->call_get_properties, |
| get_properties_reply, info, g_free); |
| |
| dbus_message_unref(message); |
| |
| return -EINPROGRESS; |
| } |
| |
| static void context_set_active_reply(struct modem_data *modem, |
| bool success) |
| { |
| DBG("%s", modem->path); |
| |
| if (success) { |
| /* |
| * Don't handle do anything on success here. oFono will send |
| * the change via PropertyChanged singal. |
| */ |
| return; |
| } |
| |
| /* |
| * Active = True might fail due a timeout. That means oFono |
| * still tries to go online. If we retry to set Active = True, |
| * we just get a InProgress error message. Should we power |
| * cycle the modem in such cases? |
| */ |
| |
| if (!modem->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(modem->network, |
| CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); |
| } |
| |
| static int context_set_active(struct modem_data *modem, |
| dbus_bool_t active) |
| { |
| int err; |
| |
| DBG("%s active %d", modem->path, active); |
| |
| err = set_property(modem, modem->context->path, |
| OFONO_CONTEXT_INTERFACE, |
| "Active", DBUS_TYPE_BOOLEAN, |
| &active, |
| context_set_active_reply); |
| |
| if (!active && err == -EINPROGRESS) |
| return 0; |
| |
| return err; |
| } |
| |
| static void cdma_cm_set_powered_reply(struct modem_data *modem, |
| bool success) |
| { |
| DBG("%s", modem->path); |
| |
| if (success) { |
| /* |
| * Don't handle do anything on success here. oFono will send |
| * the change via PropertyChanged singal. |
| */ |
| return; |
| } |
| |
| /* |
| * 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 (!modem->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(modem->network, |
| CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); |
| } |
| |
| static int cdma_cm_set_powered(struct modem_data *modem, dbus_bool_t powered) |
| { |
| int err; |
| |
| DBG("%s powered %d", modem->path, powered); |
| |
| err = set_property(modem, modem->path, OFONO_CDMA_CM_INTERFACE, |
| "Powered", DBUS_TYPE_BOOLEAN, |
| &powered, |
| cdma_cm_set_powered_reply); |
| |
| if (!powered && err == -EINPROGRESS) |
| return 0; |
| |
| return err; |
| } |
| |
| static int modem_set_online(struct modem_data *modem, dbus_bool_t online) |
| { |
| 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, dbus_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, |
| &powered, |
| NULL); |
| |
| if (!powered && err == -EINPROGRESS) |
| return 0; |
| |
| return err; |
| } |
| |
| static int modem_set_powered(struct modem_data *modem, dbus_bool_t powered) |
| { |
| int err; |
| |
| DBG("%s powered %d", modem->path, powered); |
| |
| modem->set_powered = powered; |
| |
| err = set_property(modem, modem->path, |
| OFONO_MODEM_INTERFACE, |
| "Powered", DBUS_TYPE_BOOLEAN, |
| &powered, |
| NULL); |
| |
| if (!powered && err == -EINPROGRESS) |
| return 0; |
| |
| return err; |
| } |
| |
| static bool has_interface(uint8_t interfaces, |
| enum ofono_api api) |
| { |
| if ((interfaces & api) == api) |
| return true; |
| |
| return false; |
| } |
| |
| static uint8_t extract_interfaces(DBusMessageIter *array) |
| { |
| DBusMessageIter entry; |
| uint8_t interfaces = 0; |
| |
| dbus_message_iter_recurse(array, &entry); |
| |
| while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { |
| const char *name; |
| |
| dbus_message_iter_get_basic(&entry, &name); |
| |
| if (g_str_equal(name, OFONO_SIM_INTERFACE)) |
| interfaces |= OFONO_API_SIM; |
| else if (g_str_equal(name, OFONO_NETREG_INTERFACE)) |
| interfaces |= OFONO_API_NETREG; |
| else if (g_str_equal(name, OFONO_CM_INTERFACE)) |
| interfaces |= OFONO_API_CM; |
| else if (g_str_equal(name, OFONO_CDMA_CM_INTERFACE)) |
| interfaces |= OFONO_API_CDMA_CM; |
| else if (g_str_equal(name, OFONO_CDMA_NETREG_INTERFACE)) |
| interfaces |= OFONO_API_CDMA_NETREG; |
| |
| dbus_message_iter_next(&entry); |
| } |
| |
| return interfaces; |
| } |
| |
| static char *extract_nameservers(DBusMessageIter *array) |
| { |
| DBusMessageIter entry; |
| char *nameservers = NULL; |
| char *tmp; |
| |
| dbus_message_iter_recurse(array, &entry); |
| |
| while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { |
| const char *nameserver; |
| |
| dbus_message_iter_get_basic(&entry, &nameserver); |
| |
| if (!nameservers) { |
| nameservers = g_strdup(nameserver); |
| } else { |
| tmp = nameservers; |
| nameservers = g_strdup_printf("%s %s", tmp, nameserver); |
| g_free(tmp); |
| } |
| |
| dbus_message_iter_next(&entry); |
| } |
| |
| return nameservers; |
| } |
| |
| static void extract_ipv4_settings(DBusMessageIter *array, |
| struct network_context *context) |
| { |
| DBusMessageIter dict; |
| char *address = NULL, *netmask = NULL, *gateway = NULL; |
| char *nameservers = NULL; |
| const char *interface = NULL; |
| int index = -1; |
| |
| connman_ipaddress_free(context->ipv4_address); |
| context->ipv4_address = NULL; |
| context->index = -1; |
| |
| if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) |
| return; |
| |
| dbus_message_iter_recurse(array, &dict); |
| |
| while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key, *val; |
| |
| dbus_message_iter_recurse(&dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "Interface")) { |
| dbus_message_iter_get_basic(&value, &interface); |
| |
| DBG("Interface %s", interface); |
| |
| index = connman_inet_ifindex(interface); |
| |
| DBG("index %d", index); |
| } else if (g_str_equal(key, "Method")) { |
| dbus_message_iter_get_basic(&value, &val); |
| |
| DBG("Method %s", val); |
| |
| if (g_strcmp0(val, "static") == 0) |
| context->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED; |
| else if (g_strcmp0(val, "dhcp") == 0) |
| context->ipv4_method = CONNMAN_IPCONFIG_METHOD_DHCP; |
| |
| } 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")) { |
| dbus_message_iter_get_basic(&value, &val); |
| |
| netmask = g_strdup(val); |
| |
| DBG("Netmask %s", netmask); |
| } else if (g_str_equal(key, "DomainNameServers")) { |
| nameservers = extract_nameservers(&value); |
| |
| DBG("Nameservers %s", nameservers); |
| } else if (g_str_equal(key, "Gateway")) { |
| dbus_message_iter_get_basic(&value, &val); |
| |
| gateway = g_strdup(val); |
| |
| DBG("Gateway %s", gateway); |
| } |
| |
| dbus_message_iter_next(&dict); |
| } |
| |
| 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) { |
| context->index = -1; |
| goto out; |
| } |
| |
| connman_ipaddress_set_ipv4(context->ipv4_address, address, |
| netmask, gateway); |
| |
| g_free(context->ipv4_nameservers); |
| context->ipv4_nameservers = nameservers; |
| |
| out: |
| if (context->ipv4_nameservers != nameservers) |
| g_free(nameservers); |
| |
| g_free(address); |
| g_free(netmask); |
| g_free(gateway); |
| } |
| |
| static void extract_ipv6_settings(DBusMessageIter *array, |
| struct network_context *context) |
| { |
| DBusMessageIter dict; |
| char *address = NULL, *gateway = NULL; |
| 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; |
| context->index = -1; |
| |
| if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) |
| return; |
| |
| dbus_message_iter_recurse(array, &dict); |
| |
| while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key, *val; |
| |
| dbus_message_iter_recurse(&dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "Interface")) { |
| dbus_message_iter_get_basic(&value, &interface); |
| |
| DBG("Interface %s", interface); |
| |
| index = connman_inet_ifindex(interface); |
| |
| DBG("index %d", index); |
| } else if (g_str_equal(key, "Address")) { |
| dbus_message_iter_get_basic(&value, &val); |
| |
| address = g_strdup(val); |
| |
| DBG("Address %s", address); |
| } else if (g_str_equal(key, "PrefixLength")) { |
| dbus_message_iter_get_basic(&value, &prefix_length); |
| |
| DBG("prefix length %d", prefix_length); |
| } else if (g_str_equal(key, "DomainNameServers")) { |
| nameservers = extract_nameservers(&value); |
| |
| DBG("Nameservers %s", nameservers); |
| } else if (g_str_equal(key, "Gateway")) { |
| dbus_message_iter_get_basic(&value, &val); |
| |
| gateway = g_strdup(val); |
| |
| DBG("Gateway %s", gateway); |
| } |
| |
| dbus_message_iter_next(&dict); |
| } |
| |
| if (index < 0) |
| goto out; |
| |
| context->ipv6_method = CONNMAN_IPCONFIG_METHOD_AUTO; |
| |
| context->ipv6_address = |
| connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6); |
| 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: |
| if (context->ipv6_nameservers != nameservers) |
| g_free(nameservers); |
| |
| g_free(address); |
| g_free(gateway); |
| } |
| |
| static bool ready_to_create_device(struct modem_data *modem) |
| { |
| /* |
| * There are three different modem types which behave slightly |
| * different: |
| * - GSM modems will expose the SIM interface then the |
| * CM interface. |
| * - CDMA modems will expose CM first and sometime later |
| * a unique serial number. |
| * |
| * This functions tests if we have the necessary information gathered |
| * before we are able to create a device. |
| */ |
| |
| if (modem->device) |
| return false; |
| |
| if (modem->imsi || modem->serial) |
| return true; |
| |
| return false; |
| } |
| |
| static void create_device(struct modem_data *modem) |
| { |
| struct connman_device *device; |
| char *ident = NULL; |
| |
| DBG("%s", modem->path); |
| |
| if (modem->imsi) |
| ident = modem->imsi; |
| else if (modem->serial) |
| ident = modem->serial; |
| |
| if (!connman_dbus_validate_ident(ident)) |
| ident = connman_dbus_encode_string(ident); |
| else |
| ident = g_strdup(ident); |
| |
| device = connman_device_create("ofono", CONNMAN_DEVICE_TYPE_CELLULAR); |
| if (!device) |
| goto out; |
| |
| DBG("device %p", device); |
| |
| connman_device_set_ident(device, ident); |
| |
| connman_device_set_string(device, "Path", modem->path); |
| |
| connman_device_set_data(device, modem); |
| |
| if (connman_device_register(device) < 0) { |
| connman_error("Failed to register cellular device"); |
| connman_device_unref(device); |
| goto out; |
| } |
| |
| modem->device = device; |
| |
| connman_device_set_powered(modem->device, modem->online); |
| out: |
| g_free(ident); |
| } |
| |
| static void destroy_device(struct modem_data *modem) |
| { |
| DBG("%s", modem->path); |
| |
| connman_device_set_powered(modem->device, false); |
| |
| if (modem->network) { |
| connman_device_remove_network(modem->device, modem->network); |
| connman_network_unref(modem->network); |
| modem->network = NULL; |
| } |
| |
| connman_device_unregister(modem->device); |
| connman_device_unref(modem->device); |
| |
| modem->device = NULL; |
| } |
| |
| static void add_network(struct modem_data *modem) |
| { |
| const char *group; |
| |
| DBG("%s", modem->path); |
| |
| if (modem->network) |
| return; |
| |
| modem->network = connman_network_create(modem->context->path, |
| CONNMAN_NETWORK_TYPE_CELLULAR); |
| if (!modem->network) |
| return; |
| |
| DBG("network %p", modem->network); |
| |
| connman_network_set_data(modem->network, modem); |
| |
| connman_network_set_string(modem->network, "Path", |
| modem->context->path); |
| |
| if (modem->name) |
| connman_network_set_name(modem->network, modem->name); |
| else |
| connman_network_set_name(modem->network, ""); |
| |
| connman_network_set_strength(modem->network, modem->strength); |
| |
| group = get_ident(modem->context->path); |
| connman_network_set_group(modem->network, group); |
| |
| 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; |
| } |
| } |
| |
| static void remove_network(struct modem_data *modem) |
| { |
| DBG("%s", modem->path); |
| |
| if (!modem->network) |
| return; |
| |
| DBG("network %p", modem->network); |
| |
| connman_device_remove_network(modem->device, modem->network); |
| connman_network_unref(modem->network); |
| modem->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 = NULL; |
| struct network_context *context = NULL; |
| dbus_bool_t active = FALSE; |
| const char *ip_protocol = NULL; |
| |
| DBG("%s context path %s", modem->path, context_path); |
| |
| if (modem->context) { |
| /* |
| * 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) |
| return -ENOMEM; |
| |
| while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key; |
| |
| dbus_message_iter_recurse(dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "Type")) { |
| 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")) { |
| DBG("%s Settings", modem->path); |
| |
| extract_ipv4_settings(&value, context); |
| } 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")) { |
| 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) |
| modem->valid_apn = true; |
| else |
| modem->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); |
| return -EINVAL; |
| } |
| |
| if (ip_protocol) |
| set_context_ipconfig(context, ip_protocol); |
| |
| modem->context = context; |
| modem->active = active; |
| |
| g_hash_table_replace(context_hash, g_strdup(context_path), modem); |
| |
| if (modem->valid_apn && modem->attached && |
| has_interface(modem->interfaces, |
| OFONO_API_NETREG)) { |
| add_network(modem); |
| } |
| |
| return 0; |
| } |
| |
| static void remove_cm_context(struct modem_data *modem, |
| const char *context_path) |
| { |
| if (!modem->context) |
| return; |
| |
| if (modem->network) |
| 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) |
| remove_network(modem); |
| } |
| |
| static gboolean context_changed(DBusConnection *conn, |
| DBusMessage *message, |
| void *user_data) |
| { |
| const char *context_path = dbus_message_get_path(message); |
| struct modem_data *modem = NULL; |
| DBusMessageIter iter, value; |
| const char *key; |
| |
| DBG("context_path %s", context_path); |
| |
| modem = g_hash_table_lookup(context_hash, context_path); |
| if (!modem) |
| 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); |
| |
| /* |
| * oFono guarantees the ordering of Settings and |
| * Active. Settings will always be send before Active = True. |
| * That means we don't have to order here. |
| */ |
| if (g_str_equal(key, "Settings")) { |
| DBG("%s Settings", modem->path); |
| |
| extract_ipv4_settings(&value, modem->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")) { |
| dbus_bool_t active; |
| |
| dbus_message_iter_get_basic(&value, &active); |
| modem->active = active; |
| |
| DBG("%s Active %d", modem->path, modem->active); |
| |
| if (modem->active) |
| set_connected(modem); |
| else |
| set_disconnected(modem); |
| } 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) { |
| modem->valid_apn = true; |
| |
| if (modem->network) |
| return TRUE; |
| |
| if (!modem->attached) |
| return TRUE; |
| |
| if (!has_interface(modem->interfaces, |
| OFONO_API_NETREG)) |
| return TRUE; |
| |
| add_network(modem); |
| |
| if (modem->active) |
| set_connected(modem); |
| } else { |
| modem->valid_apn = false; |
| |
| if (!modem->network) |
| return TRUE; |
| |
| remove_network(modem); |
| } |
| |
| } 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(modem->context, ip_protocol); |
| } |
| |
| return TRUE; |
| } |
| |
| static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data) |
| { |
| struct modem_data *modem = user_data; |
| DBusMessageIter array, dict, entry, value; |
| DBusMessage *reply; |
| DBusError error; |
| |
| DBG("%s", modem->path); |
| |
| modem->call_get_contexts = NULL; |
| |
| reply = dbus_pending_call_steal_reply(call); |
| |
| dbus_error_init(&error); |
| |
| if (dbus_set_error_from_message(&error, reply)) { |
| connman_error("%s", error.message); |
| dbus_error_free(&error); |
| goto done; |
| } |
| |
| if (!dbus_message_iter_init(reply, &array)) |
| goto done; |
| |
| if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) |
| goto done; |
| |
| dbus_message_iter_recurse(&array, &dict); |
| |
| while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) { |
| const char *context_path; |
| |
| dbus_message_iter_recurse(&dict, &entry); |
| dbus_message_iter_get_basic(&entry, &context_path); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (add_cm_context(modem, context_path, &value) == 0) |
| break; |
| |
| dbus_message_iter_next(&dict); |
| } |
| |
| done: |
| dbus_message_unref(reply); |
| |
| dbus_pending_call_unref(call); |
| } |
| |
| static int cm_get_contexts(struct modem_data *modem) |
| { |
| DBusMessage *message; |
| |
| DBG("%s", modem->path); |
| |
| if (modem->call_get_contexts) |
| return -EBUSY; |
| |
| message = dbus_message_new_method_call(OFONO_SERVICE, modem->path, |
| OFONO_CM_INTERFACE, GET_CONTEXTS); |
| if (!message) |
| return -ENOMEM; |
| |
| 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) { |
| connman_error("D-Bus connection not available"); |
| dbus_message_unref(message); |
| return -EINVAL; |
| } |
| |
| dbus_pending_call_set_notify(modem->call_get_contexts, |
| cm_get_contexts_reply, |
| modem, NULL); |
| |
| dbus_message_unref(message); |
| |
| return -EINPROGRESS; |
| } |
| |
| static gboolean cm_context_added(DBusConnection *conn, |
| DBusMessage *message, |
| void *user_data) |
| { |
| const char *path = dbus_message_get_path(message); |
| char *context_path; |
| struct modem_data *modem; |
| DBusMessageIter iter, properties; |
| |
| DBG("%s", path); |
| |
| modem = g_hash_table_lookup(modem_hash, path); |
| if (!modem) |
| return TRUE; |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &context_path); |
| |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &properties); |
| |
| if (add_cm_context(modem, context_path, &properties) != 0) |
| return TRUE; |
| |
| return TRUE; |
| } |
| |
| static gboolean cm_context_removed(DBusConnection *conn, |
| DBusMessage *message, |
| void *user_data) |
| { |
| const char *path = dbus_message_get_path(message); |
| const char *context_path; |
| struct modem_data *modem; |
| DBusMessageIter iter; |
| |
| DBG("context path %s", path); |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &context_path); |
| |
| modem = g_hash_table_lookup(context_hash, context_path); |
| if (!modem) |
| return TRUE; |
| |
| remove_cm_context(modem, context_path); |
| |
| 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) |
| 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) |
| 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) |
| 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_status(struct modem_data *modem, |
| DBusMessageIter *value) |
| { |
| char *status; |
| bool roaming; |
| |
| 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->network) |
| 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) |
| 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; |
| } |
| |
| static void netreg_properties_reply(struct modem_data *modem, |
| DBusMessageIter *dict) |
| { |
| DBG("%s", modem->path); |
| |
| while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key; |
| |
| dbus_message_iter_recurse(dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "Name")) |
| 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) { |
| /* |
| * netgreg_get_properties() was issued after we got |
| * cm_get_contexts_reply() where we create the |
| * context. Though before we got the |
| * netreg_properties_reply the context was removed |
| * again. Therefore we have to skip the network |
| * creation. |
| */ |
| return; |
| } |
| |
| if (modem->valid_apn) |
| add_network(modem); |
| |
| if (modem->active) |
| set_connected(modem); |
| } |
| |
| static int netreg_get_properties(struct modem_data *modem) |
| { |
| return get_properties(modem->path, OFONO_NETREG_INTERFACE, |
| netreg_properties_reply, modem); |
| } |
| |
| static void add_cdma_network(struct modem_data *modem) |
| { |
| /* 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) |
| modem->context = network_context_alloc(modem->path); |
| |
| if (!modem->name) |
| modem->name = g_strdup("CDMA Network"); |
| |
| add_network(modem); |
| |
| if (modem->cdma_cm_powered) |
| set_connected(modem); |
| } |
| |
| static gboolean cdma_netreg_changed(DBusConnection *conn, |
| DBusMessage *message, |
| void *user_data) |
| { |
| const char *path = dbus_message_get_path(message); |
| struct modem_data *modem; |
| DBusMessageIter iter, value; |
| const char *key; |
| |
| DBG(""); |
| |
| modem = g_hash_table_lookup(modem_hash, path); |
| if (!modem) |
| return TRUE; |
| |
| if (modem->ignore) |
| return TRUE; |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &key); |
| |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &value); |
| |
| if (g_str_equal(key, "Name")) |
| netreg_update_name(modem, &value); |
| else if (g_str_equal(key, "Strength")) |
| netreg_update_strength(modem, &value); |
| else if (g_str_equal(key, "DataStrength")) |
| netreg_update_datastrength(modem, &value); |
| else if (g_str_equal(key, "Status")) |
| netreg_update_status(modem, &value); |
| |
| if (modem->registered) |
| add_cdma_network(modem); |
| else |
| remove_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")) |
| 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_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) |
| { |
| dbus_bool_t attached; |
| |
| dbus_message_iter_get_basic(value, &attached); |
| modem->attached = attached; |
| |
| DBG("%s Attached %d", modem->path, modem->attached); |
| |
| if (!modem->attached) { |
| remove_network(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) |
| { |
| 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->network) |
| return; |
| |
| if (modem->cdma_cm_powered) |
| 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 *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->network) |
| cdma_netreg_get_properties(modem); |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &key); |
| |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &value); |
| |
| if (g_str_equal(key, "Powered")) |
| cdma_cm_update_powered(modem, &value); |
| if (g_str_equal(key, "Settings")) |
| cdma_cm_update_settings(modem, &value); |
| |
| return TRUE; |
| } |
| |
| static void cm_properties_reply(struct modem_data *modem, DBusMessageIter *dict) |
| { |
| DBG("%s", modem->path); |
| |
| while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key; |
| |
| dbus_message_iter_recurse(dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "Attached")) |
| cm_update_attached(modem, &value); |
| else if (g_str_equal(key, "Powered")) |
| cm_update_powered(modem, &value); |
| |
| dbus_message_iter_next(dict); |
| } |
| } |
| |
| static int cm_get_properties(struct modem_data *modem) |
| { |
| return get_properties(modem->path, OFONO_CM_INTERFACE, |
| cm_properties_reply, modem); |
| } |
| |
| static void cdma_cm_properties_reply(struct modem_data *modem, |
| DBusMessageIter *dict) |
| { |
| DBG("%s", modem->path); |
| |
| if (modem->online) |
| cdma_netreg_get_properties(modem); |
| |
| while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key; |
| |
| dbus_message_iter_recurse(dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "Powered")) |
| cdma_cm_update_powered(modem, &value); |
| if (g_str_equal(key, "Settings")) |
| cdma_cm_update_settings(modem, &value); |
| |
| dbus_message_iter_next(dict); |
| } |
| } |
| |
| static int cdma_cm_get_properties(struct modem_data *modem) |
| { |
| return get_properties(modem->path, OFONO_CDMA_CM_INTERFACE, |
| cdma_cm_properties_reply, modem); |
| } |
| |
| static void sim_update_imsi(struct modem_data *modem, |
| DBusMessageIter *value) |
| { |
| char *imsi; |
| |
| dbus_message_iter_get_basic(value, &imsi); |
| |
| DBG("%s imsi %s", modem->path, imsi); |
| |
| g_free(modem->imsi); |
| modem->imsi = g_strdup(imsi); |
| } |
| |
| static gboolean sim_changed(DBusConnection *conn, DBusMessage *message, |
| void *user_data) |
| { |
| const char *path = dbus_message_get_path(message); |
| struct modem_data *modem; |
| DBusMessageIter iter, value; |
| const char *key; |
| |
| modem = g_hash_table_lookup(modem_hash, path); |
| if (!modem) |
| return TRUE; |
| |
| if (modem->ignore) |
| return TRUE; |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &key); |
| |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &value); |
| |
| if (g_str_equal(key, "SubscriberIdentity")) { |
| sim_update_imsi(modem, &value); |
| |
| if (!ready_to_create_device(modem)) |
| return TRUE; |
| |
| /* |
| * This is a GSM modem. Create the device and |
| * register it at the core. Enabling (setting |
| * it online is done through the |
| * modem_enable() callback. |
| */ |
| create_device(modem); |
| } |
| |
| return TRUE; |
| } |
| |
| static void sim_properties_reply(struct modem_data *modem, |
| DBusMessageIter *dict) |
| { |
| DBG("%s", modem->path); |
| |
| while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key; |
| |
| dbus_message_iter_recurse(dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "SubscriberIdentity")) { |
| sim_update_imsi(modem, &value); |
| |
| if (!ready_to_create_device(modem)) |
| return; |
| |
| /* |
| * This is a GSM modem. Create the device and |
| * register it at the core. Enabling (setting |
| * it online is done through the |
| * modem_enable() callback. |
| */ |
| create_device(modem); |
| |
| if (!modem->online) |
| return; |
| |
| /* |
| * The modem is already online and we have the CM interface. |
| * There will be no interface update and therefore our |
| * state machine will not go to next step. We have to |
| * trigger it from here. |
| */ |
| if (has_interface(modem->interfaces, OFONO_API_CM)) { |
| cm_get_properties(modem); |
| cm_get_contexts(modem); |
| } |
| return; |
| } |
| |
| dbus_message_iter_next(dict); |
| } |
| } |
| |
| static int sim_get_properties(struct modem_data *modem) |
| { |
| return get_properties(modem->path, OFONO_SIM_INTERFACE, |
| sim_properties_reply, modem); |
| } |
| |
| static bool api_added(uint8_t old_iface, uint8_t new_iface, |
| enum ofono_api api) |
| { |
| if (!has_interface(old_iface, api) && |
| has_interface(new_iface, api)) { |
| DBG("%s added", api2string(api)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool api_removed(uint8_t old_iface, uint8_t new_iface, |
| enum ofono_api api) |
| { |
| if (has_interface(old_iface, api) && |
| !has_interface(new_iface, api)) { |
| DBG("%s removed", api2string(api)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static void modem_update_interfaces(struct modem_data *modem, |
| uint8_t old_ifaces, |
| uint8_t new_ifaces) |
| { |
| DBG("%s", modem->path); |
| |
| if (api_added(old_ifaces, new_ifaces, OFONO_API_SIM)) { |
| if (!modem->imsi && |
| !modem->set_powered) { |
| /* |
| * Only use do GetProperties() when |
| * device has not been powered up. |
| */ |
| sim_get_properties(modem); |
| } |
| } |
| |
| if (api_added(old_ifaces, new_ifaces, OFONO_API_CM)) { |
| if (modem->device) { |
| cm_get_properties(modem); |
| cm_get_contexts(modem); |
| } |
| } |
| |
| if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_CM)) { |
| if (ready_to_create_device(modem)) { |
| create_device(modem); |
| if (modem->registered) |
| add_cdma_network(modem); |
| } |
| |
| if (modem->device) |
| cdma_cm_get_properties(modem); |
| } |
| |
| if (api_added(old_ifaces, new_ifaces, OFONO_API_NETREG)) { |
| if (modem->attached) |
| netreg_get_properties(modem); |
| } |
| |
| if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG)) |
| cdma_netreg_get_properties(modem); |
| |
| if (api_removed(old_ifaces, new_ifaces, OFONO_API_CM)) { |
| if (modem->call_get_contexts) { |
| DBG("cancelling pending GetContexts call"); |
| dbus_pending_call_cancel(modem->call_get_contexts); |
| dbus_pending_call_unref(modem->call_get_contexts); |
| modem->call_get_contexts = NULL; |
| } |
| if (modem->context) { |
| DBG("removing context %s", modem->context->path); |
| remove_cm_context(modem, modem->context->path); |
| } |
| } |
| |
| if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_CM)) |
| remove_cm_context(modem, modem->context->path); |
| |
| if (api_removed(old_ifaces, new_ifaces, OFONO_API_NETREG)) |
| remove_network(modem); |
| |
| if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG)) |
| remove_network(modem); |
| } |
| |
| static gboolean modem_changed(DBusConnection *conn, DBusMessage *message, |
| void *user_data) |
| { |
| const char *path = dbus_message_get_path(message); |
| struct modem_data *modem; |
| DBusMessageIter iter, value; |
| const char *key; |
| |
| modem = g_hash_table_lookup(modem_hash, path); |
| if (!modem) |
| return TRUE; |
| |
| if (modem->ignore) |
| return TRUE; |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &key); |
| |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &value); |
| |
| if (g_str_equal(key, "Powered")) { |
| dbus_bool_t powered; |
| |
| dbus_message_iter_get_basic(&value, &powered); |
| modem->powered = powered; |
| |
| DBG("%s Powered %d", modem->path, modem->powered); |
| |
| if (!modem->powered) |
| modem_set_powered(modem, TRUE); |
| } else if (g_str_equal(key, "Online")) { |
| dbus_bool_t online; |
| |
| dbus_message_iter_get_basic(&value, &online); |
| modem->online = online; |
| |
| DBG("%s Online %d", modem->path, modem->online); |
| |
| if (!modem->device) |
| return TRUE; |
| |
| connman_device_set_powered(modem->device, modem->online); |
| } else if (g_str_equal(key, "Interfaces")) { |
| uint8_t interfaces; |
| |
| interfaces = extract_interfaces(&value); |
| |
| if (interfaces == modem->interfaces) |
| return TRUE; |
| |
| DBG("%s Interfaces 0x%02x", modem->path, interfaces); |
| |
| modem_update_interfaces(modem, modem->interfaces, interfaces); |
| |
| modem->interfaces = interfaces; |
| } else if (g_str_equal(key, "Serial")) { |
| char *serial; |
| |
| dbus_message_iter_get_basic(&value, &serial); |
| |
| g_free(modem->serial); |
| modem->serial = g_strdup(serial); |
| |
| DBG("%s Serial %s", modem->path, modem->serial); |
| |
| if (has_interface(modem->interfaces, |
| OFONO_API_CDMA_CM)) { |
| if (ready_to_create_device(modem)) { |
| create_device(modem); |
| if (modem->registered) |
| add_cdma_network(modem); |
| } |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| static void add_modem(const char *path, DBusMessageIter *prop) |
| { |
| struct modem_data *modem; |
| |
| DBG("%s", path); |
| |
| modem = g_hash_table_lookup(modem_hash, path); |
| if (modem) { |
| /* |
| * When oFono powers up we ask for the modems and oFono is |
| * reporting with modem_added signal the modems. Only |
| * handle them once. |
| */ |
| return; |
| } |
| |
| modem = g_try_new0(struct modem_data, 1); |
| if (!modem) |
| return; |
| |
| modem->path = g_strdup(path); |
| |
| g_hash_table_insert(modem_hash, g_strdup(path), modem); |
| |
| while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry, value; |
| const char *key; |
| |
| dbus_message_iter_recurse(prop, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| dbus_message_iter_recurse(&entry, &value); |
| |
| if (g_str_equal(key, "Powered")) { |
| dbus_bool_t powered; |
| |
| dbus_message_iter_get_basic(&value, &powered); |
| modem->powered = powered; |
| |
| DBG("%s Powered %d", modem->path, modem->powered); |
| } else if (g_str_equal(key, "Online")) { |
| dbus_bool_t online; |
| |
| dbus_message_iter_get_basic(&value, &online); |
| modem->online = online; |
| |
| DBG("%s Online %d", modem->path, modem->online); |
| } else if (g_str_equal(key, "Interfaces")) { |
| modem->interfaces = extract_interfaces(&value); |
| |
| DBG("%s Interfaces 0x%02x", modem->path, |
| modem->interfaces); |
| } else if (g_str_equal(key, "Serial")) { |
| char *serial; |
| |
| dbus_message_iter_get_basic(&value, &serial); |
| modem->serial = g_strdup(serial); |
| |
| DBG("%s Serial %s", modem->path, modem->serial); |
| } else if (g_str_equal(key, "Type")) { |
| char *type; |
| |
| dbus_message_iter_get_basic(&value, &type); |
| |
| DBG("%s Type %s", modem->path, type); |
| if (g_strcmp0(type, "hardware") != 0) { |
| DBG("%s Ignore this modem", modem->path); |
| modem->ignore = true; |
| } |
| } |
| |
| dbus_message_iter_next(prop); |
| } |
| |
| if (modem->ignore) |
| return; |
| |
| if (!modem->powered) { |
| modem_set_powered(modem, TRUE); |
| return; |
| } |
| |
| modem_update_interfaces(modem, 0, modem->interfaces); |
| } |
| |
| static void modem_power_down(gpointer key, gpointer value, gpointer user_data) |
| { |
| struct modem_data *modem = value; |
| |
| DBG("%s", modem->path); |
| |
| if (modem->ignore) |
| return; |
| |
| modem_set_powered(modem, FALSE); |
| } |
| |
| static void remove_modem(gpointer data) |
| { |
| struct modem_data *modem = data; |
| |
| DBG("%s", modem->path); |
| |
| if (modem->call_set_property) { |
| dbus_pending_call_cancel(modem->call_set_property); |
| dbus_pending_call_unref(modem->call_set_property); |
| } |
| |
| if (modem->call_get_properties) { |
| dbus_pending_call_cancel(modem->call_get_properties); |
| dbus_pending_call_unref(modem->call_get_properties); |
| } |
| |
| if (modem->call_get_contexts) { |
| dbus_pending_call_cancel(modem->call_get_contexts); |
| dbus_pending_call_unref(modem->call_get_contexts); |
| } |
| |
| if (modem->device) |
| destroy_device(modem); |
| |
| if (modem->context) |
| remove_cm_context(modem, modem->context->path); |
| |
| g_free(modem->serial); |
| g_free(modem->name); |
| g_free(modem->imsi); |
| g_free(modem->path); |
| |
| g_free(modem); |
| } |
| |
| static gboolean modem_added(DBusConnection *conn, |
| DBusMessage *message, void *user_data) |
| { |
| DBusMessageIter iter, properties; |
| const char *path; |
| |
| DBG(""); |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &path); |
| |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &properties); |
| |
| add_modem(path, &properties); |
| |
| return TRUE; |
| } |
| |
| static gboolean modem_removed(DBusConnection *conn, |
| DBusMessage *message, void *user_data) |
| { |
| DBusMessageIter iter; |
| const char *path; |
| |
| DBG(""); |
| |
| if (!dbus_message_iter_init(message, &iter)) |
| return TRUE; |
| |
| dbus_message_iter_get_basic(&iter, &path); |
| |
| g_hash_table_remove(modem_hash, path); |
| |
| return TRUE; |
| } |
| |
| static void manager_get_modems_reply(DBusPendingCall *call, void *user_data) |
| { |
| DBusMessage *reply; |
| DBusError error; |
| DBusMessageIter array, dict; |
| |
| DBG(""); |
| |
| reply = dbus_pending_call_steal_reply(call); |
| |
| dbus_error_init(&error); |
| |
| if (dbus_set_error_from_message(&error, reply)) { |
| connman_error("%s", error.message); |
| dbus_error_free(&error); |
| goto done; |
| } |
| |
| if (!dbus_message_iter_init(reply, &array)) |
| goto done; |
| |
| dbus_message_iter_recurse(&array, &dict); |
| |
| while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) { |
| DBusMessageIter value, properties; |
| const char *path; |
| |
| dbus_message_iter_recurse(&dict, &value); |
| dbus_message_iter_get_basic(&value, &path); |
| |
| dbus_message_iter_next(&value); |
| dbus_message_iter_recurse(&value, &properties); |
| |
| add_modem(path, &properties); |
| |
| dbus_message_iter_next(&dict); |
| } |
| |
| done: |
| dbus_message_unref(reply); |
| |
| dbus_pending_call_unref(call); |
| } |
| |
| static int manager_get_modems(void) |
| { |
| DBusMessage *message; |
| DBusPendingCall *call; |
| |
| DBG(""); |
| |
| message = dbus_message_new_method_call(OFONO_SERVICE, "/", |
| OFONO_MANAGER_INTERFACE, GET_MODEMS); |
| if (!message) |
| return -ENOMEM; |
| |
| if (!dbus_connection_send_with_reply(connection, message, |
| &call, TIMEOUT)) { |
| connman_error("Failed to call GetModems()"); |
| dbus_message_unref(message); |
| return -EINVAL; |
| } |
| |
| if (!call) { |
| connman_error("D-Bus connection not available"); |
| dbus_message_unref(message); |
| return -EINVAL; |
| } |
| |
| dbus_pending_call_set_notify(call, manager_get_modems_reply, |
| NULL, NULL); |
| |
| dbus_message_unref(message); |
| |
| return -EINPROGRESS; |
| } |
| |
| static void ofono_connect(DBusConnection *conn, void *user_data) |
| { |
| DBG(""); |
| |
| modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal, |
| g_free, remove_modem); |
| if (!modem_hash) |
| return; |
| |
| context_hash = g_hash_table_new_full(g_str_hash, g_str_equal, |
| g_free, NULL); |
| if (!context_hash) { |
| g_hash_table_destroy(modem_hash); |
| return; |
| } |
| |
| manager_get_modems(); |
| } |
| |
| static void ofono_disconnect(DBusConnection *conn, void *user_data) |
| { |
| DBG(""); |
| |
| if (!modem_hash || !context_hash) |
| return; |
| |
| g_hash_table_destroy(modem_hash); |
| modem_hash = NULL; |
| |
| g_hash_table_destroy(context_hash); |
| context_hash = NULL; |
| } |
| |
| static int network_probe(struct connman_network *network) |
| { |
| struct modem_data *modem = connman_network_get_data(network); |
| |
| DBG("%s network %p", modem->path, network); |
| |
| return 0; |
| } |
| |
| static void network_remove(struct connman_network *network) |
| { |
| struct modem_data *modem = connman_network_get_data(network); |
| |
| DBG("%s network %p", modem->path, network); |
| } |
| |
| static int network_connect(struct connman_network *network) |
| { |
| struct modem_data *modem = connman_network_get_data(network); |
| |
| DBG("%s network %p", modem->path, network); |
| |
| if (has_interface(modem->interfaces, OFONO_API_CM)) |
| return context_set_active(modem, 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, bool user_initiated) |
| { |
| struct modem_data *modem = connman_network_get_data(network); |
| |
| DBG("%s network %p", modem->path, network); |
| |
| if (has_interface(modem->interfaces, OFONO_API_CM)) |
| return context_set_active(modem, 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 = "cellular", |
| .type = CONNMAN_NETWORK_TYPE_CELLULAR, |
| .probe = network_probe, |
| .remove = network_remove, |
| .connect = network_connect, |
| .disconnect = network_disconnect, |
| }; |
| |
| static int modem_probe(struct connman_device *device) |
| { |
| struct modem_data *modem = connman_device_get_data(device); |
| |
| DBG("%s device %p", modem->path, device); |
| |
| return 0; |
| } |
| |
| static void modem_remove(struct connman_device *device) |
| { |
| struct modem_data *modem = connman_device_get_data(device); |
| |
| DBG("%s device %p", modem->path, device); |
| } |
| |
| static int modem_enable(struct connman_device *device) |
| { |
| struct modem_data *modem = connman_device_get_data(device); |
| |
| DBG("%s device %p", modem->path, device); |
| |
| if (modem->online) |
| return 0; |
| |
| return modem_set_online(modem, TRUE); |
| } |
| |
| static int modem_disable(struct connman_device *device) |
| { |
| struct modem_data *modem = connman_device_get_data(device); |
| |
| DBG("%s device %p", modem->path, device); |
| |
| if (!modem->online) |
| return 0; |
| |
| return modem_set_online(modem, FALSE); |
| } |
| |
| static struct connman_device_driver modem_driver = { |
| .name = "modem", |
| .type = CONNMAN_DEVICE_TYPE_CELLULAR, |
| .probe = modem_probe, |
| .remove = modem_remove, |
| .enable = modem_enable, |
| .disable = modem_disable, |
| }; |
| |
| 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; |
| static guint modem_watch; |
| static guint cm_watch; |
| static guint sim_watch; |
| static guint context_added_watch; |
| static guint context_removed_watch; |
| static guint netreg_watch; |
| static guint context_watch; |
| static guint cdma_cm_watch; |
| static guint cdma_netreg_watch; |
| |
| static int ofono_init(void) |
| { |
| int err; |
| |
| DBG(""); |
| |
| connection = connman_dbus_get_connection(); |
| 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, OFONO_SERVICE, |
| NULL, OFONO_MANAGER_INTERFACE, |
| MODEM_ADDED, |
| modem_added, |
| NULL, NULL); |
| |
| modem_removed_watch = g_dbus_add_signal_watch(connection, |
| OFONO_SERVICE, NULL, |
| OFONO_MANAGER_INTERFACE, |
| MODEM_REMOVED, |
| modem_removed, |
| NULL, NULL); |
| |
| modem_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL, |
| OFONO_MODEM_INTERFACE, |
| PROPERTY_CHANGED, |
| modem_changed, |
| NULL, NULL); |
| |
| 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, OFONO_SERVICE, NULL, |
| OFONO_SIM_INTERFACE, |
| PROPERTY_CHANGED, |
| sim_changed, |
| 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, |
| OFONO_SERVICE, NULL, |
| OFONO_CM_INTERFACE, |
| CONTEXT_REMOVED, |
| cm_context_removed, |
| NULL, NULL); |
| |
| 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, 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 || |
| cdma_cm_watch == 0 || cdma_netreg_watch == 0) { |
| err = -EIO; |
| goto remove; |
| } |
| |
| err = connman_network_driver_register(&network_driver); |
| if (err < 0) |
| goto remove; |
| |
| err = connman_device_driver_register(&modem_driver); |
| if (err < 0) { |
| connman_network_driver_unregister(&network_driver); |
| 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); |
| g_dbus_remove_watch(connection, context_added_watch); |
| g_dbus_remove_watch(connection, sim_watch); |
| g_dbus_remove_watch(connection, cm_watch); |
| g_dbus_remove_watch(connection, modem_watch); |
| g_dbus_remove_watch(connection, modem_removed_watch); |
| g_dbus_remove_watch(connection, modem_added_watch); |
| g_dbus_remove_watch(connection, watch); |
| dbus_connection_unref(connection); |
| |
| return err; |
| } |
| |
| static void ofono_exit(void) |
| { |
| DBG(""); |
| |
| if (modem_hash) { |
| /* |
| * We should propably wait for the SetProperty() reply |
| * message, because ... |
| */ |
| g_hash_table_foreach(modem_hash, modem_power_down, NULL); |
| |
| /* |
| * ... here we will cancel the call. |
| */ |
| g_hash_table_destroy(modem_hash); |
| modem_hash = NULL; |
| } |
| |
| if (context_hash) { |
| g_hash_table_destroy(context_hash); |
| context_hash = NULL; |
| } |
| |
| connman_technology_driver_unregister(&tech_driver); |
| connman_device_driver_unregister(&modem_driver); |
| connman_network_driver_unregister(&network_driver); |
| |
| g_dbus_remove_watch(connection, cdma_netreg_watch); |
| g_dbus_remove_watch(connection, cdma_cm_watch); |
| g_dbus_remove_watch(connection, netreg_watch); |
| g_dbus_remove_watch(connection, context_watch); |
| g_dbus_remove_watch(connection, context_removed_watch); |
| g_dbus_remove_watch(connection, context_added_watch); |
| g_dbus_remove_watch(connection, sim_watch); |
| g_dbus_remove_watch(connection, cm_watch); |
| g_dbus_remove_watch(connection, modem_watch); |
| g_dbus_remove_watch(connection, modem_added_watch); |
| g_dbus_remove_watch(connection, modem_removed_watch); |
| g_dbus_remove_watch(connection, watch); |
| |
| dbus_connection_unref(connection); |
| } |
| |
| CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION, |
| CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit) |