BeagleBoard上のAndroidでWiFiを使う

USB無線LAN子機であるBuffalo WLI-UC-G300Nは、Rowboatをデフォルトの状態でインストールしたBeagleBoard上のAndroidでは認識されない。
利用可能にする為には、子機が利用しているチップセットのベンダのサイトからドライバのソースを取得し、それをBeagleBoard向けにコンパイルして、作成したモジュールをインストールするなど、それなりに手間がかかった。
今回は、この方法についての一連の流れを紹介する。

前準備

ドライバのソースをコンパイルする前に、カーネルコンフィギュレーションを一部変更してカーネルイメージをコンパイルし直しておく必要がある。
これを行っておかないと、ドライバのコンパイルが上手くいかないので注意。
必要なカーネルコンフィギュレーションは、menuconfigからは表示されないため、事前にひと手間加える必要がある。ここで、カレントディレクトリは、BeagleBoard向けのカーネルディレクトリであるとする。
./net/wireless/Kconfig

--- net/wireless/Kconfig.org    2011-06-17 23:27:07.000000000 +0900
+++ net/wireless/Kconfig    2011-06-17 23:26:38.000000000 +0900
@@ -1,4 +1,5 @@
 config WIRELESS_EXT    
-    bool
+    bool "WIRELESS_EXT"
 
 config WEXT_CORE
@@ -14,6 +15,7 @@
     bool
 
 config WEXT_PRIV
-    bool
+    bool "WEXT_PRIV"
 
 config CFG80211

ここで、追加しているbool以降のダブルクォーテーション内の文字列は何でも良い。
上記修正を行った上で、menuconfigを実行する:

> make ARCH=arm CROSS_COMPILE=/home/hide/workspace/git/myFroyo/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi- menuconfig

コンフィギュレーション画面が起動したら、「Networking support」>「Wireless」以下の「WIRELESS_EXT」と「WEXT_PRIV」項目をに変更する(項目名は、おそらくbool以降のダブルクォーテーション内で指定した文字列)。
変更を保存し、カーネルイメージを作成する:

> make ARCH=arm CROSS_COMPILE=/home/hide/workspace/git/myFroyo/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi- uImage

これで前準備完了。

ドライバモジュールのコンパイル

Buffalo WLI-UC-G300Nは、内部ではRalink社のRT2870というチップセットが使われている(参考ページ)。
Ralink社のLinux向けドライバソースが公開されているページに行き、RT2870USB(RT2870/RT2770)からドライバのソースアーカイブをダウンロードする。
私が取得した際のアーカイブファイルは、「2010_0709_RT2870_Linux_STA_v2.4.0.1.tar.bz2」。どうも結構頻繁に変わっているようなので、ここに書かれているインストラクションに従う場合は、同じバージョンのものを利用した方が良い。Ralink社のサイトから該当バージョンのソースが取得できなくなってしまうかも知れないので、念のため今回使用したアーカイブをこのブログに添付しておく。
2010_0709_RT2870_Linux_STA_v2.4.0.1.tar.bz2 直
ダウンロードしたアーカイブを解凍し、解凍されて出来たフォルダ内のMakefile類に以下の追記を行う:

./Makefile
--- Makefile.org    2010-07-09 11:13:47.000000000 +0900
+++ Makefile    2011-06-15 22:38:51.000000000 +0900
@@ -8,7 +8,7 @@
 RTMP_SRC_DIR = $(RT28xx_DIR)/RT$(CHIPSET)
 
 #PLATFORM: Target platform
-PLATFORM = PC
+#PLATFORM = PC
 #PLATFORM = 5VT
 #PLATFORM = IKANOS_V160
 #PLATFORM = IKANOS_V180
@@ -38,6 +38,7 @@
 #PLATFORM = MT85XX
 #PLATFORM = NXP_TV550
 #PLATFORM = MVL5
+PLATFORM = BEAGLEBOARD
 
 
 ifeq ($(TARGET),LINUX)
@@ -217,6 +218,11 @@
 CROSS_COMPILE = /opt/montavista/pro/devkit/arm/v5t_le_mvl5/bin/arm_v5t_le-
 endif
 
