This guide provides a step by step explanation of what is involved in adding a new WiFi driver and making a new WiFi card work well in a custom Android build. (This guide was written for Android 4.1 but should be applicable to previous Android releases and hopefully future releases.)
- Understand how Android WiFi works
- Port WiFi driver.
- Compile a proper wpa_supplicant in your BoardConfig.mk
- Modify your wifi.c in HAL.
- Launch wpa_supplicant and dhcpcd services in init.rc.
- Several debug tips.
Understand How Android WiFi Works
As the following figure, Android wireless architecture can be divided into three parts:
Java Framework(WifiManager, WifiMonitor etc..),
kernel space modules(wireless stack, wifi drivers)
Java Framework communicate with wpa_supplicant using native interface (wifi.c). Wpa_supplicant and netd uses wireless extension or nl80211 to control WiFi drivers.
Port WiFi driver
Usually WiFi driver is provided as a kernel module. There are mainly two types of Android WiFi architecture:nl80211 and wext. With the implementation of nl80211/cfg80211 many wireless drivers in main line kernel support nl80211 interface instead of wireless extension.
For different vendors’ WiFi drivers, writing one Android.mk to add its compile into Android is what you should do. Here take atheros’s AR6kl as an example:
$(ATH_ANDROID_SRC_BASE)/$(ath6kl_module_file):$(mod_cleanup) $(TARGET_PREBUILT_KERNEL) $(ACP)
$(MAKE) -C $(ATH_ANDROID_SRC_BASE) O=$(ATH_LINUXPATH) ARCH=arm CROSS_COMPILE=$(ARM_EABI_TOOLCHAIN)/arm-eabi- KLIB=$(ATH_\
$(ACP) -fpt $(ATH_ANDROID_SRC_BASE)/compat/compat.ko $(TARGET_OUT)/lib/modules/
$(ACP) -fpt $(ATH_ANDROID_SRC_BASE)/net/wireless/cfg80211.ko $(TARGET_OUT)/lib/modules/
LOCAL_MODULE := ath6kl_sdio.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules
LOCAL_SRC_FILES := $(ath6kl_module_file)
Compile a proper wpa_supplicant in your BoardConfig.mk
In Android’s external directory, there are two wpa_supplicant_* projects. For wext-based wifi driver, wpa_supplicant_6 can be used. For nl80211-based WiFi driver, wpa_supplicnat_8 can only be used. But if WiFi vendors supply their own customized wpa_supplicant, it will be much easier to debug the communication between wpa_supplicant and WiFi drivers. No matter which supplicant you choose, just control their compile in your BoardConfig.mk.
Take atheros’s ath6kl as an example:
BOARD_WLAN_DEVICE := ar6003
BOARD_HAS_ATH_WLAN := true
WPA_SUPPLICANT_VERSION := VER_0_8_ATHEROS
WIFI_DRIVER_MODULE_PATH := "/system/lib/modules/ath6kl_sdio.ko"
WIFI_DRIVER_MODULE_NAME := "ath6kl_sdio"
WIFI_DRIVER_MODULE_ARG := "suspend_mode=3 wow_mode=2 ar6k_clock=26000000 ath6kl_p2p=1"
WIFI_DRIVER_P2P_MODULE_ARG := "suspend_mode=3 wow_mode=2 ar6k_clock=26000000 ath6kl_p2p=1 debug_mask=0x2413"
WIFI_SDIO_IF_DRIVER_MODULE_PATH := "/system/lib/modules/cfg80211.ko"
WIFI_SDIO_IF_DRIVER_MODULE_NAME := "cfg80211"
WIFI_SDIO_IF_DRIVER_MODULE_ARG := ""
WIFI_COMPAT_MODULE_PATH := "/system/lib/modules/compat.ko"
WIFI_COMPAT_MODULE_NAME := "compat"
WIFI_COMPAT_MODULE_ARG := ""
then you need to provide a proper wpa_supplicant.conf for your device. wpa_supplicant.conf is very important because the control socket for android is specified in this file(ctrl_interface=). This file should be copied to /system/etc/wifi.
Minimum required config options in wpa_supplicant.conf :
There are two different ways in which wpa_supplicant can be configured, one is to use a "private" socket in android namespace, created by socket_local_client_connect() function in wpa_ctrl.c and another is by using a standard UNIX socket.
Android private socket
- Unix standard socket
Modify your wifi.c in HAL
Here what you should do is modifying some codes like wifi_load_driver and wifi_unload_driver.
For Broadcom or CSR’s wifi driver, you can directly use the original wifi.c. But for atheros’s ath6kl driver, there are total three .ko modules to install. So some micro variables and codes need to be changed to adapt it.
Launch wpa_supplicant and dhcpcd services in init.rc
If you have configured to use android private socket, you should do like this:
service wpa_supplicant /system/bin/wpa_supplicant -Dwext -iwlan0 -c / data/misc/wifi /wpa_supplicant.conf
socket wpa_wlan0 dgram 660 wifi wifi
or if you have configured to use unix standard socket, you should do like this:
service wpa_supplicant /system/bin/wpa_supplicant -Dwext -iwlan0 -c/data/misc/wifi/wpa_supplicant.conf
If WiFi driver is not “wext” but “nl80211”, you should change it to –Dnl80211.
For dhcpcd, you should lunch it like the following:
service dhcpcd_wlan0 /system/bin/dhcpcd -ABKL
The parameters “-ABKL” can largely enhance wifi connection speed. About what “ABKL” stand for, you can refer to dhcpcd’s GNU manual.
Several debug tips
- Incorrect permissions will result in wpa_supplicant not being able to create/open the control socket andlibhardware_legacy/wifi/wifi.c won't connect.
Since Google modified wpa_supplicant to run as wifi user/group the directory structure and file ownership should belong to wifi user/group (see os_program_init() function in wpa_supplicant/os_unix.c ).
Otherwise errors like:
E/WifiHW ( ): Unable to open connection to supplicant on "/data/system/wpa_supplicant/wlan0": No such file or directory will appear.
Also wpa_supplicant.conf should belong to wifi user/group because wpa_supplicant will want to modify this file.
- How to Enable debug for wpa_supplicant.
By default wpa_supplicant is set to MSG_INFO that doesn't tell much.
To enable more messages:
modify common.c and set wpa_debug_level = MSG_DEBUG
modify common.h and change #define wpa_printf from if ((level) >= MSG_INFO) to if ((level) >= MSG_DEBUG)
3. WiFi driver’s softmac.
For most vendors’ WiFi driver, the mac address is fixed. We should add one softmac rule to let WiFi driver’s mac is unique for each board.