Поделиться через


Получение дескрипторов USB (приложение UWP)

Одной из main задач взаимодействия с USB-устройством является получение сведений о нем. Все USB-устройства предоставляют информацию в виде нескольких структур данных, называемых дескрипторами. В этой статье описывается, как приложение UWP может получать дескрипторы с устройства на уровне конечной точки, интерфейса, конфигурации и устройства. В этой статье также рассматриваются:

  • Основные сведения о макете USB-устройства
  • Получение стандартных дескрипторов USB
  • Получение пользовательских дескрипторов

Важные API

Дескрипторы USB

Usb-устройство описывает свои возможности в двух main дескрипторов: дескриптор устройства и дескриптор конфигурации.

USB-устройство должно предоставлять дескриптор устройства , содержащий сведения о USB-устройстве в целом. Если устройство не предоставляет этот дескриптор или дескриптор неправильного формата, Windows не сможет загрузить драйвер устройства. Наиболее важные сведения в дескрипторове — это идентификатор оборудования устройства для устройства (сочетание полей идентификатора поставщика и идентификатора продукта ). Он основан на этой информации, что Windows может сопоставить встроенный драйвер для устройства. Другим ключом является максимальный размер пакета конечной точки по умолчанию (MaxPacketSize0). Конечная точка по умолчанию является целевым объектом всех запросов управления, которые узел отправляет устройству для его настройки.

Длина дескриптора устройства фиксирована.

USB-устройство также должно предоставлять полный дескриптор конфигурации. Начальная часть этого дескриптора имеет фиксированную длину 9 байт, а остальные — переменную длину в зависимости от количества интерфейсов и конечных точек, которые поддерживают эти интерфейсы. Часть фиксированной длины содержит сведения о конфигурации USB: количество поддерживаемых интерфейсов и энергопотребление, когда устройство находится в этой конфигурации. За этими начальными 9 байтами следует переменная часть дескриптора, которая предоставляет сведения обо всех ИНТЕРФЕЙСАх USB. Каждый интерфейс состоит из одного или нескольких параметров интерфейса, и каждый параметр состоит из набора конечных точек. Дескрипторы для интерфейсов, альтернативных параметров и конечных точек включены в переменную.

Подробное описание макета устройства см. в разделе Стандартные дескрипторы USB.

Перед началом работы

  • Необходимо открыть устройство и получить объект UsbDevice . См. статью Подключение к USB-устройству (приложению UWP).
  • Полный код, показанный в этом разделе, приведен в примере CustomUsbDeviceAccess, Scenario5_UsbDescriptors файлы.
  • Получение сведений о макете устройства. Usbview.exe (входит в комплект средств разработки программного обеспечения Windows (SDK) для Windows 8) — это приложение, которое позволяет просматривать все USB-контроллеры и ПОДКЛЮЧЕННЫе к ним USB-устройства. Для каждого подключенного устройства можно просмотреть дескрипторы устройства, конфигурации, интерфейса и конечной точки, чтобы получить представление о возможностях устройства.

Получение дескриптора устройства

Приложение UWP может получить дескриптор устройства из ранее полученного объекта UsbDevice , получив значение свойства UsbDevice.DeviceDescriptor .

В этом примере кода показано, как заполнить строку значениями полей из дескриптора устройства.

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

Выходные данные приведены здесь:

дескриптор usb-устройства.

Получение дескриптора конфигурации

Чтобы получить фиксированную часть дескриптора конфигурации из ранее полученного объекта UsbDevice ,

  1. Получите объект UsbConfiguration из UsbDevice. UsbConfiguration представляет первую конфигурацию USB, определенную устройством, и также выбирается по умолчанию базовым драйвером устройства.
  2. Получите значение свойства UsbConfiguration.ConfigurationDescriptor .

Фиксированная часть дескриптора конфигурации указывает характеристики питания устройства. Например, можно определить, черпет ли устройство питания из шины или внешнего источника (см. раздел UsbConfigurationDescriptor.SelfPowered). Если устройство получает питание от шины, сколько энергии (в миллиамповых единицах) оно потребляет (см. раздел UsbConfigurationDescriptor.MaxPowerMilliamps). Кроме того, вы можете определить, может ли устройство просыпаться само или система из состояния низкого энергопотребления, получив значение UsbConfigurationDescriptor.RemoteWakeup .

В этом примере кода показано, как получить фиксированную часть дескриптора конфигурации в строке.

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

Выходные данные приведены здесь:

Дескриптор конфигурации USB.

Получение дескрипторов интерфейса

