次の方法で共有


カーネルモード SPB 周辺機器ドライバーのハードウェア リソース

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

このドライバーは、一連のプラグ アンド プレイおよび電源管理イベント コールバック関数を実装します。 これらの関数を KMDF に登録するために、ドライバーの EvtDriverDeviceAdd イベント コールバック関数は WdfDeviceInitSetPnpPowerEventCallbacks メソッドを呼び出します。 フレームワークは、電源管理イベント コールバック関数を呼び出して、周辺機器の電源状態の変化をドライバーに通知します。 これらの関数には EvtDevicePrepareHardware 関数が含まれており、ドライバーからデバイスにアクセスできるようにするのに必要な操作を実行します。

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

次のコード例は、EvtDevicePrepareHardware 関数が ResourcesTranslated パラメーターから接続 ID を取得する方法を示しています。

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 接続周辺機器の接続 ID を connectionId という名前の変数にコピーします。

次のコード例は、この接続 ID を、周辺機器への論理接続を開くのに使用できるデバイス パス名に組み込む方法を示しています。 このデバイス パス名は、リソース ハブを、周辺機器へのアクセスに必要なパラメーターを取得するシステム コンポーネントとして識別します。

// 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 マクロは、リソース ハブで使用される形式でデバイス パス名を格納するのに十分な大きさのバッファーを持つ、初期化された UNICODE_STRING 変数で szDeviceName という名前の宣言を作成します。 このマクロは Ntdef.h ヘッダー ファイルで定義されています。 RESOURCE_HUB_PATH_SIZE 定数は、デバイス パス名のバイト数を指定します。 RESOURCE_HUB_CREATE_PATH_FROM_ID マクロは、接続 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 要求の場合、ドライバーは WdfIoTargetSendReadSynchronouslyWdfIoTargetSendWriteSynchronousously、または WdfIoTargetSendIoctlSynchronously メソッドを呼び出して要求を送信します。

次のコード例では、ドライバーは WdfIoTargetSendWriteSynchronously を呼び出して、SPB に接続された周辺機器に IRP_MJ_WRITE 要求を同期的に送信します。 この例の開始時に、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 関数呼び出しは、入力バッファーを記述する WDF_MEMORY_DESCRIPTOR 構造体である memoryDescriptor 変数を初期化します。 以前は、ドライバーは、非ページ プールからバッファーを割り当てるために ExAllocatePoolWithTag などのルーチンを呼び出し、書き込みデータをこのバッファーにコピーしました。
  2. WDF_REQUEST_SEND_OPTIONS_INIT 関数呼び出しは、requestOptions 変数を初期化します。これは、書き込み要求のオプション設定を含む WDF_REQUEST_SEND_OPTIONS 構造体です。 この例では、構造体は 2 秒後に完了しない場合にタイムアウトするように要求を構成します。
  3. WdfIoTargetSendWriteSynchronously メソッドの 呼び出しは、SPB に接続された周辺機器に書き込み要求を送信します。 このメソッドは、書き込み操作の完了後またはタイムアウト後に同期的に返されます。必要に応じて、別のドライバー スレッドが WdfRequestCancelSentRequest を呼び出して要求を取り消すことができます。

WdfIoTargetSendWriteSynchronously 呼び出しでは、ドライバーは、以前に作成したフレームワーク要求オブジェクトへのハンドルである、SpbRequest という名前の変数を提供します。 WdfIoTargetSendWriteSynchronously 呼び出し後、ドライバーは通常、WdfRequestReuse メソッドを呼び出して、フレームワーク要求オブジェクトを再び使用するように準備する必要があります。