i.MX6是Freescale推出的ARM架构高端多媒体处理器,与常规的ARM处理器不同的是,i.MX6具有非常实用的PCI Express接口,这对于从事WiFi行业的人士来说具有非常大的吸引力。通常的基于WLAN SoC进行无线产品设计,很难具备高性能多媒体处理能力;通常的基于ARM处理器进行多媒体处理器平台设计,又很难实现高性能无线传输。i.MX6的出现使得高性能无线多媒体平台的设计大大简化。其实此前也使用过TI AM3894进行无线媒体服务器的设计,但是其过大的发热量极大地限制了其应用,Mindspeed公司也具有PCI-e接口的ARM处理器,但毕竟不是主流厂商,所以i.MX6深深地赢得了广大工程师的喜爱,包括我本人。
说了这么多,开始进入本文的主题。做过Qualcomm Atheros平台的读者一定知道,WiFi产品出厂前需要在产线通过复杂的产测环节,主要包括射频校准,测试,参数写入等步骤,未写入射频参数的WiFi产品是无法正常使用的。如果是基于Qualcomm Atheros WLAN SoC做的无线产品,官方会有移植完成的ark.ko内核模块,nart.out应用程序及各种所需的库文件,Freescale i.MX6不在官方支持的范围内。因此,如果想顺利产测,就必须自行移植驱动程序及测试程序。
郑重声明:本文关于驱动移植部分写得很不专业,并且是以流水账的方式记录,主要是想帮助面临同样问题的读者,所以还请读者海涵。
1 此前调试i.MX6驱动程序时已经搭建好i.MX6的开发环境,因此本文不进行搭建过程的描述,读者可以访问https://www.witimes.com/imx6-build-environment/了解大致过程。
2 首先编译art.ko 模块。将ART2 2.28源代码解压在ltib/rpm/BUILD目录下 ,进入driver/linux目录,更改makefile.artmod文件,指定Linux内核路径及工具链的路径,如下:
KDIR:=/home/tom/ltib/rpm/BUILD/linux-3.0.35
PWD:=$(shell pwd)
ROOTDIR:=$(PWD)/modules
ifeq($(ARM),1)
ARC:=arm
CROSS_CC:=/opt/freescale/usr/local/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro
toolchain/bin/arm-none-linux-gnueabi-
DEBUG:=0
endif
其中,将DEBUG改为1可以开启调试模式。
3 使用make –f makefile.artmod命令即可开始编译,但是会遇到很多的问题。
3.1. error: 'phys_t' undeclared (first use in this function),含义是phys_t数据类型未定义,查看Atheros SDK中关于phys_t数据类型的定义,将phys_t的定义复制到/modules/include/ dk_flash.h中,此错误不再出现。
3.2. modules/dk_func.c:468:2: error: unknown field 'ioctl' specified in initializer,这是由于2.6.36内核之后 去掉了原来的ioctl,添加两个新的成员,所以会出错:
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
修改modules/ dk_func.c中的ioctl为compat_ioctl,编译通过。
3.3 modules/dk_event.c:21:26: error: 'SPIN_LOCK_UNLOCKED' undeclared here (not in a function),含义是SPIN_LOCK_UNLOCKED未声明,修改modules/dk_event.c中的
spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
为
static DEFINE_SPINLOCK(driver_lock);
编译通过,得到art.ko。
4 将编译得到的art.ko模块下载到目标板中,insmod art.ko命令插入模块,出现一条错误信息“Could not find flash device!”,这是flash_init()函数报出的错误。考虑到PCIe网卡的驱动程序不应该初始化Flash设备,因为PCIe网卡的校准信息保存在EEPROM或者OTP中,推测必定有配置错误导致了这条错误信息。
5 仔细阅读makfile.artmod文件,发现意一条重要的注释信息“Making generic AP art module build. This build target is used for 3rd party AP processor”,即“编译用于第三方处理器的目标文件”,对应的编译命令为
make ARCH=$(ARC) PB42=1 DEBUG=$(DEBUG) CROSS_COMPILE=$(CROSS_CC) -C $(KDIR) M=$(PWD)/modules modules
很明显,问题就出在这里。再次编译,得到了可以正确加载的内核模块。
6 接下来就是nart的移植。遇到了诸多的问题。
6.1 /home/tom/ltib/rpm/BUILD/art2_ver_2_20ap_src/art/../wlan/hal/nartlinux/ah_osdep.h:93:15: error: conflicting types for 'va_list',
/opt/freescale/usr/local/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro-toolchain/bin/../arm-fsl-linux-gnueabi/multi-libs/usr/include/stdio.h:80:20: note: previous declaration of 'va_list' was here,含义为va_list数据类型定义冲突。既然工具链中已经定义了va_list,那么最简单的办法自然是屏蔽nart中对于va_list的定义,更改wlan/hal/nartlinux/ah_osdep.h文件
#ifndef __LINUX_POWERPC_ARCH__
//typedef void *va_list;
#endif
再次编译,问题不再出现。
6.2 uint32_t数据类型未声明的错误,我的解决办法是将uint32_t类型重命名为uint32_imx,并修改nart源码中所有涉及到uint32_t的部分。
6.3 经过6.1,6.2的调整,编译过程可以完成大部分,但在最后的link环节,出现了大堆的关于readl,writel的错误。折腾了大概1天的时间,此问题一直没有解决。
7 由于本人水平十分有限,所以不得不放弃在ART2 2.28版本上的尝试,开始使用ART2 4.6版本,好在这个版本未在link环节,现了关于readl,writel的错误,顺利地得到nart.out及各种库文件。
8 将第7步得到的art.ko ,nart.out及各种库文件下载到目标板中,nart.out可以正常运行。然而悲剧的是,更大的问题来了。每当我尝试使用artgui load dut时,总会出现“Error: get version ioctl failed !”,这个问题大概困扰了我3-4天的时间。期间非常仔细地阅读了源代码得知这条打印信息出现在get_version_info()函数中:
if ((status=get_version_info(hDevice, pDrvVer)) != A_OK) {
close(hDevice);
A_FREE(pdevInfo->pdkInfo);
A_FREE(pdevInfo);
return status;
}
说明,如果get_version_info()返回的不是OK,则关闭设备,这部分代码出现在common/linux_hw.c中。get_version_info()在get_device_client_info()中被调用,get_device_client_info()在deviceInit()中被调用看字面意思就是初始化设备,deviceInit()在setupDevice()函数中被调用。跟踪调试get_version_info(),发现其通过以下语句获得version的值
if (ioctl(hDevice,DK_IOCTL_GET_VERSION,&version) < 0)
那么问题应该就是出在ioctl函数这里。
9 是时候了解一下ioctl函数了(做驱动开发的读者可不要见笑,哈哈)。ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用方式如下:
if (ioctl(hDevice,DK_IOCTL_GET_VERSION,&version) < 0)
其中fd是用户程序打开设备时使用open函数返回的文件标示符,cmd是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,这个参数的有无和cmd的意义相关。
ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数来控制设备的I/O通道。
分析一下nart代码,的确符合这样的调用过程,下图(不知道原作者是哪位,如有侵权,请联系无线时代)很好地展示了这一过程。
那么,根据本人的粗浅理解,用户空间的ioctl调用通过内核向驱动程序传递了fd,cmd两个重要参数,其余的参数是可选的。那么ioctl(hDevice,DK_IOCTL_GET_VERSION,&version)主要就是将hDevice及DK_IOCTL_GET_VERSION命令传递给驱动程序,其中DK_IOCTL_GET_VERSION定义在driver/linux/modules/include/dk_ioctl.h中。查看驱动程序中对于cmd的处理
switch (cmd) {
case DK_IOCTL_GET_VERSION:
#ifdef DK_DEBUG
printk("DK:: DK_IOCTL_GET_VERISION n");
#endif
data = (DRV_MAJOR_VERSION << 16) | (DRV_MINOR_VERSION);
ret = put_user(data, (INT32 *)arg);
break;
case DK_IOCTL_GET_CLIENT_INFO:
#ifdef DK_DEBUG
printk("DK:: DK_IOCTL_GET_CLIENT_INFO n");
#endif
在驱动程序的编译过程中已经开启了DEBUG功能,那么应该打印出“DK:: DK_IOCTL_GET_VERISION”这条信息,然而实际的log中并不存在——ioctl没有将参数传递至驱动程序!
10 回想3.2中的改动,一定引起了内核与驱动程序不匹配,才导致fd及cmd无法正确传递,看来2.3中的改动无法根本解决问题,需要对驱动程序进行大幅度调整才行。可是想想自己的软件水平,实在是对自己没有信心。
然而,我承诺的事情一定兑现,绝不能轻易放弃。
万般无奈之下,我想到了Qualcomm Atheros的IPQ8064也是ARM架构,如果Qualcomm想在自己的Demo板上运行ART,就必须经历同样的过程!于是托朋友弄到了最新版本的nart及驱动程序源代码,此处不便透露版本信息,需要做的主要工作就是仿照AP148的config文件及makefile文件编写Freescale i.MX6的config文件及makefile文件,问题终于解决!
11 以下是nart正常运行dut上的打印信息。
以下是artgui的截图。
使用artgui读取EEPROM,发现其内容完全正确,其中MAC地址为“7C:C3:A1:B7:F2:5B”。
正常加载ath9k驱动程序,MAC地址同样为“7C:C3:A1:B7:F2:5B”,至此,为Freescale i.MX6处理器移植ART驱动程序已全部完成。