如何取得UWP app (USB描述元)
與 USB 裝置互動的主要工作之一是取得其相關信息。 所有USB裝置都會以稱為描述元的數個數據結構形式提供資訊。 本文說明 UWP app 如何從端點、介面、組態和裝置層級的裝置取得描述項。 本文也涵蓋:
- 瞭解USB裝置配置
- 取得標準USB描述元
- 取得自定義描述項
重要 API
USB 描述元
USB 裝置會在兩個主要描述項中描述其功能:裝置描述元和設定描述元。
USB 裝置必須提供包含USB裝置整體相關信息的 裝置描述項 。 如果裝置未提供該描述元或提供格式不正確的描述元,Windows 就無法載入設備驅動器。 描述元中最重要的資訊是裝置 的硬體標識碼 , (廠商 標識碼與 產品 標識符欄位的組合) 。 它以 Windows 能夠比對裝置內驅動程式的資訊為基礎。 另一個是金鑰的信息是預設端點的最大 封包大小 , (MaxPacketSize0) 。 默認端點是主機傳送至裝置以設定它的所有控制要求目標。
固定裝置描述元的長度。
USB 裝置也必須提供完整的 設定描述元。 此描述元的開頭部分固定長度為 9 個字節,其餘部分取決於這些介面支援的介面數目和端點的可變長度。 固定長度部分提供 USB 組態的相關信息:當裝置位於該組態時,支援和耗電量的介面數目。 這些初始 9 個字節後面接著提供所有 USB 介面相關信息之描述元的變數部分。 每個介面都包含一或多個介面設定,而每個設定是由一組端點所組成。 介面、替代設定和端點的描述項會包含在變數部分。
如需裝置配置的詳細描述,請參閱 標準 USB 描述元。
開始之前
- 您必須已開啟裝置並取得 UsbDevice 物件。 請參閱 如何連線到 USB 裝置 (UWP app) 。
- 您可以在 CustomUsbDeviceAccess 範例中看到本主題中顯示的完整程式代碼,Scenario5_UsbDescriptors檔案。
- 取得裝置配置的相關信息。 適用於 Windows 8) 的 Windows 軟體開發工具組 (SDK) 中包含的Usbview.exe(是一種應用程式,可讓您流覽所有 USB 控制器和與其連線的 USB 裝置。 針對每個連線的裝置,您可以檢視裝置、設定、介面和端點描述元,以了解裝置的功能。
如何取得裝置描述元
您的UWP app可以取得先前取得 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;
}
輸出如下所示:
如何取得設定描述元
若要從先前取得的 UsbDevice 物件取得組態描述元的固定部分,
- 從 UsbDevice 取得 UsbConfiguration 物件。 UsbConfiguration 代表裝置所定義的第一個 USB 組態,而且預設也會由基礎設備驅動器選取。
- 取得 UsbConfiguration.ConfigurationDescriptor 屬性值。
組態描述項的固定部分表示裝置的電源特性。 例如,您可以判斷裝置是從總線或外部來源繪製電源, (請參閱 UsbConfigurationDescriptor.SelfPowered) 。 如果裝置從總線繪製電源,) () 以 milliamp 單位為單位的電源 (,請參閱 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 物件,每個物件都會公開名為 Descriptors 的屬性。 該屬性值會擷取 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;
}