1 Android平臺的Wifi模塊移植要點1.1 Wifi結(jié)構(gòu)user interface Android WiFiService WPA_Supplicant DHD Driver Dongle Binary BCM43xxHardware 藍色:需要修改 黑色:不需要修改 藍色部分是為了在android環(huán)境下,支持bcm43xx芯片需要修改的部分,各個部分的功能如下: 1. User interface(用戶接口層):控制和設(shè)置wifi的用戶接口層 2. Android wifi service:它是android系統(tǒng)中的wifi服務(wù)部分,主要用來啟動和停止wpa_supplicant,并作為用戶接口層和wpa_supplicant交互的橋梁存在 3. Wpa_supplicant:支持wpa和wapi的外部庫 4. Dhd driver:wifi模塊的驅(qū)動 5. Dongle binary:BCM43xx的固件 6. BCM43xx:wifi的硬件芯片(是一個組合集成芯片) 1.2 Wifi模塊環(huán)境Wifi模塊環(huán)境包括以下部分: 1. bcm43xx驅(qū)動源碼 2. wpa_supplicant和支持WAPI的wapilib庫 3. nvram.txt(BCM43xx芯片配置文件) 4. wpa_supplicant.conf和wifi固件 一般來說,第三和第四條件的文件在BCM43xx源碼中都有包含。 1.3 Wifi模塊的編譯1.3.1 Wifi驅(qū)動源碼這里不以某個類型的BCM43xx芯片和android版本作為特例來講解wifi模塊的編譯過程,只是對編譯的通用部分做簡要的說明。 一般來說,wifi模塊的編譯可采用兩種紅方式:一是內(nèi)部編譯,二是模塊編譯。通常使用的是模塊編譯,這里也以模塊編譯wifi模塊為例來說明wifi模塊的編譯過程。 在wifi模塊驅(qū)動源碼中,主要包含以下幾個目錄: Firmware:里面會提供對應(yīng)BCM43xx芯片的固件 Config:提供wpa_supplicant.conf和nvram.txt以及dhcpcd.conf,還有android.mk文件。如果沒有上面的配置文件,需要找到放入該目錄。 Android.mk:這是一個文件,也是編譯android平臺編譯wifi模塊的入口 Src:wifi驅(qū)動的源碼目錄 其實,你看到的broadcom提供的驅(qū)動源碼可能的組織方式跟上面是有差別的,但大致的內(nèi)容差不多,上面只是針對模塊源碼中目錄和文件的不同用途來說明的。 1.3.2 在android平臺添加BCM43xx驅(qū)動要在android平臺編譯wifi模塊,首先要將wifi模塊的源碼添加到android平臺下的目錄中。至于添加到什么地方也不是固定的,一般會添加到vendor目錄下的某個目錄下,下面我們把BCM43xx模塊源碼放在vendor/xxxx/wlan/bcm43xx下(XXXXX代表產(chǎn)品名稱)。要知道,在添加源碼之前,這個bcm43xx目錄是不存在的,需要手動創(chuàng)建,并在該目錄下創(chuàng)建android和src兩個目錄: PC$ cd<ANDROID_ROOT> PC$ mkdir ‐p vendor/xxxx/wlan/bcm4325/ PC$ cdvendor/xxxx/wlan/bcm4325/ PC$ mkdirandroid src PC$ ls android src 在創(chuàng)建好上面的目錄后,就可以在目錄下添加BCM43xx的相關(guān)源碼了,src存放的是驅(qū)動源碼,android目錄下主要存放固件(即二級制鏡像),編譯文件(Android.mk),配置文件(nvram.txt和wpa_supplicant.conf)等。 添加好相關(guān)源碼后,還需要做一定的修改工作(主要對編譯文件的修改),不然模塊無法被正常編譯的,Android.mk的內(nèi)容修改操作如下。 PC$ cp<WORK>/bcm4325_source/open‐src/src/dhd/android/config/Android.mk <ANDROID_ROOT>/vendor/xxxx/wlan/bcm4325/android PC$ cd<ANDROID_ROOT>/vendor/xxxx/wlan/bcm4325/android PC$ viAndroid.mk //到這里,找到Android.mk文件,文件內(nèi)容如下 …… # # Install WLAN Driver, firmware, and configurationfiles. # local_target_dir :=$(TARGET_OUT_ETC)/firmware //這里定義local_target_dir的路徑,在后面會用到 ######################## LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE :=sdio-g-cdc-reclaim-idsup-wme-ccx-wapi.bin LOCAL_MODULE_TAGS := user development LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(local_target_dir) LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) ######################## include $(CLEAR_VARS) LOCAL_MODULE := nvram_4325b0.txt LOCAL_MODULE_TAGS := user development LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(local_target_dir) LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) ######################## include $(CLEAR_VARS) LOCAL_MODULE := dhd.ko LOCAL_MODULE_TAGS := user development LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH :=$(TARGET_OUT)/lib/modules LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) ######################## include $(CLEAR_VARS) LOCAL_MODULE := wpa_supplicant.conf LOCAL_MODULE_TAGS := user development LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/wifi LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) ######################## include $(CLEAR_VARS) LOCAL_MODULE := dhcpcd.conf LOCAL_MODULE_TAGS := user development LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH :=$(TARGET_OUT_ETC)/dhcpcd LOCAL_SRC_FILES := android_dhcpcd.conf include $(BUILD_PREBUILT) ######################## 對上面的內(nèi)容有必要說明一下:LOCAL_MODULE和LOCAL_MODULE_PATH的定義是這里需要修改的地方。對于LOCAL_MODULE_PATH的修改時一定的,但它主要依賴與android平臺已經(jīng)定義好的一些變量,如:TARGET_OUT _ETC和TARGET_OUT等。LOCAL_MODULE定義的是要操作的對象,也是前面提到的配置文件,編譯好的驅(qū)動模塊二進制文件和固件等。 該文件的作用:將各個文件拷貝到定義的地方,以供驅(qū)動模塊和wpa_supplicant等使用。、 對于驅(qū)動的源碼,用戶可以通過定制的wifi芯片管理函數(shù)來關(guān)閉和開啟wifi模塊,在dhd/sys/dhd_custom_gpio.c文件中有如下的函數(shù)定義: #define CUSTOMER_HW #ifdef CUSTOMER_HW extern void bcm_wlan_power_off(int); BCM43xx BCM43xx Porting Guide for Android System Broadcom Corporation Proprietary and Confidential 9 extern void bcm_wlan_power_on(int); #endif /* CUSTOMER_HW */ 所以用戶可以使用自己定制的開關(guān)函數(shù),或者使用BCM43xx自帶的開關(guān)函數(shù)來控制wifi模塊的開關(guān)。如果使用定制的開關(guān)函數(shù),則需要在平臺相關(guān)的代碼中定義這兩個函數(shù): Path:kernel/arch/arm/mach‐XXX/board‐XXXX.c …… void bcm_wlan_power_off(int onoff); void bcm_wlan_power_on(int onoff); …… 前面的Android.mk的修改中,有對dhd.ko的拷貝操作,這就是說源碼編譯后生成的模塊二進制文件應(yīng)該被暫時拷貝到Android.mk中指示的地方。這個暫時拷貝操作在驅(qū)動源碼的編譯文件dhd/linux/Makefile中被執(zhí)行。 PC$ cd<ANDROID_ROOT>/vendor/xxxx/wlan/bcm4325/src PC$ vi dhd/linux/Makefile +315 modules: $(OFILES) test -r ./Makefile || ln –s $(SRCBASE)/dhd/linux/makefile.26./Makefile $(MAKE) -C $(LINUXDIR) M=$(shell pwd) $(if $(VERBOSE),V=1)modules cp $(KMODULES) $(SRCBASE)/../android/. 下一步需要做的就是將wifi模塊源碼的頂層目錄下的android.mk文件放到bcm43xx目錄下,它是android編譯入口文件,表示在編譯android時,這個wifi驅(qū)動模塊被包含到整個android編譯系統(tǒng)中。它的內(nèi)容如下: PC$ cd <ANDROID_ROOT>/vendor/xxxx/wlan/bcm4325/ PC$ vim Android.mk ifeq ($(BOARD_WLAN_DEVICE),bcm43xx) include $(callall-subdir-makefiles) endif 很簡單,如果被BOARD_WLAN_DEVICE被定義為bcm43xx,調(diào)用call all-subdir-makefiles函數(shù)來包含所有子目錄下的Android.mk文件,這是android編譯系統(tǒng)的規(guī)則。 那么,最后一步就是在相關(guān)的平臺配置文件中定義BOARD_WLAN_DEVICE為bcm43xx,文件路徑:vendor/XXXX/XXXX/BoardConfig.mk。 1.3.3 編譯wifi驅(qū)動源碼在編譯wifi驅(qū)動源碼之前,首先要建立kernel環(huán)境,即要在編譯wifi模塊之前編譯kernel,因為模塊的編譯時依賴與內(nèi)核的。 內(nèi)核的編譯如下: PC$ cd <ANDROID_ROOT>/kernel PC$ make ARCH=arm CROSS_COMPILE=<ANDROID_ROOT>/prebuilt/ linuxx86/toolchain/arm‐eabi‐4.4.0/bin/arm‐eabi‐ XXXX_defconfig PC$ make ARCH=armCROSS_COMPILE=<ANDROID_ROOT>/prebuilt/ linuxx86/toolchain/arm‐eabi‐4.4.0/bin/arm‐eabi‐ zImage 如果設(shè)置好了ARCH和CROSS_COMPILE的環(huán)境變量,就可以省略這兩個選項的定義,直接使用make命令生成zImage了。 在編譯好kernel之后,就可以進入bcm43xx源碼目錄dhd/linux下,這里面有兩個文件Makefile和Makefile.26,如果沒有Makefile文件就將Makfeile.26拷貝成Makfile文件(實際上是建立了一個符號鏈接文件),當(dāng)然也要做前面的dhd.ko暫時拷貝命令的添加了。在該目錄下輸入如下命令編譯模塊源碼: PC$ cd<ANDROID_ROOT>/vendor/xxxx/wlan/bcm4325/src PC$ cd dhd/linux/ PC$ make ARCH=armLINUXDIR=<ANDROID_ROOT>/kernel CROSS_COMPILE=<ANDROID_ROOT>/prebuilt/linux‐x86/toolchain/arm‐eabi‐ 4.4.0/bin/arm‐eabi‐ dhd‐cdc‐sdmmc‐gpl 最后一條命令增加了一個命令選項LINUXDIR的傳值,即告訴該編譯文件kernel的所在位置,進入kernel的Makefile文件做設(shè)置工作,然后返回該目錄下的Makefile文件進行編譯工作。 到這里bcm43xx驅(qū)動源碼已經(jīng)編譯完成,可以到android目錄下查看有沒有dhd.ko文件生成,如果有,即表明編譯成功,否則查看編譯過程的出錯信息,解決錯誤問題再編譯,直至編譯成功。 1.3.4 在android中使用BCM43xx前面的過程只是在android系統(tǒng)中添加編譯了BCM43xx驅(qū)動源碼,但是要想在android中使用它,還需要費一番功夫。需要修改的地方在開始的地方已經(jīng)用藍色背景標(biāo)注了,在這里我們要看看到底要修改哪些文件。 1.3.4.1 hardware/libhardware_legacy/wifi/wifi.c為BCM43xx驅(qū)動模塊做適當(dāng)修改。Wifi.c作為加載wifi驅(qū)動模塊和啟動關(guān)閉wpa_supplicant的重要角色而存在,為了使wifi能更好的工作,該文件中的一些變量參數(shù)必須被適當(dāng)?shù)脑O(shè)置(根據(jù)wpa_supplicant.conf)。 PC$ vi hardware/libhardware_legacy/wifi/wifi.c ...... #define WIFI_DRIVER_MODULE_PATH "/system/lib/modules/dhd.ko" ...... #define WIFI_DRIVER_MODULE_NAME"dhd" ...... #define WIFI_DRIVER_MODULE_ARG "firmware_path=/etc/firmware/sdio-g-cdc-reclaim-idsup-wme-ccxwapi. bin nvram_path=/etc/firmware/nvram_4325b0.txt" ...... #define WIFI_TEST_INTERFACE "eth0" ...... static const char IFACE_DIR[] = ""; ...... static const char SUPP_CONFIG_TEMPLATE[]= "/data/misc/wifi/wpa_supplicant.conf"; …… static const char SUPP_CONFIG_FILE[] = "/etc/wifi/wpa_supplicant.conf"; ......
上面的紅色部分,就是對wifi驅(qū)動的特殊化定義,不同的wifi驅(qū)動會有不同的定義。修改還包括了固件的和配置文件的路徑,以及接口名的相關(guān)定義。 WIFI_TEST_INTERFACE定義的是wpa_supplicant和UI的交互接口名,當(dāng)通過property_get()函數(shù)獲取wifi接口名錯誤時,就會使用此定義的接口名。Property_get()函數(shù)獲取的接口名,就是init.xx.rc文件中的接口定義的wifi.interface: Setprop wifi.interface “eth0” SUPP_CONFIG_TEMPLATE定義的是wpa_supplicant配置文件的臨時文件。而SUPP_CONFIG_FILE定義的是wpa_supplicant運行時使用的配置文件,并且對wifi配置的修改也會保持到這個文件里。 IFACE_DIR是wpa_supplicant控制接口的目錄,該目錄下的接口被UI用來連接到wpa_supplicant,這個目錄是由wpa_supplicant決定的,其實,這個接口實際上是一個socket,在wpa_supplicant啟動時被創(chuàng)建,wpa_supplicant_ctrl_iface_init() 中有兩種方式來創(chuàng)建這個socket接口: (1)android系統(tǒng)的socket 它使用wpa_%ctrl_interface%組合來定義自己的名字,通過socketwpa_eth0 dgram ...命令來生成的(在init.rc中),如果使用這種socket,下面的代碼中的接口名必須是一直的: ctrl_interface=eth0 (In wpa_supplicant.conf) "socket wpa_eth0 ..." (In init.*.rc ) setprop wifi.interface "eth0" (In init.*.rc) wpa_supplicant ‐ieth0 ... (In init.*.rc) 此時,IFACE_DIR的定義為NULL。 (2)特殊socket Wpa_supplicant會通過wpa_supplicant.conf中的ctrl_interface目錄來創(chuàng)建控制接口socket,而這個值一般被定義為:"ctrl_interface=DIR=/data/misc/....",這個socket的名字由wpa_supplicant的“-i”參數(shù)傳遞,如果使用這種方式的socket,下面的變量定義必須一致: ctrl_interface=DIR=/data/misc/ (In wpa_supplicant.conf) IFACE_DIR=/data/misc/ (In wifi.c)
1.3.4.2 kernel/arch/arm/mach‐XXX/board‐XXXX.c為BCM43xx 驅(qū)動添加電源管理部分內(nèi)容,這里不詳細敘述。 1.3.4.3 system/core/rootdir/etc/init.xxx.rc為支持BCM43xx驅(qū)動模塊,修改wpa_supplicant服務(wù)。Init.xxx.rc文件的內(nèi)容一般如下所示: PC$ vi system/core/rootdir/etc/init.xxx.rc ...... //添加android系統(tǒng)接口 # to enable wifi setprop wifi.interface "eth0" # end of wifi ...... //修改wpa_supplicant.conf的權(quán)限 chmod 0660 /etc/wifi/wpa_supplicant.conf chown wifi wifi /etc/wifi/wpa_supplicant.conf ...... //wpa_supplicant服務(wù)的啟動命令 service wpa_supplicant /system/bin/logwrapper /system/bin/wpa_supplicant -Dwext -ieth0 - c/etc/wifi/wpa_supplicant.conf ...... //給wpa_supplicant修改socket接口 socket wpa_eth0 dgram 660 wifi wifi ...... //修改dhcp接口 service dhcpcd /system/bin/dhcpcd -BKL eth0 ...... 1.3.4.4 vendor/XXXX/XXXX/BoardConfig.mk為BCM43xx驅(qū)動添加配置變量。 PC$ vi vendor/XXXX/XXXX/BoardConfig.mk …… ifneq ($(BUILD_TINY_ANDROID), true) …… BOARD_WPA_SUPPLICANT_DRIVER := WEXT BOARD_WLAN_DEVICE := bcm43xx endif # !BUILD_TINY_ANDROID …… 對BCM43xx的WAPI的支持也需要做一系列的修改,這里不再對其進行詳細的敘述了。 2 總結(jié)最近一段時間里對wifi模塊的學(xué)習(xí),讓我對wifi有了更多的了解,但這些也是不夠的。對于wifi模塊,需要了解的東西很多,這里只對其工作原理和工作流程以及移植過程進行了簡單的闡述,還有很多方面沒有概括到,如:協(xié)議層的實現(xiàn)方式和過程,以及數(shù)據(jù)傳輸中的細節(jié)和SDIO相關(guān)內(nèi)容等等,這些在實際的wifi模塊工作時可能都需要我們?nèi)ミM一步的分析和把握,雖然對wifi的理解可能不到位,但對這段時間學(xué)習(xí)做一次總結(jié),也是大有裨益的,希望有機會可以更深入的學(xué)習(xí)wifi模塊。 在wifi模塊調(diào)試的過程中也會出現(xiàn)一系列的問題,這些問題可以歸納成不同的類型,對這些類型的問題做分析和總結(jié),可以幫助我們在以后調(diào)試工作中快速定位問題所在。 對wifi的了解還遠遠不夠,希望和wifi感興趣的和已經(jīng)成為高手的同仁能有更多的交流。 |
|