#include <connman/device.h>
#include <connman/rtnl.h>
#include <connman/technology.h>
+#include <connman/service.h>
#include <connman/log.h>
#include <connman/option.h>
#include <connman/storage.h>
#define CLEANUP_TIMEOUT 8 /* in seconds */
#define INACTIVE_TIMEOUT 12 /* in seconds */
-#define MAXIMUM_RETRIES 4
+#define MAXIMUM_RETRIES 2
+#define FAVORITE_MAXIMUM_RETRIES 4
#define BGSCAN_DEFAULT "simple:30:-45:300"
#define AUTOSCAN_DEFAULT "exponential:3:300"
GSList *networks;
GSupplicantInterface *interface;
GSupplicantState state;
+ connman_bool_t disabling;
connman_bool_t connected;
connman_bool_t disconnecting;
connman_bool_t tethering;
DBG("index %d flags %d change %d", wifi->index, flags, change);
- if (!change)
- return;
-
if ((wifi->flags & IFF_UP) != (flags & IFF_UP)) {
if (flags & IFF_UP)
DBG("interface up");
if (wifi == NULL)
return -ENOMEM;
+ wifi->disabling = FALSE;
wifi->connected = FALSE;
wifi->disconnecting = FALSE;
wifi->tethering = FALSE;
static void stop_autoscan(struct connman_device *device)
{
+ const struct wifi_data *wifi = connman_device_get_data(device);
+
+ if (wifi == NULL || wifi->autoscan == NULL)
+ return;
+
reset_autoscan(device);
connman_device_set_scanning(device, FALSE);
g_free(wifi);
}
+static gboolean is_duplicate(GSList *list, gchar *ssid, int ssid_len)
+{
+ GSList *iter;
+
+ for (iter = list; iter != NULL; iter = g_slist_next(iter)) {
+ struct scan_ssid *scan_ssid = iter->data;
+
+ if (ssid_len == scan_ssid->ssid_len &&
+ memcmp(ssid, scan_ssid->ssid, ssid_len) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static int add_scan_param(gchar *hex_ssid, char *raw_ssid, int ssid_len,
int freq, GSupplicantScanParams *scan_data,
- int driver_max_scan_ssids)
+ int driver_max_scan_ssids, char *ssid_name)
{
unsigned int i;
struct scan_ssid *scan_ssid;
j = ssid_len;
}
+ /*
+ * If we have already added hidden AP to the list,
+ * then do not do it again. This might happen if you have
+ * used or are using multiple wifi cards, so in that case
+ * you might have multiple service files for same AP.
+ */
+ if (is_duplicate(scan_data->ssids, ssid, j) == TRUE)
+ return 0;
+
scan_ssid = g_try_new(struct scan_ssid, 1);
if (scan_ssid == NULL) {
g_free(ssid);
scan_data->num_ssids++;
+ DBG("SSID %s added to scanned list of %d entries", ssid_name,
+ scan_data->num_ssids);
+
if (hex_ssid != NULL)
g_free(ssid);
} else
break;
}
- return 0;
+ return 1;
}
static int get_hidden_connections(int max_ssids,
struct connman_config_entry **entries;
GKeyFile *keyfile;
gchar **services;
- char *ssid;
- gchar *str;
- int i, freq;
+ char *ssid, *name;
+ int i, freq, ret;
gboolean value;
int num_ssids = 0, add_param_failed = 0;
continue;
keyfile = connman_storage_load_service(services[i]);
+ if (keyfile == NULL)
+ continue;
value = g_key_file_get_boolean(keyfile,
services[i], "Hidden", NULL);
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, NULL, 0, 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++;
- }
+ name = g_key_file_get_string(keyfile, services[i], "Name",
+ NULL);
- num_ssids++;
+ ret = add_scan_param(ssid, NULL, 0, freq, scan_data,
+ max_ssids, name);
+ if (ret < 0)
+ add_param_failed++;
+ else if (ret > 0)
+ num_ssids++;
+ g_free(name);
g_key_file_free(keyfile);
}
/*
* Check if there are any hidden AP that needs to be provisioned.
*/
- entries = connman_config_get_entries();
+ entries = connman_config_get_entries("wifi");
for (i = 0; entries && entries[i]; i++) {
int len;
if (ssid == NULL)
continue;
- DBG("[%d]->ssid = %s", i, ssid);
-
- if (add_scan_param(NULL, ssid, len, 0, scan_data,
- max_ssids) < 0) {
- DBG("Cannot scan %s (%s)", ssid, entries[i]->ident);
+ ret = add_scan_param(NULL, ssid, len, 0, scan_data,
+ max_ssids, ssid);
+ if (ret < 0)
add_param_failed++;
- }
-
- num_ssids++;
+ else if (ret > 0)
+ num_ssids++;
}
connman_config_free_entries(entries);
if (add_param_failed > 0)
- connman_warn("Unable to scan %d out of %d SSIDs (max is %d)",
+ DBG("Unable to scan %d out of %d SSIDs (max is %d)",
add_param_failed, num_ssids, max_ssids);
g_strfreev(services);
DBG("device %p %p", device, wifi->interface);
if (wifi->tethering == TRUE)
- return 0;
+ return -EBUSY;
if (connman_device_get_scanning(device) == TRUE)
return -EALREADY;
connman_device_reset_scanning(device);
connman_device_set_scanning(device, FALSE);
- start_autoscan(device);
- connman_device_unref(device);
+
+ if (result != -ENOLINK)
+ start_autoscan(device);
+
+ /*
+ * If we are here then we were scanning; however, if we are also
+ * mid-flight disabling the interface, then wifi_disable has
+ * already unreferenced the device and we needn't do it here.
+ */
+
+ if (wifi->disabling != TRUE)
+ connman_device_unref(device);
}
static void scan_callback_hidden(int result,
{
struct connman_device *device = user_data;
struct wifi_data *wifi = connman_device_get_data(device);
- int driver_max_ssids;
+ GSupplicantScanParams *scan_params;
+ int driver_max_ssids, ret;
DBG("result %d wifi %p", result, wifi);
/*
* Scan hidden networks so that we can autoconnect to them.
+ * We will assume 1 as a default number of ssid to scan.
*/
driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
wifi->interface);
- DBG("max ssids %d", driver_max_ssids);
+ if (driver_max_ssids == 0)
+ driver_max_ssids = 1;
- if (driver_max_ssids > 0) {
- GSupplicantScanParams *scan_params;
- int ret;
+ DBG("max ssids %d", driver_max_ssids);
- scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
- if (scan_params == NULL)
- goto out;
+ 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,
+ if (get_hidden_connections(driver_max_ssids, scan_params) > 0) {
+ ret = g_supplicant_interface_scan(wifi->interface,
scan_params,
scan_callback,
device);
- if (ret == 0)
- return;
- }
-
- g_supplicant_free_scan_params(scan_params);
+ if (ret == 0)
+ return;
}
+ g_supplicant_free_scan_params(scan_params);
+
out:
scan_callback(result, interface, user_data);
}
}
}
+/*
+ * The sole function of this callback is to avoid a race between scan completion
+ * and wifi_disable that can otherwise cause a reference count underflow if the
+ * disabling state is not tracked and observed.
+ */
+static void interface_remove_callback(int result,
+ GSupplicantInterface *interface,
+ void *user_data)
+{
+ struct wifi_data *wifi = user_data;
+
+ DBG("result %d ifname %s, wifi %p", result,
+ g_supplicant_interface_get_ifname(interface),
+ wifi);
+
+ if (result < 0 || wifi == NULL)
+ return;
+
+ wifi->disabling = FALSE;
+}
+
static int wifi_enable(struct connman_device *device)
{
struct wifi_data *wifi = connman_device_get_data(device);
if (ret < 0)
return ret;
+ wifi->disabling = FALSE;
+
return -EINPROGRESS;
}
remove_networks(device, wifi);
- ret = g_supplicant_interface_remove(wifi->interface, NULL, NULL);
+ ret = g_supplicant_interface_remove(wifi->interface,
+ interface_remove_callback,
+ wifi);
if (ret < 0)
return ret;
+ wifi->disabling = TRUE;
+
return -EINPROGRESS;
}
continue;
keyfile = connman_storage_load_service(services[i]);
+ if (keyfile == NULL)
+ continue;
str = g_key_file_get_string(keyfile,
services[i], "Favorite", NULL);
entry->modified.tv_sec);
add_scan_param(entry->ssid, NULL, 0, entry->freq, scan_data,
- max_ssids);
+ max_ssids, entry->ssid);
iter = g_sequence_iter_next(iter);
}
return num_ssids;
}
-static int wifi_scan(struct connman_device *device)
+static int wifi_scan_simple(struct connman_device *device)
{
reset_autoscan(device);
return throw_wifi_scan(device, scan_callback_hidden);
}
-static int wifi_scan_fast(struct connman_device *device)
+/*
+ * Note that the hidden scan is only used when connecting to this specific
+ * hidden AP first time. It is not used when system autoconnects to hidden AP.
+ */
+static int wifi_scan(struct connman_device *device,
+ const char *ssid, unsigned int ssid_len,
+ const char *identity, const char* passphrase,
+ gpointer user_data)
{
struct wifi_data *wifi = connman_device_get_data(device);
GSupplicantScanParams *scan_params = NULL;
+ struct scan_ssid *scan_ssid;
+ struct hidden_params *hidden;
int ret;
int driver_max_ssids = 0;
+ connman_bool_t do_hidden;
if (wifi == NULL)
return -ENODEV;
- DBG("device %p %p", device, wifi->interface);
+ DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, ssid);
if (wifi->tethering == TRUE)
return 0;
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);
- if (driver_max_ssids == 0)
- return wifi_scan(device);
-
- scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
- if (scan_params == NULL)
- return -ENOMEM;
+ if (ssid == NULL || ssid_len == 0 || ssid_len > 32) {
+ do_hidden = FALSE;
+ } else {
+ if (wifi->hidden != NULL)
+ return -EBUSY;
- ret = get_latest_connections(driver_max_ssids, scan_params);
- if (ret <= 0) {
- g_supplicant_free_scan_params(scan_params);
- return wifi_scan(device);
+ do_hidden = TRUE;
}
- connman_device_ref(device);
- reset_autoscan(device);
-
- ret = g_supplicant_interface_scan(wifi->interface, scan_params,
- scan_callback, device);
- if (ret == 0)
- connman_device_set_scanning(device, TRUE);
- else {
- g_supplicant_free_scan_params(scan_params);
- connman_device_unref(device);
+ if (do_hidden == FALSE) {
+ driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+ wifi->interface);
+ DBG("max ssids %d", driver_max_ssids);
+ if (driver_max_ssids == 0)
+ return wifi_scan_simple(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,
- gpointer user_data)
-{
- 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 (wifi == NULL)
- return -ENODEV;
-
- DBG("hidden SSID %s", ssid);
-
- if (wifi->tethering == TRUE || wifi->hidden != NULL)
- return -EBUSY;
-
- if (ssid == NULL || ssid_len == 0 || ssid_len > 32)
- return -EINVAL;
-
- if (connman_device_get_scanning(device) == TRUE)
- return -EALREADY;
-
scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
if (scan_params == NULL)
return -ENOMEM;
- scan_ssid = g_try_new(struct scan_ssid, 1);
- if (scan_ssid == NULL) {
- g_free(scan_params);
- return -ENOMEM;
- }
+ if (do_hidden == TRUE) {
+ 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);
+ 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;
- scan_params->num_ssids = 1;
+ hidden = g_try_new0(struct hidden_params, 1);
+ if (hidden == NULL) {
+ g_free(scan_params);
+ return -ENOMEM;
+ }
- hidden = g_try_new0(struct hidden_params, 1);
- if (hidden == NULL) {
- g_free(scan_params);
- return -ENOMEM;
+ memcpy(hidden->ssid, ssid, ssid_len);
+ hidden->ssid_len = ssid_len;
+ hidden->identity = g_strdup(identity);
+ hidden->passphrase = g_strdup(passphrase);
+ hidden->user_data = user_data;
+ wifi->hidden = hidden;
+
+ } else {
+ ret = get_latest_connections(driver_max_ssids, scan_params);
+ if (ret <= 0) {
+ g_supplicant_free_scan_params(scan_params);
+ return wifi_scan_simple(device);
+ }
}
- memcpy(hidden->ssid, ssid, ssid_len);
- hidden->ssid_len = ssid_len;
- hidden->identity = g_strdup(identity);
- hidden->passphrase = g_strdup(passphrase);
- hidden->user_data = user_data;
- wifi->hidden = hidden;
connman_device_ref(device);
reset_autoscan(device);
ret = g_supplicant_interface_scan(wifi->interface, scan_params,
- scan_callback, device);
+ scan_callback, device);
if (ret == 0)
connman_device_set_scanning(device, TRUE);
else {
- connman_device_unref(device);
g_supplicant_free_scan_params(scan_params);
- hidden_free(wifi->hidden);
- wifi->hidden = NULL;
+ connman_device_unref(device);
+
+ if (do_hidden == TRUE) {
+ hidden_free(wifi->hidden);
+ wifi->hidden = NULL;
+ }
}
return ret;
.enable = wifi_enable,
.disable = wifi_disable,
.scan = wifi_scan,
- .scan_fast = wifi_scan_fast,
- .scan_hidden = wifi_scan_hidden,
.set_regdom = wifi_set_regdom,
};
{
struct wifi_data *wifi = user_data;
+ DBG("result %d supplicant interface %p wifi %p",
+ result, interface, wifi);
+
+ if (result == -ECONNABORTED) {
+ DBG("wifi interface no longer available");
+ return;
+ }
+
if (wifi->network != NULL) {
/*
* if result < 0 supplican return an error because
switch (wifi->state) {
case G_SUPPLICANT_STATE_UNKNOWN:
+ case G_SUPPLICANT_STATE_DISABLED:
case G_SUPPLICANT_STATE_DISCONNECTED:
case G_SUPPLICANT_STATE_INACTIVE:
case G_SUPPLICANT_STATE_SCANNING:
* actually means that we are idling. */
switch (wifi->state) {
case G_SUPPLICANT_STATE_UNKNOWN:
+ case G_SUPPLICANT_STATE_DISABLED:
case G_SUPPLICANT_STATE_DISCONNECTED:
case G_SUPPLICANT_STATE_INACTIVE:
case G_SUPPLICANT_STATE_SCANNING:
struct connman_network *network,
struct wifi_data *wifi)
{
+ struct connman_service *service;
+
if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
return FALSE;
+ service = connman_service_lookup_from_network(network);
+ if (service == NULL)
+ return FALSE;
+
wifi->retries++;
- if (wifi->retries < MAXIMUM_RETRIES)
+ if (connman_service_get_favorite(service) == TRUE) {
+ if (wifi->retries < FAVORITE_MAXIMUM_RETRIES)
+ return TRUE;
+ } else if (wifi->retries < MAXIMUM_RETRIES)
return TRUE;
connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
break;
case G_SUPPLICANT_STATE_UNKNOWN:
+ case G_SUPPLICANT_STATE_DISABLED:
case G_SUPPLICANT_STATE_ASSOCIATED:
case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
return;
}
- wifi->networks = g_slist_append(wifi->networks, network);
+ wifi->networks = g_slist_prepend(wifi->networks, network);
}
if (name != NULL && name[0] != '\0')