This enables service to autoconnect to hidden wifi service.

Fixes BMC#11860
---
 plugins/wifi.c |  270 +++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 200 insertions(+), 70 deletions(-)

diff --git a/plugins/wifi.c b/plugins/wifi.c
index 2f1f7a6..1dfbc44 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -61,6 +61,14 @@
 #define BGSCAN_DEFAULT "simple:30:-45:300"
 #define AUTOSCAN_DEFAULT "exponential:3:300"
 
+/*
+ * Max number of SSIDs that can be scanned, from wps_s 0.8 or later.
+ * In wpa_s 0.7x the limit is 4.
+ * The value is only used if wpa_supplicant does not return any max limit
+ * for number of scannable SSIDs.
+ */
+#define WPAS_MAX_SCAN_SSIDS 16
+
 static struct connman_technology *wifi_technology = NULL;
 
 struct hidden_params {
@@ -252,6 +260,146 @@ static void wifi_remove(struct connman_device *device)
        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;
+       gchar *str;
+       char *ssid;
+       int i, freq;
+       gboolean hidden;
+       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]);
+
+               hidden = g_key_file_get_boolean(keyfile,
+                                       services[i], "Hidden", NULL);
+               if (hidden == FALSE) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "Favorite", NULL);
+               if (str == NULL || g_strcmp0(str, "true")) {
+                       g_free(str);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+               g_free(str);
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "AutoConnect", NULL);
+               if (str == NULL || g_strcmp0(str, "true")) {
+                       g_free(str);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+               g_free(str);
+
+               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)
 {
@@ -275,13 +423,60 @@ static int throw_wifi_scan(struct connman_device *device,
        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.
+        * If the supplicant does not give the max limit (this means that
+        * relevant patch is missing from wpa_s code), then use
+        * the default value.
+        */
+       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+       if (driver_max_ssids == 0) {
+               driver_max_ssids = WPAS_MAX_SCAN_SSIDS;
+               DBG("max ssids not found, using default %d", driver_max_ssids);
+       } else
+               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;
+               }
+       }
 
+out:
        connman_device_set_scanning(device, FALSE);
        connman_device_unref(device);
 }
@@ -490,75 +685,6 @@ static void scan_callback(int result, GSupplicantInterface 
*interface,
        start_autoscan(device);
 }
 
-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;
-}
-
 struct last_connected {
        GTimeVal modified;
        gchar *ssid;
@@ -738,6 +864,10 @@ static int wifi_scan_fast(struct connman_device *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)
-- 
1.7.5.4

_______________________________________________
connman mailing list
connman@connman.net
http://lists.connman.net/listinfo/connman

Reply via email to