流Mini驱动开发(译自Microsoft DDK)

来源:互联网 发布:淘宝无法注销怎么办 编辑:程序博客网 时间:2024/06/05 10:25
本节详细讨论了已经过时的Stream.sys Class Driver。随着Microsoft Windows XP的正式发布,Microsoft建议硬件制造商考虑使用下一代Class模型——AVStream——来开发新的,非音频多媒体驱动程序。相关细节可参看《AVStream Overview》一章。Microsoft现在只对Stream.sys提供基本的支持。如果你要开发一个音频Minidriver,请参看《Audio Miniport Drivers》一章。
很多种设备都通过提供流式Minidriver来得到(操作系统的)支持。尤其是视频捕捉设备,譬如数码相机和DVD播放机,它们都是通过提供流式Minidriver来获得支持的,类似的还有外部音频设备。更多介绍可参考《Video Capture Minidriver Design Guide》和《Vendor-Supplied DVD Drivers》。
流Minidriver支持内核流。在此我假定本文的读者已经熟悉了内核流的相关基本概念。如果你不明白,请先阅读《Kernel Streaming Overview》
设计Stream Class Driver的目的,就是通过处理许多和操作系统进行交互的细节,来使流式设备驱动的开发更加简单。
Minidriver让Stream Class Driver代表它处理同步问题。举个例子,Stream Class Driver可以为Minidriver随意地串行化I/O请求。通过让Class Driver为它处理同步问题,Minidriver可以做到多处理器平台安全,但是代码却是不可重入的。这样很适合后端(low-end)到中端(medium-end)的硬件。
Class Driver会自动对文件操作进行同步。例如,通过互斥体(Mutex)、信号量(Semaphores)或者事件(Event),打开流和打开设备的操作可以被正确地串行化,无需Minidriver的参与。
Class Driver从Minidriver中抽象了内核流的具体实现细节。
Class Driver处理所有和PnP管理器之间的交互动作。例如:
Class Driver代表Minidriver创建功能性设备对象(Functional device object)。
Class Driver管理资源设置(如翻译端口地址,翻译并映射内存范围,连接中断)
Class Driver处理PnP IRP包。比如IRP_MN_START_DEVICE, or IRP_MN_STOP_DEVICE。
所有的低级缓冲区管理也是由Class Driver完成的。
如果必要的话,分配DMA适配器对象。
映射缓冲区,并为DMA创建scatter/gather列表。
为DMA和PIO正确锁定并回写(Locking and flushing)缓冲区
所有的IO控制码的正确性验证也是由Class Driver完成的。
Class Driver通过看门狗定时器为所有的请求打上时间戳。
Minidriver自己并不创建设备对象,它在必要的时候可以分享Class Driver的设备对象。这样可以节约对系统资源占用。
每个适配器只创建一个设备对象。而适配器所支持的Mulitiple SubDevice(称为流,Stream)则用WDM Streaming Pins来描述。
一、Class Driver 和 Minidriver 的定义
Class Driver是由微软提供的一种中间驱动(或称媒介驱动),用来在硬件厂商提供的Minidriver和操作系统之间提供一个简单的接口。Minidriver是一个硬件相关的DLL,它通过函数调用的方式,使用微软提供的Class Driver来完成大部分动作,它只提供和设备紧密相关的控制。
在WDM模型中,Minidriver向Class Driver注册所有和它相关联的硬件适配器(Hardware Adapter),然后Class Driver会创建一个文件对象(File Object)来描述向它注册的每个适配器。Minidriver使用Class Driver的设备对象(Device Object)来进行系统调用。用户态的客户代码可通过WDM流(WDM Streaming)访问Class Driver。
Class Driver和Minidriver之间的交互包括:
·   Minidriver并不创建它自己的设备对象(Device Object),而是在必要的时候共享Class Driver的设备对象,这样可以节约系统资源。
·  每个适配器(Adapter)只能创建一个设备对象。而适配器所支持的Mulitiple SubDevice(称为流,Stream)则用WDM Streaming Pins来描述。
 

二、Stream Class和 Minidriver之间的接口

流类接口(Stream Class Interface)主要由介于Class DriverMinidriver之间的一系列的函数调用组成。Class Driver对请求的流程(Request Flow)进行控制,当有必要对适配器硬件进行存取时,它就调用适配器的MinidriverClass Driver还负责对多处理器和中断同步作出响应。当Class DriverMinidriver都初始化完毕之后,Minidriver将处于一个被动的地位,它只能被Class Driver所调用,而绝大多数的调用都是非常低级的服务请求。

