驱动开发之六 --- 一个简单的显示驱动之一 [译文]

这个系列的文章在网上到处都是 这里也不清楚谁才是原文作者 我这里做个整理,标注一下希望大家能看的更加舒服一点

目录

(一)驱动开发一个简单的显示驱动

(二)驱动开发一个简单的显示驱动

(三)驱动开发一个简单的显示驱动

(四)驱动开发一个简单的显示驱动

(五)驱动开发一个简单的显示驱动

(六)驱动开发一个简单的显示驱动

 

 

理论:

本篇我们将介绍下如何写一个简单的显示驱动。显示驱动是一种特殊类型的驱动,必须要满足一个框架,它不像我们前面讲的那些驱动。

 

示例程序演示了如何写一个简单的显示驱动,这个驱动无需关联任何硬件。它能实现图形到内存,然后由一个应用程序来显示这些图形。

显示驱动的体系结构

首先介绍的是windows NT下显示驱动的体系结构。在这里要特别说明的是windows vista使用了一种新的显示驱动模型,叫做LDDM.它是vista最新桌面窗口管理器的核心部分。就兼容方面,vista仍然可以与老的窗口管理器一起协作支持老的显示驱动。

 

显示驱动模型包括两部分,迷你小端口驱动和显示驱动。迷你小端口驱动加载到系统空间,负责枚举设备和管理设备资源。显示驱动加载到会话空间,负责实现实际的GDI图形调用。显示驱动完全控制怎样划线或者怎样实现透明效果。

下面的图表显示了windows显示驱动的体系结构。

驱动开发之六 --- 一个简单的显示驱动之一 [译文]

迷你小端口驱动

迷你小端口驱动加载到系统空间,负责管理显示设备资源和枚举设备。这个驱动使用其它的驱动(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。

 
  1. ULONG DriverEntry(PVOID pContext1, PVOID pContext2)

  2.  
  3. {

  4.  
  5.     VIDEO_HW_INITIALIZATION_DATA hwInitData;

  6.  
  7.     VP_STATUS vpStatus;

  8.  
  9.   

  10.  
  11.     VideoPortZeroMemory(&hwInitData, sizeof(VIDEO_HW_INITIALIZATION_DATA));

  12.  
  13.     hwInitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);

  14.  
  15.     hwInitData.HwFindAdapter              = FakeGfxCard_FindAdapter;

  16.  
  17.     hwInitData.HwInitialize              = FakeGfxCard_Initialize;

  18.  
  19.     hwInitData.HwStartIO                  = FakeGfxCard_StartIO;

  20.  
  21.     hwInitData.HwResetHw                  = FakeGfxCard_ResetHW;

  22.  
  23.     hwInitData.HwInterrupt                = FakeGfxCard_VidInterrupt;

  24.  
  25.     hwInitData.HwGetPowerState           = FakeGfxCard_GetPowerState;

  26.  
  27.     hwInitData.HwSetPowerState            = FakeGfxCard_SetPowerState;

  28.  
  29.     hwInitData.HwGetVideoChildDescriptor =FakeGfxCard_GetChildDescriptor;

  30.  
  31.     vpStatus = VideoPortInitialize(pContext1, pContext2, &hwInitData, NULL);

  32.  
  33.     return vpStatus;

  34.  
  35. }

将VideoPortInitialize的返回值作为DriverEntry函数的返回值,返回调用者。

 

在你直接简单的传递DriverObject给VideoPrt.SYS之前,你还需要填充一个数据结构,这个数据结构中包含了你驱动中的入口点,这些入口点会被VideoPrt.SYS驱动调用来执行不同的动作。"HwStartIO"是指你可以处理IOCTLs,在显示驱动和视频迷你小端口驱动之间,你可以使用IOCTLs。显示驱动只需要简单调用"EngDeviceIoControl",迷你小端口驱动中的HwStartIO就会处理IOCTL。代码如下:

 

 
  1. #pragma alloc_text(PAGE, FakeGfxCard_GetPowerState)

  2. #pragma alloc_text(PAGE, FakeGfxCard_SetPowerState)

  3. #pragma alloc_text(PAGE, FakeGfxCard_GetChildDescriptor)

  4. #pragma alloc_text(PAGE, FakeGfxCard_FindAdapter)

  5. #pragma alloc_text(PAGE, FakeGfxCard_Initialize)

  6. #pragma alloc_text(PAGE, FakeGfxCard_StartIO)

  7.  
  8. BOOLEAN FakeGfxCard_ResetHW(PVOID HwDeviceExtension,

  9.                                  ULONG Columns, ULONG Rows)

  10. {

  11.    return TRUE;

  12. }

  13.  
  14.  
  15. BOOLEAN FakeGfxCard_VidInterrupt(PVOID HwDeviceExtension)

  16. {

  17.    return FALSE;

  18. }

  19.  
  20.  
  21. VP_STATUS FakeGfxCard_GetPowerState(PVOID HwDeviceExtension,

  22.           ULONG HwId, PVIDEO_POWER_MANAGEMENT VideoPowerControl)

  23. {            

  24.    return NO_ERROR;

  25. }

  26.  
  27.  

  28.  
  29. VP_STATUS FakeGfxCard_SetPowerState(PVOID HwDeviceExtension,

  30.           ULONG HwId, PVIDEO_POWER_MANAGEMENT VideoPowerControl)

  31. {

  32. return NO_ERROR;

  33.  
  34. }

  35.  
  36.  

  37.  
  38. ULONG FakeGfxCard_GetChildDescriptor (PVOID HwDeviceExtension,

  39.  
  40.       PVIDEO_CHILD_ENUM_INFO ChildEnumInfo, PVIDEO_CHILD_TYPE pChildType,

  41.  
  42.       PVOID pChildDescriptor, PULONG pUId, PULONG pUnused)

  43.  
  44. {

  45.  
  46.    return ERROR_NO_MORE_DEVICES;

  47.  
  48. }

  49.  
  50.  

  51.  
  52. VP_STATUS FakeGfxCard_FindAdapter(PVOID HwDeviceExtension,

  53.  
  54.             PVOID HwContext, PWSTR ArgumentString,

  55.  
  56.             PVIDEO_PORT_CONFIG_INFO ConfigInfo, PUCHAR Again)

  57.  
  58. {

  59.  
  60.    return NO_ERROR;

  61.  
  62. }

  63.  
  64.  

  65.  
  66. BOOLEAN FakeGfxCard_Initialize(PVOID HwDeviceExtension)

  67.  
  68. {

  69.  
  70.    return TRUE;

  71.  
  72. }

  73.  
  74.  

  75.  
  76. BOOLEAN FakeGfxCard_StartIO(PVOID HwDeviceExtension,

  77.  
  78.                 PVIDEO_REQUEST_PACKET RequestPacket)

  79.  
  80. {

  81.  
  82.    RequestPacket->StatusBlock->Status      = 0;

  83.  
  84.    RequestPacket->StatusBlock->Information = 0;

  85.  
  86.    return TRUE;

  87.  
  88. }

 

由于我没有任何硬件,对于迷你小端口驱动,简单实现就足够了。如果我需要在系统*问或者执行一个操作,显示驱动靠它自己有限的API函数集是不能做到的。我会用到的唯一的API就是"StartIO"。可是在这个实现中,我们不需要做什么。记住,迷你小端口驱动的主要用途就是枚举硬件设备/资源和管理他们。如果你没有什么硬件设备/资源,那么为了让驱动模型高兴,除了必需的外删除其他的。