Windows显示驱动(WDDM)编程初步(1)

欢迎转载作者:张佩】【原文:http://www.yiiyee.cn/Blog/wddm1/

WDDM Frame

Windows显示驱动从Vista开始,使用新的WDDM编程框架,称为Windows Display Driver  Model。也有一种最初的名称是LDDM,L代表Longhorn,但后来微软在所有产品线上都不再使用Longhorn代号,故而改成现在的名称。虽然在有些地方还能看到LDDM的说法,但应理解成旧文档的遗存,不应该做概念上的区分。

WDDM框架是一种典型的小端口(miniport)驱动框架。NT系统中的所有小端口框架,都是基于WDM框架来实现的,但小端口框架对外提供了更高级的接口,以简化编程的难度,并提高稳定性。如下图所示,中间的WDDM是系统提供的编程框架,我们基于这个框架,编写里面的小端口驱动,也就是显示驱动。

显示驱动类型

现在的显卡设备,可以按照功能将它分成显示和计算两类。大部分的显卡是用来连接显示器显示图片和动画用的,也有些显卡主要确实用来做科学计算用的。显卡处理器(GPU)对浮点运算有较强的能力,而主机处理器(CPU)处理浮点运算的能力较弱。而在科学计算领域,浮点运算是非常重要的内容,所以工业界就想到利用GPU进行科学运算。

应该说,所有的显卡都既能够支持显示,又能够支持运算。只是看它偏向哪个方面,为哪个功能做优化罢了。对于偏重计算的显卡,就不必配置多个显示接口,图像处理的模块就不用很高级;相反,对于图形功能偏重的显卡,它就必须要大数据带宽,大显存,支持多种类型的接口,能够实现锯齿优化等等。

针对我们的驱动来讲,如果一个显示驱动,既支持显卡的显示功能,又支持运算功能,称为全功能驱动(Complete function);如果只支持显示,不支持运算,就是Display Only驱动;如果只支持运算,不支持显示功能,就是Render Only驱动。

微软在Win8的系统上,为所有不同类型的显卡,编写了Display Only和Render Only驱动。在未安装厂商驱动或者厂商驱动被破坏、禁用的情况下,系统会默认选择使用Display Only驱动来显示桌面内容。但一般系统不会选择安装Render Only驱动,那样就什么都看不到了。Render Only驱动的具体应用场景,我到目前还没有看到。可能在Render Only的数据服务器显卡上会被运用。

我的这份显示驱动初步教材,就是基于微软公开的Display Only驱动项目KMDOD来写的。不会涉及数据Render部分。其实可以很方便地把一个Display only的驱动拓展到Complete驱动,在讲完所有内容后,会有一小部分内容做介绍。

KMDOD项目可以从MSDN代码网站上下载到,地址:http://code.msdn.microsoft.com/Kernel-mode-display-only-49adea58

初始化

如果不更改编译配置,WDDM驱动的默认启动函数是DriverEntry。这是驱动对象初始化的地方,一般对于小端口驱动而言,它需要调用框架的初始化函数。WDDM框架的初始化函数是DxgkInitializeDisplayOnlyDriver。从内核编程好帮手WDK中,可以找到它的声明:

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

初始化函数会完成驱动对象的初始化,所以前面两个参数是入口函数的输入参数。在全文最后的实验章节中,会介绍如何查看驱动对象,就能够比较清晰地看到WDDM框架对显示驱动对象所进行的初始化作业了。此外,初始化函数还要完成显示驱动相关的初始化。显示驱动传入一个函数结构体参数,类型是KMDDOD_INITIALIZATION_DATA,结构体里面包含的是显示驱动向框架提供的一系列回调函数(Callback Function)。框架会在合适的时候调用这些回调函数,完成对应功能。

结构体定义如下:

InitializationData

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

第一个参数Version用来标识你所编写的显示驱动使用哪个版本的WDDM。WDDM一共有四个版本:1.0(Vista & Vista SP1);1.1(Win7);1.2(Win8);1.3(Win Blue)。KMDOD这个项目中使用的是Win8版本:DXGKDDI_INTERFACE_VERSION_WIN8。