Minidriver来说,对命令和信息(Commands and Information)进行控制的最基本的机制就是流请求块(Streaming Request Block)。每个Minidriver都有一系列的SRB来对其某个特定的功能进行访问,而且一般说来,设备所支持的每种数据流都有相应的SRB与之对应。这些信息(SRB)通过操作系统控制的DMA缓冲区(它是一个环形队列)传递给设备。

一个SRB由一个命令码字段,以及与该命令码相关联的其他数据所组成。结构体HW_STREAM_REQUEST_BLOCK包含了和特定的SRB相关的所有信息。我们常常把这个结构体就简称为SRB,它体内还包含了一些作为对命令码的补充信息的其他参数。结构体HW_STREAM_REQUEST_BLOCK的定义如下:

typedef struct _HW_STREAM_REQUEST_BLOCK

{

ULONG                      SizeOfThisPacket;

SRB_COMMAND              Command;

NTSTATUS                   Status;

PHW_STREAM_OBJECT       StreamObject;

PVOID                        HwDeviceExtension;

PVOID                        SRBExtension;

 

union _CommandData

{

    PKSSTREAM_HEADER                                                  DataBufferArray;

    PHW_STREAM_DESCRIPTOR                                        StreamBuffer;

    KSSTATE                                                         StreamState;

    PSTREAM_PROPERTY_DESCRIPTOR                                             PropertyInfo;

    PKSDATAFORMAT                                              OpenFormat;

    struct _PORT_CONFIGURATION_INFORMATION *           ConfigInfo;

    HANDLE                                                       MasterClockHandle;

    DEVICE_POWER_STATE                                        DeviceState;

    PSTREAM_DATA_INTERSECT_INFO                             IntersectInfo;

} CommandData;

 

ULONG                                                       NumberOfBuffers;

ULONG                                                       TimeoutCounter;

ULONG                                                       TimeoutOriginal;

struct _HW_STREAM_REQUEST_BLOCK*                             NextSRB;

PIRP                                                         Irp;

ULONG                                                       Flags;

PVOID                                                       HwInstanceExtension;

 

union

{

    ULONG   NumberOfBytesToTransfer;

    ULONG   ActualBytesTransferred;

};

 

PKSSCATTER_GATHER        ScatterGatherBuffer

ULONG                       NumberOfPhysicalPages;

ULONG                       Reserved[2];

} HW_STREAM_REQUEST_BLOCK, *PHW_STREAM_REQUEST_BLOCK;

关于对以上其他参数的解释,参见DDK文档,这里不再赘述。

 

下图解释了流类(Stream Class)和Minidriver在初始化时所进行的交互动作。

·初始化适配器

所有的流式Minidriver函数并不一定非要和Minidriver的中断服务例程(Interrupt Service RoutineISR)同步,因而Minidriver的代码是不可重入的(Non-re-entrant,相应的概念叫做可重入代码,又叫做纯代码。关于可重入代码的概念,请参考西电版操作系统教材),亦即,当Minidriver正在执行一个线程的时候,不能再调用Minidriver内其他任何函数,包括中断服务例程在内。这种非可重入的特性即便在支持多处理器的Windows NT/2000系统下也是存在的,这样可以令Minidriver编写起来更加方便。为了达到这样的特性,流类驱动(Stream Class Driver)会在Minidriver体内任何例程被执行时,通过内核例程KeSynchronizeExecution屏蔽Streaming Minidriver的中断请求IRQ(和所有低优先级的IRQ)。如果想了解更多关于同步的信息,请参看MiniDriver同步一章。Streaming Minidriver可以在必要的时候调用WDM系统服务。但是,Minidriver自己并没有设备对象,他是利用ClassDriver的设备对象来完成系统调用的。绝大多数Minidriver是不需要进行WDM系统调用的,因为几乎所有必要的功能都已经被Class Driver所涵盖了,没有必要直接进行系统调用。

必须知道的是,当进行WDM系统服务调用时,所有的Minidriver的入口点都会在高于DISPATCH_LEVEL的中断优先级下被调用(注意不包括DISPATCH_LEVEL中断优先级)。但有一个例外,那就是StreamClassCallAtNewPriority例程,此函数允许在DISPATCH_LEVEL

