转自:http://blog.csdn.net/dybinx/article/details/7380903
硬件环境
WIFI模块:Marvell8686 SDIO WIFI
开发板:S5PC100
软件环境: Linux 2.6.29 Android 2.1
项目目标:实现WIFI上网功能、并对WIFI休眠进行改善。
项目开发流程:
硬件分析:对Marvell8686 SDIO WIFI模块硬件工作特性了解和分析
工作原理分析:对Marvel8686 SDIO WIFI工作原理进行分析
Android WIFI框架分析:对Android WIFI系统框架分析
进行源码分析、编写、修改及编译
调试、并完善
一、1.编译内核,生成驱动模块
在内核的根目录下执行make
生成libertas.ko 和libertas_sdio.ko
[*] Networking support --->
[*] Wireless --->
--- Wireless
<*> Improved wireless configuration API
[*] cfg80211 regulatory debugging
[*] nl80211 new netlink interface
support
-*- Common routines for IEEE802.11 drivers
Device Drivers --->
[*] Network device support --->
Wireless LAN --->
<*> Marvell 8xxx Libertas WLAN driver support (注:编译进内核,若模块则是:libertas.ko)
Marvell Libertas 8385 and 8686 SDIO 802.11b/g cards (注:编译成libertas_sdio.ko)
2.编译内核,确保支持sd卡
Device Drivers ---
<*> MMC/SD/SDIO card support --->
--- MMC/SD/SDIO card support
[*] MMC debugging
[ ] Allow unsafe resume (DANGEROUS)
[*] MMC embedded SDIO device support (EXPERIMENTAL)
[ ] Enable paranoid SD card initialization (EXPERIMENTAL)
*** MMC/SD/SDIO Card Drivers ***
<*> MMC block device driver
[*] Use bounce buffer for simple hosts
[ ] Deferr MMC layer resume until I/O is requested
< > SDIO UART/GPS class support
< > MMC host test driver
*** MMC/SD/SDIO Host Controller Drivers ***
<*> Secure Digital Host
3.make zImage
make modules
(libertas_sdio.ko在drivers/net/wireless/libertas目录下)
二、制作测试工具(使用静态编译)
现在,sd卡支持了,驱动以内核自带的以模块的方式编译得到。
接下来先编译几个测试工具。
测试无线网卡用无线工具iwconfig iwlist等命令是通过开源软件wireless_tools_29.rar编译得到。
步骤:
1.解压。由于这的是win32的压缩包,先在windows底下解压,再拷贝到Ubuntu下。
2.修改Makefile:
8 PREFIX = ./tools //指定安装路径
12 CC = arm-linux-gnu-gcc (和编译你的文件系统所用保持一致,需要注意的是,如果在执行sudo
make
就要该编译链的绝对路
径)
14 AR = arm-linux-gnu-ar
#BUILD_STATIC = y --> BUILD_STATIC = y
#BUILD_STRIPPING = y --> BUILD_STRIPPING = y
CFLAGS=-Os -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow \
-Wpointer-arith -Wcast-qual -Winline -I.
在这后面添加新行:
CFLAGS += -static
3.make
4.make install
由于我是在当前目录下(PREFIX = ./tools) 产生的这个tools文件夹,
查看有:
lib sbin usr
lib目录,libiw.a静态库 ;而sbin目录为一些网络工具,如:iwlist、iwconfig等
到里面的sbin目录底下,
$file iwlist 输出信息:iwlist: ELF 32-bit LSB executable, ARM, version 1, statically linked, for GNU/Linux 2.4.3, strippe
确保是静态的。
然后将这些命令复制到android文件系统的/system/busybox/bin(就是out/target/prodruct/fs100/system/busybox/bin)
或者system/bin(就是out/target/prodruct/fs100/system/bin)目录下就可以使用这些命令了。
三、将固件放到指定目录下。
1.mkdir /system/etc/firmware(就是out/target/prodruct/fs100/system/etc/firmware)
2.cp sd8686.bin sd8686_helper.bin out/target/prodruct/fs100/system/etc/firmware
四、测试,sdio卡是否可用。
如果已经编译好了文件系统,就可以跳过第一步。
1.编译android系统。
1)在文件系统根目录下执行:
. ./build/envsetup.sh
注意这里两个’.’ 之间有一个空格,第一个’.’指定用当前 shell 解析这个脚本,否则不能执行。
2)配置板级信息:
tapas
就是上一步执行结束之后导出到环境变量里的命令,专门用来配置板级信息的。
Build for the simulator or the device?
1. Device
2. Simulator
Which would you like? [1] 1
Build type choices are:
1. release
2. debug
Which would you like? [1] 1
Which product would you like? [fs100] fs100
Variant choices are:
1. user
2. userdebug
3. eng
Which would you like? [eng] eng
确保输出的配置信息为:
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.1-update1
TARGET_PRODUCT=fs_s5pc100
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID= ERE27
3)开始编译(如果不能找到 mm,执行”source build/envsetup.sh”):
mm
2.重新生成文件系统镜像。
./make_fs100_yaffs2_image.sh
就会在 Android 源码根目录下生成目录“fs100_root”,这个目录就是编译生成的 Android 文件系统,
调试时可以直接把这个目录作为 NFS-Server 的目录。还会生成一个“fs100_root.img”文件,这个文件就
是 Android的 yaffs2 格式的镜像,可以烧写到 Nand Flash 上。
这里我是烧写上fs100_root.img.
3.手动加载驱动。
由于sdio8686。需要加载两个.ko(libertas.ko、libertas_sdio.ko) 文件。
上面linertas.ko的模块,我已经编进内核。
所以我只需执行:
1)/# insmod libertas_sdio.ko
输出信息:
libertas_sdio: Libertas SDIO driver
libertas_sdio: Copyright Pierre Ossman
__func__ = if_sdio_prog_helper
libertas_sdio mmc1:0001:1: firmware: requesting sd8686_helper.bin
init: untracked pid 2137 exited
libertas_sdio mmc1:0001:1: firmware: requesting sd8686.bin
init: untracked pid 2140 exited
libertas: 00:0b:6c:91:a3:f6, fw 9.70.3p24, cap 0x00000303
libertas: unidentified region code; using the default (USA)
wlan0 (libertas_sdio): not using net_device_ops yet
libertas: PREP_CMD: command 0x00a3 failed: 2
libertas: PREP_CMD: command 0x00a3 failed: 2
libertas: wlan0: Marvell WLAN 802.11 adapter
2)/# ifconfig -a
(要是提示:-a: No such device,则输入命令:system/busybox/sbin/ifconfig -a)
查看是否检测到无线网卡
有如下信息说明检测到无线网卡
wlan0 Link encap:Ethernet HWaddr 00:0B:6C:91:A3:F6
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
3)测试:
/#ifconfig wlan0
up
/#iwlist wlan0
scanning
/#iwconfig wlan0 essid
"fs100"
/#ifconfig wlan0 192.168.1.99 netmask 255.255.255.0 up (静态分配ip,
也可以动态分配,用DHCP)
/#route add default gw 192.168.1.1(无线路由)
/#ping 192.168.1.1
4)能够ping通后,说明sdio8686 wifi网卡没有问题
五、测试wpa_supplicant。
因为现在的无线wifi网络大多是wpa加密。 所以需要用到wpa_supplicant。
android系统中也自带有wpa_supplicant。
wifi的大致架构是这样的,app-->java framework-->|jni|-->c++ framework -->wifi.c -->wpa_supplicant-->sdio8686.
所以我们要先测试低层先通了,因此,就得先测试确保wpa_supplicant和sdio8686通了的。
我们编译android系统时,就已经把自带的wpa_supplicant和wpa_cli编译好并放到system/bin目录下了.
1.配置wpa_supplicant。
在在目录下有个wpa_supplicant.conf。
将wpa_supplicant.conf放到out/target/product/fs100/system/etc 目录下。如果是烧到板子上的话,需要重烧板子。
要是以网络挂载的方式,则放到相应的目录。
#vim /rootfs/filesystem/etc/wpa_supplicant.conf
修改内容如下:
# WPA-PSK/TKIP
ctrl_interface=/var/run/wpa_supplicant
network={
ssid="fs100" //填写无线网络的的用户名
key_mgmt=WPA-PSK
proto=WPA
pairwise=TKIP
group=TKIP
psk="1234567890" //填写密码
}
2.#mkdir –p /var/run/wpa_supplicant
3.确认是否加载驱动。
用ifconfig -a查看是否已经创建wlan0这个节点。
没有就把驱动加载进内核。
4.创建连接暗文密码(PSK密码),通过明码转换
#cd /etc
(该目录下要有
wpa_supplicant.conf)
#wpa_passphrase fs100 1234567890 >> wpa_supplicant.conf
此时将在wpa_supplicant.conf文件中生成:
network={
ssid="fs100"
#psk="1234567890" //此物为路由器中设定的人类能读得懂的密码,供我们使用
psk=d290464a66df8541cee4f100627446177ee7ce5d9eb36981e4dff412730b2d5a //这个又1234567890转换后的psk密码
}
-----------------------------------------------------------------------------------------------------<----
---->遇到问题:在测试过程中要用到wpa_passphrase这个命令程序。但是android并没提供wpa_passphrase <----
---->这命令,我只通过wpa_supplicant-0.7.3这个源码包编译出这个命令。但是这个命令在开发板上执行 <----
---->不了(编译链不对)。我又找到android源码中有wpa_passphrase.c,而编译出来的路径下通过find <----
---->命令去找这个wpa_passphrase又找不到。 <----
---->解决办法:同过分析wpa_passphrase.c才发现,里面就短短的一个mian函数。有个提示 <----
---->usage: wpa_passphrase [passphrase]\n""\nIf passphrase is left out, it will be read from "<----
---->"stdin\n <----
---->现在不用我说都知道了吧!所以这个在哪运行都可以,这命令就是为了通过ssid和passphrase得到暗文。 <----
---->所以我在Ubuntu底下直接执行:wpa_passphrase fs100 1234567890 <----
---->同样输出信息:network=
{
<----
---->
ssid="fs100"
<----
----> #psk="1234567890" <----
----> psk=d290464a66df8541cee4f100627446177ee7ce5d9eb36981e4dff412730b2d5a <----
---->
}
<----
-----------------------------------------------------------------------------------------------------<-----
5.然后修改wpa_supplicant.conf配置文件,把 psk="1234567890" 这一行明文密码改成生成的暗文密码,即:
psk=d290464a66df8541cee4f100627446177ee7ce5d9eb36981e4dff412730b2d5a
其它生成的多余信息删除,最后wpa_supplicant.conf文件如下:
# WPA-PSK/TKIP
ctrl_interface=/var/run/wpa_supplicant
network={
ssid="FS2410" //填写无线网络的的用户名
key_mgmt=WPA-PSK
proto=WPA
pairwise=TKIP
group=TKIP
psk=d290464a66df8541cee4f100627446177ee7ce5d9eb36981e4dff412730b2d5a
}
6.链接AP:
在开发板终端输入wpa_supplicant回车,会显示帮助信息,最后有个:
example:
wpa_supplicant -Dwext -iwlan0 -c/etc/wpa_supplicant.conf
拷贝example用法,执行:
# wpa_supplicant -Dwext -iwlan0 -c/etc/wpa_supplicant.conf &(需要一直放在后台运行)
打印信息有:
Trying to associate with 00:26:f2:0d:5a:c4 (SSID='fs100' freq=2412 MHz)
Associated with 00:23:68:28:4e:a8
CTRL-EVENT-DISCONNECTED bssid=00:23:68:28:4e:a8 reason=0
Associated with 00:26:f2:0d:5a:c4
WPA: Key negotiation completed with 00:26:f2:0d:5a:c4 [PTK=TKIP GTK=TKIP]
CTRL-EVENT-CONNECTED - Connection to 00:26:f2:0d:5a:c4 completed (auth) [id=0 id_str=]
分配IP地址:
#ifconfig wlan0 192.168.1.5 //也可以动态分配 dhclient wlan0
再ping下网关,是否连接成功:
#ping 192.168.1.1
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 17.627/20.023/24.631 ms
……
无线网卡连接成功!
六、android的wifi移植。
现在底层的都通了,接下来就把wifi移植到android系统中。
1.提供固件firmware给驱动。
WIFI需要的firmware要复制到/etc/firmware。 或者复制到WIFI驱动指定的位置,然后WIFI驱动会自动加载。
在此把wifi模块提供的sd8686.bin sd8686_helper.bin放到out/target/product/fs100/system/etc/firmware目录下。
其实,和上面放目录是一样的。
2.修改自己定制的BoardConfig.mk,使能wpa_supplicant。
修改vendor/farsight/fs100/BoardConfig.mk:
把BOARD_WPA_SUPPLICANT_DRIVER :=true
改为BOARD_WPA_SUPPLICANT_DRIVER :=
WEXT
目的是:把driver_wext.c作为wpa_supplicant的driver。wpa_supplicant通过它去与内核的wifi驱动打交道。
修改external/wpa_supplicant/Android.mk
把WPA_BUILD_SUPPLICANT :=false
改为WPA_BUILD_SUPPLICANT := true
默认使用驱动driver_wext.c。
如果使用定制的wpa_supplicant驱动(例如 madwifi),可以设置:
BOARD_WPA_SUPPLICANT_DRIVER := MADWIFI
3.使wpa_supplicant打印更多的调试信息
wpa_supplicant默认信息显示的等级为SG_INFO,为了输出更多信息,可修改:
修改external/wpa_supplicant/common.c
把int wpa_debug_level = MSG_INFO;
改为:int wpa_debug_level = MSG_DEBUG;
修改external/wpa_supplicant/common.h
把宏定义#define wpa_printf(level, ...)中的if ((level) >= MSG_INFO)
改为if ((level) >= MSG_DEBUG)
4.提供一个合适的wpa_supplicant.conf
修改
wpa_supplicant.conf:
把external/wpa_supplicant/wpa_supplicant.conf拷贝到out/target/product/fs100/system/etc/wifi/目录下,
并把:ctrl_interface=DIR=/data/misc/wifi/wpa_supplicant GROUP=wifi
改为:
ctrl_interface=wlan0
5.修改init.rc配置路径和权限
A)配置init.rc文件修改out/target/product/fs100/root/init.rc, 让wifi用户拥有相关的权限,
在 #give system access to wpa_supplicant.conf for backup and restore后面增加:
#add by liyulei
2011-11-19
mkdir /data/misc/wifi/sockets 0777 wifi
wifi
chown wifi wifi
/data/misc/wifi
chown wifi wifi
/data/misc/wifi/wpa_supplicant.conf
#for
dhcp
mkdir /data/misc/dhcp 0777 dhcp
dhcp
chmod 0770
/data/misc/dhcp
#end
add
B)注释原有环境变量#export PATH
改
为:
export PATH
/sbin:/system/sbin:/system/bin:/system/xbin
#above modified by liyulei
2011-11-19
6. 确保wpa_supplicant和dhcpcd是通过init.c起来的。
配置init.rc文件修改out/target/product/fs100/root/init.rc, 在末尾添加:
#add by liyulei 2011-11-19
for wifi Android private socket
service wpa_supplicant /system/bin/wpa_supplicant -dd -Dwext -iwlan0 -c/system/etc/wifi/wpa_supplicant.conf
socket wpa_wlan0 dgram 660 wifi wifi
group system wifi inet
disabled
oneshot
#for dhcp
service dhcpcd /system/bin/dhcpcd wlan0
group system dhcp
disabled
oneshot
#end add。
7.设置驱动以模块方式加载
拷贝内核生成的drivers/net/wireless/libertas/libertas_sdio驱动模块
到out/target/product/fs100/system/lib/modules/目录下。
然后修改hardware/libhardware_legacy/wifi/wifi.c
1)修改wifi.c中的宏:
#define WIFI_DRIVER_MODULE_PATH
"/system/lib/modules/libertas_sdio.ko"
#define WIFI_DRIVER_MODULE_NAME
"libertas_sdio"
#define WIFI_TEST_INTERFACE "wlan0"
或者,也可以这样改:
修改vendor/farsight/fs100/BoardConfig.mk:
WIFI_DRIVER_MODULE_PATH
="/system/lib/modules/libertas_sdio.ko"
WIFI_DRIVER_MODULE_NAME
="libertas_sdio"
WIFI_TEST_INTERFACE ="wlan0"
和上面一样的效果。
8.添加休眠唤醒代码:
1)在frameworks\base\core\Jni\android_net_wifi_Wifi.cpp中添加相应的休眠唤醒函数:
在static JNINativeMethod gWifiMethods[]结构体添加方法:
{"moduleWakeupStatus","()Z",(void*)android_net_wifi_moduleWakeupStatus},
{"WaitTime","(I)Z",(void*)android_net_wifi_WaitTime},
并实现这些方法/*add by liyulei 2011-11-20*/
static jboolean android_net_wifi_moduleWakeupStatus(JNIEnv* env, jobject clazz)
{
return (jboolean)(::wifi_module_wakeup_status() == 0);
}
static jboolean android_net_wifi_WaitTime(JNIEnv* env, jobject clazz, jint waittime)
{
return (jboolean)(::wifi_module_wait_time(waittime) == 0);
}
/*end add by liyulei*/
2)而相应的细体实现在wifi.c中:
在wifi.c添加:
/*add by caoyi 2011-10-20*/
int wifi_module_wakeup_status()
{
return 1;
}
int wifi_module_wait_time(int watiTime)
{
sleep(watiTime);
return
0;
}
/*end add*/
9.配置dhcpcd.conf
修改源码目录下external/dhcpcd下的Android.mk文件
取消注释
-->26 include $(CLEAR_VARS)
-->27 LOCAL_MODULE := dhcpcd.conf
-->28 LOCAL_MODULE_TAGS := user
-->29 LOCAL_MODULE_CLASS := ETC
-->30 LOCAL_MODULE_PATH := $(etc_dir)
-->31 LOCAL_SRC_FILES := android.conf
-->32 include $(BUILD_PREBUILT)
然后重新编译mm,将编译产生的dhcpcd.conf放置到文件系统的目录system/etc/dhcpcd/dhcpcd.conf
最后确定dhcpcd.conf内容有:
interface wlan0
option subnet_mask, routers, domain_name_servers
option ntp_servers
没有的话,就修改。
10.修改WifiStateTracker.java
将frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
里的mInterfaceName = SystemProperties.get("wifi.interface", "eth0");
改为:
mInterfaceName = SystemProperties.get("wifi.interface", "wlan0");
最后重新编译mm
编译镜像文件后烧写到开发板上就可以使用WiFi模块连接网络
阅读(5144) | 评论(0) | 转发(0) |