WDDM KMOD驱动初始化

Windows驱动的入口函数是DriverEntry,所以显示Mini小端口驱动程序也不例外。

和其它Mini小端口驱动的入口函数实现一致,在其DriverEntry只做一件事,就是分配系统指定的一个结构体,然后调用框架提供的接口函数即可。

主要过程如下:

  • 操作系统调用display miniport驱动程序的DriverEntry函数。

  • DriverEntry分配一个 DRIVER_INITIALIZATION_DATA 结构,并初始化其成员变量 Version为DXGKDDI_INTERFACE_VERSION 和其它指针变量(主要是回调函数)。

  • DriverEntry调用DxgkInitialize函数来加载Microsoft DirectX图形内核子系统(DXGKNL.sys公司)并为DirectX图形内核子系统提供指向显示微型端口驱动程序的其他入口点函数的指针。

  • DxgkInitialize返回后,DriverEntry将DxgkInitialize的返回值传播回操作系统。

DxgkInitialize函数是DispLib导出的一个函数,这是函数用于显卡Mini小端口驱动向Windows显示子系统注册。
函数原型如下:

#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WIN8)

NTSTATUS
DxgkInitializeDisplayOnlyDriver(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath,
    _In_ PKMDDOD_INITIALIZATION_DATA KmdDodInitializationData
    );

#endif // DXGKDDI_INTERFACE_VERSION

通过条件宏可以看到,这个函数支持的是Win8及以上操作系统。

DxgkInitializeDisplayOnlyDriver函数的参数总共3个,前两个直接使用DriverEntry的两个参数,而第三个参数KmdDodInitializationData是一个大大的结构体,这个结构体中包含了显示驱动向框架提供的一系列回调函数(Callback Function)。框架会在合适的时候调用这些回调函数,完成对应功能。

DxgkInitializeDisplayOnlyDriver结构体的声名如下:

#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WIN8)

