[bc9]

Overview

This article shows how to add an USB WiFi device to bc9. In this article, the example of the device is specific, and it is available
only in Japan. However, the critical parts of this article are explaining how to integrate a driver of device and Linux kernel and
how to modify of several files for the integration. The driver for the USB WiFi device is called rt73, and it is commonly used in
Linux community. If users cannot obtain the specific device, this methodology is mostly applicable for rt73 compatible devices.

  • The device used here is I-O data WN-G54/USB (the information site is in Japanese). This device is fully compatible with android-1.5r3.

  • To control the device from the GUI of android, the file located at android-1.5r3/hardware/libhardware_legacy/wifi/wifi.c/ is needed to be
    modified. (Once this file is build, it is linked to libnetutils.) As this directory is needed to be specifically altered for the use of a certain
    device, this directory is required a new modification for a newly added device.

  • Because of the android's current structure, easily change the devices as long as they use the same driver. However, it is very difficult
    to use the different drivers of the same kind of devices, simultaneously.

  • There is a wifi porting manual at android-1.5r3/development/pdk/docs/guide/wifi.jd For changing the formats of documents from
    javadoc to html, please look at mydroid/development/pdk/README.

Instructions

Following the directions of bc9/bc9-oe-sdk setup and bc9/bc9-android-sdk setup, rebuild kerne,l and build android userland.

Create Kernel with Wireless LAN driver

In this article, the USB WiFi device used here is I-O data WN G54/USB, which has been tested with gumstix/bc9. The driver is
linux-2.6.29/drivers/net/wireless/rt2x00/rt73usb.ko. This driver is dependent on rt2x00lib.ko and rt2x00usb.ko, and needed
the firmware of rt73.bin. The firmware can be downloaded from the link below.

fileRT71W_Firmware_V1.8.zip

First, the driver is built as a module, and try the module on OpenEmbedded Linux. rt73.bin is placed at /lib/firmware/.
The connection method, which utilizes wpa_supplicant, is studied from the information at /etc/network/interfaces/.

$ wpa_passphrase SSID PASS_PHRASE

Even though the output of the command above is written into /etc/wpa_supplicant.conf, due to the lack of some critical
information, such as the encryption figure, the connection is failed.

Taking this advice, add the extra information to /etc /wpa_supplicant.conf.

network={
    ssid="hoge"
    key_mgmt=WPA-PSK
    proto=WPA WPA2
    pairwise=CCMP TKIP
    group=CCMP TKIP WEP104 WEP40
    # psk="beatcraft"
    psk=1954c17037ac0255e310e30377008d5e732edb603bb4b27ee92d14e4b0d91919
} 

As writing the information correctly, check the connection by the command.

$ wpa_supplicant -Dwext -iwlan0 -c/etc/wpa_supplicant.conf

For OpenEmbedded Linux, wlan0 is up and obtain an IP address by dhcp.

android build with wpa_ supplicant

Making wpa_supplicant effective, build android and test the wireless connection by command lines.

build wpa_supplicant

Modify .config file, which is located at android-1.5r3/external/wpa_supplicant/.config. The modifications are shown below.

$ diff -u .config.orig .config
--- .config.orig	2009-08-05 22:00:52.000000000 +0900
+++ .config	2009-08-11 16:22:36.000000000 +0900
@@ -19,13 +19,13 @@
 CONFIG_PKCS12=y
 # CONFIG_PCSC=y
 CONFIG_SMARTCARD=y
-# CONFIG_WIRELESS_EXTENSION=y
+CONFIG_WIRELESS_EXTENSION=y
 CONFIG_CTRL_IFACE=y
 # CONFIG_DRIVER_HOSTAP=y
 # CONFIG_DRIVER_HERMES=y
 # CONFIG_DRIVER_MADWIFI=y
 # CONFIG_DRIVER_ATMEL=y
-# CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_WEXT=y
 # CONFIG_DRIVER_NDISWRAPPER=y
 # CONFIG_DRIVER_BROADCOM=y
 # CONFIG_DRIVER_IPW=y

The extra code defined below is added to BoardConfig.mk, which is placed at android-1.5r3/build/target/board/generic/BoardConfig.mk

WPA_BUILD_SUPPLICANT := true
BOARD_WPA_SUPPLICANT_DRIVER := WEXT

