다음을 통해 공유


복합 디바이스를 등록하는 방법

이 문서에서는 복합 드라이버라고 하는 USB 다기능 디바이스의 드라이버가 복합 디바이스를 기본 USB 드라이버 스택에 등록 및 등록 취소하는 방법을 설명합니다. Windows는 기본 복합 드라이버로 Usbccgp.sys Microsoft 제공 드라이버를 로드합니다. 이 문서의 절차는 Usbccgp.sys 대체하는 사용자 지정 WDM(Windows 드라이버 모델) 기반 복합 드라이버에 적용됩니다.

USB(유니버설 직렬 버스) 디바이스는 동시에 활성 상태인 여러 함수를 제공할 수 있습니다. 이러한 다기능 디바이스를 복합 디바이스라고도합니다. 예를 들어 복합 디바이스는 키보드 기능에 대한 함수와 마우스에 대한 다른 함수를 정의할 수 있습니다. 복합 드라이버는 디바이스의 함수를 열거합니다. 복합 드라이버는 모놀리식 모델에서 해당 함수 자체를 관리하거나 각 함수에 대한 PDO(물리적 디바이스 개체)를 만들 수 있습니다. 키보드 드라이버 및 마우스 드라이버와 같은 USB 함수 드라이버는 각각의 개별 PDO를 관리합니다.

USB 3.0 사양은 개별 함수가 다른 함수 또는 전체 디바이스의 전원 상태에 영향을 주지 않고 저전력 상태를 입력하고 종료할 수 있도록 하는 함수 일시 중단 및 원격 절전 모드 해제 기능을 정의합니다. 이 기능에 대한 자세한 내용은 복합 드라이버에서 함수 일시 중단을 구현하는 방법을 참조 하세요.

이 기능을 사용하려면 복합 드라이버가 기본 USB 드라이버 스택에 디바이스를 등록해야 합니다. 이 기능은 USB 3.0 디바이스에 적용되므로 복합 드라이버는 기본 스택이 버전 USBD_INTERFACE_VERSION_602 지원하는지 확인해야 합니다. 등록 요청을 통해 복합 드라이버는 다음과 같습니다.

  • 기본 USB 드라이버 스택에 드라이버가 원격 절전 모드 해제를 위해 함수를 무장하라는 요청을 보낼 책임이 있음을 알릴 수 있습니다. USB 드라이버 스택은 디바이스에 필요한 프로토콜 요청을 보내는 원격 절전 모드 해제 요청을 처리합니다.
  • USB 드라이버 스택에서 할당한 함수 핸들 목록(함수당 하나)을 가져옵니다. 그런 다음 복합 드라이버는 핸들과 연결된 함수의 원격 절전 모드 해제에 대한 드라이버 요청에서 함수 핸들을 사용할 수 있습니다.

일반적으로 복합 드라이버는 드라이버의 AddDevice 또는 시작 디바이스 루틴에서 등록 요청을 보내 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. REGISTER_COMPOSITE_DEVICE 구조를 할당하고 USBD_BuildRegisterCompositeDevice 루틴을 호출하여 구조를 초기화합니다. 호출에서 USBD 핸들, 초기화된 COMPOSITE_DEVICE_CAPABILITIES 구조 및 함수 수를 지정합니다.

  4. IoAllocateIrp를 호출하여 IRP(I/O 요청 패킷)를 할당하고 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 구조체의 주소로 설정하여 입력 매개 변수를 지정합니다.
    • 5단계에서 할당된 버퍼로 AssociatedIrp.SystemBuffer를 설정하여 출력 매개 변수를 지정합니다.
  7. IRP를 다음 스택 위치에 전달하여 요청을 보내려면 IoCallDriver를 호출합니다.

완료되면 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 Parameters.DeviceIoControl.IoControlCode 멤버를 IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE 설정하여 요청을 빌드합니다.
  3. IRP를 다음 스택 위치에 전달하여 요청을 보내려면 IoCallDriver를 호출합니다.

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