USB 等時性エンドポイントへのデータの転送方法

このトピックでは、クライアント ドライバーが USB デバイス内の等時性エンドポイントとの間でデータを転送する USB 要求ブロック (URB) を構築する方法について説明します。

ユニバーサル シリアル バス (USB) デバイスは、等時性エンドポイントをサポートして、オーディオ/ビデオ ストリーミングなど、時間依存データを一定の速度で転送できます。 データを転送するため、クライアント ドライバーは、等時性エンドポイントへのデータの読み取りまたは書き込みの要求を発行します。 その結果、ホスト コントローラーは、一定の間隔でデバイスをポーリングしてデータを送受信する等時性転送を開始します。

高速および最大速度デバイスの場合、ポーリングは (IN/OUT) トークン パケットを使用して行われます。 エンドポイントがデータを送信する準備ができたら、デバイスはデータを送信することによって IN トークン パケットのいずれかに応答します。 デバイスに書き込むため、ホスト コントローラーは OUT トークン パケットの後にデータ パケットを送信します。 ホスト コントローラーまたはデバイスはハンドシェイク パケットを送信しないため、確実な配信はありません。 ホスト コントローラーは転送を再試行しないため、エラーが発生するとデータが失われる可能性があります。

等時性転送の場合、ホスト コントローラーはバス上で一定の時間を予約します。 等時性エンドポイントの予約時間を管理するため、時間は連続する論理チャック (バス間隔と呼ばれます) に分割されます。 バス間隔の単位は、バス速度によって異なります。

最大速度では、バス間隔は 1 フレームです。 1 フレームの長さは 1 ミリ秒です。

高速および SuperSpeed の場合、バス間隔は 1 マイクロフレームです。 1 マイクロフレームの長さは 125 マイクロ秒です。 8 つの連続するマイクロフレームが、1 つの高速または SuperSpeed フレームを構成します。

等時性転送はパケット ベースです。 このトピックの「等時性パケット」という用語は、1 つのバス間隔で転送されるデータの量を指します。 エンドポイントの特性では、各パケットのサイズがエンドポイントの特性によって固定および決定されることが決まります。

クライアント ドライバーは、要求の URB を作成し、USB ドライバー スタックに URB を送信することにより、等時性転送を開始します。 要求は、USB ドライバー スタック内の下位ドライバーのいずれかによって処理されます。 URB を受け取ると、USB ドライバー スタックは一連の検証を実行し、要求のトランザクションをスケジュールします。 最大速度の場合、各バス間隔で転送される等時性パケットは、ネットワーク上の 1 つのトランザクションに含まれます。 特定の高速デバイスでは、バス間隔で複数のトランザクションが許可されます。 その場合、クライアント ドライバーは、1 つの要求 (URB) で等時性パケット内のより多くのデータを送受信できます。 SuperSpeed デバイスは、複数のトランザクションとバースト転送をサポートし、バス間隔ごとにさらに多くのバイトを許可します。 バースト転送について詳しくは、9-42 ページの「USB 3.0 仕様」をご覧ください。

開始する前に

等時性転送の要求を作成する前に、等時性エンドポイント用に開かれたパイプに関する情報が必要です。

Windows ドライバー モデル (WDM) ルーチンを使用するクライアント ドライバーには、USBD_INTERFACE_LIST_ENTRY 配列のいずれかの USBD_PIPE_INFORMATION 構造のパイプ情報があります。 クライアント ドライバーは、デバイス内の構成またはインターフェイスを選択するドライバーの前の要求でその配列を取得しました。

Windows Driver Framework (WDF) クライアント ドライバーは、フレームワークのターゲット パイプ オブジェクトへの参照を取得し、WdfUsbTargetPipeGetInformation を呼び出して、WDF_USB_PIPE_INFORMATION 構造のパイプ情報を取得する必要があります。