PASSIVE_LEVEL中断优先级上进行系统服务调用,具体哪个优先级取决于调用时所指定的优先级别。如果要一劳永逸地修改这个对IRQL的调用限制,可以通过把HW_INITIALIZATION_DATA结构体中的BOOL型成员变量TurnOffSynchronization简单置为TRUE来实现

 

 

三、开始着手编写Stream Minidriver

设计Stream Class Driver的主要目标,就是处理两个工作,一是处理操作系统,操作系统包含了对复杂的对多处理器的支持,二就是对内核流进行支持。这样Minidriver只需要对它必须执行的,与设备相关的操作进行处理即可。Class DriverMinidriver分配内存空间,对Minidriver可能用到的NT内核资源进行登记,并(随意地)处理同步问题。

Class Driver通过一系列由Minidriver提供的回调函数和Minidriver进行通讯。大部分对流式Minidriver写的动作都发生在写这些回调函数的时候。

在本文中,我们所提到的每种由Minidriver提供的例程都命名为StrMin×××。根据下层硬件可能执行的不同功能的数目,Minidriver也许要为每个例程提供一到更多的版本。

一个典型的流驱动都会支持数种不同的数据流。例如,DVD播放器会产生音频和视频流。在内核流环境中,每个流都用一个pin来描述。(参见KS Filters, Pins and Node Topology for a description of pins)。

流类驱动会跟踪Minidriver上的每个pin,在Class Driver看来, pin的每种类型都是一个流。流,就好比Pin的类型,有可能会有多个实例。因为流可以接收I/O请求,所以驱动程序必须为每个流提供相关的回调函数。

下面所列的例程,Minidriver一般都会提供。在参考大全中会更加详细记录这些函数的细节。

·每个Minidriver都必须提供的例程

StrMiniCancelPacket

StrMiniReceiveDevicePacket

StrMiniRequestTimeout

StrMiniEvent

StrMiniInterrupt

·Minidriver为每个单独的流提供的例程。

StrMiniReceiveStreamDataPacket

StrMiniReceiveStreamControlPacket

StrMiniEvent

StrMiniClock

对于Minidriver来说,对数个不同的流使用相同的回调例程是可以的。回调例程可以通过调用它的流的参数来确定执行何种动作。

WDM驱动一样,Minidriver必须提供DriverEntry例程。MinidriverDriverEntry例程的主要任务,就是向Class Driver注册Minidriver

Class Driver代表Minidriver接收所有的I/O请求。为了获取完成此次请求所必须的信息,Class Driver创建一个流请求块(Stream Request BlockSRB)并把它传递给形如StrMin×××Packet的众多例程中的一个。总体的来说,Class Driver向某设备分派I/O请求就等同于向StrMiniReceiveDevicePacket例程传递流请求块SRB,它把请求发送到各个独立的流的StrMiniReceiveStreamDataPacket例程(适用于内核流的读写请求)或者StrMiniReceiveStreamControlPacket例程(适用于其他请求)。

通常说来,Class Driver会对所有的请求进行排队,然后每次发送一个请求给MinidriverMinidriver可以随意地进行它自己的同步操作;Minidriver负责对它目前无法立即处理的请求进行排队。详见Minidriver Synchronization

Minidriver必须提供两个额外的例程以对SRB进行操作。当Class Driver收到一个取消IRP包(Cancel IRP)时,就会去调用StrMiniCancelPacket,以此来通知Minidriver取消某个特定的包。Class Driver会跟踪Minidriver完成处理一个SRB所花费的时间。如果Minidriver花费了很长时间还没有处理完一个请求,Class Driver将会对该请求返回超时,同时调用StrMiniRequestTimeout例程。

当发生了一个硬件中断时,操作系统会照会Class Driver,然后Class Driver调用MinidriverStrMiniInterrupt例程去处理该中断。

四、处理流请求块SRB

操作系统会把所有对设备的I/O请求分发到Class Driver。通过向Minidriver传递SRBClass Driver轮流从Minidriver处获取硬件的相关信息,Class Driver通过设置SRB的命令成员来指定它要求完成的操作。

总体上看,整个Minidriver,和它包含的每个流,都可能会收到I/O请求。Minidriver必须提供StrMiniReceiveDevicePacket例程来处理整个设备范围内的请求(Device-wide requests)每个流必须支持两个用来处理I/O请求例程:一个用来处理数据请求的例程,还有一个例程用来处理控制请求。Class Driver会调用那个用来处理数据请求的回调例程:StrMiniReceiveStreamDataPacket,来处理所有对指定流的读写请求。所有其他对流的请求会传递给StrMiniReceiveStreamControlPacket例程进行处理。

