WiFi 移植记录及心得

通过前面替换了bcmdhd驱动源码,编译后得到了想要的结果,但仅是在Linux驱动部分OK了,要想在Android设置中开启WiFi,还需要对Android源码进行修改和设置。在开始没有AP6181供应商提供资料时,只能根据网上的帖子,一步步执行,但结果总是差强人意,最后参考了WiFi模块供应商提供的移植参照,问题引刃而解,虽然开始的修改和官方文档差不多,但就是这一点点差异导致了Android设置里的WiFi一直跑不起来。下面就官方文档和我个人的理解做一个记录,方便自己以后参考


1、修改 device/fsl/sabresd_6dq/BoardConfig.mk ,添加下面部分

#for broadcom ap6181 vendor
# Wifi
BOARD_WLAN_VENDOR            := BROADCOM
#for broadcom vendor
ifeq ($(BOARD_WLAN_VENDOR),BROADCOM)

WPA_SUPPLICANT_VERSION           := VER_0_8_X
HOSTAPD_VERSION              := VER_0_8_X
BOARD_WPA_SUPPLICANT_DRIVER          := NL80211
BOARD_HOSTAPD_DRIVER                 := NL80211
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_bcmdhd
BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_bcmdhd
BOARD_WLAN_DEVICE := bcmdhd
WIFI_DRIVER_FW_PATH_PARAM  := "/sys/module/bcmdhd/parameters/firmware_path"
WIFI_DRIVER_FW_PATH_STA  := "/system/etc/firmware/fw_bcm40181a2.bin"
WIFI_DRIVER_FW_PATH_P2P  := "/system/etc/firmware/fw_bcm40181a2_p2p.bin"
WIFI_DRIVER_FW_PATH_AP  := "/system/etc/firmware/fw_bcm40181a2_apsta.bin"
WIFI_DRIVER_MODULE_NAME := "bcmdhd"
WIFI_DRIVER_MODULE_PATH  := "/system/lib/modules/bcmdhd.ko"
WIFI_DRIVER_MODULE_ARG      := "iface_name=wlan0 firmware_path=/system/etc/firmware/fw_bcm40181a2.bin nvram_path=/system/etc/firmware/nvram.txt"
TARGET_KERNEL_MODULES := \
                               kernel_imx/drivers/net/wireless/bcmdhd/bcmdhd.ko:system/lib/modules/bcmdhd.ko

include hardware/broadcom/Android.mk
include hardware/broadcom/wlan/bcmdhd/config/config-bcm.mk
endif

2、修改 hardware/broadcom/wlan/bcmdhd/config/config-bcm.mk 

########################
PRODUCT_COPY_FILES += \
    hardware/broadcom/wlan/bcmdhd/config/wpa_supplicant_overlay.conf:system/etc/wifi/wpa_supplicant_overlay.conf \
    hardware/broadcom/wlan/bcmdhd/config/p2p_supplicant_overlay.conf:system/etc/wifi/p2p_supplicant_overlay.conf
########################

3、修改 external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.conf

#####wpa_supplicant configuration file template #####
update_config=1
ctrl_interface=wlan0
eapol_version=1
ap_scan=1
fast_reauth=1

4、修改 hardware/broadcom/wlan/bcmdhd/config/wpa_supplicant_overlay.conf

disable_scan_offload=1
p2p_disabled=1

5、修改 hardware/broadcom/wlan/bcmdhd/config/p2p_supplicant_overlay.conf

disable_scan_offload=1

6、修改 device/fsl/sabresd_6dq/init.rc

on post-fs-data
    mkdir /data/misc/wifi 0770 wifi wifi
    mkdir /data/misc/wifi/sockets 0770 wifi wifi
    mkdir /data/misc/dhcp 0770 dhcp dhcp

on boot
########## 3.0.x <= kernel version <= 3.4.x ##########
service wpa_supplicant /system/bin/wpa_supplicant \
    -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \
    -I/system/etc/wifi/wpa_supplicant_overlay.conf \
    -O/data/misc/wifi/sockets \
    -e/data/misc/wifi/entropy.bin -g@android:wpa_wlan0
    #   we will start as root and wpa_supplicant will switch to user wifi
    #   after setting up the capabilities required for WEXT
    #   user wifi
    #   group wifi inet keystore
    class main
    socket wpa_wlan0 dgram 660 wifi wifi
    disabled
    oneshot
    
