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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章