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

本主題中的程式碼範例示範 使用者模式驅動程式架構 (UMDF) 驅動程式如何在 簡單的周邊匯流排 上 (SPB) 取得操作裝置所需的硬體資源。 這些資源中包含驅動程式用來建立裝置邏輯連線的資訊。 其他資源可能包括中斷,以及一或多個 GPIO 輸入或輸出針腳。 (GPIO 針腳是設定為輸入或輸出的一般用途 I/O 控制器裝置上的針腳;如需詳細資訊,請參閱 GPIO 控制器驅動程式.) 不同于記憶體對應的裝置,SPB 連接的周邊裝置不需要系統記憶體位址區塊才能將其暫存器對應到其中。

此驅動程式會實作 IPnpCallbackHardware2 介面,並在呼叫驅動程式的 IDriverEntry::OnDeviceAdd 方法期間向 UMDF 註冊此介面。 架構會呼叫 IPnpCallbackHardware2 介面中的方法,以通知驅動程式裝置電源狀態的變更。

當電源還原至 SPB 連線的周邊裝置時,驅動程式架構會呼叫 IPnpCallbackHardware2::OnPrepareHardware 方法,通知驅動程式必須準備好使用此裝置。 在此呼叫期間,驅動程式會收到兩份硬體資源清單作為輸入參數。 pWdfResourcesRaw參數會指向原始資源清單,而pWdfResourcesTranslated參數會指向翻譯的資源清單。 這兩個參數都是 IWDFCmResourceList 物件的指標。 翻譯的資源包括 SPB 周邊驅動程式需要建立 SPB 連線周邊裝置的邏輯連線的 連線 識別碼。 如需詳細資訊,請參閱 SPB 周邊裝置的連線識別碼

若要讓 UMDF 周邊驅動程式在其資源清單中接收連線識別碼,安裝驅動程式的 INF 檔案必須在其 WDF 特定的 DDInstall 區段中包含下列指示詞:

UmdfDirectHardwareAccess = AllowDirectHardwareAccess 如需此指示詞的詳細資訊,請參閱 在 INF 檔案中指定 WDF 指示詞

下列程式碼範例示範驅動程式的 OnPrepareHardware 方法如何從 pWdfResourcesTranslated 參數取得連線識別碼。

BOOLEAN fConnectIdFound = FALSE;
BOOLEAN fDuplicateFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;

resourceCount = pWdfResourcesTranslated->GetCount();

// Loop through the resources and save the relevant ones.
for (ULONG ix = 0; ix < resourceCount; ix++)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;

    pDescriptor = pWdfResourcesTranslated->GetDescriptor(ix);

    if (pDescriptor == NULL)
    {
        hr = 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 (fConnIdFound == FALSE)
                    {
                        // Save the SPB connection ID.
                        connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
                        connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
                        fConnectIdFound = TRUE;
                    }
                    else
                    {
                        fDuplicateFound = TRUE;
                    }
                }
            }

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

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

    default:
        // Ignore all other resource descriptors.
        break;
    }
}

上述程式碼範例會將 SPB 連線周邊裝置的連接識別碼複製到名為 的 connectionId 變數中。 下列程式碼範例示範如何將連線識別碼併入裝置路徑名稱,以用來識別周邊裝置。

WCHAR szTargetPath[100];
HRESULT hres;

// Create the device path using the well-known resource hub
// path name and the connection ID.
//
// TODO: Replace this hardcoded string with the appropriate
//       helper method from Reshub.h when available.
hres = StringCbPrintfW(&szTargetPath[0],
                       sizeof(szTargetPath),
                       L"\\\\.\\RESOURCE_HUB\\%0*I64x",
                       (size_t)(sizeof(LARGE_INTEGER) * 2),
                       connectionId.QuadPart);
if (FAILED(hres))
{
     // Error handling
     ...
}

上述程式碼範例會將 SPB 連線周邊裝置 szTargetPath 的路徑名稱寫入陣列中。 下列程式碼範例會使用此裝置路徑名稱來開啟 SPB 連線周邊裝置的檔案控制碼。

UMDF_IO_TARGET_OPEN_PARAMS openParams;

openParams.dwShareMode = 0;
openParams.dwCreationDisposition = OPEN_EXISTING;
openParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED;
hres = pRemoteTarget->OpenFileByName(&szTargetPath[0],
                                     (GENERIC_READ | GENERIC_WRITE),
                                     &openParams);
if (FAILED(hres))
{
    // Error handling
    ...
}

在上述程式碼範例中 pRemoteTarget ,變數是 IWDFRemoteTarget 物件的指標。 如果呼叫 IWDFRemoteTarget::OpenFileByName 方法成功,SPB 連線周邊裝置的驅動程式可以使用 IWDFRemoteTarget 物件將 I/O 要求傳送至周邊裝置。 在驅動程式將讀取、寫入或 IOCTL 要求傳送至周邊裝置之前,驅動程式會呼叫 IWDFRemoteTarget::FormatRequestForReadIWDFRemoteTarget::FormatRequestForWriteIWDFRemoteTarget::FormatRequestForIoctl 方法來格式化 I/O 要求。 IWDFRemoteTarget介面會從IWDFIoTarget介面繼承這三種方法。 接下來,驅動程式會呼叫 IWDFIoRequest::Send 方法,將 I/O 要求傳送至 SPB 連線的周邊裝置。