为了完成结构体初始化,我们要首先实现这些函数。在具体列举实现代码之前,把这些回调函数做一个简单的分类和介绍是有必要的。

PNP和POWER函数

所有现代的物理设备都必须处理Pnp和Power事件。Pnp事件对应了设备插拔、开始、移除、停止等,以及作为总线设备需要提供的子设备(显示器)枚举等;Power事件对应上电、掉电操作,以及查询设备是否运行进行电源操作。

另外把卸载回调函数也归入其中。卸载函数在驱动被停止,没有任何外部模块引用的时候,系统会尝试将驱动卸载,这时候卸载回调被调用。

InitialData.DxgkDdiAddDevice                    = BddDdiAddDevice;
InitialData.DxgkDdiStartDevice                  = BddDdiStartDevice;
InitialData.DxgkDdiStopDevice                   = BddDdiStopDevice;
InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership;
InitialData.DxgkDdiResetDevice                  = BddDdiResetDevice;
InitialData.DxgkDdiRemoveDevice                 = BddDdiRemoveDevice;
InitialData.DxgkDdiQueryChildRelations          = BddDdiQueryChildRelations;
InitialData.DxgkDdiQueryChildStatus             = BddDdiQueryChildStatus;
InitialData.DxgkDdiQueryDeviceDescriptor        = BddDdiQueryDeviceDescriptor;
InitialData.DxgkDdiSetPowerState                = BddDdiSetPowerState;
InitialData.DxgkDdiUnload                       = BddDdiUnload; 
InitialData.DxgkDdiQueryAdapterInfo             = BddDdiQueryAdapterInfo;

显示函数

显卡驱动的主要功能是配置物理设备,让它能够输出图片和动画到外部显示设备上。和这个功能相关的函数有很多,它包括对鼠标位置的更新,显示器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;

硬件操作

最后是和物理设备交互的一些函数,首先是中断处理函数,然后有获取设备属性,读写设备帧内存、显示桌面内容(Present)等函数。

InitialData.DxgkDdiDpcRoutine                   = BddDdiDpcRoutine;
InitialData.DxgkDdiInterruptRoutine             = BddDdiInterruptRoutine;
InitialData.DxgkDdiQueryVidPnHWCapability       = BddDdiQueryVidPnHWCapability;
InitialData.DxgkDdiPresentDisplayOnly           = BddDdiPresentDisplayOnly;
InitialData.DxgkDdiSystemDisplayEnable          = BddDdiSystemDisplayEnable;
InitialData.DxgkDdiSystemDisplayWrite           = BddDdiSystemDisplayWrite;

功能函数

这部分是显示驱动作为一个驱动来讲,它所实现的一般意义上的功能支持函数。这部分我只列了一个,是用户程序和内核驱动交互用的IO控制函数。

InitialData.DxgkDdiDispatchIoRequest            = BddDdiDispatchIoRequest;

完整的初始化函数:

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_WIN8;

    InitialData.DxgkDdiAddDevice = BddDdiAddDevice;
    //…… 其它的回调函数赋值过程,上面已全部列举,此处省略

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

    return Status;
}

可选的扩展

初始化部分到此本来可以结束,继续讲下面的回调函数实现。但其实依然可以扩展一下,不熟悉WDM的读者可以跳过。针对所有的端口驱动框架都基于WDM来实现的这个事实,如果有的小端口驱动有必要想直接操作IRP的话,应该怎么实现呢?

其实非常简单。在DxgkInitializeDisplayOnlyDriver被调用过之后,驱动对象的初始化已经完成了。这时候我们可以对框架的分发函数进行Hook。

比如有一个很重要的功能,很多设备驱动都要做的。就是它希望自己能够得到系统关机的讯息。通过一般意义上的PNP和Power事件,是没有办法得到系统关机讯息的。办法是注册自己的IRP_MJ_SHUTDOWN分发函数来接收此讯息。

下面是简要的实现代码:

// 下面代码应在DxgkInitializeDisplayOnlyDriver被调用过后执行
pOldFunc = pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN]; // 保存框架有可能已实现的函数
pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = BddDdiShutdown;