Alter the file, wpa_supplicant.conf, which is under the directory android-1.5r3/external/wpa_suplicant/wpa_supplicant.conf.
Change the code as it is described below.

~/android-1.5r3$ diff -u external/wpa_supplicant/wpa_supplicant.conf.orig external/wpa_supplicant/wpa_supplicant.conf
--- external/wpa_supplicant/wpa_supplicant.conf.orig	2009-08-11 16:31:15.000000000 +0900
+++ external/wpa_supplicant/wpa_supplicant.conf	2009-08-11 16:31:58.000000000 +0900
@@ -72,7 +72,8 @@
 # DACL (which will reject all connections). See README-Windows.txt for more
 # information about SDDL string format.
 #
-ctrl_interface=tiwlan0
+#ctrl_interface=wlan0
+ctrl_interface=DIR=/data/system/wpa_supplicant GROUP=system
 
 # IEEE 802.1X/EAPOL version
 # wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines

As executing make again, wpa_supplicant and wpa_cli are built at out/target/product/generic/system/bin/. Testing
wireless connection, execute shell command from android. Make sure that bc9 is connected to a wireless access
point. On android, as executing insmod, firmwares can be read. However wlan0 does not start up automatically.
Executing two commands shown below, obtain an address via dhcp, which requires few commands.

# system/bin/wpa_supplicant -Dwext -iwlan0 -c/system/etc/wpa_supplicant.conf
# netcfg wlan0 dhcp

Modification of wifi.c

The modification of wifi.c enables WiFi control from the GUI of android possible. As Executimg insmod on rt73usb.ko
and the drivers depending on rt73usb.ko, rt73.bin is loaded into USB wifi, and wlan can use. (As android does not
have modprobe drivers, the drivers depending on rt73usb.ko cannot be loaded at once. To maintain the relationship
with dependencies, insmod is executed on a single driver individually every once.) Configuring wifi's SSID and PSK
via wpa_supplicant, start up wlan0, and obtain the address by dhcp. The Code below show how wifi.c is modified.
wifi.c is located at android-1.5r3/hardware/libhardware_legacy/wifi/wifi.c/.

--- wifi.c.orig 2009-08-11 10:56:03.000000000 +0900
+++ wifi.c      2009-08-12 17:02:49.000000000 +0900
@@ -48,10 +48,16 @@
 // TODO: use new ANDROID_SOCKET mechanism, once support for multiple
 // sockets is in

-static const char IFACE_DIR[]           = "/data/system/wpa_supplicant";
-static const char DRIVER_MODULE_NAME[]  = "wlan";
-static const char DRIVER_MODULE_TAG[]   = "wlan ";
-static const char DRIVER_MODULE_PATH[]  = "/system/lib/modules/wlan.ko";
+static const char IFACE_DIR[]                          = "/data/system/wpa_supplicant";
+static const char DRIVER_MODULE_NAME[]                 = "rt73usb";
+static const char DRIVER_MODULE_TAG[]                  = "rt73usb ";
+static const char DRIVER_MODULE_PATH[]                 = "/system/lib/modules/rt73usb.ko";
+static const char RT2X00_LIB_DRIVER_MODULE_NAME[]      = "rt2x00lib";
+static const char RT2X00_LIB_DRIVER_MODULE_TAG[]       = "rt2x00lib ";
+static const char RT2X00_LIB_DRIVER_MODULE_PATH[]      = "/system/lib/modules/rt2x00lib.ko";
+static const char RT2X00_LIB_USB_DRIVER_MODULE_NAME[]  = "rt2x00usb";
+static const char RT2X00_LIB_USB_DRIVER_MODULE_TAG[]   = "rt2x00usb";
+static const char RT2X00_LIB_USB_DRIVER_MODULE_PATH[]  = "/system/lib/modules/rt2x00usb.ko";
 static const char FIRMWARE_LOADER[]     = "wlan_loader";
 static const char DRIVER_PROP_NAME[]    = "wlan.driver.status";
 static const char SUPPLICANT_NAME[]     = "wpa_supplicant";
@@ -66,6 +72,7 @@
     unsigned int size;
     int ret;

+    LOGI("%s\n", __FUNCTION__);
     module = load_file(filename, &size);
     if (!module)
         return -1;
@@ -82,6 +89,7 @@
     int ret = -1;
     int maxtry = 10; 

