복합 드라이버에서 함수 일시 중단을 구현하는 방법
이 문서에서는 USB(유니버설 직렬 버스) 3.0 다기능 디바이스(복합 디바이스)에 대한 함수 일시 중단 및 함수 원격 절전 모드 해제 기능에 대한 개요를 제공합니다. 이 문서에서는 복합 디바이스를 제어하는 드라이버에서 이러한 기능을 구현하는 방법에 대해 알아봅니다. 이 문서는 Usbccgp.sys 대체하는 복합 드라이버에 적용됩니다.
USB(유니버설 직렬 버스) 3.0 사양은 함수 일시 중단이라는 새로운 기능을 정의합니다. 이 기능을 사용하면 복합 디바이스의 개별 함수가 다른 함수와 독립적으로 저전력 상태로 진입할 수 있습니다. 키보드의 함수와 마우스용 다른 함수를 정의하는 복합 디바이스를 고려합니다. 사용자는 키보드 함수를 작동 상태로 유지하지만 일정 기간 동안 마우스를 이동하지 않습니다. 마우스용 클라이언트 드라이버는 함수의 유휴 상태를 감지하고 키보드 함수가 작동 상태를 유지하는 동안 함수를 일시 중단 상태로 보낼 수 있습니다.
디바이스 내의 모든 기능의 전원 상태에 관계없이 전체 디바이스가 일시 중단 상태로 전환할 수 있습니다. 특정 함수와 전체 디바이스가 일시 중단 상태로 들어가면 디바이스가 일시 중단 상태이고 디바이스의 일시 중단 진입 및 종료 프로세스 전체에서 함수의 일시 중단 상태가 유지됩니다.
USB 2.0 디바이스의 원격 절전 모드 해제 기능( USB 디바이스의 원격 절전 모드 해제 참조)과 마찬가지로 USB 3.0 복합 디바이스의 개별 함수는 다른 기능의 전원 상태에 영향을 주지 않고 저전력 상태에서 절전 모드를 해제할 수 있습니다. 이 기능을 함수 원격 절 해제라고 합니다. 이 기능은 디바이스의 펌웨어에서 원격 절전 모드 해제 비트를 설정하는 프로토콜 요청을 전송하여 호스트에서 명시적으로 사용하도록 설정됩니다. 이 프로세스를 원격 절전 모드 해제를 위해 함수를 무장이라고 합니다. 원격 절 해제 관련 비트에 대한 자세한 내용은 공식 USB 사양의 그림 9-6을 참조하세요.
함수가 원격 절전 모드 해제를 위해 무장하는 경우 함수(일시 중단 상태인 경우)는 물리적 디바이스에서 사용자 이벤트가 발생할 때 절전 모드 해제 다시 시작 신호를 생성하기에 충분한 전력을 유지합니다. 다시 시작 신호의 결과로 클라이언트 드라이버는 연결된 함수의 일시 중단 상태를 종료할 수 있습니다. 복합 디바이스의 마우스 함수 예제에서 사용자가 유휴 상태인 마우스를 흔들면 마우스 함수는 호스트에 다시 시작 신호를 보냅니다. 호스트에서 USB 드라이버 스택은 발생한 함수를 감지하고 해당 함수의 클라이언트 드라이버에 알림을 전파합니다. 그러면 클라이언트 드라이버가 함수를 절전 모드에서 해제하고 작업 상태를 입력할 수 있습니다.
클라이언트 드라이버의 경우 상태를 일시 중단하고 함수를 해제하는 함수를 보내는 단계는 전체 디바이스를 일시 중단 상태로 보내는 단일 함수 디바이스 드라이버와 유사합니다. 다음 절차에서는 이러한 단계를 요약합니다.
- 연결된 함수가 유휴 상태인지 검색합니다.
- IRP(유휴 I/O 요청 패킷)를 보냅니다.
- IRP(대기 절전 모드 해제 I/O 요청 패킷)를 전송하여 원격 절전 모드 해제를 위해 함수를 무장하는 요청을 제출합니다.
- Dx 전원 IRP(D2 또는 D3)를 전송하여 함수를 저전력 상태로 전환합니다.
이전 단계에 대한 자세한 내용은 USB 선택적 일시 중단에서 "USB 유휴 요청 IRP 보내기"를 참조하세요. 복합 드라이버는 복합 디바이스의 각 함수에 대해 PDO(물리적 디바이스 개체)를 만들고 클라이언트 드라이버(함수 디바이스 스택의 FDO)에서 보낸 전원 요청을 처리합니다. 클라이언트 드라이버가 해당 함수에 대한 일시 중단 상태를 성공적으로 입력하고 종료하려면 복합 드라이버가 함수 일시 중단 및 원격 절전 모드 해제 기능을 지원하고 수신된 전원 요청을 처리해야 합니다.
Windows 8 USB 3.0 디바이스용 USB 드라이버 스택은 이러한 기능을 지원합니다. 또한 함수 일시 중단 및 함수 원격 절전 모드 해제 구현이 Windows 기본 복합 드라이버인 Microsoft 제공 USB 제네릭 부모 드라이버 (Usbccgp.sys)에 추가되었습니다. 사용자 지정 복합 드라이버를 작성하는 경우 드라이버는 다음 절차에 따라 함수 일시 중단 및 원격 절전 모드 해제 요청과 관련된 요청을 처리해야 합니다.
1단계: USB 드라이버 스택이 함수 일시 중단을 지원하는지 확인
복합 드라이버의 시작 디바이스 루틴(IRP_MN_START_DEVICE)에서 다음 단계를 수행합니다.
- 기본 USB 드라이버 스택이 함수 일시 중단 기능을 지원하는지 여부를 확인하려면 USBD_QueryUsbCapability 루틴을 호출합니다. 호출에는 USBD_CreateHandle 루틴에 대한 이전 호출에서 얻은 유효한 USBD 핸들이 필요합니다.
USBD_QueryUsbCapability 성공적으로 호출하면 기본 USB 드라이버 스택이 함수 일시 중단을 지원하는지 여부를 결정합니다. 호출은 USB 드라이버 스택이 함수 일시 중단을 지원하지 않거나 연결된 디바이스가 USB 3.0 다기능 디바이스가 아님을 나타내는 오류 코드를 반환할 수 있습니다.
- 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를 받으면 복합 드라이버가 usb 드라이버 스택에 IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION I/O 컨트롤 요청을 보내야 합니다. 요청을 사용하면 스택이 다시 시작 신호에 대한 알림을 받을 때 USB 드라이버 스택이 복합 드라이버에 알릴 수 있습니다. IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATIONREQUEST_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;
}
IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION 요청은 다시 시작 신호에 대한 알림을 받을 때 절전 모드 해제 프로세스 중에 USB 드라이버 스택에 의해 완료됩니다. 이 시간 동안 USB 드라이버 스택은 원격 절전 모드 해제 완료 루틴도 호출합니다.
복합 드라이버는 대기 절전 모드 해제 IRP를 보류 상태로 유지하고 나중에 처리하기 위해 큐에 대기해야 합니다. 드라이버의 원격 절전 모드 해제 완료 루틴이 USB 드라이버 스택에서 호출될 때 복합 드라이버는 해당 IRP를 완료해야 합니다.
4단계: 원격 절 해제를 위해 함수를 무장하는 요청 보내기
함수를 저전력 상태로 보내기 위해 클라이언트 드라이버는 WDM(Windows 드라이버 모델) 디바이스 전원 상태를 D2 또는 D3으로 변경하라는 요청과 함께 IRP_MN_SET_POWER IRP를 제출합니다. 일반적으로 클라이언트 드라이버는 드라이버가 원격 절전 모드 해제를 요청하기 위해 대기 절전 모드 해제 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 드라이버 스택에 알 수 있습니다. 함수 절식 알림에 대한 자세한 내용은 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를 일시 중단 상태에서 시스템을 해제하는 데 기여한 것으로 표시해야 합니다. 전원 관리자는 시스템을 작동시킨 디바이스에 대한 정보를 포함하는 ETW(Event Tracing for Windows) 이벤트(전역 시스템 채널에서 볼 수 있습니다)를 기록합니다.