Android如果发现wifi没有正常启动,从下面两个方面
1.是否正常编译出wifi ko文件,如果没有,说明编译的有问题,ko文件的地址vendor/lib/module/devices/wifi
2.如果有编译出ko文件,但还提示Wifi HAL start failed之类的,先使用insmod手动加载ko文件,查看wifi是否能正常启动,如果可以,说明ko文件没有成功加载,如下方法查找原因
Android启动的时候会去加载wifi模块,这时候这部分代码在frameworks\opt\net\wifi\libwifi_hal\,这部分的代码主要的功能为,大概内容为通过读取uevnt的获取到系统下的wifi模块的vip和pid,从而从代码的列表中查找到是那个一个wifi模块,然后又从列表中获取wifi模块的驱动代码在那里,从而调用insmod xxxx.ko加载wifi模块。
frameworks\opt\net\wifi\libwifi_hal\rk_wifi_ctrl.cpp
static wifi_device supported_wifi_devices[] = {
{"RTL8188EU", "0bda:8179"},
{"RTL8188EU", "0bda:0179"},
{"RTL8723BU", "0bda:b720"},
{"RTL8723BS", "024c:b723"},
{"RTL8822BS", "024c:b822"},
{"RTL8723CS", "024c:b703"},
{"RTL8723DS", "024c:d723"},
{"RTL8188FU", "0bda:f179"},
{"RTL8822BU", "0bda:b82c"},
{"RTL8189ES", "024c:8179"},
{"RTL8189FS", "024c:f179"},
{"RTL8192DU", "0bda:8194"},
{"RTL8812AU", "0bda:8812"},
{"SSV6051", "3030:3030"},
{"ESP8089", "6666:1111"},
{"AP6354", "02d0:4354"},
{"AP6330", "02d0:4330"},
{"AP6356S", "02d0:4356"},
{"AP6335", "02d0:4335"},
{"AP6255", "02d0:a9bf"},
{"RTL8822BE", "10ec:b822"},
{"MVL88W8977", "02df:9145"},
};
frameworks\opt\net\wifi\libwifi_hal\wifi_hal_common.cpp
//wifi模块的驱动ko,文件的路径地址"/vendor/lib/modules/"
#define WIFI_MODULE_PATH "/vendor/lib/modules/"
wifi_ko_file_name module_list[] =
{
{"RTL8723BU", RTL8723BU_DRIVER_MODULE_NAME, RTL8723BU_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8188EU", RTL8188EU_DRIVER_MODULE_NAME, RTL8188EU_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8192DU", RTL8192DU_DRIVER_MODULE_NAME, RTL8192DU_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8822BU", RTL8822BU_DRIVER_MODULE_NAME, RTL8822BU_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8822BS", RTL8822BS_DRIVER_MODULE_NAME, RTL8822BS_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8188FU", RTL8188FU_DRIVER_MODULE_NAME, RTL8188FU_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8189ES", RTL8189ES_DRIVER_MODULE_NAME, RTL8189ES_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8723BS", RTL8723BS_DRIVER_MODULE_NAME, RTL8723BS_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8723CS", RTL8723CS_DRIVER_MODULE_NAME, RTL8723CS_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8723DS", RTL8723DS_DRIVER_MODULE_NAME, RTL8723DS_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8812AU", RTL8812AU_DRIVER_MODULE_NAME, RTL8812AU_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8189FS", RTL8189FS_DRIVER_MODULE_NAME, RTL8189FS_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"RTL8822BE", RTL8822BE_DRIVER_MODULE_NAME, RTL8822BE_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"SSV6051", SSV6051_DRIVER_MODULE_NAME, SSV6051_DRIVER_MODULE_PATH, SSV6051_DRIVER_MODULE_ARG},
{"ESP8089", ESP8089_DRIVER_MODULE_NAME, ESP8089_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"AP6335", BCM_DRIVER_MODULE_NAME, BCM_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"AP6330", BCM_DRIVER_MODULE_NAME, BCM_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"AP6354", BCM_DRIVER_MODULE_NAME, BCM_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"AP6356S", BCM_DRIVER_MODULE_NAME, BCM_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"AP6255", BCM_DRIVER_MODULE_NAME, BCM_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"APXXX", BCM_DRIVER_MODULE_NAME, BCM_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"MVL88W8977", MVL_DRIVER_MODULE_NAME, MVL_DRIVER_MODULE_PATH, MVL88W8977_DRIVER_MODULE_ARG},
{"RK912", RK912_DRIVER_MODULE_NAME, RK912_DRIVER_MODULE_PATH, UNKKOWN_DRIVER_MODULE_ARG},
{"UNKNOW", DRIVER_MODULE_NAME_UNKNOW, DRIVER_MODULE_PATH_UNKNOW, UNKKOWN_DRIVER_MODULE_ARG}
};
hardware\interfaces\wifi\1.2\default\service.cpp
//加载 WifiModeController然后会调用到WifiModeController::WifiModeController()
android::sp service =
new android::hardware::wifi::V1_2::implementation::Wifi(
std::make_shared(),
std::make_shared(),
std::make_shared());
hardware\interfaces\wifi\1.2\default\wifi_mode_controller.cpp
WifiModeController::WifiModeController() : driver_tool_(new DriverTool) {}
bool WifiModeController::initialize() {
if (!driver_tool_->LoadDriver()) { //调用到下面的 LoadDriver
LOG(ERROR) << "Failed to load WiFi driver";
return false;
}
return true;
}
加载wifi驱动代码调用路径
frameworks\opt\net\wifi\libwifi_hal\driver_tool.cpp
bool DriverTool::LoadDriver()
return ::wifi_load_driver()
wifi_load_driver() //frameworks\opt\net\wifi\libwifi_hal\wifi_hal_common.cpp
check_wifi_chip_type_string //frameworks\opt\net\wifi\libwifi_hal\wifi_hal_common.cpp
if (!strcmp(wifi_type , module_list[i].wifi_name)) {
wifi_ko_path = module_list[i].wifi_module_path; //从module_list中获取到wifi驱动,xxxx.ko的路径,在这里是"/vendor/lib/modules/xxxx.ko"
wifi_ko_arg = module_list[i].wifi_module_arg;
}
save_wifi_chip_type(wifi_type); //保存我们加载wifi的一些信息,这些信息用来给wpa_supplicant加载对于的wifi启动参数。
insmod(wifi_ko_path, wifi_ko_arg); //加载ko模块,和我们手动命令insmod xxxx.ko效果一样的
//此功能为了从supported_wifi_devices这个列表里面获取当前SDIO上面挂载的是那个模块
int check_wifi_chip_type_string(char *type)
{
if (identify_sucess == -1) {
if (get_wifi_device_id(SDIO_DIR, PREFIX_SDIO) == 0)
PLOG(DEBUG) << "SDIO WIFI identify sucess";
else if (get_wifi_device_id(USB_DIR, PREFIX_USB) == 0)
PLOG(DEBUG) << "USB WIFI identify sucess";
else if (get_wifi_device_id(PCIE_DIR, PREFIX_PCIE) == 0)
PLOG(DEBUG) << "PCIE WIFI identify sucess";
else {
PLOG(DEBUG) << "maybe there is no usb wifi or sdio or pcie wifi,set default wifi module Brocom APXXX";
strcpy(recoginze_wifi_chip, "APXXX");
identify_sucess = 1 ;
}
}
strcpy(type, recoginze_wifi_chip); //复制supported_wifi_devices里面的wifi_name的值
PLOG(ERROR) << "check_wifi_chip_type_string : " << type;
return 0;
}
wifi启动驱动加载fw流程
现在的wifi模块启动的时候都要加载一个fw固件,这个固件是做什么用的呢?可能是厂商用来做一下差异配置把,或者是加载射频参数进行使用,大家可以理解为加载一个bin文件到wifi模组中,每次wiif模组上电的时候都需要加载一次,加载的操作在wifi驱动的加载的时候,不同的厂商加载的bin文件不一样,下面我们可以通过系统启动的时候打印的log可以看到如下打印,下面的例子是博通的wifi模组的打印,Realtek不一样
Final fw_path=/vendor/etc/firmware/fw_bcm43438a1.bin
[ 64.276445] Final nv_path=/vendor/etc/firmware/nvram_ap6212a.txt
[ 64.276669] Final clm_path=/vendor/etc/firmware/clm.blob
[ 64.276896] Final conf_path=/vendor/etc/firmware/config.txt
[ 64.279224] dhd_os_open_image: /vendor/etc/firmware/fw_bcm43438a1.bin (414665 bytes) open success
[ 64.365150] dhd_os_open_image: /vendor/etc/firmware/nvram_ap6212a.txt (1003 bytes) open success
[ 64.365249] NVRAM version: AP6212A_NVRAM_V1.0.1_20160606
grep "Final fw_path=" -nr
通过查找Final fw_path=查找出在那里打印出的log,从而找到。
kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c:8341: printf("Final fw_path=%s\n", bus->fw_path);
所以可以知道在驱动里面会加载fw固件,但是这个固件的地址是在那里传进来的需要进一步追踪bus->fw_path这个值。
经过追踪发现在rockchip这个平台,固件的路径在内核里面写死了,代码在kernel\drivers\net\wireless\rockchip_wlan\rkwifi\rk_wifi_config.c里面定义了一个函数int rkwifi_set_firmware(char *fw, char *nvram),这个函数在博通的wifi驱动里面被调用,从而获取wifi固件的烧录地址,kernel\drivers\net\wireless\rockchip_wlan\rkwifi\bcmdhd\dhd_linux.c的bool dhd_update_fw_nv_path(dhd_info_t *dhdinfo)中被调用。
对于博通的wifi和蓝牙二和一芯片来说一般需要加载3个固件分别对应给wifi和蓝牙使用的:
对于wifi部分有fw_bcm43438a1.bin和nvram_ap6212a.txt
对于蓝牙部分的固件有
bcm43438a1.hcd
对于Realtek的wifi,wifi部分不需要加载固件,但是蓝牙需要加载固件,固件的地址在kernel\drivers\bluetooth\rtk_btusb.c里面定义
static patch_info fw_patch_table[] = {
/* { vid, pid, lmp_sub_default, lmp_sub, everion, mp_fw_name, fw_name, config_name, fw_cache, fw_len, mac_offset } */
{ 0x0BDA, 0x1724, 0x1200, 0, 0, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0 ,CONFIG_MAC_OFFSET_GEN_1_2, MAX_PATCH_SIZE_24K}, /* RTL8723A */
{ 0x0BDA, 0x8723, 0x1200, 0, 0, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0 ,CONFIG_MAC_OFFSET_GEN_1_2, MAX_PATCH_SIZE_24K}, /* 8723AE */
{ 0x0BDA, 0xA723, 0x1200, 0, 0, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0 ,CONFIG_MAC_OFFSET_GEN_1_2, MAX_PATCH_SIZE_24K}, /* 8723AE for LI */
{ 0x0BDA, 0x0723, 0x1200, 0, 0, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0 ,CONFIG_MAC_OFFSET_GEN_1_2, MAX_PATCH_SIZE_24K}, /* 8723AE */
{ 0x13D3, 0x3394, 0x1200, 0, 0, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0 ,CONFIG_MAC_OFFSET_GEN_1_2, MAX_PATCH_SIZE_24K}, /* 8723AE for Azurewave*/
蓝牙加载驱动的时候内核层会调用request_firmware(&cfg, config_name, &udev->dev);来下载固件。
request_firmware使用方法可以参考:https://blog.csdn.net/magod/article/details/6049558。
当内核调用request_firmware这个函数的时候,在应用层就会在这个目录/sys/class/firmware生成一个节点,这个节点由三个属性分为为:
loading
这个属性应当被加载固件的用户空间进程设置为 1. 当加载进程完成, 它应当设为 0. 写一个值 -1 到 loading 会中止固件加载进程.
data
data 是一个二进制的接收固件数据自身的属性. 在设置 loading 后, 用户空间进程应当写固件到这个属性.
device
这个属性是一个符号连接到 /sys/devices 下面的被关联入口项.
所有由此推测,需要下载蓝牙固件的时候,应用层一定会往data这个属性写入固件的数据。
android hal 加载wpa_supplicant流程
我们前面的《android hal 加载wifi ko模块流程》中已经说明了,系统启动后会获取到 wifi 芯片 vid pid 加载相应的wifi ko驱动,然后在加载ko驱动的同时保存wifi芯片的一些信息,比如我们加载的wifi模块是博通Broadcom的就保存好博通Broadcom wifi的名字等信息,如果是加载的模块是瑞昱(Realtek)的就保存瑞昱(Realtek)wifi名字的信息。之后系统会启动之后系统会读取init.connectivity.rc 这个脚本,这个脚本会启动wpa_supplicant这个服务,这个应用服务会常驻在后台,启动wpa_supplicant的时候需要指定wifi模块的参数,参数保存在“/vendor/etc/wifi/wpa_config.txt”,源码目录放在/device/rockchip/common/wpa_config.txt。
librkwifi-ctrl.so从那里来?
在\frameworks\opt\net\wifi\libwifi_hal\Android.bp
cc_library_shared { //编译成可执行文件,cc_library_shared编译成动态库
name: "librkwifi-ctrl", //生成librkwifi-ctrl.so库
vendor: true, //编译出来放在/vendor目录下(默认是放在/system目录下)
cflags: wifi_hal_cflags,
local_include_dirs: ["include"], //用户值定的头文件查找路径
shared_libs: ["libbase"], //编译依赖的动态库
header_libs: ["libcutils_headers"],
srcs: ["rk_wifi_ctrl.cpp"], //源文件,格式["a.cpp", "b.cpp"]
}