+    LOGI("%s\n", __FUNCTION__);
     while (maxtry-- > 0) {
         ret = delete_module(modname, O_NONBLOCK | O_EXCL);
        if (ret < 0 && errno == EAGAIN)
@@ -91,30 +99,35 @@
     }

     if (ret != 0)
-        LOGD("Unable to unload driver module \"%s\": %s\n",
+        LOGE("Unable to unload driver module \"%s\": %s\n",
              modname, strerror(errno));
     return ret;
 }

 int do_dhcp_request(int *ipaddr, int *gateway, int *mask,
                     int *dns1, int *dns2, int *server, int *lease) {
+    LOGI("1. inside %s\n", __FUNCTION__);
     /* For test driver, always report success */
-    if (strcmp(iface, "sta") == 0)
+    if (strcmp(iface, "wlan0") == 0)
         return 0;

+    LOGI("2. inside %s\n", __FUNCTION__);
     if (ifc_init() < 0)
         return -1;

+    LOGI("3. inside %s\n", __FUNCTION__);
     if (do_dhcp(iface) < 0) {
         ifc_close();
         return -1;
     }
     ifc_close();
     get_dhcp_info(ipaddr, gateway, mask, dns1, dns2, server, lease);
+    LOGI("4. inside %s\n", __FUNCTION__);
     return 0;
 }

 const char *get_dhcp_error_string() {
+    LOGI("Inside %s\n", __FUNCTION__);
     return dhcp_lasterror();
 }

@@ -123,8 +136,10 @@
     FILE *proc;
     char line[sizeof(DRIVER_MODULE_TAG)+10];

+    LOGI("%s\n", __FUNCTION__);
     if (!property_get(DRIVER_PROP_NAME, driver_status, NULL)
             || strcmp(driver_status, "ok") != 0) {
+         LOGE("driver %s has NOT been installed",DRIVER_MODULE_TAG);
         return 0;  /* driver not loaded */
     }
     /*
@@ -140,10 +155,12 @@
     }
     while ((fgets(line, sizeof(line), proc)) != NULL) {
         if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) {
+            LOGI("driver %s has been installed\n",DRIVER_MODULE_TAG);
             fclose(proc);
             return 1; 
         }
     }
+    LOGE("Cannot find driver %s in proc",DRIVER_MODULE_TAG);
     fclose(proc);
     property_set(DRIVER_PROP_NAME, "unloaded");
     return 0;
@@ -154,23 +171,34 @@
     char driver_status[PROPERTY_VALUE_MAX];
     int count = 100; /* wait at most 20 seconds for completion */

+    LOGI("Loading WiFi Modules\n");
+    sleep(1);
+
+    // Hack taken from EEPC WiFi path. We have statically linked the WiFi driver
+    // into the kernel, so we simply return and don't attempt to load the driver.
+    // So we set the driver property to ok, since we have already installed it.
+    property_set(DRIVER_PROP_NAME, "ok");
     if (check_driver_loaded()) {
         return 0;
     }
-
-    if (insmod(DRIVER_MODULE_PATH) < 0)
-        return -1;
-      
-    property_set("ctl.start", FIRMWARE_LOADER);
+    insmod(RT2X00_LIB_DRIVER_MODULE_PATH);
+    insmod(RT2X00_LIB_USB_DRIVER_MODULE_PATH);
+    insmod(DRIVER_MODULE_PATH);
+    property_set("ctl.start", 0); 
     sched_yield();
     while (count-- > 0) {
+               usleep(500000);
         if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
-            if (strcmp(driver_status, "ok") == 0)
+            if (strcmp(driver_status, "ok") == 0) {
+               property_set("ctl.start", "ifcfg_ralink");
+                               usleep(1000000);
+                               //property_set("ctl.start", "dhcpcd");
                 return 0;
-            else if (strcmp(DRIVER_PROP_NAME, "failed") == 0)
+            }
+            else if (strcmp(DRIVER_PROP_NAME, "failed") == 0) {
                 return -1;
+            }
         }
-        usleep(200000);
     }
     property_set(DRIVER_PROP_NAME, "timeout");
     return -1;
