Chinaunix首页 | 论坛 | 博客
  • 博客访问: 930046
  • 博文数量: 376
  • 博客积分: 154
  • 博客等级: 入伍新兵
  • 技术积分: 1558
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-13 08:42
文章分类

全部博文(376)

文章存档

2014年(11)

2013年(88)

2012年(260)

2011年(17)

分类:

2012-04-23 14:03:03

一、Overview
    KDriver类提供了一个设备驱动程序的框架。这个类的职责包括初始化驱动程序,把I/O请求传递给它们的目标设备对象。
    KDriver是一个抽象类,驱动编写人员必须写一个新的类来继承它。新的子类必须重写DriverEntry函数,这是当系统载入驱动程序时框架要调用 的函数。这个派生类会有一个构造函数,但是构造函数不允许有参数。一般来说,最好不要写这么一个构造函数。把一切的初始化工作放在DriverEntry 函数里来进行。对于任何给定的驱动,只能有一个KDriver类的实例存在,而且这个实例是框架自动创建的。任何调用者可以通过调用 DriverInstance静态函数来获得一个指向这个唯一实例的指针。
    驱动编写人员必须做的另一件事情是通知框架:哪个类作为驱动框架来提供服务。框架提供了定义了一个宏定义DECLARE_DRIVER_CLASS来实现 这个事情。任何一个驱动程序里都必须调用一次且只能调用一次这个宏,一般是在拥有DriverEntry函数的组件里。这个宏必须放在任何函数之外,因为 它事实上声明一个可以被框架调用的函数。宏的参数应该是继承了KDriver类的那个类的名字。
    除了DriverEntry,KDriver还有其他两个成员函数来做初始化的工作:RequestReinitialization() 和 Reinitialize()。一个驱动程序在DriverEntry函数里调用RequestReinitialization()函数来指示系统调用 Reinitialize()函数,作为第二级初始化。而Reinitialize()函数是一个可被重写的虚函数。
    如果想要系统具有卸载(Unload)驱动的功能,就要在function.h文件里做一个声明:#define DRIVER_FUNCTION_UNLOAD。这是虚函数Unload()的得到声明。这个函数在基类中的实现代码调用了 DeleteDevices() 函数来为这个驱动程序创建的每个设备调用析构函数。
    其他成员函数对驱动程序创建的设备进行管理。GetDeviceListHead()函数取得驱动创建的一系列设备对象中的第一个(head)。 DeleteDevices()遍历列表,并为每一个设备对象调用析构函数。IsDevicePresent()使驱动程序确认设备是否已经被删除。
    尽管通常情况下框架会将I/O请求直接派遣给目标设备对象,KDriver还是保留了一个特性:可以派生一个成员函数在I/O请求被派遣给设备对象之前对 它进行检测或预处理。这个特性通过调用EnableDispatchFilter()函数来启用,这会使所有的I/O请求被重写的 DispatchFilter()函数处理后再派遣。另外,要启用这个特性,还必须在function.h文件中声明:#define DRIVER_FUNCTION_DISPATCH_FILTERING。
    总结:不要在KDriver的子类中定义构造函数和析构函数,把初始化工作放在DriverEntry函数里,清理工作放在Unload函数里。
   
二、Member Functions
   
1、DriverEntry (Initializes the driver)
    框架自动调用这个函数进行初始化工作,所有继承KDriver的子类必须重写这个函数。
【函数原型】
    NTSTATUS DriverEntry( PUNICODE_STRING RegistryPath );
【Parameters】
    RegistryPath      系统储存驱动信息的注册表路径
【Returns】
    返回值用来说明驱动的初始化工作是否成功。除了STATUS_SUCCESS之外的返回值将阻止系统加载这个驱动程序。如果这个函数里调用了
