前 言
编写Linux设备驱动程序无疑是一项复杂的工作。本文将集中介绍非标准硬件的设备驱动程序编写,探讨硬件应用编程接口,并借用CirrusLogICEP9312片上系统嵌入式平台添加设备驱动程序这一案例来进行分析。
如果有些编程内容未能在本文中涉及,那么读者亦可以查阅相似的设备驱动程序编码,以做参考。还有一种方法,就是检索历史档案或者向Linux内核问讯中心去函问讯。
Linux概述
Linux是UNIX操作系统的翻版,1991年由LinusTorvalds最先开发出来,并通过开放源代码开发模式不断得到开放源代码组织的改进。任何使用Linux的个人和团体都无需支付任何版权费用。
只有内核还不够,通常Linux与一些在内核上运行的视窗环境、视窗管理器和应用捆绑在一起。然而,由于具备了嵌入式平台,视窗环境并非必不可少。与微软的视窗操作系统不同的是,Linux并不需要一套固定的、必须采用的应用软件或实用程序,因此能够十分符合嵌入式市场终端解决方案的客制化要求。
操作系统最基本的组成部分包括1个资源管理器、1个调度程序、1个介于硬件和应用软件之间的接口、1个网络管理器和1个文档系统管理器。Linux操作系统也包括这些组成部分,当然还有其他部分。本文主要阐述介于硬件和应用软件之间的接口--设备驱动程序。
图1解释了用户应用软件、操作系统内核和硬件平台之间的区别。
设备驱动程序类型
设备驱动程序可分为2大类:硬件设备驱动程序和软件设备驱动程序。硬件设备驱动程序和物理硬件设备相连接,如UART设备或IDE设备,而软件设备驱动程序则作为低级数据结构间的接口,或硬件设备驱动程序和高级数据结构间的接口。图形控制台驱动程序就是一个软件设备驱动程序。其中,1个LCD控制器驱动程序装载并管理该显示器,同时图形控制台对即将显示的字符进行着色,并获取从键盘输入的信息。软件设备驱动程序的另一个例子是文档系统执行--文档系统驱动程序采用1个硬盘驱动程序存储数据,而该硬盘驱动程序直接与物理硬盘相连接。
设备驱动程序的分类
Linux设备驱动程序有几类:字符、区块、网络和其他。通常,驱动程序根据设备的访问方式分类。然而,也有些设备无法按照此类方式得到区分,因此被归到"其他类型"。字符设备包括那些使数据成为数据流的设备,可通过1个文档系统的特殊文件获得(文档系统的特殊文件将在后文中加以讨论)。鉴于字符设备的特性,该设备只能根据顺序访问数据,即无法往前或往后搜索数据。串行端口和音频设备都是这种类型。图2是CirrusLogic的EP9312片上系统结构图,其中Linux字符设备以绿色标出。
区块设备能够照管1个文档系统。该类设备和字符设备一样,也是通过文档系统特殊文件访问。但是,区块设备与文档设备的差异在于其可被随机访问。这意味着,应用软件可查找在该设备中的随机位置。硬盘驱动器和CD驱动器都是区块设备,它们内部的文件指针可以指向设备内部的任何位置,惟一的限制来自设备本身。当区块设备通过文档系统特殊文件访问时,该应用接口即同字符设备一样,只是与内核的接口有所差别而已。图2中的红色部分即为CirrusLogicEP9312片上系统结构中Linux区块设备。
网络接口设备既可以是硬件设备,也可以是软件设备。硬件设备如以太网卡,软件设备如低端网络协议堆栈(本文将此类接口视为软件设备)。中间件和协议堆栈有时会被看作是软件设备。网络接口设备是信息包数据的通信设备,一般拥有惟一名称,并且无法通过文档系统特殊文件访问。相反,它们只对内核网络堆栈开放。通常,用户级应用软件可访问内核网络堆栈,而不能访问网络接口设备。图2中的蓝色部分即为CirrusLogicEP9312片上系统结构中的Linux网络接口设备。
其他的设备驱动程序还包括数据总线驱动程序(USB,I2C,AMBA等)、/proc接口和视频驱动程序。这些类型的设备无法被归入以上的3个类型中,但仍然是与Linux内核接口的设备驱动程序。
文档系统特殊文件
文档系统特殊文件提供了从文档系统访问硬件设备的可行性。这些访问点使用mknod命令在文档系统/dev目录中生成。命令如下:
mknod
其中,是给予硬件设备的名称,如/dev/hda1是给予硬盘驱动器的通用名称。是设备驱动程序的类型--字符(char)、区块等。代表设备类别和与之相配的驱动程序。表示设备类别中的一个实例,并仅对设备驱动程序适用。例如,某个系统中同时采用2个硬盘驱动器,它们都具有同样的主要编号,使用同样的设备驱动软件,但是该设备驱动程序软件却会在内部根据次要编号区分这2个硬盘驱动器。
值得注意的是,并非所有的设备都执行特殊文件接口。如同本文前面已经提及的,网络设备驱动程序就不采用这种接口访问设备。
这种情况下,在设备文档系统里,就会使用devfs来获得文档设备特殊文件。devfs目前广受欢迎,但仍然还不是内核的默认功能。如果采用devfs文档系统,那么就无需mknod来生成特殊文件了。相反,设备驱动程序软件会使用直接的devfs文档系统接口在空闲时刻或者设备刚被初始化时生成特殊文件。
编程实例概述
为便于示范非标准嵌入式平台的Linux设备驱动程序,本文将说明EP9312的设备驱动程序实现情况。其中,EP9312IDE设备驱动程序是区块设备,EP9312触摸屏为字符设备,代码中的高级API/硬件接口、初始化序列和应用软件编码均将予以说明。
字符设备驱动程序实例:触摸屏设备驱动程序
EP9312触摸屏控制器因其数据只能按顺序获取而被列为Linux字符设备。触摸屏字符驱动程序的执行是相当简单的--设备向操作系统注册,并通过文档系统特殊文件进行访问。有关硬件代码包含在文档操作表的一套函数中。我们将从内核初始化开始,解释该驱动程序的执行情况。
初始化EP9312触摸屏的函数是:
int__initep93xx_ts_init(void)
该函数处理2项工作:当设备被中断驱动时获取设备IRQ和在操作系统内注册触摸屏设备。
函数request_irq()在请求IRQ时被调用,并注册中断处理器函数以在设备发生系统中断时处理所需的任务。
而函数register_chrdev()则是用来注册字符设备的。该函数表现形式如下:
intregister_chrdev(unsignedintmajor,
constchar*name,
structfile_operations*fops)
该函数安装了字符设备硬件的内核接口。主要编号用于把驱动程序映射到/dev目录中的文档系统特殊文件。设备被赋予一个名称,以便内核识辨。此外,file_operations结构具有对函数指针表的一个指针,该表指向硬件的相应函数。
然而,仍然有一些字符设备不符合预先确定的字符设备范畴。这些设备就用主编号10一起归于"其他类型",注册设备用以下函数:
intmisc_register(structmiscdevice*misc)
misc_register()用主编号10调用register_chrdev(),设备名称和函数表指针通过miscdevice数据结构获得。同样,miscdevice数据结构还保存设备驱动程序所使用的次要号码。
以下是在设备驱动程序代码内注册EP9312触摸屏采用的函数调用:
misc_register(&ep93xx_ts_miscdev)
数据结构ep93xx_ts_miscdev是对触摸屏硬件的内核访问,定义如下:
staticstructmiscdeviceep93xx_ts_miscdev=
{
EP93XX_TS_MINOR,/*devicEMInornumber*/
"ep93xx_ts",/*nameofthedevice*/
&ep93xx_ts_fops/*devicefileoperations*/
/*tablepointer*/
}
其他类型设备驱动程序采用次要号码区分设备。
硬件接口函数在设备驱动器内即被静态定义,当设备注册时,由内核通过传递给操作系统的文档操作函数指针获得。指针列表定义如下:
staticstructfile_operationsep93xx_ts_fops=
{
owner:THIS_MODULE,
read:ep93xx_ts_read,
write:ep93xx_ts_write,
poll:ep93xx_ts_poll,
open:ep93xx_ts_open,
release:ep93xx_ts_release,
fasync:ep93xx_ts_fasync,
}
初始化触摸屏设备后,即需创建文档系统特殊文件,以便协助应用程序代码访问设备。创建EP9312触摸屏特殊文件的mknod命令如下:
mknod/dev/misc/ep93xx_tsc10240
该步骤即可在根目录系统下的初始化文档初始化Linux时得到执行,也可在命令提示里实现手动操作。
以下是用户级应用代码的一个实例,通过文档系统特殊文件访问触摸屏设备:
#defineTS_DEV"/dev/misc/ep93xx_ts"
intread_ts()
{
intfd,nbytes;
shortdata[3];
fd=open("/dev/misc/ep93xx_ts",O_NONBLOCK);