@@ -178,20 +206,31 @@

 int wifi_unload_driver()
 {
-    int count = 20; /* wait at most 10 seconds for completion */
-    
+    LOGI("Unloading WiFi Modules\n");
+    sleep(1);
+
     if (rmmod(DRIVER_MODULE_NAME) == 0) {
-       while (count-- > 0) {
-           if (!check_driver_loaded())
-               break;
-           usleep(500000);
-       }
-       if (count) {
-           return 0;
-       }
-       return -1;
-    } else
+        usleep(1000000);
+    } else {
+        LOGE("Unloading Ralink RT73USB WLAN Module Failed\n");
+        return -1;
+    }
+
+    if (rmmod(RT2X00_LIB_USB_DRIVER_MODULE_NAME) == 0) {
+        usleep(1000000);
+    } else {
+        LOGE("Unloading RT2X00_LIB_USB Module Failed\n");
         return -1;
+    }
+
+    if (rmmod(RT2X00_LIB_DRIVER_MODULE_NAME) == 0) {
+        usleep(1000000);
+    } else {
+        LOGE("Unloading RT2X00_LIB Module Failed\n");
+        return -1;
+    }
+
+    return 0;
 }

 int ensure_config_file_exists()
@@ -200,6 +239,7 @@
     int srcfd, destfd;
     int nread;

+    LOGE("%s\n", __FUNCTION__);
     if (access(SUPP_CONFIG_FILE, R_OK|W_OK) == 0) {
         return 0;
     } else if (errno != ENOENT) {
@@ -252,6 +292,7 @@
     unsigned serial = 0;
 #endif

+    LOGI("start %s",__FUNCTION__);
     /* Check whether already running */
     if (property_get(SUPP_PROP_NAME, supp_status, NULL)
             && strcmp(supp_status, "running") == 0) {
@@ -260,7 +301,7 @@

     /* Before starting the daemon, make sure its config file exists */
     if (ensure_config_file_exists() < 0) {
-        LOGE("Wi-Fi will not be enabled");
+        LOGE("Configuration file does not exis. Wi-Fi will not be enabled");
         return -1;
     } 

@@ -291,9 +332,11 @@
         if (pi != NULL) {
             __system_property_read(pi, NULL, supp_status);
             if (strcmp(supp_status, "running") == 0) {
+                LOGI("Wi-Fi is running\n");
                return 0;
             } else if (pi->serial != serial &&
                     strcmp(supp_status, "stopped") == 0) {
+                LOGI("Wi-Fi has been stopped");
                 return -1;
             }
         }
@@ -313,6 +356,7 @@
     char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
     int count = 50; /* wait at most 5 seconds for completion */

+    LOGI("%s called",__func__);
     /* Check whether supplicant already stopped */
     if (property_get(SUPP_PROP_NAME, supp_status, NULL)
         && strcmp(supp_status, "stopped") == 0) {
@@ -337,6 +381,7 @@
     char ifname[256];
     char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; 

+    LOGI("%s called",__func__);
     /* Make sure supplicant is running */
     if (!property_get(SUPP_PROP_NAME, supp_status, NULL)
             || strcmp(supp_status, "running") != 0) {
@@ -344,7 +389,7 @@
         return -1;
     } 

-    property_get("wifi.interface", iface, "sta");
+    property_get("wifi.interface", iface, "wlan0"); 

     if (access(IFACE_DIR, F_OK) == 0) {      
         snprintf(ifname, sizeof(ifname), "%s/%s", IFACE_DIR, iface);
@@ -352,6 +397,8 @@
         strlcpy(ifname, iface, sizeof(ifname));
     }

+    LOGI("Interface directory = %s", IFACE_DIR);
+    LOGI("Interface name = %s", ifname);
     ctrl_conn = wpa_ctrl_open(ifname);
     if (ctrl_conn == NULL) {
         LOGE("Unable to open connection to supplicant on \"%s\": %s",
@@ -370,6 +417,7 @@
         ctrl_conn = monitor_conn = NULL;
         return -1;
     } 
+    LOGI("Connect to Supplicant done\n");
     return 0;
 }

@@ -377,20 +425,26 @@
 {
     int ret;

+    LOGI("%s, cmd = %s\n", __FUNCTION__, cmd);
     if (ctrl_conn == NULL) {
         LOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd);
         return -1;
     }
+
+    memset(reply, 0, *reply_len);
     ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), reply, reply_len, NULL);
     if (ret == -2) {
         LOGD("'%s' command timed out.\n", cmd);
         return -2;
     } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {
+        LOGI("reply:%s\n",reply);
         return -1;
     }
     if (strncmp(cmd, "PING", 4) == 0) {
         reply[*reply_len] = '\0';
     }
+
+    LOGI("returning reply %s for cmd %s\n", reply, cmd);
     return 0;
 }