RequestReinitialization函数,那么不太可能返回一个STATUS_SUCCESS之外的值。
【Comments】
    DriverEntry的职责使初始化驱动程序。具体工作有三点:(1)从注册表中提取参数(2)保告和分配资源的使用(3)创建设备对象。如果可能的 话,系统会把驱动的参数储存在注册表中相应的驱动目录下的Parameters键里。KRegistryKey类封装了操作注册表的方法。
    资源使用情况报告有助于冲突检测。在两个驱动同时要求分配资源时,这个报告将会成为系统有限给哪个驱动分配资源的一个依据。关于更多的报告和分配资源的细节,可以参看KResourceRequest和KResourceAssignment类中的描述。
    这个函数在PASSIVE_LEVEL级别上运行。
2、AddDevice (Plug and Play entry point called to configure new device object (WDM only))
    当即插即用子系统检测倒该驱动对应的设备时,框架会自用调用这个函数。
    前提是必须在function.h文件里声明#define DRIVER_FUNCTION_ADD_DEVICE
【函数原型】
    NTSTATUS AddDevice( PDEVICE_OBJECT PnPDeviceObject );
【Parameters】
    PnPDeviceObject   一个指向系统创建的设备对象的指针,来表示检测到的设备。
【Returns】
    成功就返回TRUE。
【Comments】
    这个函数里进行什么操作依赖于驱动的类型。迷你驱动(minidriver)由于在类驱动(class driver)或总线驱动(bus driver)的上层,一般会创建一个设备对象并把它压到设备堆栈里,这个堆栈是物理设备所属的总线所拥有的。
    对大多数驱动,参数PnPDeviceObject代表物理设备对象(PDO)。PDO是物理设备在在操作系统中的设备表现形式。系统用对象来模仿对设备 的即插即用操作,比如使设备开始(starting)、停止(stopping)、把设备删除(removing)、减低电源消耗(powering down)。
    AddDevice经常创建一个新的设备对象叫做功能设备对象(FDO)。FDO的目的是使应用程序和更高级别上的驱动程序能够通过PDO控制设备。 PDO是物理设备在驱动程序里的表现形式。因此,对于同样的物理设备有两种设备对象来表示:PDO是为了系统的使用,FDO是为了驱动的使用。
    为了创建FDO,驱动要创建一个KPnpDevice类的派生类的实例。KPnpDevice类和KDevice类很相似,但是增加了更强大的处理IRP_MJ_PNP和IRP_MJ_POWER这两种IRP的功能。
    为了建立一个PDO和FDO之间的联系,AddDevice还经常要创建一个KPnpLowerDevice类的实例,或者它的派生类的实例。这个实例的 创建将FDO连接到PDO,或者连接到最后一个设备,而这个设备连接到了PDO。然后驱动就可以处理发给PDO的IRP。在表示FDO的类的实例中嵌入一 个KPnpLowerDevice类或者它的子类的实例,是一个不错的选择。
对于有些类型的迷你驱动,比如说HID(人机交互设备),这个参数代表的就是FDO而不是PDO。这是因为类驱动在迷你驱动上设置了钩子(hooks),钩住了迷你驱动的AddDevice入口并创建了FDO。详细请看KPnpLowerDevice类。
3、Unload (Clean up)
    当系统卸载驱动时框架自动调用这个函数
    前提是必须在function.h文件中声明 #define DRIVER_FUNCTION_UNLOAD
【函数原型】
    VOID Unload( void );
【Comments】
    这是一个虚函数。它在基类中的实现代码中调用了DeleteDevices()函数。
    在function.h文件中声明 DRIVER_FUNCTION_UNLOAD 使系统可以卸载驱动。但并不强制重写这个函数,因为它不是一个纯虚函数。换句话说,如果你定义了DRIVER_FUNCTION_UNLOAD而不重写函 数,也可以得到默认的卸载行为(删除设备)。但是如果你有其他的需要,就要重写这个函数。如果重写了这个函数,它应该调用子类的函数或者自己调用 DeleteDevices()函数。
    Unload函数应该负责释放驱动程序中申请的所有对象占用的内存。
    如果驱动在初始化时通过 KResourceRequest 请求了资源,那么在Unload函数里应该释放资源,细节参看ReleaseResources()函数。
    总之,Unload要确保在卸载驱动时释放所有的系统对象(包括内存)和未处理的事件(它们会使系统调用驱动程序)。最好的做法是析构顶级对象,而不是析构它们包含的子级对象。
    这个函数运行在PASSIVE_LEVEL级别上。
