找回密码
 注册会员
更新自动建库工具PCB Footprint Expert 2024.04 Pro / Library Expert 破解版

[嵌入式/ARM] 嵌入式Linux系统设备驱动程序的开发

[复制链接]
admin 发表于 2013-3-25 05:53:59 | 显示全部楼层 |阅读模式

本文包含原理图、PCB、源代码、封装库、中英文PDF等资源

您需要 登录 才可以下载或查看,没有账号?注册会员

×
引言
   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的系统更为迅速和有效。
   参考文献:
   1.  AlessandroRubmi,JonathanCorbet.Linux设备驱动程序,第3版,中国电力出版社,2006.2
   2.  毛德操、胡希明,Linux内核源代码情景分析,浙江大学出版社,2001
   3.  周立功、陈明记、陈渝,ARM嵌入式linux系统构建与驱动开发,北京航空航天大学出版社,2006年1月第1版
   4.  李祥兵、郑扣根,Linux中I2C总线驱动程序的开发,计算机工程与设计,2005;26(1)
   5.  郑伟、王钦若,Linux内核空间设备驱动程序的开发,微计算机信息,2003;19(12)
*滑块验证:
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

QQ|手机版|MCU资讯论坛 ( 京ICP备18035221号-2 )|网站地图

GMT+8, 2024-12-26 23:22 , Processed in 0.055179 second(s), 8 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表