@@ -402,10 +456,13 @@
     int result;
    struct timeval tval;
    struct timeval *tptr;
-    
+
+    LOGI("%s called",__func__);
+    LOGI("monitor_conn checking \n");
     if (monitor_conn == NULL)
         return 0;

+    LOGI("calling wpa_ctrl_recv\n");
     result = wpa_ctrl_recv(monitor_conn, buf, &nread);
     if (result < 0) {
         LOGD("wpa_ctrl_recv failed: %s\n", strerror(errno));
@@ -413,6 +470,7 @@
     }
     buf[nread] = '\0';
     /* LOGD("wait_for_event: result=%d nread=%d string=\"%s\"\n", result, nread, buf); */
+    LOGI("wait_for_event: result=%d nread=%d string=\"%s\"\n", result, nread, buf);
     /* Check for EOF on the socket */
     if (result == 0 && nread == 0) {
         /* Fabricate an event to pass up */
@@ -437,11 +495,13 @@
             memmove(buf, match+1, nread+1);
         }
     }
+    LOGI("returning nread\n");
     return nread;
 }

 void wifi_close_supplicant_connection()
 {
+    LOGI("%s called",__func__);
     if (ctrl_conn != NULL) {
         wpa_ctrl_close(ctrl_conn);
         ctrl_conn = NULL;
@@ -454,5 +514,6 @@

 int wifi_command(const char *command, char *reply, size_t *reply_len)
 {
+    LOGI("%s called, cmd:%s",__func__,command);
     return wifi_send_command(ctrl_conn, command, reply, reply_len);
 }

In addition to this modification of wifi.c, change tiwlan0 to wlan0 in the source code of android-1.5r3. Specifically, the files listed
below are needed to be altered from tiwlan0 to wlan0.

  • android-1.5r3/external/dhcpcd/android.conf
  • frameworks/base/services/java/com/android/server/WiFiWatchdogService.java
  • android-1.5r3/frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

After changing all parts of the source code, execute build command.

adjustment android userland

To turn on/off WiFi form GUI of android, the kernel module of rt73usb and rt73bin are needed to be placed in the userland of
android. The locations are shown below.

/system/lib/modules/rt2x00lib.ko
/system/lib/modules/rt2x00usb.ko
/system/lib/modules/rt73usb.ko
/system/etc/firmware/rt73.bin

Configuring the name of wifi interface, the code is added to the file, which is located at/system/build.prop

# WiFi settings
wifi.interface = wlan0

Add few code to init.rc. This allows wlan0 to start up automatically and dhcpto obtain the address, as running insmod from GUI.

  setprop wifi.interface wlan0
  setprop wlan.driver.status ok

  service ifcfg_ralink /system/bin/ifconfig wlan0 up
  #   disabled
  #   oneshot

  service wpa_supplicant /system/bin/logwrapper /system/bin/wpa_supplicant -Dwext -iwlan0 -c /system/etc/wifi/wpa_supplicant.conf -dd
      disabled
      group system

  service dhcpcd /system/bin/logwrapper /system/bin/dhcpcd -d wlan0
      disabled
      oneshot
      #group system dhcp

  on property:init.svc.wpa_supplicant=stopped
      stop dhcpcd

As the method described above is completed, WiFi is configured from GUI of android userland. Once WiFi is configured,
Wireless LAN can be turned on/off from WiFI, which can be accessed form Wireless configuration, WiFi, Wi-Fi, to WiFi.
As turning On from Of, bc9 attempts to connect the priority access point. If bc9 failed to establish the connection, it
scans access points and displays the list of available access points at Wi-Fi network.

Reference

http://groups.google.co.jp/group/android-porting/browse_thread/thread/126310f20d406bd7/
http://whitesc3.blog7.fc2.com/blog-entry-108.html (in Japanese)
http://whitesc3.blog7.fc2.com/blog-entry-111.html (in Japanese)


Attach file: fileRT71W_Firmware_V1.8.zip 1303 download [Information]

Front page   Edit Freeze Diff Backup Upload Copy Rename Reload   New List of pages Search Recent changes   RSS of recent changes
Last-modified: 2010-11-04 (Thu) 08:47:26 (4919d)