共用方式為


Kernel-Mode SPB 周邊驅動程式的硬體資源

本主題中的程式碼範例示範 Kernel-Mode 驅動程式框架(KMDF)如何為 簡單周邊匯流排(SPB)上的周邊裝置的驅動程式取得操作裝置所需的硬體資源。 這些資源中包含驅動程式用來建立裝置邏輯連線的資訊。 其他資源可能包括中斷和一或多個 GPIO 輸入或輸出引腳。 (GPIO 針腳是一般用途 I/O 控制器裝置上的針腳,設定為輸入或輸出;如需詳細資訊,請參閱 General-Purpose I/O (GPIO) 驅動程式。 與記憶體映射的裝置不同,通過 SPB 連接的周邊裝置不需要系統記憶體位址區塊來映射其暫存器。

此驅動程式會實作一組隨插即用和電源管理事件回呼函式。 若要向 KMDF 註冊這些函式,驅動程式的 EvtDriverDeviceAdd 事件回呼函式會呼叫 WdfDeviceInitSetPnpPowerEventCallbacks 方法。 架構會呼叫電源管理事件回呼函式,以通知驅動程式周邊裝置電源狀態的變更。 這些函式中包含的是 EvtDevicePrepareHardware 函式,它會執行讓驅動程式存取裝置所需的任何作業。

當電源還原至周邊裝置時,驅動程式架構會呼叫 EvtDevicePrepareHardware 函式,以通知 SPB 周邊驅動程式必須準備好此裝置才能使用。 在此呼叫期間,驅動程式會收到兩份硬體資源清單作為輸入參數。 ResourcesRaw 參數是原始資源清單的 WDFCMRESLIST 物件句柄,ResourcesTranslated 參數是轉譯資源清單的 WDFCMRESLIST 物件句柄。 翻譯的資源包含驅動程式建立周邊裝置邏輯連線所需的 連線標識碼 。 如需詳細資訊,請參閱 SPB-Connected 周邊裝置的連線識別碼

下列程式代碼範例示範 EvtDevicePrepareHardware 函式如何從 ResourcesTranslated 參數取得連線標識碼。

BOOLEAN fConnectionIdFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;
NTSTATUS status = STATUS_SUCCESS;

resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);

// Loop through the resources and save the relevant ones.

for (ULONG ix = 0; ix < resourceCount; ix++)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;

    pDescriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, ix);

    if (pDescriptor == NULL)
    {
        status = E_POINTER;
        break;
    }

    // Determine the resource type.
    switch (pDescriptor->Type)
    {
    case CmResourceTypeConnection:
        {
            // Check against the expected connection types.

            UCHAR Class = pDescriptor->u.Connection.Class;
            UCHAR Type = pDescriptor->u.Connection.Type;

            if (Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL)
            {
                if (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C)
                {
                    if (fConnectionIdFound == FALSE)
                    {
                        // Save the SPB connection ID.

                        connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
                        connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
                        fConnectionIdFound = TRUE;
                    }
                }
            }

            if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO)
            {
                // Check for GPIO pin resource.
                ...
            }
        }
        break;

    case CmResourceTypeInterrupt:
        {
            // Check for interrupt resource.
            ...
        }
        break;

    default:
        // Don't care about other resource descriptors.
        break;
    }
}

上述程式碼範例會將 SPB 連線周邊裝置的連線識別碼複製到名為 connectionId的變數中。

下列程式代碼範例示範如何將此聯機標識碼併入裝置路徑名稱,以用來開啟周邊裝置的邏輯連線。 此裝置路徑名稱會將資源中樞識別為系統元件,以從中取得存取周邊裝置所需的參數。

// Use the connection ID to create the full device path name.
 
DECLARE_UNICODE_STRING_SIZE(szDeviceName, RESOURCE_HUB_PATH_SIZE);

status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&szDeviceName,
                                          connectionId.LowPart,
                                          connectionId.HighPart);

if (!NT_SUCCESS(status))
{
     // Error handling
     ...
}

在上述程式碼範例中,DECLARE_UNICODE_STRING_SIZE巨集會建立名為 szDeviceName 的初始化UNICODE_STRING變數的宣告,其緩衝區足夠大,可以包含資源中樞所使用格式的裝置路徑名稱。 此巨集定義於 Ntdef.h 頭檔中。 RESOURCE_HUB_PATH_SIZE常數會指定裝置路徑名稱中的位元元組數目。 RESOURCE_HUB_CREATE_PATH_FROM_ID巨集會從連線標識碼產生裝置路徑名稱。 RESOURCE_HUB_PATH_SIZERESOURCE_HUB_CREATE_PATH_FROM_ID 定義於 Reshub.h 頭檔中。

