Penangguhan selektif USB

Catatan

Artikel ini untuk pengembang driver perangkat. Jika Anda mengalami kesulitan dengan perangkat USB, silakan lihat Memecahkan masalah USB umum

Fitur penangguhan selektif USB memungkinkan driver hub untuk menangguhkan port individual tanpa memengaruhi pengoperasian port lain di hub. Suspensi selektif perangkat USB sangat berguna di komputer portabel karena membantu menghemat daya baterai. Banyak perangkat, seperti pembaca sidik jari dan jenis pemindai biometrik lainnya, hanya memerlukan daya sesekali. Menangguhkan perangkat tersebut, saat perangkat tidak digunakan, mengurangi konsumsi daya secara keseluruhan. Lebih penting lagi, perangkat apa pun yang tidak ditangguhkan secara selektif dapat mencegah pengontrol host USB menonaktifkan jadwal transfernya, yang berada di memori sistem. Transfer akses memori langsung (DMA) oleh pengontrol host ke penjadwal dapat mencegah prosesor sistem memasuki status tidur yang lebih dalam, seperti C3.

Ada dua mekanisme berbeda untuk menangguhkan perangkat USB secara selektif: IRP permintaan menganggur (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) dan mengatur runtime integrasi daya (IRP_MN_SET_POWER). Mekanisme yang digunakan tergantung pada sistem operasi dan jenis perangkat: komposit atau non-komposit.

Memilih mekanisme penangguhan selektif

Driver klien, untuk antarmuka pada perangkat komposit, yang memungkinkan antarmuka untuk bangun jarak jauh dengan IRP tunggu (IRP_MN_WAIT_WAKE), harus menggunakan mekanisme IRP permintaan menganggur (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) untuk menangguhkan perangkat secara selektif.

Untuk informasi tentang bangun jarak jauh, lihat:

Versi sistem operasi Windows menentukan cara driver untuk perangkat non-komposit memungkinkan penangguhan selektif.

  • Windows XP: Pada Windows XP semua driver klien harus menggunakan IRP permintaan menganggur (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) untuk mematikan perangkat mereka. Driver klien tidak boleh menggunakan runtime integrasi daya WDM untuk menangguhkan perangkat mereka secara selektif. Melakukannya mencegah perangkat lain menangguhkan secara selektif.
  • Windows Vista dan versi Windows yang lebih baru: Penulis driver memiliki lebih banyak pilihan untuk mematikan perangkat di Windows Vista dan di versi Windows yang lebih baru. Meskipun Windows Vista mendukung mekanisme IRP permintaan menganggur Windows, driver tidak diperlukan untuk menggunakannya.

Tabel berikut menunjukkan skenario yang memerlukan penggunaan IRP permintaan menganggur dan yang dapat menggunakan IRP daya WDM untuk menangguhkan perangkat USB:

Versi Windows Fungsi pada perangkat komposit, dipersenjatai untuk bangun Fungsi pada perangkat komposit, tidak dipersenjatai untuk bangun Perangkat USB antarmuka tunggal
Windows 7 Menggunakan IRP permintaan menganggur Menggunakan IRP daya WDM Menggunakan IRP daya WDM
Windows Server 2008 Menggunakan IRP permintaan menganggur Menggunakan IRP daya WDM Menggunakan IRP daya WDM
Windows Vista Menggunakan IRP permintaan menganggur Menggunakan IRP daya WDM Menggunakan IRP daya WDM
Windows Server 2003 Menggunakan IRP permintaan menganggur Menggunakan IRP permintaan menganggur Menggunakan IRP permintaan menganggur
Windows XP Menggunakan IRP permintaan menganggur Menggunakan IRP permintaan menganggur Menggunakan IRP permintaan menganggur

Bagian ini menjelaskan mekanisme penangguhan selektif Windows.

Mengirim IRP permintaan diam USB