パイプ情報に基づいて、次の情報セットを決定します。

  • ホスト コントローラーが各パケットのパイプに送信できるデータの量。

    クライアント ドライバーが要求で送信できるデータの量は、ホスト コントローラーがエンドポイントから送受信できる最大バイト数を超えることはできません。 最大バイト数は、USBD_PIPE_INFORMATION および WDF_USB_PIPE_INFORMATION 構造の MaximumPacketSize メンバーによって示されます。 USB ドライバー スタックは、選択構成または選択インターフェイス要求時に MaximumPacketSize 値を設定します。

    最大速度デバイスの場合、MaximumPacketSize はエンドポイント記述子の wMaxPacketSize フィールドの最初の 11 ビットから派生します。これは、エンドポイントがトランザクションで送受信できる最大バイト数を示しています。 最大速度デバイスの場合、コントローラーはバス間隔ごとに 1 つのトランザクションを送信します。

    高速等時性転送では、エンドポイントで許可されている場合、ホスト コントローラーはバス間隔で追加のトランザクションを送信できます。 追加トランザクションの数はデバイスによって設定され、wMaxPacketSize のビット 12..11 で示されます。 この数値には、0、1、または 2 を指定できます。 12..11 が 0 を示す場合、マイクロフレームあたりの追加トランザクションはエンドポイントではサポートされません。 数値が 1 の場合、ホスト コントローラーは追加のトランザクション (マイクロフレームあたり 2 つのトランザクションの合計) を送信できます。2 は、2 つの追加トランザクション (マイクロフレームあたり 3 つのトランザクションの合計) を示します。 USB ドライバー スタックによって設定される MaximumPacketSize 値には、追加のトランザクションで送信できるバイト数が含まれています。

    SuperSpeed 等時性転送では、USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR の特定の値 (Usbspec.h を参照) が重要です。 USB ドライバー スタックでは、これらの値を使用して、バス間隔の最大バイト数を計算します。

    • エンドポイント コンパニオン記述子の Isochronous.Mult フィールド。 SuperSpeed 等時性転送では、追加トランザクション (高速デバイスとよく似ています) はバースト トランザクションと呼ばれます。 Mult 値は、エンドポイントがサポートするバースト トランザクションの最大数を示します。 サービス間隔には、最大 3 つのバースト トランザクション (インデックス 0 ~ 2) を使用できます。

    • エンドポイント コンパニオン記述子の bMaxBurst フィールド。 この値は、1 つのバースト トランザクションに存在できる wMaxPacketSize のチャンク数を示しています。 バースト トランザクションには、最大 16 個のチャンク (インデックス 0 ~ 15) を含めることができます。

    • wBytesPerInterval は、ホストがバス間隔で送受信できる合計バイト数を示します。 バス間隔あたりの最大バイト数は (bMaxBurst+1) * (Mult+1) * wMaxPacketSize として計算できますが、USB 3.0 仕様では、代わりに wBytesPerInterval 値を使用することをお勧めします。 wBytesPerInterval 値は、その計算値以下である必要があります。

      重要

      クライアント ドライバーの場合、前述の値は情報のみを対象とします。 ドライバーは常に、転送バッファーのレイアウトを決定するエンドポイント記述子の MaximumPacketSize 値を使用する必要があります。

  • エンドポイントはどのくらいの頻度でデータを送受信しますか。

    Interval メンバーは、エンドポイントがデータを送受信できる頻度を決定するために使用されます。 その値はデバイスにより設定されます。クライアント ドライバーが変更することはできません。 USB ドライバー スタックは、別の数値を使用して、等時性パケットをデータ ストリームに挿入する頻度を決定します。ポーリング期間は、Interval 値から派生します。

    最大速度伝送の場合、Interval 値とポーリング期間値は常に 1 です。USB ドライバー スタックは、他の値を無視します。

    次の表は、高速および SuperSpeed 転送の Interval および計算されたポーリング期間を示しています。

    Interval ポーリング期間 (2Interval-1)
    1 1; データはバス間隔ごとに転送されます。
    2 2; データは 2 バス間隔ごとに転送されます。
    3 4; データは 4 バス間隔ごとに転送されます。
    4 8; データは 8 バス間隔ごとに転送されます。
  • 各バス速度のパケット数には、どのような制限事項がありますか。

    URB では、最大速度デバイスに対して最大 255 の等時性パケットのみ送信できます。高速および SuperSpeed デバイスでは URB で 1024 パケット送信できます。 URB で送信するパケットの数は、各フレームのパケット数の倍数でなければなりません。

    ポーリング期間 高速/SuperSpeed のパケット数
    1 8 の倍数
    2 4 の倍数
    3 2 の倍数
    4 任意

wMaxPacketSize が 1,023 の最大速度エンドポイントである例を考えてみましょう。 この例では、アプリケーションは 25,575 バイトのバッファーを提供しました。 そのバッファーの転送には、25 個の等時性パケット (25575/1023) が必要です。

