Comment obtenir des descripteurs USB (application UWP)

L’une des main tâches d’interaction avec un périphérique USB consiste à obtenir des informations à ce sujet. Tous les périphériques USB fournissent des informations sous la forme de plusieurs structures de données appelées descripteurs. Cet article explique comment une application UWP peut obtenir des descripteurs à partir de l’appareil au niveau du point de terminaison, de l’interface, de la configuration et de l’appareil. Cet article traite également des sujets suivants :

  • Présentation de la disposition des périphériques USB
  • Obtention de descripteurs USB standard
  • Obtention de descripteurs personnalisés

API importantes

Descripteurs USB

Un périphérique USB décrit ses fonctionnalités dans deux descripteurs main : le descripteur d’appareil et le descripteur de configuration.

Un périphérique USB doit fournir un descripteur d’appareil qui contient des informations sur un périphérique USB dans son ensemble. Si l’appareil ne fournit pas ce descripteur ou fournit un descripteur mal formé, Windows ne peut pas charger le pilote de périphérique. Les informations les plus importantes dans le descripteur sont l’ID matériel de l’appareil pour l’appareil (combinaison des champs ID fournisseur et ID de produit ). En fonction de ces informations, Windows est en mesure de faire correspondre un pilote intégré à l’appareil. Une autre information clé est la taille de paquet maximale du point de terminaison par défaut (MaxPacketSize0). Le point de terminaison par défaut est la cible de toutes les demandes de contrôle que l’hôte envoie à l’appareil pour le configurer.

La longueur du descripteur d’appareil est fixe.

Le périphérique USB doit également fournir un descripteur de configuration complet. La partie de début de ce descripteur a une longueur fixe de 9 octets, le repos est de longueur variable en fonction du nombre d’interfaces et de points de terminaison pris en charge par ces interfaces. La partie de longueur fixe fournit des informations sur une configuration USB : nombre d’interfaces qu’elle prend en charge et consommation d’énergie lorsque l’appareil se trouve dans cette configuration. Ces 9 octets initiaux sont suivis de la partie variable du descripteur qui fournit des informations sur toutes les interfaces USB. Chaque interface se compose d’un ou plusieurs paramètres d’interface, et chaque paramètre est constitué d’un ensemble de points de terminaison. Les descripteurs pour les interfaces, les autres paramètres et les points de terminaison sont inclus dans la partie variable.

Pour obtenir une description détaillée de la disposition des appareils, consultez Descripteurs USB standard.

Avant de commencer

  • Vous devez avoir ouvert l’appareil et obtenu l’objet UsbDevice . Consultez Comment se connecter à un périphérique USB (application UWP).
  • Vous pouvez voir le code complet présenté dans cette rubrique dans l’exemple CustomUsbDeviceAccess, Scenario5_UsbDescriptors fichiers.
  • Obtenez des informations sur la disposition de l’appareil. Usbview.exe (inclus dans le Kit de développement logiciel (SDK) Windows pour Windows 8) est une application qui vous permet de parcourir tous les contrôleurs USB et les périphériques USB qui y sont connectés. Pour chaque appareil connecté, vous pouvez afficher les descripteurs d’appareil, de configuration, d’interface et de point de terminaison pour avoir une idée de la capacité de l’appareil.

Comment obtenir le descripteur d’appareil

Votre application UWP peut obtenir le descripteur d’appareil à partir de l’objet UsbDevice obtenu précédemment en obtenant la valeur de la propriété UsbDevice.DeviceDescriptor .

Cet exemple de code montre comment remplir une chaîne avec des valeurs de champ à partir du descripteur d’appareil.

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

La sortie est illustrée ici :

descripteur de périphérique usb.

Comment obtenir le descripteur de configuration

Pour obtenir la partie fixe du descripteur de configuration à partir de l’objet UsbDevice obtenu précédemment,

  1. Récupérez l’objet UsbConfiguration à partir d’UsbDevice. UsbConfiguration représente la première configuration USB définie par l’appareil et est également sélectionnée par défaut par le pilote de périphérique sous-jacent.
  2. Obtenez la valeur de la propriété UsbConfiguration.ConfigurationDescriptor .

La partie fixe du descripteur de configuration indique les caractéristiques d’alimentation de l’appareil. Par exemple, vous pouvez déterminer si l’appareil tire de l’alimentation à partir du bus ou d’une source externe (consultez UsbConfigurationDescriptor.SelfPowered). Si l’appareil tire de l’alimentation du bus, la quantité d’énergie (en unités de milliamp) qu’il consomme (voir UsbConfigurationDescriptor.MaxPowerMilliamps). En outre, vous pouvez déterminer si l’appareil est capable de se réveiller lui-même ou le système à partir d’un état d’alimentation faible, en obtenant la valeur UsbConfigurationDescriptor.RemoteWakeup .

Cet exemple de code montre comment obtenir la partie fixe d’un descripteur de configuration dans une chaîne.

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

La sortie est illustrée ici :

Descripteur de configuration usb.

Comment obtenir des descripteurs d’interface