4、Reinitialize (Secondary initialization operations)
    框架调用这个函数作为先前调用RequestReinitialization函数的回复。Reinitialize函数给当前驱动提供了一个在系统其他驱动都初始化完毕之后再进行二次初始化的机会。
    前提是必须在function.h文件中声明#define DRIVER_FUNCTION_REINITIALIZATION。
【函数原型】
VOID Reinitialize(
   PVOID Context,
   ULONG Count
);
【Parameters】
    Context     是传给RequestReinitialization函数的参数。
    Count       指这个函数调用的次序号,系统每调用一次这个函数就会给传进来的这个参数加1。
【Comments】
    这是一个纯虚函数(pure virtual function),在继承KDriver的子类里必须提供这个函数。
    需要前后两次初始化的驱动程序把这个函数安排在DriverEntry()函数中调用了RequestReinitialization函数以后执行。Context参数是调用RequestReinitialization函数时提供的参数传递过来的。
    这个例程可以通过成员变量m_RegistryPath来控制注册表路径。
    这个例程可以在必须进行额外的初始化时调用RequestReinitialization函数。
    这个函数运行在PASSIVE_LEVEL级别上。
5、RequestReinitialization (Request secondary initialization)
    这个函数的作用是:在系统其他驱动都初始化完毕后,使当前驱动可以执行一些额外的初始化代码。
    前提是必须在function.h文件中声明#define DRIVER_FUNCTION_REINITIALIZATION。
【函数原型】
    VOID RequestReinitialization( PVOID Context );
【Parameters】
    Context       任意类型的参数,最终是要传递给Reinitialize函数处理的,默认是NULL。
【Comments】
    一些其行为依赖于其他驱动的驱动程序,必须有能力在其他程序初始化之前和之后两次进行初始化。如果驱动有这样的需求,它可以在DriverEntry里进 行一部分初始化,然后把另一部分初始化工作安排在后来调用的代码中。RequestReinitialization的调用使框架在所有驱动都执行完它们 的DriverEntry函数后调用Reinitialize()函数。
    这个函数需要在DriverEntry函数里调用。调用这个函数的话,DriverEntry必须返回STATUS_SUCCESS。如果还有额外的初始化操作,这个函数还有可能被Reinitialize函数调用。
    这个函数只能运行在PASSIVE_LEVEL级别上。
    支撑它的系统服务是IoRegisterDriverReinitialization。
6、DisableUnload (Disable unloading of this driver)
    这个函数将驱动对象的Unload例程的地址设为NULL。
    前提是在function.h文件中声明#define DRIVER_FUNCTION_UNLOAD 。
【函数原型】
    VOID DisableUnload( void );
【Comments】
    调用这个函数是为了阻止驱动被卸载。如果当前的设备状态下,当前驱动程序不能够被完美的卸载掉,那么调用这个函数是有用的。驱动只能等到随后调用了EnableUnload()函数后才可以被卸载。
7、EnableUnload (Enable unloading of this driver)
    这个函数把Unload例程的地址写回系统驱动对象。
    前提是在function.h文件中声明#define DRIVER_FUNCTION_UNLOAD 。
【函数原型】
    VOID EnableUnload( void );
【Comments】
    在调用DisableUnload函数之后,用这个函数可以重新允许驱动被卸载。