Ketika perangkat diam, driver klien memberi tahu pengemudi bus dengan mengirim IRP permintaan menganggur (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Setelah driver bus menentukan bahwa aman untuk menempatkan perangkat dalam keadaan daya rendah, ia memanggil rutinitas panggilan balik yang diteruskan driver perangkat klien ke tumpukan dengan IRP permintaan menganggur.

Dalam rutinitas panggilan balik, driver klien harus membatalkan semua operasi I/O yang tertunda dan menunggu semua RUNP I/O USB selesai. Kemudian dapat mengeluarkan permintaan IRP_MN_SET_POWER untuk mengubah status daya perangkat WDM ke D2. Rutinitas panggilan balik harus menunggu permintaan D2 selesai sebelum kembali. Untuk informasi selengkapnya tentang rutinitas panggilan balik pemberitahuan menganggur, lihat "Rutinitas Panggilan Balik Pemberitahuan Menganggur USB".

Pengemudi bus tidak menyelesaikan IRP permintaan menganggur setelah memanggil rutinitas panggilan balik pemberitahuan menganggur. Sebaliknya, sopir bus menahan permintaan menganggur IRP tertunda sampai salah satu kondisi berikut ini benar:

  • IRPIRP_MN_SUPRISE_REMOVAL atau IRP_MN_REMOVE_DEVICE diterima. Ketika salah satu IRP ini diterima, IRP permintaan menganggur selesai dengan STATUS_CANCELLED.
  • Sopir bus menerima permintaan untuk menempatkan perangkat ke dalam status daya yang berfungsi (D0). Setelah menerima permintaan ini, pengemudi bus menyelesaikan IRP permintaan menganggur yang tertunda dengan STATUS_SUCCESS.

Pembatasan berikut berlaku untuk penggunaan IRP permintaan menganggur:

  • Driver harus dalam status daya perangkat D0 saat mengirim IRP permintaan menganggur.
  • Driver harus mengirim hanya satu permintaan menganggur IRP per tumpukan perangkat.

Kode contoh WDM berikut mengilustrasikan langkah-langkah yang diambil driver perangkat untuk mengirim IRP permintaan menganggur USB. Pemeriksaan kesalahan telah dihilangkan dalam contoh kode berikut.

  1. Mengalokasikan dan menginisialisasi IRP IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION

    irp = IoAllocateIrp (DeviceContext->TopOfStackDeviceObject->StackSize, FALSE);
    nextStack = IoGetNextIrpStackLocation (irp);
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
    nextStack->Parameters.DeviceIoControl.InputBufferLength =
    sizeof(struct _USB_IDLE_CALLBACK_INFO);
    
  2. Alokasikan dan inisialisasi struktur informasi permintaan menganggur (USB_IDLE_CALLBACK_INFO).

    idleCallbackInfo = ExAllocatePool (NonPagedPool,
    sizeof(struct _USB_IDLE_CALLBACK_INFO));
    idleCallbackInfo->IdleCallback = IdleNotificationCallback;
    // Put a pointer to the device extension in member IdleContext
    idleCallbackInfo->IdleContext = (PVOID) DeviceExtension;  
    nextStack->Parameters.DeviceIoControl.Type3InputBuffer =
    idleCallbackInfo;
    
  3. Tetapkan rutinitas penyelesaian.

    Driver klien harus mengaitkan rutinitas penyelesaian dengan IRP permintaan menganggur. Untuk informasi selengkapnya tentang rutinitas penyelesaian pemberitahuan diam dan contoh kode, lihat "Rutinitas Penyelesaian IRP Permintaan Menganggur USB".

    IoSetCompletionRoutine (irp,
        IdleNotificationRequestComplete,
        DeviceContext,
        TRUE,
        TRUE,
        TRUE);
    
  4. Simpan permintaan menganggur di ekstensi perangkat.

    deviceExtension->PendingIdleIrp = irp;
    
    
  5. Kirim permintaan Menganggur ke driver induk.

    ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
    

Membatalkan permintaan diam USB

Dalam keadaan tertentu, driver perangkat mungkin perlu membatalkan IRP permintaan menganggur yang telah dikirimkan ke pengemudi bus. Ini mungkin terjadi jika perangkat dihapus, menjadi aktif setelah diam dan mengirim permintaan menganggur, atau jika seluruh sistem beralih ke status daya sistem yang lebih rendah.

Driver klien membatalkan IRP menganggur dengan memanggil IoCancelIrp. Tabel berikut ini menjelaskan tiga skenario untuk membatalkan IRP menganggur dan menentukan tindakan yang harus diambil driver:

Skenario Mekanisme pembatalan permintaan menganggur
Driver klien telah membatalkan IRP diam dan tumpukan driver USB belum disebut "Rutinitas Panggilan Balik Pemberitahuan DIAM USB". Tumpukan driver USB menyelesaikan IRP menganggur. Karena perangkat tidak pernah meninggalkan D0, driver tidak mengubah status perangkat.
Driver klien telah membatalkan IRP menganggur, tumpukan driver USB telah memanggil rutinitas panggilan balik pemberitahuan menganggur USB, dan belum kembali. Ada kemungkinan bahwa rutinitas panggilan balik pemberitahuan diam USB dipanggil meskipun driver klien telah memanggil pembatalan pada IRP. Dalam hal ini, rutinitas panggilan balik driver klien masih harus mematikan perangkat dengan mengirim perangkat ke status daya yang lebih rendah secara sinkron.

Ketika perangkat dalam status daya yang lebih rendah, driver klien kemudian dapat mengirim permintaan D0 .

Atau, driver dapat menunggu tumpukan driver USB untuk menyelesaikan IRP diam dan kemudian mengirim D0 IRP.

Jika rutinitas panggilan balik tidak dapat menempatkan perangkat ke dalam status daya rendah karena memori yang tidak mencukupi untuk mengalokasikan IRP daya, itu harus membatalkan IRP menganggur dan segera keluar. IRP diam tidak akan selesai sampai rutinitas panggilan balik kembali; oleh karena itu, rutinitas panggilan balik tidak boleh memblokir menunggu IRP menganggur yang dibatalkan selesai.
Perangkat sudah dalam keadaan daya rendah. Jika perangkat sudah dalam status daya rendah, driver klien dapat mengirim D0 IRP. Tumpukan driver USB menyelesaikan IRP permintaan menganggur dengan STATUS_SUCCESS.

Atau, driver dapat membatalkan IRP diam, menunggu tumpukan driver USB untuk menyelesaikan IRP yang menganggur, lalu mengirim D0 IRP.

Rutinitas penyelesaian IRP permintaan diam USB

Dalam banyak kasus, sopir bus mungkin memanggil rutinitas penyelesaian IRP permintaan menganggur pengemudi. Jika ini terjadi, driver klien harus mendeteksi mengapa driver bus menyelesaikan IRP. Kode status yang dikembalikan dapat memberikan informasi ini. Jika kode status tidak STATUS_POWER_STATE_INVALID, driver harus meletakkan perangkatnya di D0 jika perangkat belum ada di D0. Jika perangkat masih menganggur, driver dapat mengirimkan IRP permintaan menganggur lainnya.

Catatan

Rutinitas penyelesaian IRP permintaan menganggur tidak boleh memblokir menunggu permintaan daya D0 selesai. Rutinitas penyelesaian dapat dipanggil dalam konteks IRP daya oleh driver hub, dan memblokir IRP daya lain dalam rutinitas penyelesaian dapat menyebabkan kebuntuan.

Daftar berikut menunjukkan bagaimana rutinitas penyelesaian untuk permintaan menganggur harus menginterpretasikan beberapa kode status umum:

Kode Status Deskripsi
STATUS_SUCCESS Menunjukkan bahwa perangkat tidak boleh lagi ditangguhkan. Namun, driver harus memverifikasi bahwa perangkat mereka didukung, dan memasukkannya ke D0 jika belum ada di D0.
STATUS_CANCELLED Pengemudi bus menyelesaikan IRP permintaan menganggur dengan STATUS_CANCELLED dalam salah satu keadaan berikut:
  • Driver perangkat membatalkan IRP.
  • Diperlukan perubahan status daya sistem.
  • Pada Windows XP, driver perangkat untuk salah satu perangkat USB yang terhubung gagal menempatkan perangkatnya di D2 saat menjalankan rutinitas panggilan balik permintaan menganggurnya. Akibatnya, pengemudi bus menyelesaikan semua runtime integrasi permintaan menganggur yang tertunda.
STATUS_POWER_STATE_INVALID Menunjukkan bahwa driver perangkat meminta status daya D3 untuk perangkatnya. Ketika ini terjadi, pengemudi bus menyelesaikan semua runtime integrasi diam yang tertunda dengan STATUS_POWER_STATE_INVALID.
STATUS_DEVICE_BUSY Menunjukkan bahwa driver bus sudah menahan permintaan menganggur IRP yang tertunda untuk perangkat. Hanya satu IRP menganggur yang dapat tertunda pada satu waktu untuk perangkat tertentu. Mengirimkan beberapa IRP permintaan menganggur adalah kesalahan pada bagian pemilik kebijakan daya, dan harus ditangani oleh penulis driver.

Contoh kode berikut menunjukkan implementasi sampel untuk rutinitas penyelesaian permintaan menganggur.

/*Routine Description:

  Completion routine for idle notification IRP

Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet
    DeviceExtension - pointer to device extension

Return Value:

    NT status value

--*/

NTSTATUS
IdleNotificationRequestComplete(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PDEVICE_EXTENSION DeviceExtension
    )
{
    NTSTATUS                ntStatus;
    POWER_STATE             powerState;
    PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;

    ntStatus = Irp->IoStatus.Status;

    if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED)
    {

        //Idle IRP completes with error.

        switch(ntStatus)
        {

        case STATUS_INVALID_DEVICE_REQUEST:

            //Invalid request.

            break;

        case STATUS_CANCELLED:

            //1. The device driver canceled the IRP.
            //2. A system power state change is required.

            break;

        case STATUS_POWER_STATE_INVALID:

            // Device driver requested a D3 power state for its device
            // Release the allocated resources.

            goto IdleNotificationRequestComplete_Exit;

        case STATUS_DEVICE_BUSY:

            //The bus driver already holds an idle IRP pending for the device.

            break;

        default:
            break;

        }


        // If IRP completes with error, issue a SetD0

        //Increment the I/O count because
        //a new IRP is dispatched for the driver.
        //This call is not shown.

        powerState.DeviceState = PowerDeviceD0;

        // Issue a new IRP
        PoRequestPowerIrp (
            DeviceExtension->PhysicalDeviceObject,
            IRP_MN_SET_POWER,
            powerState,
            (PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc,
            DeviceExtension,
            NULL);
    }

IdleNotificationRequestComplete_Exit:

    idleCallbackInfo = DeviceExtension->IdleCallbackInfo;

    DeviceExtension->IdleCallbackInfo = NULL;

    DeviceExtension->PendingIdleIrp = NULL;

    InterlockedExchange(&DeviceExtension->IdleReqPend, 0);

    if(idleCallbackInfo)
    {
        ExFreePool(idleCallbackInfo);
    }

    DeviceExtension->IdleState = IdleComplete;

    // Because the IRP was created using IoAllocateIrp,
    // the IRP needs to be released by calling IoFreeIrp.
    // Also return STATUS_MORE_PROCESSING_REQUIRED so that
    // the kernel does not reference this.

    IoFreeIrp(Irp);

    KeSetEvent(&DeviceExtension->IdleIrpCompleteEvent, IO_NO_INCREMENT, FALSE);

    return STATUS_MORE_PROCESSING_REQUIRED;
}

Rutinitas panggilan balik pemberitahuan menganggur USB

Driver bus (baik instans driver hub atau driver induk generik) menentukan kapan aman untuk menangguhkan anak-anak perangkatnya. Jika ya, ini memanggil rutinitas panggilan balik pemberitahuan menganggur yang disediakan oleh driver klien setiap anak.

Prototipe fungsi untuk USB_IDLE_CALLBACK adalah sebagai berikut:

typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);