エンドポイント記述子に示されている次の特性を持つ高速エンドポイントの例を考えてみましょう。

  • wMaxPacketSize は 1,024 です。
  • ビット 12..11 は、2 つの追加トランザクションを示します。
  • Interval は 1 です。

クライアント ドライバーが構成を選択すると、等時性パイプの MaximumPacketSize は 3,072 バイト (トランザクションの合計 * wMaxPacketSize) を示します。 追加のトランザクションにより、クライアント ドライバーはマイクロフレームごとに 3,072 バイト、1 フレームで合計 24,576 バイト転送できます。 次の図は、高速送信のために 1 つのマイクロフレームで等時性パケットが転送される頻度を示しています。

Diagram of isochronous transfer intervals, polling periods, and packets.

エンドポイントと SuperSpeed エンドポイント コンパニオン記述子に示されているこれらの特性を持つ SuperSpeed エンドポイントの例を考えてみましょう。

  • wMaxPacketSize は 1,024 です。
  • bMaxBurst は 15 です。
  • Interval は 1 です。
  • Isochronous.Mult は 2 です。
  • wBytesPerInterval は 45000 です。

前の例では、最大バイト数を wMaxPacketSize * (bMaxBurst +1) * (Mult + 1) として計算すると 49,152 バイトになりますが、デバイスは値を wBytesPerInterval 値 (45,000 バイト) に制限します。 この値は、MaximumPacketSize 45,000 にも反映されます。 クライアント ドライバーでは、MaximumPacketSize 値のみ使用する必要があります。 この例では、要求を 3 つのバースト トランザクションに分割できます。 最初の 2 つのバースト トランザクションには、それぞれ wMaxPacketSize の 16 個のチャンクが含まれています。 最後のバースト トランザクションには、残りのバイトを保持する 12 個のチャンクが含まれています。 この画像は、SuperSpeed 送信のために等時性パケットを介して転送されたポーリング間隔とバイトを示しています。

Diagram of superspeed isochronous transfer intervals, polling periods, and packets.

等時性転送の要求を作成するには、次のようにします。

  1. 各等時性パケットのサイズを取得します。
  2. フレームあたりの等時性パケットの数を決定します。
  3. 転送バッファー全体を保持するために必要な等時性パケットの数を計算します。
  4. 転送の詳細を記述する URB 構造を割り当てます。
  5. パケット オフセットなど、各等時性パケットの詳細を指定します。

等時性転送要求の送信に関する詳細なコード例については、USBSAMP をご覧ください。

このトピックのこの例では、等時性転送の USBSAMP の実装が簡略化されています。 このサンプルでは、転送に必要な合計フレーム数が計算されます。 フレームで送信できるデータの量に基づいて、転送バッファーはより小さいチャンク サイズのバイトに分割されます。

次の手順では、上記の手順を詳しく説明し、高速等時性エンドポイントの等時性転送要求を構築して送信するためにクライアント ドライバーが使用できる計算とルーチンを示します。 この手順で使用されている値は、前述のエンドポイント特性の例に基づいています。

手順 1: 等時性パケットのサイズを取得する

パイプの MaximumPacketSize 値を調べることにより、等時性パケットのサイズを決定します。

最大速度伝送の場合、等時性パケットのサイズは、1 フレームで転送できるバイト数になります。 高速および SuperSpeed 伝送の場合、等時性パケットのサイズは、1 つのマイクロフレームで転送できる合計バイト数になります。 これらの値は、パイプの MaximumPacketSize で示されます。

この例では、MaximumPacketSize はフレームあたり 1023 バイト (最大速度)、マイクロフレームあたり 3072 バイト (高速)、マイクロフレームあたり 45,000 バイト (SuperSpeed) です。

Note

MaximumPacketSize 値は、等時性パケットの最大許容サイズを示します。 クライアント ドライバーは、各等時パケットのサイズを MaximumPacketSize 値より小さい任意の値に設定できます。

手順 2: フレームあたりの等時性パケットの数を決定する

最大速度伝送の場合、各フレームに等時性パケットを 1 つ転送します。

高速および SuperSpeed 伝送の場合、この値は Interval 値から派生する必要があります。 この例では、Interval は 1 です。 そのため、等時性パケットの数はフレームあたり 8 個にする必要があります。 その他の Interval 値については、「前提条件」セクションの表をご覧ください。

