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

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 := WEXTBoardConfig.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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值