typedef struct _KMDDOD_INITIALIZATION_DATA {
    ULONG                                   Version;
    PDXGKDDI_ADD_DEVICE                     DxgkDdiAddDevice;
    PDXGKDDI_START_DEVICE                   DxgkDdiStartDevice;
    PDXGKDDI_STOP_DEVICE                    DxgkDdiStopDevice;
    PDXGKDDI_REMOVE_DEVICE                  DxgkDdiRemoveDevice;
    PDXGKDDI_DISPATCH_IO_REQUEST            DxgkDdiDispatchIoRequest;
    PDXGKDDI_INTERRUPT_ROUTINE              DxgkDdiInterruptRoutine;
    PDXGKDDI_DPC_ROUTINE                    DxgkDdiDpcRoutine;
    PDXGKDDI_QUERY_CHILD_RELATIONS          DxgkDdiQueryChildRelations;
    PDXGKDDI_QUERY_CHILD_STATUS             DxgkDdiQueryChildStatus;
    PDXGKDDI_QUERY_DEVICE_DESCRIPTOR        DxgkDdiQueryDeviceDescriptor;
    PDXGKDDI_SET_POWER_STATE                DxgkDdiSetPowerState;
    PDXGKDDI_NOTIFY_ACPI_EVENT              DxgkDdiNotifyAcpiEvent;
    PDXGKDDI_RESET_DEVICE                   DxgkDdiResetDevice;
    PDXGKDDI_UNLOAD                         DxgkDdiUnload;
    PDXGKDDI_QUERY_INTERFACE                DxgkDdiQueryInterface;
    PDXGKDDI_CONTROL_ETW_LOGGING            DxgkDdiControlEtwLogging;
    PDXGKDDI_QUERYADAPTERINFO               DxgkDdiQueryAdapterInfo;
    PDXGKDDI_SETPALETTE                     DxgkDdiSetPalette;
    PDXGKDDI_SETPOINTERPOSITION             DxgkDdiSetPointerPosition;
    PDXGKDDI_SETPOINTERSHAPE                DxgkDdiSetPointerShape;
    PDXGKDDI_ESCAPE                         DxgkDdiEscape;
    PDXGKDDI_COLLECTDBGINFO                 DxgkDdiCollectDbgInfo;
    PDXGKDDI_ISSUPPORTEDVIDPN               DxgkDdiIsSupportedVidPn;
    PDXGKDDI_RECOMMENDFUNCTIONALVIDPN       DxgkDdiRecommendFunctionalVidPn;
    PDXGKDDI_ENUMVIDPNCOFUNCMODALITY        DxgkDdiEnumVidPnCofuncModality;
    PDXGKDDI_SETVIDPNSOURCEVISIBILITY       DxgkDdiSetVidPnSourceVisibility;
    PDXGKDDI_COMMITVIDPN                    DxgkDdiCommitVidPn;
    PDXGKDDI_UPDATEACTIVEVIDPNPRESENTPATH   DxgkDdiUpdateActiveVidPnPresentPath;
    PDXGKDDI_RECOMMENDMONITORMODES          DxgkDdiRecommendMonitorModes;
    PDXGKDDI_GETSCANLINE                    DxgkDdiGetScanLine;
    PDXGKDDI_QUERYVIDPNHWCAPABILITY         DxgkDdiQueryVidPnHWCapability;

    //
    // New DDI for the PresentDisplayOnly function.
    //
    PDXGKDDI_PRESENTDISPLAYONLY             DxgkDdiPresentDisplayOnly;

    //
    // New DDIs for PnP stop/start support.
    //
    PDXGKDDI_STOP_DEVICE_AND_RELEASE_POST_DISPLAY_OWNERSHIP DxgkDdiStopDeviceAndReleasePostDisplayOwnership;

    //
    // New DDIs for system display support.
    //
    PDXGKDDI_SYSTEM_DISPLAY_ENABLE          DxgkDdiSystemDisplayEnable;
    PDXGKDDI_SYSTEM_DISPLAY_WRITE           DxgkDdiSystemDisplayWrite;

    //
    // New DDI for the monitor container ID support.
    //
    PDXGKDDI_GET_CHILD_CONTAINER_ID         DxgkDdiGetChildContainerId;

    //
    // New DDI for HW VSync.
    //
    PDXGKDDI_CONTROLINTERRUPT               DxgkDdiControlInterrupt;

    PDXGKDDISETPOWERCOMPONENTFSTATE         DxgkDdiSetPowerComponentFState;
    PDXGKDDIPOWERRUNTIMECONTROLREQUEST      DxgkDdiPowerRuntimeControlRequest;

    //
    // New DDI for the surprise removal support.
    //
    PDXGKDDI_NOTIFY_SURPRISE_REMOVAL        DxgkDdiNotifySurpriseRemoval;

    //
    // Display only drivers support P-State management.
    //
#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WDDM2_0)
    PDXGKDDI_POWERRUNTIMESETDEVICEHANDLE    DxgkDdiPowerRuntimeSetDeviceHandle;
#endif

} KMDDOD_INITIALIZATION_DATA, *PKMDDOD_INITIALIZATION_DATA;

#endif // DXGKDDI_INTERFACE_VERSION

第一个参数Version用来标识你所编写的显示驱动使用哪个版本的WDDM。WDDM现在为止,已经有很多版本了,在VS2019+WDK10环境下,其定义为WDDM2.6版本。

#if !defined(DXGKDDI_INTERFACE_VERSION)
#define DXGKDDI_INTERFACE_VERSION           DXGKDDI_INTERFACE_VERSION_WDDM2_6
#endif // !defined(DXGKDDI_INTERFACE_VERSION)

这里我们列出所有的WDDM版本,大家可以欣赏一下:

#define DXGKDDI_INTERFACE_VERSION_VISTA      0x1052
#define DXGKDDI_INTERFACE_VERSION_VISTA_SP1  0x1053
#define DXGKDDI_INTERFACE_VERSION_WIN7       0x2005
#define DXGKDDI_INTERFACE_VERSION_WIN8       0x300E
#define DXGKDDI_INTERFACE_VERSION_WDDM1_3    0x4002
#define DXGKDDI_INTERFACE_VERSION_WDDM1_3_PATH_INDEPENDENT_ROTATION  0x4003
#define DXGKDDI_INTERFACE_VERSION_WDDM2_0    0x5023
#define DXGKDDI_INTERFACE_VERSION_WDDM2_1    0x6003
#define DXGKDDI_INTERFACE_VERSION_WDDM2_1_5  0x6010     // Used in RS1.7 for GPU-P
#define DXGKDDI_INTERFACE_VERSION_WDDM2_1_6  0x6011     // Used in RS1.8 for GPU-P
#define DXGKDDI_INTERFACE_VERSION_WDDM2_2    0x700A
#define DXGKDDI_INTERFACE_VERSION_WDDM2_3    0x8001
#define DXGKDDI_INTERFACE_VERSION_WDDM2_4    0x9006
#define DXGKDDI_INTERFACE_VERSION_WDDM2_5    0xA00B
#define DXGKDDI_INTERFACE_VERSION_WDDM2_6    0xB004