+ifeq ($(PLATFORM),BEAGLEBOARD)
+LINUX_SRC = /home/hide/workspace/git/myFroyo/kernel
+CROSS_COMPILE = /home/hide/workspace/git/myFroyo/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-
+endif
+
 export OSABL RT28xx_DIR RT28xx_MODE LINUX_SRC CROSS_COMPILE CROSS_COMPILE_INCLUDE PLATFORM RELEASE CHIPSET RTMP_SRC_DIR LINUX_SRC_MODULE TARGET
 
 # The targets that may be used.
@@ -247,9 +253,13 @@
 ifeq ($(PLATFORM),FREESCALE8377)
     $(MAKE) ARCH=powerpc CROSS_COMPILE=$(CROSS_COMPILE) -C  $(LINUX_SRC) SUBDIRS=$(RT28xx_DIR)/os/linux modules
 else
+ifeq ($(PLATFORM),BEAGLEBOARD)
+    $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C  $(LINUX_SRC) SUBDIRS=$(RT28xx_DIR)/os/linux modules
+else
     $(MAKE) -C $(LINUX_SRC) SUBDIRS=$(RT28xx_DIR)/os/linux modules
 endif
 endif
+endif
./os/linux/config.mk
--- os/linux/config.mk.org    2010-07-09 11:46:00.000000000 +0900
+++ os/linux/config.mk    2011-06-17 00:34:42.000000000 +0900
@@ -8,7 +8,8 @@
 HAS_XLINK=n
 
 # Support Wpa_Supplicant
-HAS_WPA_SUPPLICANT=n
+# HAS_WPA_SUPPLICANT=n
+HAS_WPA_SUPPLICANT=y
 
 # Support Native WpaSupplicant for Network Maganger
 HAS_NATIVE_WPA_SUPPLICANT_SUPPORT=n
@@ -482,3 +483,7 @@
 CFLAGS := -D__KERNEL__ -I$(LINUX_SRC)/include -I$(RT28xx_DIR)/include -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-omit-frame-pointer -mapcs -mno-sched-prolog -mno-thumb-interwork -D__LINUX_ARM_ARCH__=5 -march=armv5te -mtune=arm926ej-s --param max-inline-insns-single=40000  -Uarm -Wdeclaration-after-statement -Wno-pointer-sign -DMODULE $(WFLAGS) 
 export CFLAGS
 endif
+
+ifeq ($(PLATFORM),BEAGLEBOARD)
+EXTRA_CFLAGS := $(WFLAGS) -I$(RT28xx_DIR)/include -O3
+endif

どちらのファイルの修正も主にBeagleBoard向け(ARM向け)にドライバをインストールするための設定を追記している。
ちなみに、私の場合はBeagleBoard向けのLinuxカーネルのソースは、/home/hide/workspace/git/myFroyo/kernelに、クロスコンパイル向けのツールチェーンのバイナリは/home/hide/workspace/git/myFroyo/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/以下に置かれている。

また、WLI-UC-G300Nは既に登録されているため必要ないが、他の無線LAN子機を使用する場合に、その子機がモジュールテーブルに登録されていない場合は、./common/rtusb_dev_id.cの40行目から定義されているモジュールテーブルに子機を登録する必要がある:

./common/rtusb_dev_id.c
/* module table */
USB_DEVICE_ID rtusb_dev_id[] = {
#ifdef RT2870
    {USB_DEVICE(0x148F,0x2770)}, /* Ralink */
    {USB_DEVICE(0x148F,0x2870)}, /* Ralink */
    {USB_DEVICE(0x07B8,0x2870)}, /* AboCom */
    {USB_DEVICE(0x07B8,0x2770)}, /* AboCom */
    {USB_DEVICE(0x0DF6,0x0039)}, /* Sitecom 2770 */
    {USB_DEVICE(0x0DF6,0x003F)}, /* Sitecom 2770 */
    {USB_DEVICE(0x083A,0x7512)}, /* Arcadyan 2770 */
    {USB_DEVICE(0x0789,0x0162)}, /* Logitec 2870 */
    {USB_DEVICE(0x0789,0x0163)}, /* Logitec 2870 */
    {USB_DEVICE(0x0789,0x0164)}, /* Logitec 2870 */
    {USB_DEVICE(0x177f,0x0302)}, /* lsusb */
    {USB_DEVICE(0x0B05,0x1731)}, /* Asus */
    {USB_DEVICE(0x0B05,0x1732)}, /* Asus */
    {USB_DEVICE(0x0B05,0x1742)}, /* Asus */
    {USB_DEVICE(0x0DF6,0x0017)}, /* Sitecom */
    {USB_DEVICE(0x0DF6,0x002B)}, /* Sitecom */
    {USB_DEVICE(0x0DF6,0x002C)}, /* Sitecom */
    {USB_DEVICE(0x0DF6,0x002D)}, /* Sitecom */
    {USB_DEVICE(0x14B2,0x3C06)}, /* Conceptronic */
    {USB_DEVICE(0x14B2,0x3C28)}, /* Conceptronic */
    {USB_DEVICE(0x2019,0xED06)}, /* Planex Communications, Inc. */
    {USB_DEVICE(0x07D1,0x3C09)}, /* D-Link */
    {USB_DEVICE(0x07D1,0x3C11)}, /* D-Link */
    {USB_DEVICE(0x14B2,0x3C07)}, /* AL */
    {USB_DEVICE(0x050D,0x8053)}, /* Belkin */
    {USB_DEVICE(0x14B2,0x3C23)}, /* Airlink */
    {USB_DEVICE(0x14B2,0x3C27)}, /* Airlink */
    {USB_DEVICE(0x07AA,0x002F)}, /* Corega */
    {USB_DEVICE(0x07AA,0x003C)}, /* Corega */
    {USB_DEVICE(0x07AA,0x003F)}, /* Corega */
    {USB_DEVICE(0x1044,0x800B)}, /* Gigabyte */
    {USB_DEVICE(0x15A9,0x0006)}, /* Sparklan */
    {USB_DEVICE(0x083A,0xB522)}, /* SMC */
    {USB_DEVICE(0x083A,0xA618)}, /* SMC */
    {USB_DEVICE(0x083A,0x8522)}, /* Arcadyan */
    {USB_DEVICE(0x083A,0x7522)}, /* Arcadyan */
    {USB_DEVICE(0x0CDE,0x0022)}, /* ZCOM */
    {USB_DEVICE(0x0586,0x3416)}, /* Zyxel */
    {USB_DEVICE(0x0CDE,0x0025)}, /* Zyxel */
    {USB_DEVICE(0x1740,0x9701)}, /* EnGenius */
    {USB_DEVICE(0x1740,0x9702)}, /* EnGenius */
    {USB_DEVICE(0x0471,0x200f)}, /* Philips */
    {USB_DEVICE(0x14B2,0x3C25)}, /* Draytek */
    {USB_DEVICE(0x13D3,0x3247)}, /* AzureWave */
    {USB_DEVICE(0x083A,0x6618)}, /* Accton */
    {USB_DEVICE(0x15c5,0x0008)}, /* Amit */
    {USB_DEVICE(0x0E66,0x0001)}, /* Hawking */
    {USB_DEVICE(0x0E66,0x0003)}, /* Hawking */
    {USB_DEVICE(0x129B,0x1828)}, /* Siemens */
    {USB_DEVICE(0x157E,0x300E)},    /* U-Media */
    {USB_DEVICE(0x050d,0x805c)},    
    {USB_DEVICE(0x050d,0x815c)},
    {USB_DEVICE(0x1482,0x3C09)}, /* Abocom*/
    {USB_DEVICE(0x14B2,0x3C09)}, /* Alpha */
    {USB_DEVICE(0x04E8,0x2018)}, /* samsung linkstick2 */
    {USB_DEVICE(0x1690,0x0740)}, /* Askey */
    {USB_DEVICE(0x5A57,0x0280)}, /* Zinwell */
    {USB_DEVICE(0x5A57,0x0282)}, /* Zinwell */
    {USB_DEVICE(0x7392,0x7718)},
    {USB_DEVICE(0x7392,0x7717)},
    {USB_DEVICE(0x1737,0x0070)}, /* Linksys WUSB100 */
    {USB_DEVICE(0x1737,0x0071)}, /* Linksys WUSB600N */
    {USB_DEVICE(0x0411,0x00e8)}, /* Buffalo WLI-UC-G300N*/
    {USB_DEVICE(0x050d,0x815c)}, /* Belkin F5D8053 */
    {USB_DEVICE(0x100D,0x9031)}, /* Motorola 2770 */
    {USB_DEVICE(0x0DB0,0x6899)},
#endif // RT2870 //
    { }/* Terminating entry */
};

