/* * * Connection Manager * * Copyright (C) 2007-2012 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 #endif #include #include #include #include "connman.h" static DBusConnection *connection; static GSList *technology_list = NULL; /* * List of devices with no technology associated with them either because of * no compiled in support or the driver is not yet loaded. */ static GSList *techless_device_list = NULL; static GHashTable *rfkill_list; static connman_bool_t global_offlinemode; struct connman_rfkill { unsigned int index; enum connman_service_type type; connman_bool_t softblock; connman_bool_t hardblock; }; struct connman_technology { int refcount; enum connman_service_type type; char *path; GSList *device_list; connman_bool_t enabled; char *regdom; connman_bool_t connected; connman_bool_t tethering; char *tethering_ident; char *tethering_passphrase; connman_bool_t enable_persistent; /* Save the tech state */ struct connman_technology_driver *driver; void *driver_data; DBusMessage *pending_reply; guint pending_timeout; GSList *scan_pending; connman_bool_t rfkill_driven; connman_bool_t softblocked; connman_bool_t hardblocked; connman_bool_t dbus_registered; }; static GSList *driver_list = NULL; static gint compare_priority(gconstpointer a, gconstpointer b) { const struct connman_technology_driver *driver1 = a; const struct connman_technology_driver *driver2 = b; return driver2->priority - driver1->priority; } static void rfkill_check(gpointer key, gpointer value, gpointer user_data) { struct connman_rfkill *rfkill = value; enum connman_service_type type = GPOINTER_TO_INT(user_data); /* Calling _technology_rfkill_add will update the tech. */ if (rfkill->type == type) __connman_technology_add_rfkill(rfkill->index, type, rfkill->softblock, rfkill->hardblock); } /** * connman_technology_driver_register: * @driver: Technology driver definition * * Register a new technology driver * * Returns: %0 on success */ int connman_technology_driver_register(struct connman_technology_driver *driver) { GSList *list; struct connman_device *device; enum connman_service_type type; DBG("Registering %s driver", driver->name); driver_list = g_slist_insert_sorted(driver_list, driver, compare_priority); if (techless_device_list == NULL) goto check_rfkill; /* * Check for technology less devices if this driver * can service any of them. */ for (list = techless_device_list; list; list = list->next) { device = list->data; type = __connman_device_get_service_type(device); if (type != driver->type) continue; techless_device_list = g_slist_remove(techless_device_list, device); __connman_technology_add_device(device); } check_rfkill: /* Check for orphaned rfkill switches. */ g_hash_table_foreach(rfkill_list, rfkill_check, GINT_TO_POINTER(driver->type)); return 0; } /** * connman_technology_driver_unregister: * @driver: Technology driver definition * * Remove a previously registered technology driver */ void connman_technology_driver_unregister(struct connman_technology_driver *driver) { GSList *list; struct connman_technology *technology; DBG("Unregistering driver %p name %s", driver, driver->name); for (list = technology_list; list; list = list->next) { technology = list->data; if (technology->driver == NULL) continue; if (technology->type == driver->type) { technology->driver->remove(technology); technology->driver = NULL; } } driver_list = g_slist_remove(driver_list, driver); } static void tethering_changed(struct connman_technology *technology) { connman_bool_t tethering = technology->tethering; connman_dbus_property_changed_basic(technology->path, CONNMAN_TECHNOLOGY_INTERFACE, "Tethering", DBUS_TYPE_BOOLEAN, &tethering); } void connman_technology_tethering_notify(struct connman_technology *technology, connman_bool_t enabled) { GSList *list; DBG("technology %p enabled %u", technology, enabled); if (technology->tethering == enabled) return; technology->tethering = enabled; tethering_changed(technology); if (enabled == TRUE) __connman_tethering_set_enabled(); else { for (list = technology_list; list; list = list->next) { struct connman_technology *other_tech = list->data; if (other_tech->tethering == TRUE) break; } if (list == NULL) __connman_tethering_set_disabled(); } } static int set_tethering(struct connman_technology *technology, connman_bool_t enabled) { const char *ident, *passphrase, *bridge; ident = technology->tethering_ident; passphrase = technology->tethering_passphrase; if (technology->driver == NULL || technology->driver->set_tethering == NULL) return -EOPNOTSUPP; __sync_synchronize(); if (technology->enabled == FALSE) return -EACCES; bridge = __connman_tethering_get_bridge(); if (bridge == NULL) return -EOPNOTSUPP; if (technology->type == CONNMAN_SERVICE_TYPE_WIFI && (ident == NULL || passphrase == NULL)) return -EINVAL; return technology->driver->set_tethering(technology, ident, passphrase, bridge, enabled); } void connman_technology_regdom_notify(struct connman_technology *technology, const char *alpha2) { DBG(""); if (alpha2 == NULL) connman_error("Failed to set regulatory domain"); else DBG("Regulatory domain set to %s", alpha2); g_free(technology->regdom); technology->regdom = g_strdup(alpha2); } static int set_regdom_by_device(struct connman_technology *technology, const char *alpha2) { GSList *list; for (list = technology->device_list; list; list = list->next) { struct connman_device *device = list->data; if (connman_device_set_regdom(device, alpha2) != 0) return -ENOTSUP; } return 0; } int connman_technology_set_regdom(const char *alpha2) { GSList *list; for (list = technology_list; list; list = list->next) { struct connman_technology *technology = list->data; if (set_regdom_by_device(technology, alpha2) != 0) { if (technology->driver == NULL) continue; if (technology->driver->set_regdom != NULL) technology->driver->set_regdom(technology, alpha2); } } return 0; } static void free_rfkill(gpointer data) { struct connman_rfkill *rfkill = data; g_free(rfkill); } static const char *get_name(enum connman_service_type type) { switch (type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: case CONNMAN_SERVICE_TYPE_GPS: case CONNMAN_SERVICE_TYPE_VPN: case CONNMAN_SERVICE_TYPE_GADGET: break; case CONNMAN_SERVICE_TYPE_ETHERNET: return "Wired"; case CONNMAN_SERVICE_TYPE_WIFI: return "WiFi"; case CONNMAN_SERVICE_TYPE_WIMAX: return "WiMAX"; case CONNMAN_SERVICE_TYPE_BLUETOOTH: return "Bluetooth"; case CONNMAN_SERVICE_TYPE_CELLULAR: return "Cellular"; } return NULL; } static void technology_save(struct connman_technology *technology) { GKeyFile *keyfile; gchar *identifier; DBG("technology %p", technology); keyfile = __connman_storage_load_global(); if (keyfile == NULL) keyfile = g_key_file_new(); identifier = g_strdup_printf("%s", get_name(technology->type)); if (identifier == NULL) goto done; g_key_file_set_boolean(keyfile, identifier, "Enable", technology->enable_persistent); if (technology->tethering_ident != NULL) g_key_file_set_string(keyfile, identifier, "Tethering.Identifier", technology->tethering_ident); if (technology->tethering_passphrase != NULL) g_key_file_set_string(keyfile, identifier, "Tethering.Passphrase", technology->tethering_passphrase); done: g_free(identifier); __connman_storage_save_global(keyfile); g_key_file_free(keyfile); return; } static void technology_load(struct connman_technology *technology) { GKeyFile *keyfile; gchar *identifier; GError *error = NULL; connman_bool_t enable; DBG("technology %p", technology); keyfile = __connman_storage_load_global(); /* Fallback on disabling technology if file not found. */ if (keyfile == NULL) { if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET) /* We enable ethernet by default */ technology->enable_persistent = TRUE; else technology->enable_persistent = FALSE; return; } identifier = g_strdup_printf("%s", get_name(technology->type)); if (identifier == NULL) goto done; enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error); if (error == NULL) technology->enable_persistent = enable; else { if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET) technology->enable_persistent = TRUE; else technology->enable_persistent = FALSE; technology_save(technology); g_clear_error(&error); } technology->tethering_ident = g_key_file_get_string(keyfile, identifier, "Tethering.Identifier", NULL); technology->tethering_passphrase = g_key_file_get_string(keyfile, identifier, "Tethering.Passphrase", NULL); done: g_free(identifier); g_key_file_free(keyfile); return; } connman_bool_t __connman_technology_get_offlinemode(void) { return global_offlinemode; } static void connman_technology_save_offlinemode() { GKeyFile *keyfile; keyfile = __connman_storage_load_global(); if (keyfile == NULL) keyfile = g_key_file_new(); g_key_file_set_boolean(keyfile, "global", "OfflineMode", global_offlinemode); __connman_storage_save_global(keyfile); g_key_file_free(keyfile); return; } static connman_bool_t connman_technology_load_offlinemode() { GKeyFile *keyfile; GError *error = NULL; connman_bool_t offlinemode; /* If there is a error, we enable offlinemode */ keyfile = __connman_storage_load_global(); if (keyfile == NULL) return FALSE; offlinemode = g_key_file_get_boolean(keyfile, "global", "OfflineMode", &error); if (error != NULL) { offlinemode = FALSE; g_clear_error(&error); } g_key_file_free(keyfile); return offlinemode; } static void append_properties(DBusMessageIter *iter, struct connman_technology *technology) { DBusMessageIter dict; const char *str; connman_dbus_dict_open(iter, &dict); str = get_name(technology->type); if (str != NULL) connman_dbus_dict_append_basic(&dict, "Name", DBUS_TYPE_STRING, &str); str = __connman_service_type2string(technology->type); if (str != NULL) connman_dbus_dict_append_basic(&dict, "Type", DBUS_TYPE_STRING, &str); __sync_synchronize(); connman_dbus_dict_append_basic(&dict, "Powered", DBUS_TYPE_BOOLEAN, &technology->enabled); connman_dbus_dict_append_basic(&dict, "Connected", DBUS_TYPE_BOOLEAN, &technology->connected); connman_dbus_dict_append_basic(&dict, "Tethering", DBUS_TYPE_BOOLEAN, &technology->tethering); if (technology->tethering_ident != NULL) connman_dbus_dict_append_basic(&dict, "TetheringIdentifier", DBUS_TYPE_STRING, &technology->tethering_ident); if (technology->tethering_passphrase != NULL) connman_dbus_dict_append_basic(&dict, "TetheringPassphrase", DBUS_TYPE_STRING, &technology->tethering_passphrase); connman_dbus_dict_close(iter, &dict); } static void technology_added_signal(struct connman_technology *technology) { DBusMessage *signal; DBusMessageIter iter; signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, "TechnologyAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &technology->path); append_properties(&iter, technology); dbus_connection_send(connection, signal, NULL); dbus_message_unref(signal); } static void technology_removed_signal(struct connman_technology *technology) { g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, "TechnologyRemoved", DBUS_TYPE_OBJECT_PATH, &technology->path, DBUS_TYPE_INVALID); } static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *message, void *user_data) { struct connman_technology *technology = user_data; DBusMessage *reply; DBusMessageIter iter; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); append_properties(&iter, technology); return reply; } void __connman_technology_list_struct(DBusMessageIter *array) { GSList *list; DBusMessageIter entry; for (list = technology_list; list; list = list->next) { struct connman_technology *technology = list->data; if (technology->path == NULL || (technology->rfkill_driven == TRUE && technology->hardblocked == TRUE)) continue; dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &technology->path); append_properties(&entry, technology); dbus_message_iter_close_container(array, &entry); } } static gboolean technology_pending_reply(gpointer user_data) { struct connman_technology *technology = user_data; DBusMessage *reply; /* Power request timedout, send ETIMEDOUT. */ if (technology->pending_reply != NULL) { reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT); if (reply != NULL) g_dbus_send_message(connection, reply); dbus_message_unref(technology->pending_reply); technology->pending_reply = NULL; technology->pending_timeout = 0; } return FALSE; } static int technology_affect_devices(struct connman_technology *technology, connman_bool_t enable_device) { GSList *list; int err = 0; for (list = technology->device_list; list; list = list->next) { struct connman_device *device = list->data; if (enable_device == TRUE) err = __connman_device_enable(device); else err = __connman_device_disable(device); } return err; } static int technology_enable(struct connman_technology *technology) { DBG("technology %p enable", technology); __sync_synchronize(); if (technology->enabled == TRUE) return -EALREADY; if (technology->pending_reply != NULL) return -EBUSY; if (technology->rfkill_driven == TRUE) return __connman_rfkill_block(technology->type, FALSE); return technology_affect_devices(technology, TRUE); } static int technology_disable(struct connman_technology *technology) { DBG("technology %p disable", technology); __sync_synchronize(); if (technology->enabled == FALSE) return -EALREADY; if (technology->pending_reply != NULL) return -EBUSY; if (technology->tethering == TRUE) set_tethering(technology, FALSE); if (technology->rfkill_driven == TRUE) return __connman_rfkill_block(technology->type, TRUE); return technology_affect_devices(technology, FALSE); } static DBusMessage *set_powered(struct connman_technology *technology, DBusMessage *msg, connman_bool_t powered) { DBusMessage *reply = NULL; int err = 0; if (technology->rfkill_driven && technology->hardblocked == TRUE) { err = -EACCES; goto make_reply; } if (powered == TRUE) err = technology_enable(technology); else err = technology_disable(technology); if (err != -EBUSY) { technology->enable_persistent = powered; technology_save(technology); } make_reply: if (err == -EINPROGRESS) { technology->pending_reply = dbus_message_ref(msg); technology->pending_timeout = g_timeout_add_seconds(10, technology_pending_reply, technology); } else if (err == -EALREADY) { if (powered == TRUE) reply = __connman_error_already_enabled(msg); else reply = __connman_error_already_disabled(msg); } else if (err < 0) reply = __connman_error_failed(msg, -err); else reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); return reply; } static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct connman_technology *technology = data; DBusMessageIter iter, value; const char *name; int type; DBG("conn %p", conn); if (dbus_message_iter_init(msg, &iter) == FALSE) return __connman_error_invalid_arguments(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __connman_error_invalid_arguments(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __connman_error_invalid_arguments(msg); dbus_message_iter_recurse(&iter, &value); type = dbus_message_iter_get_arg_type(&value); DBG("property %s", name); if (g_str_equal(name, "Tethering") == TRUE) { int err; connman_bool_t tethering; if (type != DBUS_TYPE_BOOLEAN) return __connman_error_invalid_arguments(msg); dbus_message_iter_get_basic(&value, &tethering); if (technology->tethering == tethering) { if (tethering == FALSE) return __connman_error_already_disabled(msg); else return __connman_error_already_enabled(msg); } err = set_tethering(technology, tethering); if (err < 0) return __connman_error_failed(msg, -err); } else if (g_str_equal(name, "TetheringIdentifier") == TRUE) { const char *str; dbus_message_iter_get_basic(&value, &str); if (technology->type != CONNMAN_SERVICE_TYPE_WIFI) return __connman_error_not_supported(msg); if (strlen(str) < 1 || strlen(str) > 32) return __connman_error_invalid_arguments(msg); if (g_strcmp0(technology->tethering_ident, str) != 0) { g_free(technology->tethering_ident); technology->tethering_ident = g_strdup(str); technology_save(technology); connman_dbus_property_changed_basic(technology->path, CONNMAN_TECHNOLOGY_INTERFACE, "TetheringIdentifier", DBUS_TYPE_STRING, &technology->tethering_ident); } } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) { const char *str; dbus_message_iter_get_basic(&value, &str); if (technology->type != CONNMAN_SERVICE_TYPE_WIFI) return __connman_error_not_supported(msg); if (strlen(str) < 8 || strlen(str) > 63) return __connman_error_passphrase_required(msg); if (g_strcmp0(technology->tethering_passphrase, str) != 0) { g_free(technology->tethering_passphrase); technology->tethering_passphrase = g_strdup(str); technology_save(technology); connman_dbus_property_changed_basic(technology->path, CONNMAN_TECHNOLOGY_INTERFACE, "TetheringPassphrase", DBUS_TYPE_STRING, &technology->tethering_passphrase); } } else if (g_str_equal(name, "Powered") == TRUE) { connman_bool_t enable; if (type != DBUS_TYPE_BOOLEAN) return __connman_error_invalid_arguments(msg); dbus_message_iter_get_basic(&value, &enable); return set_powered(technology, msg, enable); } else return __connman_error_invalid_property(msg); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static struct connman_technology *technology_find(enum connman_service_type type) { GSList *list; DBG("type %d", type); for (list = technology_list; list; list = list->next) { struct connman_technology *technology = list->data; if (technology->type == type) return technology; } return NULL; } static void reply_scan_pending(struct connman_technology *technology, int err) { DBusMessage *reply; DBG("technology %p err %d", technology, err); while (technology->scan_pending != NULL) { DBusMessage *msg = technology->scan_pending->data; DBG("reply to %s", dbus_message_get_sender(msg)); if (err == 0) reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); else reply = __connman_error_failed(msg, -err); g_dbus_send_message(connection, reply); dbus_message_unref(msg); technology->scan_pending = g_slist_delete_link(technology->scan_pending, technology->scan_pending); } } void __connman_technology_scan_started(struct connman_device *device) { DBG("device %p", device); } void __connman_technology_scan_stopped(struct connman_device *device) { int count = 0; struct connman_technology *technology; enum connman_service_type type; GSList *list; type = __connman_device_get_service_type(device); technology = technology_find(type); DBG("technology %p device %p", technology, device); if (technology == NULL) return; for (list = technology->device_list; list != NULL; list = list->next) { struct connman_device *other_device = list->data; if (device == other_device) continue; if (__connman_device_get_service_type(other_device) != type) continue; if (connman_device_get_scanning(other_device) == TRUE) count += 1; } if (count == 0) reply_scan_pending(technology, 0); } void __connman_technology_notify_regdom_by_device(struct connman_device *device, int result, const char *alpha2) { struct connman_technology *technology; enum connman_service_type type; type = __connman_device_get_service_type(device); technology = technology_find(type); if (technology == NULL) return; if (result < 0) { if (technology->driver != NULL && technology->driver->set_regdom != NULL) { technology->driver->set_regdom(technology, alpha2); return; } alpha2 = NULL; } connman_technology_regdom_notify(technology, alpha2); } static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data) { struct connman_technology *technology = data; int err; DBG ("technology %p request from %s", technology, dbus_message_get_sender(msg)); dbus_message_ref(msg); technology->scan_pending = g_slist_prepend(technology->scan_pending, msg); err = __connman_device_request_scan(technology->type); if (err < 0) reply_scan_pending(technology, err); return NULL; } static const GDBusMethodTable technology_methods[] = { { GDBUS_DEPRECATED_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL, set_property) }, { GDBUS_ASYNC_METHOD("Scan", NULL, NULL, scan) }, { }, }; static const GDBusSignalTable technology_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { }, }; static gboolean technology_dbus_register(struct connman_technology *technology) { if (technology->dbus_registered == TRUE || (technology->rfkill_driven == TRUE && technology->hardblocked == TRUE)) return TRUE; if (g_dbus_register_interface(connection, technology->path, CONNMAN_TECHNOLOGY_INTERFACE, technology_methods, technology_signals, NULL, technology, NULL) == FALSE) { connman_error("Failed to register %s", technology->path); return FALSE; } technology_added_signal(technology); technology->dbus_registered = TRUE; return TRUE; } static struct connman_technology *technology_get(enum connman_service_type type) { struct connman_technology *technology; struct connman_technology_driver *driver = NULL; const char *str; GSList *list; int err; DBG("type %d", type); str = __connman_service_type2string(type); if (str == NULL) return NULL; technology = technology_find(type); if (technology != NULL) { __sync_fetch_and_add(&technology->refcount, 1); return technology; } /* First check if we have a driver for this technology type */ for (list = driver_list; list; list = list->next) { driver = list->data; if (driver->type == type) break; else driver = NULL; } if (driver == NULL) { DBG("No matching driver found for %s.", __connman_service_type2string(type)); return NULL; } technology = g_try_new0(struct connman_technology, 1); if (technology == NULL) return NULL; technology->refcount = 1; technology->rfkill_driven = FALSE; technology->softblocked = FALSE; technology->hardblocked = FALSE; technology->type = type; technology->path = g_strdup_printf("%s/technology/%s", CONNMAN_PATH, str); technology->device_list = NULL; technology->pending_reply = NULL; technology_load(technology); if (technology_dbus_register(technology) == FALSE) { g_free(technology); return NULL; } technology_list = g_slist_prepend(technology_list, technology); technology->driver = driver; err = driver->probe(technology); if (err != 0) DBG("Driver probe failed for technology %p", technology); DBG("technology %p", technology); return technology; } static void technology_dbus_unregister(struct connman_technology *technology) { if (technology->dbus_registered == FALSE) return; technology_removed_signal(technology); g_dbus_unregister_interface(connection, technology->path, CONNMAN_TECHNOLOGY_INTERFACE); technology->dbus_registered = FALSE; } static void technology_put(struct connman_technology *technology) { DBG("technology %p", technology); if (__sync_sub_and_fetch(&technology->refcount, 1) > 0) return; reply_scan_pending(technology, -EINTR); if (technology->driver) { technology->driver->remove(technology); technology->driver = NULL; } technology_list = g_slist_remove(technology_list, technology); technology_dbus_unregister(technology); g_slist_free(technology->device_list); g_free(technology->path); g_free(technology->regdom); g_free(technology->tethering_ident); g_free(technology->tethering_passphrase); g_free(technology); } void __connman_technology_add_interface(enum connman_service_type type, int index, const char *name, const char *ident) { struct connman_technology *technology; switch (type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: return; case CONNMAN_SERVICE_TYPE_ETHERNET: case CONNMAN_SERVICE_TYPE_WIFI: case CONNMAN_SERVICE_TYPE_WIMAX: case CONNMAN_SERVICE_TYPE_BLUETOOTH: case CONNMAN_SERVICE_TYPE_CELLULAR: case CONNMAN_SERVICE_TYPE_GPS: case CONNMAN_SERVICE_TYPE_VPN: case CONNMAN_SERVICE_TYPE_GADGET: break; } connman_info("Adding interface %s [ %s ]", name, __connman_service_type2string(type)); technology = technology_find(type); if (technology == NULL || technology->driver == NULL || technology->driver->add_interface == NULL) return; technology->driver->add_interface(technology, index, name, ident); } void __connman_technology_remove_interface(enum connman_service_type type, int index, const char *name, const char *ident) { struct connman_technology *technology; switch (type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: return; case CONNMAN_SERVICE_TYPE_ETHERNET: case CONNMAN_SERVICE_TYPE_WIFI: case CONNMAN_SERVICE_TYPE_WIMAX: case CONNMAN_SERVICE_TYPE_BLUETOOTH: case CONNMAN_SERVICE_TYPE_CELLULAR: case CONNMAN_SERVICE_TYPE_GPS: case CONNMAN_SERVICE_TYPE_VPN: case CONNMAN_SERVICE_TYPE_GADGET: break; } connman_info("Remove interface %s [ %s ]", name, __connman_service_type2string(type)); technology = technology_find(type); if (technology == NULL || technology->driver == NULL) return; if (technology->driver->remove_interface) technology->driver->remove_interface(technology, index); } int __connman_technology_add_device(struct connman_device *device) { struct connman_technology *technology; enum connman_service_type type; DBG("device %p", device); type = __connman_device_get_service_type(device); technology = technology_get(type); if (technology == NULL) { /* * Since no driver can be found for this device at the moment we * add it to the techless device list. */ techless_device_list = g_slist_prepend(techless_device_list, device); return -ENXIO; } __sync_synchronize(); if (technology->rfkill_driven == TRUE) { if (technology->enabled == TRUE) __connman_device_enable(device); else __connman_device_disable(device); goto done; } if (technology->enable_persistent == TRUE && global_offlinemode == FALSE) { int err = __connman_device_enable(device); /* * connman_technology_add_device() calls __connman_device_enable() * but since the device is already enabled, the calls does not * propagate through to connman_technology_enabled via * connman_device_set_powered. */ if (err == -EALREADY) __connman_technology_enabled(type); } /* if technology persistent state is offline */ if (technology->enable_persistent == FALSE) __connman_device_disable(device); done: technology->device_list = g_slist_prepend(technology->device_list, device); return 0; } int __connman_technology_remove_device(struct connman_device *device) { struct connman_technology *technology; enum connman_service_type type; DBG("device %p", device); type = __connman_device_get_service_type(device); technology = technology_find(type); if (technology == NULL) { techless_device_list = g_slist_remove(techless_device_list, device); return -ENXIO; } technology->device_list = g_slist_remove(technology->device_list, device); technology_put(technology); return 0; } static void powered_changed(struct connman_technology *technology) { if (technology->dbus_registered == FALSE) return; if (technology->pending_reply != NULL) { g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID); dbus_message_unref(technology->pending_reply); technology->pending_reply = NULL; g_source_remove(technology->pending_timeout); technology->pending_timeout = 0; } __sync_synchronize(); connman_dbus_property_changed_basic(technology->path, CONNMAN_TECHNOLOGY_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &technology->enabled); } static int technology_enabled(struct connman_technology *technology) { __sync_synchronize(); if (technology->enabled == TRUE) return -EALREADY; technology->enabled = TRUE; powered_changed(technology); return 0; } int __connman_technology_enabled(enum connman_service_type type) { struct connman_technology *technology; technology = technology_find(type); if (technology == NULL) return -ENXIO; if (technology->rfkill_driven == TRUE) return 0; return technology_enabled(technology); } static int technology_disabled(struct connman_technology *technology) { __sync_synchronize(); if (technology->enabled == FALSE) return -EALREADY; technology->enabled = FALSE; powered_changed(technology); return 0; } int __connman_technology_disabled(enum connman_service_type type) { struct connman_technology *technology; GSList *list; technology = technology_find(type); if (technology == NULL) return -ENXIO; if (technology->rfkill_driven == TRUE) return 0; for (list = technology->device_list; list != NULL; list = list->next) { struct connman_device *device = list->data; if (connman_device_get_powered(device) == TRUE) return 0; } return technology_disabled(technology); } int __connman_technology_set_offlinemode(connman_bool_t offlinemode) { GSList *list; int err = -EINVAL; if (global_offlinemode == offlinemode) return 0; DBG("offlinemode %s", offlinemode ? "On" : "Off"); /* * This is a bit tricky. When you set offlinemode, there is no * way to differentiate between attempting offline mode and * resuming offlinemode from last saved profile. We need that * information in rfkill_update, otherwise it falls back on the * technology's persistent state. Hence we set the offline mode here * but save it & call the notifier only if its successful. */ global_offlinemode = offlinemode; /* Traverse technology list, enable/disable each technology. */ for (list = technology_list; list; list = list->next) { struct connman_technology *technology = list->data; if (offlinemode) err = technology_disable(technology); if (!offlinemode && technology->enable_persistent) err = technology_enable(technology); } if (err == 0 || err == -EINPROGRESS || err == -EALREADY) { connman_technology_save_offlinemode(); __connman_notifier_offlinemode(offlinemode); } else global_offlinemode = connman_technology_load_offlinemode(); return err; } void __connman_technology_set_connected(enum connman_service_type type, connman_bool_t connected) { struct connman_technology *technology; technology = technology_find(type); if (technology == NULL) return; DBG("technology %p connected %d", technology, connected); technology->connected = connected; connman_dbus_property_changed_basic(technology->path, CONNMAN_TECHNOLOGY_INTERFACE, "Connected", DBUS_TYPE_BOOLEAN, &connected); } static connman_bool_t technology_apply_rfkill_change(struct connman_technology *technology, connman_bool_t softblock, connman_bool_t hardblock, connman_bool_t new_rfkill) { gboolean hardblock_changed = FALSE; gboolean apply = TRUE; GList *start, *list; DBG("technology %p --> %d/%d vs %d/%d", technology, softblock, hardblock, technology->softblocked, technology->hardblocked); if (technology->hardblocked == hardblock) goto softblock_change; if (!(new_rfkill == TRUE && hardblock == FALSE)) { start = g_hash_table_get_values(rfkill_list); for (list = start; list != NULL; list = list->next) { struct connman_rfkill *rfkill = list->data; if (rfkill->type != technology->type) continue; if (rfkill->hardblock != hardblock) apply = FALSE; } g_list_free(start); } if (apply == FALSE) goto softblock_change; technology->hardblocked = hardblock; hardblock_changed = TRUE; softblock_change: if (apply == FALSE && technology->softblocked != softblock) apply = TRUE; if (apply == FALSE) return technology->hardblocked; technology->softblocked = softblock; if (technology->hardblocked == TRUE || technology->softblocked == TRUE) { if (technology_disabled(technology) != -EALREADY) technology_affect_devices(technology, FALSE); } else if (technology->hardblocked == FALSE && technology->softblocked == FALSE) { if (technology_enabled(technology) != -EALREADY) technology_affect_devices(technology, TRUE); } if (hardblock_changed == TRUE) { if (technology->hardblocked == TRUE) { DBG("%s is switched off.", get_name(technology->type)); technology_dbus_unregister(technology); } else technology_dbus_register(technology); } return technology->hardblocked; } int __connman_technology_add_rfkill(unsigned int index, enum connman_service_type type, connman_bool_t softblock, connman_bool_t hardblock) { struct connman_technology *technology; struct connman_rfkill *rfkill; DBG("index %u type %d soft %u hard %u", index, type, softblock, hardblock); rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index)); if (rfkill != NULL) goto done; rfkill = g_try_new0(struct connman_rfkill, 1); if (rfkill == NULL) return -ENOMEM; rfkill->index = index; rfkill->type = type; rfkill->softblock = softblock; rfkill->hardblock = hardblock; g_hash_table_insert(rfkill_list, GINT_TO_POINTER(index), rfkill); done: technology = technology_get(type); /* If there is no driver for this type, ignore it. */ if (technology == NULL) return -ENXIO; technology->rfkill_driven = TRUE; /* If hardblocked, there is no need to handle softblocked state */ if (technology_apply_rfkill_change(technology, softblock, hardblock, TRUE) == TRUE) return 0; /* * Depending on softblocked state we unblock/block according to * offlinemode and persistente state. */ if (technology->softblocked == TRUE && global_offlinemode == FALSE && technology->enable_persistent == TRUE) return __connman_rfkill_block(type, FALSE); else if (technology->softblocked == FALSE && global_offlinemode == TRUE && technology->enable_persistent == FALSE) return __connman_rfkill_block(type, TRUE); return 0; } int __connman_technology_update_rfkill(unsigned int index, enum connman_service_type type, connman_bool_t softblock, connman_bool_t hardblock) { struct connman_technology *technology; struct connman_rfkill *rfkill; DBG("index %u soft %u hard %u", index, softblock, hardblock); rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index)); if (rfkill == NULL) return -ENXIO; if (rfkill->softblock == softblock && rfkill->hardblock == hardblock) return 0; rfkill->softblock = softblock; rfkill->hardblock = hardblock; technology = technology_find(type); /* If there is no driver for this type, ignore it. */ if (technology == NULL) return -ENXIO; /* If hardblocked, there is no need to handle softblocked state */ if (technology_apply_rfkill_change(technology, softblock, hardblock, FALSE) == TRUE) return 0; if (global_offlinemode == TRUE) return 0; /* * Depending on softblocked state we unblock/block according to * persistent state. */ if (technology->softblocked == TRUE && technology->enable_persistent == TRUE) return __connman_rfkill_block(type, FALSE); else if (technology->softblocked == FALSE && technology->enable_persistent == FALSE) return __connman_rfkill_block(type, TRUE); return 0; } int __connman_technology_remove_rfkill(unsigned int index, enum connman_service_type type) { struct connman_technology *technology; struct connman_rfkill *rfkill; DBG("index %u", index); rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index)); if (rfkill == NULL) return -ENXIO; g_hash_table_remove(rfkill_list, GINT_TO_POINTER(index)); technology = technology_find(type); if (technology == NULL) return -ENXIO; technology_apply_rfkill_change(technology, technology->softblocked, !technology->hardblocked, FALSE); technology_put(technology); return 0; } int __connman_technology_init(void) { DBG(""); connection = connman_dbus_get_connection(); rfkill_list = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_rfkill); global_offlinemode = connman_technology_load_offlinemode(); /* This will create settings file if it is missing */ connman_technology_save_offlinemode(); return 0; } void __connman_technology_cleanup(void) { DBG(""); g_hash_table_destroy(rfkill_list); dbus_connection_unref(connection); }