wifi: Adding support for autoscan emulation
authorTomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
Mon, 23 Apr 2012 08:22:16 +0000 (11:22 +0300)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Mon, 23 Apr 2012 10:49:45 +0000 (13:49 +0300)
plugins/wifi.c

index aea53ae..cbc6cdb 100644 (file)
@@ -58,6 +58,7 @@
 #define MAXIMUM_RETRIES   4
 
 #define BGSCAN_DEFAULT "simple:30:-45:300"
+#define AUTOSCAN_DEFAULT "exponential:2:3600"
 
 struct connman_technology *wifi_technology = NULL;
 
@@ -68,6 +69,17 @@ struct hidden_params {
        char *passphrase;
 };
 
+/**
+ * Used for autoscan "emulation".
+ * Should be removed when wpa_s autoscan support will be by default.
+ */
+struct autoscan_params {
+       int base;
+       int limit;
+       int interval;
+       unsigned int timeout;
+};
+
 struct wifi_data {
        char *identifier;
        struct connman_device *device;
@@ -86,6 +98,10 @@ struct wifi_data {
        unsigned int watch;
        int retries;
        struct hidden_params *hidden;
+       /**
+        * autoscan "emulation".
+        */
+       struct autoscan_params *autoscan;
 };
 
 static GList *iface_list = NULL;
@@ -185,6 +201,27 @@ static void remove_networks(struct connman_device *device,
        wifi->networks = NULL;
 }
 
+static void stop_autoscan(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+
+       DBG("");
+
+       if (wifi == NULL || wifi->autoscan == NULL)
+               return;
+
+       autoscan = wifi->autoscan;
+
+       if (autoscan->timeout > 0)
+               g_source_remove(autoscan->timeout);
+
+       autoscan->timeout = 0;
+       autoscan->interval = 0;
+
+       connman_device_unref(device);
+}
+
 static void wifi_remove(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
@@ -194,6 +231,8 @@ static void wifi_remove(struct connman_device *device)
        if (wifi == NULL)
                return;
 
+       stop_autoscan(device);
+
        iface_list = g_list_remove(iface_list, wifi);
 
        remove_networks(device, wifi);
@@ -205,10 +244,141 @@ static void wifi_remove(struct connman_device *device)
 
        g_supplicant_interface_set_data(wifi->interface, NULL);
 
+       g_free(wifi->autoscan);
        g_free(wifi->identifier);
        g_free(wifi);
 }
 
+static int throw_wifi_scan(struct connman_device *device,
+                       GSupplicantInterfaceCallback callback)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int ret;
+
+       DBG("device %p %p", device, wifi->interface);
+
+       if (wifi->tethering == TRUE)
+               return 0;
+
+       connman_device_ref(device);
+
+       ret = g_supplicant_interface_scan(wifi->interface, NULL,
+                                               callback, device);
+       if (ret == 0)
+               connman_device_set_scanning(device, TRUE);
+       else
+               connman_device_unref(device);
+
+       return ret;
+}
+
+static void autoscan_scan_callback(int result,
+                       GSupplicantInterface *interface, void *user_data)
+{
+       struct connman_device *device = user_data;
+
+       DBG("");
+
+       connman_device_set_scanning(device, FALSE);
+}
+
+static gboolean autoscan_timeout(gpointer data)
+{
+       struct connman_device *device = data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+       int interval;
+
+       autoscan = wifi->autoscan;
+
+       if (autoscan->interval <= 0) {
+               interval = autoscan->base;
+               goto set_interval;
+       } else
+               interval = autoscan->interval * autoscan->base;
+
+       if (autoscan->interval >= autoscan->limit)
+               interval = autoscan->limit;
+
+       throw_wifi_scan(wifi->device, autoscan_scan_callback);
+
+set_interval:
+       DBG("interval %d", interval);
+
+       autoscan->interval = interval;
+
+       autoscan->timeout = g_timeout_add_seconds(interval,
+                                               autoscan_timeout, device);
+
+       return FALSE;
+}
+
+static void start_autoscan(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+
+       DBG("");
+
+       if (wifi == NULL)
+               return;
+
+       autoscan = wifi->autoscan;
+       if (autoscan == NULL)
+               return;
+
+       if (autoscan->timeout > 0 || autoscan->interval > 0)
+               return;
+
+       connman_device_ref(device);
+
+       autoscan_timeout(device);
+}
+
+static struct autoscan_params *parse_autoscan_params(const char *params)
+{
+       struct autoscan_params *autoscan;
+       char **list_params;
+       int limit;
+       int base;
+
+       DBG("Emulating autoscan");
+
+       list_params = g_strsplit(params, ":", 0);
+       if (list_params == 0)
+               return NULL;
+
+       if (g_strv_length(list_params) < 3) {
+               g_strfreev(list_params);
+               return NULL;
+       }
+
+       base = atoi(list_params[1]);
+       limit = atoi(list_params[2]);
+
+       g_strfreev(list_params);
+
+       autoscan = g_try_malloc0(sizeof(struct autoscan_params));
+       if (autoscan == NULL) {
+               DBG("Could not allocate memory for autoscan");
+               return NULL;
+       }
+
+       DBG("base %d - limit %d", base, limit);
+       autoscan->base = base;
+       autoscan->limit = limit;
+
+       return autoscan;
+}
+
+static void setup_autoscan(struct wifi_data *wifi)
+{
+       if (wifi->autoscan == NULL)
+               wifi->autoscan = parse_autoscan_params(AUTOSCAN_DEFAULT);
+
+       start_autoscan(wifi->device);
+}
+
 static void interface_create_callback(int result,
                                        GSupplicantInterface *interface,
                                                        void *user_data)
@@ -236,6 +406,9 @@ static void interface_create_callback(int result,
        }
 
        connman_device_set_powered(wifi->device, TRUE);
+
+       /* Setting up automatic scanning */
+       setup_autoscan(wifi);
 }
 
 static int wifi_enable(struct connman_device *device)
@@ -306,6 +479,8 @@ static void scan_callback(int result, GSupplicantInterface *interface,
 
        connman_device_set_scanning(device, FALSE);
        connman_device_unref(device);
+
+       start_autoscan(device);
 }
 
 static int add_scan_param(gchar *hex_ssid, int freq,
@@ -479,23 +654,9 @@ static int get_latest_connections(int max_ssids,
 
 static int wifi_scan(struct connman_device *device)
 {
-       struct wifi_data *wifi = connman_device_get_data(device);
-       int ret;
-
-       DBG("device %p %p", device, wifi->interface);
-
-       if (wifi->tethering == TRUE)
-               return 0;
-
-       connman_device_ref(device);
-       ret = g_supplicant_interface_scan(wifi->interface, NULL,
-                                       scan_callback, device);
-       if (ret == 0)
-               connman_device_set_scanning(device, TRUE);
-       else
-               connman_device_unref(device);
+       stop_autoscan(device);
 
-       return ret;
+       return throw_wifi_scan(device, scan_callback);
 }
 
 static int wifi_scan_fast(struct connman_device *device)
@@ -526,6 +687,8 @@ static int wifi_scan_fast(struct connman_device *device)
                return wifi_scan(device);
        }
 
+       stop_autoscan(device);
+
        connman_device_ref(device);
        ret = g_supplicant_interface_scan(wifi->interface, scan_params,
                                                scan_callback, device);
@@ -574,6 +737,8 @@ static int wifi_scan_hidden(struct connman_device *device,
        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);
@@ -801,6 +966,7 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
                wifi->pending_network = NULL;
        }
 
+       start_autoscan(wifi->device);
 }
 
 static int network_disconnect(struct connman_network *network)