Driver perangkat harus mengambil tindakan berikut dalam rutinitas panggilan balik pemberitahuan menganggurnya:

  • Minta IRP IRP_MN_WAIT_WAKE untuk perangkat jika perangkat perlu dipersenjatai untuk wakeup jarak jauh.
  • Batalkan semua I/O dan siapkan perangkat untuk masuk ke status daya yang lebih rendah.
  • Letakkan perangkat dalam status tidur WDM dengan memanggil PoRequestPowerIrp dengan parameter PowerState yang diatur ke nilai enumerator PowerDeviceD2 (didefinisikan dalam wdm.h; ntddk.h). Di Windows XP, driver tidak boleh meletakkan perangkatnya di PowerDeviceD3, bahkan jika perangkat tidak dipersenjatai untuk bangun jarak jauh.

Di Windows XP, driver harus mengandalkan rutinitas panggilan balik pemberitahuan menganggur untuk menangguhkan perangkat secara selektif. Jika driver yang berjalan di Windows XP menempatkan perangkat dalam status daya yang lebih rendah secara langsung tanpa menggunakan rutinitas panggilan balik pemberitahuan menganggur, ini mungkin mencegah perangkat lain di pohon perangkat USB ditangguhkan.

Baik driver hub maupun USB Generic Parent Driver (Usbccgp.sys) memanggil rutinitas panggilan balik pemberitahuan menganggur di IRQL = PASSIVE_LEVEL. Ini memungkinkan rutinitas panggilan balik untuk memblokir saat menunggu permintaan perubahan status daya selesai.

Rutinitas panggilan balik hanya dipanggil saat sistem berada di S0 dan perangkat berada di D0.

Pembatasan berikut berlaku untuk rutinitas panggilan balik pemberitahuan permintaan menganggur:

  • Driver perangkat dapat memulai transisi status daya perangkat dari D0 ke D2 dalam rutinitas panggilan balik pemberitahuan menganggur, tetapi tidak ada transisi status daya lain yang diizinkan. Secara khusus, driver tidak boleh mencoba mengubah perangkatnya ke D0 saat menjalankan rutinitas panggilan baliknya.
  • Driver perangkat tidak boleh meminta lebih dari satu IRP daya dari dalam rutinitas panggilan balik pemberitahuan menganggur.

Mempersenjatai perangkat untuk bangun dalam rutinitas panggilan balik pemberitahuan menganggur

Rutinitas panggilan balik pemberitahuan menganggur harus menentukan apakah perangkatnya memiliki permintaan IRP_MN_WAIT_WAKE tertunda. Jika tidak ada permintaan IRP_MN_WAIT_WAKE yang tertunda, rutinitas panggilan balik harus mengirimkan permintaan IRP_MN_WAIT_WAKE sebelum menangguhkan perangkat. Untuk informasi selengkapnya tentang mekanisme tunggu bangun, lihat Mendukung Perangkat yang Memiliki Kemampuan WakeUp.