如果Class Driver正在为Minidriver处理同步,那么它会把所有的流请求进行排队,然后依次把它们发送给Minidriver,每次只发送一个请求。Class Driver维护了三种相互独立的队列:一个设备请求队列,另外每个留都有一个数据请求队列和控制请求队列。Minidriver会通知说它已经准备好接收一个新的请求。每种请求队列所对应的、用来通知Class Driver的例程和参数如下表所示:

请求类型

例程

例程的通知类型参数

设备请求

StreamClassDeviceNotification

ReadyForNextDeviceRequest

流控制请求

StreamClassStreamNotification

ReadyForNextStreamControlRequest

流数据请求

StreamClassStreamNotification

ReadyForNextStreamDataRequest

Class Driver调用StrMiniReceive×××Packet例程时,它会把SRB转交给Minidriver。在Minidriver通知Class Driver,它已经对完成处理该请求之前,Minidriver中负责进行处理的例程对SRB有独占存取权限。

Minidriver完成对一个请求的处理后,它应当按如下方式通知Class Driver它已经完成对请求的处理:

1.设置SRB中表示当前请求状态的状态域;

2.调用例程StreamClassDeviceNotificationStreamClassStreamNotification,通知Class Driver说它已经完成对请求的处理。如果完成的是一个设备请求,Minidriver要使用DeviceRequestComplete作为通知类型参数来调用例程StreamClassDeviceNotification;如果完成的是一个流请求,Minidriver要用StreamRequestComplete作为通知类型参数调用例程StreamClassStreamNotification

3.如果Class Driver当前正在处理同步,并且如果与此同时Minidriver还没有通知Class Driver说它已经做好准备接收对当前类型队列(注意,前面说过有三种类型的请求队列)的下一个请求,那它现在应该立即这样做(通知Class Driver)。

Minidriver可以通过调用StreamClassCompleteRequestAndMarkQueueReady,把23两步合并一起做。

Minidriver是以异步的方式处理请求的。所以Class Driver也许会取消一个请求,或者让一个请求超时。为了达到这些目的,Minidriver必须分别提供一个StrMiniCancelPacket例程和一个StrMiniRequestTimeout例程。Class Driver会在他要取消,或者要超时一个请求的时候,调用这两个独立的例程。

当下层的I/O请求被操作系统取消,Class Driver就要取消请求。Class Driver会把那些处理时间过长的请求置为超时,那Class Driver又是如何知道一个请求有没有超时呢?原来,在SRB中有一个成员变量TimeoutCounter,它指定了此请求处理超过多少秒就算超时。Class Driver每秒将此变量减一,如果变量已经减为0,而Minidriver还没有完成该请求,Class Driver就会判定此请求超时,然后它会调用MinidriverStrMiniRequestTimeout例程。如果Minidriver要推迟很长时间再去处理一个请求,那Minidriver就应该把TimeoutCounter设为0,这样Class Driver不会误判当前请求超时。一旦Minidriver重新开始处理该请求,它应该重置SRB中的TimeoutCounter变量和TimeoutOriginal的值相等,Minidriver可以在请求超时之前重设TimeoutOriginal变量来改变耗时长度。详细信息参见HW_STREAM_REQUEST_BLOCK结构体。

 

五、Minidriver的初始化过程

当操作系统初次初始化Stream Class Minidrivers的时候会调用MinidriverDriverEntry例程。Minidriver必须调用StreamClassRegisterMinidriverClass Driver注册它自己。关于Stream Class MinidriversDriverEntry例程,请见第六部分。

Minidriver调用StreamClassRegisterMinidriver函数向Class Driver进行注册时,它会传递一个HW_INITIALIZATION_DATA结构,此结构会提供给Class Driver一些基本的信息,Class Driver通过这些信息来初始化Minidriver。这些信息包括,设备范围的回调函数(device-wide callbacks),StrMiniReceiveDevicePacketStrMiniCancelPacketStrMiniRequestTimeoutStrMiniInterrupt

HW_INITIALIZATION_DATA定义如下:

typedef struct HW_INITIALIZATION_DATA

