다음을 통해 공유


USB 설명자를 가져오는 방법(UWP 앱)

USB 디바이스와 상호 작용하는 기본 작업 중 하나는 USB 디바이스에 대한 정보를 가져오는 것입니다. 모든 USB 디바이스는 설명자라는 여러 데이터 구조 형식의 정보를 제공합니다. 이 문서에서는 UWP 앱이 엔드포인트, 인터페이스, 구성 및 디바이스 수준에서 디바이스에서 설명자를 가져오는 방법을 설명합니다. 이 문서에서는 다음과 같은 내용도 다룹니다.

  • USB 디바이스 레이아웃 이해
  • 표준 USB 설명자 가져오기
  • 사용자 지정 설명자 가져오기

중요 API

USB 설명자

USB 디바이스는 디바이스 설명자 및 구성 설명자라는 두 기본 설명자에서 해당 기능을 설명합니다.

USB 디바이스는 USB 디바이스에 대한 정보를 전체적으로 포함하는 디바이스 설명자를 제공해야 합니다. 디바이스가 해당 설명자를 제공하지 않거나 잘못된 형식의 설명자를 제공하는 경우 Windows에서 디바이스 드라이버를 로드할 수 없습니다. 설명자에서 가장 중요한 정보는 디바이스에 대한 디바이스의 하드웨어 ID ( 공급업체 ID제품 ID 필드의 조합)입니다. Windows가 디바이스의 기본 제공 드라이버와 일치시킬 수 있는 정보를 기반으로 합니다. 키인 또 다른 정보는 기본 엔드포인트의 최대 패킷 크기 (MaxPacketSize0)입니다. 기본 엔드포인트는 호스트가 디바이스를 구성하기 위해 디바이스로 보내는 모든 제어 요청의 대상입니다.

디바이스 설명자의 길이가 수정되었습니다.

USB 디바이스는 전체 구성 설명자도 제공해야 합니다. 이 설명자의 시작 부분에는 고정 길이가 9바이트이고, 나머지는 해당 인터페이스가 지원하는 인터페이스 및 엔드포인트 수에 따라 가변 길이입니다. 고정 길이 부분은 USB 구성에 대한 정보( 지원하는 인터페이스 수 및 디바이스가 해당 구성에 있을 때의 전력 소비)를 제공합니다. 이러한 초기 9바이트 뒤에는 모든 USB 인터페이스에 대한 정보를 제공하는 설명자의 변수 부분이 뒤따릅니다. 각 인터페이스는 하나 이상의 인터페이스 설정으로 구성되며 각 설정은 엔드포인트 집합으로 구성됩니다. 인터페이스, 대체 설정 및 엔드포인트에 대한 설명자는 변수 부분에 포함됩니다.

디바이스 레이아웃에 대한 자세한 설명은 표준 USB 설명자를 참조하세요.

시작하기 전에

  • 디바이스를 열고 UsbDevice 개체를 가져와야 합니다. USB 디바이스에 연결하는 방법(UWP 앱)을 참조하세요.
  • 이 항목의 전체 코드는 CustomUsbDeviceAccess 샘플 Scenario5_UsbDescriptors 파일에서 확인할 수 있습니다.
  • 디바이스 레이아웃에 대한 정보를 가져옵니다. Usbview.exe(Windows 8 용 Windows SDK(소프트웨어 개발 키트)에 포함됨)은 연결된 모든 USB 컨트롤러 및 USB 디바이스를 검색할 수 있는 애플리케이션입니다. 연결된 각 디바이스에 대해 디바이스, 구성, 인터페이스 및 엔드포인트 설명자를 보고 디바이스의 기능에 대한 아이디어를 얻을 수 있습니다.

디바이스 설명자를 가져오는 방법

UWP 앱은 UsbDevice.DeviceDescriptor 속성 값을 가져와서 이전에 가져온 UsbDevice 개체에서 디바이스 설명자를 가져올 수 있습니다.

이 코드 예제에서는 디바이스 설명자의 필드 값으로 문자열을 채우는 방법을 보여줍니다.

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. UsbDevice에서 UsbConfiguration 개체를 가져옵니다. 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 인터페이스는 인터페이스 설정의 컬렉션입니다. 따라서 전체 인터페이스를 설명하는 설명자가 없습니다. 인터페이스 설명자라는 용어는 인터페이스 내의 설정을 설명하는 데이터 구조를 나타냅니다.

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, UsbInterfaceUsbInterfaceSetting 개체는 각각 설명자라는 속성을 노출합니다. 해당 속성 값은 UsbDescriptor 개체가 나타내는 설명자 배열을 검색합니다. UsbDescriptor 개체를 사용하면 앱이 버퍼에서 설명자 데이터를 가져올 수 있습니다. UsbDescriptor.DescriptorTypeUsbDescriptor.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;
}