Cara memilih konfigurasi untuk perangkat USB

Untuk memilih konfigurasi untuk perangkat USB, driver klien untuk perangkat harus memilih setidaknya salah satu konfigurasi yang didukung dan menentukan pengaturan alternatif dari setiap antarmuka yang akan digunakan. Driver klien mengemas pilihan tersebut dalam permintaan konfigurasi tertentu dan mengirim permintaan ke tumpukan driver USB yang disediakan Microsoft, khususnya driver bus USB (USB hub PDO). Driver bus USB memilih setiap antarmuka dalam konfigurasi yang ditentukan dan menyiapkan saluran komunikasi, atau pipa, ke setiap titik akhir dalam antarmuka. Setelah permintaan selesai, driver klien menerima handel untuk konfigurasi yang dipilih, dan handel pipa untuk titik akhir yang ditentukan dalam pengaturan alternatif aktif untuk setiap antarmuka. Driver klien kemudian dapat menggunakan handel yang diterima untuk mengubah pengaturan konfigurasi dan untuk mengirim permintaan baca dan tulis I/O ke titik akhir tertentu.

Driver klien mengirimkan permintaan konfigurasi pilih di Blok Permintaan USB (URB) jenis URB_FUNCTION_SELECT_CONFIGURATION. Prosedur dalam topik ini menjelaskan cara menggunakan USBD_SelectConfigUrbAllocateAndBuild rutin untuk membangun URB tersebut. Rutinitas mengalokasikan memori untuk URB, memformat URB untuk permintaan konfigurasi tertentu, dan mengembalikan alamat URB ke driver klien.

Secara bergantian, Anda dapat mengalokasikan struktur URB lalu memformat URB secara manual atau dengan memanggil makro UsbBuildSelectConfigurationRequest .

Prasyarat

Langkah 1: Membuat array struktur USBD_INTERFACE_LIST_ENTRY

  1. Dapatkan jumlah antarmuka dalam konfigurasi. Informasi ini terkandung dalam anggota bNumInterfaces dari struktur USB_CONFIGURATION_DESCRIPTOR .

  2. Buat array struktur USBD_INTERFACE_LIST_ENTRY . Jumlah elemen dalam array harus satu lebih dari jumlah antarmuka. Inisialisasi array dengan memanggil RtlZeroMemory.

    Driver klien menentukan pengaturan alternatif di setiap antarmuka untuk diaktifkan, dalam array struktur USBD_INTERFACE_LIST_ENTRY .

    • Anggota InterfaceDescriptor dari setiap struktur menunjuk ke deskriptor antarmuka yang berisi pengaturan alternatif.
    • Anggota Antarmuka dari setiap struktur menunjuk ke struktur USBD_INTERFACE_INFORMATION yang berisi informasi pipa di anggota Pipanya . Pipa menyimpan informasi tentang setiap titik akhir yang ditentukan dalam pengaturan alternatif.
  3. Dapatkan deskriptor antarmuka untuk setiap antarmuka (atau pengaturan alternatifnya) dalam konfigurasi. Anda dapat memperoleh deskriptor antarmuka tersebut dengan memanggil USBD_ParseConfigurationDescriptorEx.

    Tentang Driver Fungsi untuk Perangkat Komposit USB:

    Jika perangkat USB adalah perangkat komposit, konfigurasi dipilih oleh DRIVER Induk Generik USB yang disediakan Microsoft (Usbccgp.sys). Driver klien, yang merupakan salah satu driver fungsi perangkat komposit, tidak dapat mengubah konfigurasi tetapi driver masih dapat mengirim permintaan konfigurasi pilih melalui Usbccgp.sys.

    Sebelum mengirim permintaan tersebut, driver klien harus mengirimkan permintaan URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE. Sebagai tanggapan, Usbccgp.sys mengambil deskriptor konfigurasi parsial yang hanya berisi deskriptor antarmuka dan deskriptor lain yang berkaitan dengan fungsi tertentu tempat driver klien dimuat. Jumlah antarmuka yang dilaporkan di bidang bNumInterfaces dari deskriptor konfigurasi parsial kurang dari jumlah total antarmuka yang ditentukan untuk seluruh perangkat komposit USB. Selain itu, dalam deskriptor konfigurasi parsial, bInterfaceNumber deskriptor antarmuka menunjukkan nomor antarmuka aktual relatif terhadap seluruh perangkat. Misalnya, Usbccgp.sys mungkin melaporkan pendeskripsi konfigurasi parsial dengan nilai bNumInterfaces 2 dan nilai bInterfaceNumber 4 untuk antarmuka pertama. Perhatikan bahwa nomor antarmuka lebih besar dari jumlah antarmuka yang dilaporkan.

    Saat menghitung antarmuka dalam konfigurasi parsial, hindari mencari antarmuka dengan menghitung nomor antarmuka berdasarkan jumlah antarmuka. Dalam contoh sebelumnya, jika USBD_ParseConfigurationDescriptorEx dipanggil dalam perulangan yang dimulai dari nol, berakhir pada (bNumInterfaces - 1), dan meningkatkan indeks antarmuka (ditentukan dalam parameter InterfaceNumber ) di setiap iterasi, rutinitas gagal mendapatkan antarmuka yang benar. Sebagai gantinya, pastikan Anda mencari semua antarmuka dalam pendeskripsi konfigurasi dengan meneruskan -1 di InterfaceNumber. Untuk detail implementasi, lihat contoh kode di bagian ini.

    Untuk informasi tentang cara Usbccgp.sys menangani permintaan konfigurasi tertentu yang dikirim oleh driver klien, lihat Mengonfigurasi Usbccgp.sys untuk Memilih Konfigurasi USB Non-Default.

  4. Untuk setiap elemen (kecuali elemen terakhir) dalam array, atur anggota InterfaceDescriptor ke alamat deskriptor antarmuka. Untuk elemen pertama dalam array, atur anggota InterfaceDescriptor ke alamat deskriptor antarmuka yang mewakili antarmuka pertama dalam konfigurasi. Demikian pula untuk elemen nth dalam array, atur anggota InterfaceDescriptor ke alamat deskriptor antarmuka yang mewakili antarmuka ke-ndalam konfigurasi.

  5. Anggota InterfaceDescriptor dari elemen terakhir harus diatur ke NULL.

