第四章 OS Environment自行开发驱动程序的方法
1. 概述
上文中,我们用大量的篇幅介绍了OS Environment所提供的接口以及OSKit去包装其它系统的驱动程序的方法。那么我们是否可以在OS Environment下开发自己的驱动程序集呢?回答是肯定的,原因也是很简单的,OS Environment为我们提供了足够强大的接口,让我们去访问系统的资源,只要我们去调用这些接口,然后根据硬件设备的说明书去写驱动程序就可以了。
然而,有一点在这里是需要考虑的,那就是OS Environment的设计意图是什么,因为这个环境的设计意图将直接影响到在这个环境中去开发驱动程序的效果。按照OSKit设计者的说法,OS Environment并非是为了新写的驱动程序去设计的,因此,OS Environment中关于驱动程序的思想也就不会是很先进的,同样,也不会有其它的系统为兼容OS Environment而去重新改进自己,因此如果想重写很多设备的驱动程序的话,那么最好的选择可能不是在OS Environment下开发,可以选择去兼容驱动程序的DDI/DKI标准或UDI等标准,因为这些标准从设计的思想上来说就是作为通用的驱动程序接口,它们会考虑到更多的系统对它们的兼容性等方面的问题。另外,也可以选择Linux等系统作为开发平台,因为这些系统内核的实现细节是公开的,更有利于写某些特殊的设备的驱动程序。
由于在什么平台上写驱动程序已经超出了本文要讨论的范围,因此这里我们不再多说,我们还是仔细讨论一下在OS Environment这个环境下,一个驱动程序应当如何去写。
2. 基本方法
写驱动程序是一件非常复杂的事情,不仅要对操作系统的知识有所了解,还要了解,还要对计算机的体系结构,以及所要驱动的设备非常的熟悉。一个驱动程序究竟是写成支持标准的POSIX标准呢,还是仅仅为了一些较为特殊的应用,按照自己的要求去写,这些都是非常复杂的问题,对于驱动程序的作者来说,是必须要考虑好的。
一个驱动程序,通常是要调用操作系统提供的接口,并且为操作系统使用此设备提供一套接口。由于OS Environment最初的设计目的与设计思路,它是与UNIX类的系统的操作系统关于驱动程序的设计思想保持兼容的。UNIX的一个基本特征就是它抽象了设备的处理,所有的硬件设备都与常规的文件十分相似,可以通过与操作文件完全一样的标准系统调用去打开、关闭、读、写一个设备。如果你希望你的操作系统最终能够让用户按照类UNIX的方式去访问设备,那么为了达到这个目的,驱动程序就必须为操作系统提供这样的功能。但是,如果你希望操作系统的用户可以用其它的更为简单或者更加出色的方式去访问设备,那么你也可以按照自己的方式去写。这完全是由目标操作系统及应用来决定的。
如果已经决定了要写一个驱动程序,希望它所提供的接口是类UNIX的,那么这个驱动程序所要提供的接口就可以在前面第二章中找到,因为实际上OS Environment对外提供的接口都是要驱动程序来支持的。
但无论你的驱动程序的具体接口按照哪一种方法去实现,都有一些调用时必须要提供给系统的,现在我们就来讨论一下这些必须提供的调用的问题。
3. 驱动程序必须实现的部分
3.1 OS Environment中驱动程序的接口及数据结构
3.1.1 基本的驱动程序接口
OS Environment中,所有的设备驱动程序最终都将抽象为一个统一的接口,通过这个接口,通过这个接口,可以获得设备的基本信息。这个COM接口的名称是oskit_driver。这个接口的定义如下:
struct oskit_driver_ops {
OSKIT_COMDECL (*query)(oskit_driver_t *drv,
const struct oskit_guid *iid,
void **out_ihandle);
OSKIT_COMDECL_U (*addref)(oskit_driver_t *drv);
OSKIT_COMDECL_U (*release)(oskit_driver_t *drv);
OSKIT_COMDECL (*getinfo)(oskit_driver_t *drv,
oskit_devinfo_t *out_info);
};
这个接口中,除了三个所有COM接口都支持的方法以外,有一个getinfo方法,这个方法可以获得驱动程序的信息,驱动程序的信息会被存储在out_info所指向的内存单元中。关于oskit_devinfo_t这个结构,我们会在下一节中介绍。
这个接口是一个基类,当一个驱动程序要去实现这个接口的时候,要继承这个接口,并且提供一个新的方法probe。probe方法的作用是对驱动程序进行初始化,检测系统中是否存在此设备,如果存在,则要把设备注册到系统总线上,把设备的信息注册到系统COM数据库中等工作。
3.1.2 表示驱动程序信息的数据结构
在上面的函数中,用到了一个数据结构oskit_devinfo,这个数据结构是用来记录驱动程序的基本信息的,在OS Environment中经常用到,并且如果编写一个驱动程序,则也要提供这个结构,因此,在这里详细介绍一下,这结构的定义如下:
struct oskit_devinfo {
const char *name;
const char *description;
const char *vendor;
const char *author;
const char *version;
};
这个结构中所有的指针所返回的字符串都应当是常量,并且对于设备节点存在时总能够使用。除了name以外的其它元素都可以是NULL以表示该信息不详。
name是设备的一个简单标识,例如"aha1542",name字符串的长度不能超过OSKIT_DEVNAME_MAX所定义的长度,在OSKit中,这个常量的缺省值是15。此字符串只能包含7位的字符,即字符的ASCII码值在0-127之间,并且第一位必须是字母。
description: 对于设备的详细描述,如"Adaptec 1542 SCSI controller"。
vender: 设备生产商的名字,例如"Adaptec"。
author: 设备驱动程序的作者。
version: 驱动程序的来源及版本,例如"Linux 1.3.36"。
3.1.3 基本的设备接口
在OSKit中,基本的设备接口是oskit_device,实现了这个接口的每一个对象对应着一个实际的设备。通常,一个设备驱动程序可以操纵多个设备,但设备驱动程序对象与设备对象之间是没有关系的,设备对象并非驱动程序对象的一部分。当然,如果你愿意,也可以将OS Environment改写成那样。设备接口的方法的定义如下:
struct oskit_device_ops {
OSKIT_COMDECL (*query)(oskit_device_t *dev,
const struct oskit_guid *iid,
void **out_ihandle);
OSKIT_COMDECL_U (*addref)(oskit_device_t *dev);
OSKIT_COMDECL_U (*release)(oskit_device_t *dev);
OSKIT_COMDECL (*getinfo)(oskit_device_t *dev,
oskit_devinfo_t *out_info);
OSKIT_COMDECL (*getdriver)(oskit_device_t *dev,
oskit_driver_t **out_driver);
};
出了基本的COM方法以外,此接口有两个自己的方法,一个是getinfo,通过这个方法可以得到设备的信息。设备接口的getinfo方法和驱动程序接口的getinfo方法基本上是相同的,但是,同一个设备的设备接口和驱动接口所返回的信息也并不一定要完全相同,例如,可以让驱动程序接口返回"aha15xx",以表示此驱动程序支持一系列的设备,而让设备接口返回"aha1542"以表示此设备的具体型号。
getdriver方法将一个指向该设备的驱动程序的指针放入out_driver所指向的内存单元中。
3.2 OS Environment中驱动程序的注册方法
驱动程序的注册,包括驱动程序本身的注册和设备的注册。驱动程序本身的注册先进行,而设备注册等程序则应当写在驱动程序的probe代码中,在检测设备的时候进行注册。
驱动程序本身的注册可以通过OS Environment所提供的接口来进行,这个接口已经在前面第二章介绍过了,注册驱动程序的方法是driver_register,注销驱动程序的方法是driver_unregister。
OS Environment也为注册设备而提供了接口,它们是device_register和device_unregister。设备除了要注册到COM对象的数据库中之外,还应当注册到系统的总线上,这样,OS Environment就可以实现一个设备树。
3.3 OS Environment中驱动程序注册、自检的全过程
OS Environment中驱动程序的注册、自检的过程,可以说是COM在OSKit中的应用的一个很好的表现。在OSKit中,用来表示驱动程序的COM数据库有两个,一个是driver_registery,另一个是device_registery。如上一小节所述,要首先将驱动程序注册到driver_registery中,然后在对系统中所有设备进行检测时,再利用驱动程序的probe函数将相应的设备注册到device_registery中。下图表现了这一过程,并标明了各个工作分别应当在何时完成。
在上图中isa_bus_probe()及pci_bus_probe() 的部分是由OS Envrionment提供的,在进行检测的时候,他们并不知道这个系统支持多少设备(注册了多少驱动),完全通过驱动程序的COM数据库,来获取这个信息。
上图中,背景色较深的方框表示要由驱动程序的作者实现的部分,而其余则是OS Envrionment的功能。可以看出,驱动程序的作者除了程序本身的功能外,要提供一个初始化驱动和一个检测设备的方法。这两个程序的写法,可以参考OSKit包装Linux或FreeBSD 的驱动程序时使用的代码。