数字集成电路设计与系统应用专业设备驱动程序案例分析数字集成电路设计与系统应用专业概要设备驱动程序的作用设备驱动程序的分类设备驱动程序在操作系统中的位置机制与策略设备驱动程序的基本结构设备驱动程序如何被使用一个简单设备驱动程序实例2数字集成电路设计与系统应用专业设备驱动程序的作用设备驱动程序将复杂的硬件抽象成一个结构良好的设备,并通过提供统一的程序接口为系统的其它部分提供使用设备的能力和方法。设备驱动程序(应该只是)为系统的其它部分提供各种使用设备的能力,使用设备的方法应该由应用程序决定。3数字集成电路设计与系统应用专业设备驱动程序的分类字符设备驱动程序各种串行接口,并行接口等。块设备驱动程序磁盘设备等网络设备驱动程序网卡等。杂项设备驱动程序不属于上述三种设备之外的一些设备,如时钟等。4数字集成电路设计与系统应用专业在操作系统中的位置设备驱动程序是内核代码的一部分。驱动程序的地址空间是内核的地址空间。驱动程序的代码直接对设备硬件(实际是设备的各种寄存器)进行控制(实际就是读写操作)。应用程序通过操作系统的系统调用执行相应的驱动程序函数。中断则直接执行相应的中断程序代码。设备驱动程序的file_operations结构体的地址被注册到内核中的设备链表中。块设备和字符设备以设备文件的方式建立在文件系统中的/dev目录下,而且每个设备都有一个主设备号和一个次设备号。5数字集成电路设计与系统应用专业6块设备驱动程序字符设备驱动程序网络设备驱动程序数字集成电路设计与系统应用专业7ls-l/devcrw-r-----1rootroot1,1Jan100:00memcrw-r-----1rootroot1,2Jan100:00kmemcrw-rw-rw-1rootroot1,3Jan100:00nullcrw-r-----1rootroot1,4Jan100:00portcrw-rw-rw-1rootroot1,5Jan100:00zerocrw-rw-rw-1rootroot1,7Jan100:00fullcrw-r--r--1rootroot1,8Jan100:00randomcrw-r--r--1rootroot1,9Jan100:00urandomcrw-rw-rw-1rootroot5,0Jan100:00ttycrw-------1rootroot5,1Jan100:00consolecrw-rw-rw-1rootroot5,2Jan100:00ptmxdrwxr-xr-x1rootroot0Jan100:00ptydrwxr-xr-x2rootroot0Jan100:00ptsdrwxr-xr-x1rootroot0Jan100:00rddrwxr-xr-x1rootroot0Jan100:00mtddrwxr-xr-x1rootroot0Jan100:00mtdblockcrw-------1rootroot4,64Jan100:15ttyS0crw-------1rootroot4,65Jan100:00ttyS1crw-------1rootroot4,66Jan100:00ttyS2crw-------1rootroot4,67Jan100:00ttyS3crw-------1rootroot4,68Jan100:00ttyS4drwxr-xr-x1rootroot0Jan100:00miscc:字符设备b:块设备主设备号次设备号数字集成电路设计与系统应用专业机制与策略机制(mechanism)设备驱动程序所具备的能力例如:串行设备驱动程序具有设置波特率的能力。策略(policy)这些能力如何被使用例如:根据需要将串口波特率设置成9.6kbps。8设备驱动程序应该是“策略无关”的,即policyfree。数字集成电路设计与系统应用专业设备驱动程序源代码的基本结构9/**驱动程序简单说明:*驱动程序的作用:这是一个字符设备驱动程序的基本框架结构*被驱动设备的简单描述:将使用AT91RM9200的PB端口为例进行说明*一些特殊的考虑等:如PB21作为可以产生中断的输入引脚(本例未实现)*版本,创建日期,作者等:1.0版,2006年1月6日,李毅*/#ifndef__KERNEL__#define__KERNEL__#endif#ifndefMODULE#defineMODULE#endif#includelinux/config.h#includelinux/module.h...#includeasm/arch/hardware.h表明这个模块将用于内核,也可以在编译时通过–D选项指定,如gcc–D__KERNEL__。参见Makefile。内核头文件,需要根据具体驱动程序和用到的内核模块确定。表明这个驱动程序将以模块的方式编译和使用,也可以在编译时通过–D选项指定,如gcc–DMODULE。参见Makefile。数字集成电路设计与系统应用专业10/**驱动程序中使用的各种函数的原型声明。标准的作法是将函数原型声明*放在一个头文件中,然后在该文件开始处使用#include引用,并在该*文件中定义。**这里我们将函数的声明和定义放在一起。所以下面的代码既是函数的声明,*也是函数的定义。*/staticssize_tspioc_read(structfile*filp,char*buffsize_tcnt,loof_t*off){/*这里是read函数的代码*/returnret;}staticssize_tspioc_write(structfile*filp,char*buffsize_tcnt,loff_t*off){/*这里是write函数的代码*/returnret;}数字集成电路设计与系统应用专业11staticintspioc_ioctl(structinode*inode,structfile*filpunsignedintcmd,unsignedlongarg){/*这里是ioctl函数的代码,它的一般格式为一个switch分支语句*switch(cmd){*caseCMD1:*...*break;*...*caseCMDn:*...*break*default:*...*break;*}*/returnret;}ioctl()函数用于控制驱动程序本身的一些特性和参数,如设定驱动程序使用的缓冲区的大小,设定串行通讯的速率等。数字集成电路设计与系统应用专业12staticintspioc_open(structinode*inode,structfile*filp){/*这里是open函数的代码*/returnret;}staticintspioc_close(structinode*inode,structfile*filp){/*这里是close函数的代码*/returnret;}上述5个函数,既read(),write(),ioctl(),open(),close(),是一个字符设备驱动程序最基本的需要由驱动程序的作者完成的函数。这5个函数将对应于相应的5个系统调用:read()-spioc_read()write()-spioc_write()ioctl()-spioc_ioctl()open()-spioc_open()close()-spioc_close()系统调用驱动程序函数数字集成电路设计与系统应用专业13staticstructfile_operationsspioc_fops={read:spioc_read,write:spioc_write,ioctl:spioc_ioctl,open:spioc_open,release:spioc_close,};file_operations是一个结构体类型,定义在include/linux/fs.h中。上述代码定义了一个file_operations类型的结构体spioc_fops,并将其中的一些成员赋了初值。由于spioc_fops是一个静态变量,所以其他成员的初值是“零”。结构体spioc_fops将作为一个参数在注册一个设备驱动程序时传递给内核。内核使用设备链表维护各种注册的设备。不同类型的设备使用不同的链表。数字集成电路设计与系统应用专业14structfile_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);int(*readdir)(structfile*,void*,filldir_t);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*flush)(structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*,intdatasync);int(*fasync)(int,structfile*,int);int(*lock)(structfile*,int,structfile_lock*);ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);};structfile_operationsinclude/linux/fs.h数字集成电路设计与系统应用专业15staticint__initspioc_init(void){/*设备初始化代码等*/if(register_chrdev(SPIOC_MAJOR,“spioc”,&spioc_fops)){printk(KERN_ERR“spioc.c:unabletoregister”“thedevicewithmajor%d.\n”,SPIOC_MAJOR);return–EIO;}/*其他初始化代码*/returnret;}staticvoid__exitspioc_exit(void){/*设备撤消代码*/if(unregister_chrdev(SPIOC_MAJOR,“spioc”)){printk(KERN_ERR“spioc.c:unabletoremovethe”“devicewithmajor%d.\n”,SPIOC_MAJOR);return;}/*其它设备撤消代码*/return;}数字集成电路设计与系统应用专业16module_init(spioc_init);module_exit(spioc_exit);这两个函数,module_init()和module_exit(),用于告诉内核,当一个驱动程序加载和退出(或撤消)时,需要执行的操作。不同驱动程序在