Come ottenere descrittori USB (app UWP)

Una delle attività principali di interagire con un dispositivo USB consiste nel ottenere informazioni su di esso. Tutti i dispositivi USB forniscono informazioni sotto forma di diverse strutture di dati denominate descrittori. Questo articolo descrive come un'app UWP può ottenere descrittori dal dispositivo a livello di endpoint, interfaccia, configurazione e dispositivo. Questo articolo illustra anche:

  • Informazioni sul layout dei dispositivi USB
  • Recupero di descrittori USB standard
  • Recupero di descrittori personalizzati

API importanti

Descrittori USB

Un dispositivo USB descrive le sue funzionalità in due descrittori principali: descrittore di dispositivo e descrittore di configurazione.

Un dispositivo USB deve fornire un descrittore di dispositivo contenente informazioni su un dispositivo USB nel suo complesso. Se il dispositivo non fornisce tale descrittore o fornisce un descrittore in formato non valido, Windows non è in grado di caricare il driver di dispositivo. Le informazioni più importanti nel descrittore sono l'ID hardware del dispositivo per il dispositivo (combinazione di campi ID fornitore e ID prodotto ). Si basa su tali informazioni che Windows è in grado di trovare una corrispondenza con un driver in-box per il dispositivo. Un'altra informazione chiave è la dimensione massima del pacchetto dell'endpoint predefinito (MaxPacketSize0). L'endpoint predefinito è la destinazione di tutte le richieste di controllo inviate dall'host al dispositivo per configurarla.

La lunghezza del descrittore del dispositivo è fissa.

Il dispositivo USB deve anche fornire un descrittore di configurazione completo. La parte iniziale di questo descrittore ha una lunghezza fissa di 9 byte, rest è di lunghezza variabile a seconda del numero di interfacce ed endpoint supportati da tali interfacce. La parte a lunghezza fissa fornisce informazioni su una configurazione USB: numero di interfacce supportate e consumo di energia quando il dispositivo si trova in tale configurazione. Questi 9 byte iniziali sono seguiti dalla parte variabile del descrittore che fornisce informazioni su tutte le interfacce USB. Ogni interfaccia è costituita da una o più impostazioni di interfaccia e ogni impostazione è costituita da un set di endpoint. I descrittori per le interfacce, le impostazioni alternative e gli endpoint sono inclusi nella parte variabile.

Per una descrizione dettagliata sul layout del dispositivo, vedi Descrittori USB standard.

Prima di iniziare

  • È necessario aver aperto il dispositivo e ottenuto l'oggetto UsbDevice . Leggi Come connettersi a un dispositivo USB (app UWP).
  • È possibile visualizzare il codice completo illustrato in questo argomento nell'esempio CustomUsbDeviceAccess, Scenario5_UsbDescriptors file.
  • Ottenere informazioni sul layout del dispositivo. Usbview.exe (incluso in Windows Software Development Kit (SDK) per Windows 8) è un'applicazione che consente di esplorare tutti i controller USB e i dispositivi USB connessi. Per ogni dispositivo connesso, è possibile visualizzare i descrittori di dispositivo, configurazione, interfaccia ed endpoint per avere un'idea della funzionalità del dispositivo.

Come ottenere il descrittore del dispositivo

L'app UWP può ottenere il descrittore del dispositivo dall'oggetto UsbDevice ottenuto in precedenza ottenendo il valore della proprietà UsbDevice.DeviceDescriptor .

In questo esempio di codice viene illustrato come popolare una stringa con i valori di campo del descrittore di 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;
}

L'output è mostrato qui:

descrittore del dispositivo USB.

Come ottenere il descrittore di configurazione

Per ottenere la parte fissa del descrittore di configurazione dall'oggetto UsbDevice ottenuto in precedenza,

  1. Ottenere l'oggetto UsbConfiguration da UsbDevice. UsbConfiguration rappresenta la prima configurazione USB definita dal dispositivo ed è selezionata anche per impostazione predefinita dal driver di dispositivo sottostante.
  2. Ottenere il valore della proprietà UsbConfiguration.ConfigurationDescriptor .

La parte fissa del descrittore di configurazione indica le caratteristiche di alimentazione del dispositivo. Ad esempio, è possibile determinare se il dispositivo sta disegnando potenza dal bus o da un'origine esterna (vedere UsbConfigurationDescriptor.SelfPowered). Se il dispositivo sta disegnando potenza dal bus, la potenza (in unità milliamp) sta consumando (vedere UsbConfigurationDescriptor.MaxPowerMilliamps). Inoltre, è possibile determinare se il dispositivo è in grado di riattivarsi o il sistema da uno stato a basso consumo, ottenendo il valore UsbConfigurationDescriptor.RemoteWakeup .

Questo esempio di codice illustra come ottenere la parte fissa di un descrittore di configurazione in una stringa.

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;
}

L'output è mostrato qui:

descrittore di configurazione usb.

Come ottenere descrittori di interfaccia

Successivamente, è possibile ottenere informazioni sulle interfacce USB che fanno parte della configurazione.