USB global ditangguhkan

Spesifikasi USB 2.0 mendefinisikan penangguhan global sebagai penangguhan seluruh bus di belakang pengontrol host USB dengan menghentikan semua lalu lintas USB di bus, termasuk paket start-of-frame. Perangkat hilir yang belum ditangguhkan mendeteksi status Menganggur pada port upstream mereka dan memasukkan status ditangguhkan sendiri. Windows tidak menerapkan penangguhan global dengan cara ini. Windows selalu secara selektif menangguhkan setiap perangkat USB di belakang pengontrol host USB sebelum menghentikan semua lalu lintas USB di bus.

Kondisi untuk penangguhan global di Windows 7

Windows 7 lebih agresif tentang menangguhkan hub USB secara selektif daripada Windows Vista. Driver hub USB Windows 7 akan secara selektif menangguhkan hub apa pun di mana semua perangkat yang terpasang berada dalam status daya perangkat D1, D2, atau D3 . Seluruh bus memasuki penangguhan global setelah semua hub USB ditangguhkan selektif. Tumpukan driver USB Windows 7 memperlakukan perangkat sebagai Diam setiap kali perangkat berada dalam status perangkat WDM D1, D2, atau D3.

Kondisi untuk penangguhan global di Windows Vista

Persyaratan untuk melakukan penangguhan global lebih fleksibel di Windows Vista daripada di Windows XP.

Secara khusus, tumpukan USB memperlakukan perangkat sebagai Diam di Windows Vista setiap kali perangkat berada dalam status perangkat WDM D1, D2, atau D3.

Diagram berikut mengilustrasikan skenario yang mungkin terjadi di Windows Vista.

Diagram yang mengilustrasikan penangguhan global di Windows Vista.

Diagram ini menggambarkan situasi yang mirip dengan yang digambarkan di bagian "Kondisi untuk global ditangguhkan di Windows XP". Namun, dalam hal ini Perangkat 3 memenuhi syarat sebagai perangkat menganggur. Karena semua perangkat menganggur, driver bus dapat memanggil rutinitas panggilan balik pemberitahuan menganggur yang terkait dengan IRP permintaan menganggur yang tertunda. Setiap driver menangguhkan perangkatnya dan driver bus menangguhkan pengontrol host USB segera setelah aman untuk melakukannya.

Pada Windows Vista semua perangkat USB non-hub harus berada di D1, D2, atau D3 sebelum penangguhan global dimulai, pada saat itu semua hub USB, termasuk hub akar, ditangguhkan. Ini berarti bahwa setiap driver klien USB yang tidak mendukung penangguhan selektif, mencegah bus memasuki penangguhan global.

Kondisi untuk penangguhan global di Windows XP

Untuk memaksimalkan penghematan daya pada Windows XP, penting bahwa setiap driver perangkat menggunakan IRP permintaan menganggur untuk menangguhkan perangkatnya. Jika satu driver menangguhkan perangkatnya dengan permintaan IRP_MN_SET_POWER alih-alih IRP permintaan menganggur, itu dapat mencegah perangkat lain menangguhkan.

Diagram berikut mengilustrasikan skenario yang mungkin terjadi di Windows XP.

Diagram yang mengilustrasikan penangguhan global di Windows XP.

Dalam gambar ini, perangkat 3 dalam status daya D3 dan tidak memiliki permintaan menganggur IRP tertunda. Perangkat 3 tidak memenuhi syarat sebagai perangkat menganggur untuk tujuan penangguhan global di Windows XP, karena tidak memiliki permintaan menganggur IRP yang tertunda dengan induknya. Ini mencegah pengemudi bus memanggil rutinitas panggilan balik permintaan menganggur yang terkait dengan driver perangkat lain di pohon.

Mengaktifkan penangguhan selektif

Penangguhan selektif dinonaktifkan untuk versi peningkatan Microsoft Windows XP. Ini diaktifkan untuk penginstalan bersih Windows XP, Windows Vista, dan versi Windows yang lebih baru.

Untuk mengaktifkan dukungan penangguhan selektif untuk hub akar tertentu dan perangkat turunannya, pilih kotak centang pada tab Manajemen Daya untuk hub root USB di Manajer Perangkat.

Atau, Anda dapat mengaktifkan atau menonaktifkan penangguhan selektif dengan mengatur nilai HcDisableSelectiveSuspend di bawah kunci perangkat lunak driver port USB. Nilai 1 menonaktifkan penangguhan selektif. Nilai 0 memungkinkan penangguhan selektif.

Misalnya, baris berikut di Usbport.inf menonaktifkan penangguhan selektif untuk pengontrol Hydra OHCI:

[OHCI_NOSS.AddReg.NT]
HKR,,"HcDisableSelectiveSuspend",0x00010001,1

Driver klien tidak boleh mencoba menentukan apakah penangguhan selektif diaktifkan sebelum mengirim permintaan menganggur. Mereka harus mengirimkan permintaan menganggur setiap kali perangkat diam. Jika permintaan menganggur gagal, driver klien harus mengatur ulang timer diam dan mencoba kembali.