// 此外还需要在StartDevice函数中调用IoRegisterShutdownNotificatin函数,本文不再继续演示

NTSTATUS BddDdiShutdown (PDEVICE_OBJECT pDev, PIRP irp)
{
	// do something you wanted here

	if (pOldFunc)
		return pOldFunc (pDev, irp);
	else
		return STATUS_SUCCESS;
}

回调函数实现

KMDOD项目定义了一个显示驱动类,来封装和实际功能相关的所有具体操作。这样一来,大部分回调函数的实现都比较简单。这个类是BASIC_DISPLAY_DRIVER,我们在第二节会具体地讲它。

生命周期

WDDM框架在设计的时候,是能够支持多个底层物理设备的。换句话说,如果系统中存在多个显卡设备,WDDM框架都能够很好地支持它们同时或者分别工作。作为这个支持的一部分,在PNP操作的起始,也就是AddDevice回调函数被调用的时候,框架要求显示驱动返回一个当前物理设备的Context,作为识别此物理设备的标识。

那么KMDOD也是可以支持多个显卡设备的,所以为每个设备创建一个BASIC_DISPLAY_DRIVER对象,作为Context返回给框架。框架在以后调用任何一个回调函数时,都会把这个Context作为其中的一个输入参数来使用。

WDDM Life

所以我们看,DriverEntry是驱动的开始,卸载函数是驱动的结束;AddDevice函数是设备工作的开始,RemoveDevice是设备结束工作的标识。我们可以用下面的框图来描述这个概念。

设备的Context作为一个标识物理显卡的变量,在设备的PNP周期里面一直运作着。当关机、设备禁用或者其他变故发生的时候,RemoveDevice回调被执行,显示驱动将负责删除它所创建的设备Context。

 

下面是AddDevice和RemoveDevice这两个回调函数的实现。

NTSTATUS
BddDdiAddDevice(
    _In_ DEVICE_OBJECT* pPhysicalDeviceObject,
    _Outptr_ PVOID*  ppDeviceContext)
{
    PAGED_CODE();

    if ((pPhysicalDeviceObject == NULL) ||
        (ppDeviceContext == NULL))
    {
        BDD_LOG_ERROR2("One of pPhysicalDeviceObject (0x%I64x), ppDeviceContext (0x%I64x) is NULL",
                        pPhysicalDeviceObject, ppDeviceContext);
        return STATUS_INVALID_PARAMETER;
    }
    *ppDeviceContext = NULL;

    BASIC_DISPLAY_DRIVER* pBDD = new(NonPagedPoolNx) BASIC_DISPLAY_DRIVER(pPhysicalDeviceObject);
    if (pBDD == NULL)
    {
        BDD_LOG_LOW_RESOURCE0("pBDD failed to be allocated");
        return STATUS_NO_MEMORY;
    }

    *ppDeviceContext = pBDD;

    return STATUS_SUCCESS;
}

NTSTATUS
BddDdiRemoveDevice(
    _In_  VOID* pDeviceContext)
{
    PAGED_CODE();

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);

    if (pBDD)
    {
        delete pBDD;
        pBDD = NULL;
    }

    return STATUS_SUCCESS;
}

其它回调函数实现

其它回调函数的实现,这里仅仅以StartDevice为例讲解。函数原型定义如下:

NTSTATUS DxgkDdiStartDevice(
  _In_   const PVOID MiniportDeviceContext,
  _In_   PDXGK_START_INFO DxgkStartInfo,
  _In_   PDXGKRNL_INTERFACE DxgkInterface,
  _Out_  PULONG NumberOfVideoPresentSources,
  _Out_  PULONG NumberOfChildren
)

第一个参数即设备Context,毫无疑问,它就是我们刚刚在AddDevice中创建的BASIC_DISPLAY_DRIVER对象。所以我们第一步需要获取对象指针,并且调用到BASIC_DISPLAY_DRIVER里面的startDevice函数中去。其实现如下:

NTSTATUS
BddDdiStartDevice(
    _In_  VOID*              pDeviceContext,
    _In_  DXGK_START_INFO*   pDxgkStartInfo,
    _In_  DXGKRNL_INTERFACE* pDxgkInterface,
    _Out_ ULONG*             pNumberOfViews,
    _Out_ ULONG*             pNumberOfChildren)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->StartDevice(pDxgkStartInfo, pDxgkInterface, pNumberOfViews, pNumberOfChildren);
}