下列程式碼範例會使用裝置路徑名稱來開啟 SPB 連線周邊裝置的檔案句柄 (名為 SpbIoTarget) 。

// Open the SPB peripheral device as a remote I/O target.
 
WDF_IO_TARGET_OPEN_PARAMS openParams;
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&openParams,
                                            &szDeviceName,
                                            (GENERIC_READ | GENERIC_WRITE));

openParams.ShareAccess = 0;
openParams.CreateDisposition = FILE_OPEN;
openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;

status = WdfIoTargetOpen(SpbIoTarget, &openParams);

if (!NT_SUCCESS(status))
{
    // Error handling
    ...
}

在上述程式碼範例中, WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME 函式會初始化 WDF_IO_TARGET_OPEN_PARAMS 結構,讓驅動程式藉由指定裝置名稱來開啟周邊裝置的邏輯連線。 變數 SpbIoTarget 是架構 I/O 目標物件的 WDFIOTARGET 句柄。 此句柄是從先前對 WdfIoTargetCreate 方法的呼叫取得的,此方法不會顯示在範例中。 如果 呼叫 WdfIoTargetOpen 方法成功,驅動程式可以使用 SpbIoTarget 句柄將 I/O 要求傳送至周邊裝置。

EvtDriverDeviceAdd 事件回呼函式中,SPB 周邊驅動程式可以呼叫 WdfRequestCreate 方法,以配置驅動程式使用的架構要求物件。 稍後,當不再需要物件時,驅動程式會呼叫 WdfObjectDelete 方法來刪除物件。 驅動程式可以重複使用從 WdfRequestCreate 呼叫取得的架構要求物件多次,以將 I/O 要求傳送至周邊裝置。 針對讀取、寫入或 IOCTL 要求,驅動程式會呼叫 WdfIoTargetSendReadSynchronouslyWdfIoTargetSendWriteSynchronouslyWdfIoTargetSendIoctlSynchronously 方法來傳送要求。

在下列程式碼範例中,驅動程式會呼叫 WdfIoTargetSendWriteSynchronously ,以同步方式將 IRP_MJ_WRITE 要求傳送至 SPB 連線的周邊裝置。 在此範例開始時, pBuffer 變數會指向包含要寫入周邊裝置之數據的非分頁緩衝區,而變數會 dataSize 指定此數據的大小,以位元組為單位。

ULONG_PTR bytesWritten;
NTSTATUS status;

// Describe the input buffer.

WDF_MEMORY_DESCRIPTOR memoryDescriptor;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, pBuffer, dataSize);

// Configure the write request to time out after 2 seconds.

WDF_REQUEST_SEND_OPTIONS requestOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
requestOptions.Timeout = WDF_REL_TIMEOUT_IN_SEC(2);

// Send the write request synchronously.

status = WdfIoTargetSendWriteSynchronously(SpbIoTarget,
                                           SpbRequest,
                                           &memoryDescriptor,
                                           NULL,
                                           &requestOptions,
                                           &bytesWritten);
if (!NT_SUCCESS(status))
{
    // Error handling
    ...
}

上述程式代碼範例會執行下列動作:

  1. WDF_MEMORY_DESCRIPTOR_INIT_BUFFER函數調用會memoryDescriptor初始化 變數,這是描述輸入緩衝區的WDF_MEMORY_DESCRIPTOR結構。 先前,驅動程式會呼叫 ExAllocatePoolWithTag 之類的例程,以從非分頁集區配置緩衝區,並將寫入數據複製到此緩衝區。
  2. WDF_REQUEST_SEND_OPTIONS_INIT函數調用會requestOptions初始化 變數,這是包含寫入要求選擇性設定的WDF_REQUEST_SEND_OPTIONS結構。 在此範例中,如果要求在兩秒后未完成,則結構會將要求設定為逾時。
  3. 呼叫 WdfIoTargetSendWriteSynchronously 方法會將寫入要求傳送至 SPB 連線的周邊裝置。 在寫入作業完成或逾時之後,方法會以同步方式傳回。如有必要,另一個驅動程式線程可以呼叫 WdfRequestCancelSentRequest 來取消要求。

WdfIoTargetSendWriteSynchronously 呼叫中,驅動程式會提供名為 SpbRequest的變數,這是驅動程式先前建立之架構要求物件的句柄。 在 WdfIoTargetSendWriteSynchronous 呼叫之後,驅動程式通常應該呼叫 WdfRequestReuse 方法來準備要再次使用的架構要求物件。