Langkah 2: Dapatkan pointer ke URB yang dialokasikan oleh tumpukan driver USB

Selanjutnya, panggil USBD_SelectConfigUrbAllocateAndBuild dengan menentukan konfigurasi yang akan dipilih dan array struktur USBD_INTERFACE_LIST_ENTRY yang diisi. Rutinitas melakukan tugas-tugas berikut:

  • Membuat URB dan mengisinya dengan informasi tentang konfigurasi yang ditentukan, antarmuka dan titik akhirnya, dan mengatur jenis permintaan ke URB_FUNCTION_SELECT_CONFIGURATION.

  • Dalam URB tersebut, mengalokasikan struktur USBD_INTERFACE_INFORMATION untuk setiap deskriptor antarmuka yang ditentukan driver klien.

  • Mengatur anggota Antarmuka dari elemen nth dari array USBD_INTERFACE_LIST_ENTRY yang disediakan pemanggil ke alamat struktur USBD_INTERFACE_INFORMATION yang sesuai dalam URB.

  • Menginisialisasi InterfaceNumber, AlternateSetting, NumberOfPipes, Pipes[i]. MaximumTransferSize, dan Pipes[i]. Anggota PipeFlags .

    Catatan

    Di Windows 7 dan ealier, driver klien membuat URB untuk permintaan konfigurasi tertentu dengan memanggil USBD_CreateConfigurationRequestEx. Di Windows 2000 USBD_CreateConfigurationRequestEx menginisialisasi Pipes[i]. MaximumTransferSize ke ukuran transfer maksimum default untuk satu permintaan baca/tulis URB. Driver klien dapat menentukan ukuran transfer maksimum yang berbeda di Pipes[i]. MaximumTransferSize. Tumpukan USB mengabaikan nilai ini di Windows XP, Windows Server 2003, dan versi sistem operasi yang lebih baru. Untuk informasi selengkapnya tentang MaximumTransferSize, lihat "Mengatur Transfer USB dan Ukuran Paket" di Alokasi Bandwidth USB.

Langkah 3: Kirim URB ke tumpukan driver USB

Untuk mengirimkan URB ke tumpukan driver USB, driver klien harus mengirim permintaan kontrol I/O IOCTL_INTERNAL_USB_SUBMIT_URB . Untuk informasi tentang mengirimkan URB, lihat Cara Mengirimkan URB.

Setelah menerima URB, tumpukan driver USB mengisi anggota lainnya dari setiap struktur USBD_INTERFACE_INFORMATION . Secara khusus, anggota array Pipes diisi dengan informasi tentang pipa yang terkait dengan titik akhir antarmuka.

Langkah 4: Berdasarkan penyelesaian permintaan, periksa struktur USBD_INTERFACE_INFORMATION dan URB

Setelah tumpukan driver USB menyelesaikan IRP untuk permintaan, tumpukan mengembalikan daftar pengaturan alternatif dan antarmuka terkait dalam array USBD_INTERFACE_LIST_ENTRY .

  1. Anggota Pipa dari setiap struktur USBD_INTERFACE_INFORMATION menunjuk ke array struktur USBD_PIPE_INFORMATION yang berisi informasi tentang pipa yang terkait dengan setiap titik akhir antarmuka tertentu. Driver klien dapat memperoleh handel pipa dari Pipes[i]. PipeHandle dan gunakan untuk mengirim permintaan I/O ke pipa tertentu. Pipa[i]. Anggota PipeType menentukan jenis titik akhir dan transfer yang didukung oleh pipa tersebut.

  2. Dalam anggota UrbSelectConfiguration dari URB, tumpukan driver USB mengembalikan handel yang dapat Anda gunakan untuk memilih pengaturan antarmuka alternatif dengan mengirimkan URB lain dari jenis URB_FUNCTION_SELECT_INTERFACE (permintaan antarmuka-pilih). Untuk mengalokasikan dan membangun struktur URB untuk permintaan tersebut, panggil USBD_SelectInterfaceUrbAllocateAndBuild.

    Permintaan select-configuration dan permintaan select-interface mungkin gagal jika bandwidth tidak mencukupi untuk mendukung titik akhir isochronous, control, dan interupsi dalam antarmuka yang diaktifkan. Dalam hal ini, driver bus USB mengatur anggota Status header URB ke USBD_STATUS_NO_BANDWIDTH.

Contoh kode berikut menunjukkan cara membuat array struktur USBD_INTERFACE_LIST_ENTRY dan memanggil USBD_SelectConfigUrbAllocateAndBuild. Contoh mengirim permintaan secara sinkron dengan memanggil SubmitUrbSync. Untuk melihat contoh kode untuk SubmitUrbSync, lihat Cara Mengirimkan URB.

/*++

Routine Description:
This helper routine selects the specified configuration.

Arguments:
USBDHandle - USBD handle that is retrieved by the 
client driver in a previous call to the USBD_CreateHandle routine.

ConfigurationDescriptor - Pointer to the configuration
descriptor for the device. The caller receives this pointer
from the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE request.

Return Value: NT status value
--*/