{

    ULONG  HwInitializationDataSize;

    PHW_INTERRUPT  HwInterrupt;

    PHW_RECEIVE_DEVICE_SRB  HwReceivePacket;

    PHW_CANCEL_SRB  HwCancelPacket;

    PHW_REQUEST_TIMEOUT_HANDLER  HwRequestTimeoutHandler;

    ULONG  DeviceExtensionSize;

    ULONG  PerRequestExtensionSize;

    ULONG  PerStreamExtensionSize;

    ULONG  FilterInstanceExtensionSize;

    BOOLEAN  BusMasterDMA;

    BOOLEAN  Dma24BitAddresses;

    ULONG  BufferAlignment;

    BOOLEAN  TurnOffSynchronization;

    ULONG  DmaBufferSize;

    ULONG  Reserved[2];

} HW_INITIALIZATION_DATA, *PHW_INITIALIZATION_DATA;

下面我详细解释该结构中各个成员的意义。

·HwInitializationDataSize

说明本结构的大小,以字节计。

·HwInterrupt

函数指针,指向MinidriverStrMiniInterrupt例程。

·HwReceivePacket

函数指针,指向MinidriverStrMiniReceiveDevicePacket例程。

·HwCancelPacket

函数指针,指向MinidriverStrMiniCancelPacket例程。

·HwRequestTimeoutHandler

函数指针,指向MinidriverStrMiniRequestTimeout例程。

·DeviceExtensionSize

说明Minidriver的“设备扩展结构(Device Extension)”的大小,这样Class Driver才能知道要为它分配多大的buffer空间。Minidriver可以利用此空间来记录,那些对Minidriver来说是公有的,但是对外是私有的信息。Class Driver会把该buffer的起始指针分别存放到结构HW_STREAM_OBJECTHW_STREAM_REQUEST_BLOCKHW_TIME_CONTEXT,和 PORT_CONFIGURATION_INFORMATION的成员HwDeviceExtension中,然后再把这些结构传递给Minidriver,这样Minidriver就知道Class Driver为它分配的buffer在哪了。

·PerRequestExtensionSize

Class Driver传递给Minidriver的结构HW_STREAM_REQUEST_BLOCKSRB)中,其成员SRBExtension是一个指针,指向一块buffer,此buffer用来存放“流请求块扩展结构(SRB Extension)”,它由Class Driver分配,所以在开辟空间之前,Class Drive必须要知道这块空间有多大。这个大小就在这里设定。每个SRB都有一个扩展结构,所以每个SRB都会有这样的一块buffer

·PerStreamExtensionSize

结构HW_STREAM_OBJECT的成员HwStreamExtension是一个指针,指向一块buffer,此buffer用来存放“流扩展结构(Stream Extension)”,它由Class Driver分配,所以在开辟空间之前,Class Drive必须要知道这块空间有多大。这个大小就在这里设定。每个流都有一个流扩展结构,所以每个流都有一块这样的buffer

·FilterInstanceExtensionSize

Class Driver传递给Minidriver的结构HW_STREAM_REQUEST_BLOCKSRB)中,其成员HwInstanceExtension也是一个指针,指向存放“实例扩展结构(Instance Extension)”的buffer的起始地址,它照样由Class Driver分配,所以在此用FilterInstanceExtensionSize成员来设定该buffer的大小。对Minidriver的每个实例,都会有这样一个buffer

·BusMasterDMA

若此成员置为TRUE,则设备可以对MinidriverDMA buffer执行直接总线控制DMADirect bus master DMA)。

·Dma24BitAddresses

如果设备所使用的DMA硬件只支持对全部地址线(对X86来说是32位)的低24位进行访问,那么Minidriver应该置此变量为TURE

·BufferAlignment

设定DMA缓冲区的对齐要求,譬如,当值为4时标识DMA缓冲区要求按4字节边界对齐。

·TurnOffSynchronization

如果本成员设为TURE,那么Minidriver会处理它自己的同步;否则Class Driver将会进行同步处理。绝大多数Minidriver应该置此成员为FALSE,更多请参见Minidriver Synchronization(第十二部分)。

·DmaBufferSize

设定DMA缓冲区的大小,然后Class Driver会按大小开辟空间。Minidriver可以通过调用StreamClassGetDmaBuffer来得到此buffer的起始地址。因为这块空间是连续的、非分页内存,并且一旦分配,该空间对操作系统和其他驱动程序都不再可用,所以应该将其大小设定的尽可能小。

·Reserved[2]

系统保留,Minidriver应该忽略此成员。

