您的位置:首页 > 移动开发 > Android开发

Porting WiFi drivers to Android (移植WiFi驱动到Android系统)

2016-03-03 17:56 615 查看


ref site: http://blog.linuxconsulting.ro/2010/04/porting-wifi-drivers-to-android.html


这篇关于android wifi移植的文章非常有参考价值, 虽然说的是android 2.1.



译文:

我们想要让picoPC支持许多USB和miniPC接口的WiFi, 这篇指导文档提供详细的步骤来说明添加一个新的wifi驱动都要做哪些以及如何让wifi在特定的Android编译环境中正常工作.(虽然这个指导文档是为android2.1编写的,
但是也适合之前版本的android发布版和将来的版本)

内容:

0. 理解Android下WiFi如何工作

1. 在BoardConfig.mk中使能编译wpa_supplicant应用

2. (可选)使能wpa_supplicant应用的debug功能

3. 提供合适的wpa_supplicant.conf文件

4. 在init.rc里面创建正确的路径和权限

5. 确认在init.rc里面启动了wpa_supplicant和dhcpcd(可选)

6. 以模块的形式提供你的驱动或者在kernel里面或kernel支持的环境下编译, 同时修改相应的android代码

7. 提供一个fireware如果你的模块需要它

8. 用android定制的wpa_supplicant命令和SIOCSIWPRIV ioctl控制方法来让你的驱动工作

0. 理解Android下WiFi如何工作

Android使用了一个修改过的wpa_supplicant守护程序(external/wpa_supplicant目录下)来支持wifi, 我们可以通过在hardware/libhardware_legacy/wifi/wifi.c(WiFiHW)文件中创建的socket来控制它,
Android UI上层APK使用了这个socket, 这个apk是frameworks/base/wifi/java/android/net/wifi/目录下生成的android.net.wifi, 它相应的JNI层的实现在frameworks/base/core/jni/android_net_wifi_Wifi.cpp文件中,
更高级别的网络管理在frameworks/base/core/java/android/net目录下实现.

1. 在BoardConfig.mk中使能编译wpa_supplicant应用

这个可以通过简单的添加BOARD_WPA_SUPPLICANT_DRIVER := WEXT到BoardConfig.mk文件中来实现. 这个变量将会导致"external/wpa_supplicant/Android.mk"中的WPA_BUILD_SUPPLICANT被设置为true,
然后编译driver_wext.c.

如果你有一个特别的wpa_supplicant驱动(例如madwifi或者我的android私有命令仿真器 -- 例如最新的paragraph), 你可以替换调AWEXT替换调WEXT, 或者使用你自己的驱动名称替换WEXT(比如MADWIFI, PRISM)

2. (可选)打开wpa_supplicant应用的debug功能

默认情况下 wpa_supplicant 程序的log级别是MSG_INFO, 有时这个并不够我们分析问题.

打开更多log的方法:

2.1 修改common.c并且设置wpa_debug_level = MSG_DEBUG

2.2 修改common.h并且把"#define wpa_printffrom if ((level) >= MSG_INFO)"修改成"if ((level) >= MSG_DEBUG)"

3. 提供合适的wpa_supplicant.conf文件

提供一个wpa_supplicant.conf非常重要,因为android的控制socket就在这个文件中指定(ctrl_interface=), 这个文件被编译文件AndroidBoard.mk拷贝到$(TARGET_OUT_ETC)/wifi目录下(通常是/system/etc/wifi/wpa_supplicant.conf).
这个位置将被添加到init.rc文件里面, 用来设置wpa_supplicant服务的配置选项.

这里有两个不通的方式来配置wpa_supplicant服务, 一个是在android的域名空间里面使用"私有"的socket, 由wpa_ctrl.c文件里面的socket_local_client_connect()函数创建,
另一个方法是使用标准的unix socket.

wpa_supplicant.conf的一个最简单的配置

- 使用android特有的socket

<span style="background-color: rgb(240, 240, 240);">ctrl_interface=wlan0 </span>
<pre code_snippet_id="1596220" snippet_file_name="blog_20160303_1_6785625" name="code" class="cpp">update_config=1



- Unix标准的socket

ctrl_interface=DIR=/data/system/wpa_supplicant GROUP=wifi
update_config=1


如果有需要你也可以加上下面的选项:

ap_scan=1


如果你的wifi连接AP有问题, 你应该设置toap_scan=0, 让驱动来实现连接AP而不是wpa_supplicant服务.

如果你想要wpa_supplicant服务连接非WPA加密的AP或者无密码的AP(默认情况下wifi忽略这些AP), 你应该添加:

network={
key_mgmt=NONE
}


4. 在init.rc里面创建正确的路径和权限

不正确的权限会导致wpa_supplicant不能创建和打开控制socket, 而且libhardware_legacy/wifi/wifi.c不能连接.

因为Google修改wpa_supplicant所在的用户和组为wifi, 所以相应的目录和文件所在用户和组应该改为wifi(参考wpa_supplicant/os_unix.c文件中的os_program_init()函数).

否则会有如下的错误:

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服务会修改wpa_supplicant.conf这个文件. 如果你系统中的/system目录是只读的, 那么使用/data/misc/wifi/wpa_supplicant.conf这个目录作为wpa_supplicant.conf的存放目录, 并且你要修改init.rc里面关于wpa_supplicant服务的配置(也就是conf目录要改),

确保路径配置正确:

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.conf使用了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


