©2005博创科技北京博创兴业科技有限公司BEIJNGUNIVERSALPIONEERINGTECHNOLOGYCo.,LTD博创科技嵌入互动Linux字符设备驱动程序设计刘淼threewater@up-tech.com2006.3.21博创科技嵌入互动©2005博创科技linux的驱动程序Linux下对外设的访问只能通过驱动程序Linux对于驱动程序有统一的接口,以文件的形式定义系统的驱动程序:Open、Release、read、write、ioctl…驱动程序是内核的一部分,可以使用中断、DMA等操作驱动程序需要在用户态和内核态之间传递数据uClinux下可以在应用层直接访问外设,操作寄存器口,但是无法处理中断——不推荐使用对于复杂的应用可以考虑是用mmap博创科技嵌入互动©2005博创科技内核功能的划分进程管理(进程之间的通讯与同步)内存管理(malloc/free)文件系统设备控制网络功能(网络通讯协议等)博创科技嵌入互动©2005博创科技Linux下设备和模块的分类按照上述系统内核的功能,Linux中把系统的设备定义成如下三类:字符设备块设备网络设备博创科技嵌入互动©2005博创科技Linux下的设备Linux的设备以文件的形式存在于/dev目录下设备文件是特殊文件,使用ls/dev-l命令可以看到:crw-------1rootroot10,7Aug312002amigamouse1crw-------1rootroot10,134Aug312002apm_biosbrw-rw----1rootdisk29,0Aug312002aztcd博创科技嵌入互动©2005博创科技主设备号和次设备号主设备号标识设备对应的驱动程序一个驱动程序可以控制若干个设备,次设备号提供了一种区分它们的方法系统增加一个驱动程序就要赋予它一个主设备号。这一赋值过程在驱动程序的初始化过程中intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);博创科技嵌入互动©2005博创科技创建设备节点设备已经注册到内核表中,对于设备的访问通过设备文件(设备文件与设备驱动程序的主设备号匹配),内核会调用驱动程序中的正确函数给程序一个它们可以请求设备驱动程序的名字。这个名字必须插入到/dev目录中,并与驱动程序的主设备号和次设备号相连使用mknod在文件系统上创建一个设备节点mknod/dev/mydevicec2540博创科技嵌入互动©2005博创科技动态分配设备号在Documentation/device.txt文件中可以找到已经静态分配给大部分设备的列表由于许多数字已经分配了,为新设备选择一个唯一的号码是很困难的如果调用register_chrdev时的major为零,函数就会选择一个空闲号码并做为返回值返回博创科技嵌入互动©2005博创科技动态分配的问题动态分配的主设备号不能保证总是一样的,无法事先创建设备节点可以从/proc/devices读取cat/proc/devices利用脚本动态创建设备文件节点博创科技嵌入互动©2005博创科技设备管理的问题如今,Linux支持很多不同种类的硬件。这意味着/dev中都有数百个特殊文件来表示所有这些设备。而且,这些特殊文件中大多数甚至不会映射到系统中存在的设备上博创科技嵌入互动©2005博创科技使用devfs在Linux2.4的内核里引入了devfs来解决linux下设备文件管理的问题在驱动程序中通过devfs_register()函数创建设备文件系统的节点系统启动的时候mount设备文件系统所有需要的设备节点都由内核自动管理。/dev目录下只有挂载的设备博创科技嵌入互动©2005博创科技Linux2.6内核与devfsLinux2.6内核引入了sysfs文件系统为每个系统的硬件树进行分级处理Devfs在Linux2.6中被标记为舍弃的特性(在Linux2.6.15及以后的版本则取消了对它的支持),而使用udev。维护动态设备从sysfs获得的信息,可以提供对特定设备的固定设备名。对于热插拔的设备,这尤其重要udev是在用户空间的脚本文件,这很容易被编辑和修改为了保证旧应用程序的兼容性,在嵌入式系统中,是用devfs还是一个好方法。即使在Linux2.6.15内核以后,也可以通过ndevfs(nanodevfs)补丁提供对devfs特性的兼容。博创科技嵌入互动©2005博创科技Linux内核硬件驱动标准模板#includelinux/module.h#includelinux/config.h#includelinux/init.hstaticint__initname_of_initialization_routine(void){/*codehere*/}staticvoid__exitname_of_cleanup_routine(void){/*codehere*/}module_init(name_of_initialization_routine);module_exit(name_of_cleanup_routine);博创科技嵌入互动©2005博创科技module_init(1)include/linux/init.h中#definemodule_init(x)__initcall(x);#define__initcall(fn)\staticinitcall_t__initcall_##fn__init_call=fnstaticinitcall_t__initcall_name_of_initialization_routine__init_call=name_of_initialization_routine博创科技嵌入互动©2005博创科技module_init(2)include/linux/init.h中定义:#define__init_call__attribute__((unused,__section__(.initcall.init)))typedefint(*initcall_t)(void);arch/arm/vmlinux-armv.lds.in文件中:__initcall_start=.;*(.initcall.init)__initcall_end=.;.=ALIGN(4096);__init_end=.;init/main.c文件中定义了do_initcalls函数博创科技嵌入互动©2005博创科技__init宏在include/linux/init.h中对于非模块加载的驱动程序:#define__init__attribute__\((__section__(.text.init)))通过__init,会把函数中的代码放到.text.init段。这个段在系统启动以后会被释放。在系统内核启动以后,会看到:Freeinginitmemory:68K博创科技嵌入互动©2005博创科技Linux设备驱动程序结构结构体file_operations的定义,在include/linux/fs.h中主要包括:open,close(或者release),read,write,ioctl,poll,mmap等博创科技嵌入互动©2005博创科技一个简单的Linux驱动程序原理应用程序驱动程序硬件相关的寄存器WriteIoctlReadIoctl博创科技嵌入互动©2005博创科技创建一个字符设备staticint__initpxa_Led_init(void){intret,i;Updateled();ret=register_chrdev(0,DEVICE_NAME,&pxa_fops);if(ret0){printk(DEVICE_NAMEcan'tgetmajornumber\n);returnret;}LedMajor=ret;#ifdefCONFIG_DEVFS_FSdevfs_Led_dir=devfs_mk_dir(NULL,led,NULL);devfs_Ledraw=devfs_register(devfs_Led_dir,0,DEVFS_FL_DEFAULT,LedMajor,LedRAW_MINOR,S_IFCHR|S_IRUSR|S_IWUSR,&pxa_fops,NULL);#endifprintk(DEVICE_NAMEinitialized\n);return0;}创建设备节点/dev/led/0博创科技嵌入互动©2005博创科技其他处理staticvoid__exitpxa_Led_exit(void){#ifdefCONFIG_DEVFS_FSdevfs_unregister(devfs_Ledraw);devfs_unregister(devfs_Led_dir);#endifunregister_chrdev(LedMajor,DEVICE_NAME);}module_init(pxa_Led_init);module_exit(pxa_Led_exit);博创科技嵌入互动©2005博创科技相关操作staticintpxa_Led_open(structinode*inode,structfile*filp){MOD_INC_USE_COUNT;DPRINTK(open\n);return0;}staticstructfile_operationspxa_fops={owner:THIS_MODULE,open:pxa_Led_open,write:pxa_Led_write,release:pxa_Led_release,};博创科技嵌入互动©2005博创科技点亮LEDstaticssize_tpxa_Led_write(structfile*file,constchar*buffer,size_tcount,loff_t*ppos){copy_from_user(&ledstatus,buffer,sizeof(ledstatus));Updateled();DPRINTK(write:led=0x%x,count=%d\n,ledstatus,count);returnsizeof(ledstatus);}博创科技嵌入互动©2005博创科技配置和编译脚本Linux内核的编译过程,是通过内核源码中根目录和各个子目录中的Makefile分级管理的。其中,根目录的Makefile是最重要的,它可以看成是Makefile最初的入口。Make脚本读取.config文件,并根据其信息最终生成vmlinux(elf格式的Linux内核)和modules(模块)。make通过向下递归调用子目录中的Makefile来编译这两个目标。博创科技嵌入互动©2005博创科技内核的kbuild脚本把驱动程序放到内核中,在编译内核的时候可以自由裁减。kbuild脚本随着Linux内核的发展,更新很快参考Documentation/kbuild目录下的相关文档博创科技嵌入互动©2005博创科技ARM的工作模式ARM处理器有7种操作模式:用户模式(usr)-正常的程序执行模式快速中断模式(fiq)-支持高速数据传输或通道处理中断模式(irq)-用于通用中断处理管理员模式(svc)-操作系统的保护模式.中止模式(abt)-支持虚拟内存和/或内存保护等异常系统模式(sys)-支持操作系统的特殊用户模式(运行操作系统任务)未定义模式(und)-支持硬件协处理器的软件仿真除了用户模式外,其他模式均可视为特权模式博创科技嵌入互动©2005博创科技ARM的寄存器(1)37个寄存器31个通用32位寄存器,包括程序计数器PC6个状态寄存器15个通用寄存器(R0toR14),以及2个状态寄存器和程序计数器(PC)在任何时候都中可见的可