ユーザーモード SPB 周辺機器ドライバーのハードウェア リソース

このトピックのコード例では、単純な周辺機器バス (SPB) 上の周辺機器のユーザー モード ドライバー フレームワーク (UMDF) ドライバーが、デバイスの操作に必要なハードウェア リソースを取得する方法を示します。 これらのリソースには、ドライバーがデバイスへの論理接続を確立するために使用する情報が含まれます。 その他のリソースには、割り込みと 1 つ以上の GPIO 入力ピンまたは出力ピンが含まれる場合があります。 (GPIO ピンは、入力または出力として構成されている汎用 I/O コントローラー デバイス上のピンです。詳細については 、「GPIO Controller Drivers」を参照してください。) メモリ マップされたデバイスとは異なり、SPB に接続された周辺機器では、そのレジスタをマップするためにシステム メモリ アドレスのブロックは必要ありません。

このドライバーは、IPnpCallbackHardware2 インターフェイスを実装し、ドライバーの IDriverEntry::OnDeviceAdd メソッドの呼び出し中に UMDF にこのインターフェイスを登録します。 フレームワークは、デバイスの電源状態の変化をドライバーに通知する IPnpCallbackHardware2 インターフェイスのメソッドを呼び出します。

SPB に接続された周辺機器に電源が復元されると、ドライバー フレームワークは IPnpCallbackHardware2::OnPrepareHardware メソッドを呼び出して、このデバイスを使用するのに準備する必要があることをドライバーに通知します。 この呼び出し中に、ドライバーは、入力パラメーターとしてハードウェア リソースの 2 つのリストを受け取ります。 pWdfResourcesRaw パラメーターは未加工リソースの一覧を指し、pWdfResourcesTranslated パラメーターは変換されたリソースの一覧を指します。 どちらのパラメーターも IWDFCmResourceList オブジェクトへのポインターです。 変換されたリソースには、 SPB 周辺機器ドライバーが SPB 接続周辺機器への論理接続を確立するために必要な接続 ID が含まれます。 詳細については、「SPB周辺機器 の接続 ID」を参照してください。

UMDF 周辺機器ドライバーがリソース一覧で接続 ID を受信できるようにするには、ドライバーをインストールする INF ファイルの WDF 固有 の DDInstall セクションに次のディレクティブを含める必要があります。

UmdfDirectHardwareAccess = AllowDirectHardwareAccess このディレクティブの詳細については、「INF ファイルでの WDF ディレクティブ指定」を参照してください。

次のコード例は、ドライバーの OnPrepareHardware メソッドが pWdfResourcesTranslated パラメーターから接続 ID を取得する方法を示しています。

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 接続周辺機器の接続 ID を connectionId という名前の変数にコピーします。 次のコード例は、周辺機器を識別するのに使用できるデバイス パス名に接続 ID を組み込む方法を示しています。

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::FormatRequestForWrite、または IWDFRemoteTarget::FormatRequestForIoctl メソッドを呼び出して I/O 要求を書式設定します。 IWDFRemoteTarget インターフェイスは、IWDFIoTarget インターフェイスからこれら 3 つのメソッドを継承します。 次に、ドライバーは IWDFIoRequest::Send メソッドを呼び出して、SPB に接続された周辺機器に I/O 要求を送信します。

次のコード例では、SPB 周辺機器ドライバーは Send メソッドを呼び出して、SPB に接続された周辺機器に IRP_MJ_WRITE 要求を送信します。

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 要求を作成し、この要求を pWdfIoRequest 変数が指す IWDFIoRequest インターフェイス インスタンスにカプセル化します。
  2. pWdfDriver 変数は、SPB 周辺機器ドライバーを表すフレームワーク ドライバー オブジェクトの IWDFDriver インターフェイスへのポインターです。 pInBuffer 変数と inBufferSize 変数は、書き込み要求のデータを含む入力バッファーのアドレスとサイズを指定します。 IWDFDriver::CreatePreallocatedWdfMemory メソッドは、入力バッファーのフレームワーク メモリ オブジェクトを作成し、メモリ オブジェクトの親オブジェクトとして pWdfIoRequest によって指す IWDFIoRequest オブジェクトを指定します (そのため、メモリ オブジェクトは親が解放されたときに自動的に解放されます)。 ドライバーは、メモリ オブジェクトへのローカル リファレンスを解放する 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 インターフェイスからこのメソッドを継承します。 要求が非同期的に送信された場合、DeleteWdfObject メソッドの呼び出しは、ドライバーの OnCompletion メソッドで後で発生するはずです。

前のコード例の別の実装では、ドライバーの初期化中に IWDFIoRequest オブジェクトと IWDFMemory オブジェクトを作成し、I/O 要求が送信されるたびに新しいオブジェクトを作成および削除する代わりに、これらの同じオブジェクトを繰り返し使用する場合があります。 詳細については、「IWDFIoRequest2::Reuse および IWDFMemory::SetBuffer」を参照してください。

また、別の実装では、Send 呼び出しが成功した場合に、I/O 要求から I/O 状態コードを検査する場合があります。 詳細については、「IWDFIoRequest::GetCompletionParams」を参照してください。