本文主要是研究分析Android平台Wifi框架及HAL层需要做的相关开发工作,目前只做了wifi扫描、关联流程的基本分析,没有包括p2p和HostAP相关部分。
HAL 是Hardware Abstraction Layer的首字母缩写,意思是硬件抽象层。
1. Windows 的HAL:位于操作系统的最底层,直接操作物理硬件,隔离与硬件相关的信息,为上层的操作系统和设备驱动程序提供一个统一的接口,起到对硬件的抽象作用。
2. 标准Linux 的HAL:位于操作系统和驱动程序之上,是一个运行在用户空间的服务程序,提供对所有设备和设备属性的管理功能,并与用户层应用程序进行交互。 Linux HAL框架如下:
Linux HAL主要基于udev和D-BUS实现,通过sysfs,uevent 及 /etc/hal/fdi 等获取管理硬件信息。
Linux HAL的描述参见: http://www.freedesktop.org/wiki/Software/hal
3. Android中的HAL
Android 的 HAL(Hardware Abstract Layer硬件抽象层)是Google因应厂商「希望不公开源码」的要求下,所推出的观念。Android HAL是由众多用户空间的动态链接模块库组成。Android HAL框架如下:
这是 Patrick Brady (Google) 在2008 Google I/O 所发表的演讲「Anatomy & Physiology of an Android」中所提出的 Android HAL 架构图。Android Framwork通过HAL层来对Linux Kernel 及Drivers进行访问,HAL层的实现可以作为动态库或模块加载,即实现了对硬件抽象访问功能,又可达到隐藏实现代码目的。
1. Android HAL的过去与现在
libhardware_legacy/ - 过去的实现、采取链接库模块的观念进行
libhardware/ - 新版的实现、调整为HAL stub的观念
ril/ - Radio Interface Layer
2. 旧版实现-HAL_legacy动态链接模块库 (libhardware_legacy)
Android用户应用程序或框架层代码由Java实现,Java运行在Dalvik虚拟机中,没有办法直接访问底层硬件,只能通过JNI调用来调用so本地库代码实现,在so本地库代码里运行对底层硬件操作代码,如下图所示
应用层或框架层Java代码,通过JNI调用libandroid_runtime代码来调用libhardware_legacy.so库代码,在so库代码中调用底层驱动,实现上层应用的提出的硬件操作请求。调用流程示例如下:
3. 新版实现- Hardware Module Stub (libhardware)
新的架构使用的是module stub方式。Stub是存根或桩的意思,就是指一个硬件模块对象的代表的意思。由上面的架构可知,上层应用层或框架层代码加载runtime so库代码,这些runtime so库代码我们称之为module,在HAL层注册了每个硬件对象的存根stub,当上层需要访问硬件的时候,就从当前注册的硬件对象stub里查找,找到之后stub会向上层module提供该硬件对象的operations interface(操作接口),该操作接口就保存在了module中,上层应用或框架再通过这个module操作接口来访问硬件。
以Led为例的调用流程:
4. HAL_legacy 和 HAL Stub的对比
HAL_legacy:旧版的HAL,是一个采用共享库形式的模块,libhardware_legacy.so。
由于采用直接函数调用形式调用,上层直接将so库映射进进程空间,因此可被多个进程使用,但会被mapping到多个进程空间中,造成内存空间浪费,存在设备可能会被多次打开的问题,同时需要考虑代码能否安全重入问题。
HAL Stub:新版的HAL采用HAL module和HAL stub结合形式,需要加载的是module runtime库
运行时上层只加载module库,通过HAL module提供的统一接口获取并操作HAL stub,底层Stub扮演了“接口提供者”的角色,当Stub第一次被使用时加载到内存,后续再使用时仅返回硬件对象操作接口,不会存在设备多次打开问题,并且由于Stub so文件只会被mapping到一个进程,多进程访问时返回的只是函数指针,也不存在重复mapping和重入问题。
Android Wifi 框架:
(插图1)
1. Wifi启动流程
开关流程:WifiSetting创建WifiEnabler; WifiEnabler响应开关按钮的onCheckedChanged()方法
在onCheckedChanged( )方法里调用WifiManager.setWifiEnabled()
WifiManager调用WifiSevice.setWifiEnabled();WifiService调用 WifiStateMachine.setWifiEnabled(); WifiStateMachine向自己发消息:
(插图2)
2. WifiStateMachine机制
Wifi状态机机制,共27个状态,采用栈的形式,子状态在栈顶,父状态依次入栈;消息自顶向下依次处理,每一个消息处理完成后会反转所有延时消息的顺序。
初始化为InitialState, 启动即判断驱动是否加载,根据结果进行状态跳转
如果驱动已加载则跳转到 DriverLoadedState,否则跳转到 DriverUnloadedState
以下仅分析开启过程正确结果处理流程,下列状态图约定:
标记为颜色的绿色模块为调用本地库方法,标记为红色部分为下发WIFI命令
标记为MSG_xxx发送消息, 标记为虚线状态机状态跳转
最左侧为状态机栈, 中间为消息队列,右侧为处理函数及消息传递
(插图3)
(接上图)
(插图4)
以上图示为启动与扫描流程。
以下为关联流程:
(插图5)
3. Android Wifi HAL层分析
Wifi HAL层采用的是libhardware_legacy的实现,代码主要在 hardware/libhardware_legacy/wifi/wifi.c; 提供的主要功能接口:
插入驱动 wifi_load_driver
移除驱动 wifi_unload_driver
启动wpa_supplicant服务 wifi_start_supplicant
wifi_start_p2psupplicant
停止wpa_supplicant服务 wifi_stop_supplicant
连接wpa_supplicant wifi_connect_to_supplicant
传送wifi命令 wifi_command
读取消息 wifi_wait_for_event
读取固件路径 wifi_get_fw_path
修改固件路径 wifi_change_fw_path
Android Wifi上层Java代码通过JNI调用将上层wifi命令传递到HAL层处理。
所有的native方法都定义在Android源码WifiNative.java:
android/frameworks/base/wifi/java/android/net/wifi/WifiNative.java;
这些native方法的实现在android_net_wifi_Wifi.cpp:
android/frameworks/base/core/jni/android_net_wifi_Wifi.cpp
1. DRIVER CMD, 与驱动相关命令
由上层下发往wifi驱动的私有命令以 “DRIVER ”开头,
在此涉及有16个函数,24个命令:
setCountryCodeCommand "DRIVER COUNTRY"
doSetScanMode "DRIVER SCAN-ACTIVE" /
"DRIVER SCAN-PASSIVE"
startDriverCommand "DRIVER START"
stopDriverCommand "DRIVER STOP"
startMultiV4Filtering "DRIVER RXFILTER-STOP" /
"DRIVER RXFILTER-REMOVE 2" /
"DRIVER RXFILTER-START"
stopMultiV4Filtering "DRIVER RXFILTER-ADD 2" /
"DRIVER RXFILTER-START"
startMultiV6Filtering "DRIVER RXFILTER-STOP" /
"DRIVER RXFILTER-REMOVE 3" /
"DRIVER RXFILTER-START"
stopMultiV6Filtering "DRIVER RXFILTER-ADD 3" /
"DRIVER RXFILTER-START"
getMacAddressCommand "DRIVER MACADDR"
setPowerModeCommand "DRIVER POWERMODE %d"
getPowerModeCommand "DRIVER GETPOWER"
setBandCommand "DRIVER SETBAND"
getBandCommand "DRIVER GETBAND"
setBluetoothCoexistenceModeCommand "DRIVER BTCOEXMODE"
setBluetoothCoexistenceScanModeCommand "DRIVER BTCOEXSCAN-START" /
"DRIVER BTCOEXSCAN-STOP"
setSuspendOptimizationsCommand "DRIVER SETSUSPENDOPT %d"
2. Wifi Command通过socket发给wpa_supplicant, 被wpa接收。
在wpa_supplicant文件 android/external/wpa_supplicant_8_bcm/wpa_supplicant/ctrl_iface.c中:
函数wpa_supplicant_ctrl_iface_process()中处理:
if (os_strncmp(buf, “DRIVER ”, 7) == 0)
reply_len= wpa_supplicant_driver_cmd(wpa_s, buf+7, reply, reply_size);
3. wpa_supplicant_driver_cmd函数在 wpa_supplicant/driver_i.h 中定义:
if (!wpa_s->driver->driver_cmd)
return -1;
return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len)
4. driver_cmd定义在 /work/android/external/wpa_supplicant_8_bcm/src/drivers/driver_nl80211.c
第6846行:
#ifdef ANDROID
.driver_cmd = wpa_driver_nl80211_driver_cmd,
#endi
5. wpa_driver_nl80211_driver_cmd的实现在文件:
android/hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/driver_cmd_nl80211.c
#ifdef ANDROID
int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, size_t buf_len);
#endif
从以上分析得知,支持android需要实现wpa_driver_nl80211_driver_cmd() 函数内容。
参考方案的实现:
即定义 BOARD_WPA_SUPPLICANT_PRIVATE_LIB 并实现它。
友商参考:
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_bcmdhd
BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_bcmdhd
lib_driver_cmd_bcmdhd在 hardware/Broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/实现。
1. Android配置修改
修改android/device/hisi/k3v2oem1/BoardConfig.mk
(待改进)
2. wpa_supplicant配置
从上面的配置,我们可以看出,wpa_supplicant采用的是 VER_0_8_X_HISI版本
把我们要用的wpa_supplicant文件夹重命名为hisi_wpa_supplicant并拷贝到external/
修改external/hisi_wpa_supplicant/Android.mk 为:
## ligang add for hi1101
ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_8_X_HISI)
include $(call all-subdir-makefiles)
endif
wpa_supplicant的编译路径在:
external/hisi_wpa_supplicant/wpa_supplicant
编辑其路径下的Android.mk:
ifdef CONFIG_DRIVER_NL80211
#L_CFLAGS += -DANDROID_BRCM_P2P_PATCH
endif
3. 开发wpa_supplicant_private_lib
在hardware下面建路径 hisi/wpa_supplicant_8_lib
并在其路径下实现wpa_supplicant_private_lib内容,即函数wpa_driver_nl80211_driver_cmd.
这个私有库是作为静态库编译并链接到wpa_supplicant里面的
注:也可以把相关文件直接放在wpa_supplicant/src/drivers目录下并修改相关配置。
4. 配置config
在hardware/hisi下面建文件夹config
并把wpa_supplicant的配置文件加入编译
注:配置文件也可以直接在wpa_supplicant的编译配置里添加。
5. 驱动修改
修改驱动以支持ioctl
6. 测试
7. 后续改进