注册完毕后,Class Driver会使用StrMiniReceiveDevicePacket来通知Minidriver初始化设备。它会向Minidriver发送SRB_INITIALIZE_DEVICE请求,并传递一个PORT_CONFIGURATION_INFORMATION结构(在SRB中含有一个指向该结构的指针),其中包含了必要的硬件信息。当该请求被处理完毕后,Minidriver会告诉Class Driver关于结构体HW_STREAM_DESCRIPTOR的大小(以字节计),待会它将用此结构来描述所有它的流。PORT_CONFIGURATION_INFORMATIONHW_STREAM_DESCRIPTOR的结构定义如下:

typedef struct _PORT_CONFIGURATION_INFORMATION

{

    ULONG  SizeOfThisPacket;

    PVOID  HwDeviceExtension;

    PDEVICE_OBJECT  ClassDeviceObject;

    PDEVICE_OBJECT  PhysicalDeviceObject;

    ULONG  SystemIoBusNumber;

    INTERFACE_TYPE  AdapterInterfaceType;

    ULONG  BusInterruptLevel;

    ULONG  BusInterruptVector;

    KINTERRUPT_MODE  InterruptMode;

    ULONG  DmaChannel;

    ULONG  NumberOfAccessRanges;

    PACCESS_RANGE  AccessRanges;

    ULONG  StreamDescriptorSize;

    PIRP  Irp;

    PKINTERRUPT  InterruptObject;

    PADAPTER_OBJECT  DmaAdapterObject;

    PDEVICE_OBJECT  RealPhysicalDeviceObject;

    ULONG  Reserved[1];

}PORT_CONFIGURATION_INFORMATION, PPORT_CONFIGURATION_INFORMATION;

 

PORT_CONFIGURATION_INFORMATION各成员解释如下:

·SizeOfThisPacket

  设定本结构的大小,由Class Driver负责填写该域。

·HwDeviceExtension

  指向Minidriver的设备扩展结构(Device Extension)的指针。Minidriver会藉此buffer记录一些对Minidriver来说是公有的,但是对外是私有的信息。此结构的大小是由Minidriver自己设定的,记录在HW_INITIALIZATION_DATA结构的DeviceExtensionSize成员中。当Minidriver调用StreamClassRegisterMinidriverClass Driver注册它自己的时候,该结构会作为参数一起传递。Class Driver会为这个扩展结构分配空间,并把一个指向该buffer的指针记录在结构HW_STREAM_OBJECTHW_STREAM_REQUEST_BLOCK HW_TIME_CONTEXT的成员HwDeviceExtension中,回传给Minidriver

·ClassDeviceObject

  Class Driver会为驱动程序的设备提供功能设备对象(Functional Device ObjectFDO),此成员保留一个指向该FDO的指针。

·PhysicalDeviceObject

  指向物理设备对象PDO的指针。它指向在Class Driver被添加到驱动程序堆栈前,位于栈顶的驱动程序所对应的物理设备对象PDOClass Driver添加到驱动程序堆栈之后,该成员就指向次栈顶的驱动程序对应的PDO)。驱动程序使用例程IoCallDriver和驱动程序堆栈进行通信时会用到该成员。RealPhysicalDeviceObject成员指向驱动程序的设备的实际PDO

·SystemIoBusNumber

  Class Driver负责用设备的系统总线ID号码(System Bus ID Number)填充该域。Bus 0就是主系统总线

·AdapterInterfaceType

  指定设备连接到系统所用的系统总线类型,譬如ISAEISAMicroChannelPCIBusPCMCIABus等。

·BusInterruptLevel

  设定总线的中端请求级IRQL,由Class Driver填写。

·BusInterruptVector

  设定设备所使用的中断向量,由Class Driver填写。