手順 3: 転送バッファー全体を保持するために必要な等時性パケットの数を計算する

バッファー全体を転送するために必要な等時性パケットの数を計算します。 この値は、転送バッファーの長さを等時性パケットのサイズで割ることによって計算できます。

この例では、各等時パケットのサイズが MaximumPacketSize であり、転送バッファーの長さが MaximumPacketSize 値の倍数であると仮定します。

たとえば、最大速度転送の場合、25,575 バイトの指定されたバッファーには 25 の等時性パケット (25575/1023) が必要です。 高速転送の場合、サイズ 24,576 のバッファーは、転送用の 8 つの等時性パケット (24576/3072) に分割されます。 SuperSpeed の場合、サイズ 360,000 バイトのバッファーは、8 つの等時性パケット (360000/45000) に収まります。

クライアント ドライバーは、以下の要件を検証する必要があります。

  • 等時性パケットの数は、フレームあたりのパケット数の倍数である必要があります。
  • 転送を行うために必要な等時性パケットの最大数は、最大速度デバイスでは 255、高速または SuperSpeed デバイスの場合は 1024 を超えてはなりません。

手順 4: 転送の詳細を記述する URB 構造を割り当てる

  1. 非ページ プールに URB 構造を割り当てます。

    クライアント ドライバーが WDM ルーチンを使用するときに、Windows 8 用 Windows Driver Kit (WDK) がある場合、ドライバーは USBD_IsochUrbAllocate を呼び出す必要があります。 クライアント ドライバーは、このルーチンを使用して、Windows Vista 以降のバージョンの Windows オペレーティング システムを対象にすることができます。 Windows 8 用 WDK がない場合、またはクライアント ドライバーが以前のバージョンのオペレーティング システムを対象としている場合、ExAllocatePoolWithTag を呼び出して、スタックまたはページ以外のプールに構造を割り当てることができます。

    WDF クライアント ドライバーは、WdfUsbTargetDeviceCreateIsochUrb メソッドを呼び出して URB 構造のメモリを割り当てることができます。

  2. URB 構造の UrbIsochronousTransfer メンバーは、等時性転送の詳細を記述する _URB_ISOCH_TRANSFER 構造を指します。 次の UrbIsochronousTransfer メンバーを次のように初期化します。

    • UrbIsochronousTransfer.Hdr.Length メンバーを URB のサイズに設定します。 URB のサイズを取得するには、GET_ISO_URB_SIZE マクロを呼び出し、パケットの数を指定します。

    • UrbIsochronousTransfer.Hdr.Function メンバーを URB_FUNCTION_ISOCH_TRANSFER に設定します。

    • UrbIsochronousTransfer.NumberOfPackets メンバーを等時性パケット数に設定します。

    • UrbIsochronousTransfer.PipeHandle を、エンドポイントに関連付けられているパイプの不透明なハンドルに設定します。 パイプ ハンドルが、ユニバーサル シリアル バス (USB) ドライバー スタックで使用される USBD パイプ ハンドルであることを確認します。

      USBD パイプ ハンドルを取得するため、WDF クライアント ドライバーは WdfUsbTargetPipeWdmGetPipeHandle メソッドを呼び出し、フレームワークのパイプ オブジェクトへの WDFUSBPIPE ハンドルを指定できます。 WDM クライアント ドライバーは、USBD_PIPE_INFORMATION 構造の PipeHandle メンバーで取得されたのと同じハンドルを使用する必要があります。

    • 転送の方向を指定します。 UrbIsochronousTransfer.TransferFlags を、等時性 IN 転送 (デバイスからの読み取り) では USBD_TRANSFER_DIRECTION_IN に、等時性 OUT 転送では USBD_TRANSFER_DIRECTION_OUT (デバイスへの書き込み) に設定します。

    • UrbIsochronousTransfer.TransferFlags で USBD_START_ISO_TRANSFER_ASAP フラグを指定します。 このフラグは、次の適切なフレームで転送を送信するよう USB ドライバー スタックに指示します。 クライアント ドライバーがこのパイプの等時性 URB を初めて送信する場合、ドライバー スタックはできるだけ早く URB 内の等時性パケットを送信します。 USB ドライバー スタックは、そのパイプ上の後続の URL に使用する次のフレームを追跡します。 USBD_START_ISO_TRANSFER_ASAP フラグを使用する後続の等時性 URB の送信に遅延がある場合、ドライバー スタックは、その URB の一部またはすべてのパケットを遅延と見なし、それらのパケットを転送しません。

      スタックがそのパイプの前の URB を完了した後、1024 フレームの等時性 URB を受信しない場合、USB ドライバー スタックは、その USBD_START_ISO_TRANSFER_ASAP 開始フレーム追跡をリセットします。 USBD_START_ISO_TRANSFER_ASAP フラグを指定する代わりに、開始フレームを指定できます。 詳細については、「解説」を参照してください。

    • 転送バッファーとそのサイズを指定します。 UrbIsochronousTransfer.TransferBuffer または UrbIsochronousTransfer.TransferBufferMDL 内のバッファーを記述する MDL 内のバッファーへのポインターを設定できます。

      転送バッファーの MDL を取得するため、WDF クライアント ドライバーは、転送の方向に応じて WdfRequestRetrieveOutputWdmMdl または WdfRequestRetrieveInputWdmMdl を呼び出すことができます。

