2.1 概述
在早期的计算机系统中,串口是最为常见的也较为简单的外部通信接口,只是现在随着各种方便快捷的外部接口如USB接口、以太网接口的出现,串口的应用逐渐减少。但是串口因为调试简单在许多数据量不大的场合依然较为流行。除了简单的通信功能之外,对于一些简单的计算机系统如单板机等等,由于缺少键盘和显示器,则可以借助串口对目标机中操作系统的运行情况进行监控等等。下图为Tornado开发软件通过串口对目标机上运行的VxWorks操作系统进行监控的结构原理图。
图2.1 Tornado通过串口对vxWorks操作系统进行监控
如第一章所述,设备的驱动程序分为与硬件相关部分和硬件无关部分,硬件无关部分实现了一系列通用的数据接口,而硬件相关部分则负责具体的硬件实现。其中硬件无关部分实现是create、remove、open、close、read、write、ioctl等7个通用的函数接口。
这7个函数的原型在文件ioLib中进行定义。分别为:
上述这7个通用接口,也就是串口驱动的一个最根本的需求,即通过这几个函数就可以实现对串口控制芯片i8250的操作。下面进行详细分析。
2.2 VxWorks系统IO设备管理的数据结构
对VxWorks操作系统来说,在文件ioLib.c中实现了7个基本的操作函数create、remove、open、close、read、write、ioctl,使用这7个基本的函数,不但能够访问串口,而且还能够对网络、磁盘文件等多类设备进行访问。如图2.2.
图2.2 ioLib库提供的通用接口
而函数库iosLib中则是上述各个接口的较为底层的实现。它首先根据访问设备类型的不同(普通磁盘文件和硬件设备)而将其分为两类,并用不同的数据结构描述,这里主要对结构DRV_ENTRY进行分析。如图2.3。
图2.3 iosLib库提供的数据结构结构
数组drvTable的每个元素drvTable[i]对应一类设备,因此其下表i将是不同类型设备之间区分的一个重要的参考。由于0通常表示无效,因此drvTable[0]为空,不代表任何设备。图2.4给出了ioLib提供的通用函数和结构DRV_ENTRY的基本关系。图中的箭头表示调用关系,由调用者指向被调用者。
图2.4 ioLib库与iosLib库的内部关系
由于数组drvTable的每个元素drvTable[i]对应一类设备,如果要使用某一类设备中的一个,如系统中两个串口的一个,就必须指明是哪个串口。在函数库ioLib中的7个通用函数中都有一个重要的参数name,表明文件的路径或者设备的名称,该名称具有唯一性,也就是一个name指向唯一一个文件或者唯一一个设备。下面要引入的数据结构是设备描述符,如图2.5所示,它在函数库iosLib中定义。
图2.5 设备描述符
基本的设备描述符的数据结构为DEV_HDR,它描述了一些最为基本的信息,数组decTable的下标devnum和设备名称name,该设备序号即是ioLib库中使用的文件描述符fd。
综合起来设备描述符DRV_HDR、结构DRV_ENTRY以及7个通用函数库的关系可以用图2.6来描述。
图2.6 DRV_HDR、DRV_ENTRY及通用接口之间的关系
上述通用接口可以满足网卡、串口等外围设备的需要,对于具体的外部设备,则需要对图2.6所说的通用接口进行扩展,如图2.7所示,这个扩展主要采用了两种手段,一是继承,即通过对结构DEV_HDR进行继承得到如串口、终端以及网卡等特殊类型的设备;而是多态,也就是对一个通用的接口,根据具体设备的不同而采取不同的函数。C不是面向对象语言,没有明确的类、继承和多态的概念,但是可以灵活地使用C达到同样的目的。
图2.7 通用接口通过多态和继承得到扩展
图2.8则更为详细地描述了VxWorks操作系统对多个设备的管理。对每类设备的操作函数,系统创建了一个DRV_ENTRY结构数组来描述,每个DRV_ENTRY结构变量对应一类设备的操作函数,对某类设备的具体操作方式的设置则是通过设定DRV_ENTRY结构变量中的函数指针来实现的;对于同一类的设备,系统为其从DEV_HDR派生了一个数据结构,而每个结构变量则是对应该类设备中的每一个具体的设备,不同的设备通过双向链表链接在一起。
图2.8 VxWorks对系统中多个设备的管理结构