共用方式為


如何在複合驅動程序中實作函式暫止

本文提供通用序列總線 (USB) 3.0 多重函式裝置 (複合裝置) 的函式暫停和函式遠端喚醒功能概觀。 在本文中,您將瞭解如何在控制複合裝置的驅動程序中實作這些功能。 本文適用於取代 Usbccgp.sys 的複合驅動程式。

通用序列總線 (USB) 3.0 規格會定義稱為「 函式暫止」的新功能。 此功能可讓複合裝置的個別函式進入低電源狀態,而與其他函式無關。 請考慮一個複合裝置,該裝置會定義鍵盤的函式,以及另一個用於滑鼠的函式。 用戶讓鍵盤功能保持運作狀態,但不會移動滑鼠一段時間。 滑鼠的用戶端驅動程式可以偵測函式的閑置狀態,並將函式傳送至暫停狀態,而鍵盤函式仍處於工作狀態。

不論裝置內任何函式的電源狀態為何,整個裝置都可以轉換為暫停狀態。 如果特定函式和整個裝置進入暫停狀態,則會在裝置處於暫停狀態時保留函式的暫停狀態,以及整個裝置的暫停進入和結束程式。

類似於USB 2.0裝置的遠端喚醒功能 (請參閱 USB裝置的遠端喚醒) ,USB 3.0複合裝置中的個別功能可以從低電源狀態喚醒,而不會影響其他函式的電源狀態。 此功能稱為函式 遠程喚醒。 主機藉由傳送通訊協定要求,在裝置韌體中設定遠端喚醒位,以明確啟用此功能。 此程式稱為 將函式用於遠端喚醒。 如需遠端喚醒相關位的相關信息,請參閱官方 USB 規格中的圖 9-6。

如果函式用於遠端喚醒,當處於暫停狀態時 (函式) 保留足夠的電源,以在實體裝置上發生使用者事件時產生喚醒 繼續訊號 。 由於該繼續訊號,用戶端驅動程序接著可以結束相關聯函式的暫停狀態。 在複合裝置中滑鼠函式的範例中,當使用者切換處於閑置狀態的滑鼠時,滑鼠函式會將繼續訊號傳送給主機。 在主機上,USB 驅動程式堆疊會偵測哪個函式喚醒,並將通知傳播至對應函式的用戶端驅動程式。 然後,客戶端驅動程式可以喚醒函式並進入工作狀態。

針對客戶端驅動程式,傳送函式以暫停狀態和喚醒函式的步驟類似於將整個裝置傳送至暫停狀態的單一函式設備驅動器。 下列程式摘要說明這些步驟。

  1. 偵測相關聯的函式處於閑置狀態。
  2. 傳送閑置 I/O 要求封包 (IRP) 。
  3. 藉由傳送等候喚醒 I/O 要求封包 (IRP) ,以提交要求以讓其功能進行遠端喚醒。
  4. 藉由傳送 Dx 電源 IRP (D2D3) ,將函式轉換為低電源狀態。

For more information about the preceding steps, see "Sending a USB Idle Request IRP" in USB Selective Suspend. 複合驅動程式會為複合裝置中的每個函式建立實體裝置物件 (PDO) ,並處理客戶端驅動程式所傳送的電源要求, (函式裝置堆疊的 FDO) 。 為了讓客戶端驅動程式成功進入和結束其函式的暫停狀態,複合驅動程式必須支援函式暫停和遠端喚醒功能,並處理收到的電源要求。

在 Windows 8 中,USB 3.0 裝置的 USB 驅動程式堆疊支援這些功能。 此外,函式暫停和函式遠端喚醒實作已新增至 Microsoft 提供的 USB 泛型父驅動程式 (Usbccgp.sys) ,這是 Windows 默認複合驅動程式。 如果您要撰寫自定義複合驅動程式,您的驅動程式必須根據下列程式處理與函式暫停和遠端喚醒要求相關的要求。

步驟 1:判斷 USB 驅動程式堆疊是否支援函式暫停

