Cómo obtener descriptores USB (aplicación para UWP)
Una de las tareas principales de interactuar con un dispositivo USB es obtener información sobre él. Todos los dispositivos USB proporcionan información en forma de varias estructuras de datos denominadas descriptores. En este artículo se describe cómo una aplicación para UWP puede obtener descriptores del dispositivo en el punto de conexión, la interfaz, la configuración y el nivel de dispositivo. En este artículo también se tratan:
- Descripción del diseño del dispositivo USB
- Obtención de descriptores USB estándar
- Obtención de descriptores personalizados
API importantes
Descriptores USB
Un dispositivo USB describe sus funcionalidades en dos descriptores principales: descriptor de dispositivo y descriptor de configuración.
Un dispositivo USB debe proporcionar un descriptor de dispositivo que contenga información sobre un dispositivo USB en su conjunto. Si el dispositivo no proporciona ese descriptor o proporciona un descriptor con formato incorrecto, Windows no puede cargar el controlador de dispositivo. La información más importante del descriptor es el identificador de hardware del dispositivo para el dispositivo (combinación de campos id . de proveedor e id. de producto ). Se basa en esa información que Windows puede hacer coincidir con un controlador en caja para el dispositivo. Otra información que es clave es el tamaño máximo del paquete del punto de conexión predeterminado (MaxPacketSize0). El punto de conexión predeterminado es el destino de todas las solicitudes de control que el host envía al dispositivo para configurarlo.
La longitud del descriptor de dispositivo es fija.
El dispositivo USB también debe proporcionar un descriptor de configuración completo. La parte inicial de este descriptor tiene una longitud fija de 9 bytes, el resto es una longitud variable en función del número de interfaces y puntos de conexión que admiten esas interfaces. La parte de longitud fija proporciona información sobre una configuración USB: número de interfaces que admite y consumo de energía cuando el dispositivo está en esa configuración. Esos 9 bytes iniciales van seguidos de la parte variable del descriptor que proporciona información sobre todas las interfaces USB. Cada interfaz consta de una o varias configuraciones de interfaz, y cada configuración se compone de un conjunto de puntos de conexión. Los descriptores de las interfaces, la configuración alternativa y los puntos de conexión se incluyen en la parte de la variable.
Para obtener una descripción detallada sobre el diseño del dispositivo, consulte Descriptores USB estándar.
Antes de empezar
- Debe haber abierto el dispositivo y obtenido el objeto UsbDevice . Lea Cómo conectarse a un dispositivo USB (aplicación para UWP).
- Puede ver el código completo que se muestra en este tema en el ejemplo CustomUsbDeviceAccess, Scenario5_UsbDescriptors archivos.
- Obtenga información sobre el diseño del dispositivo. Usbview.exe (incluido en el Kit de desarrollo de software (SDK) de Windows para Windows 8) es una aplicación que permite examinar todos los controladores USB y los dispositivos USB que están conectados a ellos. Para cada dispositivo conectado, puede ver el dispositivo, la configuración, la interfaz y los descriptores de punto de conexión para hacerse una idea de la funcionalidad del dispositivo.
Obtención del descriptor de dispositivo
La aplicación para UWP puede obtener el descriptor de dispositivo del objeto UsbDevice obtenido previamente obteniendo el valor de la propiedad UsbDevice.DeviceDescriptor .
En este ejemplo de código se muestra cómo rellenar una cadena con valores de campo del descriptor de dispositivo.
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;
}
El resultado se muestra aquí:
Obtención del descriptor de configuración
Para obtener la parte fija del descriptor de configuración del objeto UsbDevice obtenido anteriormente,
- Obtenga el objeto UsbConfiguration de UsbDevice. UsbConfiguration representa la primera configuración USB definida por el dispositivo y también está seleccionada de forma predeterminada por el controlador de dispositivo subyacente.
- Obtiene el valor de la propiedad UsbConfiguration.ConfigurationDescriptor .
La parte fija del descriptor de configuración indica las características de energía del dispositivo. Por ejemplo, puede determinar si el dispositivo está dibujando energía desde el bus o desde un origen externo (consulte UsbConfigurationDescriptor.SelfPowered). Si el dispositivo está dibujando potencia del bus, la cantidad de energía (en unidades de miliamp) que consume (consulte UsbConfigurationDescriptor.MaxPowerMilliamps). Además, puedes determinar si el dispositivo es capaz de despertarse o del sistema a partir de un estado de bajo consumo, obteniendo el valor UsbConfigurationDescriptor.RemoteWakeup .
En este ejemplo de código se muestra cómo obtener la parte fija de un descriptor de configuración en una cadena.
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;
}
El resultado se muestra aquí:
Obtención de descriptores de interfaz
A continuación, puede obtener información sobre las interfaces USB que forman parte de la configuración.
Una interfaz USB es una colección de configuraciones de interfaz. Por lo tanto, no hay ningún descriptor que describa toda la interfaz. El término descriptor de interfaz indica la estructura de datos que describe una configuración dentro de una interfaz.
El espacio de nombres Windows.Devices.Usb expone objetos que puedes usar para obtener información sobre cada interfaz USB y todos los descriptores de interfaces (para configuraciones alternativas) incluidas en esa interfaz. y descriptores de la parte de longitud variable del descriptor de configuración.
Para obtener los descriptores de interfaz de UsbConfiguration,
- Obtenga la matriz de interfaces dentro de la configuración obteniendo la propiedad UsbConfiguration.UsbInterfaces .
- Para cada interfaz (UsbInterface), obtenga esta información:
- Canalizaciones masivas e interrumpidas que están activas y pueden transferir datos.
- Matriz de configuraciones alternativas en la interfaz.
- Matriz de descriptores de interfaz.
Este código de ejemplo obtiene todos los objetos UsbInterface para la configuración. A partir de cada objeto, el método auxiliar obtiene el número de opciones alternativas y abre canalizaciones masivas e de interfaz. Si un dispositivo admite varias interfaces, la clase de dispositivo, la subclase y los códigos de protocolo de cada interfaz pueden diferir. Sin embargo, todos los descriptores de interfaz para la configuración alternativa deben especificar los mismos códigos. En este ejemplo, el método obtiene la clase de dispositivo, la subclase y los códigos de protocolo del descriptor de interfaz de la primera configuración para determinar el código de toda la interfaz.
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;
}
El resultado se muestra aquí:
Obtención de descriptores de punto de conexión
Todos los puntos de conexión USB (excepto el punto de conexión de control predeterminado) deben tener descriptores de punto de conexión. Para obtener los descriptores de punto de conexión para un punto de conexión determinado, debe saber qué interfaz y configuración alternativa a la que pertenece el punto de conexión.
Obtiene el objeto UsbInterface que contiene el punto de conexión.
Obtenga la matriz de configuraciones alternativas obteniendo UsbInterface.InterfaceSettings.
Dentro de la matriz, busque la configuración (UsbInterfaceSetting) que usa el punto de conexión.
Dentro de cada configuración, busque el punto de conexión mediante la enumeración de matrices de descriptores de interrupción y masiva.
Los descriptores de punto de conexión se representan mediante estos objetos:
Si el dispositivo solo tiene una interfaz, puede usar UsbDevice.DefaultInterface para obtener la interfaz, como se muestra en este código de ejemplo. Aquí, el método auxiliar rellena una cadena con descriptores de punto de conexión asociados a canalizaciones de la configuración de interfaz activa.
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;
}
El resultado se muestra aquí:
Obtención de descriptores personalizados
Observe que los objetos UsbConfiguration, UsbInterface y UsbInterfaceSetting exponen una propiedad denominada Descriptores. Ese valor de propiedad recupera la matriz de descriptores representados por objetos UsbDescriptor . El objeto UsbDescriptor permite a la aplicación obtener datos del descriptor en un búfer. Las propiedades UsbDescriptor.DescriptorType y UsbDescriptor.Length almacenan el tipo y la longitud del búfer necesario para contener el descriptor.
Nota Los dos primeros bytes de todos los búferes de descriptores también indican el tipo y la longitud del descriptor.
Por ejemplo, la propiedad UsbConfiguration.Descriptors obtiene la matriz del descriptor de configuración completo (partes de longitud fija y variable). El primer elemento de esa matriz es el descriptor de configuración de longitud fija (igual que UsbConfigurationDescriptor), el segundo elemento es el descriptor de interfaz de la primera configuración alternativa, etc.
Del mismo modo, la propiedad UsbInterface.Descriptors obtiene la matriz de todos los descriptores de interfaz y los descriptores de punto de conexión relacionados. La propiedad UsbInterfaceSetting.Descriptors obtiene la matriz de todos los descriptores para esa configuración, como descriptores de punto de conexión.
Esta manera de obtener descriptores es útil cuando la aplicación quiere recuperar descriptores personalizados u otros descriptores, como descriptores complementarios de punto de conexión para dispositivos SuperSpeed.
En este ejemplo de código se muestra cómo obtener datos de descriptor en un búfer desde el descriptor de configuración. En el ejemplo se obtiene el conjunto de descriptores de configuración y se analizan todos los descriptores contenidos en ese conjunto. Para cada descriptor, usa el objeto DataReader para leer el búfer y mostrar la longitud y el tipo del descriptor. Puede obtener descriptores personalizados como se muestra en este ejemplo.
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;
}