手順 5: 転送の各等時性パケットの詳細を指定する

USB ドライバー スタックは、各等時パケットに関する情報を保持するのに十分な大きさの新しい URB 構造を割り当てますが、パケットに含まれるデータは割り当てられません。 URB 構造では、UrbIsochronousTransfer.IsoPacket メンバーは、転送内の各等時性パケットの詳細を記述する USBD_ISO_PACKET_DESCRIPTOR の配列です。 パケットは連続している必要があります。 配列内の要素の数は、URB の UrbIsochronousTransfer.NumberOfPackets メンバーで指定された等時性パケットの数である必要があります。

高速転送の場合、配列内の各要素は、1 つのマイクロフレーム内の 1 つの等時性パケットに関連付けられます。 最大速度では、各要素は、1 つのフレームで転送される 1 つの等時性パケットに関連付けられます。

各要素について、要求の転送バッファー全体の先頭からの各等時性パケットのバイト オフセットを指定します。 この値を指定するには、UrbIsochronousTransfer.IsoPacket[i]Offset メンバーを設定します。 USB ドライバー スタックは、指定された値を使用して、送受信するデータの量を追跡します。

最大速度転送のオフセットの設定

この例では、転送バッファーの配列エントリを最大速度で示します。 最大速度の場合、クライアント ドライバーは、1 つの等時性パケットを最大 1,023 バイトまで 1 つのフレームで転送できます。 25,575 バイトの転送バッファーは、それぞれ 1,023 バイトの 25 の等時性パケットを保持できます。 バッファー全体に合計 25 フレーム必要です。

Frame 1 IsoPacket [0].Offset = 0 (start address)
Frame 2 IsoPacket [1].Offset = 1023
Frame 3 IsoPacket [2].Offset = 2046
Frame 4 IsoPacket [3].Offset = 3069
...
Frame 25 IsoPacket [24].Offset = 24552

Total length transferred is 25,575 bytes.

高速転送のオフセットの設定

この例では、これらは高速の転送バッファーの配列エントリです。 この例では、バッファーが 24,576 バイトであり、クライアント ドライバーには 8 つの等時性パケットを転送するフレームが 1 つあり、それぞれ 3,072 バイトの長さであると想定しています。

Microframe 1 IsoPacket [0].Offset = 0 (start address)
Microframe 2 IsoPacket [1].Offset = 3072
Microframe 3 IsoPacket [2].Offset = 6144
Microframe 4 IsoPacket [3].Offset = 9216
Microframe 5 IsoPacket [4].Offset = 12288
Microframe 6 IsoPacket [5].Offset = 15360
Microframe 7 IsoPacket [6].Offset = 18432
Microframe 8 IsoPacket [7].Offset = 21504

Total length transferred is 24,576 bytes.

SuperSpeed 転送のオフセットの設定

この例では、SuperSpeed の配列オフセットです。 最大 45,000 バイトを 1 フレームで転送できます。 サイズ 360,000 の転送バッファーは、8 つのマイクロフレーム内に収まります。

Microframe 1 IsoPacket [0].Offset = 0 (start address)
Microframe 2 IsoPacket [1].Offset = 45000
Microframe 3 IsoPacket [2].Offset = 90000
Microframe 4 IsoPacket [3].Offset = 135000
Microframe 5 IsoPacket [4].Offset = 180000
Microframe 6 IsoPacket [5].Offset = 225000
Microframe 7 IsoPacket [6].Offset = 270000
Microframe 8 IsoPacket [7].Offset = 315000