Ensuite, vous pouvez obtenir des informations sur les interfaces USB qui font partie de la configuration.

Une interface USB est une collection de paramètres d’interface. En tant que tel, il n’existe aucun descripteur qui décrit l’ensemble de l’interface. Le descripteur d’interface indique la structure de données qui décrit un paramètre au sein d’une interface.

L’espace de noms Windows.Devices.Usb expose des objets que vous pouvez utiliser pour obtenir des informations sur chaque interface USB et sur tous les descripteurs d’interfaces (pour les autres paramètres) inclus dans cette interface. et des descripteurs de la partie de longueur variable du descripteur de configuration.

Pour obtenir les descripteurs d’interface à partir d’UsbConfiguration,

  1. Obtenez le tableau d’interfaces au sein de la configuration en obtenant la propriété UsbConfiguration.UsbInterfaces .
  2. Pour chaque interface (UsbInterface), obtenez les informations suivantes :
    • Canaux en bloc et d’interruption qui sont actifs et qui peuvent transférer des données.
    • Tableau d’autres paramètres dans l’interface.
    • Tableau de descripteurs d’interface.

Cet exemple de code obtient tous les objets UsbInterface pour la configuration. À partir de chaque objet, la méthode d’assistance obtient le nombre de paramètres de remplacement et les canaux d’interface et en bloc ouverts. Si un appareil prend en charge plusieurs interfaces, les codes de classe d’appareil, de sous-classe et de protocole de chaque interface peuvent différer. Toutefois, tous les descripteurs d’interface pour les autres paramètres doivent spécifier les mêmes codes. Dans cet exemple, la méthode obtient la classe d’appareil, la sous-classe et les codes de protocole à partir du descripteur d’interface du premier paramètre pour déterminer le code de l’interface entière.

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

La sortie est illustrée ici :

Descripteur d’interface usb.

Comment obtenir des descripteurs de point de terminaison

Tous les points de terminaison USB (à l’exception du point de terminaison de contrôle par défaut) doivent avoir des descripteurs de point de terminaison. Pour obtenir les descripteurs de point de terminaison pour un point de terminaison particulier, vous devez savoir à quelle interface et à quel autre paramètre appartient le point de terminaison.

  1. Obtenez l’objet UsbInterface qui contient le point de terminaison.

  2. Obtenez le tableau d’autres paramètres en obtenant UsbInterface.InterfaceSettings.

  3. Dans le tableau, recherchez le paramètre (UsbInterfaceSetting) qui utilise le point de terminaison.

  4. Dans chaque paramètre, recherchez le point de terminaison en énumérant les tableaux de descripteurs en bloc et d’interruption.

    Les descripteurs de point de terminaison sont représentés par ces objets :

Si votre appareil n’a qu’une seule interface, vous pouvez utiliser UsbDevice.DefaultInterface pour obtenir l’interface comme indiqué dans cet exemple de code. Ici, la méthode d’assistance obtient remplit une chaîne avec des descripteurs de point de terminaison associés aux canaux du paramètre d’interface active.

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

La sortie est illustrée ici :

descripteurs de point de terminaison usb.

Comment obtenir des descripteurs personnalisés

Notez que les objets UsbConfiguration, UsbInterface et UsbInterfaceSetting exposent chacun une propriété nommée Descriptors. Cette valeur de propriété récupère le tableau de descripteurs représentés par les objets UsbDescriptor . L’objet UsbDescriptor permet à l’application d’obtenir des données de descripteur dans une mémoire tampon. Les propriétés UsbDescriptor.DescriptorType et UsbDescriptor.Length stockent le type et la longueur de la mémoire tampon requise pour contenir le descripteur.

Note Les deux premiers octets de tous les tampons de descripteur indiquent également le type et la longueur du descripteur.

Par exemple, la propriété UsbConfiguration.Descriptors obtient le tableau de descripteur de configuration complet (parties de longueur fixe et variable). Le premier élément de ce tableau est le descripteur de configuration de longueur fixe (identique à UsbConfigurationDescriptor), le deuxième élément est le descripteur d’interface du premier autre paramètre, et ainsi de suite.

De même, la propriété UsbInterface.Descriptors obtient le tableau de tous les descripteurs d’interface et les descripteurs de point de terminaison associés. La propriété UsbInterfaceSetting.Descriptors obtient le tableau de tous les descripteurs pour ce paramètre, tels que les descripteurs de point de terminaison.

Cette façon d’obtenir des descripteurs est utile lorsque l’application souhaite récupérer des descripteurs personnalisés ou d’autres descripteurs tels que des descripteurs d’accompagnement de point de terminaison pour les appareils SuperSpeed.

Cet exemple de code montre comment obtenir des données de descripteur dans une mémoire tampon à partir du descripteur de configuration. L’exemple obtient le jeu de descripteur de configuration et analyse tous les descripteurs contenus dans ce jeu. Pour chaque descripteur, il utilise l’objet DataReader pour lire la mémoire tampon et afficher la longueur et le type du descripteur. Vous pouvez obtenir des descripteurs personnalisés, comme illustré dans cet exemple.

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