ベスト プラクティス: URB の使用

このトピックでは、Windows 8 に含まれる USB ドライバー スタックに URB を割り当て、ビルドし、送信する際のクライアント ドライバーのベスト プラクティスについて説明します。

Windows 8 には、ユニバーサル シリアル バス (USB) 3.0 デバイスをサポートするための新しい USB ドライバー スタックが含まれています。 新しい USB 3.0 ドライバー スタックは、USB 3.0 仕様に従って、いくつかの新機能を実装します。 さらに、ドライバー スタックには、クライアント ドライバーが一般的なタスクを効率的に実行できるようにする他の機能も含まれています。 たとえば、新しいドライバー スタックは、クライアント ドライバーが物理メモリ内の不連続なページに転送バッファーを送信できるようにする連鎖 MDL を受け入れます。

クライアント ドライバーが Windows 8 の USB ドライバー スタックの新機能を使用できるようにするには、その前に、Windows によってデバイス用に読み込まれる基盤となる USB ドライバー スタックにドライバー自体を登録する必要があります。 クライアント ドライバーを登録するには、USBD_CreateHandle を呼び出し、コントラクトのバージョンを指定します。 クライアント ドライバーが Windows 8 での改善点と新機能を構築、実行、使用することを目的としている場合、クライアント契約のバージョンは USBD_CLIENT_CONTRACT_VERSION_602 です。

USBD_CLIENT_CONTRACT_VERSION_602 バージョンのクライアント ドライバーの場合、USB ドライバー スタックは、クライアント ドライバーが次の一連のルールに準拠していると想定します。

USB ドライバー スタックは、受信した要求の検証を実行し、可能な場合は常に違反を処理します。 そうしないと、未定義の動作が発生する可能性があります。

古いパイプ ハンドルまたは無効なパイプ ハンドルを使用して I/O 要求を送信しない

クライアント ドライバーは、USB ドライバー スタックに I/O 要求を送信するために古いパイプ ハンドルを使用notください。 古いパイプ ハンドルとは、デバイスで選択されなくなった構成、インターフェイス、または代替設定を選択する要求で取得されたパイプ ハンドルを指します。 パイプ ハンドルが古くならないようにするには、クライアント ドライバーが構成またはインターフェイスを選択するたびに、ドライバーはパイプ ハンドルのキャッシュ (通常はデバイス コンテキストに保存されている) を更新する必要があります。 一部の競合状態では、古いパイプ ハンドルが発生する可能性もあります。 たとえば、クライアント ドライバーは、選択したインターフェイスのパイプ ハンドルを使用して I/O 要求を送信します。 要求が完了する前に、クライアント ドライバーは、使用中のパイプ ハンドルに関連付けられた同じエンドポイントを使用しない代替設定を選択します。 これらの保留中の要求の両方が競合状態になり、パイプ ハンドルが無効になる可能性があります。

Windows 8 で割り当てルーチンを呼び出して URB を割り当てる

Windows 8 には、USB 要求ブロック (URB) の割り当て、ビルド、および解放のための新しいルーチンが用意されています。 URB を割り当てるには、Windows ドライバー モデル (WDM) クライアント ドライバーは、次の一覧に示す新しいルーチンを常に使用する必要があります。

前述のリストのルーチンは、追跡と処理を向上させるために、割り当てられた URB に不透明な URB コンテキストを付加する場合があります。 クライアント ドライバーは、URB コンテキストの内容を表示または変更できません。 Windows 8 での URB の割り当ての詳細については、「URB の割り当てとビルド」を参照してください。

登録中にバージョンをUSBD_CLIENT_CONTRACT_VERSION_602として識別する Windows Driver Framework (WDF) クライアント ドライバー (WdfUsbTargetDeviceCreateWithParameters を参照) の場合、USB ドライバー スタックは、新しい WdfUsbTargetDeviceCreateUrb を呼び出すことによって、クライアント ドライバーが URB のメモリを割り当てることを想定します。

保留中の要求に関連付けられているアクティブな URB を再利用しない

USB ドライバー スタックは、URB に関連付けられた要求の前に再送信されたアクティブな URB を検出すると、意図的にバグチェックを行います。 URB は、要求が保留中であり、クライアント ドライバーの IRP 完了ルーチンが呼び出されていない限りアクティブです。 アクティブな URB に対して次のタスクを実行しないでください。

  • 別の要求に対してアクティブな URB を再送信しないでください (URB を別の IRP に関連付けます)。
  • アクティブな URB の内容は変更しないでください。
  • アクティブな URB を解放しないでください。