·InterruptMode

  设定中断状态,关闭(Latched)或者中断级敏感(LevelSensitive

·DmaChannel

  如果设备是连接到ISA总线上的,Class Driver会根据设备的DMA通道填写该成员。

·NumberOfAccessRanges

  AccessRange数组中包含的元素个数。

·AccessRanges

  ACCESS_RANGE型的结构体数组,每个元素描述了硬件资源的范围,譬如I/O端口范围和内存地址范围。

·StreamDescriptorSize

  Minidriver会用HW_STREAM_DESCRIPTOR结构的大小来填充该域。

·Irp

  指向发出导致此次SRB_INITIALIZE_DEVICE请求的IRP包的PnP设备。

·InterruptObject

  如果设备使用了中断,Class Driver会填写该成员以指向关联的中断对象。

·DmaAdapterObject

  如果设备使用了DMAClass Driver会填写该成员以指向关联的DmaAdapter对象。

·RealPhysicalDeviceObject

  指向驱动程序设备的PDO

·Reserved

  保留为系统之用,应该忽略之。

 

typedef struct _HW_STREAM_DESCRIPTOR

{

    HW_STREAM_HEADER  StreamHeader;

    HW_STREAM_INFORMATION  StreamInfo;

}HW_STREAM_DESCRIPTOR, *PHW_STREAM_DESCRIPTOR;

 

一旦Minidriver处理完毕那个请求(SRB_INITIALIZE_DEVICE),Class Driver使用例程StrMiniReceiveDevicePacket发送SRB_GET_STREAM_INFO请求。Minidriver收到请求后,就会提供关于它的所有流的信息,包括每个流的回调函数。

Class Driver处理流数据完毕后,它用StrMiniReceiveDevicePacket例程发送SRB_INITIALIZATION_COMPLETE请求,至此,Minidriver已经准备好开始处理对每个流的请求。

六、Minidriver的控制流程

下面要介绍的这几步,一般都和Minidriver的初始化、调用和卸载密切相关。将要用到的命令和结构在DDK的其他章节均有详细描述。

Minidriver的初始化、调用和卸载的步骤如下:

1PnP管理器枚举到Minidriver所支持的硬件适配器插入,然后PnP管理器通过检查注册表去解析所有相关的符号引用,并向I/O子系统发送请求。

2I/O子系统加载Minidriver,然后调用MinidriverDriverEntry例程,在此例程内将分配并初始化一个HW_INITIALIZATION_DATA结构。根据DriverEntry中的信息,创建一个文件对象(file object)。

3MinidriverDriverEntry例程接着会调用流类驱动(Stream Class Driver)的StreamClassRegisterMinidriver函数,并把HW_INITIALIZATION_DATA结构作为一个参数传给此函数。HW_INITIALIZATION_DATA结构提供了Minidriver中那些用来处理SRB的函数的地址。这样Minidriver就能相应来自Class DriverSRB包了。

4.初始化期间,流类驱动(Stream Class Driver)会调用Minidriver中负责接收SRB的主收包函数。此函数由HW_INITIALIZATION_DATA的成员HwReceivePacket所指定。HwReceivePacket是一个函数指针,参见StrMiniReceiveDevicePacket)。调用时,Class Driver会把SRB_INITIALIZE_DEVICE命令放在SRB包的Command域中。Minidriver收到该SRB包后就会执行相应的对硬件适配器的初始化动作。

5.接着,流类驱动(Stream Class Driver)再次调用步骤4中的函数,不同的是,这次发给Minidriver的命令是SRB_GET_STREAM_INFO,表示想获取所有由Minidriver支持的、流的信息。Minidriver收到此命令后,就会返回关于它所支持的,所有流的信息。

6.随后,流类驱动(Stream Class Driver)继续调用步骤4中的函数,这时的命令是SRB_OPEN_STREAM,如前所述,SRB中有一个指针成员StreamObject指向一个HW_STREAM_OBJECT结构,此结构描述了流对象的信息,而流就是由一个stream number来标识的(这个stream numberHW_STREAM_OBJECT结构的一个成员)。Minidriver接收到这个SRB包后,就会执行必要的硬件动作,打开指定的流。同时,Minidriver还告诉Class Driver,它已经安排哪两个函数分别对数据请求包(ReceiveDataPacket)和命令请求包(ReceiveControlPacket)进行处理。

7.至此初始化和打开流的工作已经完成,通过向Minidriver中,专门负责对Class Driver发来的数据请求进行处理(另外有一个函数专门对控制请求进行处理)的函数发送SRB_READ_DATA或者 SRB_WRITE_DATA命令,Class Driver可以完成向流中发送数据或者从流中接收数据的操作,此函数是由HW_STREAM_OBJECT结构的ReceiveDataPacket成员所指向的。

8.通过向Minidriver中,专门负责对Class Driver发来的控制请求进行处理的函数发送控制命令,Class Driver可以完成获取流属性、设置流属性以及其他的控制性操作,此函数是由HW_STREAM_OBJECT结构的ReceiveControlPacket成员所指向的。

