X-Git-Url: http://git.maemo.org/git/?p=connman;a=blobdiff_plain;f=src%2Fdevice.c;h=0af3ccd97ac459da0c12b6452e8054a91b33ed83;hp=3930d75e89d61b06b7b014006b4f6d64da51453c;hb=050811a6aa9fcfe1885e90694eeeebc2ee0fd071;hpb=991fe1e5f6458aa30ee82ad73b2f0411b74b1d3e diff --git a/src/device.c b/src/device.c index 3930d75..0af3ccd 100644 --- a/src/device.c +++ b/src/device.c @@ -24,6 +24,8 @@ #endif #include +#include + #include #include "connman.h" @@ -33,18 +35,48 @@ struct connman_device { enum connman_device_type type; enum connman_device_mode mode; enum connman_device_policy policy; - gboolean powered; - gboolean carrier; - gboolean scanning; - char *path; + connman_bool_t secondary; + connman_bool_t powered; + connman_bool_t carrier; + connman_bool_t scanning; + connman_bool_t disconnected; + connman_uint8_t priority; + connman_uint16_t scan_interval; + char *name; + char *node; + char *address; char *interface; + char *ident; + unsigned int connections; + guint scan_timeout; struct connman_device_driver *driver; void *driver_data; + connman_bool_t registered; + + char *last_network; + struct connman_network *network; GHashTable *networks; }; +static gboolean device_scan_trigger(gpointer user_data) +{ + struct connman_device *device = user_data; + + DBG("device %p", device); + + if (device->driver == NULL) { + device->scan_timeout = 0; + return FALSE; + } + + if (device->driver->scan) + device->driver->scan(device); + + return TRUE; +} + static const char *type2description(enum connman_device_type type) { switch (type) { @@ -59,7 +91,12 @@ static const char *type2description(enum connman_device_type type) return "WiMAX"; case CONNMAN_DEVICE_TYPE_BLUETOOTH: return "Bluetooth"; + case CONNMAN_DEVICE_TYPE_GPS: + return "GPS"; case CONNMAN_DEVICE_TYPE_HSO: + case CONNMAN_DEVICE_TYPE_NOZOMI: + case CONNMAN_DEVICE_TYPE_HUAWEI: + case CONNMAN_DEVICE_TYPE_NOVATEL: return "Cellular"; } @@ -80,7 +117,12 @@ static const char *type2string(enum connman_device_type type) return "wimax"; case CONNMAN_DEVICE_TYPE_BLUETOOTH: return "bluetooth"; + case CONNMAN_DEVICE_TYPE_GPS: + return "gps"; case CONNMAN_DEVICE_TYPE_HSO: + case CONNMAN_DEVICE_TYPE_HUAWEI: + case CONNMAN_DEVICE_TYPE_NOZOMI: + case CONNMAN_DEVICE_TYPE_NOVATEL: return "cellular"; } @@ -90,6 +132,8 @@ static const char *type2string(enum connman_device_type type) static const char *policy2string(enum connman_device_policy policy) { switch (policy) { + case CONNMAN_DEVICE_POLICY_UNKNOWN: + break; case CONNMAN_DEVICE_POLICY_IGNORE: return "ignore"; case CONNMAN_DEVICE_POLICY_OFF: @@ -98,9 +142,9 @@ static const char *policy2string(enum connman_device_policy policy) return "auto"; case CONNMAN_DEVICE_POLICY_MANUAL: return "manual"; - default: - return NULL; } + + return NULL; } static enum connman_device_policy string2policy(const char *policy) @@ -117,7 +161,62 @@ static enum connman_device_policy string2policy(const char *policy) return CONNMAN_DEVICE_POLICY_UNKNOWN; } -static int set_powered(struct connman_device *device, gboolean powered) +static int set_carrier(struct connman_device *device, connman_bool_t carrier) +{ + struct connman_service *service; + + service = __connman_service_lookup_from_device(device); + __connman_service_set_carrier(service, carrier); + + if (carrier == TRUE) { + enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN; + struct connman_element *element; + + device->disconnected = TRUE; + + switch (device->policy) { + case CONNMAN_DEVICE_POLICY_UNKNOWN: + case CONNMAN_DEVICE_POLICY_IGNORE: + case CONNMAN_DEVICE_POLICY_OFF: + case CONNMAN_DEVICE_POLICY_MANUAL: + return 0; + case CONNMAN_DEVICE_POLICY_AUTO: + break; + } + + switch (device->element.ipv4.method) { + case CONNMAN_IPV4_METHOD_UNKNOWN: + case CONNMAN_IPV4_METHOD_OFF: + return 0; + case CONNMAN_IPV4_METHOD_STATIC: + type = CONNMAN_ELEMENT_TYPE_IPV4; + break; + case CONNMAN_IPV4_METHOD_DHCP: + type = CONNMAN_ELEMENT_TYPE_DHCP; + break; + } + + element = connman_element_create(NULL); + if (element != NULL) { + element->type = type; + element->index = device->element.index; + + if (connman_element_register(element, + &device->element) < 0) + connman_element_unref(element); + + device->disconnected = FALSE; + + __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_CONFIGURATION); + } + } else + connman_element_unregister_children(&device->element); + + return 0; +} + +static int set_powered(struct connman_device *device, connman_bool_t powered) { struct connman_device_driver *driver = device->driver; int err; @@ -128,16 +227,20 @@ static int set_powered(struct connman_device *device, gboolean powered) return -EINVAL; if (powered == TRUE) { - if (driver->enable) + if (driver->enable) { err = driver->enable(device); - else + __connman_notifier_device_type_increase(device->type); + } else err = -EINVAL; } else { g_hash_table_remove_all(device->networks); - if (driver->disable) + set_carrier(device, FALSE); + + if (driver->disable) { err = driver->disable(device); - else + __connman_notifier_device_type_decrease(device->type); + } else err = -EINVAL; } @@ -159,6 +262,10 @@ static int set_policy(DBusConnection *connection, return 0; switch (policy) { + case CONNMAN_DEVICE_POLICY_UNKNOWN: + return -EINVAL; + case CONNMAN_DEVICE_POLICY_IGNORE: + break; case CONNMAN_DEVICE_POLICY_OFF: if (device->powered == TRUE) err = set_powered(device, FALSE); @@ -167,8 +274,8 @@ static int set_policy(DBusConnection *connection, case CONNMAN_DEVICE_POLICY_MANUAL: if (device->powered == FALSE) err = set_powered(device, TRUE); - break; - default: + else + err = set_carrier(device, device->carrier); break; } @@ -198,6 +305,15 @@ static int set_policy(DBusConnection *connection, return 0; } +static void append_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(struct connman_device *device, DBusMessageIter *entry) { @@ -212,8 +328,7 @@ static void append_networks(struct connman_device *device, dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter); - __connman_element_list((struct connman_element *) device, - CONNMAN_ELEMENT_TYPE_NETWORK, &iter); + g_hash_table_foreach(device->networks, append_path, &iter); dbus_message_iter_close_container(&value, &iter); dbus_message_iter_close_container(entry, &value); @@ -229,6 +344,10 @@ static DBusMessage *get_properties(DBusConnection *conn, DBG("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; @@ -240,20 +359,19 @@ static DBusMessage *get_properties(DBusConnection *conn, DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - str = type2description(device->type); - if (str != NULL && device->interface != NULL) { - char *name = g_strdup_printf("%s (%s)", str, device->interface); - if (name != NULL) - connman_dbus_dict_append_variant(&dict, "Name", - DBUS_TYPE_STRING, &name); - g_free(name); - } + 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); @@ -263,6 +381,10 @@ static DBusMessage *get_properties(DBusConnection *conn, connman_dbus_dict_append_variant(&dict, "Policy", DBUS_TYPE_STRING, &str); + if (device->priority > 0) + connman_dbus_dict_append_variant(&dict, "Priority", + DBUS_TYPE_BYTE, &device->priority); + connman_dbus_dict_append_variant(&dict, "Powered", DBUS_TYPE_BOOLEAN, &device->powered); @@ -272,10 +394,16 @@ static DBusMessage *get_properties(DBusConnection *conn, switch (device->mode) { case CONNMAN_DEVICE_MODE_UNKNOWN: + break; case CONNMAN_DEVICE_MODE_TRANSPORT_IP: + __connman_element_append_ipv4(&device->element, &dict); break; case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: + if (device->scan_interval > 0) + connman_dbus_dict_append_variant(&dict, "ScanInterval", + DBUS_TYPE_UINT16, &device->scan_interval); + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); append_networks(device, &entry); @@ -294,6 +422,7 @@ static DBusMessage *set_property(DBusConnection *conn, struct connman_device *device = data; DBusMessageIter iter, value; const char *name; + int type; DBG("conn %p", conn); @@ -304,13 +433,19 @@ static DBusMessage *set_property(DBusConnection *conn, dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); - if (__connman_security_check_privileges(msg) < 0) + if (__connman_security_check_privilege(msg, + CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) return __connman_error_permission_denied(msg); + type = dbus_message_iter_get_arg_type(&value); + if (g_str_equal(name, "Powered") == TRUE) { - gboolean powered; + connman_bool_t powered; int err; + if (type != DBUS_TYPE_BOOLEAN) + return __connman_error_invalid_arguments(msg); + dbus_message_iter_get_basic(&value, &powered); if (device->powered == powered) @@ -318,12 +453,15 @@ static DBusMessage *set_property(DBusConnection *conn, err = set_powered(device, powered); if (err < 0 && err != -EINPROGRESS) - return __connman_error_failed(msg); + return __connman_error_failed(msg, -err); } else if (g_str_equal(name, "Policy") == TRUE) { enum connman_device_policy policy; const char *str; int err; + if (type != DBUS_TYPE_STRING) + return __connman_error_invalid_arguments(msg); + dbus_message_iter_get_basic(&value, &str); policy = string2policy(str); if (policy == CONNMAN_DEVICE_POLICY_UNKNOWN) @@ -331,10 +469,131 @@ static DBusMessage *set_property(DBusConnection *conn, err = set_policy(conn, device, policy); if (err < 0) - return __connman_error_failed(msg); + return __connman_error_failed(msg, -err); + } else if (g_str_equal(name, "Priority") == TRUE) { + connman_uint8_t priority; + + if (type != DBUS_TYPE_BYTE) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&value, &priority); + + device->priority = priority; + } else if (g_str_equal(name, "ScanInterval") == TRUE) { + connman_uint16_t interval; + + switch (device->mode) { + case CONNMAN_DEVICE_MODE_UNKNOWN: + case CONNMAN_DEVICE_MODE_TRANSPORT_IP: + return __connman_error_invalid_arguments(msg); + case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: + case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: + break; + } + + if (type != DBUS_TYPE_UINT16) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&value, &interval); + + device->scan_interval = interval; + + if (device->scan_timeout > 0) { + g_source_remove(device->scan_timeout); + device->scan_timeout = 0; + } + + if (device->scan_interval > 0) { + guint interval = device->scan_interval; + device->scan_timeout = g_timeout_add_seconds(interval, + device_scan_trigger, device); + } + } else if (g_str_has_prefix(name, "IPv4") == TRUE) { + switch (device->mode) { + case CONNMAN_DEVICE_MODE_UNKNOWN: + case CONNMAN_DEVICE_MODE_NETWORK_SINGLE: + case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE: + return __connman_error_invalid_arguments(msg); + case CONNMAN_DEVICE_MODE_TRANSPORT_IP: + __connman_element_set_ipv4(&device->element, + name, &value); + break; + } + } + + __connman_storage_save_device(device); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *join_network(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct connman_device *device = data; + struct connman_network *network; + enum connman_network_type type; + DBusMessageIter iter, array; + int err, index; + + DBG("conn %p", conn); + + if (__connman_security_check_privilege(msg, + CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) + return __connman_error_permission_denied(msg); + + if (!device->driver || !device->driver->join) + return __connman_error_not_supported(msg); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &array); + + switch (device->type) { + case CONNMAN_DEVICE_TYPE_WIFI: + type = CONNMAN_NETWORK_TYPE_WIFI; + break; + default: + return __connman_error_not_supported(msg); + } + + network = connman_network_create("00_00_00_00_00_00", type); + if (network == NULL) + return __connman_error_failed(msg, ENOMEM); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key, *str; + + dbus_message_iter_recurse(&array, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + switch (dbus_message_iter_get_arg_type(&value)) { + case DBUS_TYPE_STRING: + dbus_message_iter_get_basic(&value, &str); + if (g_str_equal(key, "WiFi.SSID") == TRUE) + connman_network_set_blob(network, key, + str, strlen(str)); + else + connman_network_set_string(network, key, str); + break; + } + + dbus_message_iter_next(&array); } - __connman_element_store(&device->element); + index = connman_device_get_index(device); + connman_network_set_index(network, index); + + connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP); + + err = device->driver->join(device, network); + + connman_network_unref(network); + + if (err < 0) + return __connman_error_failed(msg, -err); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -344,7 +603,8 @@ static DBusMessage *create_network(DBusConnection *conn, { DBG("conn %p", conn); - if (__connman_security_check_privileges(msg) < 0) + if (__connman_security_check_privilege(msg, + CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) return __connman_error_permission_denied(msg); return __connman_error_invalid_arguments(msg); @@ -355,7 +615,8 @@ static DBusMessage *remove_network(DBusConnection *conn, { DBG("conn %p", conn); - if (__connman_security_check_privileges(msg) < 0) + if (__connman_security_check_privilege(msg, + CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) return __connman_error_permission_denied(msg); return __connman_error_invalid_arguments(msg); @@ -381,9 +642,12 @@ static DBusMessage *propose_scan(DBusConnection *conn, if (!device->driver || !device->driver->scan) return __connman_error_not_supported(msg); + if (device->powered == FALSE) + return __connman_error_failed(msg, EINVAL); + err = device->driver->scan(device); if (err < 0) - return __connman_error_failed(msg); + return __connman_error_failed(msg, -err); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -391,6 +655,7 @@ static DBusMessage *propose_scan(DBusConnection *conn, static GDBusMethodTable device_methods[] = { { "GetProperties", "", "a{sv}", get_properties }, { "SetProperty", "sv", "", set_property }, + { "JoinNetwork", "a{sv}", "", join_network }, { "CreateNetwork", "a{sv}", "o", create_network }, { "RemoveNetwork", "o", "", remove_network }, { "ProposeScan", "", "", propose_scan }, @@ -454,6 +719,8 @@ static int register_interface(struct connman_element *element) return -EIO; } + device->registered = TRUE; + emit_devices_signal(); return 0; @@ -461,14 +728,152 @@ static int register_interface(struct connman_element *element) static void unregister_interface(struct connman_element *element) { + struct connman_device *device = element->device; + DBG("element %p name %s", element, element->name); + device->registered = FALSE; + emit_devices_signal(); g_dbus_unregister_interface(connection, element->path, CONNMAN_DEVICE_INTERFACE); } +static void device_enable(struct connman_device *device) +{ + DBG("device %p", device); + + if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE || + device->policy == CONNMAN_DEVICE_POLICY_OFF) + return; + + if (device->powered == TRUE) + return; + + if (device->driver->enable) { + device->driver->enable(device); + __connman_notifier_device_type_increase(device->type); + } +} + +static void device_disable(struct connman_device *device) +{ + DBG("device %p", device); + + if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE) + return; + + if (device->powered == FALSE) + return; + + g_hash_table_remove_all(device->networks); + + if (device->driver->disable) { + device->driver->disable(device); + __connman_notifier_device_type_decrease(device->type); + } +} + +static int setup_device(struct connman_device *device) +{ + int err; + + DBG("device %p", device); + + err = register_interface(&device->element); + if (err < 0) { + if (device->driver->remove) + device->driver->remove(device); + device->driver = NULL; + return err; + } + + 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->secondary == FALSE) + __connman_profile_add_device(device); + break; + } + + device_enable(device); + + return 0; +} + +static void probe_driver(struct connman_element *element, gpointer user_data) +{ + struct connman_device_driver *driver = user_data; + + DBG("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) + return; + + element->device->driver = driver; + + setup_device(element->device); +} + +static void remove_device(struct connman_device *device) +{ + DBG("device %p", device); + + 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: + if (device->secondary == FALSE) + __connman_profile_remove_device(device); + break; + } + + unregister_interface(&device->element); + + if (device->driver->remove) + 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("element %p name %s", element, element->name); + + if (element->device == NULL) + return; + + if (element->device->driver == driver) + remove_device(element->device); +} + +connman_bool_t __connman_device_has_driver(struct connman_device *device) +{ + if (device == NULL || device->driver == NULL) + return FALSE; + + return device->registered; +} + static GSList *driver_list = NULL; static gint compare_priority(gconstpointer a, gconstpointer b) @@ -494,7 +899,8 @@ int connman_device_driver_register(struct connman_device_driver *driver) driver_list = g_slist_insert_sorted(driver_list, driver, compare_priority); - //__connman_driver_rescan(&device_driver); + __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, + probe_driver, driver); return 0; } @@ -510,6 +916,9 @@ void connman_device_driver_unregister(struct connman_device_driver *driver) DBG("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) @@ -529,10 +938,16 @@ static void device_destruct(struct connman_element *element) DBG("element %p name %s", element, element->name); - g_free(device->path); + g_free(device->ident); + g_free(device->node); + g_free(device->name); + g_free(device->address); g_free(device->interface); + g_free(device->last_network); + g_hash_table_destroy(device->networks); + device->networks = NULL; } /** @@ -548,6 +963,7 @@ struct connman_device *connman_device_create(const char *node, enum connman_device_type type) { struct connman_device *device; + const char *str; DBG("node %s type %d", node, type); @@ -557,41 +973,72 @@ struct connman_device *connman_device_create(const char *node, DBG("device %p", device); - device->element.refcount = 1; + __connman_element_initialize(&device->element); device->element.name = g_strdup(node); device->element.type = CONNMAN_ELEMENT_TYPE_DEVICE; - device->element.index = -1; - - switch (type) { - case CONNMAN_DEVICE_TYPE_ETHERNET: - device->element.subtype = CONNMAN_ELEMENT_SUBTYPE_ETHERNET; - break; - default: - device->element.subtype = CONNMAN_ELEMENT_SUBTYPE_UNKNOWN; - break; - } device->element.device = device; device->element.destruct = device_destruct; - device->type = type; - device->mode = CONNMAN_DEVICE_MODE_UNKNOWN; - device->policy = CONNMAN_DEVICE_POLICY_AUTO; + str = type2string(type); + if (str != NULL) + connman_element_set_string(&device->element, + CONNMAN_PROPERTY_ID_TYPE, str); - device->networks = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, unregister_network); + device->element.ipv4.method = CONNMAN_IPV4_METHOD_DHCP; - return device; -} + device->type = type; + device->name = g_strdup(type2description(device->type)); + device->mode = CONNMAN_DEVICE_MODE_UNKNOWN; + device->policy = CONNMAN_DEVICE_POLICY_AUTO; + device->secondary = FALSE; -/** - * connman_device_ref: - * @device: device structure - * - * Increase reference counter of device - */ -struct connman_device *connman_device_ref(struct connman_device *device) + switch (type) { + case CONNMAN_DEVICE_TYPE_UNKNOWN: + case CONNMAN_DEVICE_TYPE_VENDOR: + device->priority = 0; + device->scan_interval = 0; + break; + case CONNMAN_DEVICE_TYPE_ETHERNET: + case CONNMAN_DEVICE_TYPE_WIFI: + device->priority = 100; + device->scan_interval = 300; + break; + case CONNMAN_DEVICE_TYPE_WIMAX: + device->priority = 20; + device->scan_interval = 0; + break; + case CONNMAN_DEVICE_TYPE_BLUETOOTH: + device->priority = 50; + device->scan_interval = 0; + break; + case CONNMAN_DEVICE_TYPE_GPS: + device->priority = 0; + device->scan_interval = 0; + break; + case CONNMAN_DEVICE_TYPE_HSO: + case CONNMAN_DEVICE_TYPE_NOZOMI: + case CONNMAN_DEVICE_TYPE_HUAWEI: + case CONNMAN_DEVICE_TYPE_NOVATEL: + device->priority = 60; + device->scan_interval = 0; + break; + } + + device->networks = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, unregister_network); + + return device; +} + +/** + * connman_device_ref: + * @device: device structure + * + * Increase reference counter of device + */ +struct connman_device *connman_device_ref(struct connman_device *device) { if (connman_element_ref(&device->element) == NULL) return NULL; @@ -610,20 +1057,31 @@ 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_set_path: + * connman_device_get_type: * @device: device structure - * @path: path name * - * Set path name of device + * Get type of device */ -void connman_device_set_path(struct connman_device *device, const char *path) +enum connman_device_type connman_device_get_type(struct connman_device *device) { - g_free(device->element.devpath); - device->element.devpath = g_strdup(path); + return device->type; +} - g_free(device->path); - device->path = g_strdup(path); +/** + * 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; } /** @@ -634,7 +1092,7 @@ void connman_device_set_path(struct connman_device *device, const char *path) */ const char *connman_device_get_path(struct connman_device *device) { - return device->path; + return device->element.path; } /** @@ -675,6 +1133,13 @@ void connman_device_set_interface(struct connman_device *device, g_free(device->interface); device->interface = g_strdup(interface); + + 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); + } } /** @@ -689,6 +1154,25 @@ const char *connman_device_get_interface(struct connman_device *device) } /** + * 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_policy: * @device: device structure * @policy: power and connection policy @@ -715,6 +1199,41 @@ void connman_device_set_mode(struct connman_device *device, } /** + * 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_set_secondary: + * @device: device structure + * @secondary: secondary value + * + * Change secondary value of device + */ +void connman_device_set_secondary(struct connman_device *device, + connman_bool_t secondary) +{ + device->secondary = secondary; +} + +/** + * connman_device_get_secondary: + * @device: device structure + * + * Get secondary value of device + */ +connman_bool_t connman_device_get_secondary(struct connman_device *device) +{ + return device->secondary; +} + +/** * connman_device_set_powered: * @device: device structure * @powered: powered state @@ -735,6 +1254,9 @@ int connman_device_set_powered(struct connman_device *device, device->powered = powered; + if (device->registered == FALSE) + return 0; + signal = dbus_message_new_signal(device->element.path, CONNMAN_DEVICE_INTERFACE, "PropertyChanged"); if (signal == NULL) @@ -751,6 +1273,26 @@ int connman_device_set_powered(struct connman_device *device, g_dbus_send_message(connection, signal); + if (powered == FALSE) + return 0; + + if (device->policy != CONNMAN_DEVICE_POLICY_AUTO) + return 0; + + if (device->scan_timeout > 0) { + g_source_remove(device->scan_timeout); + device->scan_timeout = 0; + } + + if (device->scan_interval > 0) { + guint interval = device->scan_interval; + device->scan_timeout = g_timeout_add_seconds(interval, + device_scan_trigger, device); + } + + if (device->driver->scan) + device->driver->scan(device); + return 0; } @@ -780,25 +1322,141 @@ int connman_device_set_carrier(struct connman_device *device, device->carrier = carrier; - if (carrier == TRUE) { - struct connman_element *element; + return set_carrier(device, device->carrier); +} - element = connman_element_create(NULL); - if (element != NULL) { - element->type = CONNMAN_ELEMENT_TYPE_DHCP; - element->subtype = device->element.subtype; - element->index = device->element.index; +int __connman_device_connect(struct connman_device *device) +{ + DBG("device %p", device); - if (connman_element_register(element, - &device->element) < 0) - connman_element_unref(element); - } - } else - connman_element_unregister_children(&device->element); + if (device->disconnected == FALSE) + return -EINVAL; return 0; } +int __connman_device_disconnect(struct connman_device *device) +{ + GHashTableIter iter; + gpointer key, value; + + DBG("device %p", device); + + connman_device_set_disconnected(device, TRUE); + + g_hash_table_iter_init(&iter, device->networks); + + while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { + struct connman_network *network = value; + + if (connman_network_get_connected(network) == TRUE) + __connman_network_disconnect(network); + } + + return 0; +} + +static void connect_known_network(struct connman_device *device) +{ + struct connman_network *network = NULL; + GHashTableIter iter; + gpointer key, value; + unsigned int count = 0; + + DBG("device %p", device); + + g_hash_table_iter_init(&iter, device->networks); + + while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { + connman_uint8_t old_priority, new_priority; + connman_uint8_t old_strength, new_strength; + const char *name; + + count++; + + if (connman_network_get_available(value) == FALSE) + continue; + + name = connman_network_get_string(value, + CONNMAN_PROPERTY_ID_NAME); + if (name != NULL && device->last_network != NULL) { + if (g_str_equal(name, device->last_network) == TRUE) { + network = value; + break; + } + } + + if (connman_network_get_remember(value) == FALSE) + continue; + + if (network == NULL) { + network = value; + continue; + } + + old_priority = connman_network_get_uint8(network, + CONNMAN_PROPERTY_ID_PRIORITY); + new_priority = connman_network_get_uint8(value, + CONNMAN_PROPERTY_ID_PRIORITY); + + if (new_priority != old_priority) { + if (new_priority > old_priority) + network = value; + continue; + } + + old_strength = connman_network_get_uint8(network, + CONNMAN_PROPERTY_ID_STRENGTH); + new_strength = connman_network_get_uint8(value, + CONNMAN_PROPERTY_ID_STRENGTH); + + if (new_strength > old_strength) + network = value; + } + + if (network != NULL) { + int err; + + err = connman_network_connect(network); + if (err == 0 || err == -EINPROGRESS) + return; + } + + if (count > 0) + return; + + if (device->driver && device->driver->scan) + device->driver->scan(device); +} + +static void mark_network_unavailable(gpointer key, gpointer value, + gpointer user_data) +{ + struct connman_network *network = value; + + if (connman_network_get_connected(network) == TRUE) + return; + + connman_network_set_available(network, FALSE); +} + +static gboolean remove_unavailable_network(gpointer key, gpointer value, + gpointer user_data) +{ + struct connman_network *network = value; + + if (connman_network_get_connected(network) == TRUE) + return FALSE; + + if (connman_network_get_remember(network) == TRUE) + return FALSE; + + if (connman_network_get_available(network) == TRUE) + return FALSE; + + return TRUE; +} + /** * connman_device_set_scanning: * @device: device structure @@ -839,10 +1497,160 @@ int connman_device_set_scanning(struct connman_device *device, g_dbus_send_message(connection, signal); + if (scanning == TRUE) { + if (device->scan_timeout > 0) { + g_source_remove(device->scan_timeout); + device->scan_timeout = 0; + } + + if (device->scan_interval > 0) { + guint interval = device->scan_interval; + device->scan_timeout = g_timeout_add_seconds(interval, + device_scan_trigger, device); + } + + g_hash_table_foreach(device->networks, + mark_network_unavailable, NULL); + return 0; + } + + g_hash_table_foreach_remove(device->networks, + remove_unavailable_network, NULL); + + if (device->connections > 0) + return 0; + + if (device->disconnected == TRUE) + return 0; + + if (device->policy != CONNMAN_DEVICE_POLICY_AUTO) + return 0; + + connect_known_network(device); + + return 0; +} + +/** + * connman_device_set_disconnected: + * @device: device structure + * @disconnected: disconnected state + * + * Change disconnected state of device (only for device with networks) + */ +int connman_device_set_disconnected(struct connman_device *device, + connman_bool_t disconnected) +{ + DBG("driver %p disconnected %d", device, disconnected); + + 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; + } + + if (device->disconnected == disconnected) + return -EALREADY; + + device->disconnected = disconnected; + return 0; } /** + * 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 %p key %s value %s", device, 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); + } + + 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 %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; + + return connman_element_get_string(&device->element, key); +} + +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("element %p name %s", element, element->name); + + if (device == NULL) + return; + + powered = (offlinemode == TRUE) ? FALSE : TRUE; + + if (device->powered == powered) + return; + + set_powered(device, powered); +} + +int __connman_device_set_offlinemode(connman_bool_t offlinemode) +{ + DBG("offlinmode %d", offlinemode); + + __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, + set_offlinemode, GUINT_TO_POINTER(offlinemode)); + + __connman_notifier_offline_mode(offlinemode); + + return 0; +} + +void __connman_device_increase_connections(struct connman_device *device) +{ + device->connections++; +} + +void __connman_device_decrease_connections(struct connman_device *device) +{ + device->connections--; +} + +/** * connman_device_add_network: * @device: device structure * @network: network structure @@ -868,6 +1676,8 @@ int connman_device_add_network(struct connman_device *device, __connman_network_set_device(network, device); + __connman_storage_load_network(network); + err = connman_element_register((struct connman_element *) network, &device->element); if (err < 0) { @@ -913,6 +1723,20 @@ int connman_device_remove_network(struct connman_device *device, return 0; } +void __connman_device_set_network(struct connman_device *device, + struct connman_network *network) +{ + const char *name; + + if (network != NULL) { + name = connman_network_get_string(network, + CONNMAN_PROPERTY_ID_NAME); + device->last_network = g_strdup(name); + } + + device->network = network; +} + /** * connman_device_register: * @device: device structure @@ -921,6 +1745,18 @@ int connman_device_remove_network(struct connman_device *device, */ int connman_device_register(struct connman_device *device) { + __connman_storage_load_device(device); + + 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: + __connman_storage_init_network(device); + break; + } + return connman_element_register(&device->element, NULL); } @@ -932,6 +1768,8 @@ int connman_device_register(struct connman_device *device) */ void connman_device_unregister(struct connman_device *device) { + __connman_storage_save_device(device); + connman_element_unregister(&device->element); } @@ -958,37 +1796,6 @@ void connman_device_set_data(struct connman_device *device, void *data) device->driver_data = data; } -static void device_enable(struct connman_device *device) -{ - DBG("device %p", device); - - if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE || - device->policy == CONNMAN_DEVICE_POLICY_OFF) - return; - - if (device->powered == TRUE) - return; - - if (device->driver->enable) - device->driver->enable(device); -} - -static void device_disable(struct connman_device *device) -{ - DBG("device %p", device); - - if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE) - return; - - if (device->powered == FALSE) - return; - - g_hash_table_remove_all(device->networks); - - if (device->driver->disable) - device->driver->disable(device); -} - static gboolean match_driver(struct connman_device *device, struct connman_device_driver *driver) { @@ -1003,13 +1810,15 @@ static int device_probe(struct connman_element *element) { struct connman_device *device = element->device; GSList *list; - int err; DBG("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; @@ -1027,16 +1836,7 @@ static int device_probe(struct connman_element *element) if (device->driver == NULL) return -ENODEV; - err = register_interface(element); - if (err < 0) { - if (device->driver->remove) - device->driver->remove(device); - return err; - } - - device_enable(device); - - return 0; + return setup_device(device); } static void device_remove(struct connman_element *element) @@ -1051,12 +1851,7 @@ static void device_remove(struct connman_element *element) if (device->driver == NULL) return; - device_disable(device); - - unregister_interface(element); - - if (device->driver->remove) - device->driver->remove(device); + remove_device(device); } static struct connman_driver device_driver = { @@ -1067,12 +1862,160 @@ static struct connman_driver device_driver = { .remove = device_remove, }; +static int device_load(struct connman_device *device) +{ + GKeyFile *keyfile; + gchar *pathname, *data = NULL; + gsize length; + char *str; + int val; + + DBG("device %p", device); + + pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, + device->element.name); + if (pathname == NULL) + return -ENOMEM; + + keyfile = g_key_file_new(); + + if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) { + g_free(pathname); + return -ENOENT; + } + + g_free(pathname); + + if (g_key_file_load_from_data(keyfile, data, length, + 0, NULL) == FALSE) { + g_free(data); + return -EILSEQ; + } + + g_free(data); + + str = g_key_file_get_string(keyfile, "Configuration", "Policy", NULL); + if (str != NULL) { + device->policy = string2policy(str); + g_free(str); + } + + val = g_key_file_get_integer(keyfile, "Configuration", + "Priority", NULL); + if (val > 0) + device->priority = val; + + 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, "Configuration", + "ScanInterval", NULL); + if (val > 0) + device->scan_interval = val; + break; + } + +#if 0 + str = g_key_file_get_string(keyfile, "Configuration", + "LastNetwork", NULL); + if (str != NULL) + device->last_network = str; +#endif + + g_key_file_free(keyfile); + + return 0; +} + +static int device_save(struct connman_device *device) +{ + GKeyFile *keyfile; + gchar *pathname, *data = NULL; + gsize length; + const char *str; + + DBG("device %p", device); + + pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, + device->element.name); + if (pathname == NULL) + return -ENOMEM; + + keyfile = g_key_file_new(); + + if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) + goto update; + + if (length > 0) { + if (g_key_file_load_from_data(keyfile, data, length, + 0, NULL) == FALSE) + goto done; + } + + g_free(data); + +update: + str = policy2string(device->policy); + if (str != NULL) + g_key_file_set_string(keyfile, "Configuration", "Policy", str); + + if (device->priority > 0) + g_key_file_set_integer(keyfile, "Configuration", + "Priority", device->priority); + + 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 > 0) + g_key_file_set_integer(keyfile, "Configuration", + "ScanInterval", device->scan_interval); + break; + } + + if (device->last_network != NULL) + g_key_file_set_string(keyfile, "Configuration", + "LastNetwork", device->last_network); + + data = g_key_file_to_data(keyfile, &length, NULL); + + if (g_file_set_contents(pathname, data, length, NULL) == FALSE) + connman_error("Failed to store device information"); + +done: + g_free(data); + + g_key_file_free(keyfile); + + g_free(pathname); + + if (device->network != NULL) + __connman_storage_save_network(device->network); + + 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) { DBG(""); connection = connman_dbus_get_connection(); + if (connman_storage_register(&device_storage) < 0) + connman_error("Failed to register device storage"); + return connman_driver_register(&device_driver); } @@ -1082,5 +2025,7 @@ void __connman_device_cleanup(void) connman_driver_unregister(&device_driver); + connman_storage_unregister(&device_storage); + dbus_connection_unref(connection); }