Cara mengimplementasikan fungsi yang ditangguhkan dalam driver komposit

Artikel ini memberikan gambaran umum tentang fungsi yang menangguhkan dan fungsi fitur bangun jarak jauh untuk perangkat multifungsi Universal Serial Bus (USB) 3.0 (perangkat komposit). Dalam artikel ini, Anda akan mempelajari tentang menerapkan fitur tersebut dalam driver yang mengontrol perangkat komposit. Artikel ini berlaku untuk driver komposit yang menggantikan Usbccgp.sys.

Spesifikasi Universal Serial Bus (USB) 3.0 mendefinisikan fitur baru yang disebut fungsi ditangguhkan. Fitur ini memungkinkan fungsi individual dari perangkat komposit untuk memasuki status berdaya rendah, secara independen dari fungsi lain. Pertimbangkan perangkat komposit yang menentukan fungsi untuk keyboard dan fungsi lain untuk mouse. Pengguna mempertahankan fungsi keyboard dalam status berfungsi tetapi tidak memindahkan mouse untuk jangka waktu tertentu. Driver klien untuk mouse dapat mendeteksi status menganggur fungsi dan mengirim fungsi untuk menangguhkan status saat fungsi keyboard tetap dalam status berfungsi.

Seluruh perangkat dapat beralih ke status ditangguhkan terlepas dari status daya fungsi apa pun dalam perangkat. Jika fungsi tertentu dan seluruh perangkat memasuki status ditangguhkan, status tangguhan fungsi dipertahankan saat perangkat dalam status ditangguhkan, dan selama proses masuk dan keluar perangkat ditangguhkan.

Mirip dengan fitur wake-up jarak jauh perangkat USB 2.0 (lihat Remote Wake-up Perangkat USB), fungsi individual dalam perangkat komposit USB 3.0 dapat bangun dari status daya rendah tanpa memengaruhi status daya fungsi lain. Fitur ini disebut fungsi bangun jarak jauh. Fitur ini secara eksplisit diaktifkan oleh host dengan mengirim permintaan protokol yang mengatur bit bangun jarak jauh di firmware perangkat. Proses ini disebut mempersenjatai fungsi untuk bangun jarak jauh. Untuk informasi tentang bit terkait bangun jarak jauh, lihat Gambar 9-6 dalam spesifikasi USB resmi.

Jika fungsi dipersenjatai untuk bangun jarak jauh, fungsi (saat dalam status ditangguhkan) mempertahankan daya yang cukup untuk menghasilkan sinyal resume bangun saat peristiwa pengguna terjadi pada perangkat fisik. Sebagai hasil dari sinyal resume tersebut, driver klien kemudian dapat keluar dari status tangguhan fungsi terkait. Dalam contoh untuk fungsi mouse di perangkat komposit, ketika pengguna menggoyangkan mouse yang dalam keadaan menganggur, fungsi mouse mengirimkan sinyal resume ke host. Pada host, tumpukan driver USB mendeteksi fungsi mana yang terbangun dan menyebarkan pemberitahuan ke driver klien dari fungsi yang sesuai. Driver klien kemudian dapat membangunkan fungsi dan memasuki status kerja.

Untuk driver klien, langkah-langkah untuk mengirim fungsi untuk menangguhkan status dan membangunkan fungsi mirip dengan driver perangkat fungsi tunggal yang mengirim seluruh perangkat ke status ditangguhkan. Prosedur berikut ini meringkas langkah-langkah tersebut.

  1. Deteksi kapan fungsi terkait dalam status menganggur.
  2. Kirim paket permintaan I/O diam (IRP).
  3. Kirim permintaan untuk mempersenjatai fungsinya untuk bangun jarak jauh dengan mengirim paket permintaan I/O wait-wake (IRP).
  4. Transisi fungsi ke status daya rendah dengan mengirim runtime integrasi daya Dx (D2 atau D3).

Untuk informasi selengkapnya tentang langkah-langkah sebelumnya, lihat "Mengirim USB Idle Request IRP" di USB Selective Suspend. Driver komposit membuat objek perangkat fisik (PDO) untuk setiap fungsi di perangkat komposit dan menangani permintaan daya yang dikirim oleh driver klien (FDO tumpukan perangkat fungsi). Agar driver klien berhasil masuk dan keluar dari status ditangguhkan untuk fungsinya, driver komposit harus mendukung fungsi menangguhkan dan fitur bangun jarak jauh, dan memproses permintaan daya yang diterima.