Далее можно получить сведения об ИНТЕРФЕЙСАх USB, которые являются частью конфигурации.

ИНТЕРФЕЙС USB — это набор параметров интерфейса. Таким образом, не существует дескриптора, описывающего весь интерфейс. Дескриптор термина interface указывает структуру данных, описывающую параметр в интерфейсе.

Пространство имен Windows.Devices.Usb предоставляет объекты, которые можно использовать для получения сведений о каждом интерфейсе USB и всех дескрипторах интерфейсов (для альтернативных параметров), включенных в этот интерфейс. дескрипторы и из части дескриптора конфигурации переменной длины.

Чтобы получить дескрипторы интерфейса из UsbConfiguration,

  1. Получите массив интерфейсов в конфигурации, получив свойство UsbConfiguration.UsbInterfaces .
  2. Для каждого интерфейса (UsbInterface) получите следующие сведения:
    • Каналы массовых операций и прерывания, которые активны и могут передавать данные.
    • Массив альтернативных параметров в интерфейсе.
    • Массив дескрипторов интерфейса.

Этот пример кода получает все объекты UsbInterface для конфигурации. Из каждого объекта вспомогательный метод получает количество альтернативных параметров и открытых массовых каналов и каналов интерфейса. Если устройство поддерживает несколько интерфейсов, коды классов, подклассов и протоколов каждого интерфейса могут отличаться. Однако все дескрипторы интерфейса для альтернативных параметров должны указывать одинаковые коды. В этом примере метод получает коды класса устройства, подкласса и протокола из дескриптора интерфейса первого параметра, чтобы определить код для всего интерфейса.

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

Выходные данные приведены здесь:

Дескриптор интерфейса USB.

Получение дескрипторов конечной точки

Все конечные точки USB (за исключением конечной точки управления по умолчанию) должны иметь дескрипторы конечных точек. Чтобы получить дескрипторы конечной точки для определенной конечной точки, необходимо знать, к какому интерфейсу и альтернативному параметру относится конечная точка.

  1. Получите объект UsbInterface , содержащий конечную точку.

  2. Получите массив альтернативных параметров, получив usbInterface.InterfaceSettings.

  3. В массиве найдите параметр (UsbInterfaceSetting), который использует конечную точку.

  4. В каждом параметре найдите конечную точку, перечислив массивы массовых дескрипторов и дескрипторов прерываний.

    Дескрипторы конечных точек представлены следующими объектами:

Если устройство имеет только один интерфейс, можно использовать UsbDevice.DefaultInterface , чтобы получить интерфейс, как показано в этом примере кода. Здесь вспомогательный метод получает заполнение строки дескрипторами конечной точки, связанными с каналами активного параметра интерфейса.

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

Выходные данные приведены здесь:

дескрипторы конечной точки USB.

Получение пользовательских дескрипторов

Обратите внимание, что объекты UsbConfiguration, UsbInterface и UsbInterfaceSetting предоставляют свойство с именем Дескрипторы. Это значение свойства извлекает массив дескрипторов, представленных объектами UsbDescriptor . Объект UsbDescriptor позволяет приложению получать данные дескриптора в буфере. Свойства UsbDescriptor.DescriptorType и UsbDescriptor.Length хранят тип и длину буфера, необходимого для хранения дескриптора.

Примечание Первые два байта всех буферов дескриптора также указывают тип и длину дескриптора.

Например, свойство UsbConfiguration.Descriptors получает массив полных дескрипторов конфигурации (части фиксированной и переменной длины). Первый элемент в этом массиве — это дескриптор конфигурации фиксированной длины (то же, что и UsbConfigurationDescriptor), второй элемент — дескриптор интерфейса первого альтернативного параметра и т. д.

Аналогичным образом свойство UsbInterface.Descriptors получает массив всех дескрипторов интерфейса и связанных дескрипторов конечной точки. Свойство UsbInterfaceSetting.Descriptors получает массив всех дескрипторов для этого параметра, таких как дескрипторы конечных точек.

Этот способ получения дескрипторов полезен, когда приложение хочет получить пользовательские дескрипторы или другие дескрипторы, такие как дескрипторы-компаньоны конечных точек для устройств SuperSpeed.

В этом примере кода показано, как получить данные дескриптора в буфере из дескриптора конфигурации. В примере возвращается набор дескрипторов конфигурации и выполняется синтаксический анализ всех дескрипторов, содержащихся в этом наборе. Для каждого дескриптора он использует объект DataReader для чтения буфера и отображения длины и типа дескриптора. Вы можете получить пользовательские дескрипторы, как показано в этом примере.

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