迷你小端口驱动
迷你小端口驱动加载到系统空间,负责管理显示设备资源和枚举设备。这个驱动使用其它的驱动(VIDEOPRT.SYS)作为它的框架。你的驱动会使用VIDEOPRT.SYS导出的API. 很奇怪驱动可以导出API吧?驱动使用pe格式,也有导入表和导出表。你可以从你的驱动中导出api,并且允许其他驱动链接使用它们,就像调用一个dll一样。实际上你使用的这些API连接着内核和其他驱动。
在这里要注意连接内核模式驱动和用户模式的驱动有些不同。如果一个驱动连接另一个驱动,而那个驱动当前没有加载到内存。那么那个驱动将会被加载进内存,然而那个驱动的DriverEntry不会被调用。DriverEntry自身不会被调用,只有使用ZwLoadDriver加载驱动,系统加载驱动或者使用我们前面展示过的服务api记载驱动才会调用DriverEntry。任何时候,你都可以从一个驱动中导出API,然后在另一个驱动中使用这些api. 在内核中没有类似于"GetProcAddress"的api,你需要自己写一个。
你的迷你小端口驱动会调用VideoPrt.SYS导出的api。VideoPrt.SYS驱动做了很少的事情,其中之一就是实现了通用代码。所以视频驱动的开发者就不需要重新再写同样的代码。这个代码包括在win32子系统(win32k.sys)和你的迷你小端口驱动之间进行视频设备枚举。VideoPrt.SYS还创建了一个显示用的设备对象,当你调用这个初始化例程时,他会使你的驱动对象入口点指向VideoPrt.SYS的。
所有的VideoPrt.SYS API都是"VideoPort"开头的。你第一个调用的API应该是"VideoPortInitialize".如果你注意的话,可以看到开头的两个参数就是传递给你的DriverEntry例程的,他们被称作"Context1"和"Context2",仿佛你的视频迷你小端口驱动是特别的。不要被这些迷惑,第一参数"Context1"实际上就是你的驱动对象。一旦你传递你的驱动对象给VideoPortInitialize,所有你的驱动入口点都会指向VideoPrt.Sys的。除此之外,你还要传递给VIDEO_HW_INITIALIZATION_DATA结构不同的函数指针,VideoPrt.SYS在需要的时候会用到这些。
这意味着你不要直接处理在视频迷你小端口驱动中的IRP. VideoPrt.SYS会处理他们。你需要处理的是"VRP"(视频请求包),本质上讲它其实是一种简化的用不同数据结构的IRP版本。你需要简单的返回,不需要像IRP那样对这个数据结构进行处理。
在迷你小端口驱动中,你除了使用"VideoPort"开头的api外,由于它是系统层的驱动,你还可以使用内核的api.
由于我们没有任何硬件,因此我们的迷你小端口驱动会比较简单,下面的代码演示了如何编写视频迷你小端口驱动DriverEntry。
/**********************************************************************
*
* DriverEntry
*
* This is the entry point for this video miniport driver
*
**********************************************************************/
ULONG DriverEntry(PVOID pContext1, PVOID pContext2)
{
VIDEO_HW_INITIALIZATION_DATA hwInitData;
VP_STATUS vpStatus;
/*
* The Video Miniport is "technically" restricted to calling
* "Video*" APIs.
* There is a driver that encapsulates this driver by setting your
* driver's entry points to locations in itself. It will then
* handle your IRP's for you and determine which of the entry
* points (provided below) into your driver that should be called.
* This driver however does run in the context of system memory
* unlike the GDI component.
*/
VideoPortZeroMemory(&hwInitData,
sizeof(VIDEO_HW_INITIALIZATION_DATA));
hwInitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);
hwInitData.HwFindAdapter = FakeGfxCard_FindAdapter;
hwInitData.HwInitialize = FakeGfxCard_Initialize;
hwInitData.HwStartIO = FakeGfxCard_StartIO;
hwInitData.HwResetHw = FakeGfxCard_ResetHW;
hwInitData.HwInterrupt = FakeGfxCard_VidInterrupt;
hwInitData.HwGetPowerState = FakeGfxCard_GetPowerState;
hwInitData.HwSetPowerState = FakeGfxCard_SetPowerState;
hwInitData.HwGetVideoChildDescriptor =
FakeGfxCard_GetChildDescriptor;
vpStatus = VideoPortInitialize(pContext1,
pContext2, &hwInitData, NULL);
return vpStatus;
}
将VideoPortInitialize的返回值作为DriverEntry函数的返回值,返回调用者。
在你直接简单的传递DriverObject给VideoPrt.SYS之前,你还需要填充一个数据结构,这个数据结构中包含了你驱动中的入口点,这些入口点会被VideoPrt.SYS驱动调用来执行不同的动作。"HwStartIO"是指你可以处理IOCTLs,在显示驱动和视频迷你小端口驱动之间,你可以使用IOCTLs。显示驱动只需要简单调用"EngDeviceIoControl",迷你小端口驱动中的HwStartIO就会处理IOCTL。代码如下:
/*#pragma alloc_text(PAGE, FakeGfxCard_ResetHW) Cannot be Paged*/
/*#pragma alloc_text(PAGE, FakeGfxCard_VidInterrupt) Cannot be Paged*/
#pragma alloc_text(PAGE, FakeGfxCard_GetPowerState)
#pragma alloc_text(PAGE, FakeGfxCard_SetPowerState)
#pragma alloc_text(PAGE, FakeGfxCard_GetChildDescriptor)
#pragma alloc_text(PAGE, FakeGfxCard_FindAdapter)
#pragma alloc_text(PAGE, FakeGfxCard_Initialize)
#pragma alloc_text(PAGE, FakeGfxCard_StartIO)
/**********************************************************************
* FakeGfxCard_ResetHW
* This routine would reset the hardware when a soft reboot is
* performed. Returning FALSE from this routine would force
* the HAL to perform an INT 10h and set Mode 3 (Text).
* We are not real hardware so we will just return TRUE so the HAL
* does nothing.
**********************************************************************/
BOOLEAN FakeGfxCard_ResetHW(PVOID HwDeviceExtension,
ULONG Columns, ULONG Rows)
{
return TRUE;
}
/*********************************************************************
* FakeGfxCard_VidInterrupt
* Checks if it's adapter generated an interrupt and dismisses it
* or returns FALSE if it did not.
**********************************************************************/
BOOLEAN FakeGfxCard_VidInterrupt(PVOID HwDeviceExtension)
{
return FALSE;
}
/*********************************************************************
* FakeGfxCard_GetPowerState
* Queries if the device can support the requested power state.
**********************************************************************/
VP_STATUS FakeGfxCard_GetPowerState(PVOID HwDeviceExtension,
ULONG HwId, PVIDEO_POWER_MANAGEMENT VideoPowerControl)
{
return NO_ERROR;
}
/**********************************************************************
* FakeGfxCard_SetPowerState
* Sets the power state.
**********************************************************************/
VP_STATUS FakeGfxCard_SetPowerState(PVOID HwDeviceExtension,
ULONG HwId, PVIDEO_POWER_MANAGEMENT VideoPowerControl)
{
return NO_ERROR;
}
/**********************************************************************
*
* FakeGfxCard_GetChildDescriptor
*
* Returns an identifer for any child device supported
* by the miniport.
*
**********************************************************************/
ULONG FakeGfxCard_GetChildDescriptor (PVOID HwDeviceExtension,
PVIDEO_CHILD_ENUM_INFO ChildEnumInfo, PVIDEO_CHILD_TYPE pChildType,
PVOID pChildDescriptor, PULONG pUId, PULONG pUnused)
{
return ERROR_NO_MORE_DEVICES;
}
/**********************************************************************
*
* FakeGfxCard_FindAdapter
*
* This function performs initialization specific to devices
* maintained by this miniport driver.
*
**********************************************************************/
VP_STATUS FakeGfxCard_FindAdapter(PVOID HwDeviceExtension,
PVOID HwContext, PWSTR ArgumentString,
PVIDEO_PORT_CONFIG_INFO ConfigInfo, PUCHAR Again)
{
return NO_ERROR;
}
/**********************************************************************
*
* FakeGfxCard_Initialize
*
* This initializes the device.
*
**********************************************************************/
BOOLEAN FakeGfxCard_Initialize(PVOID HwDeviceExtension)
{
return TRUE;
}
/**********************************************************************
*
* FakeGfxCard_StartIO
*
* This routine executes requests on behalf of the GDI Driver
* and the system. The GDI driver is allowed to issue IOCTLs
* which would then be sent to this routine to be performed
* on it's behalf.
*
* We can add our own proprietary IOCTLs here to be processed
* from the GDI driver.
*
**********************************************************************/
BOOLEAN FakeGfxCard_StartIO(PVOID HwDeviceExtension,
PVIDEO_REQUEST_PACKET RequestPacket)
{
RequestPacket->StatusBlock->Status = 0;
RequestPacket->StatusBlock->Information = 0;
return TRUE;
}
由于我没有任何硬件,对于迷你小端口驱动,简单实现就足够了。如果我需要在系统上访问或者执行一个操作,显示驱动靠它自己有限的API函数集是不能做到的。我会用到的唯一的API就是"StartIO"。可是在这个实现中,我们不需要做什么。记住,迷你小端口驱动的主要用途就是枚举硬件设备/资源和管理他们。如果你没有什么硬件设备/资源,那么为了让驱动模型高兴,除了必需的外删除其他的。