カスタム IOCTL に対する SPB_TRANSFER_LIST 構造体の使用
単純な周辺機器バス (SPB) コントローラー ドライバーが 1 つ以上のカスタム I/O 制御 (IOCTL) 要求をサポートしている場合は、SPB_TRANSFER_LIST 構造体を使用して、これらの要求の読み取りバッファーと書き込みバッファーを記述します。 この構造体は、要求内のバッファーを記述する一貫した方法を提供し、METHOD_BUFFERED I/O 操作に関連するバッファー コピーのオーバーヘッドを回避します。
カスタム IOCTL 要求で SPB_TRANSFER_LIST 構造体を使用する場合、SPB コントローラー ドライバーは SpbRequestCaptureIoOtherTransferList メソッドを呼び出して、要求元のプロセス コンテキストでこれらのバッファーをキャプチャする必要があります。 ドライバーは、SpbRequestGetTransferParameters メソッドを呼び出して、これらのバッファーにアクセスできます。
SPB I/O 要求インターフェイスの一部として定義されている IOCTL_SPB_FULL_DUPLEX および IOCTL_SPB_EXECUTE_SEQUENCE 要求は、SPB_TRANSFER_LIST 構造体を使用して、読み取りバッファーと書き込みバッファーを記述します。 IOCTL_SPB_FULL_DUPLEX 要求の SPB_TRANSFER_LIST 構造体は、要求の書き込みバッファーと読み取りバッファーの両方を (その順序で) 記述します。 IOCTL_SPB_EXECUTE_SEQUENCE 要求の SPB_TRANSFER_LIST 構造体は、読み取りバッファーと書き込みバッファーの任意のシーケンスを記述できます。
同様に、カスタム IOCTL を定義して、読み取りバッファーと書き込みバッファーの組み合わせを使用する SPB_TRANSFER_LIST 構造体を要求し、必要なバッファーの順序を一覧に指定できます。
SPB 周辺機器のカーネル モード ドライバー基盤 (KMDF) ドライバーは、SPB コント ローラーに IOCTL 要求を送信する WdfIoTargetSendIoctlSynchronousously などのメソッドを呼び出します。 このメソッドには、 InputBuffer パラメーターと OutputBuffer パラメーターがあります。 一部の種類のデバイスのドライバーでは、これら 2 つのパラメーターを使用して、IOCTL 要求の書き込みバッファーと読み取りバッファーをそれぞれ指定する場合があります。 ただし、IOCTL 要求を SPB コントローラーに送信するために、SPB 周辺機器ドライバーは、InputBuffer パラメーターを、SPB_TRANSFER_LIST 構造体に指定するメモリ記述子を指定するように設定します。 この構造体は、I/O 制御操作に必要な読み取りバッファーまたは書き込みバッファーを記述します。 ドライバーは、OutputBuffer パラメーターを NULL に設定します。
同様に、SPB 周辺機器のユーザー モード ドライバー基盤 (UMDF) ドライバーは、IWDFIoTarget::FormatRequestForIoctl などのメソッドを呼び出して、I/O コントロール操作の I/O 要求を書式設定します。 このメソッドには、pInputMemory パラメーターと pOutputMemory パラメーターがあります。 一部の種類のデバイスのドライバーでは、これら 2 つのパラメーターを使用して、IOCTL 要求の書き取りバッファーと読み込みバッファーをそれぞれ指定する場合があります。 ただし、SPB コントローラーに IOCTL 要求を送信するため、SPB 周辺機器ドライバーは、SPB_TRANSFER_LIST 構造体を含むメモリ オブジェクトを指す pInputMemory パラメーターを設定します。 この構造体は、I/O 制御操作に必要な読み取りバッファーまたは書き込みバッファーを記述します。 ドライバーは、pOutputMemory パラメーターを NULL に設定します。
パラメーター チェックとバッファー キャプチャ
SPB フレームワーク拡張機能 (SpbCx) が IOCTL_SPB_EXECUTE_SEQUENCE 要求を受信すると、SpbCx はドライバーの EvtSpbControllerIoSequence 関数を呼び出すことによって、SPB コントローラー ドライバーにこの要求を渡します。 この呼び出しの前に、SpbCx は要求内のバッファーを記述する SPB_TRANSFER_LIST 構造体を検査します。 SpbCx は、要求元のプロセス コンテキストでこれらのバッファーをキャプチャします。 (ユーザー モード メモリ内のバッファーには、メモリが割り当てられているプロセス内でのみアクセスできます)。さらに、SpbCx は、要求内のパラメーター値が有効かどうかをチェックします。
SpbCx が IOCTL_SPB_FULL_DUPLEX 要求またはカスタム IOCTL 要求を受信すると、SpbCx はドライバーの EvtSpbControllerIoOther コールバック関数を呼び出すことによって、SPB コントローラー ドライバーにこの要求を渡します。 この呼び出しを行う前に、SpbCx は要求内のパラメーター値の検証チェックは行わず、送信元のコンテキストで要求のバッファーをキャプチャしません。 これらの要求のパラメーター チェックとバッファー キャプチャは、SPB コントローラー ドライバーの役割です。
SPB コントローラー ドライバー が IOCTL_SPB_FULL_DUPLEX 要求をサポートしている場合、またはバッファーに SPB_TRANSFER_LIST 構造体を使用するカスタム IOCTL 要求をサポートしている場合、ドライバーは EvtIoInCallerContext コールバック関数を実装する必要があります。 ドライバーは、ドライバーの EvtSpbControllerIoOther コールバック関数を登録する SpbControllerSetIoOtherCallback メソッドの呼び出しで、この関数へのポインターを入力パラメーターとして提供します。 SpbCx が IOCTL_SPB_FULL_DUPLEX 要求またはカスタム IOCTL 要求を受信すると、SpbCx は、元のユーザーのコンテキストでドライバーの EvtIoInCallerContext 関数を呼び出します。 IOCTL 要求が SPB_TRANSFER_LIST 構造体を使用する場合、EvtIoInCallerContext 関数は SpbRequestCaptureIoOtherTransferList メソッドを呼び出して、要求内のバッファーをキャプチャします。 EvtIoInCallerContext 関数は、要求の一部の予備処理を実行する場合もあります。
次のコード例は、SPB コントローラー ドライバーによって実装される EvtIoInCallerContext 関数を示しています。
VOID
EvtIoInCallerContext(
_In_ WDFDEVICE SpbController,
_In_ WDFREQUEST FxRequest
)
{
NTSTATUS status = STATUS_SUCCESS;
WDF_REQUEST_PARAMETERS fxParams;
WDF_REQUEST_PARAMETERS_INIT(&fxParams);
WdfRequestGetParameters(FxRequest, &fxParams);
if ((fxParams.Type != WdfRequestTypeDeviceControl) &&
(fxParams.Type != WdfRequestTypeDeviceControlInternal))
{
status = STATUS_NOT_SUPPORTED;
goto exit;
}
//
// The driver should check for custom IOCTLs that it handles.
// If the IOCTL is not recognized, complete the request with a
// status of STATUS_NOT_SUPPORTED.
//
switch (fxParams.Parameters.DeviceIoControl.IoControlCode)
{
...
default:
status = STATUS_NOT_SUPPORTED;
goto exit;
}
//
// The IOCTL is recognized. Capture the buffers in the request.
//
status = SpbRequestCaptureIoOtherTransferList((SPBREQUEST)FxRequest);
//
// If the capture fails, the driver must complete the request instead
// of placing it in the SPB controller's request queue.
//
if (!NT_SUCCESS(status))
{
goto exit;
}
status = WdfDeviceEnqueueRequest(SpbController, FxRequest);
if (!NT_SUCCESS(status))
{
goto exit;
}
exit:
if (!NT_SUCCESS(status))
{
WdfRequestComplete(FxRequest, status);
}
}
前のコード例では、switch
ステートメントは、要求に SPB コントローラー ドライバーが認識する IOCTL が含まれていることを確認します。 (簡潔にするために、switch
ステートメントの本文は表示されません)。次に、SpbRequestCaptureIoOtherTransferList メソッドの呼び出しによって、要求内のバッファーがキャプチャされます。 この呼び出しが成功すると、要求は SPB コントローラーの I/O キューに追加されます。 それ以外の場合、要求はエラー ステータス コードで完了します。
EvtSpbControllerIoOther 関数によるパラメーターのチェックを示すコード例については、「IOCTL_SPB_FULL_DUPLEX要求の処理」を参照してください。