9.当系统用毕一个流后,流类驱动会发送关闭流的命令SRB_CLOSE_STREAMMinidriver的主收包函数(此函数是由HW_INITIALIZATION_DATA结构的成员HwReceivePacket所指向的)。Minidriver收到该命令后,就会关闭对应的流。

10.待到要卸载(Uninitialize)适配器时,流类驱动发送SRB_UNINITIALIZE_DEVICE命令给Minidriver的主收包函数(此函数是由HW_INITIALIZATION_DATA结构的成员HwReceivePacket所指向的)。Minidriver收到该命令后,就会卸载设备。

七、Stream Class Minidriver的DriverEntry例程

DriverEntry例程用来初始化一个流类的Minidriver。此例程无论何时都是必须的。它的原型如下:

ULONG DriverEntry(

                    IN PVOID Argument1,

                    IN PVOID Argument2

);

其中参数Argument1Arguement2提供的是两个Context Value,在Minidriver调用StreamClassRegisterMinidriver时会用到这两个值的。对Windows2000及后续版本来说,Argument1指向一个DRIVER_OBJECT结构,而Arguement2指向注册路径

DriverEntry的返回值就是StreamClassRegisterMinidriver的返回值。

StreamClassRegisterMinidriver会执行大部分所必需的驱动初始化工作,流类MinidriverDriverEntry例程的主要任务就是分配HW_INITIALIZATION _DATA结构,并用驱动指定的(driver-specific)常量和入口点填充该结构。然后DriverEntry就应该调用StreamClassRegisterMinidriver例程。

八、支持多流(Multiple Streams)

MinidriverStrMiniReceiveDevicePacket例程响应SRB_GET_STREAM_INFO命令时,Minidriver会描述所有它支持的流的信息。SRBCommandData.StreamBuffer成员指向一个HW_STREAM_DESCRIPTOR结构。Minidriver需要用它所支持的流的信息回填这个结构(HW_STREAM_DESCRIPTOR的定义请见第7页)。

HW_STREAM_DESCRIPTOR由一个HW_STREAM_HEADER结构起头,此结构描述的是Minidriver所支持的流的数目,紧随其后的是一个HW_STREAM_INFORMATION类型的结构体数组,该数组中的每个元素都描述了一个独立的流的信息。Class Driver利用这个数组中的每个元素去处理KSPROPSETID_Pin属性集——数组的下标就是Pin的类型ID

HW_STREAM_HEADER的定义如下:

typedef struct _HW_STREAM_HEADER

{

    ULONG  NumberOfStreams;

    ULONG  SizeOfHwStreamInformation;

    ULONG  NumDevPropArrayEntries;

    PKSPROPERTY_SET  DevicePropertiesArray;

    ULONG  NumDevEventArrayEntries;

    PKSEVENT_SET  DeviceEventsArray;

    PKSTOPOLOGY  Topology;

    PHW_EVENT_ROUTINE  DeviceEventRoutine

    ULONG  Reserved[2];

} HW_STREAM_HEADER, *PHW_STREAM_HEADER;

HW_STREAM_INFORMATION的定义如下:

typedef struct _HW_STREAM_INFORMATION

{

    ULONG  NumberOfPossibleInstances;

    KSPIN_DATAFLOW  DataFlow;

    BOOLEAN  DataAccessible;

    ULONG  NumberOfFormatArrayEntries;

    PKSDATARANGE*  StreamFormatsArray;

    PVOID  ClassReserved[4];

    ULONG  NumStreamPropArrayEntries;

    PKSPROPERTY_SET  StreamPropertiesArray;

    ULONG  NumStreamEventArrayEntries;

    PKSEVENT_SET  StreamEventsArray;

    GUID*  Category;

    GUID*  Name;

    ULONG  MediumsCount;

    const KSPIN_MEDIUM*  Mediums;

    BOOLEAN  BridgeStream;

    ULONG  Reserved[2];

} HW_STREAM_INFORMATION, *PHW_STREAM_INFORMATION;

对大多数Minidriver来说,HW_STREAM_DESCRIPTOR结构中的数据是在编译期就确定下来的,如果是这种情况,那么Minidriver可以为该数据结构分配静态的空间。

Minidriver通过HW_STREAM_HEADER的成员变量Topology描述流之间的拓扑连接。Class Driver用该结构来为Minidriver处理处理KSPROPSETID_Topology属性集。


原创粉丝点击