分类: LINUX
2014-09-18 02:25:51
原文地址:linux字符驱动学习 作者:sun5411
在Linux中,字符设备和块设备的I/O操作是有区别的。块设备在每次硬件操作时把多个字节传送到主存缓存中或从主存缓存中把多个字节信息传送到设备中;而字符设备并不使用缓存,信息传送是一个字节一个字节地进行的。
Linux操作系统允许设备驱动程序作为可装载内核模块实现,这也就是说,设备的接口实现不仅可以在Linux 操作系统启动时进行注册,而且还可以在Linux 操作系统启动后装载模块时进行注册。
总之,Linux操作系统支持多种设备,这些设备的驱动程序有如下一些特点:
(1)内核代码:设备驱动程序是内核的一部分,如果驱动程序出错,则可能导致系统崩溃。
(2)内核接口:设备驱动程序必须为内核或者其子系统提供一个标准接口。比如,一个终端驱动程序必须为内核提供一个文件I/O接口;一个SCSI设备驱动程序应该为SCSI子系统提供一个SCSI设备接口,同时SCSI子系统也必须为内核提供文件的I/O接口及缓冲区。
(3)内核机制和服务:设备驱动程序使用一些标准的内核服务,如内存分配等。
(4)可装载:大多数的Linux操作系统设备驱动程序都可以在需要时装载进内核,在不需要时从内核中卸载。
(5)可设置:Linux操作系统设备驱动程序可以集成为内核的一部分,并可以根据需要把其中的某一部分集成到内核中,这只需要在系统编译时进行相应的设置即可。
(6)动态性:当系统启动且各个设备驱动程序初始化后,驱动程序将维护其控制的设备。如果该设备驱动程序控制的设备不存在也不影响系统的运行,此时的设备驱动程序只是多占用了一点系统内存罢了。
二.设备驱动程序与外界的接口
每种类型的驱动程序,不管是字符设备还是块设备都为内核提供相同的调用接口,故内核能以相同的方式处理不同的设备。Linux为每种不同类型的设备驱动程序维护相应的数据结构,以便定义统一的接口并实现驱动程序的可装载性和动态性。
Linux设备驱动程序与外界的接口可以分为如下三个部分:
(1)驱动程序与操作系统内核的接口:这是通过数据结构file_operations来完成的。
(2)驱动程序与系统引导的接口:这部分利用驱动程序对设备进行初始化。
(3)驱动程序与设备的接口:这部分描述了驱动程序如何与设备进行交互,这与具体设备密切相关。
可归结为如下图2:三.设备驱动程序的组织结构
设备驱动程序有一个比较标准的组织结构,一般可以分为下面三个主要组成部分:
(1)自动配置和初始化子程序
这部分程序负责检测所要驱动的硬件设备是否存在以及是否能正常工作。如果该设备正常,则对设备及其驱动程序所需要的相关软件状态进行初始化。这部分程序仅在初始化时被调用一次。
(2)服务于I/O请求的子程序
该部分又可称为驱动程序的上半部分。系统调用对这部分进行调用。系统认为这部分程序在执行时和进行调用的进程属于同一个进程,只是由用户态变成了内核态,而且具有进行此系统调用的用户程序的运行环境。故可以在其中调用与进程运行环境有关的函数。
(3)中断服务子程序
该部分又可称为驱动程序的下半部分。设备在I/O请求结束时或其它状态改变时产生中断。中断可以产生在任何一个进程运行时,因此中断服务子程序被调用时不能依赖于任何进程的状态,因而也就不能调用与进程运行环境有关的函数。因为设备驱动程序一般支持同一类型的若干设备,所以一般在系统调用中断服务子程序时都带有一个或多个参数,以唯一标识请求服务的设备。
四.设备驱动程序的代码
设备驱动程序是一些函数和数据结构的集合,这些函数和数据结构是为实现管理设备的一个简单接口。操作系统内核使用这个接口来请求驱动程序对设备进行I/O操作。甚至,我们可以把设备驱动程序看成是一个抽象数据类型,它为计算机中的每个硬件设备都建立了一个通用函数接口。由于一个设备驱动程序就是一个模块,所以在内核内部用一个file结构来识别设备驱动程序,而且内核使用file_operatuions结构来访问设备驱动程序中的函数。
了解设备驱动程序代码的如下几个部分:
◆ 驱动程序的注册与注销。 ◆ 设备的打开与释放。 ◆ 设备的读写操作。
◆ 设备的控制操作。 ◆ 设备的中断和轮询处理。
五、字符设备驱动程序的代码
1、了解什么是字符设备
2、了解字符设备的基本入口点
字符设备的基本入口点也可称为子程序,它们被包含在驱动程序的file_operations结构中。
① open()函数;② release()函数;③ read()函数;④ write()函数;
⑤ ioctl()函数;⑥ select()函数。
3、字符设备的注册
设备驱动程序提供的入口点在设备驱动程序初始化时向系统登记,以便系统调用。Linux系统通过调用register_chrdev()向系统注册字符型设备驱动程序。
register_chrdev()定义如下:
#include
#include
int register_chrdev(unsigned int major, const char *name, struct file_operations *ops);
其中major时设备驱动程序向系统申请的主设备号。如果它为0,则系统为该驱动程序动态地分配第一个空闲的主设备号,并把设备名和文件操作表的指针置于chrdevs表的相应位置。name是设备名,ops是对各个调用入口点的说明。register_chrdev()函数返回0表示注册成功;返回-EINVAL表示申请的主设备号非法,一般主设备号大于系统所允许的最大设备号;返回-EBUSY表示所申请的主设备号正被其它设备驱动程序使用。如果动态分配主设备号成功,则该函数将返回所分配的主设备号。如果register_chrdev()操作成功,则设备名就会出现在/proc/devices文件中。
字符设备注册以后,还必须在文件系统中为其创建一个代表节点。该节点可以是在/dev目录中的一个节点,这种节点都是文件节点,且每个节点代表一个具体的设备。不过要有主设备号和从设备号两个参数才能创建一个节点。还可以是在devfs设备文件目录下的一个节点,对于这种节点应根据主设备号给每一种设备都创建一个目录节点,在这个目录下才是代表具体设备的文件节点。