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;
}
아래와 같은 출력이 표시됩니다.
구성 설명자를 가져오는 방법
이전에 가져온 UsbDevice 개체에서 구성 설명자의 고정 부분을 가져오려면
- UsbDevice에서 UsbConfiguration 개체를 가져옵니다. UsbConfiguration 은 디바이스에서 정의한 첫 번째 USB 구성을 나타내며 기본 디바이스 드라이버에 의해 기본적으로 선택되기도 합니다.
- 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 인터페이스는 인터페이스 설정의 컬렉션입니다. 따라서 전체 인터페이스를 설명하는 설명자가 없습니다. 인터페이스 설명자라는 용어는 인터페이스 내의 설정을 설명하는 데이터 구조를 나타냅니다.
Windows.Devices.Usb 네임스페이스는 각 USB 인터페이스 및 해당 인터페이스에 포함된 모든 인터페이스 설명자(대체 설정용)에 대한 정보를 가져오는 데 사용할 수 있는 개체를 노출합니다. 및 는 구성 설명자의 가변 길이 부분의 설명자입니다.
UsbConfiguration에서 인터페이스 설명자를 얻으려면
- UsbConfiguration.UsbInterfaces 속성을 가져와서 구성 내의 인터페이스 배열을 가져옵니다.
- 각 인터페이스(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 엔드포인트(기본 컨트롤 엔드포인트 제외)에는 엔드포인트 설명자가 있어야 합니다. 특정 엔드포인트에 대한 엔드포인트 설명자를 가져오려면 엔드포인트가 속한 인터페이스 및 대체 설정을 알고 있어야 합니다.
엔드포인트가 포함된 UsbInterface 개체를 가져옵니다.
UsbInterface.InterfaceSettings를 가져와서 대체 설정 배열을 가져옵니다.
배열 내에서 엔드포인트를 사용하는 설정(UsbInterfaceSetting)을 찾습니다.
각 설정 내에서 대량 및 인터럽트 설명자 배열을 열거하여 엔드포인트를 찾습니다.
엔드포인트 설명자는 다음 개체로 표시됩니다.
디바이스에 인터페이스가 하나만 있는 경우 이 예제 코드와 같이 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;
}
아래와 같은 출력이 표시됩니다.
사용자 지정 설명자를 가져오는 방법
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;
}