USB_DEVICEマクロの第一引数はプロダクトID、第二引数はデバイスID。各デバイスのIDは、Web上のリストを参照。

以上の修正を行った上でmakeを実行:

> make

makeが正常終了すると、./os/linuxディレクトリ以下にrt2870sta.koが作成される。これがRT2870向けのドライバモジュール。

このドライバは、無線LANの接続情報を設定ファイルであるRT2870STA.datから読み込む仕組みになっている。そのため、この設定ファイルを自分の無線LAN環境にあった設定に書き換える:

./RT2870STA.dat
--- RT2870STA.dat.org    2010-07-09 11:13:47.000000000 +0900
+++ RT2870STA.dat    2011-06-17 00:33:22.000000000 +0900
@@ -4,7 +4,8 @@
 CountryRegionABand=7
 CountryCode=
 ChannelGeography=1
-SSID=11n-AP
+#SSID=11n-AP
+SSID=<無線LAN SSID>
 NetworkType=Infra
 WirelessMode=5
 Channel=0
@@ -18,9 +19,12 @@
 PktAggregate=0
 WmmCapable=1
 AckPolicy=0;0;0;0
-AuthMode=OPEN
-EncrypType=NONE
-WPAPSK=
+#AuthMode=OPEN
+#EncrypType=NONE
+#WPAPSK=
+AuthMode=WPAPSK
+EncrypType=TKIP
+WPAPSK=<無線LANパスワード>
 DefaultKeyID=1
 Key1Type=0
 Key1Str=

<無線LAN SSID>や、<無線LANパスワード>の部分は、自身の無線LAN環境に合わせた情報を入力する。また、セキュリティの種類(AuthMode)や、暗号化の種類(EncrypType)が異なる場合は、適切なものに変更する。詳細は、README_STAファイルを参照。

ドライバモジュールの配置

BeagleBoard上でAndroidを起動し、ホストPCからUSBで接続する。
ADBを使って、ホストPCからAndroidにリモートログインする(adbコマンドは、Android SDK中のplatform-toolsフォルダ以下にある):

> ./adb -s 20100720 shell

以下のコマンドを実行し、モジュールや接続情報ファイル(RT2870.dat)を配置する場所を作成する:

# ドライバモジュール配置用フォルダを作成
adb> mkdir /system/etc/lib/modules
# 接続情報ファイル配置用フォルダを作成
adb> mkdir /system/etc/Wireless
adb> mkdir /system/etc/Wireless/RT2870STA

ドライバモジュールの配置場所は任意で良いが、接続情報ファイルの配置場所はドライバが指定しているので、必ず上記の通りフォルダを作成する。
ちなみに、指定されている接続情報ファイルのパスは、「/etc/Wireless/RT2870/RT2870.dat」。/system/etcは、/etcからシンボリックリンクが張られている。
次に、ADBを使って、ホストPCからドライバモジュールと、接続情報ファイルをBeagleBoardに送信する:

> ./adb -s 20100720 push /home/hide/workspace/2010_0709_RT2870_Linux_STA_v2.4.0.1/os/linux/rt2870sta.ko /system/lib/modules/rt2870sta.ko
> ./adb -s 20100720 push /home/hide/workspace/2010_0709_RT2870_Linux_STA_v2.4.0.1/RT2870STA.dat /system/etc/Wireless/RT2870STA/RT2870STA.dat

リモートログインしたシェルで以下のコマンドを実行して、無線LAN子機を有効にする:

adb> insmod /system/lib/modules/rt2870sta.ko
rtusb init --->                                                                 
                                                                                
                                                                                
=== pAd = d18c4000, size = 501744 ===                                           
                                                                                
