共用方式為


如何註冊複合裝置

本文說明 USB 多功能裝置的驅動程式,稱為複合驅動程式,如何向基礎 USB 驅動程式堆疊註冊和取消註冊複合裝置。 Windows 會載入Microsoft提供的驅動程式,Usbccgp.sys做為預設復合驅動程式。 本文中的程式適用於取代Usbccgp.sys的自定義 Windows 驅動程式模型 (WDM) 複合驅動程式。

通用序列總線 (USB) 裝置可以提供多個同時作用中的功能。 這類多功能裝置也稱為複合裝置。 例如,複合裝置可能會定義鍵盤功能的函式,以及滑鼠的另一個函式。 複合驅動程式會列舉裝置的功能。 複合驅動程式可以在整合型模型中管理這些函式本身,或為每個函式建立實體裝置物件 (PDO)。 USB 函式驅動程式,例如鍵盤驅動程式和滑鼠驅動程式,會管理各自的個別 PDO。

USB 3.0 規格會定義函式暫停和遠端喚醒功能,讓個別函式進入和結束低功率狀態,而不會影響其他函式或整個裝置的電源狀態。 如需此功能的詳細資訊,請參閱 如何在複合驅動程式中實作函式暫止。

若要使用此功能,複合驅動程序必須向基礎 USB 驅動程式堆疊註冊裝置。 由於此功能適用於 USB 3.0 裝置,因此複合驅動程式必須確定基礎堆疊支援版本USBD_INTERFACE_VERSION_602。 透過註冊要求,復合驅動程式:

  • 通知基礎 USB 驅動程式堆疊,驅動程式負責傳送要求來武裝遠端喚醒的函式。 USB 驅動程式堆疊會處理遠端喚醒要求,以將必要的通訊協定要求傳送至裝置。
  • 取得 USB 驅動程式堆疊指派的函式句柄清單(每個函式一個)。 複合驅動程式接著可以在驅動程式的要求中使用函式句柄,要求遠端喚醒與句柄相關聯的函式。

復合驅動程式通常會在驅動程式的 AddDevice 或 start-device 例程中傳送註冊要求,以處理 IRP_MN_START_DEVICE。 因此,復合驅動程式會釋放配置給驅動程式卸除例程中註冊的資源,例如停止裝置(IRP_MN_STOP_DEVICE)或移除裝置例程(IRP_MN_REMOVE_DEVICE)。

必要條件

傳送註冊要求之前,請確定:

  • 您有裝置中的函式數目。 該數位可以衍生 get-configuration 要求所擷取的描述項。
  • 您在先前呼叫 USBD_CreateHandle 中取得 USBD 句柄。
  • 基礎 USB 驅動程式堆疊支援 USB 3.0 裝置。 若要這樣做,請呼叫 USBD_IsInterfaceVersionSupported ,並傳遞USBD_INTERFACE_VERSION_602作為要檢查的版本。

如需程式代碼範例,請參閱 如何在複合驅動程式中實作函式暫止。

註冊複合裝置

下列程式說明您應該如何建置和傳送註冊要求,以將複合驅動程式與 USB 驅動程式堆疊產生關聯。

  1. 配置COMPOSITE_DEVICE_CAPABILITIES結構,並藉由呼叫COMPOSITE_DEVICE_CAPABILITIES_INIT巨集加以初始化。

  2. COMPOSITE_DEVICE_CAPABILITIES 的 CapabilityFunctionSuspend 成員設定為 1。

  3. 呼叫 USBD_BuildRegisterCompositeDevice 例程來配置REGISTER_COMPOSITE_DEVICE結構並初始化 結構。 在呼叫中,指定USBD句柄、初始化 COMPOSITE_DEVICE_CAPABILITIES 結構,以及函式數目。

  4. 呼叫 IoAllocateIrp 以設定 I/O 要求封包(IRP),並藉由呼叫 IoGetNextIrpStackLocation 來取得 IRP 第一個堆棧位置 (IO_STACK_LOCATION) 的指標。

  5. 為足以容納函式句柄數位的緩衝區配置記憶體(USBD_FUNCTION_HANDLE)。 陣列中的元素數目必須是 PDO 的數目。

  6. 藉由設定下列IO_STACK_LOCATION成員來建置要求:

    • 將 Parameters.DeviceIoControl.IoControlCode 設定為 IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE,以指定要求的類型。
    • 將 Parameters.Others.Argument1 設定為初始化REGISTER_COMPOSITE_DEVICE結構的位址,以指定輸入參數。
    • 將 AssociatedIrp.SystemBuffer 設定為步驟 5 中所配置的緩衝區,以指定輸出參數。
  7. 呼叫 IoCallDriver ,將IRP傳遞至下一個堆疊位置,以傳送要求。

完成後,檢查 USB 驅動程式堆疊所傳回的函式句柄陣列。 您可以將數位儲存在驅動程式的裝置內容中,以供日後使用。

下列程式代碼範例示範如何建置和傳送註冊要求。 此範例假設複合驅動程式會在驅動程式的裝置內容中儲存先前取得的函式數目和 USBD 句柄。

VOID  RegisterCompositeDriver(PPARENT_FDO_EXT parentFdoExt)
{
    PIRP                            irp;
    REGISTER_COMPOSITE_DRIVER       registerInfo;
    COMPOSITE_DRIVER_CAPABILITIES   capabilities;
    NTSTATUS                        status;
    PVOID                           buffer;
    ULONG                           bufSize;
    PIO_STACK_LOCATION              nextSp;

    buffer = NULL;

    COMPOSITE_DRIVER_CAPABILITIES_INIT(&capabilities);
    capabilities.CapabilityFunctionSuspend = 1;

    USBD_BuildRegisterCompositeDriver(parentFdoExt->usbdHandle,
        capabilities,
        parentFdoExt->numFunctions,
        &registerInfo);

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

    if (irp == NULL)
    {
        //IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitRegisterCompositeDriver;
    }

    nextSp = IoGetNextIrpStackLocation(irp);

    bufSize = parentFdoExt->numFunctions * sizeof(USBD_FUNCTION_HANDLE);

    buffer = ExAllocatePoolWithTag (NonPagedPool, bufSize, POOL_TAG);

    if (buffer == NULL)
    {
        // Memory alloc for function-handles failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitRegisterCompositeDriver;
    }

    nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DRIVER;

    //Set the input buffer in Argument1
    nextSp->Parameters.Others.Argument1 = &registerInfo;

    //Set the output buffer in SystemBuffer field for USBD_FUNCTION_HANDLE.
    irp->AssociatedIrp.SystemBuffer = buffer;

    // Pass the IRP down to the next device object in the stack. Not shown.
    status = CallNextDriverSync(parentFdoExt, irp, FALSE);

    if (!NT_SUCCESS(status))
    {
        //Failed to register the composite driver.
        goto ExitRegisterCompositeDriver;
    }

    parentFdoExt->compositeDriverRegistered = TRUE;

    parentFdoExt->functionHandleArray = (PUSBD_FUNCTION_HANDLE) buffer;

End:
    if (!NT_SUCCESS(status))
    {
        if (buffer != NULL)
        {
            ExFreePoolWithTag (buffer, POOL_TAG);
            buffer = NULL;
        }
    }

    if (irp != NULL)
    {
        IoFreeIrp(irp);
        irp = NULL;
    }

    return;
}

取消註冊複合裝置

  1. 藉由呼叫 IoAllocateIrp 來設定 IRP,並藉由呼叫 IoGetNextIrpStackLocation 來取得 IRP 第一個堆疊位置 (IO_STACK_LOCATION) 的指標。
  2. 將 IO_STACK_LOCATION 的參數.DeviceIoControl.IoControlCode 成員設定為 IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE,以建置要求。
  3. 呼叫 IoCallDriver ,將IRP傳遞至下一個堆疊位置,以傳送要求。

IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE要求是由復合驅動程式在 remove-device 例程的內容中傳送一次。 要求的目的是要移除 USB 驅動程式堆疊與複合驅動程式及其列舉函式之間的關聯。 要求也會清除任何為了維護該關聯而建立的資源,以及先前註冊要求中傳回的所有函式句柄。

下列程式代碼範例示範如何建置和傳送要求來取消註冊複合裝置。 此範例假設複合驅動程式先前是透過註冊要求註冊,如本文稍早所述。

VOID  UnregisterCompositeDriver(
    PPARENT_FDO_EXT parentFdoExt )
{
    PIRP                irp;
    PIO_STACK_LOCATION  nextSp;
    NTSTATUS            status;

    PAGED_CODE();

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

    if (irp == NULL)
    {
        //IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        return;
    }

    nextSp = IoGetNextIrpStackLocation(irp);

    nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DRIVER;

    // Pass the IRP down to the next device object in the stack. Not shown.
    status = CallNextDriverSync(parentFdoExt, irp, FALSE);

    if (NT_SUCCESS(status))
    {
        parentFdoExt->compositeDriverRegistered = FALSE;
    }

    IoFreeIrp(irp);

    return;
}