驱动程序

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

linux 驱动程序设计实验

一实验目的

1.了解LINUX操作系统中的设备驱动程序的组成

2.编写简单的字符设备驱动程序并进行测试

3.编写简单的块设备驱动程序并进行测试

4.理解LINUX操作系统的设备管理机制

二准备知识

1. LINUX下驱动程序基础知识

Linux抽象了对硬件的处理,所有的硬件设备都可以像普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。在Linux操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。块设备则是利用一块系统内存作为缓冲区,当用户进程对设备进行读写请求时,驱动程序先查看缓冲区中的内容,如果缓冲区中的数据能满足用户的要求就返回相应的数据,否则就调用相应的请求函数来进行实际的I/O操作。块设备主要是针对磁盘等慢速设备设计的,其目的是避免耗费过多的CPU时间来等待操作的完成。一般说来,PCI卡通常都属于字符设备。

我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载. 而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM (高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。当我们加载了设备驱动模块后,应该怎样访问这些设备呢?Linux是一种类Unix系统,Unix的一个基本特点是“一切皆为文件”,它抽象了设备的处理,将所有的硬件设备都像普通文件一样看待,也就是说硬件可以跟普通文件一样来打开、关闭和读写。

系统中的设备都用一个设备特殊文件代表,叫做设备文件,设备文件又分为Block (块)型设备文件、Character(字符)型设备文件和Socket (网络插件)型设备文件。Block设备文件常常指定哪些需要以块(如512字节)的方式写入的设备,比如IDE硬盘、SCSI硬盘、光驱等。而Character型设备文件常指定直接读写,没有缓冲区的设备,比如并口、虚拟控制台等。Socket(网络插件)型设备文件指定的是网络设备访问的BSD socket 接口。

设备文件都放在/dev目录下,比如硬盘就是用/dev/hd*来表示,/dev/hda表示第一个IDE 接口的主设备,/dev/hda1表示第一个硬盘上的第一个分区;而/dev/hdc 表示第二个IDE接口的主设备。对于Block和Character型设备,使用主(Major)和辅(minor)设备编号来描述设备。主设备编号来表示某种驱动程序,同一个设备驱动程序模块所控制的所有设备都有一

个共同的主设备编号,而辅设备编号用于区分该控制器下不同的设备,比如,/dev/hda1(block 3/1)、/dev/hda2(block 3/2 )和/dev/hda3( block3/3 )都代表着同一块硬盘的三个分区,他们的主设备号都是3,辅设备号分别为1、2、3。

所有已经注册(即已经加载了驱动程序)的硬件设备的主设备号可以从/proc/devices 文件中得到。使用mknod命令可以创建指定类型的设备文件,同时为其分配相应的主设备号和次设备号。

2.设备驱动程序的接口

每种类型的驱动程序,不管是字符还是块设备都为内核提供乡土的调用接口,故内核能以相同的方式处理不同的设备。LINUX为不同类型的设备驱动程序维护和各自的数据结构,以便定义统一的接口并实现驱动程序的可装载性和动态性。Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过include/linux/fs.h中的数据结构file_operations来完成的:

struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char *, size_t, loff_t *);

int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, struct poll_table_struct *);

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*flush) (struct file *);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, struct dentry *, int datasync);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

};

当应用程序对设备文件进行诸如open、close、read、write等操作时,Linux内核将通过file_operations结构访问驱动程序提供的函数。例如,当应用程序对设备文件执行读操作时,内核将调用file_operations结构中的read函数

3.设备驱动程序结构

驱动程序的注册与注销

在系统初启,或者模块加载时候,必须将设备登记到相应的设备数组,并返回设备的主驱动号,例如:对快设备来说调用refister_blkdec()将设备添加到数组blkdev中.并且获得该设备号. 并利用这些设备号对此数组进行索引。对于字符驱动设备来说,要用module_register_chrdev()来获得设备的驱动号.对这个设备的调用都用这个设备号来实现;而在关闭字符设备或者块设备时,则需要通过调用unregister_chrdev( )或

相关文档
最新文档