Abrufen von USB-Deskriptoren (UWP-App)
Eine der Standard Aufgaben der Interaktion mit einem USB-Gerät besteht darin, Informationen darüber zu erhalten. Alle USB-Geräte stellen Informationen in Form mehrerer Datenstrukturen bereit, die als Deskriptoren bezeichnet werden. In diesem Artikel wird beschrieben, wie eine UWP-App Deskriptoren vom Gerät auf Endpunkt-, Schnittstellen-, Konfigurations- und Geräteebene abrufen kann. In diesem Artikel werden außerdem folgende Themen behandelt:
- Grundlegendes zum Layout von USB-Geräten
- Abrufen von USB-Standarddeskriptoren
- Abrufen benutzerdefinierter Deskriptoren
Wichtige APIs
USB-Deskriptoren
Ein USB-Gerät beschreibt seine Funktionen in zwei Standard-Deskriptoren: Gerätedeskriptor und Konfigurationsdeskriptor.
Ein USB-Gerät muss einen Gerätedeskriptor bereitstellen, der Informationen zu einem USB-Gerät als Ganzes enthält. Wenn das Gerät diesen Deskriptor nicht bereitstellt oder einen fehlerhaften Deskriptor bereitstellt, kann Windows den Gerätetreiber nicht laden. Die wichtigsten Informationen im Deskriptor sind die Hardware-ID des Geräts für das Gerät (Kombination aus Den Feldern Anbieter-ID und Produkt-ID ). Es basiert auf diesen Informationen, dass Windows in der Lage ist, einen im Lieferumfang enthaltenen Treiber für das Gerät zu finden. Eine weitere Wichtige Information ist die maximale Paketgröße des Standardendpunkts (MaxPacketSize0). Der Standardendpunkt ist das Ziel aller Steuerelementanforderungen, die der Host zum Konfigurieren an das Gerät sendet.
Die Länge des Gerätedeskriptors ist festgelegt.
Das USB-Gerät muss auch einen vollständigen Konfigurationsdeskriptor bereitstellen. Der Anfangsteil dieses Deskriptors hat eine feste Länge von 9 Bytes, rest ist variable Länge, abhängig von der Anzahl der Schnittstellen und Endpunkte, die diese Schnittstellen unterstützen. Der Teil mit fester Länge enthält Informationen zu einer USB-Konfiguration: Anzahl der unterstützten Schnittstellen und Stromverbrauch, wenn sich das Gerät in dieser Konfiguration befindet. Auf diese anfänglichen 9 Bytes folgt der variable Teil des Deskriptors, der Informationen zu allen USB-Schnittstellen bereitstellt. Jede Schnittstelle besteht aus einer oder mehreren Schnittstelleneinstellungen, und jede Einstellung besteht aus einem Satz von Endpunkten. Deskriptoren für Schnittstellen, alternative Einstellungen und Endpunkte sind im Variablenteil enthalten.
Ausführliche Beschreibungen zum Gerätelayout finden Sie unter Standard-USB-Deskriptoren.
Vorbereitung
- Sie müssen das Gerät geöffnet und das UsbDevice-Objekt abgerufen haben. Weitere Informationen finden Sie unter Herstellen einer Verbindung mit einem USB-Gerät (UWP-App).
- Den vollständigen Code, der in diesem Thema gezeigt wird, finden Sie im CustomUsbDeviceAccess-Beispiel Scenario5_UsbDescriptors Dateien.
- Rufen Sie Informationen zum Gerätelayout ab. Usbview.exe (im Windows Software Development Kit (SDK) für Windows 8 enthalten) ist eine Anwendung, mit der Sie alle USB-Controller und die mit ihnen verbundenen USB-Geräte durchsuchen können. Für jedes verbundene Gerät können Sie die Geräte-, Konfigurations-, Schnittstellen- und Endpunktdeskriptoren anzeigen, um eine Vorstellung von der Funktion des Geräts zu erhalten.
Abrufen des Gerätedeskriptors
Ihre UWP-App kann den Gerätedeskriptor aus dem zuvor abgerufenen UsbDevice-Objekt abrufen, indem sie den Wert der UsbDevice.DeviceDescriptor-Eigenschaft erhält.
In diesem Codebeispiel wird gezeigt, wie eine Zeichenfolge mit Feldwerten aus dem Gerätedeskriptor aufgefüllt wird.
String GetDeviceDescriptorAsString (UsbDevice device)
{
String content = null;
var deviceDescriptor = device.DeviceDescriptor;
content = "Device Descriptor\n"
+ "\nUsb Spec Number : 0x" + deviceDescriptor.BcdUsb.ToString("X4", NumberFormatInfo.InvariantInfo)
+ "\nMax Packet Size (Endpoint 0) : " + deviceDescriptor.MaxPacketSize0.ToString("D", NumberFormatInfo.InvariantInfo)
+ "\nVendor ID : 0x" + deviceDescriptor.IdVendor.ToString("X4", NumberFormatInfo.InvariantInfo)
+ "\nProduct ID : 0x" + deviceDescriptor.IdProduct.ToString("X4", NumberFormatInfo.InvariantInfo)
+ "\nDevice Revision : 0x" + deviceDescriptor.BcdDeviceRevision.ToString("X4", NumberFormatInfo.InvariantInfo)
+ "\nNumber of Configurations : " + deviceDescriptor.NumberOfConfigurations.ToString("D", NumberFormatInfo.InvariantInfo);
return content;
}
Die Ausgabe ist hier dargestellt:
Abrufen des Konfigurationsdeskriptors
Um den festen Teil des Konfigurationsdeskriptors aus dem zuvor abgerufenen UsbDevice-Objekt abzurufen,
- Rufen Sie das UsbConfiguration-Objekt von UsbDevice ab. UsbConfiguration stellt die erste USB-Konfiguration dar, die vom Gerät definiert wird, und wird standardmäßig auch vom zugrunde liegenden Gerätetreiber ausgewählt.
- Rufen Sie den Wert der UsbConfiguration.ConfigurationDescriptor-Eigenschaft ab.
Der feste Teil des Konfigurationsdeskriptors gibt die Leistungseigenschaften des Geräts an. Sie können beispielsweise bestimmen, ob das Gerät Strom vom Bus oder einer externen Quelle ziehe (siehe UsbConfigurationDescriptor.SelfPowered). Wenn das Gerät Strom vom Bus ziehe, wie viel Strom (in Milliampereeinheiten) es verbraucht (siehe UsbConfigurationDescriptor.MaxPowerMilliamps). Außerdem können Sie bestimmen, ob das Gerät in der Lage ist, sich selbst oder das System aus einem stromarmen Zustand zu wecken, indem Sie den Wert UsbConfigurationDescriptor.RemoteWakeup abrufen.
In diesem Codebeispiel wird gezeigt, wie Sie den festen Teil eines Konfigurationsdeskriptors in einer Zeichenfolge abrufen.
String GetConfigurationDescriptorAsString(UsbDevice device)
{
String content = null;
var usbConfiguration = device.Configuration;
var configurationDescriptor = usbConfiguration.ConfigurationDescriptor;
content = "Configuration Descriptor\n"
+ "\nNumber of Interfaces : " + usbConfiguration.UsbInterfaces.Count.ToString("D", NumberFormatInfo.InvariantInfo)
+ "\nConfiguration Value : 0x" + configurationDescriptor.ConfigurationValue.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nSelf Powered : " + configurationDescriptor.SelfPowered.ToString()
+ "\nRemote Wakeup : " + configurationDescriptor.RemoteWakeup.ToString()
+ "\nMax Power (milliAmps) : " + configurationDescriptor.MaxPowerMilliamps.ToString("D", NumberFormatInfo.InvariantInfo);
return content;
}
Die Ausgabe ist hier dargestellt:
Abrufen von Schnittstellendeskriptoren
Als Nächstes können Sie Informationen zu den USB-Schnittstellen abrufen, die Teil der Konfiguration sind.
Eine USB-Schnittstelle ist eine Sammlung von Schnittstelleneinstellungen. Daher gibt es keinen Deskriptor, der die gesamte Schnittstelle beschreibt. Der Begriff Schnittstellendeskriptor gibt die Datenstruktur an, die eine Einstellung innerhalb einer Schnittstelle beschreibt.
Der Windows.Devices.Usb-Namespace macht Objekte verfügbar, mit denen Sie Informationen zu jeder USB-Schnittstelle und allen Schnittstellendeskriptoren (für alternative Einstellungen) abrufen können, die in dieser Schnittstelle enthalten sind. und Deskriptoren aus dem Teil mit variabler Länge des Konfigurationsdeskriptors.
So rufen Sie die Schnittstellendeskriptoren von UsbConfiguration ab:
- Rufen Sie das Array von Schnittstellen innerhalb der Konfiguration ab, indem Sie die UsbConfiguration.UsbInterfaces-Eigenschaft abrufen.
- Rufen Sie für jede Schnittstelle (UsbInterface) die folgenden Informationen ab:
- Bulk- und Interruptpipes, die aktiv sind und Daten übertragen können.
- Array mit alternativen Einstellungen in der Schnittstelle.
- Array von Schnittstellendeskriptoren.
Dieser Beispielcode ruft alle UsbInterface-Objekte für die Konfiguration ab. Von jedem Objekt ruft die Hilfsmethode die Anzahl der alternativen Einstellungen und geöffneten Bulk- und Schnittstellenpipes ab. Wenn ein Gerät mehrere Schnittstellen unterstützt, können sich Geräteklasse, Unterklasse und Protokollcodes jeder Schnittstelle unterscheiden. Alle Schnittstellendeskriptoren für alternative Einstellungen müssen jedoch dieselben Codes angeben. In diesem Beispiel ruft die Methode die Geräteklasse, Unterklasse und Protokollcodes aus dem Schnittstellendeskriptor der ersten Einstellung ab, um den Code für die gesamte Schnittstelle zu bestimmen.
String GetInterfaceDescriptorsAsString(UsbDevice device)
{
String content = null;
var interfaces = device.Configuration.UsbInterfaces;
content = "Interface Descriptors";
foreach (UsbInterface usbInterface in interfaces)
{
// Class/subclass/protocol values from the first interface setting.
UsbInterfaceDescriptor usbInterfaceDescriptor = usbInterface.InterfaceSettings[0].InterfaceDescriptor;
content +="\n\nInterface Number: 0x" +usbInterface.InterfaceNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nClass Code: 0x" +usbInterfaceDescriptor.ClassCode.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nSubclass Code: 0x" +usbInterfaceDescriptor.SubclassCode.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nProtocol Code: 0x" +usbInterfaceDescriptor.ProtocolCode.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nNumber of Interface Settings: "+usbInterface.InterfaceSettings.Count.ToString("D", NumberFormatInfo.InvariantInfo)
+ "\nNumber of open Bulk In pipes: "+usbInterface.BulkInPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo)
+ "\nNumber of open Bulk Out pipes: "+usbInterface.BulkOutPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo)
+ "\nNumber of open Interrupt In pipes: "+usbInterface.InterruptInPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo)
+ "\nNumber of open Interrupt Out pipes: "+usbInterface.InterruptOutPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo);
}
return content;
}
Die Ausgabe ist hier dargestellt:
Abrufen von Endpunktdeskriptoren
Alle USB-Endpunkte (mit Ausnahme des Standard-Steuerelementendpunkts) müssen Über Endpunktdeskriptoren verfügen. Um die Endpunktdeskriptoren für einen bestimmten Endpunkt abzurufen, müssen Sie wissen, zu welcher Schnittstelle und alternativen Einstellung der Endpunkt gehört.
Rufen Sie das UsbInterface-Objekt ab, das den Endpunkt enthält.
Rufen Sie das Array mit alternativen Einstellungen ab, indem Sie UsbInterface.InterfaceSettings abrufen.
Suchen Sie innerhalb des Arrays nach der Einstellung (UsbInterfaceSetting), die den Endpunkt verwendet.
Suchen Sie in jeder Einstellung nach dem Endpunkt, indem Sie Massen- und Interruptdeskriptorenarrays auflisten.
Endpunktdeskriptoren werden durch die folgenden Objekte dargestellt:
Wenn Ihr Gerät nur über eine Schnittstelle verfügt, können Sie usbDevice.DefaultInterface verwenden, um die Schnittstelle abzurufen, wie in diesem Beispielcode gezeigt. Hier befüllt die Hilfsmethode eine Zeichenfolge mit Endpunktdeskriptoren, die Pipes der aktiven Schnittstelleneinstellung zugeordnet sind.
private String GetEndpointDescriptorsAsString(UsbDevice device)
{
String content = null;
var usbInterface = device.DefaultInterface;
var bulkInPipes = usbInterface.BulkInPipes;
var bulkOutPipes = usbInterface.BulkOutPipes;
var interruptInPipes = usbInterface.InterruptInPipes;
var interruptOutPipes = usbInterface.InterruptOutPipes;
content = "Endpoint Descriptors for open pipes";
// Print Bulk In Endpoint descriptors
foreach (UsbBulkInPipe bulkInPipe in bulkInPipes)
{
var endpointDescriptor = bulkInPipe.EndpointDescriptor;
content +="\n\nBulk In Endpoint Descriptor"
+ "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
}
// Print Bulk Out Endpoint descriptors
foreach (UsbBulkOutPipe bulkOutPipe in bulkOutPipes)
{
var endpointDescriptor = bulkOutPipe.EndpointDescriptor;
content +="\n\nBulk Out Endpoint Descriptor"
+ "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
}
// Print Interrupt In Endpoint descriptors
foreach (UsbInterruptInPipe interruptInPipe in interruptInPipes)
{
var endpointDescriptor = interruptInPipe.EndpointDescriptor;
content +="\n\nInterrupt In Endpoint Descriptor"
+ "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
+ "\nInterval : " + endpointDescriptor.Interval.Duration.ToString();
}
// Print Interrupt Out Endpoint descriptors
foreach (UsbInterruptOutPipe interruptOutPipe in interruptOutPipes)
{
var endpointDescriptor = interruptOutPipe.EndpointDescriptor;
content +="\n\nInterrupt Out Endpoint Descriptor"
+ "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
+ "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
+ "\nInterval : " + endpointDescriptor.Interval.Duration.ToString();
}
return content;
}
Die Ausgabe ist hier dargestellt:
Abrufen benutzerdefinierter Deskriptoren
Beachten Sie, dass die Objekte UsbConfiguration, UsbInterface und UsbInterfaceSetting jeweils eine Eigenschaft mit dem Namen Descriptors verfügbar machen. Dieser Eigenschaftswert ruft das Array von Deskriptoren ab, die von UsbDescriptor-Objekten dargestellt werden. Mit dem UsbDescriptor-Objekt kann die App Deskriptordaten in einem Puffer abrufen. Die Eigenschaften UsbDescriptor.DescriptorType und UsbDescriptor.Length speichern den Typ und die Länge des Puffers, der zum Speichern des Deskriptors erforderlich ist.
Hinweis Die ersten beiden Bytes aller Deskriptorpuffer geben auch den Typ und die Länge des Deskriptors an.
Die UsbConfiguration.Descriptors-Eigenschaft ruft beispielsweise das Array des vollständigen Konfigurationsdeskriptors ab (Teile mit fester und variabler Länge). Das erste Element in diesem Array ist der Konfigurationsdeskriptor mit fester Länge (identisch mit UsbConfigurationDescriptor), das zweite Element ist der Schnittstellendeskriptor der ersten alternativen Einstellung usw.
In ähnlicher Weise ruft die UsbInterface.Descriptors-Eigenschaft das Array aller Schnittstellendeskriptoren und die zugehörigen Endpunktdeskriptoren ab. Die UsbInterfaceSetting.Descriptors-Eigenschaft ruft das Array aller Deskriptoren für diese Einstellung ab, z. B. Endpunktdeskriptoren.
Diese Methode zum Abrufen von Deskriptoren ist nützlich, wenn die App benutzerdefinierte Deskriptoren oder andere Deskriptoren wie Endpunktbegleitungsdeskriptoren für SuperSpeed-Geräte abrufen möchte.
In diesem Codebeispiel wird gezeigt, wie Sie Deskriptordaten in einem Puffer aus dem Konfigurationsdeskriptor abrufen. Im Beispiel wird der Konfigurationsdeskriptorsatz abgerufen und alle in diesem Satz enthaltenen Deskriptoren analysiert. Für jeden Deskriptor wird das DataReader-Objekt verwendet, um den Puffer zu lesen und Länge und Typ des Deskriptors anzuzeigen. Sie können benutzerdefinierte Deskriptoren abrufen, wie in diesem Beispiel gezeigt.
private String GetCustomDescriptorsAsString(UsbDevice device)
{
String content = null;
// Descriptor information will be appended to this string and then printed to UI
content = "Raw Descriptors";
var configuration = device.Configuration;
var allRawDescriptors = configuration.Descriptors;
// Print first 2 bytes of all descriptors within the configuration descriptor
// because the first 2 bytes are always length and descriptor type
// the UsbDescriptor's DescriptorType and Length properties, but we will not use these properties
// in order to demonstrate ReadDescriptorBuffer() and how to parse it.
foreach (UsbDescriptor descriptor in allRawDescriptors)
{
var descriptorBuffer = new Windows.Storage.Streams.Buffer(descriptor.Length);
descriptor.ReadDescriptorBuffer(descriptorBuffer);
DataReader reader = DataReader.FromBuffer(descriptorBuffer);
// USB data is Little Endian according to the USB spec.
reader.ByteOrder = ByteOrder.LittleEndian;
// ReadByte has a side effect where it consumes the current byte, so the next ReadByte will read the next character.
// Putting multiple ReadByte() on the same line (same variable assignment) may cause the bytes to be read out of order.
var length = reader.ReadByte().ToString("D", NumberFormatInfo.InvariantInfo);
var type = "0x" + reader.ReadByte().ToString("X2", NumberFormatInfo.InvariantInfo);
content += "\n\nDescriptor"
+ "\nLength : " + length
+ "\nDescriptorType : " + type;
}
return content;
}