android移植wifi驱动流程porting
android载入wifi驱动流程因需要将公司驱动程序移植到android 10平台,故而学习了android源码关于wifi driver 载入相关的函数流程。主要涉及到的代码位于~/android-10/frameworks/opt/net/wifi下。接下来以wifi_load_driver为起点开始分析函数调用流程。wifi_load_driverint wifi_load_driver(
因需要将公司驱动程序移植到android 10平台,故而学习了android源码关于wifi driver 载入相关的函数流程。主要涉及到的代码位于~/android-10/frameworks/opt/net/wifi
下。
接下来以wifi_load_driver为起点开始分析函数调用流程。
wifi_load_driver
位置:~/android-10/frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp
int wifi_load_driver() {
#ifdef WIFI_DRIVER_MODULE_PATH
char* wifi_ko_path = NULL ;
char* wifi_ko_arg =NULL;
int i = 0;
int count = 100;
if (is_wifi_driver_loaded()) {//判断是否已经加载了WiFi驱动
return 0;
}
if (wifi_type[0] == 0) {//wifi_type是全局变量char数组,初始化为0
check_wifi_chip_type_string(wifi_type);
save_wifi_chip_type(wifi_type);
}
/*module_list相关定义如下:
typedef struct _wifi_ko_file_name
{
char wifi_name[64];
char wifi_driver_name[64];
char wifi_module_path[128];
char wifi_module_arg[128];
}wifi_ko_file_name;
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},
............
};
*/
for (i=0; i< (int)(sizeof(module_list) / sizeof(module_list[0])); i++) {
if (!strcmp(wifi_type , module_list[i].wifi_name)) {
wifi_ko_path = module_list[i].wifi_module_path;
wifi_ko_arg = module_list[i].wifi_module_arg;
PLOG(ERROR) << "matched ko file path " << wifi_ko_path;
break;
}
}
if (wifi_ko_path == NULL) {
PLOG(ERROR) << "falied to find wifi driver for type=" << wifi_type;
return -1;
}
if (strstr(wifi_ko_path, MVL_DRIVER_MODULE_NAME)) {
insmod(MLAN_DRIVER_MODULE_PATH, "");
}
if (insmod(wifi_ko_path, wifi_ko_arg) < 0) {
return -1;
}
#endif
#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
if (is_wifi_driver_loaded()) {
return 0;
}
if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0) return -1;
#endif
while (count-- > 0) {
if (is_wifi_driver_loaded()) {
property_set(DRIVER_PROP_NAME, "ok");
return 0;
}
usleep(200000);
}
property_set(DRIVER_PROP_NAME, "timeout");
return -1;
}
其中wifi_type是全局变量,并被初始化为0:
若当前wifi_type未被赋值,则进入if (wifi_type[0] == 0) 里。
先来看check_wifi_chip_type_string。
check_wifi_chip_type_string
位置:~/android-10/frameworks/opt/net/wifi/libwifi_hal/rk_wifi_ctrl.cpp
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 ;
}
}
//无论找到与否,将全局变量recoginze_wifi_chip的值复制到type中,返回给上层
strcpy(type, recoginze_wifi_chip);
PLOG(ERROR) << "check_wifi_chip_type_string : " << type;
return 0;
}
identify_sucess,XXX_DIR和PREFIX_XXX均为全局变量。
static int identify_sucess = -1;//初始化为-1
static const char USB_DIR[] = "/sys/bus/usb/devices";
static const char SDIO_DIR[]= "/sys/bus/sdio/devices";
static const char PCIE_DIR[]= "/sys/bus/pci/devices";
static const char PREFIX_SDIO[] = "SDIO_ID=";
static const char PREFIX_PCIE[] = "PCI_ID=";
static const char PREFIX_USB[] = "PRODUCT=";
如代码所见,系统先去获取sdio的设备ID,获取失败后按顺序是USB再而是PCIE的设备ID。而获取各类设备ID的函数get_wifi_device_id代码见下。
get_wifi_device_id
位置:~/android-10/frameworks/opt/net/wifi/libwifi_hal/rk_wifi_ctrl.cpp
int get_wifi_device_id(const char *bus_dir, const char *prefix)
{
int idnum;
int i = 0;
int ret = invalid_wifi_device_id;//invalid_wifi_device_id是定义的全局变量,值为-1标识此种错误类型
DIR *dir;
struct dirent *next;
FILE *fp = NULL;
idnum = sizeof(supported_wifi_devices) / sizeof(supported_wifi_devices[0]);
//supported_wifi_devices是一个静态wifi_device的数组,列出了所有wifi设备的名字与vid、pid
/*定义格式如下
static wifi_device supported_wifi_devices[] = {
{"RTL8188EU", "0bda:8179"},
{"RTL8188EU", "0bda:0179"},
..........};
*/
/*wifi_device 格式
typedef struct _wifi_devices
{
char wifi_name[64];
char wifi_vid_pid[64];
} wifi_device;
*/
dir = opendir(bus_dir);//不同设备类型传进来的bus_dir目录不同。打开对应设备类型的目录文件
if (!dir) {
PLOG(ERROR) << "open dir failed:" << strerror(errno);
return invalid_wifi_device_id;
}
while ((next = readdir(dir)) != NULL) {//readdir是依次读出目录中的文件,每次只能读一个
char line[256];
char uevent_file[256] = {0};
sprintf(uevent_file, "%s/%s/uevent", bus_dir, next->d_name);//将uevent_file按此格式赋值
PLOG(DEBUG) << "uevent path:" << uevent_file;
fp = fopen(uevent_file, "r");//挨个打开该目录下的所有文件
if (NULL == fp) {
continue;
}
while (fgets(line, sizeof(line), fp)) {//以行为单位读取文件,一行不超过256B的字符可以存储进line
char *pos = NULL;
int product_vid = 0;
int product_did = 0;
int producd_bcddev = 0;
char temp[10] = {0};
pos = strstr(line, prefix);//判断prefix是否为line的字串
PLOG(DEBUG) << "line:" << line << ", prefix:" << prefix << ".";
if (pos != NULL) {//如果line中包含prefix的字符串,则说明找到所需内容
//接下来通过判断bus_dir与XXX_DIR是否相同来判断是哪类设备
//根据不同设备类型的不同格式,读取到对应的参数,存到did、vid中
if (strncmp(bus_dir, USB_DIR, sizeof(USB_DIR)) == 0)
sscanf(pos + 8, "%x/%x/%x", &product_vid, &product_did, &producd_bcddev);
else if (strncmp(bus_dir, SDIO_DIR, sizeof(SDIO_DIR)) == 0)
sscanf(pos + 8, "%x:%x", &product_vid, &product_did);
else if (strncmp(bus_dir, PCIE_DIR, sizeof(PCIE_DIR)) == 0)
sscanf(pos + 7, "%x:%x", &product_vid, &product_did);
else
return invalid_wifi_device_id;
sprintf(temp, "%04x:%04x", product_vid, product_did);//将did、vid值按照格式复制到temp中
PLOG(DEBUG) << "pid:vid :" << temp;
for (i = 0; i < idnum; i++) {//将获取到的记录着did和vid值的temp依次与supported_wifi_devices中的值对比
if (0 == strncmp(temp, supported_wifi_devices[i].wifi_vid_pid, 9)) {//若有匹配项
PLOG(ERROR) << "found device pid:vid :" << temp;
strcpy(recoginze_wifi_chip, supported_wifi_devices[i].wifi_name);//将已找到的匹配项的设备名存储到recoginze_wifi_chip中
/*其中recoginze_wifi_chip是全局变量。
static char recoginze_wifi_chip[64];
*/
identify_sucess = 1 ;//成功匹配的标识
ret = 0;
fclose(fp);//关闭文件
goto ready;
}
}
}
}
fclose(fp);
}
ret = invalid_wifi_device_id;//未找到匹配项说明invalid设备ID
ready:
closedir(dir);//关闭目录
PLOG(DEBUG) << "wifi detectd return ret:" << ret;
return ret;
}
get_wifi_device_id返回后,会将check_wifi_chip_type_string里的type赋值为匹配到设备的名称,再返回上一层函数后,会回到wifi_load_driver里,此时wifi_load_driver内的wifi_type即为全局变量recognize_wifi_chip,也就是匹配设备名称。
之后再来看save_wifi_chip_type。
save_wifi_chip_type
位置:~/android-10/frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp
int save_wifi_chip_type(char *type)
{
int ret;
int fd;
int len;
char buf[64];
ret = access(RECOGNIZE_WIFI_CHIP, R_OK|W_OK);//第一个参数是一个路径,判断该路径是否可读或可写。若均不可返回-1,否则返回0
/*全局变量定义:
static const char RECOGNIZE_WIFI_CHIP[] = "/data/misc/wifi/wifi_chip";
*/
if ((ret == 0) || (errno == EACCES)) {//errno 是记录系统的最后一次错误代码,EACCES表明Permission denied
if ((ret != 0) && (chmod(RECOGNIZE_WIFI_CHIP, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) != 0)) {//尝试修改目录权限
PLOG(ERROR) << "Cannot set RW to "<< RECOGNIZE_WIFI_CHIP << ":" << strerror(errno);
return -1;
}
PLOG(DEBUG) << RECOGNIZE_WIFI_CHIP << " is exit\n";
return 0;
}
fd = TEMP_FAILURE_RETRY(open(RECOGNIZE_WIFI_CHIP, O_CREAT|O_RDWR, 0664));//TEMP_FAILURE_RETRY用于忽略系统中断造成的错误,打开指定文件
if (fd < 0) {
PLOG(ERROR) << "Cannot create " << RECOGNIZE_WIFI_CHIP << ":" << strerror(errno);
return -1;
}
PLOG(DEBUG) << "is not exit,save wifi chip" << RECOGNIZE_WIFI_CHIP;
strcpy(buf, type);//将存储着设备名称的type复制到buf中
PLOG(DEBUG) << "recognized wifi chip = "<< buf << ", save to" << RECOGNIZE_WIFI_CHIP;
len = strlen(buf)+1;
if (TEMP_FAILURE_RETRY(write(fd, buf, len)) != len) {//向指定文件写入设备名称
PLOG(ERROR) << "Error writing " << RECOGNIZE_WIFI_CHIP << ":" << strerror(errno);
close(fd);
return -1;
}
close(fd);
if (chmod(RECOGNIZE_WIFI_CHIP, 0664) < 0) {
PLOG(ERROR) << "Error changing permissions of" << RECOGNIZE_WIFI_CHIP << "to" << "0664:" << strerror(errno);
unlink(RECOGNIZE_WIFI_CHIP);
return -1;
}
if (chown(RECOGNIZE_WIFI_CHIP, AID_SYSTEM, AID_WIFI) < 0) {
PLOG(ERROR) << "Error changing group ownership of" << RECOGNIZE_WIFI_CHIP << "to" << AID_WIFI << ":" << strerror(errno);
unlink(RECOGNIZE_WIFI_CHIP);
return -1;
}
return 1;
}
此函数返回后,回到wifi_load_driver函数中,此时如果正常流程下,应已经找到了对应的设备并将其信息写入系统指定文件中去了。
接下来接着看wifi_load_driver。此时开始进入for循环,这个循环中,依次遍历module_list,试图找到能与设备名称匹配的项,未找到则报错return-1。找到,则将path和alg复制到对应的wifi_ko_path、wifi_ko_alg中。
第一个if里,是与全局变量MVL_DRIVER_MODULE_NAME相关的判断和操作。
下一个if里,直接insmod在wifi_ko_path上的驱动。
#define MVL_DRIVER_MODULE_NAME "sd8xxx"
#define WIFI_MODULE_PATH "/vendor/lib/modules/"
#define MLAN_DRIVER_MODULE_PATH WIFI_MODULE_PATH"mlan.ko"
到此,载入流程结束了。wifi_load_driver中剩余的代码是一些状态控制的操作。
由上述代码,可知移植驱动要修改的几处为:
- #define XXX_DRIVER_MODULE_NAME “testxxx”。(libwifi_hal/wifi_hal_common.cpp)
- #define XXX_DRIVER_MODULE_PATH WIFI_MODULE_PATH"testxxx.ko" //填写你驱动将要存放到安卓文件系统中的位置(一般是系统默认位置WIFI_MODULE_PATH),除了目录写全,还需写出驱动最终make出的ko文件全名,即也是MODULE_NAME的值。(libwifi_hal/wifi_hal_common.cpp)
- 在module_list中添加一项,参数可添UNKNOWN_DRIVER_MODULE_ARG。(libwifi_hal/wifi_hal_common.cpp)
- 在supported_list中添加一项。(libwifi_hal/rk_wifi_ctrl.cpp)
- 此外还需注意驱动中某些变量关于不同安卓版本的支持变化,可以借助#if LINUX_VERSION_CODE>=KERNEL_VERSION(x.x.x)来兼容不同版本的支持差异
- 此外,可能还需替换掉工程中的/device/rockchip/common/wpa_config.txt,里面有一些特有的配置信息
更多推荐
所有评论(0)