service p2p_supplicant /system/bin/wpa_supplicant \ 
	-iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \ 
	-I/system/etc/wifi/wpa_supplicant_overlay.conf -N \
    -ip2p0 -Dnl80211 -c/data/misc/wifi/p2p_supplicant.conf \
    -I/system/etc/wifi/p2p_supplicant_overlay.conf \
    -O/data/misc/wifi/sockets -puse_p2p_group_interface=1 \
    -e/data/misc/wifi/entropy.bin -g@android:wpa_wlan0
#   we will start as root and wpa_supplicant will switch to user wifi
#   after setting up the capabilities required for WEXT
#   user wifi
#   group wifi inet keystore
    class main
    socket wpa_wlan0 dgram 660 wifi wifi
    disabled
    oneshot
    
service dhcpcd_wlan0 /system/bin/dhcpcd -ABKL
    class main
    disabled
    oneshot

service dhcpcd_p2p /system/bin/dhcpcd -aABKL
    class main
    disabled
    oneshot

service iprenew_wlan0 /system/bin/dhcpcd -n
    class main
    disabled
    oneshot

service iprenew_p2p /system/bin/dhcpcd -n
    class main
    disabled
    oneshot




因为Android连接WiFi需要用到wpa_supplicant这个工具,并且Android源码中自带了这个,但不知道什么原因,这个工具一直编译不过,所以只能想办法把这个工具整个目录替换掉,编译下OK了,在Android设置界面中打开WiFi,串口信息可以看到WiFi驱动已经挂载成功了 ,但屏幕一直显示失败。但是一度怀疑是wpa这个工具的问题,因为毕竟用的不是Android源码自带的那个,在网上通过查找资料,发现了一种可以用命令通过wpa_supplicant 连接WiFi,详细步骤见早先博文。

通过命令测试,通过wpa_supplicant 是可以扫描连接WiFi的,并且可以ping通网关,说明wpa_supplicant 这个工具是OK了

既然工具OK  驱动OK ,那就是Android配置问题了,在参考迟来的官方移植文档后,WiFi设置界面终于可以看到扫描的WiFi热点了,并且能成功的连接到WiFi,这是个激动人心的消息------------历史性的进步,也说明了及时资料可以省下不少时间。


在WiFi连接OK后,不久就发现了一个问题,就是在Android设置界面中第一次打开WiFi是OK的,没任何问题,然而关闭后再打开,WiFi就挂了,查看串口,发现是因为WiFi驱动没有释放总线,也就是在WiFi设置中关闭WiFi后,驱动并没有真正卸载,所以导致第二次开启WiFi时无法挂载驱动。发现问题后就一直想着如何能真正卸载WiFi驱动,咨询了模块厂家,他们给出的结论是 需要把平台sdio detect和remove函数有分别加到dhd_gpio.c的bcm_wlan_power_on和bcm_wlan_power_off 中,但由于水平有限,一直找不到现在使用的平台中关于SDIO 的detect 和remove函数。

后来看到以前Android移植WiFi的帖子中,有人在wifi.c中的 wifi_load_driver 和  wifi_unload_driver 直接 return 0; 当时移植时并不知道这是什么意思,所以没太在意,因为没有去阅读源码。 后来仔细一看,原来如此:

int wifi_load_driver()
{
#ifdef WIFI_DRIVER_MODULE_PATH
    char driver_status[PROPERTY_VALUE_MAX];
    int count = 100; /* wait at most 20 seconds for completion */

    if (is_wifi_driver_loaded()) {
        return 0;
    }

    if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
        return -1;

    if (strcmp(FIRMWARE_LOADER,"") == 0) {
        /* usleep(WIFI_DRIVER_LOADER_DELAY); */
        property_set(DRIVER_PROP_NAME, "ok");
    }
    else {
        property_set("ctl.start", FIRMWARE_LOADER);
    }
    sched_yield();
    while (count-- > 0) {
        if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
            if (strcmp(driver_status, "ok") == 0)
                return 0;
            else if (strcmp(DRIVER_PROP_NAME, "failed") == 0) {
                wifi_unload_driver();
                return -1;
            }
        }
        usleep(200000);
    }
    property_set(DRIVER_PROP_NAME, "timeout");
    wifi_unload_driver();
    return -1;
#else
    property_set(DRIVER_PROP_NAME, "ok");
    return 0;
#endif
}