如果你使用了标准的Android私有socket,那么不要在init.rc里面添加上面的配置,因为它将使得wpa_supplicant失去作用, 因为hardware/libhardware_legacy/wifi/wifi.c会检查/data/system/wpa_supplicant目录是否存在, 如果存在, 它将会传递一个错误的控制socket名字给wpa_ctrl_open().

5. 确认在init.rc里面启动了wpa_supplicant和dhcpcd(可选)

对于wpa_supplicant服务, init.rc的配置取决于你选择的路径:

- Android私有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标准的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驱动所创建的wifi接口名字不是wlan0, 那么你要修改相应的行.

同时你也需要在init.rc里面启动dhcpcd服务:

service dhcpcd /system/bin/dhcpcd wlan0
group system dhcp
disabled
oneshot


在最新姜饼版本后的Android开源项目使用而来"dhcpcd_wlan0"作为服务名.

6. 以模块的形式提供你的驱动或者在kernel里面或kernel支持的环境下编译, 同时修改相应的android代码

首先确认在kernel已经打开了CONFIG_PACKET和CONFIG_NET_RADIO(wireless extensions)两个宏. 这个驱动可以被编译为一个模块(Android默认的方式)或者通过修改代码在kernel里面(如果你希望kernel自动探测它的驱动, 例如USB接口的wifi)编译它(例如下面).

- 作为一个kernel的模块:

在BoardConfig.mk里面定义下面的变量:

1. WIFI_DRIVER_MODULE_PATH :=模块的装载路径

2. WIFI_DRIVER_MODULE_NAME:=驱动所创建的网络接口名, 例如wlan0

3. WIFI_DRIVER_MODULE_ARG:=任何你希望在导入时传递给驱动的参数, 例如nohwcrypt

确认在编译android时拷贝模块到正确的位置.

- 在kernel内编译(开机自动加载驱动)

- 首先, 修改init.rc, 让它通知"hardware/libhardware_legacy/wifi/wifi.c", 让它知道接口的名字以及驱动已经加载, 并且把wpa_supplicant服务运行起来:

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


千万不要添加属性"setprop init.svc.wpa_supplicant"为"running", 因为我之前提过它将会阻止wpa_supplicant服务被init程序启动.

-其次, 修改"hardware/libhardware_legacy/wifi/wifi.c"的insmod()和rmmod()两个函数,让它们返回0(简单的在第一行添加"return 0;", 因为在kernel里面编译wifi驱动它们是不需要的), 并且要让它们在"check_driver_loaded()"里面检查"/proc/modules"之前返回.

也许你的WifiHW模块会遇到不能正确的连上wpa_supplicant socket的问题,即使你提供了正确的授权密码. 那么试试从图形界面开关一下wifi.

7. 提供一个fireware如果你的模块需要它

如果你的驱动需要一个firmware, 那么android编译的过程中,你必须要拷贝这个firmware文件到/etc/firmware目录下. Android没有使用标准的热拔插固件(尽管也在android-x86也有一个可用的实现代码system/code/toolbox/hotplug.c), 它使用了一个替代方法, 在init程序启动时会检查固件事件,并且从/etc/firmware目录下导入固件文件.(可以查看system/core/init/devices.c里面的handle_firmware_event()函数)

8. 用android定制的wpa_supplicant命令和SIOCSIWPRIV ioctl控制方法来让你的驱动工作

Android使用SIOCSIWPRIV ioctl来发送命令修改驱动的行为, 并且接受一些信息,比如信号强度, AP的mac地址, 连接速度等. 这个ioctl一般没有在已知的wifi驱动里面实现,但是有个例外,bcm4329,它在google的高通kernel分支里面有.

如果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


在四个WEXT_NUMBER_SEQUENTIAL_ERRORS错误后, android将会放弃使用这个设备.

为了从命令接口尽快测试你的wifi, 你可以通过设置"ret = 0"来去掉在"external/wpa_supplicant/driver_wext.c"里面对错误的检查; 在wpa_drSIOCSIWPRIV ioctl调用后的iver_priv_driver_cmd()函数里面. 这个将使所有在android UI上显示的AP没有信号和MAC地址. 为了适配这个ioctl命令,你需要修改kernel, 最重要的是, 在里面添加RSSI(信号强度)和MAC地址,反馈给SIOCSIWPRIV
ioctl控制接口.

一个更好的方法是添加一个特殊的驱动到google的external/wpa_supplicant/目录下, 例如driver_xxx.c, 实现函数wpa_driver_priv_driver_cmd(), 它可以携带 RSSI, MACADDR等信息, 在driver_wext.c里面其他的函数中, 我们通过ioctls的SIOCGIWSTATS,SIOCGIFHWADDR命令得到这些信息.

下面是我为"formini-box.com picoPC Android"的wpa_supplicant服务编译提供的一个patch(http://www.mini-box.com/pico-SAM9G45-X). 它创建了一个新的驱动"awext", 它可以使用"wireless extensions ioctls"命令来"枚举"android wifi驱动命令.

怎样使用这个新驱动:

1. 在你的BoardConfig.mk里面定义:

BOARD_WPA_SUPPLICANT_DRIVER := AWEXT

2. 修改init.rc里面wpa_supplicant服务的命令行参数:

用-Dawext替换-Dwext

AWEXT驱动patch下载地址:
http://www.linuxconsulting.ro/android/patches/android_wext_emulation_driver_awext.patch
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: