Cara mendapatkan deskriptor USB (aplikasi UWP)

Salah satu tugas utama berinteraksi dengan perangkat USB adalah mendapatkan informasi tentang hal itu. Semua perangkat USB memberikan informasi dalam bentuk beberapa struktur data yang disebut deskriptor. Artikel ini menjelaskan bagaimana aplikasi UWP bisa mendapatkan deskriptor dari perangkat di tingkat titik akhir, antarmuka, konfigurasi, dan perangkat. Artikel ini juga membahas:

  • Memahami tata letak perangkat USB
  • Mendapatkan deskriptor USB standar
  • Mendapatkan deskriptor kustom

API penting

Deskriptor USB

Perangkat USB menjelaskan kemampuannya dalam dua deskriptor utama: deskriptor perangkat dan deskriptor konfigurasi.

Perangkat USB harus menyediakan deskriptor perangkat yang berisi informasi tentang perangkat USB secara keseluruhan. Jika perangkat tidak menyediakan deskriptor tersebut atau menyediakan deskriptor cacat, Windows tidak dapat memuat driver perangkat. Informasi terpenting dalam deskriptor adalah ID perangkat keras perangkat untuk perangkat (kombinasi ID vendor dan bidang ID produk ). Ini didasarkan pada informasi bahwa Windows dapat mencocokkan driver dalam kotak untuk perangkat. Informasi lain yang merupakan kunci adalah ukuran paket maksimum dari titik akhir default (MaxPacketSize0). Titik akhir default adalah target semua permintaan kontrol yang dikirim host ke perangkat untuk mengonfigurasinya.

Panjang deskriptor perangkat diperbaiki.

Perangkat USB juga harus menyediakan deskriptor konfigurasi lengkap. Bagian awal deskriptor ini memiliki panjang tetap 9 byte, sisanya adalah panjang variabel tergantung pada jumlah antarmuka dan titik akhir yang didukung antarmuka tersebut. Bagian panjang tetap menyediakan informasi tentang konfigurasi USB: jumlah antarmuka yang didukungnya dan konsumsi daya saat perangkat berada dalam konfigurasi tersebut. 9 byte awal tersebut diikuti oleh bagian variabel deskriptor yang menyediakan informasi tentang semua antarmuka USB. Setiap antarmuka terdiri dari satu atau beberapa pengaturan antarmuka, dan setiap pengaturan terdiri dari satu set titik akhir. Deskriptor untuk antarmuka, pengaturan alternatif, dan titik akhir disertakan dalam bagian variabel.

Untuk deskripsi terperinci tentang tata letak perangkat, lihat Deskriptor USB Standar.

Sebelum memulai

  • Anda harus telah membuka perangkat dan mendapatkan objek UsbDevice . Baca Cara menyambungkan ke perangkat USB (aplikasi UWP).
  • Anda dapat melihat kode lengkap yang ditampilkan dalam topik ini dalam sampel CustomUsbDeviceAccess, Scenario5_UsbDescriptors file.
  • Dapatkan informasi tentang tata letak perangkat. Usbview.exe (termasuk dalam Windows Software Development Kit (SDK) untuk Windows 8) adalah aplikasi yang memungkinkan Anda menelusuri semua pengontrol USB dan perangkat USB yang terhubung dengannya. Untuk setiap perangkat yang terhubung, Anda dapat melihat perangkat, konfigurasi, antarmuka, dan deskriptor titik akhir untuk mendapatkan gambaran tentang kemampuan perangkat.

Cara mendapatkan deskriptor perangkat

Aplikasi UWP Anda bisa mendapatkan pendeskripsi perangkat dari objek UsbDevice yang diperoleh sebelumnya dengan mendapatkan nilai properti UsbDevice.DeviceDescriptor .

Contoh kode ini menunjukkan cara mengisi string dengan nilai bidang dari deskriptor perangkat.

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

Output ditampilkan di sini:

deskriptor perangkat usb.

Cara mendapatkan deskriptor konfigurasi

Untuk mendapatkan bagian tetap dari deskriptor konfigurasi dari objek UsbDevice yang diperoleh sebelumnya,

  1. Dapatkan objek UsbConfiguration dari UsbDevice. UsbConfiguration mewakili konfigurasi USB pertama yang ditentukan oleh perangkat dan juga dipilih secara default oleh driver perangkat yang mendasar.
  2. Dapatkan nilai properti UsbConfiguration.ConfigurationDescriptor .

Bagian tetap dari deskriptor konfigurasi menunjukkan karakteristik daya perangkat. Misalnya, Anda dapat menentukan apakah perangkat menarik daya dari bus atau sumber eksternal (lihat UsbConfigurationDescriptor.SelfPowered). Jika perangkat menarik daya dari bus, berapa banyak daya (dalam unit miliamp) yang dikonsumsinya (lihat UsbConfigurationDescriptor.MaxPowerMilliamps). Selain itu, Anda dapat menentukan apakah perangkat mampu membangunkan dirinya sendiri atau sistem dari status daya rendah, dengan mendapatkan nilai UsbConfigurationDescriptor.RemoteWakeup .

Contoh kode ini menunjukkan cara mendapatkan bagian tetap dari deskriptor konfigurasi dalam string.

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

Output ditampilkan di sini:

deskriptor konfigurasi usb.

Cara mendapatkan deskriptor antarmuka

