Übertragen von Daten an isochrone USB-Endpunkte
In diesem Thema wird beschrieben, wie ein Clienttreiber einen USB-Anforderungsblock (URB) erstellen kann, um Daten zu und von isochronen Endpunkten auf einem USB-Gerät zu übertragen.
Ein USB-Gerät (Universal Serial Bus) kann isochrone Endpunkte unterstützen, um zeitabhängige Daten mit einer konstanten Rate zu übertragen, z. B. mit Audio-/Videostreaming. Zum Übertragen von Daten sendet der Clienttreiber eine Anforderung zum Lesen oder Schreiben von Daten an einen isochronen Endpunkt. Daher initiiert der Hostcontroller eine isochrone Übertragung, die Daten sendet oder empfängt, indem er das Gerät in regelmäßigen Abständen abruft.
Bei Geräten mit hoher Geschwindigkeit und voller Geschwindigkeit erfolgt die Abfrage mithilfe von (IN/OUT)-Tokenpaketen. Wenn der Endpunkt zum Senden von Daten bereit ist, antwortet das Gerät auf eines der IN-Tokenpakete, indem Es Daten sendet. Um auf das Gerät zu schreiben, sendet der Hostcontroller ein OUT-Tokenpaket gefolgt von Datenpaketen. Der Hostcontroller oder das Gerät sendet keine Handshake-Pakete, daher gibt es keine garantierte Zustellung. Da der Hostcontroller nicht versucht, die Übertragung erneut zu versuchen, gehen möglicherweise Daten verloren, wenn ein Fehler auftritt.
Für isochrone Übertragungen reserviert der Hostcontroller bestimmte Zeiträume im Bus. Um die reservierte Zeit für isochrone Endpunkte zu verwalten, wird die Zeit in aufeinanderfolgende logische Chucks unterteilt, die als Busintervalle bezeichnet werden. Die Einheit des Busintervalls hängt von der Busgeschwindigkeit ab.
Bei voller Geschwindigkeit ist ein Busintervall ein Frame. Die Länge eines Rahmens beträgt 1 Millisekunde.
Für High Speed und SuperSpeed ist das Busintervall ein Microframe. Die Länge eines Microframes beträgt 125 Mikrosekunden. Acht aufeinanderfolgende Microframes bilden einen Hochgeschwindigkeits- oder SuperSpeed-Frame.
Isochrone Übertragungen sind paketbasiert. Der Begriff isochrones Paket in diesem Thema bezieht sich auf die Datenmenge, die in einem Busintervall übertragen wird. Die Merkmale des Endpunkts bestimmen, dass die Größe jedes Pakets festgelegt und durch die Merkmale des Endpunkts bestimmt wird.
Der Clienttreiber startet eine isochrone Übertragung, indem er eine URB für die Anforderung erstellt und die URB an den USB-Treiberstapel übermittelt. Die Anforderung wird von einem der unteren Treiber im USB-Treiberstapel verarbeitet. Beim Empfang der URB führt der USB-Treiberstapel eine Reihe von Validierungen aus und plant Transaktionen für die Anforderung. Für volle Geschwindigkeit ist ein isochrones Paket, das in jedem Busintervall übertragen werden soll, in einer einzelnen Transaktion auf der Leitung enthalten. Bestimmte Hochgeschwindigkeitsgeräte ermöglichen mehrere Transaktionen in einem Busintervall. In diesem Fall kann der Clienttreiber mehr Daten im isochronen Paket in einer einzelnen Anforderung (URB) senden oder empfangen. SuperSpeed-Geräte unterstützen mehrere Transaktionen und Burstübertragungen, sodass noch mehr Bytes pro Busintervall möglich sind. Weitere Informationen zu Burstübertragungen finden Sie auf der Usb 3.0-Spezifikationsseite 9-42.
Vorbereitung
Bevor Sie eine Anforderung für eine isochrone Übertragung erstellen, benötigen Sie Informationen über die Pipe, die für den isochronen Endpunkt geöffnet ist.
Ein Clienttreiber, der WDM-Routinen (Windows Driver Model) verwendet, verfügt über die Pipeinformationen in einer der USBD_PIPE_INFORMATION Strukturen eines USBD_INTERFACE_LIST_ENTRY Arrays. Der Clienttreiber hat dieses Array in der vorherigen Anforderung des Treibers abgerufen, um eine Konfiguration oder eine Schnittstelle auf dem Gerät auszuwählen.
Ein WDF-Clienttreiber (Windows Driver Framework) muss einen Verweis auf das Zielpipeobjekt des Frameworks abrufen und WdfUsbTargetPipeGetInformation aufrufen, um Pipeinformationen in einer WDF_USB_PIPE_INFORMATION-Struktur abzurufen.
Ermitteln Sie anhand der Pipeinformationen diesen Satz von Informationen:
Wie viele Daten kann der Hostcontroller an die Pipe in jedem Paket senden?
Die Datenmenge, die der Clienttreiber in einer Anforderung senden kann, darf die maximale Anzahl von Bytes, die der Hostcontroller von einem Endpunkt senden oder empfangen kann, nicht überschreiten. Die maximale Anzahl von Bytes wird durch den MaximumPacketSize-Member von USBD_PIPE_INFORMATION - und WDF_USB_PIPE_INFORMATION-Strukturen angegeben. Der USB-Treiberstapel legt während einer Select-Configuration- oder Select-Interface-Anforderung den MaximumPacketSize-Wert fest.
Bei Geräten mit voller Geschwindigkeit wird MaximumPacketSize von den ersten 11 Bits des wMaxPacketSize-Felds des Endpunktdeskriptors abgeleitet, was die maximale Anzahl von Bytes angibt, die der Endpunkt in einer Transaktion senden oder empfangen kann. Bei Geräten mit voller Geschwindigkeit sendet der Controller eine Transaktion pro Busintervall.
Bei einer isochronen Hochgeschwindigkeitsübertragung kann der Hostcontroller zusätzliche Transaktionen in einem Busintervall senden, wenn der Endpunkt dies zulässt. Die Anzahl der zusätzlichen Transaktionen wird vom Gerät festgelegt und in Bits 12..11 des wMaxPacketSize angegeben. Diese Zahl kann 0, 1 oder 2 sein. Wenn 12..11 den Wert 0 angibt, werden zusätzliche Transaktionen pro Microframe vom Endpunkt nicht unterstützt. Wenn die Zahl 1 ist, kann der Hostcontroller eine zusätzliche Transaktion senden (insgesamt zwei Transaktionen pro Microframe); 2 gibt zwei zusätzliche Transaktionen an (insgesamt drei Transaktionen pro Microframe). Der Vom USB-Treiberstapel festgelegte MaximumPacketSize-Wert enthält die Anzahl der Bytes, die in zusätzlichen Transaktionen gesendet werden können.
Für die isochrone SuperSpeed-Übertragung sind bestimmte Werte von USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR (siehe Usbspec.h) wichtig. Der USB-Treiberstapel verwendet diese Werte, um die maximale Anzahl von Bytes in einem Busintervall zu berechnen.
Isochronous.Mult-Feld des Endpunktbegleitungsdeskriptors. Bei isochronen SuperSpeed-Übertragungen werden die zusätzlichen Transaktionen (ähnlich wie Hochgeschwindigkeitsgeräte) als Bursttransaktionen bezeichnet. Der Mult-Wert gibt die maximale Anzahl von Bursttransaktionen an, die der Endpunkt unterstützt. Es kann bis zu drei Bursttransaktionen (indiziert 0 bis 2) in einem Dienstintervall geben.
bMaxBurst-Feld des Endpunkt-Begleitdeskriptors. Dieser Wert gibt die Anzahl der Blöcke von wMaxPacketSize an, die in einer einzelnen Bursttransaktion vorhanden sein können. Es kann bis zu 16 Blöcke (indiziert 0 bis 15) in einer Bursttransaktion geben.
wBytesPerInterval gibt die Gesamtzahl der Bytes an, die der Host in einem Busintervall senden oder empfangen kann. Obwohl die maximale Anzahl von Bytes pro Busintervall als (bMaxBurst+1) * (Mult+1) * wMaxPacketSize berechnet werden kann, empfiehlt die USB 3.0-Spezifikation stattdessen die Verwendung des wBytesPerInterval-Werts . Der wBytesPerInterval-Wert muss kleiner oder gleich diesem berechneten Wert sein.
Wichtig
Für einen Clienttreiber dienen die oben beschriebenen Werte nur zur Information. Der Treiber muss immer den MaximumPacketSize-Wert des Endpunktdeskriptors verwenden, um das Layout des Übertragungspuffers zu bestimmen.
Wie oft sendet oder empfängt der Endpunkt Daten?
Das Interval-Element wird verwendet, um zu bestimmen, wie oft der Endpunkt Daten senden oder empfangen kann. Das Gerät legt diesen Wert fest, und der Clienttreiber kann ihn nicht ändern. Der USB-Treiberstapel verwendet eine andere Zahl, um die Häufigkeit zu bestimmen, mit der isochrone Pakete in den Datenstrom eingefügt werden: der Abrufzeitraum, der vom Interval-Wert abgeleitet wird.
Bei Vollgeschwindigkeitsübertragungen sind die Werte für Intervall und Abrufzeitraum immer 1; Der USB-Treiberstapel ignoriert andere Werte.
Die folgende Tabelle zeigt das Intervall und den berechneten Abrufzeitraum für Hochgeschwindigkeits- und SuperSpeed-Übertragungen:
Intervall Abrufzeitraum (2Interval-1) 1 1; Daten werden in jedem Busintervall übertragen. 2 2; Daten werden jedes zweite Busintervall übertragen. 3 4; Daten werden jedes vierte Busintervall übertragen. 4 8; Daten werden jedes achte Busintervall übertragen. Welche Einschränkungen gelten für die Anzahl von Paketen für jede Busgeschwindigkeit.
In einer URB können Sie nur bis zu 255 isochrone Pakete für ein Gerät mit voller Geschwindigkeit senden. 1024 Pakete in einer URB für Hochgeschwindigkeits- und SuperSpeed-Geräte. Die Anzahl der Pakete, die Sie in der URB senden, muss ein Vielfaches der Anzahl der Pakete in jedem Frame sein.
Abrufzeitraum Anzahl der Pakete für Hochgeschwindigkeits-/SuperSpeed 1 Vielfaches von 8 2 Vielfaches von 4 3 Vielfaches von 2 4 Any
Ein Beispiel für einen Vollgeschwindigkeitsendpunkt mit wMaxPacketSize ist 1.023. Für dieses Beispiel hat die Anwendung einen Puffer von 25.575 Bytes bereitgestellt. Die Übertragung für diesen Puffer erfordert 25 isochrone Pakete (25575/1023).
Betrachten Sie einen Beispiel-Hochgeschwindigkeitsendpunkt mit den folgenden Merkmalen, die im Endpunktdeskriptor angegeben sind.
- wMaxPacketSize ist 1.024.
- Bits 12..11 geben zwei zusätzliche Transaktionen an.
- Intervall ist 1.
Nachdem der Clienttreiber eine Konfiguration ausgewählt hat, gibt MaximumPacketSize für die isochrone Pipe 3.072 Bytes an (Gesamttransaktionen * wMaxPacketSize). Die zusätzlichen Transaktionen ermöglichen es dem Clienttreiber, 3.072 Bytes in jedem Microframe und insgesamt 24.576 Bytes in einem Frame zu übertragen. Die folgende Abbildung zeigt, wie oft ein isochrones Paket in einem Mikroframe für Hochgeschwindigkeitsübertragungen übertragen wird.
Betrachten Sie ein Beispiel für einen SuperSpeed-Endpunkt mit den folgenden Merkmalen, die in den Endpunkt- und SuperSpeed-Endpunkt-Begleitdeskriptoren angegeben sind:
- wMaxPacketSize ist 1.024.
- bMaxBurst ist 15.
- Intervall ist 1.
- Isochronous.Mult ist 2.
- wBytesPerInterval ist 45000.
Auch wenn im vorherigen Beispiel die maximale Anzahl von Bytes als wMaxPacketSize * (bMaxBurst +1) * (Mult + 1) berechnet werden kann, was zu 49.152 Bytes führt, beschränkt das Gerät den Wert auf den wBytesPerInterval-Wert , der 45.000 Bytes beträgt. Dieser Wert wird auch in MaximumPacketSize 45.000 angezeigt. Der Clienttreiber darf nur den Wert MaximumPacketSize verwenden. In diesem Beispiel kann die Anforderung in drei Bursttransaktionen unterteilt werden. Die ersten beiden Bursttransaktionen enthalten jeweils 16 Blöcke von wMaxPacketSize. Die letzte Bursttransaktion enthält 12 Blöcke, die die verbleibenden Bytes enthalten. Diese Abbildung zeigt das Abrufintervall und bytes, die über ein isochrones Paket für die SuperSpeed-Übertragung übertragen werden.
So erstellen Sie eine Anforderung für eine isochrone Übertragung:
- Ruft die Größe jedes isochronen Pakets ab.
- Bestimmen Sie die Anzahl der isochronen Pakete pro Frame.
- Berechnen Sie die Anzahl der isochronen Pakete, die erforderlich sind, um den gesamten Übertragungspuffer zu enthalten.
- Ordnen Sie eine URB-Struktur zu, um die Details der Übertragung zu beschreiben.
- Geben Sie die Details jedes isochronen Pakets an, z. B. paketoffset.
Ein vollständiges Codebeispiel zum Senden isochroner Übertragungsanforderungen finden Sie unter USBSAMP.
In diesem Beispiel in diesem Thema wird die USBSAMP-Implementierung der isochronen Übertragung vereinfacht. Im Beispiel wird die Gesamtzahl der frames berechnet, die für die Übertragung erforderlich sind. Basierend auf der Datenmenge, die in einem Frame gesendet werden kann, wird der Übertragungspuffer in kleinere Blockbytes unterteilt.
Das folgende Verfahren erläutert die vorherigen Schritte und zeigt Berechnungen und Routinen, mit denen ein Clienttreiber eine isochrone Übertragungsanforderung für einen isochronen Hochgeschwindigkeitsendpunkt erstellen und senden kann. Die in der Prozedur verwendeten Werte basieren auf den zuvor beschriebenen Beispielendpunktmerkmalen.
Schritt 1: Abrufen der Größe eines isochronen Pakets
Ermitteln Sie die Größe eines isochronen Pakets, indem Sie den MaximumPacketSize-Wert der Pipe überprüfen.
Bei Übertragungen mit voller Geschwindigkeit ist die Größe eines isochronen Pakets die Anzahl der Bytes, die Sie in einem Frame übertragen können. Bei Hochgeschwindigkeits- und SuperSpeed-Übertragungen entspricht die Größe eines isochronen Pakets der Gesamtzahl der Bytes, die in einem Microframe übertragen werden können. Diese Werte werden im MaximumPacketSize der Pipe angegeben.
Im Beispiel beträgt MaximumPacketSize 1023 Bytes pro Frame (volle Geschwindigkeit); 3072 Bytes pro Microframe (Hochgeschwindigkeit); 45.000 Bytes pro Microframe (SuperSpeed).
Hinweis
Der Wert MaximumPacketSize gibt die maximal zulässige Größe des isochronen Pakets an. Der Clienttreiber kann die Größe jedes isochronen Pakets auf einen beliebigen Wert festlegen, der kleiner als der MaximumPacketSize-Wert ist.
Schritt 2: Ermitteln der Anzahl isochroner Pakete pro Frame
Bei Übertragungen mit voller Geschwindigkeit übertragen Sie ein isochrones Paket in jedem Frame.
Für Hochgeschwindigkeits- und SuperSpeed-Übertragungen muss dieser Wert vom Wert interval abgeleitet werden. Im Beispiel ist Intervall 1. Daher muss die Anzahl der isochronen Pakete acht pro Frame sein. Weitere Intervallwerte finden Sie in der Tabelle im Abschnitt Voraussetzungen.
Schritt 3: Berechnen der Anzahl der isochronen Pakete, die erforderlich sind, um den gesamten Übertragungspuffer zu enthalten
Berechnen Sie die Anzahl der isochronen Pakete, die zum Übertragen des gesamten Puffers erforderlich sind. Dieser Wert kann berechnet werden, indem die Länge des Übertragungspuffers durch die Größe eines isochronen Pakets dividiert wird.
In diesem Beispiel wird davon ausgegangen, dass die Größe jedes isochronen Pakets MaximumPacketSize und die Übertragungspufferlänge ein Vielfaches des MaximumPacketSize-Werts ist.
Für die Übertragung mit voller Geschwindigkeit benötigt ein bereitgestellter Puffer von 25.575 Bytes beispielsweise 25 isochrone Pakete (25575/1023). Für die Hochgeschwindigkeitsübertragung wird ein Puffer der Größe 24.576 für die Übertragung in acht isochrone Pakete (24576 /3072) unterteilt. Für SuperSpeed passt ein Puffer der Größe 360.000 Bytes in acht isochrone Pakete (360000/45000).
Der Clienttreiber sollte diese Anforderungen überprüfen:
- Die Anzahl der isochronen Pakete muss ein Vielfaches der Anzahl der Pakete pro Frame sein.
- Die maximale Anzahl von isochronen Paketen, die für die Übertragung erforderlich sind, darf 255 für ein Gerät mit voller Geschwindigkeit nicht überschreiten; 1024 für ein Hochgeschwindigkeits- oder SuperSpeed-Gerät.
Schritt 4: Zuordnen einer URB-Struktur zur Beschreibung der Details der Übertragung
Ordnen Sie eine URB-Struktur in einem nicht ausgelagerten Pool zu.
Wenn Ihr Clienttreiber WDM-Routinen verwendet, muss der Treiber die USBD_IsochUrbAllocate aufrufen, wenn Sie über das Windows Driver Kit (WDK) für Windows 8 verfügen. Ein Clienttreiber kann die Routine verwenden, um Windows Vista und höhere Versionen des Windows-Betriebssystems als Ziel zu verwenden. Wenn Sie nicht über das WDK für Windows 8 verfügen oder der Clienttreiber für eine frühere Version des Betriebssystems vorgesehen ist, können Sie die Struktur im Stapel oder im nicht ausgelagerten Pool zuordnen, indem Sie ExAllocatePoolWithTag aufrufen.
Ein WDF-Clienttreiber kann die WdfUsbTargetDeviceCreateIsochUrb-Methode aufrufen, um Arbeitsspeicher für die URB-Struktur zuzuweisen.
Der UrbIsochronousTransfer-Member der URB-Struktur verweist auf eine _URB_ISOCH_TRANSFER-Struktur , die die Details einer isochronen Übertragung beschreibt. Initialisieren Sie die folgenden UrbIsochronousTransfer-Member wie folgt:
Legen Sie den UrbIsochronousTransfer.Hdr.Length-Member auf die Größe der URB fest. Um die Größe der URB abzurufen, rufen Sie GET_ISO_URB_SIZE Makro auf, und geben Sie die Anzahl der Pakete an.
Legen Sie den UrbIsochronousTransfer.hdr.Function-Member auf fest
URB_FUNCTION_ISOCH_TRANSFER
.Legen Sie den UrbIsochronousTransfer.NumberOfPackets-Member auf die Anzahl der isochronen Pakete fest.
Legen Sie urbIsochronousTransfer.PipeHandle auf das undurchsichtige Handle für die Pipe fest, die dem Endpunkt zugeordnet ist. Stellen Sie sicher, dass der Pipehandle das USBD-Pipehandle ist, das vom USB-Treiberstapel (Universal Serial Bus) verwendet wird.
Um das USBD-Pipehandle abzurufen, kann ein WDF-Clienttreiber die WdfUsbTargetPipeWdmGetPipeHandle-Methode aufrufen und das WDFUSBPIPE-Handle für das Pipeobjekt des Frameworks angeben. Ein WDM-Clienttreiber muss dasselbe Handle verwenden, das im PipeHandle-Member der USBD_PIPE_INFORMATION-Struktur abgerufen wurde.
Geben Sie die Richtung der Übertragung an. Legen Sie UrbIsochronousTransfer.TransferFlags auf USBD_TRANSFER_DIRECTION_IN für eine isochrone IN-Übertragung fest (Lesen vom Gerät); USBD_TRANSFER_DIRECTION_OUT für eine isochrone OUT-Übertragung (Schreiben auf das Gerät).
Geben Sie das flag USBD_START_ISO_TRANSFER_ASAP in UrbIsochronousTransfer an. TransferFlags. Das Flag weist den USB-Treiberstapel an, die Übertragung im nächsten geeigneten Frame zu senden. Wenn der Clienttreiber zum ersten Mal eine isochrone URB für diese Pipe sendet, sendet der Treiberstapel die isochronen Pakete in der URB so bald wie möglich. Der USB-Treiberstapel verfolgt den nächsten Frame nach, der für nachfolgende URBs in dieser Pipe verwendet werden soll. Wenn es zu einer Verzögerung beim Senden einer nachfolgenden isochronen URB kommt, die das flag USBD_START_ISO_TRANSFER_ASAP verwendet, betrachtet der Treiberstapel einige oder alle Pakete dieser URB als verspätet und überträgt diese Pakete nicht.
Der USB-Treiberstapel setzt seine USBD_START_ISO_TRANSFER_ASAP Framenachverfolgung zurück, wenn der Stapel keine isochrone URB für 1024 Frames erhält, nachdem er die vorherige URB für diese Pipe abgeschlossen hat. Anstatt das USBD_START_ISO_TRANSFER_ASAP-Flag anzugeben, können Sie den Startframe angeben. Weitere Informationen finden Sie im Abschnitt mit Hinweisen.
Geben Sie den Übertragungspuffer und seine Größe an. Sie können einen Zeiger auf den Puffer in UrbIsochronousTransfer.TransferBuffer oder der MDL festlegen, der den Puffer in UrbIsochronousTransfer.TransferBufferMDL beschreibt.
Um die MDL für den Übertragungspuffer abzurufen, kann ein WDF-Clienttreiber je nach Übertragungsrichtung WdfRequestRetrieveOutputWdmMdl oder WdfRequestRetrieveInputWdmMdl aufrufen.
Schritt 5: Angeben der Details jedes isochronen Pakets in der Übertragung
Der USB-Treiberstapel ordnet die neue URB-Struktur zu, die groß genug ist, um Informationen zu jedem isochronen Paket zu enthalten, aber nicht die im Paket enthaltenen Daten. In der URB-Struktur ist der UrbIsochronousTransfer.IsoPacket-Member ein Array von USBD_ISO_PACKET_DESCRIPTOR , das die Details der einzelnen isochronen Pakete in der Übertragung beschreibt. Pakete müssen zusammenhängend sein. Die Anzahl der Elemente im Array muss der Anzahl isochroner Pakete entsprechen, die im UrbIsochronousTransfer.NumberOfPackets-Element der URB angegeben ist.
Bei einer Hochgeschwindigkeitsübertragung korreliert jedes Element im Array mit einem isochronen Paket in einem Microframe. Bei voller Geschwindigkeit korreliert jedes Element mit einem isochronen Paket, das in einem Frame übertragen wird.
Geben Sie für jedes Element den Byteoffset jedes isochronen Pakets ab dem Anfang des gesamten Übertragungspuffers für die Anforderung an. Sie können diesen Wert angeben, indem Sie urbIsochronousTransfer.IsoPacket[i] festlegen. Offsetmember . Der USB-Treiberstapel verwendet den angegebenen Wert, um die Datenmenge zu verfolgen, die gesendet oder empfangen werden soll.
Festlegen des Offsets für eine Full-Speed-Übertragung
Für das Beispiel sind dies die Arrayeinträge für den Übertragungspuffer in voller Geschwindigkeit. Bei voller Geschwindigkeit verfügt der Clienttreiber über einen Frame, um ein isochrones Paket mit bis zu 1.023 Bytes zu übertragen. Ein Übertragungspuffer von 25.575 Bytes kann 25 isochrone Pakete enthalten, die jeweils 1.023 Bytes lang sind. Für den gesamten Puffer sind insgesamt 25 Frames erforderlich.
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.
Festlegen des Offsets für eine High-Speed-Übertragung
Für das Beispiel sind dies die Arrayeinträge für einen Übertragungspuffer in Hoher Geschwindigkeit. Im Beispiel wird davon ausgegangen, dass der Puffer 24.576 Bytes beträgt, und der Clienttreiber über einen Frame zum Übertragen von acht isochronen Paketen mit jeweils 3.072 Bytes Länge verfügt.
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.
Festlegen des Offsets für eine SuperSpeed-Übertragung
Für das Beispiel ist dies der Arrayoffset für SuperSpeed. Sie können bis zu 45.000 Bytes in einem Frame übertragen. Der Übertragungspuffer der Größe 360.000 passt in acht Mikroframes.
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-Member impliziert nicht die Länge jedes Pakets der isochronen URB. IsoPacket[i]. Die Länge wird vom USB-Treiberstapel aktualisiert, um die tatsächliche Anzahl von Bytes anzugeben, die vom Gerät für isochrone IN-Übertragungen empfangen werden. Bei isochronen OUT-Übertragungen ignoriert der Treiberstapel den Wert, der in IsoPacket[i] festgelegt ist. Länge.
Geben Sie die Startnummer des USB-Frames für die Übertragung an.
Das UrbIsochronousTransfer.StartFrame-Element der URB gibt die START-USB-Framenummer für die Übertragung an. Zwischen dem Zeitpunkt, zu dem der Clienttreiber eine URB übermittelt, und der Zeit, zu der der USB-Treiberstapel die URB verarbeitet, gibt es immer Latenz. Daher sollte der Clienttreiber immer einen Startframe angeben, der sich später als der aktuelle Frame befindet, wenn der Treiber die URB übermittelt. Um die aktuelle Framenummer abzurufen, kann der Clienttreiber die URB_FUNCTION_GET_CURRENT_FRAME_NUMBER Anforderung an den USB-Treiberstapel (_URB_GET_CURRENT_FRAME_NUMBER) senden.
Bei isochronen Übertragungen muss der absolute Unterschied zwischen dem aktuellen Frame und dem StartFrame-Wert kleiner als USBD_ISO_START_FRAME_RANGE sein. Wenn sich StartFrame nicht innerhalb des richtigen Bereichs befindet, legt der USB-Treiberstapel das Statuselement des URB-Headers (siehe _URB_HEADER) auf USBD_STATUS_BAD_START_FRAME fest und verwirft die gesamte URB.
Der in der URB angegebene StartFrame-Wert gibt die Framenummer an, in der das erste isochrone Paket der URB übertragen wird. Die Framenummer für nachfolgende Pakete hängt von der Busgeschwindigkeit und den Werten des Abrufzeitraums des Endpunkts ab. Bei einer Übertragung mit voller Geschwindigkeit wird beispielsweise das erste Paket in StartFrame übertragen. Das zweite Paket wird in StartFrame+1 usw. übertragen. Die Art und Weise, in der der USB-Treiberstapel isochrone Pakete für volle Geschwindigkeit in Frames überträgt, wird wie folgt dargestellt:
Frame (StartFrame) IsoPacket [0]
Frame (StartFrame+1) IsoPacket [1]
Frame (StartFrame+2) IsoPacket [2]
Frame (StartFrame+3) IsoPacket [3]
...
Bei Hochgeschwindigkeitsgeräten mit dem Intervallwert 1 ändert sich die Framenummer bei jedem achten Mikroframe. Die Art und Weise, in der der USB-Treiberstapel isochrone Pakete für hohe Geschwindigkeit in Frames überträgt, wird wie folgt dargestellt:
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]
Wenn der USB-Treiberstapel die URB verarbeitet, verwirft der Treiber alle isochronen Pakete in der URB, deren Framezahlen niedriger als die aktuelle Framenummer sind. Der Treiberstapel legt das Statuselement des Paketdeskriptors für jedes verworfene Paket auf USBD_STATUS_ISO_NA_LATE_USBPORT, USBD_STATUS_ISO_NOT_ACCESSED_BY_HW oder USBD_STATUS_ISO_NOT_ACCESSED_LATE fest. Obwohl einige Pakete in der URB verworfen werden, versucht der Treiberstapel, nur die Pakete zu übertragen, deren Framezahlen höher als die aktuelle Framenummer sind.
Die Überprüfung eines gültigen StartFrame-Members ist bei Hochgeschwindigkeitsübertragungen etwas komplizierter, da der USB-Treiberstapel jedes isochrone Paket in einen Hochgeschwindigkeits-Microframe lädt. Der Wert in StartFrame bezieht sich jedoch auf die Framenummer von 1 Millisekunde (Full-Speed) und nicht auf den Mikroframe. Wenn der in der URB aufgezeichnete StartFrame-Wert beispielsweise ein Wert kleiner als der aktuelle Frame ist, kann der Treiberstapel bis zu acht Pakete verwerfen. Die genaue Anzahl verworfener Pakete hängt vom Abfragezeitraum ab, der der isochronen Pipe zugeordnet ist.
Beispiel für isochrone Übertragung
Das folgende Codebeispiel zeigt, wie Sie eine URB für eine isochrone Übertragung für Die Übertragung mit voller Geschwindigkeit, Hochgeschwindigkeits- und SuperSpeed-Übertragung erstellen.
#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;
}