在複合驅動程式的啟動裝置例程 (IRP_MN_START_DEVICE) 中,執行下列步驟:

  1. 呼叫 USBD_QueryUsbCapability 例程,以判斷基礎 USB 驅動程式堆疊是否支援函式暫停功能。 呼叫需要您在先前呼叫 USBD_CreateHandle 例程中取得的有效 USBD 句柄。

成功呼叫 USBD_QueryUsbCapability 判斷基礎 USB 驅動程式堆疊是否支援函式暫停。 呼叫可以傳回錯誤碼,指出USB驅動程式堆疊不支援函式暫停,或連結的裝置不是USB 3.0多函式裝置。

  1. 如果 USBD_QueryUsbCapability 呼叫指出支援函式暫停,請使用基礎 USB 驅動程式堆疊註冊複合裝置。 若要註冊複合裝置,您必須傳送 IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE I/O 控制要求。 如需此要求的詳細資訊,請參閱 如何註冊複合裝置

註冊要求會使用 REGISTER_COMPOSITE_DEVICE 結構來指定復合驅動程式的相關信息。 請務必將 CapabilityFunctionSuspend 設定為 1,以指出複合驅動程式支援函式暫停。

如需示範如何判斷 USB 驅動程式堆疊是否支援函式暫停的程式代碼範例,請參閱 USBD_QueryUsbCapability

步驟 2:處理閑置 IRP

用戶端驅動程式可以傳送閑置的 IRP (請參閱 IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) 。 要求會在客戶端驅動程式偵測到函式的閑置狀態之後傳送。 IRP 包含回呼完成例程的指標, (稱為 閑置 回呼) 客戶端驅動程式所實作。 在閑置回呼內,用戶端會執行工作,例如取消擱置的 I/O 傳輸,就在將函式傳送至暫停狀態之前。

注意

閑置 IRP 機制是 USB 3.0 裝置客戶端驅動程式的選擇性機制。 不過,大部分的用戶端驅動程式都會寫入以支援USB 2.0和USB 3.0裝置。 若要支援 USB 2.0 裝置,驅動程式必須傳送閑置 IRP,因為複合驅動程式依賴該 IRP 來追蹤每個函式的電源狀態。 如果所有函式都處於閑置狀態,復合驅動程式會將整個裝置傳送至暫停狀態。

從客戶端驅動程式接收閑置 IRP 時,複合驅動程式必須立即叫用閑置回呼,通知用戶端驅動程式客戶端驅動程式可能會將函式傳送至暫停狀態。

步驟 3:傳送遠端喚醒通知的要求

用戶端驅動程式可以提交要求以將次要函式程式代碼設定為 IRP_MN_WAIT_WAKE ( wait-wake IRP) ,以將IRP_MJ_POWER IRP 提交至遠端喚醒的函式。 只有在驅動程式想要輸入工作狀態做為使用者事件的結果時,客戶端驅動程式才會提交此要求。

收到等候喚醒 IRP 時,復合驅動程式必須將 IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION I/O 控制要求傳送至 USB 驅動程式堆疊。 要求可讓 USB 驅動程式堆疊在堆疊收到有關繼續訊號的通知時通知複合驅動程式。 IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION會使用 REQUEST_REMOTE_WAKE_NOTIFICATION 結構來指定要求參數。 複合驅動程序必須指定的其中一個值,就是遠端喚醒所啟動之函式的函式句柄。 復合驅動程式在先前的要求中取得該驅動程式,以向 USB 驅動程式堆疊註冊複合裝置。 如需複合驅動程式註冊要求的詳細資訊,請參閱 如何註冊複合裝置

在要求的 IRP 中,復合驅動程式會提供 (遠端喚醒) 完成例程的指標,由複合驅動程式實作。

下列範例程式代碼示範如何傳送遠端喚醒要求。

/*++

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

當 USB 驅動程式堆疊收到有關繼續訊號的通知時,USB 驅動程式堆疊會在喚醒程式期間完成 IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION 要求。 在此期間,USB 驅動程式堆疊也會叫用遠端喚醒完成例程。

復合驅動程序必須保留等候喚醒 IRP 暫止,並將它排入佇列以供稍後處理。 複合驅動程式必須在驅動程式的遠端喚醒完成例程由 USB 驅動程式堆疊叫用時完成該 IRP。

步驟 4:將要求傳送給 arm 函式以進行遠端喚醒

若要將函式傳送至低電源狀態,用戶端驅動程式會提交 IRP_MN_SET_POWER IRP,並要求將 Windows 驅動程式模型 (WDM) 裝置電源狀態變更為 D2D3。 一般而言,如果驅動程式稍早傳送等候喚醒 IRP 要求遠端喚醒,用戶端驅動程式就會傳送 D2 IRP。 否則,客戶端驅動程式會傳送 D3 IRP。

收到 D2 IRP 時,復合驅動程式必須先判斷等候喚醒 IRP 是否從用戶端驅動程式所傳送的先前要求擱置中。 如果該 IRP 擱置中,復合驅動程式必須為遠端喚醒提供函式。 若要這樣做,複合驅動程式必須將SET_FEATURE控件要求傳送至函式的第一個介面,讓裝置能夠傳送繼續訊號。 若要傳送控制要求,請呼叫 USBD_UrbAllocate 例程配置 URB 結構,並呼叫 UsbBuildFeatureRequest 宏來格式化SET_FEATURE要求的 URB。 在呼叫中,將URB_FUNCTION_SET_FEATURE_TO_INTERFACE指定為作業程序代碼,並將USB_FEATURE_FUNCTION_SUSPEND指定為功能選取器。 在 Index 參數中,設定最大有效位元 組的位 1 。 該值會複製到傳輸設定封包中的 wIndex 欄位。

下列範例示範如何傳送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;

}

複合驅動程序接著會將 D2 IRP 向下傳送至 USB 驅動程式堆疊。 如果所有其他函式處於暫停狀態,USB 驅動程式堆疊會藉由操作控制器上的特定埠緩存器來暫停埠。

備註

在滑鼠函式範例中,由於遠端喚醒功能已啟用, (請參閱步驟 4) ,因此當使用者切換滑鼠時,滑鼠函式會在線路上游對主控制器產生繼續訊號。 控制器接著會傳送通知封包,其中包含喚醒函式的相關信息,以通知驅動程式堆疊。 如需函數喚醒通知的相關信息,請參閱 USB 3.0 規格中的圖 8-17。

收到通知封包時,USB 驅動程式堆疊會完成擱置IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION要求 (請參閱步驟 3) ,並 (叫用要求中指定的遠端喚醒) 完成回呼例程,並由複合驅動程式實作。 當通知到達複合驅動程式時,它會藉由完成稍早客戶端驅動程式傳送的等候喚醒 IRP,通知對應的用戶端驅動程式已進入工作狀態。

在 (遠端喚醒) 完成例程中,復合驅動程式應該將工作專案排入佇列,以完成擱置的等候喚醒 IRP。 對於USB 3.0裝置,複合驅動程式只會喚醒傳送繼續訊號的函式,並將其他函式保留在暫停狀態。 將工作專案排入佇列可確保與 USB 2.0 裝置之函式驅動程式的現有實作相容。 如需佇列工作專案的相關信息,請參閱 IoQueueWorkItem

背景工作線程會完成等候喚醒 IRP,並叫用用戶端驅動程式的完成例程。 完成例程接著會傳送 D0 IRP,以進入工作狀態的函式。 完成等候喚醒 IRP 之前,復合驅動程式應該呼叫 PoSetSystemWake ,將等候喚醒 IRP 標示為導致從暫停狀態喚醒系統。 Power Manager 會記錄 Windows (ETW 的事件追蹤) 事件 (全域系統通道中可檢視) ,其中包含喚醒系統的裝置相關信息。