共用方式為


USB 選擇性暫停

注意

本文適用於設備驅動器開發人員。 如果您遇到 USB 裝置的困難,請參閱 針對常見的 USB 問題進行疑難解答

USB 選擇性暫停功能可讓中樞驅動程式暫停個別埠,而不會影響中樞上其他埠的作業。 選擇性暫停 USB 裝置在可攜式電腦中特別有用,因為它有助於節省電池電力。 許多裝置,例如指紋讀取器和其他類型的生物特徵辨識掃描器,只需要間歇性電源。 當裝置未使用時暫停這類裝置,可降低整體耗電量。 更重要的是,未選擇性暫停的任何裝置都可能會防止 USB 主機控制器停用其傳輸排程,而該排程位於系統記憶體中。 主機控制器對排程器的直接記憶體存取 (DMA) 傳輸,可能會防止系統的處理器進入更深入的睡眠狀態,例如 C3。

選擇性暫停 USB 裝置有兩種不同的機制:閑置要求 IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) ,並設定電源 IRP (IRP_MN_SET_POWER) 。 要使用的機制取決於操作系統和裝置類型:複合或非複合。

選取選擇性暫停機制

複合裝置上介面的用戶端驅動程式,可啟用遠端喚醒與等候喚醒 IRP (IRP_MN_WAIT_WAKE) 的介面,必須使用閑置要求 IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) 機制來選擇性地暫停裝置。

如需遠端喚醒的相關信息,請參閱:

Windows 作業系統版本會決定非複合裝置驅動程式啟用選擇性暫停的方式。

  • Windows XP:在 Windows XP 上,所有用戶端驅動程式都必須使用閑置要求 IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) 來關閉其裝置電源。 用戶端驅動程式不得使用 WDM 電源 IRP 選擇性地暫停其裝置。 這樣做可防止其他裝置選擇性地暫停。
  • Windows Vista 和更新版本的 Windows:驅動程式寫入器有更多選擇可關閉 Windows Vista 中的裝置,以及更新版本的 Windows。 雖然 Windows Vista 支援 Windows 閑置要求 IRP 機制,但不需要驅動程式即可使用它。

下表顯示需要使用閑置要求 IRP 的案例,以及可以使用 WDM 電源 IRP 暫停 USB 裝置的案例:

Windows 版本 複合裝置上的功能,用於喚醒 複合裝置上的功能,沒有喚醒功能 單一介面 USB 裝置
Windows 7 使用閑置要求 IRP 使用WDM電源 IRP 使用WDM電源 IRP
Windows Server 2008 使用閑置要求 IRP 使用WDM電源 IRP 使用WDM電源 IRP
Windows Vista 使用閑置要求 IRP 使用WDM電源 IRP 使用WDM電源 IRP
Windows Server 2003 使用閑置要求 IRP 使用閑置要求 IRP 使用閑置要求 IRP
Windows XP 使用閑置要求 IRP 使用閑置要求 IRP 使用閑置要求 IRP

本節說明 Windows 選擇性暫停機制。

傳送USB閑置要求IRP

當裝置閑置時,客戶端驅動程式會傳送閑置要求 IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) ,通知總線驅動程式。 在總線驅動程式判斷裝置處於低電源狀態安全之後,它會呼叫用戶端設備驅動器透過閑置要求 IRP 向下傳遞堆疊的回呼例程。

在回呼例程中,用戶端驅動程序必須取消所有擱置的 I/O 作業,並等候所有 USB I/O IRP 完成。 然後,它可以發出 IRP_MN_SET_POWER 要求,將WDM裝置電源狀態變更為 D2。 回呼例程必須在傳回之前等候 D2 要求完成。 For more information about the idle notification callback routine, see "USB Idle Notification Callback Routine".

在呼叫閑置通知回呼例程之後,總線驅動程式不會完成閑置要求 IRP。 相反地,總線驅動程式會保留閑置要求 IRP 擱置,直到下列其中一個條件成立為止:

  • 收到 IRP_MN_SUPRISE_REMOVALIRP_MN_REMOVE_DEVICE IRP 。 收到其中一個 IRP 時,閑置要求 IRP 會以 STATUS_CANCELLED 完成。
  • 總線驅動程式會收到要求,要求將裝置置於 D0) (運作中的電源狀態。 收到此要求總線驅動程式時,會使用 STATUS_SUCCESS 完成擱置的閑置要求 IRP。

下列限制適用於使用閑置要求 IRP:

  • 當傳送閑置要求 IRP 時,驅動程式必須處於裝置電源狀態 D0
  • 驅動程式每個裝置堆疊只能傳送一個閑置要求 IRP。

下列 WDM 範例程式代碼說明設備驅動器傳送 USB 閑置要求 IRP 所採取的步驟。 下列程式代碼範例中已省略錯誤檢查。

  1. 配置和初始化 IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION IRP

    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. (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. 設定完成例程。

    用戶端驅動程序必須將完成例程與閑置要求 IRP 產生關聯。 For more information about the idle notification completion routine and example code, see "USB Idle Request IRP Completion Routine".

    IoSetCompletionRoutine (irp,
        IdleNotificationRequestComplete,
        DeviceContext,
        TRUE,
        TRUE,
        TRUE);
    
  4. 將閑置要求儲存在裝置擴充功能中。

    deviceExtension->PendingIdleIrp = irp;
    
    
  5. 將閑置要求傳送至父驅動程式。

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

取消USB閑置要求

在某些情況下,設備驅動器可能需要取消已提交至總線驅動程序的閑置要求 IRP。 如果裝置已移除、在閑置後變成作用中並傳送閑置要求,或整個系統正在轉換為較低的系統電源狀態,就可能發生此情況。

用戶端驅動程式會藉由呼叫 IoCancelIrp來取消閒置的 IRP。 下表描述取消閑置 IRP 的三個案例,並指定驅動程式必須採取的動作:

案例 閑置要求取消機制
用戶端驅動程式已取消閑置 IRP,且 USB 驅動程式堆疊未呼叫「USB 閑置通知回呼例程」。 USB 驅動程式堆疊會完成閑置的 IRP。 因為裝置永遠不會離開 D0,所以驅動程式不會變更裝置狀態。
用戶端驅動程式已取消閑置 IRP,USB 驅動程式堆疊已呼叫 USB 閑置通知回呼例程,而且尚未傳回。 即使客戶端驅動程式已在 IRP 上叫用取消,仍可能會叫用 USB 閑置通知回呼例程。 在此情況下,客戶端驅動程式的回呼例程仍必須同步將裝置傳送至較低的電源狀態來關閉裝置電源。

當裝置處於較低的電源狀態時,用戶端驅動程序接著可以傳送 D0 要求。

或者,驅動程式可以等候 USB 驅動程式堆疊完成閒置 IRP,然後傳送 D0 IRP。

如果回呼例程因記憶體不足而無法讓裝置進入低電源狀態,而無法配置電源 IRP,它應該取消閑置的 IRP 並立即結束。 在回呼例程傳回之前,不會完成閑置的 IRP;因此,回呼例程不應該封鎖等候取消的閑置 IRP 完成。
裝置已處於低電源狀態。 如果裝置已經處於低電源狀態,用戶端驅動程式可以傳送 D0 IRP。 USB 驅動程式堆疊會使用 STATUS_SUCCESS 完成閑置要求 IRP。

或者,驅動程式可以取消閑置 IRP、等候 USB 驅動程式堆疊完成閒置 IRP,然後傳送 D0 IRP。

USB 閑置要求 IRP 完成例程

在許多情況下,公交車驅動程式可能會呼叫驅動程序的閑置要求 IRP 完成例程。 如果發生這種情況,用戶端驅動程式必須偵測總線驅動程式完成 IRP 的原因。 傳回的狀態代碼可以提供這項資訊。 如果狀態代碼未STATUS_POWER_STATE_INVALID,如果裝置尚未在 D0 中,驅動程式應該將其裝置放在 D0 中。 如果裝置仍然閑置,驅動程式可以提交另一個閑置要求 IRP。

注意

閑置要求 IRP 完成例程不應該封鎖等候 D0 電源要求完成。 完成例程可由中樞驅動程式在電源 IRP 的內容中呼叫,而完成例程中的另一個電源 IRP 封鎖可能會導致死結。

下列清單指出閑置要求的完成例程如何解譯一些常見的狀態代碼:

狀態碼 Description
STATUS_SUCCESS 表示裝置不應再暫停。 不過,驅動程式應該確認其裝置已開機,如果裝置尚未在 D0 中,請將它們放在 D0 中。
STATUS_CANCELLED 在下列任何情況下,總線驅動程式會完成閑置要求 IRP,並STATUS_CANCELLED:
  • 設備驅動器已取消 IRP。
  • 需要系統電源狀態變更。
  • 在 Windows XP 上,其中一個連線 USB 裝置的裝置驅動程式在執行閑置要求回呼例程時,無法將其裝置放在 D2 中。 因此,總線驅動程式已完成所有擱置的閑置要求 IRP。
STATUS_POWER_STATE_INVALID 指出設備驅動器要求其裝置 的 D3 電源狀態。 發生這種情況時,總線驅動程式會以STATUS_POWER_STATE_INVALID完成所有擱置的閑置 IRP。
STATUS_DEVICE_BUSY 表示總線驅動程式已保留裝置擱置的閑置要求 IRP。 給定裝置一次只能擱置一個閑置 IRP。 提交多個閑置要求 IRP 是電源原則擁有者一部分的錯誤,應該由驅動程式寫入器處理。

下列程式代碼範例示範閑置要求完成例程的範例實作。

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

USB 閑置通知回呼例程

總線驅動程式 (中樞驅動程序的實例或一般父驅動程式,) 判斷何時可以安全地暫停其裝置的子系。 如果是,它會呼叫每個子用戶端驅動程式所提供的閑置通知回呼例程。

USB_IDLE_CALLBACK的函式原型如下所示:

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

設備驅動器必須在閑置通知回呼例程中採取下列動作:

  • 如果裝置需要進行遠端喚醒,請要求裝置 IRP_MN_WAIT_WAKE IRP
  • 取消所有 I/O 並準備裝置以進入較低的電源狀態。
  • 呼叫 PoRequestPowerIrp 並將 PowerState 參數設定為 wdm.h 中定義的列舉值 PowerDeviceD2 (,讓裝置進入 WDM 睡眠狀態;ntddk.h) 。 在 Windows XP 中,驅動程式不得將其裝置放在 PowerDeviceD3 中,即使裝置沒有遠端喚醒也一樣。

在 Windows XP 中,驅動程式必須依賴閑置通知回呼例程,選擇性地暫停裝置。 如果 Windows XP 中執行的驅動程式直接將裝置置於較低的電源狀態,而不使用閑置通知回呼例程,這可能會防止 USB 裝置樹狀結構中的其他裝置暫停。

中樞驅動程式和 USB 一般父驅動程式 (Usbccgp.sys) 在 IRQL = PASSIVE_LEVEL呼叫閑置通知回呼例程。 這可讓回呼例程在等候電源狀態變更要求完成時封鎖。

只有在系統位於 S0 且裝置位於 D0 時,才會叫用回呼例程。

下列限制適用於閑置要求通知回呼例程:

  • 設備驅動器可以在閑置通知回呼例程中起始從 D0D2 的裝置電源狀態轉換,但不允許其他電源狀態轉換。 特別是,驅動程式在執行回呼例程時,不得嘗試將其裝置變更為 D0
  • 設備驅動器不得在閑置通知回呼例程內要求多個電源 IRP。

在閑置通知回呼例程中為喚醒的裝置進行Arming裝置

閑置通知回呼例程應該判斷其裝置是否有 擱置IRP_MN_WAIT_WAKE 要求。 如果沒有擱置IRP_MN_WAIT_WAKE要求,回呼例程應該先提交IRP_MN_WAIT_WAKE要求,再暫停裝置。 如需等候喚醒機制的詳細資訊,請參閱 支援具有喚醒功能的裝置

USB 全域暫停

USB 2.0 規格會將全域暫停定義為USB主機控制器後方的整個總線暫停,方法是減少總線上的所有USB流量,包括開始畫面封包。 尚未暫停的下游裝置會偵測其上游埠上的閑置狀態,並自行進入暫停狀態。 Windows 不會以這種方式實作全域暫停。 Windows 一律會選擇性地在 USB 主機控制器後方暫停每個 USB 裝置,再停止總線上的所有 USB 流量。

Windows 7 中的全域暫停條件

Windows 7 對於選擇性地暫停 USB 中樞比 Windows Vista 更積極。 Windows 7 USB 中樞驅動程式會選擇性地暫停其所有連結裝置都處於 D1D2D3 裝置電源狀態的任何中樞。 一旦所有 USB 中樞都選擇性暫停,整個總線就會進入全域暫停。 每當裝置處於 D1D2D3 的 WDM 裝置狀態時,Windows 7 USB 驅動程式堆疊會將裝置視為閑置。

Windows Vista 中全域暫停的條件

在 Windows Vista 中執行全域暫停的需求比在 Windows XP 中更有彈性。

特別是,只要裝置處於 D1D2D3 的 WDM 裝置狀態,USB 堆疊就會將裝置視為 Windows Vista 中的閑置。

下圖說明 Windows Vista 中可能發生的案例。

說明 Windows Vista 中全域暫停的圖表。

This diagram illustrates a situation similar to the one depicted in the section "Conditions for global suspend in Windows XP". 不過,在此情況下,裝置 3 會限定為閑置裝置。 由於所有裝置都處於閑置狀態,因此總線驅動程式能夠呼叫與擱置閑置要求 IRP 相關聯的閑置通知回呼例程。 每個驅動程式都會暫停其裝置,而總線驅動程式會在安全這麼做時立即暫停USB主機控制器。

在 Windows Vista 上,所有非中樞 USB 裝置都必須在 D1D2D3 中,才會起始全域暫停,此時會暫停所有 USB 中樞,包括根中樞。 這表示任何不支援選擇性暫停的USB用戶端驅動程式,都可防止總線進入全域暫停。

Windows XP 中的全域暫停條件

為了將 Windows XP 上的電源節省最大化,每個設備驅動器都使用閑置要求 IRP 來暫停其裝置非常重要。 如果某個驅動程式使用 IRP_MN_SET_POWER 要求暫停其裝置,而不是閑置要求 IRP,可能會防止其他裝置暫停。

下圖說明 Windows XP 中可能發生的案例。

說明 Windows XP 中全域暫停的圖表。

在此圖中,裝置 3 處於電源狀態 D3,且沒有閑置要求 IRP 擱置中。 裝置 3 不符合 Windows XP 中全域暫停的閒置裝置資格,因為它沒有閒置要求 IRP 擱置在其父系中。 這可防止總線驅動程式呼叫與樹狀結構中其他裝置驅動程式相關聯的閑置要求回呼例程。

啟用選擇性暫停

已針對 Microsoft Windows XP 的升級版本停用選擇性暫停。 它已啟用 Windows XP、Windows Vista 和更新版本的 Windows 全新安裝。

若要啟用指定根中樞及其子裝置的選擇性暫停支援,請選取 裝置管理員 中 USB 根中樞的 [電源管理] 索引卷標上的複選框。

或者,您可以在 USB 埠驅動程式的軟體金鑰下設定 HcDisableSelectiveSuspend 的值,以啟用或停用選擇性暫停。 值為 1 會停用選擇性暫停。 值為 0 會啟用選擇性暫止。

例如,Usbport.inf 中的下列幾行會停用 Hydra OHCI 控制器的選擇性暫停:

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

用戶端驅動程式不應該嘗試判斷是否在傳送閑置要求之前啟用選擇性暫停。 每當裝置閑置時,他們都應該提交閑置要求。 如果閑置要求失敗,客戶端驅動程序應該重設閑置定時器,然後再試一次。