Bagikan melalui


Penangguhan selektif USB

Nota

Artikel ini untuk pengembang driver perangkat. Jika Anda mengalami kesulitan dengan perangkat USB, lihat Memperbaiki masalah USB-C di Windows

Fitur penangguhan selektif USB memungkinkan driver hub untuk menangguhkan port individual tanpa memengaruhi pengoperasian port lain di hub. Fungsionalitas ini berguna dalam komputer portabel karena membantu menghemat daya baterai. Banyak perangkat, seperti pemindai biometrik, hanya memerlukan daya sesekali. Menangguhkan perangkat tersebut, saat 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.

Penangguhan selektif diaktifkan secara default. Microsoft sangat menyarankan tidak menonaktifkan suspensi selektif.

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

Untuk menangguhkan perangkat USB secara selektif, ada dua mekanisme berbeda: permintaan IRP yang menganggur (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) dan IRP pengaturan daya (IRP_MN_SET_POWER). Mekanisme yang digunakan tergantung pada jenis perangkat: komposit atau nonkomposit.

Memilih mekanisme penangguhan selektif

Driver klien untuk antarmuka pada perangkat komposit yang mengaktifkan antarmuka untuk remote wake-up dengan wait wake IRP (IRP_MN_WAIT_WAKE), harus menggunakan mekanisme IRP permintaan idle (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) untuk menangguhkan perangkat secara selektif.

Untuk informasi tentang pemicu jarak jauh, lihat:

Bagian ini menjelaskan mekanisme penangguhan selektif Windows.

Mengirim permintaan IRP idle USB

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

Dalam rutinitas panggilan balik, driver klien harus membatalkan semua operasi I/O yang tertunda dan menunggu semua IRP 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 Menerapkan Rutinitas Panggilan Balik IRP Permintaan Menganggur USB.

Driver bus tidak menyelesaikan IRP permintaan siaga setelah memanggil rutin panggilan balik pemberitahuan siaga. ** Sebagai gantinya, pengemudi bus menahan permintaan idle IRP yang tertunda hingga salah satu kondisi berikut ini terpenuhi:

  • Sebuah IRP_MN_SURPRISE_REMOVAL atau IRP_MN_REMOVE_DEVICE IRP diterima. Ketika salah satu IRP ini diterima, IRP permintaan idle selesai dengan STATUS_CANCELLED.
  • Sopir bus menerima permintaan untuk menempatkan perangkat ke dalam status daya yang berfungsi (D0). Setelah menerima permintaan ini, sopir bus menyelesaikan permintaan tidak aktif IRP yang tertunda dengan "STATUS_SUCCESS".

