LonWorks现场总线设备驱动设计与实现
(2) 设备标识方式
本文引用地址:https://www.eepw.com.cn/article/83666.htmLinux设备由一个主设备号和一个次设备号标识。主设备号唯一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中相应表项的索引。次设备号仅由设备驱动程序解释,一般用于识别在若干可能的硬件设备中,I/O请求所涉及到的那个设备。值得一提的是次设备号还可以被分成几个部分用来区分子设备驱动程序和具体的设备。
(3) Linux设备驱动程序组成部分
Linux设备驱动程序可以分为三个主要组成部分:
●自动配置和初始化子程序。负责检测所要驱动的硬件设备是否存在和是否能正常工作。如果该设备正常,则对这个设备及其相关的、设备驱动程序需要的软硬件进行初始化。
● 服务于I/O请求的子程序。它们主要是对file_operations结构的各个入口点的实现。这部分的实现支持了文件系统的调用(如open,close, read等等)。
●中断服务子程序。在Linux系统中,并不是直接从中断向量表中调用设备驱动程序的中断服务子程序,而是由Linux系统来接收硬件中断,再由系统来调用中断服务子程序。
但是,这三个部分不是必须在每个驱动程序中必须具有的。
3.3 LonWorks现场总线网卡驱动程序
研究了Linux的设备管理以及设备驱动程序实现方法后,我们来设计LonWorks现场总线设备驱动程序,并对实现中的一些关键问题进行探讨。
(1) LonWorks现场总线网卡驱动程序
在驱动程序设计和开发中,我们一定要注意的问题是机制(Mechanism)与策略(Policy)的分离。这里所谓的机制是指我们的驱动程序提供的接口应该很忠实地反映设备的原始功能(bare function),而与应用无关。而策略是指一旦这个设备驱动程序为设备机制提供了相应的软件接口,那么应用程序开发人员就能按照特定的方式使用机制接口。可以说,在内核驱动程序开发过程中,所设计的数据结构,以及确定的接口命令都是为以后的应用策略提供的一种机制。而如前所述,这种机制在Unix类系统内部是通过一组固定的入口点来提供的。由于我们要开发的设备驱动程序是一个字符型的设备,所以接下来我们首先分析字符型设备驱动程序中常用的入口点:
● open入口点
打开设备准备I/O操作。对字符设备文件进行打开操作,都会调用设备的open入口点。open子程序必须对将要进行的I/O操作做好必要的准备工作,如清除缓冲区等。如果设备是独占的,即同一时刻只能有一个程序访问此设备,则open子程序必须设置一些标志以表示设备处于忙状态。
●release入口点
关闭一个设备。当最后一次使用设备终结后,调用release子程序。独占设备必须改变前由open子程序设置的标志,以便设备可再次被使用。
●read入口点
从设备上读数据。对于有缓冲区的I/O操作,一般是从缓冲区里读数据。对字符设备文件进行读操作将调用read子程序。
●write入口点
往设备上写数据。对于有缓冲区的I/O操作,一般是把数据写入缓冲区里。对字符设备文件进行写操作将调用write子程序。
● ioctl入口点
执行读、写之外的一些硬件控制操作。
●poll入口点
把对许多非阻塞操作的设备描述符集合起来,等待事件的发生,以便于集中检查,看数据是否可从设备读取或设备是否可用于写数据,这样就做到了所谓的多路复用。
以上入口点构成了设备驱动程序的三大组成部分中I/O请求的部分,在Linux中它们由file_operations结构来封装,并不是所有的字符设备驱动程序都必须提供以上每一个入口点的实现,如果设备驱动程序没有提供上述入口点中的某几个,系统会用缺省的子程序来代替。
由上面的描述可见,在内核设备驱动程序的设计中,相应的机制的提供主要是对设备入口点的选择和设计。
针对LonWorks现场总线网卡的特点,我们选择并实现了五个入口点,即open, release, read,write, ioctl。
对于open和release入口点由于设备特点,我们只需要控制设备驱动模块在使用时,不被异常释放即可。
接下来,我们将描述以上设计实现中与Linux内核相关的一些调用和问题。
(2) 对file_operations结构的初始化
file_operations结构是Linux操作系统中用于实现驱动程序的最重要的数据结构,我们已经在前面提到过,它对Linux提供I/O请求的子程序的一系列入口点进行了封装。该结构贯穿在整个驱动程序中,故我们在文件作用域内定义了它的一个变量,并对本程序中用到的入口点做了初始化,其代码如下:
struct file_operations lmdev_fops= {
NULL,
lmdev_read,
//把实现的lmdev_read函数指针赋给read入口点。
lmdev_write,
//把实现的lmdev_write函数指针赋给write入口点。
NULL,
NULL,
lmdev_ioctl,
//把实现的lmdev_ioctl函数指针赋给ioctl入口点。
NULL,
lmdev_open,
//把实现的lmdev_ open函数指针赋给open入口点。
lmdev_release,
//把实现的lmdev_release函数指针赋给release入口点。
NULL,
NULL,
NULL,
NULL,
};
对于lmdev-*函数的实现方法,我们将在后面做详细的讨论。
评论