int wifi_unload_driver()
{
    usleep(200000); /* allow to finish interface down */
#ifdef WIFI_DRIVER_MODULE_PATH
    if (rmmod(DRIVER_MODULE_NAME) == 0) {
        int count = 20; /* wait at most 10 seconds for completion */
        while (count-- > 0) {
            if (!is_wifi_driver_loaded())
                break;
            usleep(500000);
        }
        usleep(500000); /* allow card removal */
        if (count) {
            return 0;
        }
        return -1;
    } else
        return -1;
#else
    property_set(DRIVER_PROP_NAME, "unloaded");
    return 0;
#endif
}
这两个函数就是在WiFi设置中打开和关闭时调用的挂载和卸载驱动的函数,仔细一想为什么非得卸载掉WiFi驱动呢?其实可以在开机时就将驱动挂载好,就像编译进内核一样,在WiFi打开和关闭时,并不真正的去执行挂载和卸载驱动的动作,这也就是为什么有的WiFi移植文档中将这两个函数直接return 0的原因了,

所以解决办法是:

int wifi_load_driver()
{
//#ifdef WIFI_DRIVER_MODULE_PATH
#if 0
    char driver_status[PROPERTY_VALUE_MAX];
    int count = 100; /* wait at most 20 seconds for completion */

    if (is_wifi_driver_loaded()) {
        return 0;
    }

    if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
        return -1;

    if (strcmp(FIRMWARE_LOADER,"") == 0) {
        /* usleep(WIFI_DRIVER_LOADER_DELAY); */
        property_set(DRIVER_PROP_NAME, "ok");
    }
    else {
        property_set("ctl.start", FIRMWARE_LOADER);
    }
    sched_yield();
    while (count-- > 0) {
        if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
            if (strcmp(driver_status, "ok") == 0)
                return 0;
            else if (strcmp(DRIVER_PROP_NAME, "failed") == 0) {
                wifi_unload_driver();
                return -1;
            }
        }
        usleep(200000);
    }
    property_set(DRIVER_PROP_NAME, "timeout");
    wifi_unload_driver();
    return -1;
#else
    property_set(DRIVER_PROP_NAME, "ok");
    return 0;
#endif
}


int wifi_unload_driver()
{
    usleep(200000); /* allow to finish interface down */
//#ifdef WIFI_DRIVER_MODULE_PATH
#if 0
    if (rmmod(DRIVER_MODULE_NAME) == 0) {
        int count = 20; /* wait at most 10 seconds for completion */
        while (count-- > 0) {
            if (!is_wifi_driver_loaded())
                break;
            usleep(500000);
        }
        usleep(500000); /* allow card removal */
        if (count) {
            return 0;
        }
        return -1;
    } else
        return -1;
#else
    property_set(DRIVER_PROP_NAME, "unloaded");
    return 0;
#endif
}
也可以将WIFI_DRIVER_MODULE_PATH 定义屏蔽掉 或是 网上其他帖子一样直接return 0; 反正就是不执行挂载和卸载的动作就OK了


总结:

在做整个项目下来,因为是第一次接触到Android,所以很多东西都是云里雾里的,在Linux驱动部分每次都能很快的发现和定位到问题,虽然工作量不是特别大,但也是一个接触Android的一个开始吧,从一开始什么都不会,到慢慢了解Android源码结构,之后自己会修改一些小东西,例如开机logo、上下状态栏,之后到WiFi移植,虽然WiFi这部分花费了不少时间,但至少算是一个好的开始吧。在WiFi移植这段时间,深刻意识到资料的及时性真的可以节约不少时间,如果一开始就能拿到供应商的移植文档,应该一周甚至更短时间内就能解决,要少走很多弯路。当然WiFi这块,花费了这么长的时间也还是有所收获的 ,至少将WiFi驱动的源码弄明白是怎么回事了,也清楚的记得了Android中WiFi部分的源码结构和组成。

再一个就是通过这次WiFi的教训,明白了思考问题的角度应该多面化,不要只局限一个角度,就像这次WiFi驱动卸载的问题,一开始认定的想法就是如何去卸载WiFi这个模块,尽阅读了很多资料,也试过很多方法,但最终还是没能成功。当时也没去想为什么要卸载这个呢,事后一想这WiFi驱动在开机时就挂载上去之后就不在卸载一直到关机,其实这样对系统没任何负担和影响。也算是给自己一个教训吧 ,遇到问题从多方面去思考,甚至是背向思考,才能更快更好的定位和解决问题,bingo!

你可能感兴趣的:(Linux)