导言
Linux是一个遵从POSIX规范的免费操作体系。具有BSD和SYSV的扩展特性。与其他操作体系比较,嵌入式Linux体系以其可应用于多种硬件渠道、内核高效安稳、源码敞开、软件丰厚、网络通信和文件办理机制完善等优秀特性而正被作为研讨热门,越来越多的研讨人员选用Linux渠道来开发自己的产品。Linux设备驱动程序在Linux内核源代码中占有很大份额,从2.0、2.2到 2.4版别的内核,源代码的长度日益添加,其实首要是设备驱动程序在添加。
设备驱动程序的编写
设备驱动程序是linux内核的一部分,是操作体系内核和机器硬件之间的接口,它由一组函数和一些私有数据组成,是衔接应用程序与详细硬件的桥梁。Linux的一个根本特点是它对硬件设备的办理抽象化,体系中的每一个设备都用一个特别的文件来表明。一切的硬件设备都像一般的文件相同看待,运用与操作体系相同的规范体系来进行翻开、读写和封闭。
在Linux 操作体系下有3类首要的设备文件类型:块设备、字符设备、网络设备。字符设备是指存取时没有缓存的设备。可像文件相同拜访字符设备,字符设备驱动程序担任完成这些行为。体系的控制台和并口便是字符设备的比方,它们能够很好地用“流”来描绘。块设备是文件体系的宿主,如磁盘。 Linux答应像字符设备那样读取块设备——答应一次传输恣意数目的字节。成果是,字符设备和块设备读取数方法共同。而网络设备不同于字符设备和块设备, 它面向的上一层不是文件体系而是网络协议层,是经过BSD套接口拜访数据。与设备相对应的是三类设备驱动程序,字符设备驱动程序、块设备驱动程序、网络设备驱动程序。
字符设备驱动程序、块设备驱动程序与网络设备驱动程序的结构体是不同的。
在linux 源代码linux/ include / linux/ fs. h中界说了字符设备和块设备驱动程序中有必要运用的file_operations结构,每个设备驱动都完成这个接口所界说的部分或悉数函数。跟着内核的不断晋级, file_operaTIons结构也越来越大,不同的版别的内核会稍有不同。file_operaTIons界说如下:
struct file_operaTIons{
int( * lseek) ( struct inode * , struct file * , off_t , int) ; int( *release) ( struct inode * , struct file * ) ;
int( * read) ( struct inode * , struct file * , char * , int) ; int( * fsync) ( struct inode *, struct file * ) ;
int( *write) ( struct inode * , struct file * , const char *, int) ; int( * fasync) ( struct inode * , struct file *, int) ;
int( * readdir) ( struct inode , struct file , void * , dilldir) ; int( *check_media_change) ( kdev_t dev) ;
int(*select) ( struct inode *, struct file * , int, select_table * ) ; int( * revalidate) ( kdev_t dev) ; };
int ( * ioctl) ( struct inode * , struct file *, unsigned int, unsigned long) ;
int( *mmap) ( struct inode * , struct file * , struct vm_area_struct * ) ;
int( * open) ( struct inode *, struct file *) ;
应用程序只要经过对设备文件的open、release、read、write、ioctl等才干拜访字符设备和块设备。用户自己界说好 file_operaTIons结构后,编写出设备实践所需求的各操作函数,关于不需求的操作函数用NULL初始化,这些操作函数将被注册到内核,当应用程序对设备相应的设备文件进行文件操作时,内核会找到相应的操作函数,并进行调用。假如操作函数运用NULL,操作函数就进行默许处理。
关于字符设备而言,llseek( ),read( ),write(),ioctl( ),open( ),release( )这些函数是不行缺的;关于块设备,open( ),release( ),ioctl(),check_media_change( ),revalidate( )是不行短少的。
网络设备结构体 net_device 界说在 includelinuxnetdevice.h 里,如下所示:
struct net_device
{
char name ; int (*init)(struct
net_device *dev);
unsigned short flags ; int (*open)
(struct net_device *dev);
unsigned long base_addr; int
(*stop)(struct net_device *dev)
unsigned int irq ; int
(*hard_start_xmit)(struct sk_buff *skb,
unsigned char dev_addr; struct
net_device *dev);
unsigned char addr_len; int
(*set_mac_address)( struct net_device
unsigned long trans_start; *dev,void* addr);
……
}
界说好net_device结构体后,依据实践情况编写操作函数,其间hard_start_xmit()函数是用来发送数据的,set_mac_address()是进行网络参数设置的。
当linux初始化时将调用初始化函数int device_init( ),该函数包含以下内容:
注册所用设备。linux用设备号来标识字符设备和块设备。设备号分为主设备号和从设备号,终究构成设备接点。设备节点在拜访字符设备和块设备的设备驱动程序时将运用。一般主设备号标识设备对应的驱动程序,大多数设备是“一个主设备号对应一个驱动程序”,如:虚拟控制台和串口终端由驱动程序4办理。次设备号由内核运用,用于确认设备文件所指的设备。字符设备和块设备注册时有必要先界说好设备号。
字符设备注册函数如下:
int register_chrdev(unsigned int major ,const char *name, struct file_oprations *fops);
其间 major是主设备号。
由于对网络设备驱动程序的拜访不需求设备节点,它的注册函数如下:
int register_netdev(struct net_device *dev)
注册设备所用的中止。中止在现代计算机结构中有重要的位置,操作体系有必要供给程序呼应中止的才能。一般是把一个中止处理程序注册到体系中去。操作体系在硬件中止发生后调用驱动程序的处理程序。
注册中止所用的函数如下:
int request_irq (unsigned irq,void(*handler)(int,void*,struct pt_regs*),unsigned long flags,const char*device,void* dev_id);
其间,irq是中止向量;handler是中止处理函数;flags是中止处理中的掩码;devices是设备名;dev_id是在中止同享运用的id。
当linux不运用该设备时,就要调用铲除函数void_devicie_exit ( ),它同初始化函数相对应的,首要是:
刊出设备,字符设备刊出函数如下:
int unregister_chrdev(unsigned int major ,const char *name, struct file_oprations *fops);
刊出中止,刊出中止所用的函数如下:
int free_irq (unsigned irq,void(*handler)(int,void*,struct pt_regs*),unsigned long flags,const char*device,void* dev_id);
开释资源,模块初始化和铲除函数选用module_init(device_init),module_exit(device_exit) 方式
编写服务子程序
服务于I/O恳求的子程序,又称为驱动程序的上半部分。调用这部分是由于体系调用的成果。这部分程序在履行的时分.体系仍认为是和进行调用的进程归于同一个进程. 仅仅用户态变成了核心态,具有进行此体系调用的用户程序的运转环境.因而能够在其间调用sleep等与进程运转环境有关的函数。
中止服务子程序,又称为驱动程序的下半部分。在Linux体系中.并不是直接从中止向量表中调用设备驱动程序的中止服务子程序,而是由Linux体系来接纳硬件中止,再由体系调用中止服务子程序。中止能够发生在任何一个进程运转的时分,因而在中止服务程序被调用的时分.不能依赖于仟何进程的状况,也就不能调用任何与进程运转环境相关的函数。由于设备驱动程序一般支撑同一类型的若干设备,所以一般在体系调用中止服务程序的时分,都带有一个或多个参数,以仅有标识恳求服务的设备。
设备驱动程序的运用
直接将驱动程序编译进linux内核
将设备驱动程序复制到 linux/drivers相关的子目录下,比方字符设备驱动程序 就放在linux/drivers/char下。
修正linux/drivers相关的子目录的Makefile,
如obj-$(config_dev_driver) +=dev_driver.o,这样在编译内核时将会编译dev_driver.c,生成 dev_driver.o.
对内核进行从头编译时,进行相关的装备,比方要运用AT91RM9200的UART,就要如下装备:
Character devices -》 Serial drivers -》AT91RM9200 serial port support
将驱动程序编译成驱动模块
在设备驱动程序中要有两个重要函数:
module_init(dev_init),module_exit(dev_exit)
运用相应的穿插编译器以及编译指令将驱动程序dev_driver.c编译成dev_driver.o 这样的动态驱动模块。运用insmod指令给体系装置驱动模块,假如在/dev目录下没有相应的设备文件,就能够运用mknod创立一个设备文件。运用 rmmod指令卸载驱动模块,设备文件的删去能够用rm指令。
结语
设备驱动程序的开发是在Linux环境中最杂乱的编程使命之一 。它需求和硬件打交道,简单引起体系溃散,并且很难调试。把握设备驱动程序的开发技能,将使得开发嵌入式Linux的体系更为敏捷和有用。