OSDN Git Service

wifi: fix unable to unload driver
[android-x86/hardware-libhardware_legacy.git] / wifi / wifi.c
index 5f24343..4827357 100644 (file)
@@ -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
@@ -456,7 +591,7 @@ int wifi_start_supplicant(int p2p_supported)
     if (pi != NULL) {
         serial = __system_property_serial(pi);
     }
-    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();