8、GetDeviceListHead (Get head of list of devices created by driver)
    获得一个指向驱动创建的处于设备列表最前面的那个设备对象的指针。
【函数原型】
    KDevice* GetDeviceListHead( void );
【Returns】
    返回一个指向当前驱动创建的KDevice派生类实例的指针。如果驱动没有创建任何设备对象,函数返回NULL。
【Comments】
    驱动创建的设备对象集合被连成一个链表,链表最前面的那个(the head of the list)存储在驱动对象里。创建的这个链表可以使最后被创建的设备处于链表的最前端(the head)。
    这个函数可以在任何中断级别上调用(any IRQL)。
9、DeleteDevices (Delete all devices created by driver)
    函数删除驱动创建的所有设备对象。
【函数原型】
    VOID DeleteDevices( void );
【Comments】
    这个函数遍历当前驱动的设备列表,分别调用它们的析构函数并释放它们的存储空间。需要注意的是KDevice的析构函数是虚函数,一般来说一个驱动需要通 告Unload()函数来间接调用它。DeleteDevices也可用于清除一个在DriverEntry里初始化失败的驱动程序。
    这个函数运行在PASSIVE_LEVEL级别上。   
10、ReleaseResources (Releases resources claimed by driver)
    用于释放驱动申请的资源。
【函数原型】
    NTSTATUS ReleaseResources( void );
【Returns】
    返回STATUS_SUCCESS表示资源释放成功。
【Comments】
    这个函数可以释放任何通过 KResourceRequest::Submit 为驱动分配的资源。如果资源是被一个或一些特殊的设备申请的,你必须调用KDevice::ReleaseResources来释放。
    支撑它的系统调用是IoAssignResources。
    这个函数只能在PASSIVE_LEVEL级别上调用。
11、EnableDispatchFilter (Enable dispatch filter)
    这个函数可以使能或制止所有的I/O请求调用一个可重写的DispatchFilter函数。
    前提是必须在function.h文件中声明#define DRIVER_FUNCTION_DISPATCH_FILTERING 。
【函数原型】
    VOID EnableDispatchFilter( BOOLEAN enable );
【Parameters】
    enable        为TRUE就允许I/O请求调用DispatchFilter,为FALSE就不允许。
【Comments】
    默认情况下,类库框架将所有派遣者调用(Read, Write, DeviceControl, etc.) 直接对应到目标设备对象的一个成员函数上。然而,当派遣过滤器被使能后,框架会调用虚函数DispatchFilter。这种做法只有在KDriver的 DispatchFilter()函数被重写的情况下才有意义。这种机制允许驱动类(the driver class)在所有的I/O请求被传递给KDevice子类之前对它们进行预处理。
    这个函数可能在任何中断级别上被调用(any IRQL)。
12、DispatchFilter (Overridable dispatch entry point)
    这是一个可以被重写的虚函数,实现对发送给派遣函数的I/O请求进行预处理。
【函数原型】
NTSTATUS DispatchFilter(
   KDevice* pDevice,
   KIrp I,
   NTSTATUS (KDevice::*func)(KIrp)
);
【Parameters】
    pDevice         I/O请求发送的目标类KDevice派生类的实例的地址
    I               I/O请求
    func            一般情况下*pDevice里处理该请求的成员函数的地址
【Returns】
    返回值是一种STATUS_xxxx形式的常量,用来告知I/O管理器I/O请求的当前状态。
【Comments】
    当派遣过滤器使能后,框架会将所有派遣者调用连到这个KDriver类的可重写的函数。这使驱动类(the driver class)可以先于所有KDevice子类的函数看到I/O请求,并且可能提供一个单一的处理点来监听驱动接收到的所有I/O请求。
    这个函数负责处理I/O请求,或者它自己处理,或者传递给设备对象(the device object)提供的函数处理。它必须返回一个I/O请求的状态给I/O管理器。
    它的基类的实现代码中只是简单的将I/O请求传递给了设备类(the device class):