其它的回调函数,实现方法和StartDevice很类似。唯一的区别是,如果StartDevice调用失败的话,也就是设备启动失败的话,道理上讲,很多后续的函数都不应该被调用,因为既然设备没有启动,就不应该有任何针对于它的动作存在。所以这些函数被调用的时候,很多都会先确认一下,设备是否处于启动状态(IsDriverActive),就是判断StartDevice是否执行成功。

下面的函数清单是所有回调函数的实现。

NTSTATUS
BddDdiStopDevice(
    _In_  VOID* pDeviceContext)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->StopDevice();
}

NTSTATUS
BddDdiDispatchIoRequest(
    _In_  VOID*                 pDeviceContext,
    _In_  ULONG                 VidPnSourceId,
    _In_  VIDEO_REQUEST_PACKET* pVideoRequestPacket)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->DispatchIoRequest(VidPnSourceId, pVideoRequestPacket);
}

NTSTATUS
BddDdiSetPowerState(
    _In_  VOID*              pDeviceContext,
    _In_  ULONG              HardwareUid,
    _In_  DEVICE_POWER_STATE DevicePowerState,
    _In_  POWER_ACTION       ActionType)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        // If the driver isn't active, SetPowerState can still be called, however in BDD's case
        // this shouldn't do anything, as it could for instance be called on BDD Fallback after
        // Fallback has been stopped and BDD PnP is being started. Fallback doesn't have control
        // of the hardware in this case.
        return STATUS_SUCCESS;
    }
    return pBDD->SetPowerState(HardwareUid, DevicePowerState, ActionType);
}

NTSTATUS
BddDdiQueryChildRelations(
    _In_                             VOID*                  pDeviceContext,
    _Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations,
    _In_                             ULONG                  ChildRelationsSize)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->QueryChildRelations(pChildRelations, ChildRelationsSize);
}

NTSTATUS
BddDdiQueryChildStatus(
    _In_    VOID*              pDeviceContext,
    _Inout_ DXGK_CHILD_STATUS* pChildStatus,
    _In_    BOOLEAN            NonDestructiveOnly)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->QueryChildStatus(pChildStatus, NonDestructiveOnly);
}

NTSTATUS
BddDdiQueryDeviceDescriptor(
    _In_  VOID*                     pDeviceContext,
    _In_  ULONG                     ChildUid,
    _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        // During stress testing of PnPStop, it is possible for BDD Fallback to get called to start then stop in quick succession.
        // The first call queues a worker thread item indicating that it now has a child device, the second queues a worker thread
        // item that it no longer has any child device. This function gets called based on the first worker thread item, but after
        // the driver has been stopped. Therefore instead of asserting like other functions, we only warn.
        BDD_LOG_WARNING1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->QueryDeviceDescriptor(ChildUid, pDeviceDescriptor);
}

//
// WDDM Display Only Driver DDIs
//

NTSTATUS
APIENTRY
BddDdiQueryAdapterInfo(
    _In_ CONST HANDLE                    hAdapter,
    _In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    return pBDD->QueryAdapterInfo(pQueryAdapterInfo);
}

NTSTATUS
APIENTRY
BddDdiSetPointerPosition(
    _In_ CONST HANDLE                      hAdapter,
    _In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->SetPointerPosition(pSetPointerPosition);
}

NTSTATUS
APIENTRY
BddDdiSetPointerShape(
    _In_ CONST HANDLE                   hAdapter,
    _In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->SetPointerShape(pSetPointerShape);
}

NTSTATUS
APIENTRY
BddDdiPresentDisplayOnly(
    _In_ CONST HANDLE                       hAdapter,
    _In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->PresentDisplayOnly(pPresentDisplayOnly);
}

NTSTATUS
APIENTRY
BddDdiStopDeviceAndReleasePostDisplayOwnership(
    _In_  VOID*                          pDeviceContext,
    _In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _Out_ DXGK_DISPLAY_INFORMATION*      DisplayInfo)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->StopDeviceAndReleasePostDisplayOwnership(TargetId, DisplayInfo);
}