Total length transferred is 360,000 bytes.

UrbIsochronousTransfer.IsoPacket[i].Length メンバーは、等時性 URB の各パケットの長さを意味するわけではありません。 IsoPacket[i].Length は、等時性 IN 転送のためにデバイスから受信した実際のバイト数を示すため、USB ドライバー スタックによって更新されます。 等時性 OUT 転送の場合、ドライバー スタックは IsoPacket[i].Length に設定されている値を無視します。

転送の開始 USB フレーム番号の指定

URB の UrbIsochronousTransfer.StartFrame メンバーは、転送の開始 USB フレーム番号を指定します。 クライアント ドライバーが URB を送信してから、USB ドライバー スタックが URB を処理するまで間、常に待機時間があります。 そのため、クライアント ドライバーは必ず、ドライバーが URB を送信するときに現在のフレームより後の開始フレームを指定する必要があります。 現在のフレーム番号を取得するため、クライアント ドライバーは USB ドライバー スタック (_URB_GET_CURRENT_FRAME_NUMBER) に URB_FUNCTION_GET_CURRENT_FRAME_NU MBER 要求を送信できます。

等時性転送の場合、現在のフレームと StartFrame 値の絶対差が USBD_ISO_START_FRAME_RANGE 未満でなければなりません。 StartFrame が適切な範囲内にない場合、USB ドライバー スタックは URB ヘッダーの Status メンバー (_URB_HEADER を参照) を USBD_STATUS_BAD_START_FRAME に設定し、URB 全体をカード解除します。

URB で指定された StartFrame 値は、URB の最初の等時性パケットが転送されるフレーム番号を示します。 後続のパケットのフレーム番号は、エンドポイントのバス速度とポーリング期間の値によって異なります。 たとえば、最大速度伝送の場合、最初のパケットは StartFrame で転送され、2 番目のパケットは StartFrame+ 1 というように転送されます。 USB ドライバー スタックが等時性パケットを最大速度でフレームで転送する方法を次に示します。

Frame (StartFrame)   IsoPacket [0]
Frame (StartFrame+1) IsoPacket [1]
Frame (StartFrame+2) IsoPacket [2]
Frame (StartFrame+3) IsoPacket [3]
...

Interval 値が 1 の高速デバイスの場合、フレーム番号は 8 マイクロフレームごとに変化します。 USB ドライバー スタックが等時性パケットを高速でフレームで転送する方法を次に示します。

Frame (StartFrame) Microframe 1 IsoPacket [0]
...
Frame (StartFrame) Microframe 8 IsoPacket [7]
Frame (StartFrame+1) Microframe 1 IsoPacket [8]
...
Frame (StartFrame+1) Microframe 8 IsoPacket [15]
Frame (StartFrame+2) Microframe 1 IsoPacket [16]
...
Frame (StartFrame+2) Microframe 8 IsoPacket [23]

USB ドライバー スタックが URB を処理すると、ドライバーはカード フレーム番号が現在のフレーム番号より小さい URB 内の等時性パケットをすべて破棄します。 ドライバー スタックは、破棄された各パケットのパケット記述子の Status メンバーを、USBD_STATUS_ISO_NA_LATE_USBPORT、USBD_STATUS_ISO_NOT_ACCESSED_BY_HW、または USBD_STATUS_ISO_NOT_ACCESSED_LATE に設定します。 URB 内の一部のパケットが破棄された場合でも、ドライバー スタックは、フレーム番号が現在のフレーム番号より大きいパケットのみを送信しようとします。

有効な StartFrame メンバーのチェックは、USB ドライバー スタックが各等時性パケットを高速マイクロフレームに読み込むため、高速伝送では若干複雑になります。ただし、StartFrame の値は、マイクロフレームではなく 1 ミリ秒 (最大速度) のフレーム番号を指します。 たとえば、URB に記録された StartFrame 値が現在のフレームより 1 小さい場合、ドライバー スタックは 8 個のパケットを破棄できます。 破棄されるパケットの正確な数は、等時性パイプに関連付けられているポーリング期間によって異なります。

等時性転送の例

