Como obter descritores USB (aplicativo UWP)

Uma das tarefas main de interagir com um dispositivo USB é obter informações sobre ele. Todos os dispositivos USB fornecem informações na forma de várias estruturas de dados chamadas descritores. Este artigo descreve como um aplicativo UWP pode obter descritores do dispositivo no ponto de extremidade, interface, configuração e nível do dispositivo. Este artigo também aborda:

  • Noções básicas sobre o layout do dispositivo USB
  • Obtendo descritores USB padrão
  • Obtendo descritores personalizados

APIs importantes

Descritores USB

Um dispositivo USB descreve seus recursos em dois descritores main: descritor de dispositivo e descritor de configuração.

Um dispositivo USB deve fornecer um descritor de dispositivo que contenha informações sobre um dispositivo USB como um todo. Se o dispositivo não fornecer esse descritor ou fornecer um descritor malformado, o Windows não poderá carregar o driver do dispositivo. As informações mais importantes no descritor são a ID de hardware do dispositivo (combinação de ID do fornecedor e campos de ID do produto ). É baseado nessas informações que o Windows é capaz de corresponder a um driver in-box para o dispositivo. Outra informação que é a chave é o tamanho máximo do pacote do ponto de extremidade padrão (MaxPacketSize0). O ponto de extremidade padrão é o destino de todas as solicitações de controle que o host envia ao dispositivo para configurá-lo.

O comprimento do descritor do dispositivo é fixo.

O dispositivo USB também deve fornecer um descritor de configuração completo. A parte inicial desse descritor tem comprimento fixo de 9 bytes, rest é comprimento variável dependendo do número de interfaces e pontos de extremidade compatíveis com essas interfaces. A parte de comprimento fixo fornece informações sobre uma configuração USB: número de interfaces compatíveis e consumo de energia quando o dispositivo está nessa configuração. Esses 9 bytes iniciais são seguidos pela parte variável do descritor que fornece informações sobre todas as interfaces USB. Cada interface consiste em uma ou mais configurações de interface e cada configuração é composta por um conjunto de pontos de extremidade. Descritores para interfaces, configurações alternativas e pontos de extremidade são incluídos na parte variável.

Para obter uma descrição detalhada sobre o layout do dispositivo, consulte Descritores USB padrão.

Antes de começar

  • Você deve ter aberto o dispositivo e obtido o objeto UsbDevice . Leia Como se conectar a um dispositivo USB (aplicativo UWP).
  • Você pode ver o código completo mostrado neste tópico no exemplo CustomUsbDeviceAccess, Scenario5_UsbDescriptors arquivos.
  • Obtenha informações sobre o layout do dispositivo. Usbview.exe (incluído no SDK (Software Development Kit) do Windows para Windows 8) é um aplicativo que permite procurar todos os controladores USB e os dispositivos USB conectados a eles. Para cada dispositivo conectado, você pode exibir o dispositivo, a configuração, a interface e os descritores de ponto de extremidade para ter uma ideia sobre a funcionalidade do dispositivo.

Como obter o descritor do dispositivo

Seu aplicativo UWP pode obter o descritor do dispositivo do objeto UsbDevice obtido anteriormente obtendo o valor da propriedade UsbDevice.DeviceDescriptor .

Este exemplo de código mostra como preencher uma cadeia de caracteres com valores de campo do descritor 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;
}

A saída é mostrada aqui:

descritor de dispositivo usb.

Como obter o descritor de configuração

Para obter a parte fixa do descritor de configuração do objeto UsbDevice obtido anteriormente,

  1. Obtenha o objeto UsbConfiguration de UsbDevice. UsbConfiguration representa a primeira configuração USB definida pelo dispositivo e também é selecionada por padrão pelo driver de dispositivo subjacente.
  2. Obtenha o valor da propriedade UsbConfiguration.ConfigurationDescriptor .

A parte fixa do descritor de configuração indica as características de energia do dispositivo. Por exemplo, você pode determinar se o dispositivo está extraindo energia do barramento ou de uma fonte externa (consulte UsbConfigurationDescriptor.SelfPowered). Se o dispositivo estiver extraindo energia do barramento, quanta energia (em unidades miliâmpias) ele está consumindo (consulte UsbConfigurationDescriptor.MaxPowerMilliamps). Além disso, você pode determinar se o dispositivo é capaz de acordar a si mesmo ou o sistema de um estado de baixa potência, obtendo o valor UsbConfigurationDescriptor.RemoteWakeup .

Este exemplo de código mostra como obter a parte fixa de um descritor de configuração em uma cadeia de caracteres.

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

A saída é mostrada aqui:

descritor de configuração usb.

Como obter descritores de interface

