Senden einer USB-Steuerübertragung
In diesem Artikel wird die Struktur einer Steuerungsübertragung erläutert und erläutert, wie ein Clienttreiber eine Steuerungsanforderung an das Gerät senden soll.
Informationen zum Standardendpunkt
Alle USB-Geräte müssen mindestens einen Endpunkt unterstützen, der als Standardendpunkt bezeichnet wird. Jede Übertragung, die auf den Standardendpunkt abzielt, wird als Steuerungsübertragung bezeichnet. Der Zweck einer Steuerungsübertragung besteht darin, dem Host zu ermöglichen, Geräteinformationen abzurufen, das Gerät zu konfigurieren oder Steuerungsvorgänge auszuführen, die für das Gerät eindeutig sind.
Beginnen wir mit der Untersuchung dieser Merkmale des Standardendpunkts.
- Die Adresse des Standardendpunkts ist 0.
- Der Standardendpunkt ist bidirektional, d. h. der Host kann Daten an den Endpunkt senden und daten von diesem innerhalb einer Übertragung empfangen.
- Der Standardendpunkt ist auf Geräteebene verfügbar und in keiner Schnittstelle des Geräts definiert.
- Der Standardendpunkt ist aktiv, sobald eine Verbindung zwischen dem Host und dem Gerät hergestellt wird. Er ist bereits aktiv, bevor eine Konfiguration ausgewählt wird.
- Die maximale Paketgröße des Standardendpunkts hängt von der Busgeschwindigkeit des Geräts ab. Niedrige Geschwindigkeit, 8 Bytes; volle und hohe Geschwindigkeit, 64 Bytes; SuperSpeed, 512 Bytes.
Layout einer Steuerelementübertragung
Da Steuerungsübertragungen Übertragungen mit hoher Priorität sind, wird eine bestimmte Bandbreite auf dem Bus vom Host reserviert. Bei Geräten mit niedriger und voller Geschwindigkeit 10 % der Bandbreite; 20 % für High- und SuperSpeed-Transfergeräte. Sehen wir uns nun das Layout einer Steuerelementübertragung an.
Eine Steuerungsübertragung ist in drei Transaktionen unterteilt: Setuptransaktion, Datentransaktion und status Transaktion. Jede Transaktion enthält drei Pakettypen: Tokenpaket, Datenpaket und Handshakepaket.
Bestimmte Felder sind für alle Pakete gemeinsam. Die Felder lauten:
- Synchronisierungsfeld, das den Start des Pakets angibt.
- Packet Identifier (PID), der den Pakettyp, die Richtung der Transaktion und im Falle eines Handshakepakets den Erfolg oder Fehler der Transaktion angibt.
- EOP-Feld gibt das Ende des Pakets an.
Andere Felder hängen vom Pakettyp ab.
Tokenpaket
Jede Setuptransaktion beginnt mit einem Tokenpaket. Hier sehen Sie die Struktur des Pakets. Der Host sendet immer das Tokenpaket.
Der PID-Wert gibt den Typ des Tokenpakets an. Die folgenden Werte sind möglich:
- SETUP: Gibt den Start einer Setuptransaktion in einer Steuerungsübertragung an.
- IN: Gibt an, dass der Host Daten vom Gerät anfordert (Lesefall).
- OUT: Gibt an, dass der Host Daten an das Gerät sendet (Schreibfall).
- SOF: Gibt den Anfang des Frames an. Dieser Typ von Tokenpaket enthält eine 11-Bit-Framenummer. Der Host sendet das SOF-Paket. Die Häufigkeit, mit der dieses Paket gesendet wird, hängt von der Busgeschwindigkeit ab. Bei voller Geschwindigkeit sendet der Host das Paket alle 1 Millisekunden; alle 125 Mikrosekunden auf einem Hochgeschwindigkeitsbus.
Datenpaket
Unmittelbar nach dem Tokenpaket befindet sich das Datenpaket, das die Nutzlast enthält. Die Anzahl der Bytes, die jedes Datenpaket enthalten kann, hängt von der maximalen Paketgröße des Standardendpunkts ab. Das Datenpaket kann je nach Übertragungsrichtung entweder vom Host oder vom Gerät gesendet werden.
Handshakepaket
Unmittelbar nach dem Datenpaket befindet sich das Handshakepaket. Die PID des Pakets gibt an, ob das Paket vom Host oder vom Gerät empfangen wurde. Das Handshakepaket kann je nach Übertragungsrichtung entweder vom Host oder vom Gerät gesendet werden.
Sie können die Struktur von Transaktionen und Paketen mithilfe eines beliebigen USB-Analysetools wie Beagle, Ellisys und LeCroy USB-Protokollanalysetools anzeigen. Ein Analysegerät zeigt an, wie Daten über das Kabel an ein USB-Gerät gesendet oder von diesem empfangen werden. In diesem Beispiel untersuchen wir einige Ablaufverfolgungen, die von einem LeCroy-USB-Analysetool erfasst wurden. Dieses Beispiel dient nur für Informationen. Dies ist keine Bestätigung durch Microsoft.
Einrichten der Transaktion
Der Host initiiert immer eine Steuerungsübertragung. Dazu wird eine Setuptransaktion gesendet. Diese Transaktion enthält ein Tokenpaket namens Setuptoken , gefolgt von einem 8-Byte-Datenpaket. Dieser Screenshot zeigt ein Beispiel für eine Setuptransaktion.
In der vorherigen Ablaufverfolgung initiiert der Host (angegeben durch H)die Steuerungsübertragung, indem er das Setuptokenpaket #434 sendet. Beachten Sie, dass die PID SETUP angibt und ein Setuptoken angibt. Auf die PID folgen die Geräteadresse und die Adresse des Endpunkts. Bei Steuerungsübertragungen ist diese Endpunktadresse immer 0.
Als Nächstes sendet der Host das Datenpaket #435. Die PID ist DATA0, und dieser Wert wird für die Paketsequenzierung verwendet (zu diskutieren). Auf die PID folgen 8 Bytes, die die Standard Informationen zu dieser Anforderung enthält. Diese 8 Bytes geben den Typ der Anforderung und die Größe des Puffers an, in den das Gerät seine Antwort schreibt.
Alle Bytes werden in umgekehrter Reihenfolge empfangen. Wie in Abschnitt 9.3 beschrieben, werden die folgenden Felder und Werte angezeigt:
Feld | Size | Wert | BESCHREIBUNG |
---|---|---|---|
bmRequestType (siehe 9.3.1 bmRequestType) | 1 | 0x80 | Die Datenübertragungsrichtung ist vom Gerät zum Host (D7 ist 1) Die Anforderung ist eine Standardanforderung (D6... D5 ist 0) Der Empfänger der Anforderung ist device (D4 ist 0). |
bRequest (siehe Abschnitt siehe 9.3.2 und Tabelle 9-4) | 1 | 0x06 | Der Anforderungstyp ist GET_DESCRIPTOR. |
wValue (siehe Tabelle 9-5) | 2 | 0x0100 | Der Anforderungswert gibt an, dass der Deskriptortyp DEVICE ist. |
wIndex (siehe Abschnitt 9.3.4) | 2 | 0x0000 | Die Richtung ist vom Host zum Gerät (D7 ist 1). Die Endpunktnummer ist 0. |
wLength (siehe Abschnitt 9.3.5) | 2 | 0x0012 | Die Anforderung besteht darin, 18 Bytes abzurufen. |
Daher können wir daraus schließen, dass der Host in dieser Steuerelementübertragung (Lese-)Übertragung eine Anforderung zum Abrufen des Gerätedeskriptors sendet und 18 Bytes als Übertragungslänge angibt, um diesen Deskriptor zu halten. Wie das Gerät diese 18 Bytes sendet, hängt davon ab, wie viele Daten der Standardendpunkt in einer Transaktion senden kann. Diese Informationen sind im Gerätedeskriptor enthalten, der vom Gerät in der Datentransaktion zurückgegeben wird.
Als Antwort sendet das Gerät ein Handshakepaket (#436, die von D)angegeben wird. Beachten Sie, dass der PID-Wert ACK (ACK-Paket) ist. Dies gibt an, dass das Gerät die Transaktion bestätigt hat.
Datentransaktion
Nun sehen wir uns an, was das Gerät als Antwort auf die Anforderung zurückgibt. Die tatsächlichen Daten werden in einer Datentransaktion übertragen.
Hier ist die Ablaufverfolgung für die Datentransaktion.
Beim Empfang des ACK-Pakets initiiert der Host die Datentransaktion. Um die Transaktion zu initiieren, sendet sie ein Tokenpaket (#450) mit der Richtung ALS IN (sogenanntes IN-Token).
Als Antwort sendet das Gerät ein Datenpaket (#451), das dem IN-Token folgt. Dieses Datenpaket enthält den tatsächlichen Gerätedeskriptor. Das erste Byte gibt die Länge des Gerätedeskriptors an, 18 Bytes (0x12). Das letzte Byte in diesem Datenpaket gibt die maximale Paketgröße an, die vom Standardendpunkt unterstützt wird. In diesem Fall sehen wir, dass das Gerät 8 Bytes gleichzeitig über seinen Standardendpunkt senden kann.
Hinweis
Die maximale Paketgröße des Standardendpunkts hängt von der Geschwindigkeit des Geräts ab. Der Standardendpunkt eines Hochgeschwindigkeitsgeräts ist 64 Bytes. Low-Speed-Gerät ist 8 Bytes.
Der Host bestätigt die Datentransaktion, indem er ein ACK-Paket (#452) an das Gerät sendet.
Berechnen wir die Menge der zurückgegebenen Daten. Im Feld wLength des Datenpakets (#435) in der Setuptransaktion hat der Host 18 Bytes angefordert. In der Datentransaktion sehen wir, dass nur die ersten 8 Bytes des Gerätedeskriptors vom Gerät empfangen wurden. Wie empfängt der Host also Informationen, die in den verbleibenden 10 Bytes gespeichert sind? Das Gerät tut dies in zwei Transaktionen: 8 Bytes und dann die letzten 2 Bytes.
Nachdem der Host nun die maximale Paketgröße des Standardendpunkts kennt, initiiert der Host eine neue Datentransaktion und fordert basierend auf der Paketgröße den nächsten Teil an.
Hier ist die nächste Datentransaktion:
Der Host initiiert die vorherige Datentransaktion, indem er ein IN-Token (#463) sendet und die nächsten 8 Bytes vom Gerät anfordert. Das Gerät antwortet mit einem Datenpaket (#464), das die nächsten 8 Bytes des Gerätedeskriptors enthält.
Beim Empfang der 8 Bytes sendet der Host ein ACK-Paket (#465) an das Gerät.
Als Nächstes fordert der Host die letzten 2 Bytes in einer anderen Datentransaktion wie folgt an:
Daher sehen wir, dass der Host, um 18 Bytes vom Gerät auf den Host zu übertragen, die Anzahl der übertragenen Bytes nachverfolgt und drei Datentransaktionen (8+8+2) initiiert hat.
Hinweis
Beachten Sie die PID der Datenpakete in Datentransaktionen 19, 23, 26. Die PID wechselt zwischen DATA0 und DATA1. Diese Sequenz wird als Datenschalter bezeichnet. In Fällen, in denen mehrere Datentransaktionen vorhanden sind, wird die Datenschaltung verwendet, um die Paketsequenz zu überprüfen. Diese Methode stellt sicher, dass die Datenpakete nicht dupliziert oder verloren gehen.
Durch Zuordnen der konsolidierten Datenpakete zur Struktur des Gerätedeskriptors (siehe Tabelle 9-8) werden die folgenden Felder und Werte angezeigt:
Feld | Size | Wert | BESCHREIBUNG |
---|---|---|---|
bLength | 1 | 0x12 | Die Länge des Gerätedeskriptors beträgt 18 Bytes. |
bDescriptorType | 1 | 0x01 | Der Deskriptortyp ist Gerät. |
bcdUSB | 2 | 0x0100 | Die Spezifikationsversionsnummer ist 1.00. |
bDeviceClass | 1 | 0x00 | Die Geräteklasse ist 0. Jede Schnittstelle in der Konfiguration verfügt über die Klasseninformationen. |
bDeviceSubClass | 1 | 0x00 | Die Unterklasse ist 0, da die Geräteklasse 0 ist. |
bProtocol | 1 | 0x00 | Protokoll ist 0. Dieses Gerät verwendet keine klassenspezifischen Protokolle. |
bMaxPacketSize0 | 1 | 0x08 | Die maximale Paketgröße des Endpunkts beträgt 8 Bytes. |
idVendor | 2 | 0x0562 | Fernkommunikation. |
idProduct | 2 | 0x0002 | USB-Mikrofon. |
bcdDevice | 2 | 0x0100 | Gibt die Gerätefreigabenummer an. |
iManufacturer | 1 | 0x01 | Herstellerzeichenfolge. |
iProduct | 1 | 0x02 | Produktzeichenfolge. |
iSerialNumber | 1 | 0x03 | Seriennummer. |
bNumConfigurations | 1 | 0x01 | Anzahl der Konfigurationen. |
Durch die Untersuchung dieser Werte haben wir einige vorläufige Informationen über das Gerät. Das Gerät ist ein USB-Mikrofon mit niedriger Geschwindigkeit. Die maximale Paketgröße des Standardendpunkts beträgt 8 Bytes. Das Gerät unterstützt eine Konfiguration.
Statustransaktion
Schließlich schließt der Host die Steuerungsübertragung ab, indem er die letzte Transaktion initiiert: status Transaktion.
Der Host startet die Transaktion mit einem OUT-Tokenpaket (#481). Der Zweck dieses Pakets besteht darin, zu überprüfen, ob das Gerät alle angeforderten Daten gesendet hat. In dieser status Transaktion wird kein Datenpaket gesendet. Das Gerät antwortet mit einem ACK-Paket. Wenn ein Fehler aufgetreten ist, könnte die PID entweder NAK oder STALL sein.
Treibermodelle
Voraussetzungen
Bevor der Clienttreiber Pipes aufzählen kann, stellen Sie sicher, dass diese Anforderungen erfüllt sind:
Der Clienttreiber muss das Framework-USB-Zielgerätobjekt erstellt haben.
Wenn Sie die USB-Vorlagen verwenden, die mit Microsoft Visual Studio Professional 2012 bereitgestellt werden, führt der Vorlagencode diese Aufgaben aus. Der Vorlagencode ruft das Handle für das Zielgerätobjekt ab und speichert im Gerätekontext.
KMDF-Clienttreiber
Ein KMDF-Clienttreiber muss ein WDFUSBDEVICE-Handle abrufen, indem die WdfUsbTargetDeviceCreateWithParameters-Methode aufgerufen wird. Weitere Informationen finden Sie unter "Gerätequellcode" unter Grundlegendes zur Codestruktur des USB-Clienttreibers (KMDF).
UMDF-Clienttreiber
Ein UMDF-Clienttreiber muss einen IWDFUsbTargetDevice-Zeiger abrufen, indem das Framework-Zielgerätobjekt abfragt. Weitere Informationen finden Sie unter "IPnpCallbackHardware-Implementierung und USB-spezifische Aufgaben" unter Grundlegendes zur Codestruktur des USB-Clienttreibers (UMDF).
Der wichtigste Aspekt für eine Steuerungsübertragung besteht darin, das Setuptoken entsprechend zu formatieren. Sammeln Sie vor dem Senden der Anforderung die folgenden Informationen:
- Richtung der Anforderung: Hosten zu Gerät oder Gerät zum Hosten.
- Empfänger der Anforderung: Gerät, Schnittstelle, Endpunkt oder andere.
- Anforderungskategorie: Standard, Klasse oder Anbieter.
- Anforderungstyp, z. B. eine GET_DESCRIPTPOR Anforderung. Weitere Informationen finden Sie in Abschnitt 9.5 der USB-Spezifikation.
- wValue - und wIndex-Werte . Diese Werte hängen vom Typ der Anforderung ab.
Sie können alle diese Informationen aus der offiziellen USB-Spezifikation abrufen.
Wenn Sie einen UMDF-Treiber schreiben, rufen Sie die Headerdatei Usb_hw.h aus dem UMDF-Beispieltreiber für OSR USB Fx2 Learning Kit ab. Diese Headerdatei enthält nützliche Makros und Struktur zum Formatieren des Setuppakets für die Steuerelementübertragung.
Alle UMDF-Treiber müssen mit einem Kernelmodustreiber kommunizieren, um Daten von Geräten senden und empfangen zu können. Bei einem USB-UMDF-Treiber ist der Kernelmodustreiber immer der von Microsoft bereitgestellte Treiber WinUSB (Winusb.sys).
Wenn ein UMDF-Treiber eine Anforderung für den USB-Treiberstapel sendet, sendet der Windows E/A-Manager die Anforderung an WinUSB. Nach dem Empfang der Anforderung verarbeitet WinUSB entweder die Anforderung oder leitet sie an den USB-Treiberstapel weiter.
Von Microsoft definierte Methoden zum Senden von Steuerungsübertragungsanforderungen
Ein USB-Clienttreiber auf dem Host initiiert die meisten Steuerungsanforderungen, um Informationen zum Gerät abzurufen, das Gerät zu konfigurieren oder Anbietersteuerungsbefehle zu senden. Alle diese Anforderungen können in folgende Kategorien unterteilt werden:
Standardanforderungen sind in der USB-Spezifikation definiert. Der Zweck des Sendens von Standardanforderungen besteht darin, Informationen über das Gerät, seine Konfigurationen, Schnittstellen und Endpunkte zu erhalten. Der Empfänger jeder Anforderung hängt vom Typ der Anforderung ab. Der Empfänger kann das Gerät, eine Schnittstelle oder ein Endpunkt sein.
Hinweis
Das Ziel einer beliebigen Steuerungsübertragung ist immer der Standardendpunkt. Der Empfänger ist die Entität des Geräts, für deren Informationen (Deskriptor, status usw.) der Host interessiert ist.
Anforderungen können weiter unterteilt werden in: Konfigurationsanforderungen, Featureanforderungen und status Anforderungen.
- Konfigurationsanforderungen werden gesendet, um Informationen vom Gerät abzurufen, damit der Host es konfigurieren kann, z. B. eine GET_DESCRIPTOR-Anforderung. Diese Anforderungen können auch Schreibanforderungen sein, die vom Host gesendet werden, um eine bestimmte Konfiguration oder eine alternative Einstellung im Gerät festzulegen.
- Featureanforderungen werden vom Clienttreiber gesendet, um bestimmte boolesche Geräteeinstellungen zu aktivieren oder zu deaktivieren, die vom Gerät, der Schnittstelle oder einem Endpunkt unterstützt werden.
- Statusanforderungen ermöglichen es dem Host, die usb-definierten status Bits eines Geräts, Endpunkts oder einer Schnittstelle abzurufen oder festzulegen.
Weitere Informationen finden Sie in Abschnitt 9.4 der USB-Spezifikation, Version 2.0. Die Standardanforderungstypen werden in der Headerdatei Usbspec.h definiert.
Klassenanforderungen werden durch eine bestimmte Geräteklassenspezifikation definiert.
Anbieteranforderungen werden vom Anbieter bereitgestellt und hängen von den anforderungen ab, die vom Gerät unterstützt werden.
Der von Microsoft bereitgestellte USB-Stapel verarbeitet die gesamte Protokollkommunikation mit dem Gerät, wie in den vorherigen Ablaufverfolgungen gezeigt. Der Treiber macht Gerätetreiberschnittstellen (Device Driver Interfaces, DDIs) verfügbar, die es einem Clienttreiber ermöglichen, Steuerungsübertragungen auf viele Arten zu senden. Wenn es sich bei Ihrem Clienttreiber um einen WDF-Treiber (Windows Driver Foundation) handelt, können Routinen direkt aufgerufen werden, um die gängigen Typen von Steuerelementanforderungen zu senden. WDF unterstützt steuerungsinterne Übertragungen sowohl für KMDF als auch für UMDF.
Bestimmte Arten von Steuerelementanforderungen werden nicht über WDF verfügbar gemacht. Für diese Anforderungen kann der Clienttreiber das WDF-Hybridmodell verwenden. Dieses Modell ermöglicht es dem Clienttreiber, Anforderungen im WDM-URB-Stil zu erstellen und zu formatieren und diese Anforderungen dann mithilfe von WDF-Frameworkobjekten zu senden. Das Hybridmodell gilt nur für Kernelmodustreiber.
Für UMDF-Treiber:
Verwenden Sie die in usb_hw.h definierten Hilfsmakros und -struktur. Dieser Header ist im UMDF-Beispieltreiber für OSR USB Fx2 Learning Kit enthalten.
Verwenden Sie diese Tabelle, um zu ermitteln, wie Sie Steuerungsanforderungen am besten an den USB-Treiberstapel senden können. Wenn Sie diese Tabelle nicht anzeigen können, lesen Sie die Tabelle in diesem Artikel.
Wenn Sie eine Steuerungsanforderung an... | Für einen KMDF-Treiber... | Für einen UMDF-Treiber... | Erstellen Sie für einen WDM-Treiber eine URB-Struktur (Hilfsroutine) |
---|---|---|---|
CLEAR_FEATURE: Deaktivieren Sie bestimmte Featureeinstellungen im Gerät, dessen Konfigurationen, Schnittstellen und Endpunkten. Siehe Abschnitt 9.4.1 in der USB-Spezifikation. |
|
|
_URB_CONTROL_FEATURE_REQUEST (UsbBuildFeatureRequest) URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT URB_FUNCTION_CLEAR_FEATURE_TO_OTHER |
GET_CONFIGURATION: Rufen Sie die aktuelle USB-Konfiguration ab. Siehe Abschnitt 9.4.2 in der USB-Spezifikation. | KMDF wählt standardmäßig die erste Konfiguration aus. So rufen Sie die gerätedefinierte Konfigurationsnummer ab:
|
UMDF wählt standardmäßig die erste Konfiguration aus. So rufen Sie die gerätedefinierte Konfigurationsnummer ab:
|
_URB_CONTROL_GET_CONFIGURATION_REQUEST URB_FUNCTION_GET_CONFIGURATION |
GET_DESCRIPTOR: Abrufen von Geräte-, Konfigurations-, Schnittstellen- und Endpunktdeskriptoren. Siehe Abschnitt 9.4.3 in der USB-Spezifikation. Weitere Informationen finden Sie unter USB-Deskriptoren. |
Rufen Sie die folgenden Methoden auf:
|
Rufen Sie die folgenden Methoden auf:
|
_URB_CONTROL_DESCRIPTOR_REQUEST (UsbBuildGetDescriptorRequest) URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE |
GET_INTERFACE: Rufen Sie die aktuelle alternative Einstellung für eine Schnittstelle ab. Siehe Abschnitt 9.4.4 in der USB-Spezifikation. |
|
|
_URB_CONTROL_GET_INTERFACE_REQUEST URB_FUNCTION_GET_INTERFACE |
GET_STATUS: Abrufen status Bits von einem Gerät, Endpunkt oder einer Schnittstelle. Siehe Abschnitt 9.4.5. in der USB-Spezifikation. |
|
|
_URB_CONTROL_GET_STATUS_REQUEST (UsbBuildGetStatusRequest) URB_FUNCTION_GET_STATUS_FROM_DEVICE URB_FUNCTION_GET_STATUS_FROM_INTERFACE URB_FUNCTION_GET_STATUS_FROM_ENDPOINT URB_FUNCTION_GET_STATUS_FROM_OTHER. |
SET_ADDRESS: Siehe Abschnitt 9.4.6 der USB-Spezifikation. | Diese Anforderung wird vom USB-Treiberstapel verarbeitet. Der Clienttreiber kann diesen Vorgang nicht ausführen. | Diese Anforderung wird vom USB-Treiberstapel verarbeitet. Der Clienttreiber kann diesen Vorgang nicht ausführen. | Diese Anforderung wird vom USB-Treiberstapel verarbeitet. Der Clienttreiber kann diesen Vorgang nicht ausführen. |
SET_CONFIGURATION: Legen Sie eine Konfiguration fest. Siehe Abschnitt 9.4.7 der USB-Spezifikation. Weitere Informationen finden Sie unter Auswählen einer Konfiguration für ein USB-Gerät. |
Standardmäßig wählt KMDF die Standardkonfiguration und die erste alternative Einstellung in jeder Schnittstelle aus. Der Clienttreiber kann die Standardkonfiguration ändern, indem er die WdfUsbTargetDeviceSelectConfigType-Methode aufruft und WdfUsbTargetDeviceSelectConfigTypeUrb als Anforderungsoption angibt. Anschließend müssen Sie eine URB für diese Anforderung formatieren und an den USB-Treiberstapel übermitteln. | Standardmäßig wählt UMDF die Standardkonfiguration und die erste alternative Einstellung in jeder Schnittstelle aus. Der Clienttreiber kann die Konfiguration nicht ändern. | _URB_SELECT_CONFIGURATION (USBD_SelectConfigUrbAllocateAndBuild) URB_FUNCTION_SELECT_CONFIGURATION |
SET_DESCRIPTOR: Aktualisieren Sie ein vorhandenes Gerät, eine vorhandene Konfiguration oder einen Zeichenfolgendeskriptor. Siehe Abschnitt 9.4.8 unter USB-Spezifikation. Diese Anforderung wird häufig nicht verwendet. Der USB-Treiberstapel akzeptiert jedoch eine solche Anforderung vom Clienttreiber. |
|
|
_URB_CONTROL_DESCRIPTOR_REQUEST URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE |
SET_FEATURE: Aktivieren Sie bestimmte Featureeinstellungen im Gerät, seinen Konfigurationen, Schnittstellen und Endpunkten. Siehe Abschnitt 9.4.9 in der USB-Spezifikation. |
|
|
_URB_CONTROL_FEATURE_REQUEST (UsbBuildFeatureRequest) URB_FUNCTION_SET_FEATURE_TO_DEVICE URB_FUNCTION_SET_FEATURE_TO_INTERFACE URB_FUNCTION_SET_FEATURE_TO_ENDPOINT URB_FUNCTION_SET_FEATURE_TO_OTHER |
SET_INTERFACE: Ändert die alternative Einstellung in einer Schnittstelle. Siehe Abschnitt 9.4.9 in der USB-Spezifikation. Weitere Informationen finden Sie unter Auswählen einer alternativen Einstellung in einer USB-Schnittstelle. |
WdfUsbTargetDeviceSelectConfig
|
|
_URB_SELECT_INTERFACE (USBD_SelectInterfaceUrbAllocateAndBuild) URB_FUNCTION_SELECT_INTERFACE |
SYNC_FRAME: Legen Sie die Synchronisierungsrahmennummer des Endpunkts fest, und rufen Sie sie ab. Siehe Abschnitt 9.4.10 in der USB-Spezifikation. | Diese Anforderung wird vom USB-Treiberstapel verarbeitet. Der Clienttreiber kann diesen Vorgang nicht ausführen. | Diese Anforderung wird vom USB-Treiberstapel verarbeitet. Der Clienttreiber kann diesen Vorgang nicht ausführen. | Diese Anforderung wird vom USB-Treiberstapel verarbeitet. Der Clienttreiber kann diesen Vorgang nicht ausführen. |
Für geräteklassenspezifische Anforderungen und Anbieterbefehle. |
|
|
_URB_CONTROL_VENDOR_OR_CLASS_REQUEST (UsbBuildVendorRequest) URB_FUNCTION_VENDOR_DEVICE URB_FUNCTION_VENDOR_INTERFACE URB_FUNCTION_VENDOR_ENDPOINT URB_FUNCTION_VENDOR_OTHER URB_FUNCTION_CLASS_DEVICE URB_FUNCTION_CLASS_INTERFACE URB_FUNCTION_CLASS_ENDPOINT URB_FUNCTION_CLASS_OTHER |
Senden einer Steuerungsübertragung für Anbieterbefehle – KMDF
Dieses Verfahren zeigt, wie ein Clienttreiber eine Steuerungsübertragung senden kann. In diesem Beispiel sendet der Clienttreiber einen Anbieterbefehl, der die Firmwareversion vom Gerät abruft.
Deklarieren Sie eine Konstante für den Vendor-Befehl. Untersuchen Sie die Hardwarespezifikation, und ermitteln Sie den gewünschten Anbieterbefehl.
Deklarieren Sie eine WDF_MEMORY_DESCRIPTOR-Struktur , und initialisieren Sie sie, indem Sie das makro WDF_MEMORY_DESCRIPTOR_INIT_BUFFER aufrufen. Diese Struktur erhält die Antwort vom Gerät, nachdem der USB-Treiber die Anforderung abgeschlossen hat.
Je nachdem, ob Sie die Anforderung synchron oder asynchron senden, geben Sie Ihre Sendeoptionen an:
Wenn Sie die Anforderung synchron senden, indem Sie WdfUsbTargetDeviceSendControlTransferSynchronously aufrufen, geben Sie einen Timeoutwert an. Dieser Wert ist wichtig, da Sie den Thread ohne Timeout unbegrenzt blockieren können.
Deklarieren Sie hierzu eine WDF_REQUEST_SEND_OPTIONS-Struktur , und initialisieren Sie sie, indem Sie das makro WDF_REQUEST_SEND_OPTIONS_INIT aufrufen. Geben Sie die Option als WDF_REQUEST_SEND_OPTION_TIMEOUT an.
Legen Sie als Nächstes den Timeoutwert fest, indem Sie das Makro WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT aufrufen.
Wenn Sie die Anforderung asynchron senden, implementieren Sie eine Vervollständigungsroutine. Geben Sie alle zugeordneten Ressourcen in der Vervollständigungsroutine frei.
Deklarieren Sie eine WDF_USB_CONTROL_SETUP_PACKET Struktur, die das Setuptoken enthält, und formatieren Sie die Struktur. Rufen Sie hierzu das Makro WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR auf, um das Setuppaket zu formatieren. Geben Sie im Aufruf die Richtung der Anforderung, den Empfänger, die Optionen für die gesendete Anforderung (initialisiert in Schritt 3) und die Konstante für den Vendor-Befehl an.
Senden Sie die Anforderung, indem Sie WdfUsbTargetDeviceSendControlTransferSynchronously oder WdfUsbTargetDeviceFormatRequestForControlTransfer aufrufen.
Überprüfen Sie den vom Framework zurückgegebenen NTSTATUS-Wert, und überprüfen Sie den empfangenen Wert.
In diesem Codebeispiel wird eine Steuerungsübertragungsanforderung an ein USB-Gerät gesendet, um dessen Firmwareversion abzurufen. Die Anforderung wird synchron gesendet, und der Clienttreiber gibt einen relativen Timeoutwert von 5 Sekunden (in 100 Nanosekundeneinheiten) an. Der Treiber speichert die empfangene Antwort im vom Treiber definierten Gerätekontext.
enum {
USBFX2_GET_FIRMWARE_VERSION = 0x1,
....
} USBFX2_VENDOR_COMMANDS;
#define WDF_TIMEOUT_TO_SEC ((LONGLONG) 1 * 10 * 1000 * 1000) // defined in wdfcore.h
const __declspec(selectany) LONGLONG
DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC;
typedef struct _DEVICE_CONTEXT
{
...
union {
USHORT VersionAsUshort;
struct {
BYTE Minor;
BYTE Major;
} Version;
} Firmware; // Firmware version.
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
__drv_requiresIRQL(PASSIVE_LEVEL)
VOID GetFirmwareVersion(
__in PDEVICE_CONTEXT DeviceContext
)
{
NTSTATUS status;
WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket;
WDF_REQUEST_SEND_OPTIONS sendOptions;
USHORT firmwareVersion;
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
PAGED_CODE();
firmwareVersion = 0;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID) &firmwareVersion, sizeof(firmwareVersion));
WDF_REQUEST_SEND_OPTIONS_INIT(
&sendOptions,
WDF_REQUEST_SEND_OPTION_TIMEOUT
);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(
&sendOptions,
DEFAULT_CONTROL_TRANSFER_TIMEOUT
);
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket,
BmRequestDeviceToHost, // Direction of the request
BmRequestToDevice, // Recipient
USBFX2_GET_FIRMWARE_VERSION, // Vendor command
0, // Value
0); // Index
status = WdfUsbTargetDeviceSendControlTransferSynchronously(
DeviceContext->UsbDevice,
WDF_NO_HANDLE, // Optional WDFREQUEST
&sendOptions,
&controlSetupPacket,
&memoryDescriptor, // MemoryDescriptor
NULL); // BytesTransferred
if (!NT_SUCCESS(status))
{
KdPrint(("Device %d: Failed to get device firmware version 0x%x\n", DeviceContext->DeviceNumber, status));
TraceEvents(DeviceContext->DebugLog,
TRACE_LEVEL_ERROR,
DBG_RUN,
"Device %d: Failed to get device firmware version 0x%x\n",
DeviceContext->DeviceNumber,
status);
}
else
{
DeviceContext->Firmware.VersionAsUshort = firmwareVersion;
TraceEvents(DeviceContext->DebugLog,
TRACE_LEVEL_INFORMATION,
DBG_RUN,
"Device %d: Get device firmware version : 0x%x\n",
DeviceContext->DeviceNumber,
firmwareVersion);
}
return;
}
Senden einer Steuerungsübertragung für GET_STATUS – UMDF
Dieses Verfahren zeigt, wie ein Clienttreiber eine Steuerungsübertragung für einen GET_STATUS-Befehl senden kann. Der Empfänger der Anforderung ist das Gerät, und die Anforderung ruft Informationen in den Bits D1-D0 ab. Weitere Informationen finden Sie unter Abbildung 9-4 in der USB-Spezifikation.
Schließen Sie die Headerdatei Usb_hw.h ein, die mit dem UMDF-Beispieltreiber für OSR USB Fx2 Learning Kit verfügbar ist.
Deklarieren Sie eine WINUSB_CONTROL_SETUP_PACKET-Struktur .
Initialisieren Sie das Setuppaket, indem Sie das Hilfsmakro aufrufen, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.
Geben Sie BmRequestToDevice als Empfänger an.
Geben Sie 0 im Indexwert an.
Rufen Sie die Hilfsmethode SendControlTransferSynchronously auf, um die Anforderung synchron zu senden.
Die Hilfsmethode erstellt die Anforderung, indem das initialisierte Setuppaket dem Frameworkanforderungsobjekt und dem Übertragungspuffer zugeordnet wird, indem die IWDFUsbTargetDevice::FormatRequestForControlTransfer-Methode aufgerufen wird. Die Hilfsmethode sendet dann die Anforderung durch Aufrufen der IWDFIoRequest::Send-Methode . Überprüfen Sie nach der Rückgabe der Methode den zurückgegebenen Wert.
Verwenden Sie die folgenden Werte, die in der WINUSB_DEVICE_TRAITS-Enumeration definiert sind, um festzustellen, ob die status auf eine selbstgesteuerte Remotereaktivierung hinweist:
In diesem Codebeispiel wird eine Steuerungsübertragungsanforderung an ein get the status des Geräts gesendet. Im Beispiel wird die Anforderung synchron gesendet, indem eine Hilfsmethode namens SendControlTransferSynchronously aufgerufen wird.
HRESULT
CDevice::GetDeviceStatus ()
{
HRESULT hr = S_OK;
USHORT deviceStatus;
ULONG bytesTransferred;
TraceEvents(TRACE_LEVEL_INFORMATION,
DRIVER_ALL_INFO,
"%!FUNC!: entry");
// Setup the control packet.
WINUSB_CONTROL_SETUP_PACKET setupPacket;
WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(
&setupPacket,
BmRequestToDevice,
0);
hr = SendControlTransferSynchronously(
&(setupPacket.WinUsb),
& deviceStatus,
sizeof(USHORT),
&bytesReturned
);
if (SUCCEEDED(hr))
{
if (deviceStatus & USB_GETSTATUS_SELF_POWERED)
{
m_Self_Powered = true;
}
if (deviceStatus & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)
{
m_remote_wake-enabled = true;
}
}
return hr;
}
Das folgende Codebeispiel zeigt die Implementierung der Hilfsmethode sendControlTransferSynchronously. Diese Methode sendet eine Anforderung synchron.
HRESULT
CDevice::SendControlTransferSynchronously(
_In_ PWINUSB_SETUP_PACKET SetupPacket,
_Inout_ PBYTE Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG LengthTransferred
)
{
HRESULT hr = S_OK;
IWDFIoRequest *pWdfRequest = NULL;
IWDFDriver * FxDriver = NULL;
IWDFMemory * FxMemory = NULL;
IWDFRequestCompletionParams * FxComplParams = NULL;
IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL;
*LengthTransferred = 0;
hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface
NULL, //pParentObject
&pWdfRequest);
if (SUCCEEDED(hr))
{
m_FxDevice->GetDriver(&FxDriver);
hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,
BufferLength,
NULL, //pCallbackInterface
pWdfRequest, //pParetObject
&FxMemory );
}
if (SUCCEEDED(hr))
{
hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest,
SetupPacket,
FxMemory,
NULL); //TransferOffset
}
if (SUCCEEDED(hr))
{
hr = pWdfRequest->Send( m_pIUsbTargetDevice,
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
0); //Timeout
}
if (SUCCEEDED(hr))
{
pWdfRequest->GetCompletionParams(&FxComplParams);
hr = FxComplParams->GetCompletionStatus();
}
if (SUCCEEDED(hr))
{
HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams));
WUDF_TEST_DRIVER_ASSERT(SUCCEEDED(hrQI));
WUDF_TEST_DRIVER_ASSERT( WdfUsbRequestTypeDeviceControlTransfer ==
FxUsbComplParams->GetCompletedUsbRequestType() );
FxUsbComplParams->GetDeviceControlTransferParameters( NULL,
LengthTransferred,
NULL,
NULL );
}
SAFE_RELEASE(FxUsbComplParams);
SAFE_RELEASE(FxComplParams);
SAFE_RELEASE(FxMemory);
pWdfRequest->DeleteWdfObject();
SAFE_RELEASE(pWdfRequest);
SAFE_RELEASE(FxDriver);
return hr;
}
Wenn Sie Winusb.sys als Funktionstreiber für Ihr Gerät verwenden, können Sie Steuerungsübertragungen von einer Anwendung senden. Verwenden Sie zum Formatieren des Setuppakets in WinUSB die INDF-Hilfsmakros und -Strukturen, die in der Tabelle in diesem Artikel beschrieben werden. Um die Anforderung zu senden, rufen Sie WinUsb_ControlTransfer-Funktion auf.
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für