现在的位置: 首页 > 综合 > 正文

设备驱动程序学习(二)设备驱动程序的开发

2012年10月07日 ⁄ 综合 ⁄ 共 2504字 ⁄ 字号 评论关闭

一、Linux device driver 的概念

 

系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。

 

通常LINUX驱动程序接口分为如下四层:

1)应用程序进程与内核的接口;

2)内核与文件系统的接口;

3)文件系统与设备驱动程序的接口;

4)设备驱动程序与硬件设备的接口。

 

设备驱动程序是内核的一部分,它完成以下的功能:

 

1、对设备初始化和释放;

 

2、把数据从内核传送到硬件和从硬件读取数据;

 

3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据;

 

4、检测和处理设备出现的错误。

 

二、设备文件

 

在Linux操作系统下有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。

 

上一节已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件节点inode,有文件属性(c/b),表示是字符设备还是块设备?另外Linux的设备inode有文件属性Struct kdev_t,由一个主设备号(major number)和一个次设备号(minor number)标识。主设备号唯一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。次设备号仅由设备驱动程序解释,用于识别同类设备中,I/O请求所涉及到的那个设备。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。

 

用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统访问设备表device_struct数组,通过设备文件的主设备号找到相应的file_operations,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写函数,填充file_operations的各个域,并将这个file_operations和某个主设备号映射起来。这是通过register_chrdev实现的。

 

register_chrdev向系统注册字符型设备驱动程序。register_chrdev定义为:

#include<linux/fs.h>

#include<linux/errno.h>

int register_chrdev (unsigned int major,const char *name,struct file_operations *fops);

 

其中,major即为设备驱动程序向系统申请的主设备号,如果为0则系统为此驱动程序动态地分配一个主设备号。name是设备名。fops就是前面所说的对各个调用的入口点的说明。此函数返回0表示成功。返回-EINVAL表示申请的主设备号非法,一般来说是主设备号大于系统所允许的最大设备号。返回-EBUSY表示所申请的主设备号正在被其它设备驱动程序使用。如果是动态分配主设备号成功,此函数将返回所分配的主设备号。如果register_chrdev操作成功,设备名就会出现在/proc/devices文件里。

 

 

 

三、设备驱动程序开发方法:

 

方法一:直接修改系统核心的源代码,把设备驱动程序加进核心里

 

1.确定设备的设备名称和主设备号:

找一个还没有被使用的主设备号,分配给自己的字符设备。

2.确定编写需要的file_operations中的操作函数。

3.将上述的file_operations中的操作函数的地址赋给某个file_operations

的结构变量my_fops中的相应域;然后调用标准内核函数登记该设备:

register_chrdev(10,"mychd",&my_fops);最后对必要的变量(例如读写忙标志、跟踪标志等)赋初值。*/

4.在drivers/char/mem.c中添加相应语句;

在chr_dev_init函数之前添加drgn_init的原型说明:

void my_init (void);

在chr_dev_init函数的return语句之前添加以下语句:

my_init (); /*用于在字符设备初始化时初始化新设备*/

5.修改drivers/char/Makefile;

假设我们把所有必要的函数写mychd.c中,则找到“L_OBJS:= tty_io.o n_tty.o console.o /”行,将“mychd.o”加到其中。

6.将该设备私有的*.c,*.h复制到目录drivers/char下。

7.用命令:make clean;make dep;make zImage重新编译内核。

8.用mknod命令在目录/dev下建立相应主设备号的用于读写的特殊文件。

完成了上述步骤,你在linux环境下编程时就可以使用新设备了。

 

 

方法二:把设备驱动程序作为可加载的模块

 

在LINUX里,除了直接修改系统核心的源代码,把设备驱动程序加进核心里以外,还可以把设备驱动程序作为可加载的模块,由系统管理员动态地加载它,使之成为核心地一部分。也可以由系统管理员把已加载地模块动态地卸载下来。具体的LINUX核心模块开发步骤见链接。

 

在成功的向系统注册了设备驱动程序后(调用register_chrdev成功后),就可以用mknod命令来把设备映射为一个特别文件,其它程序使用这个设备的时候,只要对此特别文件进行操作就行了。

 

 

最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。

抱歉!评论已关闭.