OSDN Git Service

wifi: fix unable to unload driver
[android-x86/hardware-libhardware_legacy.git] / wifi / wifi.c
index 5c16e3e..4827357 100644 (file)
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <dirent.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <poll.h>
 
 #include "cutils/misc.h"
 #include "cutils/properties.h"
 #include "private/android_filesystem_config.h"
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
-#endif
 
 extern int do_dhcp();
 extern int ifc_init();
@@ -97,12 +97,12 @@ static char primary_iface[PROPERTY_VALUE_MAX];
 #endif
 
 #define WIFI_DRIVER_LOADER_DELAY       1000000
+#define SYSFS_PATH_MAX                 256
 
 static const char IFACE_DIR[]           = "/data/system/wpa_supplicant";
 #ifdef WIFI_DRIVER_MODULE_PATH
-static const char DRIVER_MODULE_NAME[]  = WIFI_DRIVER_MODULE_NAME;
-static const char DRIVER_MODULE_TAG[]   = WIFI_DRIVER_MODULE_NAME " ";
-static const char DRIVER_MODULE_PATH[]  = WIFI_DRIVER_MODULE_PATH;
+static const char DRIVER_NAME_PROP[]    = "wlan.modname";
+static const char DRIVER_PATH_PROP[]    = "wlan.modpath";
 static const char DRIVER_MODULE_ARG[]   = WIFI_DRIVER_MODULE_ARG;
 #endif
 static const char FIRMWARE_LOADER[]     = WIFI_FIRMWARE_LOADER;
@@ -116,6 +116,9 @@ static const char SUPP_CONFIG_FILE[]    = "/data/misc/wifi/wpa_supplicant.conf";
 static const char P2P_CONFIG_FILE[]     = "/data/misc/wifi/p2p_supplicant.conf";
 static const char CONTROL_IFACE_PATH[]  = "/data/misc/wifi/sockets";
 static const char MODULE_FILE[]         = "/proc/modules";
+static const char SYSFS_CLASS_NET[]     = "/sys/class/net";
+static const char MODULE_DEFAULT_DIR[]  = "/system/lib/modules";
+static const char SYS_MOD_NAME_DIR[]    = "device/driver/module";
 
 static const char IFNAME[]              = "IFNAME=";
 #define IFNAMELEN                      (sizeof(IFNAME) - 1)
@@ -190,18 +193,117 @@ const char *get_dhcp_error_string() {
     return dhcp_lasterror();
 }
 