クライアント ドライバーの完了ルーチンが呼び出された後、ドライバーは完了ルーチン内で特定の種類の要求に対して URB を再送信できます。 再送信には次のルールが適用されます。

  • クライアント ドライバーは、同じ構成を選択する選択構成要求以外の要求の種類に対して、USBD_SelectConfigUrbAllocateAndBuild によって割り当てられた URB を再利用することはできません。

  • クライアント ドライバーは、インターフェイス内の同じ代替設定を選択する選択インターフェイス要求以外の要求の種類に対して、USBD_SelectInterfaceUrbAllocateAndBuild によって割り当てられた URB を再利用しないでください。 例については、USBD_SelectInterfaceUrbAllocateAndBuild の解説を参照してください。

  • USBD_IsochUrbAllocate によって割り当てられる URB は、等時性転送要求にのみ再利用する必要があります。 逆に、他の種類の I/O 要求 (制御、一括、または割り込み) に割り当てられる URB は、等時性要求には使用できません。

    たとえば、クライアント ドライバーは、バルク転送要求の URB 構造体を割り当てて構築します。 また、クライアント ドライバーは、デバイス内の等時性エンドポイントにデータを送信する必要もあります。 一括転送要求が完了した後、クライアント ドライバーは、等時性要求の URB を再フォーマットして送信してはなりません。 これは、等時性要求に関連付けられた URB がパケット数に応じて可変長になるためです。 さらに、パケットはフレーム境界で開始および終了する必要があります。 (バルク転送用に) 割り当てられた URB が等時性転送に必要なバッファー レイアウトに適合しない可能性があり、要求が失敗する可能性があります。

  • USBD_UrbAllocate によって割り当てられた URB は、等時性、選択構成、またはインターフェイス選択要求に再利用することはできません。 URB を再利用して NULL 構成を選択し、デバイスで選択した構成を無効にすることができます。 URB はアクティブでなく、クライアント ドライバーは UsbBuildSelectConfigurationRequest マクロを呼び出し、ConfigurationDescriptor パラメーターに NULL を渡すことによって URB を再フォーマットする必要があります。

  • URB を再送信する前に、クライアント ドライバーは、要求の種類に対して定義されている適切な UsbBuildXxx マクロを使用して URB を再フォーマットする必要があります。 USB スタックによってその内容の一部が変更されている可能性があるため、ドライバーが URB をフォーマットすることが重要です。

    たとえば、ドライバーが UsbBuildInterruptOrBulkTransferRequest を呼び出して、一括転送要求の URB を初期化するとします (_URB_BULK_OR_INTERRUPT_TRANSFER を参照)。 ドライバーが URB 構造体の TransferBufferMDL メンバーを NULL に初期化すると、USB ドライバー スタックは、MDL の代わりに、TransferBuffer で指定された転送バッファーを使用してデバイスとデータを交換します。 ただし、内部的には、USB ドライバー スタックが MDL を作成し、TransferBufferMDL に MDL へのポインターを格納し、MDL を使用してデータをスタックに渡す場合があります。 USB ドライバー スタックによって MDL メモリが解放されても、クライアント ドライバーが完了ルーチンで URB を処理している場合、TransferBufferMDL が NULL にならない可能性があります。 URB のメンバーが正しく書式設定されていることを確認するには、ドライバーは UsbBuildInterruptOrBulkTransferRequest をもう一度呼び出して、要求を送信する前に URB を再フォーマットする必要があります。

高速および SuperSpeed 等時性転送に 8 より大きいポーリング期間を使用しない

USB ドライバー スタックは、ポーリング期間番号 1、2、4、または 8 の高速および SuperSpeed 等時性パイプをサポートします。 クライアント ドライバーは、期間が 8 を超えるエンドポイントに IO を送信することはできません。 そうするとバグチェックが行われる可能性があります。

フレームあたりのパケット数の倍数である等時性パケットの数を確認する

High Speed および SuperSpeed アイソクロナス転送の場合、フレームあたりのアイソクロナス パケット数は 8 / ポーリング周期として計算されます。 クライアント ドライバーは、URB で指定された NumberOfPackets 値 (_URB_ISOCH_TRANSFER を参照) がフレームあたりのパケット数の倍数であることを確認する必要があります。

USB ドライバー スタックは、NumberOfPackets がフレームあたりのパケット数の倍数ではない等時性転送 URL をサポートしていません。

文書化された IRQL レベルでルーチンを呼び出す

契約バージョンとして USBD_CLIENT_CONTRACT_VERSION_602 を使用してクライアント ドライバーを登録すると、USB ドライバー スタックは、クライアント ドライバーが適切な IRQL レベルで要求を送信したと想定します。 クライアント ドライバーが DISPATCH_LEVEL で要求を送信する場合、その要求は PASSIVE_LEVEL で送信される必要があります。 場合によっては、要求を受信すると、USB ドライバー スタックが IRQL 値を検証し、要求が失敗します。 ただし、他の場合には、USB ドライバー スタックがバグチェックを生成する可能性があります。

USB デバイスへの要求の送信