嵌入式LINUX设备驱动程序本讲主要内容1.嵌入式linux设备驱动的基本框架与实现2.数码管显示驱动程序分析3.帧缓冲显示驱动实现方法。Linux系统中的设备文件Linux将所有外部设备看成是一类特殊文件,称之为“设备文件”。如果说系统调用是Linux内核和应用程序之间的接口,那么设备驱动程序则可以看成是Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件在实现上的细节,使得应用程序可以像操作普通文件一样来操作外部设备Linux设备驱动在系统中的层次进程虚拟文件系统VFS普通文件设备文件设备逻辑空间到物理空间的映射设备A设备B物理输入/输出设备驱动层文件系统层应用层设备驱动的作用•设备驱动是位于应用和物理设备之间的软件层,负责完成二者间的I/O操作;•在LINUX系统里,对用户程序,设备驱动隐藏了设备的具体细节,为不同设备提供了一致的接口;•设备驱动是嵌入式系统开发的重要内容之一设备驱动完成的主要功能对设备的初始化和释放;将数据从内核传递到硬件设备,以及从硬件读取数据;读取应用程序传送给设备文件的数据,并回送应用程序请求的数据。–这需要在用户空间、内核空间、总线以及外设之间传输数据;检查和处理设备出现的错误。设备驱动的分类Linux支持3种不同类型的设备字符设备块设备网络接口字符设备能够像字节流一样被访问的设备,一般不需要缓存技术,也不支持随机访问。典型的字符设备有:鼠标、键盘、I/O设备等。设备驱动的分类块设备支持面向块访问的设备,每块包含2的N次幂字节数据,典型的块大小为512或1024B;大多数块设备允许随机访问,而且常常采用buffer、cache等缓存技术;块设备的访问常常通过文件系统来进行;典型的块设备有:如磁盘、硬盘、光盘驱动器等。设备驱动的分类网络接口能够和其他主机交换数据的设备接口。网络接口只是面向数据包而不是数据流,它没有被映射为任何设备文件,其访问要通过BSD套接口进行。设备驱动的分类主设备号与次设备号LINUX系统中,设备由一个主设备号和一个次设备号来唯一标识;主设备号唯一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。一些典型设备的主设备号已经基本固定下来,例如,软驱的主设备号为2,IDE硬盘的主设备号为3等等。设备宏操作MAJOR()可以获取主设备号。主设备号与次设备号次设备号用于标识使用同一设备驱动程序的不同硬件,并仅由设备驱动程序解释当应用程序操作某个设备文件时,Linux内核根据其主设备号调用相应的驱动程序,并从用户态进入内核态驱动程序判断次设备号,并完成相应的硬件操作。用户空间和内核空间Linux运行在2种模式下内核模式用户模式内核模式对应内核空间,而用户模式对应用户空间。驱动程序作为内核的一部分,它对应内核空间,应用程序不能直接访问其数据,用户空间和内核空间用户空间和内核空间的数据传递:copy_to_user(void*to,constvoid*from,unsignedlongcount);copy_from_user(void*to,constvoid*from,unsignedlongcount);__copy_to_user()__copy_from_user()第一种方法在复制数据的同时,会进行地址空间的有效性检查。内核设备驱动模块设备驱动程序以两种方式添加到内核:Buildin直接把驱动程序编译到内核代码中Module以模块的方式加载驱动程序加载:insmoddriver.o卸载:rmmoddriver查看:lsmodLinux设备节点的添加设备节点位于/dev路径下可用如下命令添加dev节点,b代表块设备,C表示字符设备mknod/dev/hda1b(/c)1271块设备,主设备号127,次设备号1Linux设备驱动代码的分布/char:字符设备驱动源码;/block:块设备驱动源码;/cdrom:LinuxCDROM驱动源码。这里可以找到某些特殊的CDROM设备(如SoundblasterCDROM)。IDE接口的CD驱动位于drivers/ide/ide-cd.c中而SCSICD驱动位于drivers/scsi/scsi.c中。/pci:它包含了PCI伪设备驱动源码。这里可以找到关于PCI子系统映射与初始化的代码。Linux设备驱动代码的分布/scsi:所有的SCSI代码以及Linux支持的SCSI设备的设备驱动。/net:包含网络驱动源码。/sound:所有的声卡驱动源码。/video:所有的视频卡驱动源码。19Linux设备驱动的代码结构驱动程序的注册与注销设备的打开与释放设备的读写操作设备的控制操作设备的中断和轮询处理等设备驱动的注册方法1•result=devfs_register_chrdev(0,MOD_NAME,&keypad_fops)•if(result0){return-ENODEV;}•devfs_handle=devfs_register(NULL,MOD_NAME,,result,0,,&keypad_fops,NULL);–MOD_NAME:设备驱动的名称;keypad_fops:文件操作指针。–优点:无需手动创建设备节点,由系统自动完成。–对于块设备的注册,具有类似函数:devfs_register_blkdev以及操作过程。设备驱动的注册方法2•result=register_chrdev(0,MOD_NAME,&keypad_fops);•或者result=register_blkdev(0,MOD_NAME,&keypad_fops);•if(result0){return-ENODEV;}•mknod/dev/MOD_NAMECMAJORMINOR设备驱动的注销方法1•devfs_unregister_chrdev(MAJOR,MOD_NAME);•或者devfs_unregister_blkdev(MAJOR,MOD_NAME);•devfs_unregister(devfs_handle);方法2•unregister_chrdev(Major,MOD_NAME);•或者unregister_blkdev(Major,MOD_NAME);•rm/dev/MOD_NAME设备的打开与释放设备的打开与释放要通过文件操作结构体中定义的相关函数open()和release()来完成;主要完成设备的初始化工作以及设备的释放。文件操作结构体的定义structfile_operations{structmodule*owner;ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);文件操作结构体的定义int(*open)(structinode*,structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*,intdatasync);};文件操作结构体的主要函数open:用于打开文件设备release:在关闭文件的调用read:用于从设备中读取数据write:向设备发送数据poll:查询设备是否可读或可写ioctl:提供执行设备特定命令的方法fasync:用于设备的异步通知操作设备的读写字符设备的读写操作可以直接使用read()和write()函数来完成,块设备的读写,需要调用block_read()和block_write()函数,这2个函数向设备请求表中增加读写请求;块设备的读写是对内存缓冲区进行操作,如果内存缓冲区中没有要读入的数据,或者缓冲区中的数据需要写入设备,可以通过调用数据结构blk_dev_struct中的函数request_fn()完成。设备的控制操作对设备的控制操作可通过文件操作数据结构中的ioctl()函数来完成。控制操作与具体的设备有密切关系,需要根据设备实际情况进行具体分析。设备的轮询和中断处理轮询方式对于不支持中断的硬件设备,读写时需要轮流查询设备的状态,以便决定随后的数据操作。如果轮询处理方式的驱动程序被链接到内核,则意味着查询过程中,内核一直处于闲置状态。解决办法是使用内核定时器,进行定期查询。设备的轮询和中断处理中断处理方式硬件在需要的时候,向内核发出中断请求信号,而内核则负责把中断信号传递给相应的设备驱动。驱动程序必须在设备驱动的初始化时申请中断资源,并注册中断处理函数:–申请:request_irq(irq,irq_handler,flag,dev_name,dev_id);–释放:free_irq(irq,dev_id);其中irq为申请的中断线号,irq_handler为注册的中断处理函数,dev_id用于共享中断信号线。典型字符驱动程序框架staticstructfile_operationsdemo_fops={read:demo_readwrite:demo_writeopen:demo_open……};intinit_module(void){returndemo_init();}用于注册设备驱动、申请中断线,初始化等voidcleanup_module(void){demo_cleanup();}用于注销设备驱动、释放中断线等接口函数的实现……Example1PXA270开发系统的按键驱动程序与数码显示驱动数码显示源代码按键驱动源代码调试驱动程序,使用printk输出信息,信息的优先级依次为:1.KERN_EMERG2.KERN_ALERT3.KERN_CRIT4.KERN_ERR5.KERN_WARNING6.KERN_NOTICE7.KERN_INFO8.KERN_DEBUG如果要在终端输出信息,编译选项:-Wall–O2根据/kernel/printk.c的不同设置,不显示的信息会出现在/var/log/messages中驱动调试方法参见LinuxDeviceDriverschapter3帧缓冲设备驱动程序LCD分类LCD可由为液晶照明的方式有两种:传送式和反射式传送式屏幕要使用外加光源照明,称为背光(backlight),照明光源要安装在LCD的背后。传送式LCD在正常光线及暗光线下,显示效果都很好,但在户外,尤其在日光下,很难辩清显示内容。反射式屏幕,则不需要外加照明电源,使用周围环境的光线(或在某些笔记本中,使用前部照明系统的光线)。这样,反射式屏幕就没有背光,所以,此种屏幕在户外或光线充足的室内,才会有出色的显示效果,但在一般室内光线下,这种显示屏的显示效果就不及背光传送式的。帧缓冲(Framebuffer)帧缓冲(framebuffer)-显示缓冲区是Linux为显示设备提供的一个接口,是把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。Xwindow绘制窗口,显示汉字都是通过fb帧缓冲设备Linux可支持最多32个/dev/fb0~/dev/fb31缺省是/dev/fb0可以直接拷贝位图到fb来显示到屏幕cp/bitmap/penguin.bin/dev/fb0帧缓冲设备驱动原理帧缓冲设备属于字符设备,其目的就是通过配置PXA270的寄存器,在一段指定的内存与LCD之间建立一个自