X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Ftechnology.c;h=8166d17c763172c14d493f66ac2039acbc340658;hb=35f61b1f8f53033a7333b56be18328d426dc24a1;hp=4e810663ab1c07391c2b4aad781a9df502600a77;hpb=cf1707d354d8244debcdf650d1df04fcc2535533;p=platform%2Fupstream%2Fconnman.git diff --git a/src/technology.c b/src/technology.c index 4e81066..8166d17 100644 --- a/src/technology.c +++ b/src/technology.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. * * 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 @@ -34,6 +34,13 @@ 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 { @@ -43,22 +50,14 @@ struct connman_rfkill { connman_bool_t hardblock; }; -enum connman_technology_state { - CONNMAN_TECHNOLOGY_STATE_UNKNOWN = 0, - CONNMAN_TECHNOLOGY_STATE_OFFLINE = 1, - CONNMAN_TECHNOLOGY_STATE_ENABLED = 2, - CONNMAN_TECHNOLOGY_STATE_CONNECTED = 3, -}; - struct connman_technology { - gint refcount; + int refcount; enum connman_service_type type; - enum connman_technology_state state; char *path; - GHashTable *rfkill_list; GSList *device_list; - gint enabled; + connman_bool_t enabled; char *regdom; + connman_bool_t connected; connman_bool_t tethering; char *tethering_ident; @@ -71,6 +70,13 @@ struct connman_technology { 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; @@ -83,6 +89,17 @@ static gint compare_priority(gconstpointer a, gconstpointer 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 @@ -94,23 +111,39 @@ static gint compare_priority(gconstpointer a, gconstpointer b) int connman_technology_driver_register(struct connman_technology_driver *driver) { GSList *list; - struct connman_technology *technology; + struct connman_device *device; + enum connman_service_type type; - DBG("driver %p name %s", driver, driver->name); + DBG("Registering %s driver", driver->name); driver_list = g_slist_insert_sorted(driver_list, driver, compare_priority); - for (list = technology_list; list; list = list->next) { - technology = list->data; + if (techless_device_list == NULL) + goto check_rfkill; - if (technology->driver != NULL) + /* + * 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; - if (technology->type == driver->type) - technology->driver = driver; + 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; } @@ -125,7 +158,7 @@ void connman_technology_driver_unregister(struct connman_technology_driver *driv GSList *list; struct connman_technology *technology; - DBG("driver %p name %s", driver, driver->name); + DBG("Unregistering driver %p name %s", driver, driver->name); for (list = technology_list; list; list = list->next) { technology = list->data; @@ -154,6 +187,8 @@ static void tethering_changed(struct connman_technology *technology) 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) @@ -165,14 +200,21 @@ void connman_technology_tethering_notify(struct connman_technology *technology, if (enabled == TRUE) __connman_tethering_set_enabled(); - else - __connman_tethering_set_disabled(); + 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, - const char *bridge, connman_bool_t enabled) + connman_bool_t enabled) { - const char *ident, *passphrase; + const char *ident, *passphrase, *bridge; ident = technology->tethering_ident; passphrase = technology->tethering_passphrase; @@ -181,6 +223,14 @@ static int set_tethering(struct connman_technology *technology, 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; @@ -203,79 +253,46 @@ void connman_technology_regdom_notify(struct connman_technology *technology, technology->regdom = g_strdup(alpha2); } -int connman_technology_set_regdom(const char *alpha2) +static int set_regdom_by_device(struct connman_technology *technology, + const char *alpha2) { GSList *list; - for (list = technology_list; list; list = list->next) { - struct connman_technology *technology = list->data; - - if (technology->driver == NULL) - continue; + for (list = technology->device_list; list; list = list->next) { + struct connman_device *device = list->data; - if (technology->driver->set_regdom) - technology->driver->set_regdom(technology, alpha2); + if (connman_device_set_regdom(device, alpha2) != 0) + return -ENOTSUP; } return 0; } -static void free_rfkill(gpointer data) -{ - struct connman_rfkill *rfkill = data; - - g_free(rfkill); -} - -void __connman_technology_list(DBusMessageIter *iter, void *user_data) +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 (technology->path == NULL) - continue; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, - &technology->path); - } -} - -static void technologies_changed(void) -{ - connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH, - CONNMAN_MANAGER_INTERFACE, "Technologies", - DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL); -} + if (set_regdom_by_device(technology, alpha2) != 0) { + if (technology->driver == NULL) + continue; -static const char *state2string(enum connman_technology_state state) -{ - switch (state) { - case CONNMAN_TECHNOLOGY_STATE_UNKNOWN: - break; - case CONNMAN_TECHNOLOGY_STATE_OFFLINE: - return "offline"; - case CONNMAN_TECHNOLOGY_STATE_ENABLED: - return "enabled"; - case CONNMAN_TECHNOLOGY_STATE_CONNECTED: - return "connected"; + if (technology->driver->set_regdom != NULL) + technology->driver->set_regdom(technology, + alpha2); + } } - return NULL; + return 0; } -static void state_changed(struct connman_technology *technology) +static void free_rfkill(gpointer data) { - const char *str; - - str = state2string(technology->state); - if (str == NULL) - return; + struct connman_rfkill *rfkill = data; - connman_dbus_property_changed_basic(technology->path, - CONNMAN_TECHNOLOGY_INTERFACE, "State", - DBUS_TYPE_STRING, &str); + g_free(rfkill); } static const char *get_name(enum connman_service_type type) @@ -291,36 +308,124 @@ static const char *get_name(enum connman_service_type type) 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 "3G"; + 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 int connman_technology_save_offlinemode() +static void connman_technology_save_offlinemode() { GKeyFile *keyfile; - keyfile = __connman_storage_open_profile("default"); + keyfile = __connman_storage_load_global(); if (keyfile == NULL) - return -EIO; + keyfile = g_key_file_new(); g_key_file_set_boolean(keyfile, "global", "OfflineMode", global_offlinemode); - __connman_storage_close_profile("default", keyfile, TRUE); + __connman_storage_save_global(keyfile); - return 0; + g_key_file_free(keyfile); + + return; } static connman_bool_t connman_technology_load_offlinemode() @@ -330,41 +435,29 @@ static connman_bool_t connman_technology_load_offlinemode() connman_bool_t offlinemode; /* If there is a error, we enable offlinemode */ - keyfile = __connman_storage_open_profile("default"); + keyfile = __connman_storage_load_global(); if (keyfile == NULL) - return TRUE; + return FALSE; offlinemode = g_key_file_get_boolean(keyfile, "global", "OfflineMode", &error); if (error != NULL) { - offlinemode = TRUE; + offlinemode = FALSE; g_clear_error(&error); } - __connman_storage_close_profile("default", keyfile, FALSE); + + g_key_file_free(keyfile); return offlinemode; } -static DBusMessage *get_properties(DBusConnection *conn, - DBusMessage *message, void *user_data) +static void append_properties(DBusMessageIter *iter, + struct connman_technology *technology) { - struct connman_technology *technology = user_data; - DBusMessage *reply; - DBusMessageIter array, dict; + DBusMessageIter dict; const char *str; - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - dbus_message_iter_init_append(reply, &array); - - connman_dbus_dict_open(&array, &dict); - - str = state2string(technology->state); - if (str != NULL) - connman_dbus_dict_append_basic(&dict, "State", - DBUS_TYPE_STRING, &str); + connman_dbus_dict_open(iter, &dict); str = get_name(technology->type); if (str != NULL) @@ -376,21 +469,207 @@ static DBusMessage *get_properties(DBusConnection *conn, 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); + DBUS_TYPE_STRING, + &technology->tethering_ident); if (technology->tethering_passphrase != NULL) connman_dbus_dict_append_basic(&dict, "TetheringPassphrase", - DBUS_TYPE_STRING, - &technology->tethering_passphrase); + 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; - connman_dbus_dict_close(&array, &dict); + 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; } @@ -408,8 +687,15 @@ static DBusMessage *set_property(DBusConnection *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); @@ -419,21 +705,20 @@ static DBusMessage *set_property(DBusConnection *conn, if (g_str_equal(name, "Tethering") == TRUE) { int err; connman_bool_t tethering; - const char *bridge; if (type != DBUS_TYPE_BOOLEAN) return __connman_error_invalid_arguments(msg); dbus_message_iter_get_basic(&value, &tethering); - if (technology->tethering == tethering) - return __connman_error_in_progress(msg); - - bridge = __connman_tethering_get_bridge(); - if (bridge == NULL) - return __connman_error_not_supported(msg); + if (technology->tethering == tethering) { + if (tethering == FALSE) + return __connman_error_already_disabled(msg); + else + return __connman_error_already_enabled(msg); + } - err = set_tethering(technology, bridge, tethering); + err = set_tethering(technology, tethering); if (err < 0) return __connman_error_failed(msg, -err); @@ -445,7 +730,20 @@ static DBusMessage *set_property(DBusConnection *conn, if (technology->type != CONNMAN_SERVICE_TYPE_WIFI) return __connman_error_not_supported(msg); - technology->tethering_ident = g_strdup(str); + 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; @@ -454,27 +752,35 @@ static DBusMessage *set_property(DBusConnection *conn, if (technology->type != CONNMAN_SERVICE_TYPE_WIFI) return __connman_error_not_supported(msg); - if (strlen(str) < 8) - return __connman_error_invalid_arguments(msg); + if (strlen(str) < 8 || strlen(str) > 63) + return __connman_error_passphrase_required(msg); - technology->tethering_passphrase = g_strdup(str); - } else + 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 GDBusMethodTable technology_methods[] = { - { "GetProperties", "", "a{sv}", get_properties }, - { "SetProperty", "sv", "", set_property }, - { }, -}; - -static GDBusSignalTable technology_signals[] = { - { "PropertyChanged", "sv" }, - { }, -}; - static struct connman_technology *technology_find(enum connman_service_type type) { GSList *list; @@ -491,23 +797,184 @@ static struct connman_technology *technology_find(enum connman_service_type type 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) { - g_atomic_int_inc(&technology->refcount); - goto done; + __sync_fetch_and_add(&technology->refcount, 1); + return technology; } - str = __connman_service_type2string(type); - if (str == NULL) + /* 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) @@ -515,62 +982,58 @@ static struct connman_technology *technology_get(enum connman_service_type type) 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->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal, - NULL, free_rfkill); technology->device_list = NULL; technology->pending_reply = NULL; - technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE; - __connman_storage_load_technology(technology); + technology_load(technology); - 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); + if (technology_dbus_register(technology) == FALSE) { g_free(technology); return NULL; } - technology_list = g_slist_append(technology_list, technology); - - technologies_changed(); + technology_list = g_slist_prepend(technology_list, technology); - if (technology->driver != NULL) - goto done; - - for (list = driver_list; list; list = list->next) { - struct connman_technology_driver *driver = list->data; + technology->driver = driver; + err = driver->probe(technology); + if (err != 0) + DBG("Driver probe failed for technology %p", technology); - DBG("driver %p name %s", driver, driver->name); + DBG("technology %p", technology); - if (driver->type != technology->type) - continue; + return technology; +} - if (driver->probe(technology) == 0) { - technology->driver = driver; - break; - } - } +static void technology_dbus_unregister(struct connman_technology *technology) +{ + if (technology->dbus_registered == FALSE) + return; -done: - DBG("technology %p", technology); + technology_removed_signal(technology); + g_dbus_unregister_interface(connection, technology->path, + CONNMAN_TECHNOLOGY_INTERFACE); - return technology; + technology->dbus_registered = FALSE; } static void technology_put(struct connman_technology *technology) { DBG("technology %p", technology); - if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE) + 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; @@ -578,16 +1041,14 @@ static void technology_put(struct connman_technology *technology) technology_list = g_slist_remove(technology_list, technology); - technologies_changed(); - - g_dbus_unregister_interface(connection, technology->path, - CONNMAN_TECHNOLOGY_INTERFACE); + technology_dbus_unregister(technology); g_slist_free(technology->device_list); - g_hash_table_destroy(technology->rfkill_list); g_free(technology->path); g_free(technology->regdom); + g_free(technology->tethering_ident); + g_free(technology->tethering_passphrase); g_free(technology); } @@ -602,7 +1063,6 @@ void __connman_technology_add_interface(enum connman_service_type type, 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: @@ -611,10 +1071,10 @@ void __connman_technology_add_interface(enum connman_service_type type, break; } - connman_info("Create interface %s [ %s ]", name, + connman_info("Adding interface %s [ %s ]", name, __connman_service_type2string(type)); - technology = technology_get(type); + technology = technology_find(type); if (technology == NULL || technology->driver == NULL || technology->driver->add_interface == NULL) @@ -635,7 +1095,6 @@ void __connman_technology_remove_interface(enum connman_service_type type, 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: @@ -654,8 +1113,6 @@ void __connman_technology_remove_interface(enum connman_service_type type, if (technology->driver->remove_interface) technology->driver->remove_interface(technology, index); - - technology_put(technology); } int __connman_technology_add_device(struct connman_device *device) @@ -666,19 +1123,47 @@ int __connman_technology_add_device(struct connman_device *device) DBG("device %p", device); type = __connman_device_get_service_type(device); - __connman_notifier_register(type); technology = technology_get(type); - if (technology == NULL) + 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); - if (technology->enable_persistent && !global_offlinemode) - __connman_device_enable(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) + if (technology->enable_persistent == FALSE) __connman_device_disable(device); - technology->device_list = g_slist_append(technology->device_list, +done: + technology->device_list = g_slist_prepend(technology->device_list, device); return 0; @@ -692,206 +1177,102 @@ int __connman_technology_remove_device(struct connman_device *device) DBG("device %p", device); type = __connman_device_get_service_type(device); - __connman_notifier_unregister(type); technology = technology_find(type); - if (technology == NULL) + 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); - if (technology->device_list == NULL) { - technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE; - state_changed(technology); - } + technology_put(technology); return 0; } -static gboolean technology_pending_reply(gpointer user_data) +static void powered_changed(struct connman_technology *technology) { - struct connman_technology *technology = user_data; - DBusMessage *reply; + if (technology->dbus_registered == FALSE) + return; - /* 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); - + 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; } - return FALSE; + __sync_synchronize(); + connman_dbus_property_changed_basic(technology->path, + CONNMAN_TECHNOLOGY_INTERFACE, "Powered", + DBUS_TYPE_BOOLEAN, &technology->enabled); } -int __connman_technology_enabled(enum connman_service_type type) +static int technology_enabled(struct connman_technology *technology) { - struct connman_technology *technology; + __sync_synchronize(); + if (technology->enabled == TRUE) + return -EALREADY; - technology = technology_find(type); - if (technology == NULL) - return -ENXIO; + technology->enabled = TRUE; - if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) { - __connman_notifier_enable(type); - technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED; - state_changed(technology); - } - - 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; - technology->pending_timeout = 0; - } + powered_changed(technology); return 0; } -int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg) +int __connman_technology_enabled(enum connman_service_type type) { struct connman_technology *technology; - GSList *list; - int err = 0; - int ret = -ENODEV; - DBusMessage *reply; - - DBG("type %d enable", type); technology = technology_find(type); - if (technology == NULL) { - err = -ENXIO; - goto done; - } - - if (technology->pending_reply != NULL) { - err = -EBUSY; - goto done; - } - - if (msg != NULL) { - technology->pending_reply = dbus_message_ref(msg); - /* - * This is a bit of a trick. When msg is not NULL it means - * thats technology_enable was invoked from the manager API. Hence we save - * the state here. - */ - technology->enable_persistent = TRUE; - __connman_storage_save_technology(technology); - } - - __connman_rfkill_block(technology->type, FALSE); - - for (list = technology->device_list; list; list = list->next) { - struct connman_device *device = list->data; - - err = __connman_device_enable(device); - /* - * err = 0 : Device was enabled right away. - * If atleast one device gets enabled, we consider - * the technology to be enabled. - */ - if (err == 0) - ret = 0; - } - -done: - if (ret == 0) - return ret; + if (technology == NULL) + return -ENXIO; - if (msg != NULL) { - if (err == -EINPROGRESS) - technology->pending_timeout = g_timeout_add_seconds(10, - technology_pending_reply, technology); - else { - reply = __connman_error_failed(msg, -err); - if (reply != NULL) - g_dbus_send_message(connection, reply); - } - } + if (technology->rfkill_driven == TRUE) + return 0; - return err; + return technology_enabled(technology); } -int __connman_technology_disabled(enum connman_service_type type) +static int technology_disabled(struct connman_technology *technology) { - struct connman_technology *technology; - - technology = technology_find(type); - if (technology == NULL) - return -ENXIO; + __sync_synchronize(); + if (technology->enabled == FALSE) + return -EALREADY; - 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; - } + technology->enabled = FALSE; - if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) { - __connman_notifier_disable(type); - technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE; - state_changed(technology); - } + powered_changed(technology); return 0; } -int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg) +int __connman_technology_disabled(enum connman_service_type type) { struct connman_technology *technology; GSList *list; - int err = 0; - int ret = -ENODEV; - DBusMessage *reply; - - DBG("type %d disable", type); technology = technology_find(type); - if (technology == NULL) { - err = -ENXIO; - goto done; - } - - if (technology->pending_reply != NULL) { - err = -EBUSY; - goto done; - } - - if (msg != NULL) { - technology->pending_reply = dbus_message_ref(msg); - technology->enable_persistent = FALSE; - __connman_storage_save_technology(technology); - } + if (technology == NULL) + return -ENXIO; - __connman_rfkill_block(technology->type, TRUE); + if (technology->rfkill_driven == TRUE) + return 0; - for (list = technology->device_list; list; list = list->next) { + for (list = technology->device_list; list != NULL; list = list->next) { struct connman_device *device = list->data; - err = __connman_device_disable(device); - if (err == 0) - ret = 0; - } - -done: - if (ret == 0) - return ret; - - if (msg != NULL) { - if (err == -EINPROGRESS) - technology->pending_timeout = g_timeout_add_seconds(10, - technology_pending_reply, technology); - else { - reply = __connman_error_failed(msg, -err); - if (reply != NULL) - g_dbus_send_message(connection, reply); - } + if (connman_device_get_powered(device) == TRUE) + return 0; } - return err; + return technology_disabled(technology); } int __connman_technology_set_offlinemode(connman_bool_t offlinemode) @@ -920,10 +1301,10 @@ int __connman_technology_set_offlinemode(connman_bool_t offlinemode) struct connman_technology *technology = list->data; if (offlinemode) - err = __connman_technology_disable(technology->type, NULL); + err = technology_disable(technology); if (!offlinemode && technology->enable_persistent) - err = __connman_technology_enable(technology->type, NULL); + err = technology_enable(technology); } if (err == 0 || err == -EINPROGRESS || err == -EALREADY) { @@ -935,6 +1316,92 @@ int __connman_technology_set_offlinemode(connman_bool_t 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, @@ -946,9 +1413,9 @@ int __connman_technology_add_rfkill(unsigned int index, DBG("index %u type %d soft %u hard %u", index, type, softblock, hardblock); - technology = technology_get(type); - if (technology == NULL) - return -ENXIO; + 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) @@ -959,27 +1426,33 @@ int __connman_technology_add_rfkill(unsigned int index, rfkill->softblock = softblock; rfkill->hardblock = hardblock; - g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill); + 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 (hardblock) { - DBG("%s is switched off.", get_name(type)); + /* If hardblocked, there is no need to handle softblocked state */ + if (technology_apply_rfkill_change(technology, + softblock, hardblock, TRUE) == TRUE) return 0; - } /* - * If Offline mode is on, we softblock the device if it isnt already. - * If Offline mode is off, we rely on the persistent state of tech. + * Depending on softblocked state we unblock/block according to + * offlinemode and persistente state. */ - if (global_offlinemode) { - if (!softblock) - return __connman_rfkill_block(type, TRUE); - } else { - if (technology->enable_persistent && softblock) - return __connman_rfkill_block(type, FALSE); - /* if technology persistent state is offline */ - if (!technology->enable_persistent && !softblock) - return __connman_rfkill_block(type, TRUE); - } + 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; } @@ -994,32 +1467,40 @@ int __connman_technology_update_rfkill(unsigned int index, DBG("index %u soft %u hard %u", index, softblock, hardblock); - technology = technology_find(type); - if (technology == NULL) - return -ENXIO; - - rfkill = g_hash_table_lookup(technology->rfkill_list, &index); + rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index)); if (rfkill == NULL) return -ENXIO; if (rfkill->softblock == softblock && - rfkill->hardblock == hardblock) + rfkill->hardblock == hardblock) return 0; rfkill->softblock = softblock; rfkill->hardblock = hardblock; - if (hardblock) { - DBG("%s is switched off.", get_name(type)); + 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) { - if (technology->enable_persistent && softblock) - return __connman_rfkill_block(type, FALSE); - if (!technology->enable_persistent && !softblock) - return __connman_rfkill_block(type, TRUE); - } + 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; } @@ -1032,102 +1513,46 @@ int __connman_technology_remove_rfkill(unsigned int index, DBG("index %u", index); - technology = technology_find(type); - if (technology == NULL) - return -ENXIO; - - rfkill = g_hash_table_lookup(technology->rfkill_list, &index); + rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index)); if (rfkill == NULL) return -ENXIO; - g_hash_table_remove(technology->rfkill_list, &index); - - technology_put(technology); - - return 0; -} - -static int technology_load(struct connman_technology *technology) -{ - GKeyFile *keyfile; - gchar *identifier; - GError *error = NULL; - connman_bool_t enable; - - DBG("technology %p", technology); + g_hash_table_remove(rfkill_list, GINT_TO_POINTER(index)); - keyfile = __connman_storage_open_profile("default"); - if (keyfile == NULL) - return 0; - - 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 - technology->enable_persistent = FALSE; - - g_clear_error(&error); -done: - g_free(identifier); - - __connman_storage_close_profile("default", keyfile, FALSE); - - return 0; -} - -static int technology_save(struct connman_technology *technology) -{ - GKeyFile *keyfile; - gchar *identifier; - - DBG("technology %p", technology); - - keyfile = __connman_storage_open_profile("default"); - if (keyfile == NULL) - return 0; - - identifier = g_strdup_printf("%s", get_name(technology->type)); - if (identifier == NULL) - goto done; + technology = technology_find(type); + if (technology == NULL) + return -ENXIO; - g_key_file_set_boolean(keyfile, identifier, "Enable", - technology->enable_persistent); + technology_apply_rfkill_change(technology, + technology->softblocked, !technology->hardblocked, FALSE); -done: - g_free(identifier); - - __connman_storage_close_profile("default", keyfile, TRUE); + technology_put(technology); return 0; } -static struct connman_storage tech_storage = { - .name = "technology", - .priority = CONNMAN_STORAGE_PRIORITY_LOW, - .tech_load = technology_load, - .tech_save = technology_save, -}; - 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(); - return connman_storage_register(&tech_storage); + /* This will create settings file if it is missing */ + connman_technology_save_offlinemode(); + + return 0; } void __connman_technology_cleanup(void) { DBG(""); - dbus_connection_unref(connection); + g_hash_table_destroy(rfkill_list); - connman_storage_unregister(&tech_storage); + dbus_connection_unref(connection); }