Un'interfaccia USB è una raccolta di impostazioni dell'interfaccia. Di conseguenza, non esiste alcun descrittore che descrive l'intera interfaccia. Il termine descrittore di interfaccia indica la struttura dei dati che descrive un'impostazione all'interno di un'interfaccia.

Lo spazio dei nomi Windows.Devices.Usb espone oggetti che è possibile usare per ottenere informazioni su ogni interfaccia USB e su tutti i descrittori di interfacce (per le impostazioni alternative) inclusi in tale interfaccia. descrittori e dalla parte a lunghezza variabile del descrittore di configurazione.

Per ottenere i descrittori di interfaccia da UsbConfiguration,

  1. Ottenere la matrice di interfacce all'interno della configurazione ottenendo la proprietà UsbConfiguration.UsbInterfaces .
  2. Per ogni interfaccia (UsbInterface), ottenere queste informazioni:
    • Pipe bulk e interrupt attive e in grado di trasferire i dati.
    • Matrice di impostazioni alternative nell'interfaccia.
    • Matrice di descrittori di interfaccia.

Questo codice di esempio ottiene tutti gli oggetti UsbInterface per la configurazione. Da ogni oggetto, il metodo helper ottiene il numero di impostazioni alternative e le pipe bulk e interface aperte. Se un dispositivo supporta più interfacce, classe di dispositivo, sottoclasse e codici di protocollo di ogni interfaccia può differire. Tuttavia, tutti i descrittori di interfaccia per le impostazioni alternative devono specificare gli stessi codici. In questo esempio, il metodo ottiene la classe del dispositivo, la sottoclasse e i codici di protocollo dal descrittore di interfaccia della prima impostazione per determinare il codice per l'intera interfaccia.

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;
}

L'output è mostrato qui:

Descrittore dell'interfaccia USB.

Come ottenere i descrittori di endpoint

Tutti gli endpoint USB (ad eccezione dell'endpoint di controllo predefinito) devono avere descrittori di endpoint. Per ottenere i descrittori di endpoint per un determinato endpoint, è necessario conoscere l'interfaccia e l'impostazione alternativa a cui appartiene l'endpoint.

  1. Ottiene l'oggetto UsbInterface che contiene l'endpoint.

  2. Ottenere la matrice di impostazioni alternative ottenendo UsbInterface.InterfaceSettings.

  3. All'interno della matrice trovare l'impostazione (UsbInterfaceSetting) che usa l'endpoint.

  4. All'interno di ogni impostazione, trovare l'endpoint enumerando matrici di descrittori bulk e interrupt.

    I descrittori di endpoint sono rappresentati da questi oggetti:

Se il dispositivo ha una sola interfaccia, è possibile usare UsbDevice.DefaultInterface per ottenere l'interfaccia come illustrato in questo codice di esempio. In questo caso, il metodo helper popola una stringa con descrittori di endpoint associati alle pipe dell'impostazione dell'interfaccia attiva.

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;
}

L'output è mostrato qui:

descrittori di endpoint usb.

Come ottenere descrittori personalizzati

Si noti che gli oggetti UsbConfiguration, UsbInterface e UsbInterfaceSetting espongono una proprietà denominata Descriptors. Tale valore della proprietà recupera la matrice di descrittori rappresentati da oggetti UsbDescriptor . L'oggetto UsbDescriptor consente all'app di ottenere i dati del descrittore in un buffer. Le proprietà UsbDescriptor.DescriptorType e UsbDescriptor.Length archiviano il tipo e la lunghezza del buffer necessario per contenere il descrittore.

Nota I primi due byte di tutti i buffer del descrittore indicano anche il tipo e la lunghezza del descrittore.

Ad esempio, la proprietà UsbConfiguration.Descriptors ottiene la matrice del descrittore di configurazione completo (parti a lunghezza fissa e variabile). Il primo elemento in tale matrice è il descrittore di configurazione a lunghezza fissa (uguale a UsbConfigurationDescriptor), il secondo elemento è il descrittore di interfaccia della prima impostazione alternativa e così via.

Analogamente, la proprietà UsbInterface.Descriptors ottiene la matrice di tutti i descrittori di interfaccia e i descrittori di endpoint correlati. La proprietà UsbInterfaceSetting.Descriptors ottiene la matrice di tutti i descrittori per tale impostazione, ad esempio i descrittori di endpoint.

Questo modo di ottenere descrittori è utile quando l'app vuole recuperare descrittori personalizzati o altri descrittori come descrittori complementari dell'endpoint per i dispositivi SuperSpeed.

Questo esempio di codice illustra come ottenere dati descrittori in un buffer dal descrittore di configurazione. L'esempio ottiene il descrittore di configurazione e analizza tutti i descrittori contenuti in tale set. Per ogni descrittore, usa l'oggetto DataReader per leggere il buffer e visualizzare la lunghezza e il tipo del descrittore. È possibile ottenere descrittori personalizzati come illustrato in questo esempio.

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;
}