Dalam Windows 8, tumpukan driver USB untuk perangkat USB 3.0 mendukung fitur tersebut. Selain itu, fungsi menangguhkan dan implementasi bangun jarak jauh fungsi telah ditambahkan ke driver induk generik USB yang disediakan Microsoft (Usbccgp.sys), yang merupakan driver komposit default Windows. Jika Anda menulis driver komposit kustom, driver Anda harus menangani permintaan yang terkait dengan permintaan penangguhan fungsi dan bangun jarak jauh, sesuai prosedur berikut.

Langkah 1: Tentukan apakah tumpukan driver USB mendukung fungsi ditangguhkan

Dalam rutinitas perangkat mulai (IRP_MN_START_DEVICE) driver komposit Anda, lakukan langkah-langkah berikut:

  1. Panggil rutinitas USBD_QueryUsbCapability untuk menentukan apakah tumpukan driver USB yang mendasar mendukung kemampuan penangguhan fungsi. Panggilan memerlukan handel USBD valid yang Anda peroleh dalam panggilan sebelumnya ke rutinitas USBD_CreateHandle .

Panggilan yang berhasil ke USBD_QueryUsbCapability menentukan apakah tumpukan driver USB yang mendasar mendukung fungsi ditangguhkan. Panggilan dapat mengembalikan kode kesalahan yang menunjukkan bahwa tumpukan driver USB tidak mendukung fungsi ditangguhkan atau perangkat yang terpasang bukan perangkat multifungsi USB 3.0.

  1. Jika panggilan USBD_QueryUsbCapability menunjukkan bahwa fungsi yang ditangguhkan didukung, daftarkan perangkat komposit dengan tumpukan driver USB yang mendasar. Untuk mendaftarkan perangkat komposit, Anda harus mengirim permintaan kontrol I/O IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE . Untuk informasi selengkapnya tentang permintaan ini, lihat Cara Mendaftarkan Perangkat Komposit.

Permintaan pendaftaran menggunakan struktur REGISTER_COMPOSITE_DEVICE untuk menentukan informasi tersebut tentang driver komposit. Pastikan Anda mengatur CapabilityFunctionSuspend ke 1 untuk menunjukkan bahwa driver komposit mendukung fungsi ditangguhkan.

Untuk contoh kode yang menunjukkan cara menentukan apakah tumpukan driver USB mendukung fungsi ditangguhkan, lihat USBD_QueryUsbCapability.

Langkah 2: Menangani IRP menganggur

Driver klien dapat mengirim IRP menganggur (lihat IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Permintaan dikirim setelah driver klien mendeteksi status menganggur untuk fungsi tersebut. IRP berisi pointer untuk rutinitas penyelesaian panggilan balik (disebut idle callback) yang diimplementasikan oleh driver klien. Dalam panggilan balik diam, klien melakukan tugas, seperti membatalkan transfer I/O yang tertunda, tepat sebelum mengirim fungsi ke status ditangguhkan.

Catatan

Mekanisme IRP menganggur bersifat opsional untuk driver klien perangkat USB 3.0. Namun, sebagian besar driver klien ditulis untuk mendukung perangkat USB 2.0 dan USB 3.0. Untuk mendukung perangkat USB 2.0, driver harus mengirim IRP menganggur, karena driver komposit bergantung pada IRP tersebut untuk melacak status daya setiap fungsi. Jika semua fungsi diam, driver komposit mengirim seluruh perangkat untuk menangguhkan status.

Setelah menerima IRP diam dari driver klien, driver komposit harus segera memanggil panggilan balik menganggur untuk memberi tahu driver klien bahwa driver klien dapat mengirim fungsi untuk menangguhkan status.

Langkah 3: Mengirim permintaan untuk pemberitahuan bangun jarak jauh

Driver klien dapat mengirimkan permintaan untuk mempersenjatai fungsinya untuk bangun jarak jauh dengan mengirimkan IRP IRP_MJ_POWER dengan kode fungsi minor yang diatur ke IRP_MN_WAIT_WAKE (wait-wake IRP). Driver klien mengirimkan permintaan ini hanya jika driver ingin memasuki status kerja sebagai akibat dari peristiwa pengguna.

Setelah menerima IRP wait-wake, driver komposit harus mengirim permintaan kontrol I/O IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION ke tumpukan driver USB. Permintaan memungkinkan tumpukan driver USB untuk memberi tahu driver komposit ketika tumpukan menerima pemberitahuan tentang sinyal resume. IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION menggunakan struktur REQUEST_REMOTE_WAKE_NOTIFICATION untuk menentukan parameter permintaan. Salah satu nilai yang harus ditentukan driver komposit adalah handel fungsi untuk fungsi yang dipersenjatai untuk bangun jarak jauh. Driver komposit memperoleh handel tersebut dalam permintaan sebelumnya untuk mendaftarkan perangkat komposit dengan tumpukan driver USB. Untuk informasi selengkapnya tentang permintaan pendaftaran driver komposit, lihat Cara Mendaftarkan Perangkat Komposit.

Dalam IRP untuk permintaan, driver komposit memasok pointer ke rutinitas penyelesaian (bangun jarak jauh), yang diimplementasikan oleh driver komposit.

Contoh kode berikut menunjukkan cara mengirim permintaan bangun jarak jauh.

/*++

Description:
    This routine sends a IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION request
    to the USB driver stack. The IOCTL is completed by the USB driver stack
    when the function wakes up from sleep.

    Parameters:
    parentFdoExt: The device context associated with the FDO for the
    composite driver.

    functionPdoExt: The device context associated with the PDO (created by
    the composite driver) for the client driver.
--*/

VOID
SendRequestForRemoteWakeNotification(
    __inout PPARENT_FDO_EXT parentFdoExt,
    __inout PFUNCTION_PDO_EXT functionPdoExt
)

{
    PIRP                                irp;
    REQUEST_REMOTE_WAKE_NOTIFICATION    remoteWake;
    PIO_STACK_LOCATION                  nextStack;
    NTSTATUS                            status;

    // Allocate an IRP
    irp =  IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (irp)
    {

        //Initialize the USBDEVICE_REMOTE_WAKE_NOTIFICATION structure
        remoteWake.Version = 0;
        remoteWake.Size = sizeof(REQUEST_REMOTE_WAKE_NOTIFICATION);
        remoteWake.UsbdFunctionHandle = functionPdoExt->functionHandle;
        remoteWake.Interface = functionPdoExt->baseInterfaceNumber;

        nextStack = IoGetNextIrpStackLocation(irp);

        nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION;

        nextStack->Parameters.Others.Argument1 = &remoteWake;

        // Caller's completion routine will free the IRP when it completes.

        SetCompletionRoutine(functionPdoExt->debugLog,
                             parentFdoExt->fdo,
                             irp,
                             CompletionRemoteWakeNotication,
                             (PVOID)functionPdoExt,
                             TRUE, TRUE, TRUE);

        // Pass the IRP
        IoCallDriver(parentFdoExt->topDevObj, irp);

    }

    return;
}

Permintaan IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION diselesaikan oleh tumpukan driver USB selama proses bangun ketika menerima pemberitahuan tentang sinyal resume. Selama waktu itu, tumpukan driver USB juga memanggil rutinitas penyelesaian wake-up jarak jauh.

Driver komposit harus menjaga IRP tunggu-bangun tertunda dan mengantrenya untuk diproses nanti. Driver komposit harus menyelesaikan IRP tersebut ketika rutinitas penyelesaian bangun dari jarak jauh driver dipanggil oleh tumpukan driver USB.

Langkah 4: Kirim permintaan untuk mempersenjatai fungsi untuk bangun jarak jauh

Untuk mengirim fungsi ke status daya rendah, driver klien mengirimkan IRP_MN_SET_POWER IRP dengan permintaan untuk mengubah status daya perangkat Windows Driver Model (WDM) ke D2 atau D3. Biasanya, driver klien mengirim D2 IRP jika driver mengirim IRP wait-wake sebelumnya untuk meminta bangun jarak jauh. Jika tidak, driver klien mengirim D3 IRP.

Setelah menerima D2 IRP, driver komposit harus terlebih dahulu menentukan apakah IRP wait-wake tertunda dari permintaan sebelumnya yang dikirim oleh driver klien. Jika IRP tertunda, driver komposit harus mempersenjatai fungsi untuk bangun jarak jauh. Untuk melakukannya, driver komposit harus mengirim permintaan kontrol SET_FEATURE ke antarmuka pertama fungsi, untuk memungkinkan perangkat mengirim sinyal resume. Untuk mengirim permintaan kontrol, alokasikan struktur URB dengan memanggil USBD_UrbAllocate rutin dan panggil makro UsbBuildFeatureRequest untuk memformat URB untuk permintaan SET_FEATURE. Dalam panggilan, tentukan URB_FUNCTION_SET_FEATURE_TO_INTERFACE sebagai kode operasi dan USB_FEATURE_FUNCTION_SUSPEND sebagai pemilih fitur. Dalam parameter Indeks , atur Bit 1 dari byte yang paling signifikan. Nilai tersebut disalin ke bidang wIndex dalam paket penyiapan transfer.

Contoh berikut menunjukkan cara mengirim permintaan kontrol SET_FEATURE.

/*++

Routine Description:

Sends a SET_FEATURE for REMOTE_WAKEUP to the device using a standard control request.

Parameters:
parentFdoExt: The device context associated with the FDO for the
composite driver.

functionPdoExt: The device context associated with the PDO (created by
the composite driver) for the client driver.

Returns:

NTSTATUS code.

--*/
VOID
    NTSTATUS SendSetFeatureControlRequestToSuspend(
    __inout PPARENT_FDO_EXT parentFdoExt,
    __inout PFUNCTION_PDO_EXT functionPdoExt,
    )

{
    PURB                            urb
    PIRP                            irp;
    PIO_STACK_LOCATION              nextStack;
    NTSTATUS                        status;

    status = USBD_UrbAllocate(parentFdoExt->usbdHandle, &urb);

    if (!NT_SUCCESS(status))
    {
        //USBD_UrbAllocate failed.
        goto Exit;
    }

    //Format the URB structure.
    UsbBuildFeatureRequest (
        urb,
        URB_FUNCTION_SET_FEATURE_TO_INTERFACE, // Operation code
        USB_FEATURE_FUNCTION_SUSPEND,          // feature selector
        functionPdoExt->firstInterface,           // first interface of the function
        NULL);

    irp =  IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (!irp)
    {
        // IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;

        goto Exit;
    }

    nextStack = IoGetNextIrpStackLocation(irp);

    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;

    //  Attach the URB to the IRP.
    USBD_AssignUrbToIoStackLocation(nextStack, (PURB)urb);

    // Caller's completion routine will free the IRP when it completes.
    SetCompletionRoutine(functionPdoExt->debugLog,
        parentFdoExt->fdo,
        irp,
        CompletionForSuspendControlRequest,
        (PVOID)functionPdoExt,
        TRUE, TRUE, TRUE);


    // Pass the IRP
    IoCallDriver(parentFdoExt->topDevObj, irp);


Exit:
    if (urb)
    {
        USBD_UrbFree( parentFdoExt->usbdHandle, urb);
    }

    return status;

}

Driver komposit kemudian mengirim IRP D2 ke tumpukan driver USB. Jika semua fungsi lain dalam status ditangguhkan, tumpukan driver USB menangguhkan port dengan memanipulasi register port tertentu pada pengontrol.

Keterangan

Dalam contoh fungsi mouse, karena fitur bangun jarak jauh diaktifkan (lihat langkah 4), fungsi mouse menghasilkan sinyal resume pada upstream kawat ke pengontrol host saat pengguna menggoyangkan mouse. Pengontrol kemudian memberi tahu tumpukan driver USB dengan mengirim paket pemberitahuan yang berisi informasi tentang fungsi yang terbangun. Untuk informasi tentang Pemberitahuan Bangun Fungsi, lihat Gambar 8-17 dalam spesifikasi USB 3.0.

Setelah menerima paket pemberitahuan, tumpukan driver USB menyelesaikan permintaan IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION yang tertunda (lihat langkah 3) dan memanggil rutinitas panggilan balik penyelesaian (bangun jarak jauh) yang ditentukan dalam permintaan dan diimplementasikan oleh driver komposit. Ketika pemberitahuan mencapai driver komposit, pemberitahuan memberi tahu driver klien yang sesuai bahwa fungsi telah memasuki status kerja dengan menyelesaikan IRP tunggu-bangun yang telah dikirim driver klien sebelumnya.

Dalam rutinitas penyelesaian (bangun dari jarak jauh), driver komposit harus mengantre item kerja untuk menyelesaikan IRP tunggu-bangun yang tertunda. Untuk perangkat USB 3.0, driver komposit hanya membangunkan fungsi yang mengirim sinyal resume dan meninggalkan fungsi lain dalam status ditangguhkan. Mengantre item kerja memastikan kompatibilitas dengan implementasi yang ada untuk driver fungsi perangkat USB 2.0. Untuk informasi tentang mengantre item kerja, lihat IoQueueWorkItem.

Utas pekerja menyelesaikan IRP wait-wake dan memanggil rutinitas penyelesaian driver klien. Rutinitas penyelesaian kemudian mengirim D0 IRP untuk memasukkan fungsi dalam status kerja. Sebelum menyelesaikan IRP wait-wake, driver komposit harus memanggil PoSetSystemWake untuk menandai IRP wait-wake sebagai yang berkontribusi membangunkan sistem dari status ditangguhkan. Manajer daya mencatat peristiwa Pelacakan Peristiwa untuk Windows (ETW) (dapat dilihat di saluran sistem global) yang mencakup informasi tentang perangkat yang membangunkan sistem.