Pembatasan berikut berlaku untuk penggunaan IRP permintaan yang menganggur.

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

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

  1. Alokasikan dan inisialisasi 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 idle (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. Atur rutinitas penyelesaian.

    Driver klien harus mengaitkan rutinitas penyelesaian dengan IRP permintaan idle. Untuk informasi selengkapnya tentang rutin penyelesaian pemberitahuan idle dan contoh kode, lihat Menerapkan Rutinitas Penyelesaian IRP Permintaan Idle USB.

    IoSetCompletionRoutine (irp,
        IdleNotificationRequestComplete,
        DeviceContext,
        TRUE,
        TRUE,
        TRUE);
    
  4. Simpan permintaan yang tidak aktif di perluasan 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 yang menerapkan permintaan idle yang dikirimkan ke driver bus. Situasi ini mungkin terjadi jika perangkat dihapus, menjadi aktif setelah diam dan mengirim permintaan diam, atau jika seluruh sistem beralih ke status daya sistem yang lebih rendah.

Driver klien membatalkan IRP yang sedang idle dengan menggunakan panggilan fungsi IoCancelIrp. Tabel berikut ini menjelaskan tiga skenario untuk membatalkan IRP menganggur dan menentukan tindakan yang harus diambil driver:

Skenario Mekanisme pembatalan permintaan tidak aktif
Driver klien membatalkan IRP tidak aktif dan rutinitas callback pemberitahuan USB yang menganggur belum dipanggil. Tumpukan driver USB menyelesaikan IRP menganggur. Karena perangkat tidak pernah meninggalkan D0, driver tidak mengubah status perangkat.
Driver klien membatalkan IRP menganggur, tumpukan driver USB memanggil rutinitas panggilan balik pemberitahuan USB yang menganggur, dan belum dikembalikan. Mungkin rutin panggilan balik pemberitahuan idle USB tetap dipanggil meskipun driver klien telah membatalkan 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 yang tidak aktif dan kemudian mengirimkan 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 menganggur tidak selesai sampai fungsi callback selesai dijalankan. Oleh karena itu, rutin panggilan balik tidak boleh menghalangi menunggu penyelesaian IRP idle yang dibatalkan.
Perangkat sudah dalam status daya rendah. Jika perangkat sudah dalam status daya rendah, driver klien dapat mengirim D0 IRP. Stack driver USB menyelesaikan IRP permintaan idle dengan STATUS_SUCCESS.

Atau, pengemudi dapat membatalkan IRP saat menganggur, menunggu tumpukan driver USB untuk menyelesaikan IRP tersebut, lalu mengirimkan IRP D0.

Rutinitas penyelesaian permintaan idle IRP USB

Dalam banyak kasus, pengemudi bus mungkin memanggil rutinitas penyelesaian IRP permintaan menganggur pengemudi. Jika situasi ini terjadi, pengemudi klien harus mendeteksi alasan mengapa pengemudi bus menyelesaikan IRP. Kode status yang dikembalikan dapat memberikan informasi ini. Jika kode status bukan STATUS_POWER_STATE_INVALID, pengendali harus mengatur perangkatnya ke D0 jika perangkat tidak dalam D0. Jika perangkat masih dalam keadaan siaga, driver dapat mengirim permintaan IRP siaga lainnya.

Nota

Rutin penyelesaian IRP untuk permintaan idle seharusnya tidak menghambat sementara menunggu permintaan daya D0 diselesaikan. 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 diam harus menginterpretasikan beberapa kode status umum:

Kode Status Deskripsi
STATUS_BERHASIL Menunjukkan bahwa perangkat tidak seharusnya ditangguhkan lagi. Namun, driver harus memverifikasi bahwa perangkat mereka menyala, dan memasukkannya ke dalam D0 jika perangkat belum berada di D0.
STATUS_DIBATALKAN pengemudi bus melengkapi IRP permintaan penundaan dengan status dibatalkan dalam salah satu keadaan berikut:
  • Pengandar perangkat membatalkan IRP.
  • Diperlukan perubahan status daya sistem.
Status_Tenaga_Tidak_Sah (note: keep the original if needed in a technical or user interface context) Menunjukkan bahwa driver perangkat meminta status daya D3 untuk perangkatnya. Ketika permintaan ini terjadi, pengemudi bus menyelesaikan semua IRP diam yang tertunda dengan STATUS_POWER_STATE_INVALID.
STATUS_PERANGKAT_SIBUK Menunjukkan bahwa pengemudi bus sudah memegang permintaan penangguhan IRP yang masih menunggu untuk perangkat. Hanya satu IRP tidak aktif yang dapat menunggu pada satu waktu untuk perangkat tertentu. Mengirimkan beberapa IRP permintaan menganggur adalah kesalahan pada bagian pemilik kebijakan daya. Penulis perangkat lunak driver memperbaiki kesalahan.

Contoh kode berikut menunjukkan implementasi sampel untuk rutin penyelesaian permintaan idle.

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

Rutin panggilan balik pemberitahuan USB yang tidak aktif

Driver bus (baik instans driver hub atau driver induk generik) menentukan kapan aman untuk menangguhkan anak-anak perangkatnya. Jika itu benar, ini memanggil rutinitas panggilan balik pemberitahuan idle yang disediakan oleh driver klien untuk 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 pemanggilan balik pemberitahuan saat perangkat tidak aktif.

  • Minta IRP IRP_MN_WAIT_WAKE untuk perangkat jika perangkat harus diaktifkan untuk bangun 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 (ditentukan dalam wdm.h; ntddk.h).

Baik driver hub maupun USB Generic Parent Driver (Usbccgp.sys) memanggil rutinitas callback pemberitahuan idle di IRQL = PASSIVE_LEVEL. Rutinitas panggilan balik kemudian dapat diblokir saat menunggu permintaan perubahan status daya selesai.

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

Terdapat pembatasan berikut untuk fungsi panggilan balik notifikasi permintaan siaga.

  • Driver perangkat dapat memulai transisi status daya perangkat dari D0 ke D2 dalam rutinitas panggilan balik pemberitahuan menganggur, tetapi tidak ada transisi status daya lainnya 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 rutin pemberitahuan panggilan balik siaga.

Mengaktifkan perangkat untuk bangun dalam rutinitas callback pemberitahuan siaga

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

USB ditangguhkan secara global

Spesifikasi USB 2.0 mendefinisikan penangguhan global sebagai penangguhan seluruh bus oleh pengontrol host USB dengan menghentikan semua aktivitas USB pada bus, termasuk paket start-of-frame. Perangkat hilir yang belum ditangguhkan mendeteksi status menganggur pada port hulu mereka dan memasuki status ditangguhkan secara otomatis. 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 penghentian sementara secara global

Driver hub USB 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 secara selektif. Tumpukan driver USB memperlakukan perangkat sebagai Diam setiap kali perangkat berada dalam status perangkat WDM D1, D2, atau D3.