Em seguida, você pode obter informações sobre as interfaces USB que fazem parte da configuração.

Uma interface USB é uma coleção de configurações de interface. Como tal, não há nenhum descritor que descreva toda a interface. O descritor de interface de termo indica a estrutura de dados que descreve uma configuração dentro de uma interface.

O namespace Windows.Devices.Usb expõe objetos que você pode usar para obter informações sobre cada interface USB e todos os descritores de interfaces (para configurações alternativas) incluídos nessa interface. e descritores da parte de comprimento variável do descritor de configuração.

Para obter os descritores de interface do UsbConfiguration,

  1. Obtenha a matriz de interfaces dentro da configuração obtendo a propriedade UsbConfiguration.UsbInterfaces .
  2. Para cada interface (UsbInterface), obtenha estas informações:
    • Pipes em massa e de interrupção que estão ativos e podem transferir dados.
    • Matriz de configurações alternativas na interface.
    • Matriz de descritores de interface.

Este código de exemplo obtém todos os objetos UsbInterface para a configuração. De cada objeto, o método auxiliar obtém o número de configurações alternativas e abre pipes de massa e interface. Se um dispositivo der suporte a várias interfaces, a classe de dispositivo, a subclasse e os códigos de protocolo de cada interface poderão ser diferentes. No entanto, todos os descritores de interface para configurações alternativas devem especificar os mesmos códigos. Neste exemplo, o método obtém a classe de dispositivo, a subclasse e os códigos de protocolo do descritor de interface da primeira configuração para determinar o código de toda a interface.

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

A saída é mostrada aqui:

descritor de interface usb.

Como obter descritores de ponto de extremidade

Todos os pontos de extremidade USB (exceto o ponto de extremidade de controle padrão) devem ter descritores de ponto de extremidade. Para obter os descritores de ponto de extremidade para um ponto de extremidade específico, você deve saber qual interface e configuração alternativa ao qual o ponto de extremidade pertence.

  1. Obtenha o objeto UsbInterface que contém o ponto de extremidade.

  2. Obtenha a matriz de configurações alternativas obtendo UsbInterface.InterfaceSettings.

  3. Dentro da matriz, localize a configuração (UsbInterfaceSetting) que usa o ponto de extremidade.

  4. Em cada configuração, localize o ponto de extremidade enumerando matrizes de descritores em massa e interrompendo.

    Os descritores de ponto de extremidade são representados por estes objetos:

Se o dispositivo tiver apenas uma interface, você poderá usar o UsbDevice.DefaultInterface para obter a interface, conforme mostrado neste código de exemplo. Aqui, o método auxiliar obtém preencher uma cadeia de caracteres com descritores de ponto de extremidade associados a pipes da configuração de interface ativa.

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

A saída é mostrada aqui:

descritores de ponto de extremidade usb.

Como obter descritores personalizados

Observe que os objetos UsbConfiguration, UsbInterface e UsbInterfaceSetting expõem uma propriedade chamada Descritores. Esse valor de propriedade recupera a matriz de descritores representada por objetos UsbDescriptor . O objeto UsbDescriptor permite que o aplicativo obtenha dados do descritor em um buffer. As propriedades UsbDescriptor.DescriptorType e UsbDescriptor.Length armazenam o tipo e o comprimento do buffer necessários para manter o descritor.

Nota Os dois primeiros bytes de todos os buffers de descritor também indicam o tipo e o comprimento do descritor.

Por exemplo, a propriedade UsbConfiguration.Descriptors obtém a matriz do descritor de configuração completo (porções de comprimento fixo e variável). O primeiro elemento nessa matriz é o descritor de configuração de comprimento fixo (o mesmo que UsbConfigurationDescriptor), o segundo elemento é o descritor de interface da primeira configuração alternativa e assim por diante.

Da mesma forma, a propriedade UsbInterface.Descriptors obtém a matriz de todos os descritores de interface e os descritores de ponto de extremidade relacionados. A propriedade UsbInterfaceSetting.Descriptors obtém a matriz de todos os descritores para essa configuração, como descritores de ponto de extremidade.

Essa maneira de obter descritores é útil quando o aplicativo deseja recuperar descritores personalizados ou outros descritores, como descritores complementares de ponto de extremidade para dispositivos SuperSpeed.

Este exemplo de código mostra como obter dados de descritor em um buffer do descritor de configuração. O exemplo obtém o conjunto de descritores de configuração e analisa todos os descritores contidos nesse conjunto. Para cada descritor, ele usa o objeto DataReader para ler o buffer e mostrar o comprimento e o tipo do descritor. Você pode obter descritores personalizados, conforme mostrado neste exemplo.

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