APP下载

嵌入式系统Linux及USB驱动开发

2010-08-29

山西焦煤科技 2010年4期
关键词:驱动程序数据结构内核

朱 宁

(淮南矿业集团有限责任公司装备管理中心)

嵌入式系统Linux及USB驱动开发

朱 宁①

(淮南矿业集团有限责任公司装备管理中心)

嵌入式ARM处理器是当今世界上最流行的嵌入式处理器,广泛应用于个人通信等嵌入式领域。基于Linux良好的开放性和USB总线极佳的通用性,本文通过在ARM920T处理器的开发板上实现嵌入式Linux系统,并重点描述了Linux USB设备驱动程序的实现过程。

嵌入式Linux;设备驱动;USB

1 Linux内核

Linux操作系统主要由4大部分组成:用户应用程序、操作系统服务、操作系统内核、硬件系统。操作系统内核是Linux操作系统中最核心的部分,也是嵌入式系统开发中修改、移植的重点。一个最小型化的Linux系统,其内核的构造见图1。

图1 Linux系统的内核构造示意图

ARM9中上运行的Linux系统不同于传统意义上的PC版GNU/Linux,ARM9芯片本身的存储单元有限,它所要运行的系统软件和应用程序都是装载到外部存储器里面的。为了适应ARM9的硬件要求,完善的支持Linux系统的运行,必须对通用的GNU/Linux进行定制和裁减。

一个小型的嵌入式Linux系统需要包括两个基本元素:引导工具(boot模块)和Linux微内核。引导工具一般称为启动代码,主要工作是对嵌入式系统中的CPU、FLASH、SRAM、系统CACHE等硬件进行初始化,复杂的启动代码还提供了类似于操作系统的指令集对这些硬件设备进行修改和升级,比较有代表性的是U-BOOT程序。

1)引导模块(boot)。引导加载程序又称为Boot loader,它是CPU加电以后运行的第一段程序。因此,Bootloader的设计是嵌入式Linux开发的基础,其基本功能是初始化硬件设备、建立内存空间的映射图,从而为调用嵌入式Linux内核准备好硬件环境。

SRAM、SDRAM等存储设备属于挥发性的存储器[1],掉电以后其中的内容就会全部丢失,所以必须把操作系统的内核镜像存放在Flash等不挥发性存储介质上。但是操作系统在运行时,需要动态的创建一些如数据段、堆栈、页表(针对使用虚拟地址的操作系统)等内容,所以需要在RAM中运行操作系统。因此,就需要一个应到程序把操作系统的内核镜像从Flash存储器拷贝到RAM中,然后再从RAM中执行操作系统的内核。Bootloader就是可以完成这样一种功能的程序。从本质上来讲,Bootloader不属于操作系统内核,因为它仅仅起到一个创立初始化环境和引导内核的作用。

U-Boot从功能上来分主要分为两大部分[2],一是Boot,包括主要的初始化代码和自解压源代码,用于建立U-Boot,并使其进入可操作的状态,更加特殊的是,它能启动CPU的高速时钟,并能描述片上的存储器。二是自解压可执行文件,这是一个gunzip程序优化的版本,它能将U-Boot解压到RAM中并跳转去自我运行,开发者不参与下载数据。

U-Boot的启动分析[3]:U-Boot与目标板的硬件有高度的依存关系,它的启动过程主要分为两个阶段,即stage1和stage2:stage1用汇编语言编写,通常是与CPU的体系结构有关,如设备初始化代码等,由源码cpu/start.s实现。stage2为C语言程序,相对stage1具有更好的移植性和可读性,用来加载操作系统内核,有源码lib_arm/board.c实现。

Stage1的具体步骤:a)目标板的硬件设备初始化,包括初始化CPU的关键寄存器。b)为加载stage2的代码准备RAM空间。c)拷贝stage2的代码到RAM空间中。d)设置好堆栈。e)跳转到stage2的C入口点。

而stage2的具体步骤:a)初始化本阶段要使用到的硬件设备。b)检测系统内存映射。c)将kernel映像和根文件系统ramdisk映像从FLASH上载到RAM空间。d)为内核设置启动参数。e)调用内核。

本文U-Boot的移植方法是结合嵌入式开发板HHARM9200的源码结构。/HHARM9200目录下的子目录(/HHARM9200/bootloader/)可以通过修改这些源码来修改bootloader;这里的bootloader for AT91RM9200分为3个:

A T91RM9200-Loader:生成loader.bin,在CPU内部SRAM中运行:

MEMORY{

Ram:ORIGIN=0x200000,L ENGTH= 0x3000

}

0x200000就是INTERNAL SRAM的地址。

Simple_boot:生成boot.bin,烧到FLASH上运行。

u-boot-0.4.8:生成u-boot.bin,可在SDRAM中运行,实际应用是烧到FLASH上,由上面的boot.bin加载到SDRAM中运行,见图2。

2)内核的定制和裁减。嵌入式系统开发一般都是通过宿主机(PC)与目标板的模式。因为对于嵌入式系统的开发,没有足够的资源在本机(开发板)运行开发工具和调试工具。开发时使用宿主机上的交叉编译、汇编及连接工具形成可执行的二进制代码,然后把可执行文件下载到目标机上运行。