+#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
+int wifi_change_driver_state(const char *state)
+{
+    int len;
+    int fd;
+    int ret = 0;
+
+    if (!state)
+        return -1;
+    fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY));
+    if (fd < 0) {
+        ALOGE("Failed to open driver state control param (%s)", strerror(errno));
+        return -1;
+    }
+    len = strlen(state) + 1;
+    if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) {
+        ALOGE("Failed to write driver state control param (%s)", strerror(errno));
+        ret = -1;
+    }
+    close(fd);
+    return ret;
+}
+#endif
+
+#ifdef WIFI_DRIVER_MODULE_PATH
+static int get_driver_path(const char *mod, const char *path, char *buf) {
+    DIR *dir;
+    struct dirent *de;
+    char modpath[SYSFS_PATH_MAX];
+    int ret = 0;
+
+    if ((dir = opendir(path))) {
+        while ((de = readdir(dir))) {
+            struct stat sb;
+            if (de->d_name[0] == '.')
+                continue;
+            snprintf(modpath, SYSFS_PATH_MAX, "%s/%s", path, de->d_name);
+            if (!strcmp(de->d_name, mod)) {
+                strncpy(buf, modpath, SYSFS_PATH_MAX - 1);
+                buf[SYSFS_PATH_MAX - 1] = '\0';
+                ret = 1;
+                break;
+            }
+            if (!stat(modpath, &sb) && (sb.st_mode & S_IFMT) == S_IFDIR)
+                if ((ret = get_driver_path(mod, modpath, buf)))
+                    break;
+        }
+        closedir(dir);
+    }
+    return ret;
+}
+
+static int get_driver_info(char *buf) {
+    DIR *netdir;
+    struct dirent *de;
+    char path[SYSFS_PATH_MAX];
+    char link[SYSFS_PATH_MAX];
+    int ret = 0;
+
+    if ((netdir = opendir(SYSFS_CLASS_NET))) {
+        while ((de = readdir(netdir))) {
+            int cnt;
+            char *pos;
+            if (de->d_name[0] == '.')
+                continue;
+            snprintf(path, SYSFS_PATH_MAX, "%s/%s/wireless", SYSFS_CLASS_NET, de->d_name);
+            if (access(path, F_OK)) {
+                snprintf(path, SYSFS_PATH_MAX, "%s/%s/phy80211", SYSFS_CLASS_NET, de->d_name);
+                if (access(path, F_OK))
+                    continue;
+            }
+            /* found the wifi interface */
+            property_set("wlan.interface", de->d_name);
+            snprintf(path, SYSFS_PATH_MAX, "%s/%s/%s", SYSFS_CLASS_NET, de->d_name, SYS_MOD_NAME_DIR);
+            if ((cnt = readlink(path, link, SYSFS_PATH_MAX - 1)) < 0) {
+                ALOGW("can not find link of %s", path);
+                continue;
+            }
+            link[cnt] = '\0';
+            if ((pos = strrchr(link, '/'))) {
+                property_set(DRIVER_NAME_PROP, ++pos);
+                strncpy(buf, pos, PROPERTY_VALUE_MAX - 1);
+                buf[PROPERTY_VALUE_MAX - 1] = '\0';
+                ret = 1;
+                break;
+            }
+        }
+        closedir(netdir);
+    }
+
+    return ret;
+}
+#endif
+
 int is_wifi_driver_loaded() {
-    char driver_status[PROPERTY_VALUE_MAX];
 #ifdef WIFI_DRIVER_MODULE_PATH
+    char modname[PROPERTY_VALUE_MAX];
+    char line[PROPERTY_VALUE_MAX];
     FILE *proc;
-    char line[sizeof(DRIVER_MODULE_TAG)+10];
-#endif
+    int cnt = property_get(DRIVER_NAME_PROP, modname, NULL);
 
-    if (!property_get(DRIVER_PROP_NAME, driver_status, NULL)
-            || strcmp(driver_status, "ok") != 0) {
-        return 0;  /* driver not loaded */
+    if (!cnt) {
+        if (get_driver_info(modname))
+            cnt = strlen(modname);
+        else if (property_get("wlan.interface", line, NULL))
+            return 1; // found an interface without modname, assume built-in
+        else
+            goto unloaded;
     }
-#ifdef WIFI_DRIVER_MODULE_PATH
+    modname[cnt++] = ' ';
+
     /*
      * If the property says the driver is loaded, check to
      * make sure that the property setting isn't just left
@@ -210,16 +312,18 @@ int is_wifi_driver_loaded() {
      */
     if ((proc = fopen(MODULE_FILE, "r")) == NULL) {
         ALOGW("Could not open %s: %s", MODULE_FILE, strerror(errno));
-        property_set(DRIVER_PROP_NAME, "unloaded");
-        return 0;
+        goto unloaded;
     }
+
     while ((fgets(line, sizeof(line), proc)) != NULL) {
-        if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) {
+        if (strncmp(line, modname, cnt) == 0) {
             fclose(proc);
             return 1;
         }
     }
     fclose(proc);
+
+unloaded:
     property_set(DRIVER_PROP_NAME, "unloaded");
     return 0;
 #else
@@ -231,13 +335,23 @@ int wifi_load_driver()
 {
 #ifdef WIFI_DRIVER_MODULE_PATH
     char driver_status[PROPERTY_VALUE_MAX];
+    char modname[PROPERTY_VALUE_MAX];
+    char modpath[SYSFS_PATH_MAX];
     int count = 100; /* wait at most 20 seconds for completion */
 
     if (is_wifi_driver_loaded()) {
         return 0;
     }
 
-    if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
+    if (!property_get(DRIVER_PATH_PROP, modpath, NULL)) {
+        property_get(DRIVER_NAME_PROP, modname, NULL);
+        strcat(modname, ".ko");
+        if (!get_driver_path(modname, MODULE_DEFAULT_DIR, modpath))
+            strcpy(modpath, WIFI_DRIVER_MODULE_PATH);
+    }
+
+    ALOGI("got module path %s", modpath);
+    if (insmod(modpath, DRIVER_MODULE_ARG) < 0)
         return -1;
 
     if (strcmp(FIRMWARE_LOADER,"") == 0) {
@@ -250,9 +364,10 @@ int wifi_load_driver()
     sched_yield();
     while (count-- > 0) {
         if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
-            if (strcmp(driver_status, "ok") == 0)
+            if (strcmp(driver_status, "ok") == 0) {
+                get_driver_info(modname);
                 return 0;
-            else if (strcmp(DRIVER_PROP_NAME, "failed") == 0) {
+            } else if (strcmp(driver_status, "failed") == 0) {
                 wifi_unload_driver();
                 return -1;
             }
@@ -263,6 +378,14 @@ int wifi_load_driver()
     wifi_unload_driver();
     return -1;
 #else
+#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
+    if (is_wifi_driver_loaded()) {
+        return 0;
+    }
+
+    if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0)
+        return -1;
+#endif
     property_set(DRIVER_PROP_NAME, "ok");
     return 0;
 #endif
@@ -272,7 +395,14 @@ int wifi_unload_driver()
 {
     usleep(200000); /* allow to finish interface down */
 #ifdef WIFI_DRIVER_MODULE_PATH
-    if (rmmod(DRIVER_MODULE_NAME) == 0) {
+    char modname[PROPERTY_VALUE_MAX];
+    if (property_get("wlan.no-unload-driver", modname, NULL)
+            && strcmp(modname, "1") == 0)
+        return 0;
+    if (!property_get(DRIVER_NAME_PROP, modname, NULL))
+        return -1;
+
+    if (rmmod(modname) == 0) {
         int count = 20; /* wait at most 10 seconds for completion */
         while (count-- > 0) {
             if (!is_wifi_driver_loaded())
@@ -283,10 +413,15 @@ int wifi_unload_driver()
         if (count) {
             return 0;
         }
-        return -1;
-    } else
-        return -1;
+    }
+    return -1;
 #else
+#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
+    if (is_wifi_driver_loaded()) {
+        if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0)
+            return -1;
+    }
+#endif
     property_set(DRIVER_PROP_NAME, "unloaded");
     return 0;
 #endif
@@ -336,84 +471,6 @@ int ensure_entropy_file_exists()
     return 0;
 }
 
-int update_ctrl_interface(const char *config_file) {
-
-    int srcfd, destfd;
-    int nread;
-    char ifc[PROPERTY_VALUE_MAX];
-    char *pbuf;
-    char *sptr;
-    struct stat sb;
-    int ret;
-
-    if (stat(config_file, &sb) != 0)
-        return -1;
-
-    pbuf = malloc(sb.st_size + PROPERTY_VALUE_MAX);
-    if (!pbuf)
-        return 0;
-    srcfd = TEMP_FAILURE_RETRY(open(config_file, O_RDONLY));
-    if (srcfd < 0) {
-        ALOGE("Cannot open \"%s\": %s", config_file, strerror(errno));
-        free(pbuf);
-        return 0;
-    }
-    nread = TEMP_FAILURE_RETRY(read(srcfd, pbuf, sb.st_size));
-    close(srcfd);
-    if (nread < 0) {
-        ALOGE("Cannot read \"%s\": %s", config_file, strerror(errno));
-        free(pbuf);
-        return 0;
-    }
-
-    if (!strcmp(config_file, SUPP_CONFIG_FILE)) {
-        property_get("wifi.interface", ifc, WIFI_TEST_INTERFACE);
-    } else {
-        strcpy(ifc, CONTROL_IFACE_PATH);
-    }
-    /* Assume file is invalid to begin with */
-    ret = -1;
-    /*
-     * if there is a "ctrl_interface=<value>" entry, re-write it ONLY if it is
-     * NOT a directory.  The non-directory value option is an Android add-on
-     * that allows the control interface to be exchanged through an environment
-     * variable (initialized by the "init" program when it starts a service
-     * with a "socket" option).
-     *
-     * The <value> is deemed to be a directory if the "DIR=" form is used or
-     * the value begins with "/".
-     */
-    if ((sptr = strstr(pbuf, "ctrl_interface="))) {
-        ret = 0;
-        if ((!strstr(pbuf, "ctrl_interface=DIR=")) &&
-                (!strstr(pbuf, "ctrl_interface=/"))) {
-            char *iptr = sptr + strlen("ctrl_interface=");
-            int ilen = 0;
-            int mlen = strlen(ifc);
-            int nwrite;
-            if (strncmp(ifc, iptr, mlen) != 0) {
-                ALOGE("ctrl_interface != %s", ifc);
-                while (((ilen + (iptr - pbuf)) < nread) && (iptr[ilen] != '\n'))
-                    ilen++;
-                mlen = ((ilen >= mlen) ? ilen : mlen) + 1;
-                memmove(iptr + mlen, iptr + ilen + 1, nread - (iptr + ilen + 1 - pbuf));
-                memset(iptr, '\n', mlen);
-                memcpy(iptr, ifc, strlen(ifc));
-                destfd = TEMP_FAILURE_RETRY(open(config_file, O_RDWR, 0660));
-                if (destfd < 0) {
-                    ALOGE("Cannot update \"%s\": %s", config_file, strerror(errno));
-                    free(pbuf);
-                    return -1;
-                }
-                TEMP_FAILURE_RETRY(write(destfd, pbuf, nread + mlen - ilen -1));
-                close(destfd);
-            }
-        }
-    }
-    free(pbuf);
-    return ret;
-}
-
 int ensure_config_file_exists(const char *config_file)
 {
     char buf[2048];
@@ -429,14 +486,7 @@ int ensure_config_file_exists(const char *config_file)
             ALOGE("Cannot set RW to \"%s\": %s", config_file, strerror(errno));
             return -1;
         }
-        /* return if we were able to update control interface properly */
-        if (update_ctrl_interface(config_file) >=0) {
-            return 0;
-        } else {
-            /* This handles the scenario where the file had bad data
-             * for some reason. We continue and recreate the file.
-             */
-        }
+        return 0;
     } else if (errno != ENOENT) {
         ALOGE("Cannot access \"%s\": %s", config_file, strerror(errno));
         return -1;
@@ -483,17 +533,15 @@ int ensure_config_file_exists(const char *config_file)
         unlink(config_file);
         return -1;
     }
-    return update_ctrl_interface(config_file);
+    return 0;
 }
 
 int wifi_start_supplicant(int p2p_supported)
 {
     char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
     int count = 200; /* wait at most 20 seconds for completion */
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
     const prop_info *pi;
     unsigned serial = 0, i;
-#endif
 
     if (p2p_supported) {
         strcpy(supplicant_name, P2P_SUPPLICANT_NAME);
@@ -532,7 +580,6 @@ int wifi_start_supplicant(int p2p_supported)
     /* Reset sockets used for exiting from hung state */
     exit_sockets[0] = exit_sockets[1] = -1;
 
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
     /*
      * Get a reference to the status property, so we can distinguish
      * the case where it goes stopped => running => stopped (i.e.,
@@ -544,14 +591,12 @@ int wifi_start_supplicant(int p2p_supported)
     if (pi != NULL) {
         serial = __system_property_serial(pi);
     }
-#endif
-    property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE);
+    property_get("wlan.interface", primary_iface, WIFI_TEST_INTERFACE);
 
     property_set("ctl.start", supplicant_name);
     sched_yield();
 
     while (count-- > 0) {
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
         if (pi == NULL) {
             pi = __system_property_find(supplicant_prop_name);
         }
@@ -568,12 +613,6 @@ int wifi_start_supplicant(int p2p_supported)
                 }
             }
         }
-#else
-        if (property_get(supplicant_prop_name, supp_status, NULL)) {
-            if (strcmp(supp_status, "running") == 0)
-                return 0;
-        }
-#endif
         usleep(100000);
     }
     return -1;