次のコード例は、最大速度、高速、および SuperSpeed 伝送における等時性転送用の URB を作成する方法を示しています。

#define MAX_SUPPORTED_PACKETS_FOR_HIGH_OR_SUPER_SPEED 1024
#define MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED 255

NTSTATUS CreateIsochURB  ( PDEVICE_OBJECT         DeviceObject,
                          PUSBD_PIPE_INFORMATION  PipeInfo,
                          ULONG                   TotalLength,
                          PMDL                    RequestMDL,
                          PURB                    Urb)
{
    PDEVICE_EXTENSION        deviceExtension;
    ULONG                    numberOfPackets;
    ULONG                    numberOfFrames;
    ULONG                    isochPacketSize = 0;
    ULONG                    transferSizePerFrame;
    ULONG                    currentFrameNumber;
    size_t                   urbSize;
    ULONG                    index;
    NTSTATUS                 ntStatus;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    isochPacketSize = PipeInfo->MaximumPacketSize;

    // For high-speed transfers
    if (deviceExtension->IsDeviceHighSpeed || deviceExtension->IsDeviceSuperSpeed)
    {
        // Ideally you can pre-calculate numberOfPacketsPerFrame for the Pipe and
        // store it in the pipe context.

        switch (PipeInfo->Interval)
        {
        case 1:
            // Transfer period is every microframe (eight times a frame).
            numberOfPacketsPerFrame = 8;
            break;

        case 2:
            // Transfer period is every 2 microframes (four times a frame).
            numberOfPacketsPerFrame = 4;
            break;

        case 3:
            // Transfer period is every 4 microframes (twice in a frame).
            numperOfPacketsPerFrame = 2;
            break;

        case 4:
        default:
            // Transfer period is every 8 microframes (once in a frame).
            numberOfPacketsPerFrame = 1;
            break;
        }

        //Calculate the number of packets.
        numberOfPackets = TotalLength / isochPacketSize;

        if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_HIGH_OR_SUPER_SPEED)
        {
            // Number of packets cannot be  greater than 1021.
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }

        if (numberOfPackets % numberOfPacketsPerFrame != 0)
        {

            // Number of packets should be a multiple of numberOfPacketsPerFrame
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }

    }
    else if (deviceExtension->IsDeviceFullSpeed)
    {
        //For full-speed transfers
        // Microsoft USB stack only supports bInterval value of 1 for
        // full-speed isochronous endpoints.

        //Calculate the number of packets.
        numberOfPacketsPerFrame = 1;

        numberOfPackets = TotalLength / isochPacketSize;

        if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED)
        {
            // Number of packets cannot be greater than 255.
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }
    }

    // Allocate an isochronous URB for the transfer
    ntStatus = USBD_IsochUrbAllocate (deviceExtension->UsbdHandle,
        numberOfPackets,
        &Urb);

    if (!NT_SUCCESS(ntStatus))
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    urbSize = GET_ISO_URB_SIZE(numberOfPackets);

    Urb->UrbIsochronousTransfer.Hdr.Length = (USHORT) urbSize;
    Urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;
    Urb->UrbIsochronousTransfer.PipeHandle = PipeInfo->PipeHandle;

    if (USB_ENDPOINT_DIRECTION_IN(PipeInfo->EndpointAddress))
    {
        Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN;
    }
    else
    {
        Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT;
    }

    Urb->UrbIsochronousTransfer.TransferBufferLength = TotalLength;
    Urb->UrbIsochronousTransfer.TransferBufferMDL = RequestMDL;
    Urb->UrbIsochronousTransfer.NumberOfPackets = numberOfPackets;
    Urb->UrbIsochronousTransfer.UrbLink = NULL;

    // Set the offsets for every packet for reads/writes

    for (index = 0; index < numberOfPackets; index++)
    {
        Urb->UrbIsochronousTransfer.IsoPacket[index].Offset = index * isochPacketSize;
    }

    // Length is a return value for isochronous IN transfers.
    // Length is ignored by the USB driver stack for isochronous OUT transfers.

    Urb->UrbIsochronousTransfer.IsoPacket[index].Length = 0;
    Urb->UrbIsochronousTransfer.IsoPacket[index].Status = 0;

    // Set the USBD_START_ISO_TRANSFER_ASAP. The USB driver stack will calculate the start frame.
    // StartFrame value set by the client driver is ignored.
    Urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP;

Exit:

    return ntStatus;
}