在宿主机上建立交叉编译调试的开发环境需要许多的软件模块协同工作,这在套件光盘的安装中已经自动完成了。在PC上安装附带光盘后会在根目录下生成工作目录:⑴/HHARM9200(内含Linux内核、应用程序源代码以及各个工具软件);⑵Linux内核源代码目录(/HHARM9200/linux-2.4.19-rmk7/);⑶应用程序目录(/HHARM9200/applications)。

图2 地址空间分布示意图

通过make menuconfig则出现可对内核和驱动模块进行选择和配置的界面,且看到内核版本为: Linux Kernel-2.4.19-rmk7。

以上配置完成设置后,执行make zImage即可编译生成自己定制的内核映像文件,并自动被复制到/ tftpboot/目录下以供烧写。编译过程可以如下观察:

编译过程如下:

这里生成的是kernel目录下的vmlinux文件。它的地址分布就是由/HHARM9200/linux-2.4.19 -rmk7/arch/arm/vmlinux.lds文件指定的。

下面这句则说明了System.map文件是如何生成的:

/usr/local/arm/2.95. 3/bin/arm-linuxnmvmlinux|grep

-v‘(complied)|(.o$)|([aUw])\ (..ng$)|(LASH[RL]DI‘|sort>System. map

然后将这个内核用gzip进行压缩后,与head.S这个解压缩代码部分整合到一起生成zImage。在压缩过程中先将ELF格式的vmlinux转换为二进制格式,这样的好处是可以使文件大大缩小,并且由原来的2 M多变为700~800字节,更适合嵌入式系统。

/usr/local/arm/2.95.3/bin/arm-linux-objcopy-Obinary-R.note-R.comment-S/ HHARM9200/linux-2.4.19-rmk7/vmlinux temp

gzip-9temp.gz

/usr/local/arm/2.95. 3/bin/arm-linux-ld -r -o temp.o -b binary temp.gz

rm-f temp temp.gz

/usr/local/arm/2.95.3/bin/arm-linux-ld -p -X -T vmlinux.lds head.o misc.o head-AT91RM9200.o temp.o/usr/

local/arm/2.95.3/lib/gcc-lib/arm-linux/2.95.2/ soft-float/libgcc.a -o vmlinux

make[2]: Leaving directory

‘/HHARM9200/linux-2.4.19-rmk7/arch/arm/ boot/compressed’

/usr/local/arm/2.95.3/bin/arm-linux-objcopy

-O binary -R.note -R.comment -S compressed/vmlinux zImage

cp-f zImage/tftpboot/

上述表明:首先用temp作为临时文件名,对内核进行gzip压缩,且转换为.o格式的文件并将解压缩部分代码和压缩内核链接为一个新的vmlinux,然后再次将解压缩代码部分的ELF格式转换为二进制文件格式,即最终的zImage文件,并复制到/tftpboot目录下,以供TFTP下载烧写。

2 Linux中USB设备驱动分析

1)USB驱动概述。从2.2.18版内核开始, Linux增加了对USB的支持,2.4.x版本的内核对USB的支持已比较完善。其主机驱动程序已经包括在内核中,而设备驱动需要程序员根据不同的设备进行编写。在设备驱动程序中,有一个叫做“USB核心”的子系统[4],它的作用是提供支持USB设备驱动程序的API和USB的主机驱动程序。它提供了许多数据结构,宏定义和功能函数来对硬件或设备进行支持。在Linux下编写USB设备的驱动程序从严格意义上讲,就是使用这些USB核心的子系统定义的数据结构,宏和函数来编写数据的处理功能。

2)USB设备连接与初始化。USB设备接入主机后,主机枚举识别设备及设备状态的变化,通过总线的上拉电阻判断新设备的连接是全速还是低速设备,并发送复位信号。设备端在收到复位信号后,对总线作出相应,主机通过默认地址0收到设备响应后,为设备分配一个空闲的地址,以后就通过这个地址和设备进行通信。主机读取设备的相应描述符对设备进行配置,若设备符合要求就发送配置完毕的信号给主机,然后调用设备驱动的probe函数对设备进行初始化。如下是一个简单的probe函数[5]:

static void*naked_usb_probe(struct usb_device*dev)

{//检查硬件定义与驱动是否相符……

{//初始化设备相关资源

if(device->obuf=(char)kma11oc(OBUF_ SIZE,GFP_KERNEL)){…}

device->dev=dev;

……

printk(“My usb device pluged in. ”);

return;}}

在初始化中如果设备有中断端点,则填充中断请求URB,并提交给主机,然后再对这个特定数据结构相应字段分配缓冲区、赋初值、并返回此数据结构,完成对设备的初始化。

3)USB设备驱动框架。设备驱动完成的功能主要是:提供与应用程序的接口;读取并解析USB设备特有的描述符;获得设备提供的传输通道;发送设备特有的和基本的USB命令请求;通过设备提供的传输通道与设备进行数据传输;通过USB命令请求重新配置没备。

