二、Stream Class和 Minidriver之间的接口
流类接口(Stream Class Interface)主要由介于Class Driver和Minidriver之间的一系列的函数调用组成。Class Driver对请求的流程(Request Flow)进行控制,当有必要对适配器硬件进行存取时,它就调用适配器的Minidriver。Class Driver还负责对多处理器和中断同步作出响应。当Class Driver和Minidriver都初始化完毕之后,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 Routine,ISR)同步,因而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 Driver为Minidriver分配内存空间,对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都必须提供的例程
·Minidriver为每个单独的流提供的例程。
StrMiniReceiveStreamDataPacket
StrMiniReceiveStreamControlPacket
对于Minidriver来说,对数个不同的流使用相同的回调例程是可以的。回调例程可以通过调用它的流的参数来确定执行何种动作。
同WDM驱动一样,Minidriver必须提供DriverEntry例程。Minidriver的DriverEntry例程的主要任务,就是向Class Driver注册Minidriver。
Class Driver代表Minidriver接收所有的I/O请求。为了获取完成此次请求所必须的信息,Class Driver创建一个流请求块(Stream Request Block,SRB)并把它传递给形如StrMin×××Packet的众多例程中的一个。总体的来说,Class Driver向某设备分派I/O请求就等同于向StrMiniReceiveDevicePacket例程传递流请求块SRB,它把请求发送到各个独立的流的StrMiniReceiveStreamDataPacket例程(适用于内核流的读写请求)或者StrMiniReceiveStreamControlPacket例程(适用于其他请求)。
通常说来,Class Driver会对所有的请求进行排队,然后每次发送一个请求给Minidriver。Minidriver可以随意地进行它自己的同步操作;Minidriver负责对它目前无法立即处理的请求进行排队。详见Minidriver Synchronization。
Minidriver必须提供两个额外的例程以对SRB进行操作。当Class Driver收到一个取消IRP包(Cancel IRP)时,就会去调用StrMiniCancelPacket,以此来通知Minidriver取消某个特定的包。Class Driver会跟踪Minidriver完成处理一个SRB所花费的时间。如果Minidriver花费了很长时间还没有处理完一个请求,Class Driver将会对该请求返回超时,同时调用StrMiniRequestTimeout例程。
当发生了一个硬件中断时,操作系统会照会Class Driver,然后Class Driver调用Minidriver的StrMiniInterrupt例程去处理该中断。
四、处理流请求块SRB
操作系统会把所有对设备的I/O请求分发到Class Driver。通过向Minidriver传递SRB,Class 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的例程和参数如下表所示:
请求类型 | 例程 | 例程的通知类型参数 |
设备请求 | ReadyForNextDeviceRequest | |
流控制请求 | ReadyForNextStreamControlRequest | |
流数据请求 | ReadyForNextStreamDataRequest |
当Class Driver调用StrMiniReceive×××Packet例程时,它会把SRB转交给Minidriver。在Minidriver通知Class Driver,它已经对完成处理该请求之前,Minidriver中负责进行处理的例程对SRB有独占存取权限。
当Minidriver完成对一个请求的处理后,它应当按如下方式通知Class Driver它已经完成对请求的处理:
1.设置SRB中表示当前请求状态的状态域;
2.调用例程StreamClassDeviceNotification或StreamClassStreamNotification,通知Class Driver说它已经完成对请求的处理。如果完成的是一个设备请求,Minidriver要使用DeviceRequestComplete作为通知类型参数来调用例程StreamClassDeviceNotification;如果完成的是一个流请求,Minidriver要用StreamRequestComplete作为通知类型参数调用例程StreamClassStreamNotification。
3.如果Class Driver当前正在处理同步,并且如果与此同时Minidriver还没有通知Class Driver说它已经做好准备接收对当前类型队列(注意,前面说过有三种类型的请求队列)的下一个请求,那它现在应该立即这样做(通知Class Driver)。
Minidriver可以通过调用StreamClassCompleteRequestAndMarkQueueReady,把2、3两步合并一起做。
Minidriver是以异步的方式处理请求的。所以Class Driver也许会取消一个请求,或者让一个请求超时。为了达到这些目的,Minidriver必须分别提供一个StrMiniCancelPacket例程和一个StrMiniRequestTimeout例程。Class Driver会在他要取消,或者要超时一个请求的时候,调用这两个独立的例程。
当下层的I/O请求被操作系统取消,Class Driver就要取消请求。Class Driver会把那些处理时间过长的请求置为超时,那Class Driver又是如何知道一个请求有没有超时呢?原来,在SRB中有一个成员变量TimeoutCounter,它指定了此请求处理超过多少秒就算超时。Class Driver每秒将此变量减一,如果变量已经减为0,而Minidriver还没有完成该请求,Class Driver就会判定此请求超时,然后它会调用Minidriver的StrMiniRequestTimeout例程。如果Minidriver要推迟很长时间再去处理一个请求,那Minidriver就应该把TimeoutCounter设为0,这样Class Driver不会误判当前请求超时。一旦Minidriver重新开始处理该请求,它应该重置SRB中的TimeoutCounter变量和TimeoutOriginal的值相等,Minidriver可以在请求超时之前重设TimeoutOriginal变量来改变耗时长度。详细信息参见HW_STREAM_REQUEST_BLOCK结构体。
五、Minidriver的初始化过程
当操作系统初次初始化Stream Class Minidrivers的时候会调用Minidriver的DriverEntry例程。Minidriver必须调用StreamClassRegisterMinidriver向Class Driver注册它自己。关于Stream Class Minidrivers的DriverEntry例程,请见第六部分。
在Minidriver调用StreamClassRegisterMinidriver函数向Class Driver进行注册时,它会传递一个HW_INITIALIZATION_DATA结构,此结构会提供给Class Driver一些基本的信息,Class Driver通过这些信息来初始化Minidriver。这些信息包括,设备范围的回调函数(device-wide callbacks),StrMiniReceiveDevicePacket,StrMiniCancelPacket,StrMiniRequestTimeout和StrMiniInterrupt。
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
函数指针,指向Minidriver的StrMiniInterrupt例程。
·HwReceivePacket
函数指针,指向Minidriver的StrMiniReceiveDevicePacket例程。
·HwCancelPacket
函数指针,指向Minidriver的StrMiniCancelPacket例程。
·HwRequestTimeoutHandler
函数指针,指向Minidriver的StrMiniRequestTimeout例程。
·DeviceExtensionSize
说明Minidriver的“设备扩展结构(Device Extension)”的大小,这样Class Driver才能知道要为它分配多大的buffer空间。Minidriver可以利用此空间来记录,那些对Minidriver来说是公有的,但是对外是私有的信息。Class Driver会把该buffer的起始指针分别存放到结构HW_STREAM_OBJECT,HW_STREAM_REQUEST_BLOCK,HW_TIME_CONTEXT,和 PORT_CONFIGURATION_INFORMATION的成员HwDeviceExtension中,然后再把这些结构传递给Minidriver,这样Minidriver就知道Class Driver为它分配的buffer在哪了。
·PerRequestExtensionSize
在Class Driver传递给Minidriver的结构HW_STREAM_REQUEST_BLOCK(SRB)中,其成员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_BLOCK(SRB)中,其成员HwInstanceExtension也是一个指针,指向存放“实例扩展结构(Instance Extension)”的buffer的起始地址,它照样由Class Driver分配,所以在此用FilterInstanceExtensionSize成员来设定该buffer的大小。对Minidriver的每个实例,都会有这样一个buffer。
·BusMasterDMA
若此成员置为TRUE,则设备可以对Minidriver的DMA buffer执行直接总线控制DMA(Direct 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_INFORMATION和HW_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调用StreamClassRegisterMinidriver向Class Driver注册它自己的时候,该结构会作为参数一起传递。Class Driver会为这个扩展结构分配空间,并把一个指向该buffer的指针记录在结构HW_STREAM_OBJECT、HW_STREAM_REQUEST_BLOCK和 HW_TIME_CONTEXT的成员HwDeviceExtension中,回传给Minidriver。
·ClassDeviceObject
Class Driver会为驱动程序的设备提供功能设备对象(Functional Device Object,FDO),此成员保留一个指向该FDO的指针。
·PhysicalDeviceObject
指向物理设备对象PDO的指针。它指向在Class Driver被添加到驱动程序堆栈前,位于栈顶的驱动程序所对应的物理设备对象PDO(Class Driver添加到驱动程序堆栈之后,该成员就指向次栈顶的驱动程序对应的PDO)。驱动程序使用例程IoCallDriver和驱动程序堆栈进行通信时会用到该成员。RealPhysicalDeviceObject成员指向驱动程序的设备的实际PDO。
·SystemIoBusNumber
Class Driver负责用设备的系统总线ID号码(System Bus ID Number)填充该域。Bus 0就是主系统总线
·AdapterInterfaceType
指定设备连接到系统所用的系统总线类型,譬如ISA,EISA,MicroChannel,PCIBus和PCMCIABus等。
·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
如果设备使用了DMA,Class 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的初始化、调用和卸载的步骤如下:
1.PnP管理器枚举到Minidriver所支持的硬件适配器插入,然后PnP管理器通过检查注册表去解析所有相关的符号引用,并向I/O子系统发送请求。
2.I/O子系统加载Minidriver,然后调用Minidriver的DriverEntry例程,在此例程内将分配并初始化一个HW_INITIALIZATION_DATA结构。根据DriverEntry中的信息,创建一个文件对象(file object)。
3.Minidriver的DriverEntry例程接着会调用流类驱动(Stream Class Driver)的StreamClassRegisterMinidriver函数,并把HW_INITIALIZATION_DATA结构作为一个参数传给此函数。HW_INITIALIZATION_DATA结构提供了Minidriver中那些用来处理SRB的函数的地址。这样Minidriver就能相应来自Class Driver的SRB包了。
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 number是HW_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_STREAM给Minidriver的主收包函数(此函数是由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
);
其中参数Argument1和Arguement2提供的是两个Context Value,在Minidriver调用StreamClassRegisterMinidriver时会用到这两个值的。对Windows2000及后续版本来说,Argument1指向一个DRIVER_OBJECT结构,而Arguement2指向注册路径
DriverEntry的返回值就是StreamClassRegisterMinidriver的返回值。
StreamClassRegisterMinidriver会执行大部分所必需的驱动初始化工作,流类Minidriver的DriverEntry例程的主要任务就是分配HW_INITIALIZATION _DATA结构,并用驱动指定的(driver-specific)常量和入口点填充该结构。然后DriverEntry就应该调用StreamClassRegisterMinidriver例程。
八、支持多流(Multiple Streams)
在Minidriver的StrMiniReceiveDevicePacket例程响应SRB_GET_STREAM_INFO命令时,Minidriver会描述所有它支持的流的信息。SRB的CommandData.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属性集。