近期需要把
WiFi无线网络功能移植到在iTOP4412 开发平台,查阅了相关资料,经过一段时间的研究、调试
,终于成功,将
WiFi功能移植到了开发板上面,这里笔者记录移植过程及注意事项,方便以后工作需要。
iTOP4412
开发板的WiFi模块与板卡之间的连接采用SDIO接口,WiFi硬件模块使用的是MTK的MT6620芯片,MTK提供了Android4.0及Android4.4的driver, Porting Guid,有了这些就为我们的移植工作做了总体性的指导。
但是仅仅有
MTK提供的文档还是远远不够的,毕竟硬件接口定义不同,kernel版本也不同,Android层与MTK提供的代码也有差异,这就需要我们在MTK文档的指导下, Step by Step 进行 Porting 工作.
移植环境
:
1 iTOP4412
精英版 + MT6620 WiFi模块
2 kernel 3.0.15 version
3 Android4.4.4
4 Ubuntu12.04 64BIt
开发环境
1.2 硬件相关部分
下图为
WiFi模块与开发板连接的引脚定义,通过该接口可以看出WiFi模块与CPU的交互接口.
查看
WiFi模块的原理图可知,WiFi模块与CPU之间采用SDIO接口进行数据和命令的交互工作,Pin1,2,3,5,6,7 为SDIO接口,另外还需要Pin8,Pin10 UART 串口, 另外Pin18,Pin17用于串口流控,实际是可以不使用流控功能。 MT6620芯片是复合芯片,除了具备WiFi功能以后,还支持蓝牙,FM,GPS功能,他们与CPU之间的通信需要串口,另外MT6620固件补丁的下载也是通过串口进行的,所以说串口是必不可少的硬件接口,即使您只使用该芯片的WiFi功能。
除了
SDIO接口,UART口以外,还需要与CPU进行交互的接口包括Pin16 PMU_EN ,使能引脚,默认低电平状态,高电平有效。
Pin15 WiFI_RESET
引脚,默认低电平状态,高电平有效,用于复位WiFi芯片;
Pin 13 WiFi INT WiFI
的中断引脚,用于告知CPU有数据来了;
以上这些引脚需要在
BSP中配置,WiFI的驱动会通过这些引脚与MT6620芯片进行通信;
另外需要强调的是
WiFi模块占用CPU的MMC3端口, 也可以说就是SDIO总线,关于MMC,SD,SDIO总线的来历这里不再描述,下图为核心板WiFi相关部分:
在
iTOP4412 精英版中 mmc0,mmc1总线用于eMMC,mmc2用于TF卡,mmc3用于SDIO WiFi,关于wifi的其他硬件连接比如串口,使能(GPIO),复位(GPIO),中断(GPIO)这里不再贴出,具体的请查看开发板的原理图。
这里重点强调一下 HUB_CONNECT,也就是上图中的
L5 引脚,该引脚复用为mmc总线的CD引脚,也就是Card Detect引脚,用于告知MMC3控制器有设备插入,属于中断引脚,我们需要把引脚与CPU的 某GPIO引脚连接:
以上为
iTOP4412 底板原理图WiFi接口部分,请注意HUB_CONNECT引脚通过0欧电阻与6060_GPIO2进行了连接,6060_GPIO2为核心板
GPIO引脚。
这样连接的目的是通过软件输出
6060_GPIO2低电平,从而HUB_CONNECT引脚为低电平输入状态,MMC3控制认为有设备插入到了MMC3总线上面,原理同TF卡,SD卡的检测。
以上为进行
Porting前的准备工作,当然需要万用表,示波器工具进行辅助的检测,查看WiFi模块的工作电压是否正常,GPIO的当前状态,MMC总线上面的时钟及是否有数据从MMC3控制器输出等等.
1.3 Kernel
1.3.1 概述
iTOP4412
开发板采用的是Linux 3.0.15 版本,MTK官方给的移植Porting没有说明针对具体的 kernel版本,由于是Android4.4,所以kernel应该是3.0以后的版本或者更高支持;
首先按照
PoringGuid的指导说明,把New和Modify文件夹下面关于kernel部分的修改放到我们的kernel代码里面,MT6620的驱动分两个部分, 一部分放在driver/misc/目录下面,文件夹名称 mediatek,
里面存放的是WMT,既wireless manage tools, 里面提供了与MT6620 download firmware patch ,enable /disable WIFi芯片,power on, power off操作的相关驱动部分, 及SDIO总线设备接口驱动Host Interface drivers,这些驱动工作正常后才开始加载WiFi 网络相关驱动。
我们以驱动库
.ko 的形式编译驱动模块,driver/misc/mediatek/ 库文件与WiFi网络库文件 列表如下::
mtk_hif_sdio.ko ----mmc
总线相关接口,mmc总线发现SDIO设备,分配总线地址后,会与该驱动进行适配.适配成功后该驱动会调用WiFi网络驱动;
mtk_stp_uart.ko-----
串口相关驱动,通过串口下载固件补丁,设置芯片参数;
mtk_stp_wmt.ko-----core
部分,提供WiFi上电,断电等等相关操作;
mtk_wmt_wifi.ko ----
字符设备驱动,创建设备节点用于与用户空间交互;
wlan_mt6620.ko -- -- WiFi
网络相关驱动,不需要我们进行修改;
另外这里附一张
MMC驱动框架图:
因为我们的
MT6620模块挂载到了MMC总线上面,属于MMC子系统的工作实例,我们非常有必要熟悉一下mmc驱动架构,是我们移植工作的非常重要的一部分,关于MMC子系统的详细介绍这里不再说明.
1.3.2 代码修改
1
根据硬件连接情况配置必要的平台资源
修改文件
: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
关键函数
1: 该函数配置WiFi相关的GPIO引脚为初始化输出状态,或者配置为中断状态
WIFI
驱动会改变这些引脚的状态,这里仅仅是初始化.
static void __init mtk_combo_init(void)
{
//MT66XX PMUEN
if(gpio_request(EXYNOS4_GPC1(0), "GPC1_0"))
{
printk(KERN_ERR "failed to request GPC1_0 for MT6620 PMUEN control
");
}
//MT66XX SYSRST
if(gpio_request(EXYNOS4_GPC1(1), "GPC1_1"))
{
printk(KERN_ERR "failed to request GPC1_1 for MT6620 SYSRST control
");
}
s3c_gpio_cfgpin(EXYNOS4_GPC1(0), S3C_GPIO_OUTPUT);
s3c_gpio_cfgpin(EXYNOS4_GPC1(1), S3C_GPIO_OUTPUT);
gpio_direction_output(EXYNOS4_GPC1(0), 0);
gpio_direction_output(EXYNOS4_GPC1(1), 0);
gpio_free(EXYNOS4_GPC1(0));
gpio_free(EXYNOS4_GPC1(1));
mdelay(5);
//need config eint models for Wifi & BGA Interupt
if (gpio_request(EXYNOS4_GPX2(5), "WiFi INT"))
printk(KERN_WARNING "MT6620 WiFi INT(GPX2.5) Port request error!!!
");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(5), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(5), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(5));
}
if (gpio_request(EXYNOS4_GPX2(4), "BGF INT"))
printk(KERN_WARNING "MT6620 BGA INT(GPX2.4) Port request error!!!
");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(4), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(4), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(4));
}
//normal it is high level
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2
");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), 1);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
return; }
关键函数
2: setup_mt6620_wlan_power_for_onoff
该函数为导出函数,
WIFi驱动会调用该函数,该函数关键地方是让MMC控制器驱动扫描MMC总线上面的设备,MMC扫描到了WiFI模块才会加载相应的WiFi驱动,这里是主动让MMC扫描,我们的SD卡是采用中断触发的方式扫描,他们本质上都是扫描MMC总线上面的新设备,然后加载对应的设备驱动,具体的可以看一下MMC子系统相关内容.
函数所属文件
: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
void setup_mt6620_wlan_power_for_onoff(int on)
{
int chip_pwd_low_val;
int outValue;
printk("[mt6620] +++ %s : wlan power %s
",__func__, on?"on":"off");
#if 1
if (on) {
outValue = 0;
} else {
outValue = 1;
}
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2
");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), outValue);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
if(on)
{
//need reset on mt6620 ? need test......
}
#endif
extern void sdhci_s3c_sdio_card_detect(struct platform_device *pdev);
// mdelay(200);
//need sdhc controler check wifi catd states......
sdhci_s3c_sdio_card_detect(&s3c_device_hsmmc3);
printk("[mt6620] --- %s
",__func__);
}
EXPORT_SYMBOL(setup_mt6620_wlan_power_for_onoff);
关键结构体
: 该结构体告诉WiFi驱动相关部分使用了平台的哪些GPIO资源.
结构体所属文件
: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
static struct mtk_wmt_platform_data mtk_wmt_pdata = {
.pmu =EXYNOS4_GPC1(0), //RK30SDK_WIFI_GPIO_POWER_N,//RK30_PIN0_PB5, //MUST set to pin num in target system
.rst = EXYNOS4_GPC1(1),//RK30SDK_WIFI_GPIO_RESET_N,//RK30_PIN3_PD0, //MUST set to pin num in target system
.bgf_int=EXYNOS4_GPX2(4), //IRQ_EINT(20),//RK30SDK_WIFI_GPIO_BGF_INT_B,//RK30_PIN0_PA5,//MUST set to pin num in target system if use UART interface.
.urt_cts = -EINVAL, // set it to the correct GPIO num if use common SDIO, otherwise set it to -EINVAL.
.rtc = -EINVAL, //Optipnal. refer to HW design.
.gps_sync = -EINVAL, //Optional. refer to HW design.
.gps_lna = -EINVAL, //Optional. refer to HW design.
};
static struct mtk_sdio_eint_platform_data mtk_sdio_eint_pdata = {
.sdio_eint = EXYNOS4_GPX2(5),//IRQ_EINT(21) ,//RK30SDK_WIFI_GPIO_WIFI_INT_B,//53, //MUST set pin num in target system.
};
static struct platform_device mtk_wmt_dev = {
.name = "mtk_wmt",
.id = 1,
.dev = {
.platform_data = &mtk_wmt_pdata,
},
};
static struct platform_device mtk_sdio_eint_dev = {
.name = "mtk_sdio_eint",
.id = 1,
.dev = {
.platform_data = &mtk_sdio_eint_pdata,
},
};
2 WIFI
驱动导出函数.
文件:
kernel/iTop4412_Kernel_3.0/drivers/misc/mediatek/combo_mt66xx/wmt/platform/vendor/wmt_plat.c
修改函数
: wmt_plat_sdio_ctrl
函数说明
: 该函数会调用我们上面导出的接口,让MMC总线控制器扫描新设备
INT32 wmt_plat_sdio_ctrl (WMT_SDIO_SLOT_NUM sdioPortType, ENUM_FUNC_STATE on)
{
int ret = 0;
extern void setup_mt6620_wlan_power_for_onoff(int on);
if (FUNC_OFF == on) {
/* add control logic here to generate SDIO CARD REMOVAL event to mmc/sd
* controller. SDIO card removal operation and remove success messages
* are expected.
*/
//add by dg 2015-04-14
setup_mt6620_wlan_power_for_onoff(0);
}
else {
/* add control logic here to generate SDIO CARD INSERTION event to mmc/sd
* controller. SDIO card detection operation and detect success messages
* are expected.
*/
//add by dg 2015-04-14
setup_mt6620_wlan_power_for_onoff(1);
}
//extern int omap_mmc_update_mtk_card_status(int state);
//ret = omap_mmc_update_mtk_card_status((FUNC_OFF == on)? 0: 1);
WMT_INFO_FUNC(KERN_INFO "%s, on=%d, ret=%d
", __FUNCTION__, on, ret);
return ret;
}
以上两个文件的修改最为关键,当然您还需要配置
MMC3的相关引脚为MMC工作状态,默认情况下面MMC3相关引脚为复用引脚中的GPIO状态,我们需要配置为MMC总线状态,笔者在调试过程中总是发现MMC总线上面没有命令或者数据输出,后发现默认情况下MMC3相关引脚并没有配置成MMC工作模式,查看Exynos4412 Datasheet后才发现这一问题.修改工作模式后,此问题得到解决.
MTK
官方给的移植文档中会告诉你需要在原始内核代码里面增加哪些文件,如何在make menuconfig中配置相关部分,这里就不再详细描述.
1.4 用户态空间
下面我们描述一下采用
Linux系统和Android系统的用户都需要注意的地方:
驱动层移植完成后,
MTK的Porting Guid会告诉你需要在用户态运行wmt_launcher工具,作为后台的一个服务程序运行,该服务会配置串口的工作参数,下载固件补丁到MT6620中,他的源代码相对比较简单,只有一个.c文件:
原始文件位于
MTK发布包:
APEX_Android_4.4_MP_SW_package_V2.0/APEX_Android_4.4_MP_001_panda_combo_mt66xx_Package_Common/New/hardware/mediatek/wmt/ stp_uart_launcher.c
修改后的文件位于
iTOP4412 Android4.4发布包:
iTop4412_KK4.4/hardware/mediatek/wmt/stp_uart_launcher.c
修改点主要在串口参数配置上,由于内核版本不同,串口设置参数也略有不同
.
具体修改可以使用代码比对工具进行比较。
另外需要说明的是运行
wmt_launcher的运行参数 跟MTK给的移植文档有点不同,Porting Guid 里面推荐串口波特率使用921600,
而在iTOP4412的板子上面采用该值会工作不正常,导致固件补丁无法下载,开始怀疑板卡不支持该波特率,后使用串口测试工具专门针对这个串口进行921600测试,也没发现问题,后没有继续查找,而是运行wmt_launcher时采用115200波特率:
wmt_launcher -b 115200 -d /dev/ttySAC0 -p /system/etc/firmware &
注意
: 如果您的操作系统使用的是Linux而不是Android,需要修改stp_uart_launcher.c
原始代码里面有
Android特有的属性相关部分,Linux系统不具有这个特性,我们提供了修改好的文件:stp_uart_launcher-linux-ok.c ,用户可以作为参考,该文件与原始文件stp_uart_launcher-ori.c,及正常工作的文件 stp_uart_launcher.c 位于相同目录下面
.
运行
wmt_launcher 服务后,然后执行 "echo 1 > /dev/wmtWifi"命令,如果工作正常,会产生wlan0网络节点,如果没有产生设备节点中间会提示出错信息,需要根据信息查找相关问题,默认情况下WiFi驱动的调试级别为DEBUG级别,可以提升调试级别为更高,当然不要忘记把Kernel控制台输出级别也设置的高一些,驱动的输出信息依赖于驱动代码设置的调试级别及Kernel的控制台级别两部分。
调试信息多一些,方便定位与分析问题,驱动工作正常后需要把调试级别恢复为正常状态,过多的调试信息输出会影响驱动的工作效率和工作的结果,笔者在调试
MMC部分由于把MMC总线的调试信息全部放开,导致MMC工作效率降低,WiFi相关驱动总是适配不到SDIO设备,因为WiFI驱动会按一定的循环次数查找SDIO设备,由于SDIO相关驱动工作效率很低(大量的调试信息输出引起),导致WiFi驱动轮训次数结束了都没有匹配到设备。
如果产生了
wlan0设备节点,那么下一步就是移植wpa_supplicant及wpa_cli程序了,Android4.4采用的wpa_supplicanat_8 版本,而不是以前Android4.0采用的wpa_supplicant版本,他们之间的差异还是比较大的,显著的一个区别是Android4.4里面 wpa_supplicant_8使用的是 NL80211驱动库。而Android4.0中的wpa_supplicant采用的是WEXT 驱动库,如果您使用的是Android4.4的内核版本,运行的是Linux系统,那么需要您移植wpa_supplicanat_8到Linux文件系统中。
Android4.4
系统包含wpa_supplicant_8代码,编译Android4.4时会编译生产wpa_supplicant_8.
wpa_cli
为wpa_supplicant的客户端程序,可以使用该程序扫描无线网络,设置网络的ESSID和密码,连接到无线网络,Linux用户需要使用wpa_cli进行网络连接。Android用户也可以在命令行中使用该工具验证WiFi驱动及wpa_supplicant_8是否工作正常.
这些都没有问题后我们需要移植
HAL层相关代码。
1.5 HAL层移植
HAL
层移植相对简单,MT6620 采用的是Android的WiFi架构,没有经过修改,按照MTK的指导文档移植即可,这里需要注意的是wifi.c文件和init.connectivity.rc文件.
1.5.1 wifi.c文件
wifi.c
文件的路径
iTop4412_KK4.4/hardware/libhardware_legacy/wifi.c
该文件会与
wpa_supplicnat服务进行通信,是Android进行WIFi控制的HAL层的实现,根据logcat 输出信息判断WiFi 工作流程哪里出了问题,笔者修改了wifi.c文件的宏定义:
static char primary_iface[PROPERTY_VALUE_MAX];
// TODO: use new ANDROID_SOCKET mechanism, once support for multiple
// sockets is in
//dg cancel for mt6620
//#define WIFI_DRIVER_MODULE_NAME1"rtl8188eu"
//#define WIFI_DRIVER_MODULE_PATH1 "/system/lib/modules/rtl8188eu.ko"
//#define WIFI_DRIVER_MODULE_NAME2"rtl8192cu"
//#define WIFI_DRIVER_MODULE_PATH2 "/system/lib/modules/rtl8192cu.ko"
//#define WIFI_DRIVER_MODULE_NAME3"rt5370sta"
//#define WIFI_DRIVER_MODULE_PATH3 "/system/lib/modules/rt5370sta.ko"
由于我们在
init.connectivity.rc 里面加载了 WiFi驱动库及运行 wmt_launcher服务,所以不再需要wif.c加载驱动了,直接注释掉相关宏即可.
wifi.c
的 int wifi_load_driver() 函数会设置
wifi的相关属性:
static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
property_set(DRIVER_PROP_NAME, "ok");
设置属性后会触发
WiFi上电操作,因为我们在init.connectivity.rc设置了属性触发:
# monitor property and power on/off wlan
on property:wlan.driver.status=ok
write /dev/wmtWifi "1"
on property:wlan.driver.status=unloaded
write /dev/wmtWifi "0"
wifi.c
会启动wpa_suplicant服务:
int wifi_start_supplicant(int p2p_supported)
该函数会查找wpa_supplicant服务是否已经运行,如没有运行会启动该服务.
1.5.2 init.connectivity.rc 文件
init.connectivity.rc 原始文件有
MTK提供:
iTop4412_KK4.4/hardware/mediatek/config/combo_mt66xx/ init.combo_mt66xx.rc
原始文件名称为 init.combo_mt66xx.rc,拷贝到
ramdisk的root目录下面名称变更为init.connectivity.rc文件 。
我们在该文件增加了加载驱动模块库操作,运行
wmt_lanucher服务操作,另外需要注意文件原有的创建wifi相关目录操作,及修改权限,变更拥有者,这些command非常的重要,比如:
mkdir /data/misc/wifi 0770 wifi wifi
mkdir /data/misc/wifi/sockets 0770 wifi wifi
mkdir /data/misc/wpa_supplicant 0770 wifi wifi
mkdir /data/misc/p2p_supplicant 0770 wifi wifi
chown wifi wifi /data/misc/wifi/wpa_supplicant.conf
chown wifi wifi /data/misc/wifi/p2p_supplicant.conf
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
chmod 0660 /data/misc/wifi/p2p_supplicant.conf
wpa_supplicant
会在/data/misc/wifi/sockets目录下面创建
wlan0文件节点用于与外部程序wpa_cli或者Android层服务进行通信.
这样在
Android4.4的Setting里面打开WiFi,就可以扫描到热点,连接互联网了,启动WiFi之前注意关闭有线连接,否则会存在网络访问冲突.
注意
: Android系统第一次运行是没有开启WiFi功能的,如果您的开发板上面有WiFi模块,且手动开启了Android Setting界面的 WiFi功能 ,那么请不要把WiFi模块从底板上面拆除,否则Android启动过程中因为找不到WiFi模块 ,会频繁 打印出调试信息,导致Android启动失败 ,如果确实产生了这个问题,请重新烧写Android系统此问题即可解决.
1.5 总结
以上作为
iTOP4412开发平台移植 WiFi功能的过程总结,即将发布的Android4.4的Kernel及Android层代码均包含Porting 后的代码,也就是Wifi正常工作的代码,方便大家学习和产品研发.
如果您在实际的项目中需要
WiFi功能,请参考我们的原理图设计硬件,尽力使用相同的WiFi资源,比如WiFi使能配置引脚,WiFi中断配置引脚,WiFi 复位引脚,串口配置引脚等等,这样您只需要关注硬件部分,驱动使用我们移植好的即可,否则需要您修改WiFi引脚配置,进行必要的调试工作,增加自己的工作量。