| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * 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 <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <gdbus.h> |
| |
| #include <connman/assert.h> |
| #include <connman/inet.h> |
| |
| #include "connman.h" |
| |
| #define _DBG_DEVICE(fmt, arg...) DBG(DBG_DEVICE, fmt, ## arg) |
| |
| static DBusConnection *connection = NULL; |
| |
| struct connman_device { |
| struct connman_element element; /* base class */ |
| enum connman_device_type type; |
| enum connman_device_mode mode; |
| connman_bool_t registered; |
| connman_bool_t offlinemode; |
| connman_bool_t blocked; |
| connman_bool_t powered; |
| connman_bool_t powered_pending; |
| connman_bool_t powered_persistent; |
| connman_bool_t carrier; |
| connman_bool_t scanning; |
| connman_bool_t connected; |
| connman_bool_t reconnect; |
| struct connman_rtnl rtnl; /* RTNL watch for dev rename */ |
| connman_uint16_t scan_interval; /* long scan interval (secs) */ |
| connman_uint16_t scan_short; /* short scan interval (secs) */ |
| int signal_threshold; /* short/long threshold (dBm) */ |
| char *scan_method; /* background scan method */ |
| char *name; |
| char *node; |
| char *address; /* local address */ |
| char *bssid; /* connected address */ |
| char *interface; |
| char *control; |
| char *ident; |
| int phyindex; |
| uint32_t configmask; /* bitmask of ip configs */ |
| guint scan_timeout; |
| struct { |
| enum connman_device_cellular_family family; |
| char *carrier; |
| char *esn; |
| char *meid; |
| char *imei; |
| char *imsi; |
| char *mdn; |
| char *min; |
| char *model_id; |
| char *manufacturer; |
| char *firmware_revision; |
| char *firmware_image; |
| char *hardware_revision; |
| char *unlock_required; |
| char *selected_network; |
| guint prl_version; |
| guint unlock_retries; |
| connman_bool_t lock_enabled; |
| connman_bool_t roaming_allowed; |
| connman_bool_t scanning_supported; |
| GPtrArray *found_networks; |
| GPtrArray *apn_list; |
| struct connman_network_operator home_provider; |
| connman_bool_t provider_requires_roaming; |
| } cellular; |
| |
| struct connman_device_driver *driver; |
| void *driver_data; |
| |
| struct connman_network *network; /* current connected network */ |
| GHashTable *networks; /* associated networks */ |
| |
| DBusMessage *pending; |
| guint timeout; |
| |
| /* Address and path of an external DBus object backing this device. */ |
| char *external_dbus_connection; |
| char *external_dbus_service; |
| char *external_dbus_object; |
| |
| /* count of times the reverse path filter has been disabled */ |
| int rp_filter_count; |
| }; |
| |
| static const char *kStringDictArraySig = |
| DBUS_TYPE_ARRAY_AS_STRING |
| DBUS_TYPE_ARRAY_AS_STRING |
| DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING |
| DBUS_DICT_ENTRY_END_CHAR_AS_STRING; |
| |
| #define DEFAULT_SCAN_METHOD NULL /* Leave it up to the device plugin */ |
| #define DEFAULT_SCAN_INTERVAL 180 |
| #define DEFAULT_SCAN_SHORT 30 |
| #define DEFAULT_SIGNAL_THRESHOLD -50 |
| |
| #define SCAN_INTERVAL_UNSET ((connman_uint16_t)-1) |
| #define SIGNAL_THRESHOLD_UNSET 9999 |
| |
| static void device_newlink(void *user_data, int index, unsigned short type, |
| const char *ifname, unsigned flags, int change); |
| |
| /* |
| * Scan timer handling. |
| */ |
| |
| /* |
| * Timer callback to do a bgscan request. We assume bgscan operations |
| * while connected are handled elsewhere (e.g. wpa_supplicant for wifi, |
| * bluez for bluetooth). If a device needs explicit scan requests made |
| * while connected the device can schedule it's own timer and use the |
| * connman_device_scan request below. |
| */ |
| static gboolean device_scan(gpointer user_data) |
| { |
| struct connman_device *device = user_data; |
| |
| _DBG_DEVICE("device %p", device); |
| |
| if (device->driver == NULL) { |
| device->scan_timeout = 0; |
| return FALSE; |
| } |
| if (device->connected == FALSE) { |
| /* TODO(sleffler) can driver->scan ever be NULL? */ |
| if (device->driver->scan) |
| device->driver->scan(device); |
| } else { |
| /* |
| * NB: this can happen during suspend/resume so just drop |
| * the request; the timer should be cleared shortly. |
| */ |
| connman_warn("%s: scanning while connected", device->name); |
| } |
| return TRUE; |
| } |
| |
| /* |
| * Stop and disable bgscan work. |
| */ |
| static void bgscan_disable(struct connman_device *device) |
| { |
| if (device->scan_timeout != 0) { |
| g_source_remove(device->scan_timeout); |
| device->scan_timeout = 0; |
| } |
| } |
| |
| /* |
| * Enable bgscan work using the current settings. |
| */ |
| static void bgscan_enable(struct connman_device *device) |
| { |
| int scan_interval = connman_device_bgscan_get_long(device); |
| bgscan_disable(device); |
| |
| if (scan_interval > 0) { |
| guint interval = scan_interval; |
| device->scan_timeout = g_timeout_add_seconds(interval, |
| device_scan, device); |
| } |
| } |
| |
| static const char *type2description(enum connman_device_type type) |
| { |
| switch (type) { |
| case CONNMAN_DEVICE_TYPE_UNKNOWN: |
| case CONNMAN_DEVICE_TYPE_VENDOR: |
| break; |
| case CONNMAN_DEVICE_TYPE_ETHERNET: |
| return "Ethernet"; |
| case CONNMAN_DEVICE_TYPE_WIFI: |
| return "Wireless"; |
| case CONNMAN_DEVICE_TYPE_WIMAX: |
| return "WiMAX"; |
| case CONNMAN_DEVICE_TYPE_BLUETOOTH: |
| return "Bluetooth"; |
| case CONNMAN_DEVICE_TYPE_GPS: |
| return "GPS"; |
| case CONNMAN_DEVICE_TYPE_CELLULAR: |
| return "Cellular"; |
| } |
| |
| return NULL; |
| } |
| |
| static const char *type2string(enum connman_device_type type) |
| { |
| switch (type) { |
| case CONNMAN_DEVICE_TYPE_UNKNOWN: |
| case CONNMAN_DEVICE_TYPE_VENDOR: |
| break; |
| case CONNMAN_DEVICE_TYPE_ETHERNET: |
| return "ethernet"; |
| case CONNMAN_DEVICE_TYPE_WIFI: |
| return "wifi"; |
| case CONNMAN_DEVICE_TYPE_WIMAX: |
| return "wimax"; |
| case CONNMAN_DEVICE_TYPE_BLUETOOTH: |
| return "bluetooth"; |
| case CONNMAN_DEVICE_TYPE_GPS: |
| return "gps"; |
| case CONNMAN_DEVICE_TYPE_CELLULAR: |
| return "cellular"; |
| } |
| return NULL; |
| } |
| |
| static const char *family2string(enum connman_device_cellular_family family) |
| { |
| switch (family) { |
| case CONNMAN_DEVICE_CELLULAR_FAMILY_CDMA: |
| return "CDMA"; |
| case CONNMAN_DEVICE_CELLULAR_FAMILY_GSM: |
| return "GSM"; |
| } |
| return NULL; |
| } |
| |
| enum connman_service_type __connman_device_get_service_type( |
| struct connman_device *device) |
| { |
| enum connman_device_type type = connman_device_get_type(device); |
| |
| switch (type) { |
| case CONNMAN_DEVICE_TYPE_UNKNOWN: |
| case CONNMAN_DEVICE_TYPE_VENDOR: |
| case CONNMAN_DEVICE_TYPE_GPS: |
| break; |
| case CONNMAN_DEVICE_TYPE_ETHERNET: |
| return CONNMAN_SERVICE_TYPE_ETHERNET; |
| case CONNMAN_DEVICE_TYPE_WIFI: |
| return CONNMAN_SERVICE_TYPE_WIFI; |
| case CONNMAN_DEVICE_TYPE_WIMAX: |
| return CONNMAN_SERVICE_TYPE_WIMAX; |
| case CONNMAN_DEVICE_TYPE_BLUETOOTH: |
| return CONNMAN_SERVICE_TYPE_BLUETOOTH; |
| case CONNMAN_DEVICE_TYPE_CELLULAR: |
| return CONNMAN_SERVICE_TYPE_CELLULAR; |
| } |
| |
| return CONNMAN_SERVICE_TYPE_UNKNOWN; |
| } |
| |
| struct connman_ipconfig *connman_device_add_ipconfig( |
| struct connman_device *device, enum connman_ipconfig_type type) |
| { |
| struct connman_ipconfig *ipconfig; |
| gchar name[32]; |
| int ix; |
| |
| /* XXX not unique if config's deleted */ |
| ix = ffs(~(device->configmask)); |
| if (ix == 0) { |
| /* XXX should not happen */ |
| connman_error("%s: too many ipconfigs for %s", __func__, |
| device->name); |
| return NULL; |
| } |
| g_snprintf(name, sizeof(name), "%s_%d", device->ident, ix-1); |
| ipconfig = connman_ipconfig_create(type, name, device, |
| device->element.index, TRUE); |
| if (ipconfig == NULL) { |
| connman_error("%s: failed to create %s ipconfig for %s", |
| __func__, type2string(type), device->name); |
| return NULL; |
| } |
| /* ipconfig's are children of the device */ |
| /* TODO(sleffler) check return value */ |
| connman_ipconfig_register(ipconfig, &device->element); |
| device->configmask |= 1<<(ix-1); |
| |
| __connman_profile_save_device(device); |
| |
| return ipconfig; |
| } |
| |
| /** |
| * set_connected |
| * @device: connman device that is now (dis)connected |
| * @connected: is device connected or disconnected |
| * |
| * Returns: %0 on success |
| */ |
| static int set_connected(struct connman_device *device, |
| connman_bool_t connected) |
| { |
| struct connman_service *service = |
| __connman_service_lookup_from_device(device); |
| enum connman_service_type type = |
| __connman_device_get_service_type(device); |
| |
| if (connected == TRUE) { |
| connman_bool_t was_previously_connected; |
| device->connected = TRUE; |
| bgscan_disable(device); |
| |
| /* kick off L3 configuration */ |
| if (device->configmask == 0) { |
| /* |
| * Auto-add DHCP if no ipconfig records are set up |
| * and the device is backed by a network interface. |
| * |
| * TODO(sleffler) this is policy, perhaps add a |
| * config knob to disable |
| */ |
| connman_device_add_ipconfig(device, |
| CONNMAN_IPCONFIG_TYPE_DHCP); |
| } |
| was_previously_connected = |
| __connman_service_get_connected(service); |
| connman_ipconfig_request(device); |
| /* |
| * We manually ena/clear ipv6 routes and IP addresses because |
| * we rely on the kernel to install them and they are only |
| * cleared if the interface is marked down--which does not |
| * happen in flimflam. |
| */ |
| connman_inet_enable_ipv6(connman_device_get_index(device)); |
| |
| /* |
| * Some ipconfig types immediately mark the service as |
| * connected. We can detect this by checking whether |
| * the service state has changed from not-connected |
| * to connected since before we called |
| * connman_ipconfig_request above. If the the service |
| * was connected as a result of conman_ipconfig_request, |
| * do NOT mark the service back in the "configuring" state. |
| */ |
| if (!(was_previously_connected == FALSE && |
| __connman_service_get_connected(service) == TRUE)) { |
| __connman_service_indicate_state(service, |
| CONNMAN_SERVICE_STATE_CONFIGURATION); |
| } |
| __connman_notifier_connect(type); |
| } else { |
| /* tear down L3 configuration */ |
| connman_inet_disable_ipv6(connman_device_get_index(device)); |
| connman_ipconfig_release(device); |
| |
| device->connected = FALSE; |
| __connman_notifier_disconnect(type); |
| __connman_service_indicate_state(service, |
| CONNMAN_SERVICE_STATE_IDLE); |
| bgscan_enable(device); |
| } |
| return 0; |
| } |
| |
| static int set_carrier(struct connman_device *device, connman_bool_t carrier) |
| { |
| if (carrier == TRUE) |
| __connman_profile_add_device(device); |
| else |
| __connman_profile_remove_device(device); |
| |
| return set_connected(device, carrier); |
| } |
| |
| static connman_bool_t powered_changed(struct connman_device *device) |
| { |
| return connman_dbus_send_property_changed_variant(device->element.path, |
| CONNMAN_DEVICE_INTERFACE, "Powered", |
| DBUS_TYPE_BOOLEAN, &device->powered); |
| } |
| |
| static int set_powered(struct connman_device *device, connman_bool_t powered) |
| { |
| struct connman_device_driver *driver = device->driver; |
| enum connman_service_type type; |
| int err; |
| |
| _DBG_DEVICE("device %p powered %d (currently %d/%d)", |
| device, powered, |
| device->powered, device->powered_pending); |
| |
| if (device->powered_pending == powered) |
| return -EALREADY; |
| |
| if (driver == NULL) |
| return -EINVAL; |
| |
| type = __connman_device_get_service_type(device); |
| |
| if (powered == TRUE) { |
| if (driver->enable != NULL) { |
| device->powered_pending = powered; |
| err = driver->enable(device); |
| if (err == 0) |
| __connman_notifier_enable(type); |
| } else |
| err = -EINVAL; |
| } else { |
| device->powered_pending = powered; |
| device->reconnect = FALSE; |
| bgscan_disable(device); |
| g_hash_table_remove_all(device->networks); |
| set_carrier(device, FALSE); |
| |
| if (driver->disable != NULL) { |
| err = driver->disable(device); |
| if (err == 0) |
| __connman_notifier_disable(type); |
| } else |
| err = -EINVAL; |
| } |
| |
| if (err == 0) { |
| device->powered = powered; |
| if (device->registered == TRUE) |
| powered_changed(device); |
| } else if (err == -EINPROGRESS) { |
| /* Not really an error, do not roll back powered_pending */ |
| } else { |
| /* Roll back powered_pending. No-op in -EINVAL case |
| * because powered_pending is already !powered */ |
| device->powered_pending = !powered; |
| } |
| return err; |
| } |
| |
| /* |
| * Append object paths for each network associated with a device. |
| */ |
| static void append_network_path(gpointer key, gpointer value, |
| gpointer user_data) |
| { |
| struct connman_element *element = value; |
| DBusMessageIter *iter = user_data; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, |
| &element->path); |
| } |
| |
| static void append_networks(DBusMessageIter *dict, void *arg) |
| { |
| struct connman_device *device = arg; |
| |
| g_hash_table_foreach(device->networks, append_network_path, dict); |
| } |
| |
| /* |
| * Append object paths for each ipconfig associated with a device. |
| */ |
| static void append_ipconfig_path(struct connman_ipconfig *ipconfig, void *arg) |
| { |
| const char *str = connman_ipconfig_get_path(ipconfig); |
| DBusMessageIter *iter = arg; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str); |
| } |
| |
| static void append_ipconfig(DBusMessageIter *dict, void *arg) |
| { |
| struct connman_device *device = arg; |
| |
| __connman_ipconfig_foreach(device, append_ipconfig_path, dict); |
| } |
| |
| static void append_unlock_status(DBusMessageIter *dict, void *arg) |
| { |
| struct connman_device *device = arg; |
| if (device->cellular.unlock_required) { |
| connman_dbus_dict_append_basic(dict, "LockType", |
| DBUS_TYPE_STRING, |
| &device->cellular.unlock_required); |
| connman_dbus_dict_append_basic(dict, "RetriesLeft", |
| DBUS_TYPE_UINT32, |
| &device->cellular.unlock_retries); |
| connman_dbus_dict_append_basic(dict, "LockEnabled", |
| DBUS_TYPE_BOOLEAN, |
| &device->cellular.lock_enabled); |
| } |
| } |
| |
| static void append_string_dict_item(gpointer key, gpointer value, gpointer data) |
| { |
| DBusMessageIter *iter = data; |
| connman_dbus_dict_append_string(iter, key, value); |
| } |
| |
| static void append_string_dict_array(DBusMessageIter *iter, gpointer data) |
| { |
| DBusMessageIter array, dict; |
| int i; |
| GPtrArray *arrayptr = (GPtrArray *)data; |
| const char *array_sig = kStringDictArraySig |
| + strlen(DBUS_TYPE_ARRAY_AS_STRING); |
| const char *dict_sig = array_sig |
| + strlen(DBUS_TYPE_ARRAY_AS_STRING); |
| |
| dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, |
| array_sig, &array); |
| for (i = 0; i < arrayptr->len; i++) { |
| GHashTable *props = g_ptr_array_index(arrayptr, i); |
| dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, |
| dict_sig, &dict); |
| g_hash_table_foreach(props, append_string_dict_item, &dict); |
| dbus_message_iter_close_container(&array, &dict); |
| } |
| dbus_message_iter_close_container(iter, &array); |
| } |
| |
| static void append_operator(DBusMessageIter *iter, void *arg) |
| { |
| struct connman_network_operator *op = |
| (struct connman_network_operator *)arg; |
| |
| if (op->name != NULL) |
| connman_dbus_dict_append_string(iter, "name", op->name); |
| if (op->country != NULL) |
| connman_dbus_dict_append_string(iter, "country", op->country); |
| if (op->code != NULL) |
| connman_dbus_dict_append_string(iter, "code", op->code); |
| } |
| |
| /* |
| * Count of the times the reverse path filter has been disabled on all |
| * devices. This is used to control the "all" reverse path filter. |
| */ |
| static int rp_filter_all_count; |
| |
| static void rp_filter_set(const char *interface, connman_bool_t enabled) |
| { |
| /* gcc 4.6.0 still warns on (void) write(...), hence unused */ |
| int fd, cnt __attribute__ ((unused)); |
| char filename[PATH_MAX]; |
| const char *str_value = (enabled == TRUE) ? "1" : "0"; |
| |
| snprintf(filename, sizeof(filename), |
| "/proc/sys/net/ipv4/conf/%s/rp_filter", interface); |
| fd = open(filename, O_WRONLY); |
| if (fd == -1) |
| return; |
| cnt = write(fd, str_value, strlen(str_value)); |
| close(fd); |
| } |
| |
| /** |
| * connman_device_rp_filter_disable: |
| * @device: device structure |
| * |
| * Disable the reverse path filter for this device. Calls to disable |
| * are counted, and only the first call changes the setting. |
| * |
| * If this is the first device to disable the reverse path filter, |
| * disable the reverse path filter globally also. |
| */ |
| void connman_device_rp_filter_disable(struct connman_device *device) |
| { |
| if (device->rp_filter_count == 0) |
| rp_filter_set(device->interface, FALSE); |
| device->rp_filter_count++; |
| |
| if (rp_filter_all_count == 0) |
| rp_filter_set("all", FALSE); |
| rp_filter_all_count++; |
| } |
| |
| /** |
| * connman_device_rp_filter_disable: |
| * @device: device structure |
| * |
| * Enable the reverse path filter for this device. Calls to disable |
| * are counted, and only when the count gets to zero is the filter |
| * enabled. |
| * |
| * If all devices have enabled the reverse path filter, then globally |
| * enable it. |
| */ |
| void connman_device_rp_filter_enable(struct connman_device *device) |
| { |
| device->rp_filter_count--; |
| if (device->rp_filter_count == 0) |
| rp_filter_set(device->interface, TRUE); |
| |
| rp_filter_all_count--; |
| if (rp_filter_all_count == 0) |
| rp_filter_set("all", TRUE); |
| } |
| |
| static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| DBusMessage *reply; |
| DBusMessageIter array, dict; |
| const char *str; |
| int val; |
| |
| _DBG_DEVICE("conn %p", conn); |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| reply = dbus_message_new_method_return(msg); |
| if (reply == NULL) |
| return NULL; |
| |
| dbus_message_iter_init_append(reply, &array); |
| |
| dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, |
| DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING |
| DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); |
| |
| if (device->name != NULL) |
| connman_dbus_dict_append_variant(&dict, "Name", |
| DBUS_TYPE_STRING, &device->name); |
| |
| str = type2string(device->type); |
| if (str != NULL) |
| connman_dbus_dict_append_variant(&dict, "Type", |
| DBUS_TYPE_STRING, &str); |
| |
| if (device->address != NULL) |
| connman_dbus_dict_append_variant(&dict, "Address", |
| DBUS_TYPE_STRING, &device->address); |
| |
| if (device->interface != NULL) |
| connman_dbus_dict_append_variant(&dict, "Interface", |
| DBUS_TYPE_STRING, &device->interface); |
| |
| connman_dbus_dict_append_variant(&dict, "Powered", |
| DBUS_TYPE_BOOLEAN, &device->powered); |
| |
| if (device->driver && device->driver->scan) |
| connman_dbus_dict_append_variant(&dict, "Scanning", |
| DBUS_TYPE_BOOLEAN, &device->scanning); |
| |
| connman_dbus_dict_append_variant(&dict, "Reconnect", |
| DBUS_TYPE_BOOLEAN, &device->reconnect); |
| |
| connman_dbus_dict_append_variant_array(&dict, "IPConfigs", |
| DBUS_TYPE_OBJECT_PATH, append_ipconfig, device); |
| |
| if (device->type == CONNMAN_DEVICE_TYPE_CELLULAR) { |
| str = family2string(device->cellular.family); |
| if (str != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.Family", |
| DBUS_TYPE_STRING, &str); |
| if (device->cellular.carrier != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.Carrier", |
| DBUS_TYPE_STRING, |
| &device->cellular.carrier); |
| if (device->cellular.imsi != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.IMSI", |
| DBUS_TYPE_STRING, |
| &device->cellular.imsi); |
| if (device->cellular.imei != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.IMEI", |
| DBUS_TYPE_STRING, |
| &device->cellular.imei); |
| if (device->cellular.meid != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.MEID", |
| DBUS_TYPE_STRING, |
| &device->cellular.meid); |
| if (device->cellular.esn != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.ESN", |
| DBUS_TYPE_STRING, |
| &device->cellular.esn); |
| if (device->cellular.mdn != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.MDN", |
| DBUS_TYPE_STRING, |
| &device->cellular.mdn); |
| if (device->cellular.min != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.MIN", |
| DBUS_TYPE_STRING, |
| &device->cellular.min); |
| if (device->cellular.model_id != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.ModelID", |
| DBUS_TYPE_STRING, |
| &device->cellular.model_id); |
| if (device->cellular.manufacturer != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.Manufacturer", |
| DBUS_TYPE_STRING, |
| &device->cellular.manufacturer); |
| if (device->cellular.firmware_revision != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.FirmwareRevision", |
| DBUS_TYPE_STRING, |
| &device->cellular.firmware_revision); |
| if (device->cellular.firmware_image != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.FirmwareImageName", |
| DBUS_TYPE_STRING, |
| &device->cellular.firmware_image); |
| if (device->cellular.hardware_revision != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.HardwareRevision", |
| DBUS_TYPE_STRING, |
| &device->cellular.hardware_revision); |
| if (device->cellular.home_provider.name != NULL) |
| connman_dbus_dict_append_string_dict( |
| &dict, "Cellular.HomeProvider", |
| append_operator, |
| &device->cellular.home_provider); |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.PRLVersion", |
| DBUS_TYPE_UINT16, |
| &device->cellular.prl_version); |
| connman_dbus_dict_append_dict(&dict, "Cellular.SIMLockStatus", |
| append_unlock_status, device); |
| if (device->cellular.selected_network != NULL) |
| connman_dbus_dict_append_variant( |
| &dict, "Cellular.SelectedNetwork", |
| DBUS_TYPE_STRING, |
| &device->cellular.selected_network); |
| if (device->cellular.found_networks != NULL) |
| connman_dbus_dict_append_variant_container(&dict, |
| "Cellular.FoundNetworks", |
| kStringDictArraySig, |
| append_string_dict_array, |
| device->cellular.found_networks); |
| if (device->cellular.apn_list != NULL) |
| connman_dbus_dict_append_variant_container(&dict, |
| "Cellular.APNList", |
| kStringDictArraySig, |
| append_string_dict_array, |
| device->cellular.apn_list); |
| connman_dbus_dict_append_variant(&dict, "Cellular.AllowRoaming", |
| DBUS_TYPE_BOOLEAN, |
| &device->cellular.roaming_allowed); |
| connman_dbus_dict_append_variant( |
| &dict, |
| "Cellular.SupportNetworkScan", |
| DBUS_TYPE_BOOLEAN, |
| &device->cellular.scanning_supported); |
| connman_dbus_dict_append_variant(&dict, "Cellular.ProviderRequiresRoaming", |
| DBUS_TYPE_BOOLEAN, |
| &device->cellular.provider_requires_roaming); |
| } |
| |
| if (device->external_dbus_connection != NULL) |
| connman_dbus_dict_append_variant(&dict, "DBus.Connection", |
| DBUS_TYPE_STRING, &device->external_dbus_connection); |
| if (device->external_dbus_service != NULL) |
| connman_dbus_dict_append_variant(&dict, "DBus.Service", |
| DBUS_TYPE_STRING, &device->external_dbus_service); |
| if (device->external_dbus_object != NULL) |
| connman_dbus_dict_append_basic(&dict, "DBus.Object", |
| DBUS_TYPE_OBJECT_PATH, &device->external_dbus_object); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| break; |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| val = connman_device_bgscan_get_long(device); |
| if (val > 0) |
| connman_dbus_dict_append_variant(&dict, "ScanInterval", |
| DBUS_TYPE_UINT16, &val); |
| str = connman_device_bgscan_get_method(device); |
| if (str != NULL) |
| connman_dbus_dict_append_variant(&dict, "BgscanMethod", |
| DBUS_TYPE_STRING, &str); |
| val = connman_device_bgscan_get_short(device); |
| if (val > 0) |
| connman_dbus_dict_append_variant(&dict, |
| "BgscanShortInterval", DBUS_TYPE_UINT16, &val); |
| val = connman_device_bgscan_get_signal_threshold(device); |
| if (val != 0) |
| connman_dbus_dict_append_variant(&dict, |
| "BgscanSignalThreshold", DBUS_TYPE_INT32, &val); |
| |
| connman_dbus_dict_append_variant_array(&dict, "Networks", |
| DBUS_TYPE_OBJECT_PATH, append_networks, device); |
| break; |
| } |
| |
| dbus_message_iter_close_container(&array, &dict); |
| |
| return reply; |
| } |
| |
| static gboolean powered_timeout(gpointer user_data) |
| { |
| struct connman_device *device = user_data; |
| |
| _DBG_DEVICE("device %p", device); |
| |
| device->timeout = 0; |
| |
| if (device->pending != NULL) { |
| DBusMessage *reply; |
| |
| reply = __connman_error_operation_timeout(device->pending); |
| if (reply != NULL) |
| g_dbus_send_message(connection, reply); |
| |
| dbus_message_unref(device->pending); |
| device->pending = NULL; |
| } |
| return FALSE; |
| } |
| |
| static int check_bgscan_parameter(struct connman_device *device, |
| DBusMessageIter *value, int type) |
| { |
| if (device->mode == CONNMAN_DEVICE_MODE_UNKNOWN || |
| device->mode == CONNMAN_DEVICE_MODE_TRANSPORT_IP) |
| return 0; |
| return (dbus_message_iter_get_arg_type(value) == type); |
| } |
| |
| static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| DBusMessageIter iter, value; |
| const char *name; |
| |
| _DBG_DEVICE("conn %p", conn); |
| |
| if (dbus_message_iter_init(msg, &iter) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| dbus_message_iter_get_basic(&iter, &name); |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &value); |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| if (g_str_equal(name, "Powered") == TRUE) { |
| connman_bool_t powered; |
| int type, err; |
| |
| type = dbus_message_iter_get_arg_type(&value); |
| if (type != DBUS_TYPE_BOOLEAN) |
| return __connman_error_invalid_arguments(msg); |
| |
| dbus_message_iter_get_basic(&value, &powered); |
| |
| device->powered_persistent = powered; |
| |
| __connman_profile_save_device(device); |
| |
| if (device->powered == powered) |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| |
| if (device->pending != NULL) |
| return __connman_error_in_progress(msg); |
| |
| err = set_powered(device, powered); |
| if (err < 0) { |
| if (err != -EINPROGRESS) |
| return __connman_error_failed(msg, -err); |
| |
| device->pending = dbus_message_ref(msg); |
| |
| device->timeout = g_timeout_add_seconds(15, |
| powered_timeout, device); |
| |
| return NULL; |
| } |
| } else if (g_str_equal(name, "ScanInterval") == TRUE) { |
| connman_uint16_t interval; |
| |
| if (!check_bgscan_parameter(device, &value, DBUS_TYPE_UINT16)) |
| return __connman_error_invalid_arguments(msg); |
| dbus_message_iter_get_basic(&value, &interval); |
| |
| if (device->scan_interval != interval) { |
| device->scan_interval = interval; |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| |
| if (device->scan_timeout != 0) |
| bgscan_enable(device); |
| } |
| } else if (g_str_equal(name, "BgscanMethod") == TRUE) { |
| const char *method; |
| |
| if (!check_bgscan_parameter(device, &value, DBUS_TYPE_STRING)) |
| return __connman_error_invalid_arguments(msg); |
| dbus_message_iter_get_basic(&value, &method); |
| |
| if (g_strcmp0(method, device->scan_method) != 0) { |
| g_free(device->scan_method); |
| device->scan_method = g_strdup(method); |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| } |
| } else if (g_str_equal(name, "BgscanShortInterval") == TRUE) { |
| connman_uint16_t interval; |
| |
| if (!check_bgscan_parameter(device, &value, DBUS_TYPE_UINT16)) |
| return __connman_error_invalid_arguments(msg); |
| dbus_message_iter_get_basic(&value, &interval); |
| |
| if (device->scan_short != interval) { |
| device->scan_short = interval; |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| } |
| } else if (g_str_equal(name, "BgscanSignalThreshold") == TRUE) { |
| int threshold; |
| |
| if (!check_bgscan_parameter(device, &value, DBUS_TYPE_INT32)) |
| return __connman_error_invalid_arguments(msg); |
| dbus_message_iter_get_basic(&value, &threshold); |
| |
| if (device->signal_threshold != threshold) { |
| device->signal_threshold = threshold; |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| } |
| } else if (g_str_equal(name, "Cellular.AllowRoaming") && |
| device->type == CONNMAN_DEVICE_TYPE_CELLULAR) { |
| int type; |
| dbus_bool_t allow; |
| type = dbus_message_iter_get_arg_type(&value); |
| if (type != DBUS_TYPE_BOOLEAN) |
| return __connman_error_invalid_arguments(msg); |
| dbus_message_iter_get_basic(&value, &allow); |
| if (allow != device->cellular.roaming_allowed) { |
| device->cellular.roaming_allowed = allow; |
| __connman_profile_save_device(device); |
| connman_dbus_send_property_changed_variant(device->element.path, |
| CONNMAN_DEVICE_INTERFACE, |
| "Cellular.AllowRoaming", |
| DBUS_TYPE_BOOLEAN, &allow); |
| } |
| if (!connman_device_roaming_allowed(device) && |
| device->network != NULL) { |
| struct connman_service *service = |
| connman_service_lookup_from_network(device->network); |
| if (service != NULL) |
| __connman_service_disconnect_if_roaming(service); |
| } |
| } else |
| return __connman_error_invalid_property(msg); |
| |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| } |
| |
| static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| DBusMessageIter iter; |
| const char *name; |
| |
| _DBG_DEVICE("conn %p", conn); |
| |
| if (dbus_message_iter_init(msg, &iter) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| dbus_message_iter_get_basic(&iter, &name); |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| if (g_str_equal(name, "ScanInterval") == TRUE) { |
| device->scan_interval = SCAN_INTERVAL_UNSET; |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| if (device->scan_timeout != 0) |
| bgscan_enable(device); |
| } else if (g_str_equal(name, "BgscanMethod") == TRUE) { |
| g_free(device->scan_method); |
| device->scan_method = NULL; |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| } else if (g_str_equal(name, "BgscanShortInterval") == TRUE) { |
| device->scan_short = SCAN_INTERVAL_UNSET; |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| } else if (g_str_equal(name, "BgscanSignalThreshold") == TRUE) { |
| device->signal_threshold = SIGNAL_THRESHOLD_UNSET; |
| __connman_profile_save_device(device); |
| connman_element_update(&device->element); |
| } else |
| return __connman_error_invalid_property(msg); |
| |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| } |
| |
| static DBusMessage *propose_scan(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| int err; |
| |
| _DBG_DEVICE("conn %p", conn); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| return __connman_error_not_supported(msg); |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| break; |
| } |
| |
| err = connman_device_scan(device); |
| if (err < 0) |
| return __connman_error_failed(msg, -err); |
| |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| } |
| |
| static DBusMessage *add_ipconfig(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| struct connman_ipconfig *ipconfig; |
| enum connman_ipconfig_type type; |
| const char *str; |
| |
| _DBG_DEVICE("conn %p", conn); |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str, |
| DBUS_TYPE_INVALID) == FALSE) |
| return NULL; |
| |
| if (__connman_ipconfig_parse_type(str, &type) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| ipconfig = connman_device_add_ipconfig(device, type); |
| if (ipconfig == NULL) |
| return __connman_error_invalid_arguments(msg); /* XXX */ |
| |
| str = connman_ipconfig_get_path(ipconfig); |
| return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &str, |
| DBUS_TYPE_INVALID); |
| } |
| |
| static const char *error_to_dbus_error_name(enum connman_element_error error) |
| { |
| const char *errname; |
| |
| if (error == CONNMAN_ELEMENT_ERROR_PIN_REQUIRED) |
| errname = CONNMAN_ERROR_INTERFACE ".PinRequired"; |
| else if (error == CONNMAN_ELEMENT_ERROR_PIN_BLOCKED) |
| errname = CONNMAN_ERROR_INTERFACE ".PinBlocked"; |
| else if (error == CONNMAN_ELEMENT_ERROR_INCORRECT_PIN) |
| errname = CONNMAN_ERROR_INTERFACE ".IncorrectPin"; |
| else |
| errname = CONNMAN_ERROR_INTERFACE ".InternalError"; |
| return errname; |
| } |
| |
| static void pin_method_callback(enum connman_element_error error, |
| struct connman_dbus_method_callback *cb, |
| void *data) |
| { |
| DBusError dberror; |
| |
| _DBG_DEVICE("%s error: %d", connman_dbus_callback_method_name(cb), error); |
| dbus_error_init(&dberror); |
| if (error != CONNMAN_ELEMENT_ERROR_NO_ERROR) { |
| dbus_set_error_const(&dberror, |
| error_to_dbus_error_name(error), |
| "PIN error"); |
| } |
| connman_dbus_callback_send_reply(cb, &dberror); |
| dbus_error_free(&dberror); |
| connman_dbus_free_callback(cb); |
| } |
| |
| static DBusMessage *require_pin(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| struct connman_dbus_method_callback *callback; |
| const char *pin; |
| connman_bool_t require; |
| int err; |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| if (dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &pin, |
| DBUS_TYPE_BOOLEAN, &require, |
| DBUS_TYPE_INVALID) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| if (device->driver == NULL || device->driver->require_pin == NULL) |
| return __connman_error_not_supported(msg); |
| |
| callback = connman_dbus_callback_new(pin_method_callback, msg, NULL); |
| err = device->driver->require_pin(device, pin, require, callback); |
| if (err < 0) { |
| connman_dbus_free_callback(callback); |
| return __connman_error_failed(msg, -err); |
| } |
| return NULL; |
| } |
| |
| static DBusMessage *enter_pin(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| struct connman_dbus_method_callback *callback; |
| const char *pin; |
| int err; |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| if (dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &pin, |
| DBUS_TYPE_INVALID) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| if (device->driver == NULL || device->driver->enter_pin == NULL) |
| return __connman_error_not_supported(msg); |
| |
| callback = connman_dbus_callback_new(pin_method_callback, msg, NULL); |
| err = device->driver->enter_pin(device, pin, callback); |
| if (err < 0) { |
| connman_dbus_free_callback(callback); |
| return __connman_error_failed(msg, -err); |
| } |
| return NULL; |
| } |
| |
| static DBusMessage *unblock_pin(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| struct connman_dbus_method_callback *callback; |
| const char *unblock_code; |
| const char *pin; |
| int err; |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| if (dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &unblock_code, |
| DBUS_TYPE_STRING, &pin, |
| DBUS_TYPE_INVALID) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| if (device->driver == NULL || device->driver->unblock_pin == NULL) |
| return __connman_error_not_supported(msg); |
| |
| callback = connman_dbus_callback_new(pin_method_callback, msg, NULL); |
| err = device->driver->unblock_pin(device, unblock_code, pin, callback); |
| if (err < 0) { |
| connman_dbus_free_callback(callback); |
| return __connman_error_failed(msg, -err); |
| } |
| return NULL; |
| } |
| |
| static DBusMessage *change_pin(DBusConnection *conn, DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| struct connman_dbus_method_callback *callback; |
| const char *old_pin, *new_pin; |
| int err; |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| if (dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &old_pin, |
| DBUS_TYPE_STRING, &new_pin, |
| DBUS_TYPE_INVALID) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| if (device->driver == NULL || device->driver->change_pin == NULL) |
| return __connman_error_not_supported(msg); |
| |
| callback = connman_dbus_callback_new(pin_method_callback, msg, NULL); |
| err = device->driver->change_pin(device, old_pin, new_pin, callback); |
| if (err < 0) { |
| connman_dbus_free_callback(callback); |
| return __connman_error_failed(msg, -err); |
| } |
| return NULL; |
| } |
| |
| static void registration_callback(enum connman_element_error error, |
| struct connman_dbus_method_callback *cb, |
| void *data) |
| { |
| struct connman_device *device = data; |
| DBusError dberror; |
| |
| _DBG_DEVICE("%s error: %d", connman_dbus_callback_method_name(cb), error); |
| dbus_error_init(&dberror); |
| if (error != CONNMAN_ELEMENT_ERROR_NO_ERROR) { |
| dbus_set_error_const(&dberror, |
| CONNMAN_ERROR_INTERFACE ".RegistrationFailed", |
| "Registration Failed"); |
| g_free(device->cellular.selected_network); |
| device->cellular.selected_network = NULL; |
| } else if (device->cellular.selected_network != NULL) { |
| __connman_profile_save_device(device); |
| } |
| connman_device_unref(device); |
| connman_dbus_callback_send_reply(cb, &dberror); |
| dbus_error_free(&dberror); |
| connman_dbus_free_callback(cb); |
| } |
| |
| static DBusMessage *register_on_network(DBusConnection *conn, |
| DBusMessage *msg, |
| void *data) |
| { |
| struct connman_device *device = data; |
| struct connman_dbus_method_callback *callback; |
| const char *network_id; |
| int err; |
| |
| if (dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &network_id, |
| DBUS_TYPE_INVALID) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| if (device->driver == NULL || device->driver->register_on_network == NULL) |
| return __connman_error_not_supported(msg); |
| |
| callback = connman_dbus_callback_new(registration_callback, msg, |
| connman_device_ref(device)); |
| err = device->driver->register_on_network(device, network_id, callback); |
| if (err < 0) { |
| connman_dbus_free_callback(callback); |
| return __connman_error_failed(msg, -err); |
| } |
| /* Clear any previously saved selected network */ |
| connman_device_clear_selected_network(device); |
| /* Selected network will be persisted if registration succeeds */ |
| if (strlen(network_id) != 0) |
| device->cellular.selected_network = g_strdup(network_id); |
| return NULL; |
| } |
| |
| static GDBusMethodTable device_methods[] = { |
| { "GetProperties", "", "a{sv}", get_properties }, |
| { "SetProperty", "sv", "", set_property, |
| G_DBUS_METHOD_FLAG_ASYNC }, |
| { "ClearProperty", "s", "", clear_property }, |
| { "ProposeScan", "", "", propose_scan }, |
| { "AddIPConfig", "s", "o", add_ipconfig }, |
| { "Register", "s", "", register_on_network, |
| G_DBUS_METHOD_FLAG_ASYNC }, |
| { "RequirePin", "sb", "", require_pin, |
| G_DBUS_METHOD_FLAG_ASYNC }, |
| { "EnterPin", "s", "", enter_pin, |
| G_DBUS_METHOD_FLAG_ASYNC }, |
| { "UnblockPin", "ss", "", unblock_pin, |
| G_DBUS_METHOD_FLAG_ASYNC }, |
| { "ChangePin", "ss", "", change_pin, |
| G_DBUS_METHOD_FLAG_ASYNC }, |
| { }, |
| }; |
| |
| static GDBusSignalTable device_signals[] = { |
| { "PropertyChanged", "sv" }, |
| { }, |
| }; |
| |
| /* NB: needed to match type signature */ |
| static void register_ipconfig(struct connman_ipconfig *ipconfig, void *arg) |
| { |
| connman_ipconfig_register(ipconfig, arg); |
| } |
| |
| static void append_devices(DBusMessageIter *iter, void *arg) |
| { |
| __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, iter); |
| } |
| static void emit_devices_signal(void) |
| { |
| connman_dbus_send_property_changed_array(CONNMAN_MANAGER_PATH, |
| CONNMAN_MANAGER_INTERFACE, "Device", |
| DBUS_TYPE_OBJECT_PATH, append_devices, NULL); |
| } |
| |
| static int register_interface(struct connman_element *element) |
| { |
| struct connman_device *device = element->device; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| if (g_dbus_register_interface(connection, element->path, |
| CONNMAN_DEVICE_INTERFACE, |
| device_methods, device_signals, |
| NULL, device, NULL) == FALSE) { |
| connman_error("Failed to register %s device", element->path); |
| return -EIO; |
| } |
| device->registered = TRUE; |
| emit_devices_signal(); |
| /* register ipconfig records loaded from the profile */ |
| __connman_ipconfig_foreach(device, register_ipconfig, element); |
| return 0; |
| } |
| |
| static void unregister_interface(struct connman_element *element) |
| { |
| struct connman_device *device = element->device; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| device->registered = FALSE; |
| emit_devices_signal(); |
| g_dbus_unregister_interface(connection, element->path, |
| CONNMAN_DEVICE_INTERFACE); |
| } |
| |
| static int setup_device(struct connman_device *device, |
| struct connman_device_driver *driver) |
| { |
| enum connman_service_type type; |
| int err; |
| |
| _DBG_DEVICE("device %p driver %p", device, driver); |
| |
| device->driver = driver; |
| |
| err = register_interface(&device->element); |
| if (err < 0) { |
| if (device->driver->remove) |
| device->driver->remove(device); |
| device->driver = NULL; |
| return err; |
| } |
| |
| type = __connman_device_get_service_type(device); |
| __connman_notifier_register(type); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| break; |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| if (device->carrier == TRUE) |
| __connman_profile_add_device(device); |
| break; |
| } |
| |
| if (device->offlinemode == FALSE && device->powered_persistent == TRUE) |
| __connman_device_enable(device); |
| return 0; |
| } |
| |
| static void probe_driver(struct connman_element *element, gpointer user_data) |
| { |
| struct connman_device_driver *driver = user_data; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| if (element->device == NULL) |
| return; |
| if (element->device->driver != NULL) |
| return; |
| if (driver->type != element->device->type) |
| return; |
| if (driver->probe(element->device) == 0) |
| setup_device(element->device, driver); |
| } |
| |
| /* |
| * Extract the index from the ipconfig name. |
| */ |
| static int parse_ipconfig_index(const char *tag) |
| { |
| const char *cp = strrchr(tag, '_'); |
| return (cp == NULL ? -1 : atoi(cp+1)); |
| } |
| |
| /* |
| * Reclaim state for ipconfig records bound to a device. |
| * We clear the entry from the bitmask and delete the |
| * in-memory copy of the ipconfig record. |
| */ |
| static void remove_ipconfig(struct connman_ipconfig *ipconfig, void *arg) |
| { |
| struct connman_device *device = arg; |
| struct connman_service *service = |
| __connman_service_lookup_from_device(device); |
| connman_bool_t was_connected; |
| int ix; |
| |
| _DBG_DEVICE("device %p ipconfig %p configmask %d", device, ipconfig, |
| device->configmask); |
| ix = parse_ipconfig_index(connman_ipconfig_get_name(ipconfig)); |
| CONNMAN_ASSERT(0 <= ix && ix < 32); |
| device->configmask &= ~(1<<ix); |
| |
| was_connected = __connman_service_get_connecting_or_connected(service); |
| connman_ipconfig_delete(ipconfig); |
| |
| _DBG_DEVICE("was_connected: %d now_connected: %d configmask: %d", |
| was_connected, |
| __connman_service_get_connecting_or_connected(service), |
| device->configmask); |
| if (device->connected == TRUE && was_connected == TRUE && |
| __connman_service_get_connecting_or_connected(service) == FALSE && |
| device->configmask != 0) { |
| /* |
| * Removing this ipconfig caused the service to disconnect |
| * but the underlying device is still connected and other |
| * ipconfigs exist. Restart the configuration process so |
| * the service is connected with the new ipconfig parameters. |
| */ |
| _DBG_DEVICE("reconnecting %p", device); |
| set_connected(device, TRUE); |
| } |
| } |
| |
| /* |
| * Public version of remove_ipconfig. |
| */ |
| void __connman_device_remove_ipconfig(struct connman_device *device, |
| struct connman_ipconfig *ipconfig) |
| { |
| remove_ipconfig(ipconfig, device); |
| } |
| |
| static void remove_device(struct connman_device *device) |
| { |
| enum connman_service_type type; |
| |
| _DBG_DEVICE("device %p", device); |
| |
| __connman_device_disable(device); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| break; |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| __connman_profile_remove_device(device); |
| break; |
| } |
| |
| __connman_ipconfig_foreach(device, remove_ipconfig, device); |
| |
| type = __connman_device_get_service_type(device); |
| __connman_notifier_unregister(type); |
| |
| unregister_interface(&device->element); |
| |
| /* NB: callers all verify device->driver is not NULL */ |
| if (device->driver->remove != NULL) |
| device->driver->remove(device); |
| device->driver = NULL; |
| } |
| |
| static void remove_driver(struct connman_element *element, gpointer user_data) |
| { |
| struct connman_device_driver *driver = user_data; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| if (element->device != NULL && element->device->driver == driver) |
| remove_device(element->device); |
| } |
| |
| connman_bool_t __connman_device_has_driver(struct connman_device *device) |
| { |
| return (device == NULL || device->driver == NULL) ? |
| FALSE : device->registered; |
| } |
| |
| static GSList *driver_list = NULL; |
| |
| static gint compare_priority(gconstpointer a, gconstpointer b) |
| { |
| const struct connman_device_driver *driver1 = a; |
| const struct connman_device_driver *driver2 = b; |
| |
| return driver2->priority - driver1->priority; |
| } |
| |
| /** |
| * connman_device_driver_register: |
| * @driver: device driver definition |
| * |
| * Register a new device driver |
| * |
| * Returns: %0 on success |
| */ |
| int connman_device_driver_register(struct connman_device_driver *driver) |
| { |
| _DBG_DEVICE("driver %p name %s", driver, driver->name); |
| |
| /* TODO(sleffler) check return value */ |
| driver_list = g_slist_insert_sorted(driver_list, driver, |
| compare_priority); |
| __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, |
| probe_driver, driver); |
| return 0; |
| } |
| |
| /** |
| * connman_device_driver_unregister: |
| * @driver: device driver definition |
| * |
| * Remove a previously registered device driver |
| */ |
| void connman_device_driver_unregister(struct connman_device_driver *driver) |
| { |
| _DBG_DEVICE("driver %p name %s", driver, driver->name); |
| |
| driver_list = g_slist_remove(driver_list, driver); |
| __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, |
| remove_driver, driver); |
| } |
| |
| static void unregister_network(gpointer data) |
| { |
| struct connman_network *network = data; |
| |
| _DBG_DEVICE("network %p", network); |
| |
| connman_element_unregister((struct connman_element *) network); |
| |
| connman_network_unref(network); |
| } |
| |
| static void device_destruct(struct connman_element *element) |
| { |
| struct connman_device *device = element->device; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| if (device->timeout > 0) { |
| g_source_remove(device->timeout); |
| device->timeout = 0; |
| } |
| if (device->pending != NULL) { |
| dbus_message_unref(device->pending); |
| device->pending = NULL; |
| } |
| |
| /* NB: tear down rtnl monitoring */ |
| connman_device_set_index(device, -1); |
| |
| g_free(device->ident); |
| g_free(device->node); |
| g_free(device->name); |
| g_free(device->address); |
| g_free(device->control); |
| g_free(device->interface); |
| g_free(device->cellular.carrier); |
| g_free(device->cellular.meid); |
| g_free(device->cellular.imei); |
| g_free(device->cellular.imsi); |
| g_free(device->cellular.esn); |
| g_free(device->cellular.mdn); |
| g_free(device->cellular.min); |
| g_free(device->cellular.manufacturer); |
| g_free(device->cellular.model_id); |
| g_free(device->cellular.hardware_revision); |
| g_free(device->cellular.home_provider.code); |
| g_free(device->cellular.home_provider.name); |
| g_free(device->cellular.home_provider.country); |
| g_free(device->cellular.firmware_revision); |
| g_free(device->cellular.firmware_image); |
| g_free(device->cellular.unlock_required); |
| g_free(device->cellular.selected_network); |
| g_free(device->external_dbus_connection); |
| g_free(device->external_dbus_service); |
| g_free(device->external_dbus_object); |
| |
| g_hash_table_destroy(device->networks); |
| if (device->cellular.found_networks != NULL) |
| g_ptr_array_free(device->cellular.found_networks, TRUE); |
| if (device->cellular.apn_list != NULL) |
| g_ptr_array_free(device->cellular.apn_list, TRUE); |
| device->networks = NULL; |
| } |
| |
| /** |
| * connman_device_create: |
| * @node: device node name (for example an address) |
| * @type: device type |
| * |
| * Allocate a new device of given #type and name it #node. |
| * |
| * Returns: a newly-allocated #connman_device structure |
| */ |
| struct connman_device * connman_device_create(const char *node, |
| enum connman_device_type type) |
| { |
| struct connman_device *device; |
| const char *str; |
| |
| _DBG_DEVICE("node %s type %d", node, type); |
| |
| device = g_try_new0(struct connman_device, 1); |
| if (device == NULL) { |
| connman_error("%s: cannot allocate device", __func__); |
| return NULL; |
| } |
| device->networks = g_hash_table_new_full(g_str_hash, g_str_equal, |
| g_free, unregister_network); |
| if (device->networks == NULL) { |
| connman_error("%s: cannot allocate hash table", __func__); |
| g_free(device); |
| return NULL; |
| } |
| |
| _DBG_DEVICE("device %p", device); |
| |
| __connman_element_initialize(&device->element); |
| |
| device->element.name = g_strdup(node); |
| device->element.type = CONNMAN_ELEMENT_TYPE_DEVICE; |
| |
| device->element.device = device; |
| device->element.destruct = device_destruct; |
| |
| str = type2string(type); |
| if (str != NULL) |
| connman_element_set_string(&device->element, |
| CONNMAN_PROPERTY_ID_TYPE, str); |
| |
| device->type = type; |
| device->name = g_strdup(type2description(device->type)); |
| device->mode = CONNMAN_DEVICE_MODE_UNKNOWN; |
| device->powered_persistent = TRUE; |
| device->phyindex = -1; |
| |
| /* setup RTNL device rename support */ |
| RTNL_INIT(&device->rtnl, |
| device->name, |
| CONNMAN_RTNL_PRIORITY_DEFAULT, |
| CONNMAN_RTNL_DEVICE_ANY, /* NB: filled in later */ |
| device); |
| device->rtnl.newlink = device_newlink; |
| |
| switch (type) { |
| case CONNMAN_DEVICE_TYPE_WIFI: |
| device->scan_method = NULL; |
| device->scan_interval = SCAN_INTERVAL_UNSET; |
| device->scan_short = SCAN_INTERVAL_UNSET; |
| device->signal_threshold = SIGNAL_THRESHOLD_UNSET; |
| break; |
| default: |
| device->scan_interval = 0; |
| break; |
| } |
| return device; |
| } |
| |
| /** |
| * connman_device_ref: |
| * @device: device structure |
| * |
| * Increase reference counter of device |
| */ |
| struct connman_device *connman_device_ref(struct connman_device *device) |
| { |
| connman_element_ref(&device->element); |
| return device; |
| } |
| |
| /** |
| * connman_device_unref: |
| * @device: device structure |
| * |
| * Decrease reference counter of device |
| */ |
| void connman_device_unref(struct connman_device *device) |
| { |
| connman_element_unref(&device->element); |
| } |
| |
| const char *__connman_device_get_type(struct connman_device *device) |
| { |
| return type2string(device->type); |
| } |
| |
| /** |
| * connman_device_get_type: |
| * @device: device structure |
| * |
| * Get type of device |
| */ |
| enum connman_device_type connman_device_get_type(struct connman_device *device) |
| { |
| return device->type; |
| } |
| |
| /** |
| * connman_device_get_name: |
| * @device: device structure |
| * |
| * Get unique name of device |
| */ |
| const char *connman_device_get_name(struct connman_device *device) |
| { |
| return device->element.name; |
| } |
| |
| /** |
| * connman_device_get_path: |
| * @device: device structure |
| * |
| * Get path name of device |
| */ |
| const char *connman_device_get_path(struct connman_device *device) |
| { |
| return device->element.path; |
| } |
| |
| static void device_newlink(void *user_data, int index, unsigned short type, |
| const char *ifname, unsigned flags, int change) |
| { |
| struct connman_device *device = user_data; |
| |
| if (g_strcmp0(device->interface, ifname) != 0) { |
| _DBG_DEVICE("device %p ifname %s -> %s", device, |
| device->interface, ifname); |
| connman_device_set_interface(device, ifname, device->control); |
| } |
| } |
| |
| /** |
| * connman_device_set_index: |
| * @device: device structure |
| * @index: index number |
| * |
| * Set index number of device |
| */ |
| void connman_device_set_index(struct connman_device *device, int index) |
| { |
| if (index == device->element.index) |
| return; |
| /* |
| * Setup/teardown the RTNL newlink monitor callback |
| * used to track device renaming. |
| */ |
| if (device->element.index == -1) { |
| device->rtnl.index = index; |
| connman_rtnl_register(&device->rtnl); |
| } |
| device->element.index = index; |
| if (index == -1) { |
| connman_rtnl_unregister(&device->rtnl); |
| device->rtnl.index = CONNMAN_RTNL_DEVICE_ANY; |
| } |
| } |
| |
| /** |
| * connman_device_get_index: |
| * @device: device structure |
| * |
| * Get index number of device |
| */ |
| int connman_device_get_index(struct connman_device *device) |
| { |
| return device->element.index; |
| } |
| |
| int __connman_device_get_phyindex(struct connman_device *device) |
| { |
| return device->phyindex; |
| } |
| |
| void __connman_device_set_phyindex(struct connman_device *device, |
| int phyindex) |
| { |
| device->phyindex = phyindex; |
| } |
| |
| /** |
| * connman_device_get_interface: |
| * @device: device structure |
| * |
| * Get interface name for device |
| */ |
| const char *connman_device_get_interface(struct connman_device *device) |
| { |
| return device->interface; |
| } |
| |
| /** |
| * connman_device_set_interface: |
| * @device: device structure |
| * @interface: interface name |
| * @control: control interface |
| * |
| * Set interface name of device |
| */ |
| void connman_device_set_interface(struct connman_device *device, |
| const char *interface, const char *control) |
| { |
| g_free(device->element.devname); |
| device->element.devname = g_strdup(interface); |
| |
| g_free(device->interface); |
| device->interface = g_strdup(interface); |
| |
| g_free(device->control); |
| device->control = g_strdup(control); |
| |
| if (device->name == NULL) { |
| const char *str = type2description(device->type); |
| if (str != NULL && device->interface != NULL) |
| device->name = g_strdup_printf("%s (%s)", str, |
| device->interface); |
| } |
| } |
| |
| const char *connman_device_get_control(struct connman_device *device) |
| { |
| return device->control; |
| } |
| |
| /** |
| * connman_device_set_ident: |
| * @device: device structure |
| * @ident: unique identifier |
| * |
| * Set unique identifier of device |
| */ |
| void connman_device_set_ident(struct connman_device *device, const char *ident) |
| { |
| g_free(device->ident); |
| device->ident = g_strdup(ident); |
| } |
| |
| const char *__connman_device_get_ident(struct connman_device *device) |
| { |
| return device->ident; |
| } |
| |
| /** |
| * connman_device_set_mode: |
| * @device: device structure |
| * @mode: network mode |
| * |
| * Change network mode of device |
| */ |
| void connman_device_set_mode(struct connman_device *device, |
| enum connman_device_mode mode) |
| { |
| device->mode = mode; |
| } |
| |
| /** |
| * connman_device_get_mode: |
| * @device: device structure |
| * |
| * Get network mode of device |
| */ |
| enum connman_device_mode connman_device_get_mode(struct connman_device *device) |
| { |
| return device->mode; |
| } |
| |
| /** |
| * connman_device_get_selected_network: |
| * @device: device structure |
| * |
| * Get the network ID of the cellular network, if any, on which |
| * the user requested manual registration. |
| */ |
| const char *connman_device_get_selected_network(struct connman_device *device) |
| { |
| return device->cellular.selected_network; |
| } |
| |
| /** |
| * connman_device_clear_selected_network: |
| * @device: device structure |
| * |
| * Forget any previously selected cellular network ID, thus causing |
| * the next network reigstration attempt to be automatic (i.e., on |
| * the home network). |
| */ |
| void connman_device_clear_selected_network(struct connman_device *device) |
| { |
| g_free(device->cellular.selected_network); |
| device->cellular.selected_network = NULL; |
| __connman_profile_save_device(device); |
| } |
| |
| /** |
| * connman_device_set_powered: |
| * @device: device structure |
| * @powered: powered state |
| * |
| * Change power state of device |
| */ |
| int connman_device_set_powered(struct connman_device *device, |
| connman_bool_t powered) |
| { |
| enum connman_service_type type; |
| |
| _DBG_DEVICE("driver %p powered %d", device, powered); |
| |
| if (device->timeout > 0) { |
| g_source_remove(device->timeout); |
| device->timeout = 0; |
| } |
| |
| if (device->pending != NULL) { |
| g_dbus_send_reply(connection, device->pending, |
| DBUS_TYPE_INVALID); |
| dbus_message_unref(device->pending); |
| device->pending = NULL; |
| } |
| |
| bgscan_disable(device); |
| |
| if (device->powered == powered) |
| return -EALREADY; |
| |
| device->powered = powered; |
| device->powered_pending = powered; |
| |
| type = __connman_device_get_service_type(device); |
| |
| if (device->powered == TRUE) |
| __connman_notifier_enable(type); |
| else |
| __connman_notifier_disable(type); |
| |
| if (device->registered == FALSE) |
| return 0; |
| |
| powered_changed(device); |
| |
| if (device->powered == TRUE && |
| device->type != CONNMAN_DEVICE_TYPE_CELLULAR) { |
| connman_device_scan(device); |
| bgscan_enable(device); |
| } |
| |
| return 0; |
| } |
| |
| void connman_device_set_powered_failed(struct connman_device *device, |
| enum connman_element_error error) |
| { |
| _DBG_DEVICE("device %s error %d", device->ident, error); |
| |
| if (device->timeout > 0) { |
| g_source_remove(device->timeout); |
| device->timeout = 0; |
| } |
| |
| if (device->pending != NULL) { |
| const char *errname = error_to_dbus_error_name(error); |
| const char *msg = device->powered_pending ? |
| "Enable Failure" : "Disable Failure"; |
| DBusMessage *reply; |
| reply = g_dbus_create_error(device->pending, errname, msg); |
| g_dbus_send_message(connection, reply); |
| dbus_message_unref(device->pending); |
| device->pending = NULL; |
| } |
| device->powered_pending = device->powered; |
| } |
| |
| int __connman_device_set_blocked(struct connman_device *device, |
| connman_bool_t blocked) |
| { |
| connman_bool_t powered; |
| |
| _DBG_DEVICE("device %p blocked %d", device, blocked); |
| |
| device->blocked = blocked; |
| |
| if (device->offlinemode == TRUE) |
| return 0; |
| |
| if (blocked == FALSE) |
| powered = device->powered_persistent; |
| else |
| powered = FALSE; |
| |
| return set_powered(device, powered); |
| } |
| |
| /** |
| * connman_device_set_carrier: |
| * @device: device structure |
| * @carrier: carrier state |
| * |
| * Change carrier state of device (only for device without scanning) |
| */ |
| int connman_device_set_carrier(struct connman_device *device, |
| connman_bool_t carrier) |
| { |
| _DBG_DEVICE("device %p carrier %d", device, carrier); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| return -EINVAL; |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| break; |
| } |
| |
| if (device->carrier == carrier) |
| return -EALREADY; |
| |
| device->carrier = carrier; |
| |
| return set_carrier(device, device->carrier); |
| } |
| |
| /** |
| * connman_device_bgscan_get_short: |
| * @device: device structure |
| * |
| * Return interval (in seconds) for urgent bgscan's done when we believe |
| * we want to roam (e.g. the signal level drops below the threshold). |
| */ |
| int connman_device_bgscan_get_short(const struct connman_device *device) |
| { |
| if (device->scan_short == SCAN_INTERVAL_UNSET) |
| return DEFAULT_SCAN_SHORT; |
| return device->scan_short; |
| } |
| |
| /** |
| * connman_device_bgscan_get_long: |
| * @device: device structure |
| * |
| * Return interval (in seconds) for slow bgscan's done when we our |
| * connection is satisfactory (e.g. the signal level is above the threshold). |
| */ |
| int connman_device_bgscan_get_long(const struct connman_device *device) |
| { |
| if (device->scan_interval == SCAN_INTERVAL_UNSET) |
| return DEFAULT_SCAN_INTERVAL; |
| return device->scan_interval; |
| } |
| |
| /** |
| * connman_device_bgscan_get_method: |
| * @device: device structure |
| * |
| * Return background scan method (string) to use. This function can return |
| * NULL which means "use default". It is up to the plugin to interpret this |
| * string and determine what to do if it does not recognize the string. |
| */ |
| const char *connman_device_bgscan_get_method(const struct |
| connman_device *device) |
| { |
| if (device->scan_method == NULL) |
| return DEFAULT_SCAN_METHOD; |
| return device->scan_method; |
| } |
| |
| /** |
| * connman_device_bgscan_get_signal_threshold: |
| * @device: device structure |
| * |
| * Return signal level threshold (in dBm) at which we proactively scan |
| * for a new network. |
| */ |
| int connman_device_bgscan_get_signal_threshold(const struct connman_device *device) |
| { |
| if (device->signal_threshold == SIGNAL_THRESHOLD_UNSET) |
| return DEFAULT_SIGNAL_THRESHOLD; |
| return device->signal_threshold; |
| } |
| |
| /** |
| * connman_device_scan: |
| * @device: device structure |
| * |
| * Kick off a scan for the specified device. The device must have |
| * a driver that supports scan requests and must be pwoered up. |
| */ |
| int connman_device_scan(struct connman_device *device) |
| { |
| if (device->driver == NULL || device->driver->scan == NULL) |
| return -EOPNOTSUPP; |
| if (device->powered == FALSE) |
| return -ENOLINK; |
| |
| /* |
| * If bgscan is active push the next scan forward so |
| * we don't fire multiple scans too quickly. |
| */ |
| if (device->scan_timeout != 0) |
| bgscan_enable(device); |
| |
| return device->driver->scan(device); |
| } |
| |
| void connman_device_auto_connect(struct connman_device *device) |
| { |
| __connman_service_device_auto_connect(device); |
| } |
| |
| int __connman_device_enable(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| if (device->driver == NULL || device->driver->enable == NULL) |
| return -EOPNOTSUPP; |
| |
| return set_powered(device, TRUE); |
| } |
| |
| int __connman_device_enable_persistent(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| device->powered_persistent = TRUE; |
| |
| __connman_profile_save_device(device); |
| |
| return __connman_device_enable(device); |
| } |
| |
| int __connman_device_disable(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| if (device->driver == NULL || device->driver->disable == NULL) |
| return -EOPNOTSUPP; |
| if (device->powered == FALSE) |
| return -ENOLINK; |
| |
| return set_powered(device, FALSE); |
| } |
| |
| int __connman_device_disable_persistent(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| device->powered_persistent = FALSE; |
| |
| __connman_profile_save_device(device); |
| |
| return __connman_device_disable(device); |
| } |
| |
| int __connman_device_connect(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| if (device->connected == TRUE) |
| return -EALREADY; |
| |
| if (device->driver != NULL && device->driver->connect != NULL) |
| device->driver->connect(device); |
| /* TODO(sleffler) else connman_device_set_connected(device, TRUE)? */ |
| |
| return 0; |
| } |
| |
| int __connman_device_disconnect(struct connman_device *device) |
| { |
| GHashTableIter iter; |
| gpointer key, value; |
| |
| _DBG_DEVICE("device %p connected %d", device, device->connected); |
| |
| if (device->connected == FALSE) |
| return -EALREADY; |
| |
| g_hash_table_iter_init(&iter, device->networks); |
| while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { |
| struct connman_network *network = value; |
| |
| __connman_network_disconnect(network); |
| } |
| if (device->driver != NULL && device->driver->disconnect != NULL) |
| device->driver->disconnect(device); |
| else |
| connman_device_set_connected(device, FALSE); |
| return 0; |
| } |
| |
| /** |
| * connman_device_set_scanning_state: |
| * @device: device structure |
| * @scanning: scanning state |
| * |
| * Change scanning state of device |
| */ |
| int connman_device_set_scanning_state(struct connman_device *device, |
| connman_bool_t scanning) |
| { |
| _DBG_DEVICE("device %p scanning %d", device, scanning); |
| |
| if (device->driver == NULL || device->driver->scan == NULL) |
| return -EINVAL; |
| if (device->scanning == scanning) |
| return -EALREADY; |
| |
| device->scanning = scanning; |
| |
| /* NB: connman_dbus_send_property_changed logs msg on failure */ |
| (void) connman_dbus_send_property_changed_variant(device->element.path, |
| CONNMAN_DEVICE_INTERFACE, "Scanning", DBUS_TYPE_BOOLEAN, &scanning); |
| return 0; |
| } |
| |
| static void mark_network_unavailable(gpointer key, gpointer value, |
| gpointer user_data) |
| { |
| struct connman_network *network = value; |
| |
| if (connman_network_get_in_use(network) == FALSE) |
| connman_network_set_available(network, FALSE); |
| } |
| |
| void __connman_device_mark_networks_unavailable(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| g_hash_table_foreach(device->networks, mark_network_unavailable, NULL); |
| } |
| |
| static gboolean remove_unavailable_network(gpointer key, gpointer value, |
| gpointer user_data) |
| { |
| struct connman_network *network = value; |
| |
| return (connman_network_get_in_use(network) == FALSE && |
| connman_network_get_available(network) == FALSE); |
| } |
| |
| void __connman_device_cleanup_networks(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| g_hash_table_foreach_remove(device->networks, |
| remove_unavailable_network, NULL); |
| } |
| |
| /** |
| * connman_device_flush_networks: |
| * @device: device structure |
| * |
| * Remove all !connected networks. |
| */ |
| void connman_device_flush_networks(struct connman_device *device) |
| { |
| __connman_device_mark_networks_unavailable(device); |
| __connman_device_cleanup_networks(device); |
| } |
| |
| /** |
| * connman_device_set_scanning: |
| * @device: device structure |
| * @scanning: scanning state (TRUE = started, FALSE = completed) |
| * |
| * Change scanning state of device and manage associated |
| * networks for devices that do not provide incremental |
| * state updates. Specifically, when a scan is started |
| * all networks are marked "unavailable" and when the scan |
| * completes any unavailable networks are cleared. |
| * |
| * When a scan completes and the device is not connected |
| * the autoconnect process is scheduled. |
| */ |
| int connman_device_set_scanning(struct connman_device *device, |
| connman_bool_t scanning) |
| { |
| int err; |
| |
| _DBG_DEVICE("device %p scanning %d", device, scanning); |
| |
| err = connman_device_set_scanning_state(device, scanning); |
| if (err != 0) |
| return err; |
| |
| if (scanning == TRUE) { |
| __connman_device_mark_networks_unavailable(device); |
| } else { |
| __connman_device_cleanup_networks(device); |
| if (device->connected == FALSE) |
| __connman_service_device_auto_connect(device); |
| } |
| return 0; |
| } |
| |
| /** |
| * connman_device_set_connected: |
| * @device: device structure |
| * @connected: connected state |
| * |
| * Change connected state of device |
| */ |
| int |
| connman_device_set_connected(struct connman_device *device, |
| connman_bool_t connected) |
| { |
| _DBG_DEVICE("device %p carrier %d connected %d -> %d", |
| device, device->carrier, device->connected, connected); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| if (device->connected == connected) |
| return -EALREADY; |
| return set_connected(device, connected); |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| /* TODO(sleffler) device->connected == connected? */ |
| if (device->carrier == FALSE) |
| return -ENOTCONN; |
| return set_connected(device, connected); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| /** |
| * connman_device_set_string: |
| * @device: device structure |
| * @key: unique identifier |
| * @value: string value |
| * |
| * Set string value for specific key |
| */ |
| int connman_device_set_string(struct connman_device *device, const char *key, |
| const char *value) |
| { |
| _DBG_DEVICE("device %p key %s value %s", device, key, |
| connman_log_get_masked_value(key, value)); |
| |
| if (g_str_equal(key, "Address") == TRUE) { |
| g_free(device->address); |
| device->address = g_strdup(value); |
| } else if (g_str_equal(key, "Name") == TRUE) { |
| g_free(device->name); |
| device->name = g_strdup(value); |
| } else if (g_str_equal(key, "Node") == TRUE) { |
| g_free(device->node); |
| device->node = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.Carrier") == TRUE) { |
| g_free(device->cellular.carrier); |
| device->cellular.carrier = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.MEID") == TRUE) { |
| g_free(device->cellular.meid); |
| device->cellular.meid = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.IMEI") == TRUE) { |
| g_free(device->cellular.imei); |
| device->cellular.imei = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.IMSI") == TRUE) { |
| g_free(device->cellular.imsi); |
| device->cellular.imsi = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.ESN") == TRUE) { |
| g_free(device->cellular.esn); |
| device->cellular.esn = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.MDN") == TRUE) { |
| g_free(device->cellular.mdn); |
| device->cellular.mdn = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.MIN") == TRUE) { |
| g_free(device->cellular.min); |
| device->cellular.min = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.ModelID") == TRUE) { |
| g_free(device->cellular.model_id); |
| device->cellular.model_id = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.Manufacturer") == TRUE) { |
| g_free(device->cellular.manufacturer); |
| device->cellular.manufacturer = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.FirmwareRevision") == TRUE) { |
| g_free(device->cellular.firmware_revision); |
| device->cellular.firmware_revision = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.FirmwareImageName") == TRUE) { |
| g_free(device->cellular.firmware_image); |
| device->cellular.firmware_image = g_strdup(value); |
| } else if (g_str_equal(key, "Cellular.HardwareRevision") == TRUE) { |
| g_free(device->cellular.hardware_revision); |
| device->cellular.hardware_revision = g_strdup(value); |
| } else if (g_str_equal(key, "DBus.Connection") == TRUE) { |
| g_free(device->external_dbus_connection); |
| device->external_dbus_connection = g_strdup(value); |
| } else if (g_str_equal(key, "DBus.Service") == TRUE) { |
| g_free(device->external_dbus_service); |
| device->external_dbus_service = g_strdup(value); |
| } else if (g_str_equal(key, "DBus.Object") == TRUE) { |
| g_free(device->external_dbus_object); |
| device->external_dbus_object = g_strdup(value); |
| } |
| return connman_element_set_string(&device->element, key, value); |
| } |
| |
| /** |
| * connman_device_get_string: |
| * @device: device structure |
| * @key: unique identifier |
| * |
| * Get string value for specific key |
| */ |
| const char *connman_device_get_string(struct connman_device *device, |
| const char *key) |
| { |
| _DBG_DEVICE("device %p key %s", device, key); |
| |
| if (g_str_equal(key, "Address") == TRUE) |
| return device->address; |
| else if (g_str_equal(key, "Name") == TRUE) |
| return device->name; |
| else if (g_str_equal(key, "Node") == TRUE) |
| return device->node; |
| else if (g_str_equal(key, "Cellular.Carrier") == TRUE) |
| return device->cellular.carrier; |
| else if (g_str_equal(key, "Cellular.MEID") == TRUE) |
| return device->cellular.meid; |
| else if (g_str_equal(key, "Cellular.IMEI") == TRUE) |
| return device->cellular.imei; |
| else if (g_str_equal(key, "Cellular.IMSI") == TRUE) |
| return device->cellular.imsi; |
| else if (g_str_equal(key, "Cellular.ESN") == TRUE) |
| return device->cellular.esn; |
| else if (g_str_equal(key, "Cellular.MDN") == TRUE) |
| return device->cellular.mdn; |
| else if (g_str_equal(key, "Cellular.MIN") == TRUE) |
| return device->cellular.min; |
| else if (g_str_equal(key, "Cellular.ModelID") == TRUE) |
| return device->cellular.model_id; |
| else if (g_str_equal(key, "Cellular.Manufacturer") == TRUE) |
| return device->cellular.manufacturer; |
| else if (g_str_equal(key, "Cellular.FirmwareRevision") == TRUE) |
| return device->cellular.firmware_revision; |
| else if (g_str_equal(key, "Cellular.FirmwareImageName") == TRUE) |
| return device->cellular.firmware_image; |
| else if (g_str_equal(key, "Cellular.HardwareRevision") == TRUE) |
| return device->cellular.hardware_revision; |
| else if (g_str_equal(key, "DBus.Connection") == TRUE) |
| return device->external_dbus_connection; |
| else if (g_str_equal(key, "DBus.Service") == TRUE) |
| return device->external_dbus_service; |
| else if (g_str_equal(key, "DBus.Object") == TRUE) |
| return device->external_dbus_object; |
| return connman_element_get_string(&device->element, key); |
| } |
| |
| void connman_device_set_cellular_family(struct connman_device *device, |
| enum connman_device_cellular_family family) |
| { |
| device->cellular.family = family; |
| } |
| |
| void connman_device_set_prl_version(struct connman_device *device, |
| guint prl_version) |
| { |
| device->cellular.prl_version = prl_version; |
| } |
| |
| void connman_device_set_unlock_properties(struct connman_device *device, |
| const char *unlock_required, |
| guint unlock_retries, |
| gboolean lock_enabled) |
| { |
| g_free(device->cellular.unlock_required); |
| device->cellular.unlock_required = g_strdup(unlock_required); |
| device->cellular.unlock_retries = unlock_retries; |
| device->cellular.lock_enabled = lock_enabled; |
| connman_dbus_send_property_changed_dict(device->element.path, |
| CONNMAN_DEVICE_INTERFACE, |
| "Cellular.SIMLockStatus", |
| append_unlock_status, |
| device); |
| } |
| |
| void connman_device_set_found_networks(struct connman_device *device, |
| GPtrArray *networks) |
| { |
| if (device->cellular.found_networks != NULL) |
| g_ptr_array_free(device->cellular.found_networks, TRUE); |
| device->cellular.found_networks = networks; |
| |
| connman_dbus_send_property_changed_container(device->element.path, |
| CONNMAN_DEVICE_INTERFACE, |
| "Cellular.FoundNetworks", |
| kStringDictArraySig, |
| append_string_dict_array, |
| device->cellular.found_networks); |
| } |
| |
| void connman_device_set_apn_list(struct connman_device *device, |
| GPtrArray *apn_list) |
| { |
| if (device->cellular.apn_list != NULL) |
| g_ptr_array_free(device->cellular.apn_list, TRUE); |
| device->cellular.apn_list = apn_list; |
| |
| connman_dbus_send_property_changed_container(device->element.path, |
| CONNMAN_DEVICE_INTERFACE, |
| "Cellular.APNList", |
| kStringDictArraySig, |
| append_string_dict_array, |
| device->cellular.apn_list); |
| } |
| |
| void connman_device_set_provider_requires_roaming(struct connman_device *device, |
| connman_bool_t requires_roaming) |
| { |
| device->cellular.provider_requires_roaming = requires_roaming; |
| } |
| |
| static void set_offlinemode(struct connman_element *element, gpointer user_data) |
| { |
| struct connman_device *device = element->device; |
| connman_bool_t offlinemode = GPOINTER_TO_UINT(user_data); |
| connman_bool_t powered; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| if (device == NULL) |
| return; |
| |
| device->offlinemode = offlinemode; |
| |
| powered = (offlinemode == TRUE) ? FALSE : TRUE; |
| |
| if (device->powered == powered) |
| return; |
| |
| if (device->powered_persistent == FALSE) |
| powered = FALSE; |
| |
| set_powered(device, powered); |
| } |
| |
| int __connman_device_set_offlinemode(connman_bool_t offlinemode) |
| { |
| _DBG_DEVICE("offlinmode %d", offlinemode); |
| |
| __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, |
| set_offlinemode, GUINT_TO_POINTER(offlinemode)); |
| |
| __connman_notifier_offlinemode(offlinemode); |
| |
| return 0; |
| } |
| |
| |
| /** |
| * __connman_device_set_operator: |
| * @device: device structure |
| * @info: operator structure |
| * |
| * Set operator information structure for the service |
| */ |
| void connman_device_set_home_provider(struct connman_device *device, |
| struct connman_network_operator *op) |
| { |
| g_free(device->cellular.home_provider.name); |
| device->cellular.home_provider.name = g_strdup(op->name); |
| g_free(device->cellular.home_provider.code); |
| device->cellular.home_provider.code = g_strdup(op->code); |
| g_free(device->cellular.home_provider.country); |
| device->cellular.home_provider.country = g_strdup(op->country); |
| } |
| |
| struct connman_network_operator *connman_device_get_home_provider( |
| struct connman_device *device) |
| { |
| if (device->cellular.home_provider.name == NULL) |
| return NULL; |
| return &device->cellular.home_provider; |
| } |
| |
| connman_bool_t connman_device_roaming_allowed(struct connman_device *device) |
| { |
| return device->cellular.roaming_allowed || |
| device->cellular.provider_requires_roaming; |
| } |
| |
| void connman_device_set_scanning_supported(struct connman_device *device, |
| connman_bool_t supported) |
| { |
| device->cellular.scanning_supported = supported; |
| } |
| |
| |
| /** |
| * connman_device_add_network: |
| * @device: device structure |
| * @network: network structure |
| * |
| * Add new network to the device |
| */ |
| int connman_device_add_network(struct connman_device *device, |
| struct connman_network *network) |
| { |
| const char *identifier = connman_network_get_identifier(network); |
| int err; |
| |
| _DBG_DEVICE("device %p network %p", device, network); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| return -EINVAL; |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| break; |
| } |
| |
| __connman_network_set_device(network, device); |
| |
| err = connman_element_register((struct connman_element *) network, |
| &device->element); |
| if (err < 0) { |
| __connman_network_set_device(network, NULL); |
| return err; |
| } |
| |
| g_hash_table_insert(device->networks, g_strdup(identifier), network); |
| |
| return 0; |
| } |
| |
| /** |
| * connman_device_get_network: |
| * @device: device structure |
| * @identifier: network identifier |
| * |
| * Get network for given identifier |
| */ |
| struct connman_network *connman_device_get_network( |
| struct connman_device *device, const char *identifier) |
| { |
| _DBG_DEVICE("device %p identifier %s", device, identifier); |
| |
| return g_hash_table_lookup(device->networks, identifier); |
| } |
| |
| /** |
| * connman_device_remove_network: |
| * @device: device structure |
| * @identifier: network identifier |
| * |
| * Remove network for given identifier |
| */ |
| int connman_device_remove_network(struct connman_device *device, |
| const char *identifier) |
| { |
| _DBG_DEVICE("device %p identifier %s", device, identifier); |
| |
| /* NB: this implicitly calls unregister_network */ |
| g_hash_table_remove(device->networks, identifier); |
| |
| return 0; |
| } |
| |
| struct connman_network *__connman_device_get_current_network( |
| struct connman_device *device) |
| { |
| return device->network; |
| } |
| |
| void __connman_device_set_current_network(struct connman_device *device, |
| struct connman_network *network) |
| { |
| const char *bssid = NULL; |
| |
| _DBG_DEVICE("device %p network %p existing %p", device, |
| network, device->network); |
| |
| if (network != NULL) |
| bssid = connman_network_get_string(network, "Address"); |
| if (device->network == network) { |
| if (network == NULL) |
| return; |
| /* |
| * NB: roaming for old supplicant clobbers the bssid of the |
| * current network so we must compare addresses. |
| */ |
| if (g_strcmp0(device->bssid, bssid) != 0) { |
| connman_info("%s: ROAMING %s -> %s", device->interface, |
| device->bssid, bssid); |
| |
| g_free(device->bssid); |
| device->bssid = g_strdup(bssid); |
| /* NB: this will kick off L3 re-configuration */ |
| set_connected(device, TRUE); |
| } else { |
| /* |
| * Same BSSID; this is what happens when we |
| * re-associate (e.g. due to idle timeout). |
| */ |
| connman_ipconfig_renew(device); |
| } |
| return; |
| } |
| if (network != NULL && device->network != NULL) { |
| /* |
| * Roaming with new supplicant plugin; we get a proper |
| * network handle instead of clobbering the bssid of the |
| * same network object. Same tasks as above, just more |
| * straightforward |
| */ |
| connman_info("%s: ROAMING %s -> %s", device->interface, |
| device->bssid, bssid); |
| |
| connman_network_unref(device->network); |
| device->network = connman_network_ref(network); |
| g_free(device->bssid); |
| device->bssid = g_strdup(bssid); |
| set_connected(device, TRUE); |
| } else { |
| /* |
| * Not roaming; either clear state or setup fresh. |
| */ |
| if (device->network != NULL) |
| connman_network_unref(device->network); |
| |
| if (network != NULL) { |
| device->network = connman_network_ref(network); |
| g_free(device->bssid); |
| device->bssid = g_strdup(bssid); |
| connman_device_set_connected(device, TRUE); |
| } else { |
| g_free(device->bssid); |
| device->bssid = NULL; |
| connman_device_set_connected(device, FALSE); |
| /* |
| * Don't null out the network until after the |
| * preceding call to connman_device_set_connected. |
| * The latter results in the ipconfig being released, |
| * which may remove the default gateway. When that |
| * happens, we want to notify the service associated |
| * with the device, but we will be unable to find it |
| * if the link between device and network has been |
| * broken. |
| */ |
| device->network = NULL; |
| } |
| } |
| } |
| |
| void __connman_device_set_reconnect(struct connman_device *device, |
| connman_bool_t onoff) |
| { |
| device->reconnect = onoff; |
| } |
| |
| connman_bool_t __connman_device_get_reconnect(struct connman_device *device) |
| { |
| return device->reconnect; |
| } |
| |
| /** |
| * connman_device_register: |
| * @device: device structure |
| * |
| * Register device with the system |
| */ |
| int connman_device_register(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| __connman_profile_load_device(device); |
| |
| device->offlinemode = __connman_profile_get_offlinemode(); |
| |
| return connman_element_register(&device->element, NULL); |
| } |
| |
| /** |
| * connman_device_unregister: |
| * @device: device structure |
| * |
| * Unregister device with the system |
| */ |
| void connman_device_unregister(struct connman_device *device) |
| { |
| _DBG_DEVICE("device %p", device); |
| |
| __connman_profile_save_device(device); |
| |
| connman_element_unregister(&device->element); |
| } |
| |
| /** |
| * connman_device_get_data: |
| * @device: device structure |
| * |
| * Get private device data pointer |
| */ |
| void *connman_device_get_data(struct connman_device *device) |
| { |
| return device->driver_data; |
| } |
| |
| /** |
| * connman_device_set_data: |
| * @device: device structure |
| * @data: data pointer |
| * |
| * Set private device data pointer |
| */ |
| void connman_device_set_data(struct connman_device *device, void *data) |
| { |
| device->driver_data = data; |
| } |
| |
| static gboolean match_driver(const struct connman_device *device, |
| const struct connman_device_driver *driver) |
| { |
| return (device->type == driver->type || |
| driver->type == CONNMAN_DEVICE_TYPE_UNKNOWN); |
| } |
| |
| static int device_probe(struct connman_element *element) |
| { |
| struct connman_device *device = element->device; |
| GSList *list; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| if (device == NULL) |
| return -ENODEV; |
| |
| if (device->driver != NULL) |
| return -EALREADY; |
| |
| for (list = driver_list; list; list = list->next) { |
| struct connman_device_driver *driver = list->data; |
| |
| if (match_driver(device, driver) == FALSE) |
| continue; |
| |
| _DBG_DEVICE("driver %p name %s", driver, driver->name); |
| |
| if (driver->probe(device) == 0) |
| return setup_device(device, driver); |
| } |
| return -ENODEV; |
| } |
| |
| static void device_remove(struct connman_element *element) |
| { |
| struct connman_device *device = element->device; |
| |
| _DBG_DEVICE("element %p name %s", element, element->name); |
| |
| if (device != NULL && device->driver != NULL) |
| remove_device(device); |
| } |
| |
| static struct connman_driver device_driver = { |
| .name = "device", |
| .type = CONNMAN_ELEMENT_TYPE_DEVICE, |
| .priority = CONNMAN_DRIVER_PRIORITY_LOW, |
| .probe = device_probe, |
| .remove = device_remove, |
| }; |
| |
| /* |
| * Create any ipconfig records for a device. |
| */ |
| static connman_bool_t device_load_ipconfigs(struct connman_device *device, |
| gsize nconfigs, gchar **configs) |
| { |
| int i; |
| |
| for (i = 0; i < nconfigs; i++) { |
| enum connman_ipconfig_type type; |
| struct connman_ipconfig *ipconfig; |
| char *str; |
| int ix; |
| |
| /* Syntax: <unique-tag>:<method>, e.g. "ipconfig_xxx_1:dhcp" */ |
| str = strchr(configs[i], ':'); |
| if (str == NULL) { |
| connman_error("%s: no Method for %s", __func__, |
| configs[i]); |
| return FALSE; |
| } |
| *str++ = '\0'; |
| if (!__connman_ipconfig_parse_type(str, &type)) { |
| connman_error("%s: unknown Method %s", __func__, str); |
| return FALSE; |
| } |
| ix = parse_ipconfig_index(configs[i]); |
| if (!(0 <= ix && ix < 32)) { |
| connman_error("%s: cannot parse index from ipconfig " |
| "name %s", __func__, configs[i]); |
| return FALSE; |
| } |
| if ((device->configmask & (1<<ix)) != 0) { |
| /* NB: just ignore for backwards compatibility */ |
| connman_warn("%s: ignore duplicate ipconfig record " |
| "with name %s", __func__, configs[i]); |
| continue; |
| } |
| |
| ipconfig = connman_ipconfig_create(type, configs[i], device, |
| device->element.index, FALSE); |
| if (ipconfig == NULL) { |
| connman_error("%s: invalid ipconfig type %s (%d)", |
| __func__, str, type); |
| return FALSE; |
| } |
| device->configmask |= 1<<ix; |
| } |
| return TRUE; |
| } |
| |
| static int device_load(struct connman_device *device, GKeyFile *keyfile) |
| { |
| GError *error = NULL; |
| gchar *identifier; |
| gchar *method; |
| gchar *selected_network; |
| connman_bool_t powered; |
| connman_bool_t roaming_allowed; |
| int val; |
| gchar **configs; |
| gsize nconfigs; |
| int err = 0; |
| |
| _DBG_DEVICE("device %p", device); |
| |
| identifier = g_strdup_printf("device_%s", device->element.name); |
| if (identifier == NULL) { |
| err = -ENOMEM; |
| goto done; |
| } |
| |
| if (g_key_file_has_group(keyfile, identifier) == FALSE) { |
| err = -ESRCH; |
| goto done; |
| } |
| |
| powered = g_key_file_get_boolean(keyfile, identifier, |
| "Powered", &error); |
| if (error == NULL) |
| device->powered_persistent = powered; |
| g_clear_error(&error); |
| |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| break; |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| val = g_key_file_get_integer(keyfile, identifier, |
| "ScanInterval", &error); |
| if (error == NULL && val > 0) |
| device->scan_interval = val; |
| g_clear_error(&error); |
| method = g_key_file_get_string(keyfile, identifier, |
| "BgscanMethod", &error); |
| if (error == NULL && method != NULL) { |
| g_free(device->scan_method); |
| device->scan_method = method; |
| } |
| g_clear_error(&error); |
| val = g_key_file_get_integer(keyfile, identifier, |
| "BgscanShortInterval", &error); |
| if (error == NULL && val > 0) |
| device->scan_short = val; |
| g_clear_error(&error); |
| val = g_key_file_get_integer(keyfile, identifier, |
| "BgscanSignalThreshold", &error); |
| if (error == NULL) |
| device->signal_threshold = val; |
| g_clear_error(&error); |
| roaming_allowed = g_key_file_get_boolean(keyfile, |
| identifier, "Cellular.AllowRoaming", &error); |
| if (error == NULL) |
| device->cellular.roaming_allowed = roaming_allowed; |
| else |
| device->cellular.roaming_allowed = FALSE; |
| g_clear_error(&error); |
| selected_network = g_key_file_get_string(keyfile, identifier, |
| "Cellular.SelectedNetwork", &error); |
| if (error == NULL) { |
| g_free(device->cellular.selected_network); |
| device->cellular.selected_network = selected_network; |
| } else { |
| g_free(selected_network); |
| } |
| g_clear_error(&error); |
| break; |
| } |
| |
| configs = g_key_file_get_string_list(keyfile, identifier, "IPConfigs", |
| &nconfigs, &error); |
| if (error == NULL && configs != NULL) |
| device_load_ipconfigs(device, nconfigs, configs); |
| g_strfreev(configs); |
| g_clear_error(&error); |
| done: |
| g_free(identifier); |
| |
| return err; |
| } |
| |
| static int device_save(struct connman_device *device, GKeyFile *keyfile) |
| { |
| gchar *identifier; |
| const char const *paths[32]; |
| int npaths; |
| int i; |
| |
| _DBG_DEVICE("device %p", device); |
| |
| identifier = g_strdup_printf("device_%s", device->element.name); |
| if (identifier == NULL) |
| goto done; |
| |
| g_key_file_set_boolean(keyfile, identifier, |
| "Powered", device->powered_persistent); |
| switch (device->mode) { |
| case CONNMAN_DEVICE_MODE_UNKNOWN: |
| case CONNMAN_DEVICE_MODE_TRANSPORT_IP: |
| break; |
| case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: |
| case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: |
| if (device->scan_interval != SCAN_INTERVAL_UNSET) |
| g_key_file_set_integer(keyfile, identifier, |
| "ScanInterval", device->scan_interval); |
| else |
| g_key_file_remove_key(keyfile, identifier, |
| "ScanInterval", NULL); |
| if (device->scan_method != NULL) |
| g_key_file_set_string(keyfile, identifier, |
| "BgscanMethod", device->scan_method); |
| else |
| g_key_file_remove_key(keyfile, identifier, |
| "BgscanMethod", NULL); |
| if (device->scan_short != SCAN_INTERVAL_UNSET) |
| g_key_file_set_integer(keyfile, identifier, |
| "BgscanShortInterval", device->scan_short); |
| else |
| g_key_file_remove_key(keyfile, identifier, |
| "BgscanShortInterval", NULL); |
| if (device->signal_threshold != SIGNAL_THRESHOLD_UNSET) |
| g_key_file_set_integer(keyfile, identifier, |
| "BgscanSignalThreshold", device->signal_threshold); |
| else |
| g_key_file_remove_key(keyfile, identifier, |
| "BgscanSignalThreshold", NULL); |
| if (device->type == CONNMAN_DEVICE_TYPE_CELLULAR) { |
| g_key_file_set_boolean(keyfile, identifier, |
| "Cellular.AllowRoaming", |
| device->cellular.roaming_allowed); |
| if (device->cellular.selected_network != NULL) |
| g_key_file_set_string(keyfile, identifier, |
| "Cellular.SelectedNetwork", |
| device->cellular.selected_network); |
| else |
| g_key_file_remove_key(keyfile, identifier, |
| "Cellular.SelectedNetwork", NULL); |
| } |
| break; |
| } |
| |
| __connman_ipconfig_get_keys(device, 32, paths, &npaths); |
| g_key_file_set_string_list(keyfile, identifier, "IPConfigs", |
| paths, npaths); |
| for (i = 0; i < npaths; i++) |
| g_free((gpointer)paths[i]); |
| done: |
| g_free(identifier); |
| |
| return 0; |
| } |
| |
| static struct connman_storage device_storage = { |
| .name = "device", |
| .priority = CONNMAN_STORAGE_PRIORITY_LOW, |
| .device_load = device_load, |
| .device_save = device_save, |
| }; |
| |
| int __connman_device_init(void) |
| { |
| /* TODO(sleffler) check return values */ |
| connection = connman_dbus_get_connection(); |
| if (connman_storage_register(&device_storage) < 0) |
| connman_error("%s: failed to register storage", __func__); |
| return connman_driver_register(&device_driver); |
| } |
| |
| void __connman_device_cleanup(void) |
| { |
| connman_driver_unregister(&device_driver); |
| connman_storage_unregister(&device_storage); |
| dbus_connection_unref(connection); |
| } |