Selanjutnya, Anda bisa mendapatkan informasi tentang antarmuka USB yang merupakan bagian dari konfigurasi.

Antarmuka USB adalah kumpulan pengaturan antarmuka. Dengan demikian tidak ada deskriptor yang menjelaskan seluruh antarmuka. Istilah pendeskripsi antarmuka menunjukkan struktur data yang menjelaskan pengaturan dalam antarmuka.

Namespace Windows.Devices.Usb mengekspos objek yang dapat Anda gunakan untuk mendapatkan informasi tentang setiap antarmuka USB dan semua deskriptor antarmuka (untuk pengaturan alternatif) yang disertakan dalam antarmuka tersebut. dan deskriptor dari bagian panjang variabel deskriptor konfigurasi.

Untuk mendapatkan deskriptor antarmuka dari UsbConfiguration,

  1. Dapatkan array antarmuka dalam konfigurasi dengan mendapatkan properti UsbConfiguration.UsbInterfaces .
  2. Untuk setiap antarmuka (UsbInterface), dapatkan informasi ini:
    • Pipa massal dan interupsi yang aktif dan dapat mentransfer data.
    • Array pengaturan alternatif di antarmuka.
    • Array deskriptor antarmuka.

Contoh kode ini mendapatkan semua objek UsbInterface untuk konfigurasi. Dari setiap objek, metode pembantu mendapatkan jumlah pengaturan alternatif dan membuka pipa massal dan antarmuka. Jika perangkat mendukung beberapa antarmuka, kelas perangkat, subkelas, dan kode protokol setiap antarmuka dapat berbeda. Namun, semua deskriptor antarmuka untuk pengaturan alternatif harus menentukan kode yang sama. Dalam contoh ini, metode mendapatkan kelas perangkat, subkelas, dan kode protokol dari deskriptor antarmuka pengaturan pertama untuk menentukan kode untuk seluruh antarmuka.

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

Output ditampilkan di sini:

deskriptor antarmuka usb.

Cara mendapatkan deskriptor titik akhir

Semua titik akhir USB (kecuali titik akhir kontrol default) harus memiliki deskriptor titik akhir. Untuk mendapatkan deskriptor titik akhir untuk titik akhir tertentu, Anda harus mengetahui antarmuka mana dan pengaturan alternatif tempat titik akhir berada.

  1. Dapatkan objek UsbInterface yang berisi titik akhir.

  2. Dapatkan array pengaturan alternatif dengan mendapatkan UsbInterface.InterfaceSettings.

  3. Dalam array, temukan pengaturan (UsbInterfaceSetting) yang menggunakan titik akhir.

  4. Dalam setiap pengaturan, temukan titik akhir dengan menghitung array deskriptor massal dan interupsi.

    Deskriptor titik akhir diwakili oleh objek ini:

Jika perangkat Anda hanya memiliki satu antarmuka, Anda dapat menggunakan UsbDevice.DefaultInterface untuk mendapatkan antarmuka seperti yang ditunjukkan dalam contoh kode ini. Di sini, metode pembantu akan mengisi string dengan deskriptor titik akhir yang terkait dengan pipa pengaturan antarmuka aktif.

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

Output ditampilkan di sini:

deskriptor titik akhir usb.

Cara mendapatkan deskriptor kustom

Perhatikan bahwa objek UsbConfiguration, UsbInterface, dan UsbInterfaceSetting , masing-masing mengekspos properti bernama Descriptors. Nilai properti tersebut mengambil array deskriptor yang diwakili oleh objek UsbDescriptor . Objek UsbDescriptor memungkinkan aplikasi untuk mendapatkan data deskriptor dalam buffer. Properti UsbDescriptor.DescriptorType dan UsbDescriptor.Length menyimpan jenis dan panjang buffer yang diperlukan untuk menahan deskriptor.

Catatan Dua byte pertama dari semua buffer deskriptor juga menunjukkan jenis dan panjang deskriptor.

Misalnya, properti UsbConfiguration.Descriptors mendapatkan array deskriptor konfigurasi lengkap (bagian panjang tetap dan variabel). Elemen pertama dalam array tersebut adalah deskriptor konfigurasi panjang tetap (sama dengan UsbConfigurationDescriptor), elemen kedua adalah deskriptor antarmuka dari pengaturan alternatif pertama, dan sebagainya.

Demikian pula, properti UsbInterface.Descriptors mendapatkan array semua deskriptor antarmuka dan deskriptor titik akhir terkait. Properti UsbInterfaceSetting.Descriptors mendapatkan array semua deskriptor untuk pengaturan tersebut, seperti deskriptor titik akhir.

Cara mendapatkan deskriptor ini berguna ketika aplikasi ingin mengambil deskriptor kustom atau deskriptor lain seperti deskriptor pendamping titik akhir untuk perangkat SuperSpeed.

Contoh kode ini menunjukkan cara mendapatkan data deskriptor dalam buffer dari deskriptor konfigurasi. Contohnya mendapatkan set deskriptor konfigurasi dan mengurai semua deskriptor yang terkandung dalam set tersebut. Untuk setiap deskriptor, ia menggunakan objek DataReader untuk membaca buffer dan menampilkan panjang dan jenis deskriptor. Anda bisa mendapatkan deskriptor kustom seperti yang ditunjukkan dalam contoh ini.

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