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);
continue;
keyfile = connman_storage_load_service(services[i]);
+ if (keyfile == NULL)
+ continue;
value = g_key_file_get_boolean(keyfile,
services[i], "Hidden", NULL);
/*
* 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;
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);
{
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:
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: