使用 Driver-Defined 介面

驅動程式可以定義其他驅動程式可以存取的裝置特定介面。 這些 驅動程式定義的介面 可以包含一組可呼叫的常式、一組資料結構,或兩者。 驅動程式通常會提供驅動程式定義介面結構中這些常式和結構的指標,而驅動程式可提供給其他驅動程式使用。

例如,如果子裝置的資源清單中沒有該資訊,匯流排驅動程式可能會提供一或多個較高層級驅動程式可以呼叫的常式,以取得子裝置的相關資訊。

如需 WDK 中記載的一組驅動程式定義介面範例,請參閱 USB 常式。 此外,請參閱以架構為基礎的 咖啡機 範例版本。

建立介面

每個驅動程式定義的介面都是由下列方式指定:

  • GUID

  • 版本號碼

  • 驅動程式定義的介面結構

  • 參考和取值常式

若要建立介面並使其可供其他驅動程式使用,架構型驅動程式可以使用下列步驟:

  1. 定義介面結構。

    這個驅動程式定義結構的第一個成員必須是 INTERFACE 標頭結構。 其他成員可能包含介面資料和其他驅動程式可以呼叫之其他結構或常式的指標。

    驅動程式必須提供 WDF_QUERY_INTERFACE_CONFIG 結構,其描述您已定義的介面。

    注意

    使用 WDF_QUERY_INTERFACE_CONFIG時,WDF 不支援使用相同介面 GUID 的單一介面多個版本。

    因此,引進新版的現有介面時,建議您建立新的 GUID,而不是修改INTERFACE結構的[大小] 或 [版本] 欄位。

    如果您的驅動程式會重複使用具有相同的介面 GUID 與修改的大小版本欄位,驅動程式不應該提供WDF_QUERY_INTERFACE_CONFIG,而是應該為IRP_MN_QUERY_INTERFACE提供EvtDeviceWdmIrpPreprocess回呼常式。

  2. 呼叫 WdfDeviceAddQueryInterface

    WdfDeviceAddQueryInterface方法會執行下列動作:

    • 儲存介面的相關資訊,例如其 GUID、版本號碼和結構大小,因此架構可以辨識介面的另一個驅動程式要求。
    • 註冊選擇性 的 EvtDeviceProcessQueryInterfaceRequest 事件回呼函式,當另一個驅動程式要求介面時,架構會呼叫此函式。

驅動程式定義介面的每個實例都與個別裝置相關聯,因此驅動程式通常會從EvtDriverDeviceAddEvtChildListCreateDevice回呼函式內呼叫WdfDeviceAddQueryInterface

存取介面

如果您的驅動程式已定義介面,另一個架構型驅動程式可以藉由呼叫 WdfFdoQueryForInterface 並傳遞 GUID、版本號碼、結構的指標和結構大小來要求存取介面。 架構會建立 I/O 要求,並將其傳送至驅動程式堆疊頂端。

驅動程式通常會從EvtDriverDeviceAdd回呼函式內呼叫WdfFdoQueryForInterface。 或者,如果驅動程式必須在裝置處於運作狀態時釋放介面,驅動程式可以從EvtDevicePrepareHardware回呼函式內呼叫WdfFdoQueryForInterface,並從EvtDeviceReleaseHardware回呼函式內呼叫介面的取值常式。

如果驅動程式 A 向驅動程式 B 要求驅動程式 B 已定義的介面,架構就會處理驅動程式 B 的要求。架構會確認 GUID 和版本代表支援的介面,以及驅動程式 A 所提供的結構大小足以保存介面。

當驅動程式呼叫 WdfFdoQueryForInterface時,架構所建立的 I/O 要求會一路傳輸至驅動程式堆疊底部。 如果簡單的驅動程式堆疊包含三個驅動程式 -A、B 和 C -,如果驅動程式 A 要求介面,則驅動程式 B 和驅動程式 C 都可以支援 介面。 例如,驅動程式 B 可能會在將要求向下傳遞至驅動程式 C 之前填入驅動程式 A 的介面結構。驅動程式 C 可以提供 EvtDeviceProcessQueryInterfaceRequest 回呼函式,以檢查介面結構的內容,並可能加以修改。

如果驅動程式 A 需要存取驅動程式 B 的介面,而驅動程式 B 是遠端 I/O 目標 () ,則驅動程式 A 必須呼叫 WdfIoTargetQueryForInterface ,而不是 WdfFdoQueryForInterface

使用One-Way或Two-Way通訊

您可以定義提供單向通訊的介面,或定義提供雙向通訊的介面。 若要指定雙向通訊,您的驅動程式會將其WDF_QUERY_INTERFACE_CONFIG結構的ImportInterface成員設定為TRUE

如果介面提供單向通訊,如果驅動程式 A 要求驅動程式 B 的介面,介面資料只會從驅動程式 B 流向驅動程式 A。當架構收到支援單向通訊之介面的驅動程式 A 要求時,架構會將驅動程式定義的介面值複製到驅動程式 A 的介面結構中。 然後它會呼叫驅動程式 B 的 EvtDeviceProcessQueryInterfaceRequest 回呼函式,如果存在,它就可以檢查並可能修改介面值。

如果介面提供雙向通訊,介面結構會包含驅動程式 A 在傳送要求給驅動程式 B 之前填入的一些成員。驅動程式 B 可以讀取驅動程式 A 所提供的參數值,並根據這些值,根據要提供給驅動程式 A 的資訊做出選擇。當架構收到支援雙向通訊之介面的驅動程式 A 要求時,架構會呼叫驅動程式 B 的 EvtDeviceProcessQueryInterfaceRequest 回呼函式,以便檢查收到的值並提供輸出值。 針對雙向通訊,需要回呼函式,因為架構不會將任何介面值複製到驅動程式 A 的介面結構。

維護參考計數

每個介面都必須包含參考函式和取值函式,以遞增和遞減介面的參考計數。 定義 介面的驅動程式會在其 INTERFACE 結構中指定這些函式的位址。

當驅動程式 A 向驅動程式 B 要求介面時,架構會先呼叫介面的參考函式,再讓驅動程式 A 使用介面。當驅動程式 A 使用介面完成時,它必須呼叫介面的取值函式。

大部分介面的參考和取值函式可以是不執行任何動作的作業函式。 此架構提供大部分驅動程式可以使用的 no-op 參考計數函式 WdfDeviceInterfaceReferenceNoOpWdfDeviceInterfaceDereferenceNoOp

驅動程式必須追蹤介面的參考計數,並提供實際參考和取值函式的唯一時間,就是當驅動程式 A 從遠端 I/O 目標要求介面時, (也就是位於不同驅動程式堆疊的驅動程式) 。 在此情況下,驅動程式 B (在不同的堆疊) 必須實作參考計數,以避免在驅動程式 A 使用驅動程式 B 的介面時移除其裝置。

如果您要設計定義介面的驅動程式 B,您必須決定是否要從不同的驅動程式堆疊存取驅動程式的介面。 (驅動程式 B 無法判斷其介面的要求是否來自本機驅動程式堆疊或遠端堆疊。) 如果您的驅動程式將支援來自遠端堆疊的介面要求,驅動程式必須實作參考計數。

如果您要設計驅動程式 A,以存取遠端 I/O 目標上的介面,驅動程式必須提供 EvtIoTargetQueryRemove 回呼函式,以在即將移除驅動程式 B 的裝置時釋放介面、 EvtIoTargetRemoveComplete 回呼函式,該函式會在驅動程式 B 的裝置遭到意外移除時釋放介面,以及 EvtIoTargetRemoveCanceled 如果嘗試移除裝置已取消,則會重新取得介面的回呼函式。