前言:
这篇文章是从一个外国人写的wifi移植教程那里翻译过来的,原文链接暂时没找到,以后找到了再补上去。
在参考了原文的教程下,我也成功的将中龙通usb rt5370 wifi 模组移植到了android 平台上面。
PS:原文拥有很长很长的评论和探讨,太精彩了!佩服佩服!
好了,下面是自己的译文:
0 理解android wifi如何工作(了解下面代码的功能)
hardware/libhardware_legacy/wifi/wifi.c
frameworks/base/wifi/java/android/net/wifi
frameworks/base/core/jni/android_net_wifi_Wifi.cpp
frameworks/base/core/java/android/net
1 BoardConfig.mk里面使能wap_supplicant编译选项
在BoardConfig.mk文件里加入:
BOARD_WPA_SUPPLICANT_DRIVER := WEXT
这将会使得在external/wpa_supplicant/Android.mk 里设置WPA_BUILD_SUPPLICANT 为true,
从而打开编译driver_wext.c的编译选项。
如果你有定制的wap_supplicant驱动,你可以用AWEXT或者你的驱动的名字替代WEXT(MADWIFI,PRISM etc)。
2 使能wap_supplicant debug(可选的)
2.1 修改common.c ,设置wpa_debug_level = MSG_DEBUG(设置为MSG_INFO的话会输出少量信息)
2.2 修改commmon.h文件 , 在 #define wpa_printf(level, ...) 里面,
修改 if ((level) >= MSG_INFO) 为 if ((level) >= MSG_DEBUG)
3 为你的设备提供正确的wpa_supplicant.conf
提供 wpa_supplicant.conf是非常重要的,因为这个文件里指定了android 的控制socket 的路径(ctr_interface=),
这个文件会被你的AndroidBoard.mk拷贝至$(TARGET_OUT_ETC)/wifi,通常路径为:/system/etc/wifi/wpa_supplicant.conf 。
这个路径会被init.rc的wpa_supplicant服务所使用。
这里存在两种方式来配置wpa_supplicant服务,一种就是在android 命名空间里使用私有的socket,这个私有的socket
由wpa_ctrl.c里的socket_local_client_connect()函数生成。另一种就是使用标准的unix socket。
wpa_supplicant.conf 配置的最低要求:
--Adroid 私有的 socket
ctrl_interface=wlan0
update_config=1
-- Unix standard socket
ctrl_interface=DIR=/data/system/wpa_supplicant GROUP=wifi
update_config=1
根据的你的驱动你可能会加入:
ap_scan=1
假如你有AP关联问题,那么改为ap_scan=0,让你的驱动代替wpa_supplicant去关联。
假如你希望让你的wpa_suppilca发t连接到non-WPA或者开放的无线网络,那么加入:
network={
key_mgmt=NONE
}
4 在init.rc里面创建正确的权限和路径
不正确的权限将会导致wpa_supplicant不能创建或者打开控制socket 和libhardware_legacy/wifi/wifi.c 无法连接.
自从谷歌修改wpa_supplicant,让wifi 用户/组 来运行以来,这个目录架构和文件的拥有者应该属于wifi 用户/组
(see os_program_init() function in wpa_supplicant/os_unix.c ).
其他的一些错误如:
E/WifiHW ( ): Unable to open connection to supplicant on "/data/system/wpa_supplicant/wlan0": No such file or directory will appear.
wpa_supplicant.conf也应该属于wifi 用户/组,因为wpa_supplicant会试图修改这个它。假如你的系统拥有/system,
并且文件系统是只读的,那么请使用/data/misc/wifi/wpa_supplicant.conf 这个位置,还有要使用新的位置来修改
init.rc里面的wpa_supplicant service。
确保这个路径在 init.rc里面是正确的:
mkdir /system/etc/wifi 0770 wifi wifi
chmod 0770 /system/etc/wifi
chmod 0660 /system/etc/wifi/wpa_supplicant.conf
chown wifi wifi /system/etc/wifi/wpa_supplicant.conf
#wpa_supplicant control socket for android wifi.c (android private socket)
mkdir /data/misc/wifi 0770 wifi wifi
mkdir /data/misc/wifi/sockets 0770 wifi wifi
chmod 0770 /data/misc/wifi
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
chown wifi wifi /data/misc/wifi
chown wifi wifi /data/misc/wifi/wpa_supplicant.conf
假如你在wpa_supplicant里面使用的是标准的unix socket,请假如:
# wpa_supplicant socket (unix socket mode)
mkdir /data/system/wpa_supplicant 0771 wifi wifi
chmod 0771 /data/system/wpa_supplicant
chown wifi wifi /data/system/wpa_supplicant
Do not add these if you use Android private socket because it will make wpa_supplicant non-functional, becausehardware/libhardware_legacy/wifi/wifi.c check for existence of the/data/system/wpa_supplicant folder and will pass a wrong interface name to wpa_ctrl_open()function.
如果你使用的是私有的socket,那么不要加入上面这一段,否则会使得wpa_supplicant无效,因为hardware/libhardware_legacy/wifi/wifi.c会检查
/data/system/wpa_supplicant 目录的存在性,会不错误的接口名字传递给wpa_ctrl_open()函数。
5 确保你的wpa_supplicant 和 dhcpcd (optional)在init.rc里启动
- 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
- Unix standard socket:
service wpa_supplicant /system/bin/wpa_supplicant -dd -Dwext -iwlan0 -c /system/etc/wifi/wpa_supplicant.conf
group system wifi inet
disabled
oneshot
假如你的wifi驱动采用其他名字而不是wlan0 来创建一个wifi接口,那么将上面文字中wlan0修改为你的接口名。
在init.rc里面也要启动dhcpcd:
service dhcpcd /system/bin/dhcpcd wlan0
group system dhcp
disabled
oneshot
6提供你的驱动模块或者编译进你的内核的驱动,
还有支持你的驱动的内核。
首先确保CONFIG_PACKET 和CONFIG_NET_RADIO(wirele extentions) 在你的内核里面是使能的。
驱动编译的两种方式:一是编译为模块。二是直接编译进内核,(假如你希望依靠内核自己检测驱动的话,比如usb wifi )
但编译进内核的话就要修改代码。
-- 编译为模块:
在你的 BoardConfig.mk 里面定义:
1. WIFI_DRIVER_MODULE_PATH := 模块加载的路径
你还需要在路径里指定模块名,通常的是这样的:/system/lib/modules/wlan.ko
2. WIFI_DRIVER_MODULE_NAME:= 在驱动里面常见的网络接口名字,例如wlan0。
3. WIFI_DRIVER_MODULE_ARG:= 一些在你加载驱动时想要传过去的声明,例如:nohwcrypt
当你要编译android系统是,确保你的内核模块已经复制进正确的目录里面。
-- 编译进内核:
-首先需要修改init.rc,以使hardware/libhardware_legacy/wifi/wifi.c 知道驱动里面已经加载了的网络接口名字,
以及设置wpa_supplicant运行的状态:
setprop wifi.interface "wlan0"
setprop wlan.driver.status "ok"
不要像我前面提到的那样设置init.svc.wpa_supplicant "running",因为这会阻碍wpa_supplicant从init进程里启动。
-第二,修改文件hardware/libhardware_legacy/wifi/wifi.c ,使得函数insmod()和rmmod() return 0 (简单的加入 return 0 ;因为要编译进内核啦,所以函数的第一行已经不需要了。)在check_driver_loaded()function里面检查/proc/modules之前也返回。
你可能会遇见wifi HW无法连接到wpa_supplicant socket的问题,即使拥有了正确的权限。可以尝试在GUI里面关闭/打开wifi来解决问题。
7 提供你的驱动所需的固件(如果需要的话)
如果你的驱动需要固件,那么将它拷贝至/etc/firmware 。 固件名字由驱动来定义,可能包含着一个目录例如:RTL8192SU/rtl8192sfw.bin,完整的路径应该是可用的。
8 确保你的驱动和android定制的wpa_supplicant 命令行
以及SIOCSIWPRIV ioctl 一起工作。
android 使用SIOCSIWPRIV ioctl 发送命令来修改驱动的行为和接受像信号强度,AP的mac地址,链接速度这些信息。除了谷歌 msm内核里分支里面的bcm4329驱动程序,这些ioctl通常不会在任何已知的无线驱动里实现。
那些没有实现ioctl的会出现像下面一样的错误信息:
E/wpa_supplicant( ): wpa_driver_priv_driver_cmd failed wpa_driver_priv_driver_cmd RSSI len = 4096
E/wpa_supplicant( ): wpa_driver_priv_driver_cmd failed
D/wpa_supplicant( ): wpa_driver_priv_driver_cmd LINKSPEED len = 4096
E/wpa_supplicant( ): wpa_driver_priv_driver_cmd failed
I/wpa_supplicant( ): CTRL-EVENT-DRIVER-STATE HANGED
After 4, WEXT_NUMBER_SEQUENTIAL_ERRORS errors, android will abort using the device.