wifi 启动流程
系统根据以下流程开启wifi
1、载入wifi kernel module。
在wifi kernel module 中,主要是透过ioctl 的方式与user space 下运行的wpa_supplicant 进行沟通。另外在硬体的实现上,由于CPU 需要有另一个介面与wifi chip 进行沟通,如wifi firmware 的载入、command request 以及资料的传送/接收。目前较为常用的界面为4bit SDIO (Secure Digital I/O)。
2、启动wpa_supplicant。
运行于user space 下的一个service,可以在init.rc 下启动并初始化这个service。当然在启动以及关闭wifi 时也会一起启动以及关闭。
3、创建interface。
建立wpa_supplicant 以及wifi kernel module 的interface 作为实现wifi 的各种操作如找寻适当AP (Access Point)、secure method (WPA、WPA2 或WEP) 或建立/解除与AP 的连线。
问题描述
在user 通过UI setting去启动wifi 时,wifi 找不到任何AP 且画面一直停留在scanning…的状态此问题有可能是AP 信号太弱或是security method 不对。但除了这些之外,本文想引出另一个思考的方向— 软体启动流程不对引发的问题。
问题分析
重新检视上述启动流程,可以发现wifi kernel module 是在kernel space 进行载入而wpa_supplicant 是在user space 下启动的。如果要正确地控制其启动流程,较常见的方法是程式中透过cat /proc/modules。如果可以在/proc/modules 中读取到 module name,代表wifi kernel module 已经载入完成。但显然在笔者的开发经验上,这样的方法上并不能适用于所有的平台。在这样的情况下,如果在user space 中因为对wifi kernel module 确实的载入完成时间有任何的误判而提前启动wpa_supplicant,将会使得wpa_supplicant 找不到wifi device 而会有如以下的error logs 出现
I/wpa_supplicant( 677): rfkill: Cannot open RFKILL control device
E/wpa_supplicant( 677): Could not read interface wlan0 flags: No such device
E/wpa_supplicant( 677): WEXT: Could not set interface 'wlan0' UP
E/wpa_supplicant( 677): wlan0: Failed to initialize driver interface
问题分析方法
- 可以在adb shell 中透过手动方式运行启动流程。这可以帮助分析以及厘清问题。假如可以利用手动方式启动,则可以确认在软体中可能是因为kernel module 载入时间过长而发生问题。
a). 可利用以下command load/unload/list module
$ insmod /system/lib/modules/wlan.ko (Insert module)
$ rmmod wlan.ko (Remove module)
$ lsmod (list module)
b). 可利用以下command start/stop wpa_supplicant
$ start wpa_supplicant
$ stop wpa_supplicant
解决方法
- 在kernel space 里增加flag 来检查kernel driver 是否已经载入完成。并且透过set property 的方式记录在prop file 中, 这种方式可以确保user space 启动wpa_supplicant 的时间。
- 如果你无法从kernel space 去完成上述动作,那可能只能加一些 Timeout 或是delay 来解决这个问题。