IOCTL_SPB_FULL_DUPLEX 要求の処理
SPI などの一部のバスでは、バス コントローラーとバス上のデバイスの間で読み取りと書き込みの転送が同時に行われます。 これらのフル二重転送をサポートするために、簡易周辺機器バス (SPB) I/O 要求インターフェイスの定義には、オプション として、IOCTL_SPB_FULL_DUPLEX I/O 制御コード (IOCTL) が含まれています。 ハードウェアでフル二重転送を実装するバス コントローラーの SPB コントローラー ドライバーのみが、IOCTL_SPB_FULL_DUPLEX IOCTL をサポートする必要があります。
SPB コントローラー ドライバーがフル二重転送の I/O 要求をサポートしている場合、ドライバーはこれらの要求に IOCTL_SPB_FULL_DUPLEX IOCTL を使用する必要があり、このトピックに記載されている実装ガイドラインに従う必要があります。 これらのガイドラインの目的は、IOCTL_SPB_FULL_DUPLEX 要求をサポートするすべてのハードウェア プラットフォームで一様な動作を促進することです。 SPB に接続された周辺機器のドライバーは、これらの要求に依存して、実行されているプラットフォームに関係なく同様の結果を生成できます。
バッファーの要件
IOCTL_SPB_FULL_DUPLEX 要求の形式は、IOCTL_SPB_EXECUTE_SEQUENCE 要求と同じですが、次の制約があります。
- 要求の SPB_TRANSFER_LIST 構造体には、2 つのエントリが含まれている必要があります。 最初のエントリは、デバイスに書き込むデータを含むバッファーを記述します。 2 番目のエントリでは、デバイスから読み取られたデータを保持するのに使用されるバッファーについて説明します。
- 転送リスト内の各 SPB_TRANSFER_LIST_ENTRY 構造体では、DelayInUs 値を 0 に 指定する必要があります。
フル二重転送中は、読み取りと書き込みの転送が一致して開始されます。 書き込みデータの最初のバイトは、読み取りデータの最初のバイトと同時にバス経由で送信されます。
IOCTL_SPB_FULL_DUPLEX 要求の書き込みバッファーと読み取りバッファーは、同じ長さにする必要はありません。
読み取りバッファーが書き込みバッファーよりも短い場合、フル二重バス転送は、書き込みバッファーの内容全体がデバイスに書き込まれるまで続行されます。 読み取りバッファーがいっぱいになった後、バス コントローラーはフル二重バス転送が完了するまで、デバイスから受信したすべての追加データをすべて破棄します。
書き込みバッファーが読み取りバッファーよりも短い場合、読み取りバッファーがいっぱいになるまで、フル二重バス転送が続行されます。 書き込みバッファーの内容全体がデバイスに書き込まれた後、バス コントローラーは、フル二重バス転送が完了するまでデバイスにゼロを書き込みます。
IOCTL_SPB_FULL_DUPLEX 要求が正常に完了した場合、SPB コントローラー ドライバーは、I/O ステータス ブロックのStatus メンバーを STATUS_SUCCESS に設定し、Information メンバーをフル二重転送中に転送されたバイトの合計数 (読み取りバイト数と書き込まれたバイト数) に設定します。 Information メンバーのカウント値は、読み取りバッファー サイズと書き込みバッファー サイズの合計を超えないようにしてください。
読み取りバッファーが書き込みバッファーよりも短い場合、Information メンバーのカウント値には、読み取りバッファーがいっぱいになった後にバス コントローラーがデバイスから読み取るデータのバイト数 (および disカード を含めないようにする必要があります。 たとえば、1 バイトの書き込みバッファーと 4 バイトの読み取りバッファーを使用したフル二重転送が正常に完了した場合、カウント値は 8 ではなく 5 である必要があります。 同様に、書き込みバッファーが読み取りバッファーよりも短い場合、書き込みバッファーが空になった後にデバイスに書き込まれたゼロをカウント値に含めないようにする必要があります。
パラメーターのチェック
IOCTL_SPB_EXECUTE_SEQUENCE 要求と IOCTL_SPB_FULL_DUPLEX 要求の形式は似ていますが、SPB フレームワーク拡張機能 (SpbCx) によって異なる方法で処理されます。 IOCTL_SPB_EXECUTE_SEQUENCE 要求の場合、SpbCx は要求のパラメーター値を検証し、要求の送信元のプロセス コンテキストで要求のバッファーをキャプチャします。 SpbCx は、IOCTL_SPB_EXECUTE_SEQUENCE 要求をドライバーの EvtSpbControllerIoSequence コールバック関数を介して SPB コントローラー ドライバーに渡します。このコールバック関数は、これらの要求専用です。
これに対し、SpbCx は IOCTL_SPB_FULL_DUPLEX 要求をカスタム、ドライバー定義 IOCTL 要求として扱います。 SpbCx は、ドライバーの EvtSpbControllerIoOther コールバック関数を介して SPB コントローラー ドライバーに IOCTL_SPB_FULL_DUPLEX 要求を渡します。これにより、ドライバーがサポートするカスタム IOCTL 要求も処理されます。 SpbCx は、これらの要求のパラメーター チェックやバッファー キャプチャを行いません。 ドライバーは、ドライバーが EvtSpbControllerIoOther 関数を介して受信する IOCTL 要求に必要なパラメーターのチェックまたはバッファー キャプチャを担当します。 バッファー キャプチャを有効にするには、ドライバーが EvtSpbControllerIoOther 関数を登録するときに、EvtIoInCallerContext コールバック関数を指定する必要があります。 詳細については、「Custom IOCTL のSPB_TRANSFER_LIST 構造体の使用」を参照してください。
通常、SPB コントローラー ドライバーは、EvtIoInCallerContext 関数ではなく、EvtSpbControllerIoOther 関数の IOCTL_SPB_FULL_DUPLEX 要求のパラメーター値を検証します。 次のコード例は、ドライバーがパラメーターのチェックを実装する方法を示しています。 この例では、ドライバーは、次のパラメーター要件が満たされていることを確認します。
- 要求の転送リストには、2 つのエントリが含まれています。
- 転送リストの最初のエントリは書き込みバッファー用で、2 番目のエントリは読み取りバッファー用です。
- 両方の エントリの DelayInUs 値は 0 です。
//
// Validate the transfer count.
//
SPB_REQUEST_PARAMETERS params;
SPB_REQUEST_PARAMETERS_INIT(¶ms);
SpbRequestGetParameters(SpbRequest, ¶ms);
if (params.SequenceTransferCount != 2)
{
//
// The full-duplex request must have
// exactly two transfer descriptors.
//
status = STATUS_INVALID_PARAMETER;
goto exit;
}
//
// Retrieve the write and read transfer descriptors.
//
const ULONG fullDuplexWriteIndex = 0;
const ULONG fullDuplexReadIndex = 1;
SPB_TRANSFER_DESCRIPTOR writeDescriptor;
SPB_TRANSFER_DESCRIPTOR readDescriptor;
PMDL pWriteMdl;
PMDL pReadMdl;
SPB_TRANSFER_DESCRIPTOR_INIT(&writeDescriptor);
SPB_TRANSFER_DESCRIPTOR_INIT(&readDescriptor);
SpbRequestGetTransferParameters(
SpbRequest,
fullDuplexWriteIndex,
&writeDescriptor,
&pWriteMdl);
SpbRequestGetTransferParameters(
SpbRequest,
fullDuplexReadIndex,
&readDescriptor,
&pReadMdl);
//
// Validate the transfer direction of each descriptor.
//
if ((writeDescriptor.Direction != SpbTransferDirectionToDevice) ||
(readDescriptor.Direction != SpbTransferDirectionFromDevice))
{
//
// For full-duplex I/O, the direction of the first transfer
// must be SpbTransferDirectionToDevice, and the direction
// of the second must be SpbTransferDirectionFromDevice.
//
status = STATUS_INVALID_PARAMETER;
goto exit;
}
//
// Validate the delay for each transfer descriptor.
//
if ((writeDescriptor.DelayInUs != 0) || (readDescriptor.DelayInUs != 0))
{
//
// The write and read buffers for full-duplex I/O are transferred
// simultaneously over the bus. The delay parameter in each transfer
// descriptor must be set to 0.
//
status = STATUS_INVALID_PARAMETER;
goto exit;
}
MyDriverPerformFullDuplexTransfer(
pDevice,
pRequest,
writeDescriptor,
pWriteMdl,
readDescriptor,
pReadMdl);
前のコード例では、パラメーター値をチェックした後、MyDriverPerformFullDuplexTransfer
という名前のドライバー内部ルーチンを呼び出して、フル二重 I/O 転送を開始します。