*
* 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
#define MAXIMUM_RETRIES 4
#define BGSCAN_DEFAULT "simple:30:-45:300"
-#define AUTOSCAN_DEFAULT "exponential:2:3600"
+#define AUTOSCAN_DEFAULT "exponential:3:300"
-struct connman_technology *wifi_technology = NULL;
+static struct connman_technology *wifi_technology = NULL;
struct hidden_params {
char ssid[32];
autoscan = wifi->autoscan;
- if (autoscan->timeout > 0)
- g_source_remove(autoscan->timeout);
+ if (autoscan->timeout == 0 && autoscan->interval == 0)
+ return;
+
+ g_source_remove(autoscan->timeout);
autoscan->timeout = 0;
autoscan->interval = 0;
+ connman_device_set_scanning(device, FALSE);
+
connman_device_unref(device);
}
stop_autoscan(device);
+ /* In case of a user scan, device is still referenced */
+ if (connman_device_get_scanning(device) == TRUE)
+ connman_device_unref(wifi->device);
+
iface_list = g_list_remove(iface_list, wifi);
remove_networks(device, wifi);
g_free(wifi);
}
+static int add_scan_param(gchar *hex_ssid, int freq,
+ GSupplicantScanParams *scan_data,
+ int driver_max_scan_ssids)
+{
+ unsigned int i;
+ struct scan_ssid *scan_ssid;
+
+ if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) {
+ gchar *ssid;
+ unsigned int j = 0, hex;
+ size_t hex_ssid_len = strlen(hex_ssid);
+
+ ssid = g_try_malloc0(hex_ssid_len / 2);
+ if (ssid == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < hex_ssid_len; i += 2) {
+ sscanf(hex_ssid + i, "%02x", &hex);
+ ssid[j++] = hex;
+ }
+
+ scan_ssid = g_try_new(struct scan_ssid, 1);
+ if (scan_ssid == NULL) {
+ g_free(ssid);
+ return -ENOMEM;
+ }
+
+ memcpy(scan_ssid->ssid, ssid, j);
+ scan_ssid->ssid_len = j;
+ scan_data->ssids = g_slist_prepend(scan_data->ssids,
+ scan_ssid);
+
+ scan_data->num_ssids++;
+
+ g_free(ssid);
+ } else
+ return -EINVAL;
+
+ scan_data->ssids = g_slist_reverse(scan_data->ssids);
+
+ if (scan_data->freqs == NULL) {
+ scan_data->freqs = g_try_malloc0(sizeof(uint16_t) *
+ scan_data->num_ssids);
+ if (scan_data->freqs == NULL) {
+ g_slist_free_full(scan_data->ssids, g_free);
+ return -ENOMEM;
+ }
+ } else {
+ scan_data->freqs = g_try_realloc(scan_data->freqs,
+ sizeof(uint16_t) * scan_data->num_ssids);
+ if (scan_data->freqs == NULL) {
+ g_slist_free_full(scan_data->ssids, g_free);
+ return -ENOMEM;
+ }
+ scan_data->freqs[scan_data->num_ssids - 1] = 0;
+ }
+
+ /* Don't add duplicate entries */
+ for (i = 0; i < scan_data->num_ssids; i++) {
+ if (scan_data->freqs[i] == 0) {
+ scan_data->freqs[i] = freq;
+ break;
+ } else if (scan_data->freqs[i] == freq)
+ break;
+ }
+
+ return 0;
+}
+
+static int get_hidden_connections(int max_ssids,
+ GSupplicantScanParams *scan_data)
+{
+ GKeyFile *keyfile;
+ gchar **services;
+ char *ssid;
+ gchar *str;
+ int i, freq;
+ gboolean value;
+ int num_ssids = 0, add_param_failed = 0;
+
+ services = connman_storage_get_services();
+ for (i = 0; services && services[i]; i++) {
+ if (strncmp(services[i], "wifi_", 5) != 0)
+ continue;
+
+ keyfile = connman_storage_load_service(services[i]);
+
+ value = g_key_file_get_boolean(keyfile,
+ services[i], "Hidden", NULL);
+ if (value == FALSE) {
+ g_key_file_free(keyfile);
+ continue;
+ }
+
+ value = g_key_file_get_boolean(keyfile,
+ services[i], "Favorite", NULL);
+ if (value == FALSE) {
+ g_key_file_free(keyfile);
+ continue;
+ }
+
+ value = g_key_file_get_boolean(keyfile,
+ services[i], "AutoConnect", NULL);
+ if (value == FALSE) {
+ g_key_file_free(keyfile);
+ continue;
+ }
+
+ ssid = g_key_file_get_string(keyfile,
+ services[i], "SSID", NULL);
+
+ freq = g_key_file_get_integer(keyfile, services[i],
+ "Frequency", NULL);
+
+ if (add_scan_param(ssid, freq, scan_data, max_ssids) < 0) {
+ str = g_key_file_get_string(keyfile,
+ services[i], "Name", NULL);
+ DBG("Cannot scan %s (%s)", ssid, str);
+ g_free(str);
+ add_param_failed++;
+ }
+
+ num_ssids++;
+
+ g_key_file_free(keyfile);
+ }
+
+ if (add_param_failed > 0)
+ connman_warn("Unable to scan %d out of %d SSIDs (max is %d)",
+ add_param_failed, num_ssids, max_ssids);
+
+ g_strfreev(services);
+
+ return num_ssids > max_ssids ? max_ssids : num_ssids;
+}
+
static int throw_wifi_scan(struct connman_device *device,
GSupplicantInterfaceCallback callback)
{
if (wifi->tethering == TRUE)
return 0;
+ if (connman_device_get_scanning(device) == TRUE)
+ return -EALREADY;
+
connman_device_ref(device);
ret = g_supplicant_interface_scan(wifi->interface, NULL,
return ret;
}
+static void hidden_scan_callback(int result,
+ GSupplicantInterface *interface, void *user_data)
+{
+ struct connman_device *device = user_data;
+
+ DBG("result %d", result);
+
+ connman_device_set_scanning(device, FALSE);
+ connman_device_unref(device);
+}
+
static void autoscan_scan_callback(int result,
GSupplicantInterface *interface, void *user_data)
{
struct connman_device *device = user_data;
+ struct wifi_data *wifi = connman_device_get_data(device);
+ int driver_max_ssids;
- DBG("");
+ DBG("result %d", result);
+ /*
+ * Scan hidden networks so that we can autoconnect to them.
+ */
+ driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+ wifi->interface);
+ DBG("max ssids %d", driver_max_ssids);
+
+ if (driver_max_ssids > 0) {
+ GSupplicantScanParams *scan_params;
+ int ret;
+
+ scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+ if (scan_params == NULL)
+ goto out;
+
+ if (get_hidden_connections(driver_max_ssids,
+ scan_params) > 0) {
+ ret = g_supplicant_interface_scan(wifi->interface,
+ scan_params,
+ hidden_scan_callback,
+ device);
+ if (ret == 0)
+ return;
+ }
+
+ g_supplicant_free_scan_params(scan_params);
+ }
+
+out:
connman_device_set_scanning(device, FALSE);
+ connman_device_unref(device);
}
static gboolean autoscan_timeout(gpointer data)
start_autoscan(device);
}
-static int add_scan_param(gchar *hex_ssid, int freq,
- GSupplicantScanParams *scan_data,
- int driver_max_scan_ssids)
-{
- unsigned int i;
-
- if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) {
- gchar *ssid;
- unsigned int j = 0, hex;
- size_t hex_ssid_len = strlen(hex_ssid);
-
- ssid = g_try_malloc0(hex_ssid_len / 2);
- if (ssid == NULL)
- return -ENOMEM;
-
- for (i = 0; i < hex_ssid_len; i += 2) {
- sscanf(hex_ssid + i, "%02x", &hex);
- ssid[j++] = hex;
- }
-
- memcpy(scan_data->ssids[scan_data->num_ssids].ssid, ssid, j);
- scan_data->ssids[scan_data->num_ssids].ssid_len = j;
- scan_data->num_ssids++;
-
- g_free(ssid);
- }
-
- /* Don't add duplicate entries */
- for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
- if (scan_data->freqs[i] == 0) {
- scan_data->freqs[i] = freq;
- break;
- } else if (scan_data->freqs[i] == freq)
- break;
- }
-
- return 0;
-}
-
struct last_connected {
GTimeVal modified;
gchar *ssid;
g_strfreev(services);
- num_ssids = num_ssids > G_SUPPLICANT_MAX_FAST_SCAN ?
- G_SUPPLICANT_MAX_FAST_SCAN : num_ssids;
+ num_ssids = num_ssids > max_ssids ? max_ssids : num_ssids;
iter = g_sequence_get_begin_iter(latest_list);
if (wifi->tethering == TRUE)
return 0;
+ stop_autoscan(device);
+
+ if (connman_device_get_scanning(device) == TRUE)
+ return -EALREADY;
+
driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
wifi->interface);
DBG("max ssids %d", driver_max_ssids);
ret = get_latest_connections(driver_max_ssids, scan_params);
if (ret <= 0) {
- g_free(scan_params);
+ g_supplicant_free_scan_params(scan_params);
return wifi_scan(device);
}
- stop_autoscan(device);
-
connman_device_ref(device);
ret = g_supplicant_interface_scan(wifi->interface, scan_params,
scan_callback, device);
if (ret == 0)
connman_device_set_scanning(device, TRUE);
else {
- g_free(scan_params);
+ g_supplicant_free_scan_params(scan_params);
connman_device_unref(device);
}
return ret;
}
+/*
+ * This func is only used when connecting to this specific AP first time.
+ * It is not used when system autoconnects to hidden AP.
+ */
static int wifi_scan_hidden(struct connman_device *device,
const char *ssid, unsigned int ssid_len,
const char *identity, const char* passphrase)
{
struct wifi_data *wifi = connman_device_get_data(device);
GSupplicantScanParams *scan_params = NULL;
+ struct scan_ssid *scan_ssid;
struct hidden_params *hidden;
int ret;
if (ssid == NULL || ssid_len == 0 || ssid_len > 32)
return -EINVAL;
+ stop_autoscan(device);
+
+ if (connman_device_get_scanning(device) == TRUE)
+ return -EALREADY;
+
scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
if (scan_params == NULL)
return -ENOMEM;
- memcpy(scan_params->ssids[0].ssid, ssid, ssid_len);
- scan_params->ssids[0].ssid_len = ssid_len;
+
+ scan_ssid = g_try_new(struct scan_ssid, 1);
+ if (scan_ssid == NULL) {
+ g_free(scan_params);
+ return -ENOMEM;
+ }
+
+ memcpy(scan_ssid->ssid, ssid, ssid_len);
+ scan_ssid->ssid_len = ssid_len;
+ scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid);
+
scan_params->num_ssids = 1;
hidden = g_try_new0(struct hidden_params, 1);
hidden->passphrase = g_strdup(passphrase);
wifi->hidden = hidden;
- stop_autoscan(device);
-
connman_device_ref(device);
ret = g_supplicant_interface_scan(wifi->interface, scan_params,
scan_callback, device);
connman_device_set_scanning(device, TRUE);
else {
connman_device_unref(device);
- g_free(scan_params);
+ g_supplicant_free_scan_params(scan_params);
hidden_free(wifi->hidden);
wifi->hidden = NULL;
}
ssid->use_wps = connman_network_get_bool(network, "WiFi.UseWPS");
ssid->pin_wps = connman_network_get_string(network, "WiFi.PinWPS");
- ssid->bgscan = BGSCAN_DEFAULT;
+ if (connman_setting_get_bool("BackgroundScanning") == TRUE)
+ ssid->bgscan = BGSCAN_DEFAULT;
}
static int network_connect(struct connman_network *network)
case G_SUPPLICANT_STATE_AUTHENTICATING:
case G_SUPPLICANT_STATE_ASSOCIATING:
- connman_network_set_associating(network, TRUE);
+ stop_autoscan(device);
+
+ if (wifi->connected == FALSE)
+ connman_network_set_associating(network, TRUE);
+
break;
case G_SUPPLICANT_STATE_COMPLETED:
+ /* though it should be already stopped: */
+ stop_autoscan(device);
+
if (handle_wps_completion(interface, network, device, wifi) ==
FALSE)
break;
- /* reset scan trigger and schedule background scan */
- connman_device_schedule_scan(device);
-
connman_network_set_connected(network, TRUE);
break;
FALSE) != 0)
DBG("Could not disables selected network");
- connman_network_set_associating(network, FALSE);
connman_network_set_connected(network, FALSE);
+ connman_network_set_associating(network, FALSE);
+ wifi->disconnecting = FALSE;
+
+ start_autoscan(device);
+
break;
case G_SUPPLICANT_STATE_INACTIVE:
connman_network_set_associating(network, FALSE);
+ start_autoscan(device);
+
break;
case G_SUPPLICANT_STATE_UNKNOWN:
wifi->state = state;
+ /* Saving wpa_s state policy:
+ * If connected and if the state changes are roaming related:
+ * --> We stay connected
+ * If completed
+ * --> We are connected
+ * All other case:
+ * --> We are not connected
+ * */
+ switch (state) {
+ case G_SUPPLICANT_STATE_AUTHENTICATING:
+ case G_SUPPLICANT_STATE_ASSOCIATING:
+ case G_SUPPLICANT_STATE_ASSOCIATED:
+ case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+ case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+ if (wifi->connected == TRUE)
+ connman_warn("Probably roaming right now!"
+ " Staying connected...");
+ else
+ wifi->connected = FALSE;
+ break;
+ case G_SUPPLICANT_STATE_COMPLETED:
+ wifi->connected = TRUE;
+ break;
+ default:
+ wifi->connected = FALSE;
+ break;
+ }
+
DBG("DONE");
}
if (ssid != NULL)
connman_network_set_group(network, group);
- if (wifi->hidden != NULL) {
+ if (wifi->hidden != NULL && ssid != NULL) {
if (wifi->hidden->ssid_len == ssid_len &&
memcmp(wifi->hidden->ssid, ssid,
ssid_len) == 0) {