<-- RTMPAllocTxRxRingMemory, Status=0                                           
<-- RTMPAllocAdapterBlock, Status=0                                             
usbcore: registered new interface driver rt2870                                 
-->RTUSBVenderReset                                                             
<--RTUSBVenderReset                                                             
Key1Str is Invalid key length(0) or Type(0)                                     
Key2Str is Invalid key length(0) or Type(0)                                     
Key3Str is Invalid key length(0) or Type(0)                                     
Key4Str is Invalid key length(0) or Type(0)                                     
1. Phy Mode = 5                                                                 
2. Phy Mode = 5                                                                 
phy mode> Error! The chip does not support 5G band 1!                           
RTMPSetPhyMode: channel is out of range, use first channel=1                    
3. Phy Mode = 9                                                                 
MCS Set = ff ff 00 00 01                                                        
<==== rt28xx_init, Status=0                                                     
0x1300 = 00064300
adb> netcfg ra0 up
adb> netcfg ra0 dhcp

これで、子機のLEDが点滅を開始し、デバイスが認識されたことになる(※insmodの実行時にエラーメッセージが出力されているが、機能しているので無視)。
確認のため、ifconfigでra0の状態を確認する:

> ifconfig ra0
ra0: ip 192.168.11.10 mask 255.255.255.0 flags [up broadcast running multicast]

自分のプライベートIPがDHCPにより、ちゃんと割り振られていることが分かる。
次に、pingを打ってみる:

adb> ping 192.168.11.1
PING 192.168.11.1 (192.168.11.1) 56(84) bytes of data.
64 bytes from 192.168.11.1: icmp_seq=1 ttl=64 time=0.854 ms
64 bytes from 192.168.11.1: icmp_seq=2 ttl=64 time=0.855 ms
64 bytes from 192.168.11.1: icmp_seq=3 ttl=64 time=14.1 ms
64 bytes from 192.168.11.1: icmp_seq=4 ttl=64 time=15.5 ms
64 bytes from 192.168.11.1: icmp_seq=5 ttl=64 time=8.69 ms
64 bytes from 192.168.11.1: icmp_seq=6 ttl=64 time=2.99 ms
^C
--- 192.168.11.1 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5005ms
rtt min/avg/max/mdev = 0.854/7.197/15.595/6.053 ms

ここで、192.168.11.1は、無線LANルータのプライベートIPアドレス
今度は、WANに名前を使ってpingを打ってみる:

adb> ping www.google.com
ping: unknown host www.google.com

これは、DNSが正しく設定出来ていないことが原因。単純なことだが、私はこのエラーの解決に結構時間がかかってしまった。
これを解決する為には、以下のコマンドを実行して、DNSを登録する:

adb> setprop net.dns1 192.168.11.1

今回は、DNSとして、無線LANルータを指定した。
これ以降は、WANへのアクセスも名前で行うことが出来るようになる:

adb> ping www.google.com
PING www.google.com (74.125.153.106) 56(84) bytes of data.
64 bytes from ty-in-f106.1e100.net (74.125.153.106): icmp_seq=1 ttl=50 time=40.6 ms
64 bytes from ty-in-f106.1e100.net (74.125.153.106): icmp_seq=2 ttl=50 time=41.3 ms
64 bytes from ty-in-f106.1e100.net (74.125.153.106): icmp_seq=3 ttl=50 time=42.4 ms
64 bytes from ty-in-f106.1e100.net (74.125.153.106): icmp_seq=4 ttl=50 time=41.6 ms
64 bytes from ty-in-f106.1e100.net (74.125.153.106): icmp_seq=5 ttl=50 time=40.5 ms
64 bytes from ty-in-f106.1e100.net (74.125.153.106): icmp_seq=6 ttl=50 time=42.9 ms
^C
--- www.google.com ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5005ms
rtt min/avg/max/mdev = 40.589/41.605/42.969/0.907 ms

ここまで設定すれば、Android付属のWebブラウザなどからも、無線LANを使ってWebにアクセス出来るようになるはず。

参考サイト

今回の作業完了までには以下のサイトを参考にさせて頂いた:

残作業

今回は、リモートシェルを使ってモジュールをインストールやデバイスの有効化、DNSの登録を行ったが、これは本来であればAndroid起動時に自動的に行いたいもの。
次回は、この方法について紹介する。