摄像头驱动程序的主要目的是通过硬件捕捉视频信号。微软公司提供了一套视频驱动的接口,可以满足这个接口的视频驱动程序。第三方厂商开发的软件,如QQ和MSN等软件,都可以通过这个接口读取视频摄像头采集的数据。本文将介绍一个虚拟摄像头的实现,读者可以根据自己的需要,结合硬件设计出符合这个接口的USB或者PCI摄像头驱动程序。
WinXP下采用WDM模式进行摄像头驱动程序开发主要包含两个部分,一部分时类驱动程序,一部分是小驱动程序。类驱动程序由微软提供,而小驱动程序是由程序自己编写,并实现相关扩展功能。
摄像头采集的数据都是纯粹的数据文件,然后源源不断地传递给PC机。因此得名Stream(流设备)。编写摄像头驱动程序就是编写流设备的小驱动,但首先需要了解类设备驱动与小驱动之间是如何配合的。在WDM驱动程序当中,小驱动需要首先将自己注册给类驱动,类驱动拥有真正的设备对象,然而小驱动程序不创建对象,它仅利用类驱动程序实现系统调用。
流设备的类驱动主要是控制请求。这需要通过调用小驱动的适配器来访问具体的硬件。在流设备的类驱动和小驱动都初始化以后,小驱动需要被动地等待类驱动来调用。类驱动用(stream request block)SRB向小驱动程序发送标准的请求,小驱动通过解析SRB后向Class Driver的请求。例如在SRB中,类驱动与小驱动采用数据结构HW_STREAM_REQUEST_BLOCK来传送命令和数据。
与其他驱动程序不同,类驱动与小驱动采用HW_INITIALIZATION_DATA数据结构进行初始化操作,其初始化步骤如下。
当设备接入插口时,即插即用管理器能够监测到新的设备,进而调用Mini Driver的DriverEntry例程。
申请HW_INITIALIZATION_DATA数据内存,并填充相关域,调用StreamClassRegisterMinidriver函数注册该小驱动。如下代码;
RtlZeroMemory(&HwInitData, sizeof(HwInitData)); HwInitData.HwInitializationDataSize = sizeof(HwInitData); // // Set the Adapter entry points for the driver // HwInitData.HwInterrupt = NULL; // HwInterrupt; HwInitData.HwReceivePacket = AdapterReceivePacket; HwInitData.HwCancelPacket = AdapterCancelPacket; HwInitData.HwRequestTimeoutHandler = AdapterTimeoutPacket; HwInitData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION); HwInitData.PerRequestExtensionSize = sizeof(SRB_EXTENSION); HwInitData.FilterInstanceExtensionSize = 0; HwInitData.PerStreamExtensionSize = sizeof(STREAMEX); HwInitData.BusMasterDMA = FALSE; HwInitData.Dma24BitAddresses = FALSE; HwInitData.BufferAlignment = 3; HwInitData.DmaBufferSize = 0; // Don't rely on the stream class using raised IRQL to synchronize // execution. This single paramter most affects the overall structure // of the driver. HwInitData.TurnOffSynchronization = TRUE; ReturnValue = StreamClassRegisterAdapter(DriverObject, RegistryPath, &HwInitData); |
AdapterReceivePacket函数用于接收类驱动向小驱动发送的SRB,其中SRB_INITIALIZE_DEVICE用于初始化小驱动,SRB_INITIALIZE_DEVICE结构体中ConfigInfo记录着摄像头的意见相关信息。初始化之后类驱动再向小驱动发送SRB_GET_STREAM_INFO类型的SRB,用于向小驱动程序咨询HW_STREAM_HEADER数据信息和HW_STREAM_INFORMATION等信息。这些信息包含了摄像头驱动提供的视频图像大小、图像格式等信息。最后类驱动程序小驱动再发送HW_STREAM_INFORMATION的SRB,这次请求之后,小驱动的初始化操作基本完成,并返回给类驱动程序。上述这些过程的代码如下;
switch (pSrb->Command) { case SRB_INITIALIZE_DEVICE: // open the device HwInitialize(pSrb); break; case SRB_UNINITIALIZE_DEVICE: // close the device. HwUnInitialize(pSrb); break; case SRB_OPEN_STREAM: // open a stream AdapterOpenStream(pSrb); break; case SRB_CLOSE_STREAM: // close a stream AdapterCloseStream(pSrb); break; case SRB_GET_STREAM_INFO: AdapterStreamInfo(pSrb); break; case SRB_GET_DATA_INTERSECTION: AdapterFormatFromRange(pSrb); break; case SRB_OPEN_DEVICE_INSTANCE: case SRB_CLOSE_DEVICE_INSTANCE:
TRAP; pSrb->Status = STATUS_NOT_IMPLEMENTED; break; case SRB_GET_DEVICE_PROPERTY: AdapterGetProperty (pSrb); break; case SRB_SET_DEVICE_PROPERTY: AdapterSetProperty (pSrb); break; case SRB_PAGING_OUT_DRIVER: DbgLogInfo(("'Testcap: Receiving SRB_PAGING_OUT_DRIVER -- SRB=%x/n", pSrb)); break; case SRB_CHANGE_POWER_STATE: DbgLogInfo(("'Testcap: Receiving SRB_CHANGE_POWER_STATE ------ SRB=%x/n", pSrb)); AdapterPowerState(pSrb); break; case SRB_INITIALIZATION_COMPLETE: DbgLogInfo(("'Testcap: Receiving SRB_INITIALIZATION_COMPLETE-- SRB=%x/n", pSrb)); break; case SRB_UNKNOWN_DEVICE_COMMAND: default: pSrb->Status = STATUS_NOT_IMPLEMENTED; } CompleteDeviceSRB (pSrb); |
类驱动程序的目标是与操作系统进行交互,其中包括处理同步、提供标准接口。而小驱动由类驱动调用,主要负责具体的硬件相关操作。因此编写小驱动的程序应该会向类驱动注册一些回调函数,方便类驱动调用。其中有几个函数是必须注册的,如下;
Ø StrMiniCancelPacket:对HW_STREAM_REQUEST_BLOCK数据包进行取消的回调函数。
Ø StrMiniReceieveDevicePacket:获取HW_STREAM_REQUEST_BLOCK数据包回调函数。
Ø StrMiniRequestTimeout:当HW_STREAM_REQUEST_BLOCK数据包超时的回调函数。
Ø StrMiniEvent:使小驱动程序支持某种事件。
Ø StrMiniInterrupt:当驱动获取中断时还用该支持中断处理函数。
编写小驱动程序时,还需要注意实现流控制操作,流控制命令和数据主要保存在SRB当中,其中以下几种流控制命令必须实现。
Ø SRB_INITIALIZE_DEVICE:调用HW_INITIALIZATION_DATA数据结构中初始化小驱动程序。
Ø SRB_GET_STREAM_INFO:调用HW_INITIALIZATION_DATA数据结构中获取流相关信息。
Ø SRB_OPEN_STREAM:打开一个流实例,并填充HW_STREAM_OBJECT结构体流描述信息。
Ø SRB_READ_DATA和SRB_WRITE_DATA:这两个命令用于接收和发送数据。
Ø HW_STREAM_REQUEST_BLOCK:类驱动为了获得或者设置小驱动的某项属性或者流的某项属性,会向小驱动提供HwReceivePacket函数发送HW_STREAM_OBJECT请求。
Ø SRB_CLOSE_STREAM:用于关闭小驱动程序。
在微软提供的类驱动程序之上编写虚拟驱动程序,其实最主要的工作就是编写子驱动程序,实现初始化、流控制、数据接收等相关回调函数。