USB设备驱动在内核模块中必须注册2个入口点和1个设备节点。对于特别的USB设备一个驱动可以注册一对文件操作符和一个次设备号。Linux usb驱动程序首先时调用usb_register()函数在linux usb子系统里注册[6],其注册结构如下:

struct usb_driver{

const char*name;//模块名字

void*(*probe)(struct usb_device*,unsigned int);//probe功能入口点

void(*disconnect)(struct usb_device*,void*);//disconnect功能入口点

struct list_head driver_list;//为子系统内部初始化用,一般为0

struct file_operations*fops;//文件操作列表指针

int minor;//次设备号

};

USB设备注册后,驱动就已经和设备绑定了,任何用户态程序要操作此设备都可以通过file_operations结构所定义的函数进行。结构中struct usb_ driver这个数据结构在内核中注册一个USB设备驱动程序。其中包括有指向设备初始化函数的指针*probe()、指向设备卸载函数指针*disconnect()以及对设备进行具体操作的File_operations函数指针Fops,用于识别设备的Usb_device_id数据结构Id_ table。由于一个设备驱动程序可以支持多个相同或同类的设备,在Usb_driver结构中还包含一个整型的次设备号Minor,每个次设备号和一个具体设备对应,USB规范中规定此设备号必须是16的倍数,一个USB设备驱动程序最多可支持16个设备。

当USB设备连接到系统时,主机系统调用初始化函数probe(),并读取Usb_dev结构。在Usb_dev结构中主要描述了USB设备的硬件信息:设备、接口等描述符,以及符合的USB规范等。根据这些硬件信息判断该设备是否被驱动程序所支持,然后给设备分配一个特定的数据结构。

4)USB设备驱动的释放及卸载。设备的卸载之前首先要释放对设备的控制权[7],减少模块引用计数器等函数:

static int usb_device_close(struct inode*inode,struct file*file)

{……}

module_dec_use_count;//减少模块引用计数设备卸载模块和初始化模块相反,主要是释放在初始化时分配的各种资源以及取消在初始化函数中提交的中断请求URB等。

static void disconnect_device(struct usb_device*dev,void*ptr)

{…}

kfree(device->iobuffer);

kfree(device->retbuffer);//释放在内核中申请的内存

…;

3 结束语

本文介绍了嵌入式Linux系统中USB设备驱动程序开发的基本原理,通过分析USB驱动程序开发的程序框架和重要数据结构,对Linux文件系统机制及USB底层驱动进行了一定的研究。USB的广泛应用其正在成为外设与PC机及膝上型电脑连接的工业标准USB外设主要是便携式设备,随着其数量的不断增多,设备之间无主机参与的直接通信成为亟待解决的问题。通过研究并调试基于嵌入式Linux系统的USB底层驱动,取得了较满意的效果。Linux系统版本在不断更新,基于2.6版本的Linux嵌入式操作还有待于进一步的验证。

[1] 沈 沙,苏佳宁,田骏骅,等.μClinux操作系统在嵌入式SOC平台上的移植[J].计算机工程与应用,2004(26):104-108.

[2] 童大鹏,冉蜀阳,张 礁,等.基于AT91RM9200微控制器的BootLoader的分析与开发[J].微计算机应用,2005,26(3):345-349.

[3] 朱继杭,杨世武.基于AT91RM9200的U-Boot移植方法[J].仪器仪表用户,2005,12(6):121-122.

[4] 张宏伟.Linux下USB设备驱动程序的编写[J].计算机应用研究,2001(9):141-146.

[5] (美)诺顿·格蕾菲斯著,翟大昆译.Linux实用指南[M].北京:机械工业出版社,1999:79-80.

[6] (美)马克斯韦尔.Linux内核源代码分析[M].北京:机械工业出版社,2000:57-58.

[7] 陈华瑛,李建国.UNIX操作系统设计与实现[M].北京:电子工业出版社,1999:45-47.

Embedded Linux and USB Driver Development

Zhu Ning

Implanting the dyadic ARM processor is that the most popular implanting the dyadic processor,apply to an individual broadly communicate by letter and so on implants the dyadic field in the world nowadays.Owing to fine Linux opening to the outside world and extremely nice general availability of USB highway,the main body of a book is passed realizing the process implanting dyadic Linux system,and concentrating on having described Linux USB equipment driver to come true on ARM920T processor exploitation board.

Embedded Linux;Device Driver;USB

book=4,ebook=123

TD672

A

1672-0652(2010)04-0052-05

2010-03-22

朱 宁 男 1981年出生 2006年毕业于安徽理工大学 助理工程师 淮南 232082

猜你喜欢

驱动程序数据结构内核
强化『高新』内核 打造农业『硅谷』
基于嵌入式Linux内核的自恢复设计
Linux内核mmap保护机制研究
计算机硬件设备驱动程序分析
“翻转课堂”教学模式的探讨——以《数据结构》课程教学为例
微生物内核 生态型农资
高职高专数据结构教学改革探讨
TRIZ理论在“数据结构”多媒体教学中的应用
《数据结构》教学方法创新探讨
基于MPC8280的CPU单元与内部总线驱动程序设计