因需要将公司驱动程序移植到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中剩余的代码是一些状态控制的操作。

由上述代码,可知移植驱动要修改的几处为:

  1. #define XXX_DRIVER_MODULE_NAME “testxxx”。(libwifi_hal/wifi_hal_common.cpp)
  2. #define XXX_DRIVER_MODULE_PATH WIFI_MODULE_PATH"testxxx.ko" //填写你驱动将要存放到安卓文件系统中的位置(一般是系统默认位置WIFI_MODULE_PATH),除了目录写全,还需写出驱动最终make出的ko文件全名,即也是MODULE_NAME的值。(libwifi_hal/wifi_hal_common.cpp)
  3. 在module_list中添加一项,参数可添UNKNOWN_DRIVER_MODULE_ARG。(libwifi_hal/wifi_hal_common.cpp)
  4. 在supported_list中添加一项。(libwifi_hal/rk_wifi_ctrl.cpp)
  5. 此外还需注意驱动中某些变量关于不同安卓版本的支持变化,可以借助#if LINUX_VERSION_CODE>=KERNEL_VERSION(x.x.x)来兼容不同版本的支持差异
  6. 此外,可能还需替换掉工程中的/device/rockchip/common/wpa_config.txt,里面有一些特有的配置信息
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