NTSTATUS
APIENTRY
BddDdiIsSupportedVidPn(
    _In_ CONST HANDLE                 hAdapter,
    _Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        // This path might hit because win32k/dxgport doesn't check that an adapter is active when taking the adapter lock.
        // The adapter lock is the main thing BDD Fallback relies on to not be called while it's inactive. It is still a rare
        // timing issue around PnpStart/Stop and isn't expected to have any effect on the stability of the system.
        BDD_LOG_WARNING1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->IsSupportedVidPn(pIsSupportedVidPn);
}

NTSTATUS
APIENTRY
BddDdiRecommendFunctionalVidPn(
    _In_ CONST HANDLE                                  hAdapter,
    _In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->RecommendFunctionalVidPn(pRecommendFunctionalVidPn);
}

NTSTATUS
APIENTRY
BddDdiRecommendVidPnTopology(
    _In_ CONST HANDLE                                 hAdapter,
    _In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST  pRecommendVidPnTopology)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->RecommendVidPnTopology(pRecommendVidPnTopology);
}

NTSTATUS
APIENTRY
BddDdiRecommendMonitorModes(
    _In_ CONST HANDLE                                hAdapter,
    _In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST  pRecommendMonitorModes)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->RecommendMonitorModes(pRecommendMonitorModes);
}

NTSTATUS
APIENTRY
BddDdiEnumVidPnCofuncModality(
    _In_ CONST HANDLE                                 hAdapter,
    _In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->EnumVidPnCofuncModality(pEnumCofuncModality);
}

NTSTATUS
APIENTRY
BddDdiSetVidPnSourceVisibility(
    _In_ CONST HANDLE                            hAdapter,
    _In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->SetVidPnSourceVisibility(pSetVidPnSourceVisibility);
}

NTSTATUS
APIENTRY
BddDdiCommitVidPn(
    _In_ CONST HANDLE                     hAdapter,
    _In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->CommitVidPn(pCommitVidPn);
}

NTSTATUS
APIENTRY
BddDdiUpdateActiveVidPnPresentPath(
    _In_ CONST HANDLE                                      hAdapter,
    _In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->UpdateActiveVidPnPresentPath(pUpdateActiveVidPnPresentPath);
}

NTSTATUS
APIENTRY
BddDdiQueryVidPnHWCapability(
    _In_ CONST HANDLE                       hAdapter,
    _Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->QueryVidPnHWCapability(pVidPnHWCaps);
}
//END: Paged Code
#pragma code_seg(pop)

#pragma code_seg(push)
#pragma code_seg()
// BEGIN: Non-Paged Code

VOID
BddDdiDpcRoutine(
    _In_  VOID* pDeviceContext)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return;
    }
    pBDD->DpcRoutine();
}

BOOLEAN
BddDdiInterruptRoutine(
    _In_  VOID* pDeviceContext,
    _In_  ULONG MessageNumber)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->InterruptRoutine(MessageNumber);
}

VOID
BddDdiResetDevice(
    _In_  VOID* pDeviceContext)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    pBDD->ResetDevice();
}

NTSTATUS
APIENTRY
BddDdiSystemDisplayEnable(
    _In_  VOID* pDeviceContext,
    _In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _In_  PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags,
    _Out_ UINT* Width,
    _Out_ UINT* Height,
    _Out_ D3DDDIFORMAT* ColorFormat)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->SystemDisplayEnable(TargetId, Flags, Width, Height, ColorFormat);
}

VOID
APIENTRY
BddDdiSystemDisplayWrite(
    _In_  VOID* pDeviceContext,
    _In_  VOID* Source,
    _In_  UINT  SourceWidth,
    _In_  UINT  SourceHeight,
    _In_  UINT  SourceStride,
    _In_  UINT  PositionX,
    _In_  UINT  PositionY)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    pBDD->SystemDisplayWrite(Source, SourceWidth, SourceHeight, SourceStride, PositionX, PositionY);
}

版本历史:

V1.0:2013/7/23

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值