NTSTATUS KDriver::DispatchFilter(
   KDevice* pDevice,
   KIrp I,
   NTSTATUS (KDevice::*func)(KIrp)
)
{
   return (pDevice->*func)(I);
}
13、DriverObject (Accessor to retrieve pointer to system driver object)
    获得一个和KDriver派生类实例一致的系统对象指针。
【函数原型】
    PDRIVER_OBJECT DriverObject( void );
【Returns】
    当系统最初调用驱动时,返回一个用来传给框架使用的系统驱动对象指针。
    这个函数决不会返回NULL。
【Comments】
    当调用一个系统服务,用到系统驱动对象指针时,就会用到这个函数。这个指针存储在KDriver子类的唯一实例中。
    这个函数可能在任何中断级别上被调用(any IRQL)。
   
14、RegistryPath (Accessor to retrieve registry path)
【函数原型】
    PUNICODE_STRING RegistryPath( void );
【Returns】
    返回一个字符串指针来说明系统存储与当前驱动相关信息的注册表键。
【Comments】
    在系统最初调用驱动时,这个函数相当于一个存储在框架里的成员变量的存取器(accessor)。
    这个函数可能在任何中断级别上被调用(any IRQL)。但是,这个字符串一旦被存在分页内存里,它就只能在PASSIVE_LEVEL级别上被引用。
15、DriverInstance (Accessor to return pointer to single instance of KDriver)
【函数原型】
    KDriver* DriverInstance( void );
【Returns】
    返回KDriver派生类的唯一实例的地址。
【Comments】
    KDriver类的构造函数不允许多于一个的派生类实例出现。框架会自动创建这个实例。这个函数返回这个实例的地址。如果需要,我们可以放心的把返回值强制转换成KDriver派生类的类型。
    这个函数可能在任何中断级别上被调用(any IRQL)。
16、IsDevicePresent (Test if a device still exists or has been deleted)
    测试一个设备仍然存在着还是已经被删除。
【函数原型】
    BOOLEAN IsDevicePresent( PDEVICE_OBJECT pDevice );
【Parameters】
    pDevice       被测试的系统设备对象的地址
【Returns】
    如果被测试的设备对象存在于驱动创建的设备列表中,就返回TRUE;否则返回FALSE。
【Comments】
    参数是一个系统设备对象的指针。KDevice类重载了一个类型转换操作符(cast operator)使得编译器可以自动把一个KDevice派生类的实例转化成PDEVICE_OBJECT类型。
    一个驱动程序只能测试它自己创建的设备对象。
    这个函数可能在任何中断级别上被调用(any IRQL)。
17、DECLARE_DRIVER_CLASS (Macro to declare the driver class to the framework)
    宏定义指明哪个类作为驱动程序框架。
【宏原型】
    DECLARE_DRIVER_CLASS( cpp_classname, driver_class_string )
【Parameters】
    cpp_classname        驱动类的名字(the driver class)
    driver_class_string  一个没有终止符的Unicode字符串(null terminated),指明驱动的类,用来存放系统注册表资源信息,可为NULL。
【Comments】
    框架创建的每个驱动都指定一个类作为驱动类(the driver class)。这个类继承自KDriver类,并且当系统最初调用驱动时,框架会自动创建一个此类的唯一实例。
    框架要求驱动编写人员用DECLARE_DRIVER_CLASS宏定义来指明驱动类。这个宏调用必须出现在一个声明了这个类的源文件的模块里(通常是一个包含文件in an include file)。这个宏调用必须在所有{}之外。
    这个宏调用实际上定义了一个函数,动态分配一块非分页内存区给指定类的实例。这个宏定义假定这个类有一个缺省的构造函数,由于这个原因,这个类不能定义一个带参数的构造函数。
 
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/kaizitop/archive/2008/03/22/2206680.aspx
阅读(754) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~