NTSTATUS SelectConfiguration (PDEVICE_OBJECT DeviceObject,
                              PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PDEVICE_EXTENSION deviceExtension;
    PIO_STACK_LOCATION nextStack;
    PIRP irp;
    PURB urb = NULL;

    KEVENT    kEvent;
    NTSTATUS ntStatus;    

    PUSBD_INTERFACE_LIST_ENTRY   interfaceList = NULL; 
    PUSB_INTERFACE_DESCRIPTOR    interfaceDescriptor = NULL;
    PUSBD_INTERFACE_INFORMATION  Interface = NULL;
    USBD_PIPE_HANDLE             pipeHandle;

    ULONG                        interfaceIndex;

    PUCHAR StartPosition = (PUCHAR)ConfigurationDescriptor;

    deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

    // Allocate an array for the list of interfaces
    // The number of elements must be one more than number of interfaces.
    interfaceList = (PUSBD_INTERFACE_LIST_ENTRY)ExAllocatePool (
        NonPagedPool, 
        sizeof(USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    if(!interfaceList)
    {
        //Failed to allocate memory
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    // Initialize the array by setting all members to NULL.
    RtlZeroMemory (interfaceList, sizeof (
        USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    // Enumerate interfaces in the configuration.
    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
            ConfigurationDescriptor, 
            StartPosition, // StartPosition 
            -1,            // InterfaceNumber
            0,             // AlternateSetting
            -1,            // InterfaceClass
            -1,            // InterfaceSubClass
            -1);           // InterfaceProtocol

        if (!interfaceDescriptor) 
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto Exit;
        }

        // Set the interface entry
        interfaceList[interfaceIndex].InterfaceDescriptor = interfaceDescriptor;
        interfaceList[interfaceIndex].Interface = NULL;

        // Move the position to the next interface descriptor
        StartPosition = (PUCHAR)interfaceDescriptor + interfaceDescriptor->bLength;

    }

    // Make sure that the InterfaceDescriptor member of the last element to NULL.
    interfaceList[deviceExtension->NumInterfaces].InterfaceDescriptor = NULL;

    // Allocate and build an URB for the select-configuration request.
    ntStatus = USBD_SelectConfigUrbAllocateAndBuild(
        deviceExtension->UsbdHandle, 
        ConfigurationDescriptor, 
        interfaceList,
        &urb);

    if(!NT_SUCCESS(ntStatus)) 
    {
        goto Exit;
    }

    // Allocate the IRP to send the buffer down the USB stack.
    // The IRP will be freed by IO manager.
    irp = IoAllocateIrp((deviceExtension->NextDeviceObject->StackSize)+1, TRUE);  

    if (!irp)
    {
        //Irp could not be allocated.
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    ntStatus = SubmitUrbSync( 
        deviceExtension->NextDeviceObject, 
        irp, 
        urb, 
        CompletionRoutine);

    // Enumerate the pipes in the interface information array, which is now filled with pipe
    // information.

    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        ULONG i;

        Interface = interfaceList[interfaceIndex].Interface;

        for(i=0; i < Interface->NumberOfPipes; i++) 
        {
            pipeHandle = Interface->Pipes[i].PipeHandle;

            if (Interface->Pipes[i].PipeType == UsbdPipeTypeInterrupt)
            {
                deviceExtension->InterruptPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkInPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkOutPipe = pipeHandle;
            }
        }
    }

Exit:

    if(interfaceList) 
    {
        ExFreePool(interfaceList);
        interfaceList = NULL;
    }

    if (urb)
    {
        USBD_UrbFree( deviceExtension->UsbdHandle, urb); 
    }

    return ntStatus;
}

NTSTATUS CompletionRoutine ( PDEVICE_OBJECT DeviceObject,
                            PIRP           Irp,
                            PVOID          Context)
{
    PKEVENT kevent;

    kevent = (PKEVENT) Context;

    if (Irp->PendingReturned == TRUE)
    {
        KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
    }

    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Select-configuration request completed. \n" ));

    return STATUS_MORE_PROCESSING_REQUIRED;
}

Menonaktifkan konfigurasi untuk perangkat USB

Untuk menonaktifkan perangkat USB, buat dan kirim permintaan konfigurasi pilih dengan deskriptor konfigurasi NULL. Untuk jenis permintaan tersebut, Anda dapat menggunakan kembali URB yang Anda buat untuk permintaan yang memilih konfigurasi di perangkat. Secara bergantian, Anda dapat mengalokasikan URB baru dengan memanggil USBD_UrbAllocate. Sebelum mengirimkan permintaan, Anda harus memformat URB dengan menggunakan makro UsbBuildSelectConfigurationRequest seperti yang ditunjukkan dalam kode contoh berikut.

URB Urb;
UsbBuildSelectConfigurationRequest(
  &Urb,
  sizeof(_URB_SELECT_CONFIGURATION),
  NULL
);