在下列程式碼範例中,SPB 周邊驅動程式會呼叫 Send 方法,將 IRP_MJ_WRITE 要求傳送至 SPB 連線的周邊裝置。

HRESULT hres;
IWDFMemory *pInputMemory = NULL;
IWDFRemoteTarget *pWdfIoRequest = NULL;

// Create a new I/O request.
if (SUCCEEDED(hres))
{
    hres = pWdfDevice->CreateRequest(NULL, 
                                     pWdfDevice, 
                                     &pWdfIoRequest);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Allocate memory for the input buffer.
if (SUCCEEDED(hres))
{
    hres = pWdfDriver->CreatePreallocatedWdfMemory(pInBuffer, 
                                                   inBufferSize, 
                                                   NULL,
                                                   pWdfIoRequest,
                                                   &pInputMemory);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }

    // After this call, the parent holds the only reference.
    pWdfMemory->Release();
}

// Format the I/O request for a write operation.
if (SUCCEEDED(hres))
{
    hres = pRemoteTarget->FormatRequestForWrite(pWdfIoRequest,
                                                NULL,
                                                pInputMemory, 
                                                NULL, 
                                                0);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Send the request to the SPB controller.
if (SUCCEEDED(hres))
{
    ULONG Flags = fSynchronous ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0;

    // Set the I/O completion callback.
    if (!fSynchronous)
    {
        pWdfIoRequest->SetCompletionCallback(pCallback, NULL);
    }

    hres = pWdfIoRequest->Send(pRemoteTarget, Flags, 0);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

if (fSynchronous || FAILED(hres))
{
    pWdfIoRequest->DeleteWdfObject();
    SAFE_RELEASE(pWdfIoRequest);
}

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

  1. 變數 pWdfDevice 是表示 SPB 連線周邊裝置之架構裝置物件的 IWDFDevice 介面指標。 IWDFDevice::CreateRequest方法會建立 I/O 要求,並將此要求封裝在變數所指向的IWDFIoRequest介面實例中 pWdfIoRequest
  2. 變數 pWdfDriver 是架構驅動程式物件的 IWDFDriver 介面指標,代表 SPB 周邊驅動程式。 pInBufferinBufferSize 變數會指定包含寫入要求資料的輸入緩衝區位址和大小。 IWDFDriver::CreatePreallocatedWdfMemory方法會建立輸入緩衝區的架構記憶體物件,並指定所指向 pWdfIoRequestIWDFIoRequest物件做為記憶體物件的父物件 (,以便在其父物件釋放) 時自動釋放記憶體物件。 在驅動程式呼叫 Release 方法以釋放其記憶體物件的本機參考之後,父系會保留這個物件的唯一參考。
  3. 變數 pWdfRemoteTarget 是從先前程式碼範例中的 OpenFileByName 呼叫取得的遠端目標指標。 IWDFRemoteTarget::FormatRequestForWrite方法會格式化寫入作業的 I/O 要求。
  4. 如果 fSynchronous 寫入要求是同步傳送,則變數為 TRUE ,如果是以非同步方式傳送,則為 FALSE 。 變數 pCallback 是先前建立 之 IRequestCallbackRequestCompletion 介面的指標。 如果要以非同步方式傳送要求, 對 IWDFIoRequest::SetCompletionCallback 方法的呼叫會註冊此介面。 稍後會呼叫 IRequestCallbackRequestCompletion::OnCompletion 方法,以在要求非同步完成時通知驅動程式。
  5. Send方法會將格式化的寫入要求傳送至 SPB 連接的周邊裝置。 變數 Flags 會指出寫入要求是以同步或非同步方式傳送。
  6. 如果同步傳送要求, IWDFIoRequest::D eleteWdfObject 方法會刪除 所 pWdfIoRequest 指向的 I/O 要求物件,以及 所 pInputMemory 指向的子物件。 IWDFIoRequest介面會從IWDFObject介面繼承這個方法。 如果要求是以非同步方式傳送,則應該稍後在驅動程式的OnCompletion方法中呼叫DeleteWdfObject方法。

上述程式碼範例的替代實作可能會在驅動程式初始化期間建立 IWDFIoRequestIWDFMemory 物件,並重複使用這些相同的物件,而不是每次傳送 I/O 要求時建立和刪除新的物件。 如需詳細資訊,請參閱 IWDFIoRequest2::ReuseIWDFMemory::SetBuffer

此外,如果 傳送 呼叫成功,替代實作可能會檢查來自 I/O 要求的 I/O 狀態碼。 如需詳細資訊,請參閱 IWDFIoRequest::GetCompletionParams