可以看到,WDDM的技术一直在发展,虽然每个版本的变化都是增加一些回调函数等成员,但对于程序的开发我们只也只需要实现我们所支持的功能即可,完全不用实现所有功能。

DxgkInitializeDisplayOnlyDriver结构体中的回调函数按功能可分为三类:

  • 第一部分为关于PNP处理的回调函数,如PDXGKDDI_ADD_DEVICE,PDXGKDDI_START_DEVICE…,这些回调函数用于处理设备创建,起始,停止,电源相关和子设备的相关回调。
    InitialData.DxgkDdiAddDevice                    = BddDdiAddDevice;
    InitialData.DxgkDdiStartDevice                  = BddDdiStartDevice;
    InitialData.DxgkDdiStopDevice                   = BddDdiStopDevice;
    InitialData.DxgkDdiResetDevice                  = BddDdiResetDevice;
    InitialData.DxgkDdiRemoveDevice                 = BddDdiRemoveDevice;
    InitialData.DxgkDdiUnload                       = BddDdiUnload;
    InitialData.DxgkDdiDispatchIoRequest            = BddDdiDispatchIoRequest;
  • 第二部分是与总线子设备上报相关的。类示总线设备的IRP_MN_QUERY_DEVICE_RELATIONS及有PNP子设备。

      InitialData.DxgkDdiQueryChildRelations          = BddDdiQueryChildRelations;
      InitialData.DxgkDdiQueryChildStatus             = BddDdiQueryChildStatus;
      InitialData.DxgkDdiQueryDeviceDescriptor        = BddDdiQueryDeviceDescriptor;
      InitialData.DxgkDdiSetPowerState                = BddDdiSetPowerState;
      InitialData.DxgkDdiQueryAdapterInfo             = BddDdiQueryAdapterInfo;
      InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership;
    
  • 第三部分为与硬件相关的IRP处理,如中断及DPC处理的DxgkDdiInterruptRoutine和DxgkDdiDpcRoutine,有获取设备属性,读写设备帧内存、显示桌面内容(Present)等函数,如DxgkDdiQueryVidPnHWCapability,DxgkDdiPresentDisplayOnly,DxgkDdiSystemDisplayEnable,DxgkDdiSystemDisplayWrite。

      InitialData.DxgkDdiInterruptRoutine             = BddDdiInterruptRoutine;
      InitialData.DxgkDdiDpcRoutine                   = BddDdiDpcRoutine;
      InitialData.DxgkDdiQueryVidPnHWCapability       = BddDdiQueryVidPnHWCapability;
      InitialData.DxgkDdiPresentDisplayOnly           = BddDdiPresentDisplayOnly;
      InitialData.DxgkDdiSystemDisplayEnable          = BddDdiSystemDisplayEnable;
      InitialData.DxgkDdiSystemDisplayWrite           = BddDdiSystemDisplayWrite
    
  • 第四部分则是与显卡输出相关的操作,如包括对鼠标位置的更新,显示器Mode的枚举和设置等函数:
      InitialData.DxgkDdiSetPointerPosition           = BddDdiSetPointerPosition;
      InitialData.DxgkDdiSetPointerShape              = BddDdiSetPointerShape;
      InitialData.DxgkDdiIsSupportedVidPn             = BddDdiIsSupportedVidPn;
      InitialData.DxgkDdiRecommendFunctionalVidPn     = BddDdiRecommendFunctionalVidPn;
      InitialData.DxgkDdiEnumVidPnCofuncModality      = BddDdiEnumVidPnCofuncModality;
      InitialData.DxgkDdiSetVidPnSourceVisibility     = BddDdiSetVidPnSourceVisibility;
      InitialData.DxgkDdiCommitVidPn                  = BddDdiCommitVidPn;
      InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath;
      InitialData.DxgkDdiRecommendMonitorModes        = BddDdiRecommendMonitorModes;
    

完整的代码如下:

extern "C"
NTSTATUS
DriverEntry(
    _In_  DRIVER_OBJECT*  pDriverObject,
    _In_  UNICODE_STRING* pRegistryPath)
{
    PAGED_CODE();


    // Initialize DDI function pointers and dxgkrnl
    KMDDOD_INITIALIZATION_DATA InitialData = {0};

    InitialData.Version = DXGKDDI_INTERFACE_VERSION;
    //系统IRP回调及WDM框架相关
    InitialData.DxgkDdiAddDevice                    = BddDdiAddDevice;
    InitialData.DxgkDdiStartDevice                  = BddDdiStartDevice;
    InitialData.DxgkDdiStopDevice                   = BddDdiStopDevice;
    InitialData.DxgkDdiResetDevice                  = BddDdiResetDevice;
    InitialData.DxgkDdiRemoveDevice                 = BddDdiRemoveDevice;
    InitialData.DxgkDdiUnload                       = BddDdiUnload;
    InitialData.DxgkDdiDispatchIoRequest            = BddDdiDispatchIoRequest;

    //子设备枚举相关
    InitialData.DxgkDdiQueryChildRelations          = BddDdiQueryChildRelations;
    InitialData.DxgkDdiQueryChildStatus             = BddDdiQueryChildStatus;
    InitialData.DxgkDdiQueryDeviceDescriptor        = BddDdiQueryDeviceDescriptor;
    InitialData.DxgkDdiSetPowerState                = BddDdiSetPowerState;

    InitialData.DxgkDdiQueryAdapterInfo             = BddDdiQueryAdapterInfo;
    InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership;


    //硬件及中断处理相关
    InitialData.DxgkDdiInterruptRoutine             = BddDdiInterruptRoutine;
    InitialData.DxgkDdiDpcRoutine                   = BddDdiDpcRoutine;
    InitialData.DxgkDdiQueryVidPnHWCapability       = BddDdiQueryVidPnHWCapability;
    InitialData.DxgkDdiPresentDisplayOnly           = BddDdiPresentDisplayOnly;
    InitialData.DxgkDdiSystemDisplayEnable          = BddDdiSystemDisplayEnable;
    InitialData.DxgkDdiSystemDisplayWrite           = BddDdiSystemDisplayWrite;

    //图像输出相关
    InitialData.DxgkDdiSetPointerPosition           = BddDdiSetPointerPosition;
    InitialData.DxgkDdiSetPointerShape              = BddDdiSetPointerShape;
    InitialData.DxgkDdiIsSupportedVidPn             = BddDdiIsSupportedVidPn;
    InitialData.DxgkDdiRecommendFunctionalVidPn     = BddDdiRecommendFunctionalVidPn;
    InitialData.DxgkDdiEnumVidPnCofuncModality      = BddDdiEnumVidPnCofuncModality;
    InitialData.DxgkDdiSetVidPnSourceVisibility     = BddDdiSetVidPnSourceVisibility;
    InitialData.DxgkDdiCommitVidPn                  = BddDdiCommitVidPn;
    InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath;
    InitialData.DxgkDdiRecommendMonitorModes        = BddDdiRecommendMonitorModes;

    NTSTATUS Status = DxgkInitializeDisplayOnlyDriver(pDriverObject, pRegistryPath, &InitialData);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("DxgkInitializeDisplayOnlyDriver failed with Status: 0x%I64x", Status);
    }

    return Status;
}

从代码来看,KMOD中并没有完全实现KMDDOD_INITIALIZATION_DATA的结构体,所以它不能支持WDDM提供的全部Display相关的功能。比如D3D用户程序通过DC句柄和显示驱动进行交互的escape回调函数,这里就没有实现。对于没有实现的回调函数,在结构体中的对应函数指针应被初始化为NULL。

取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

Powered by bytekits.com,汇天下文字,成非凡梦想!!!