낮은 드라이버가 완료될 때까지 PnP IRP 처리 연기

일부 PnP 및 전원 IRP는 먼저 디바이스에 대한 부모 버스 드라이버에 의해 처리된 다음 디바이스 스택의 각 다음 상위 드라이버에 의해 처리되어야 합니다. 예를 들어 부모 버스 드라이버는 디바이스(IRP_MN_START_DEVICE)에 대한 시작 작업을 수행한 후 다음으로 높은 각 드라이버에 대해 첫 번째 드라이버여야 합니다. 이러한 IRP의 경우 함수 및 필터 드라이버는 I/O 완료 루틴을 설정하고, IRP를 다음 하위 드라이버에 전달하고, 하위 드라이버가 IRP로 완료될 때까지 IRP를 처리하는 작업을 연기해야 합니다.

IRQL DISPATCH_LEVEL IoCompletion 루틴을 호출할 수 있지만 함수 또는 필터 드라이버는 IRQL = PASSIVE_LEVEL IRP를 처리해야 할 수 있습니다. IoCompletion 루틴에서 PASSIVE_LEVEL 돌아가려면 드라이버는 커널 이벤트를 사용할 수 있습니다. 드라이버는 커널 모드 이벤트를 설정하는 IoCompletion 루틴을 등록한 다음, 드라이버가 DispatchPnP 루틴에서 이벤트를 기다립니다. 이벤트가 설정되면 하위 드라이버가 IRP를 완료하고 드라이버가 IRP를 처리할 수 있습니다.

드라이버는 낮은 드라이버가 전원 IRP(IRP_MJ_POWER)를 완료할 때까지 기다리기 위해 이 기술을 사용하면 안 됩니다. IoCompletion 루틴에 설정된 DispatchPower 루틴에서 이벤트를 대기하면 교착 상태가 발생할 수 있습니다. 자세한 내용은 Power IRP 전달 을 참조하세요.

다음 두 그림에서는 드라이버가 낮은 드라이버가 PnP IRP를 완료할 때까지 대기하는 방법의 예를 보여 줍니다. 이 예제에서는 함수 및 버스 드라이버가 수행해야 하는 작업과 PnP 관리자 및 I/O 관리자와 상호 작용하는 방법을 보여줍니다.

플러그 앤 플레이 irp 처리를 연기하는 다이어그램, 1부.

다음 노트는 이전 그림의 원을 그리는 숫자에 해당합니다.

  1. PnP 관리자는 I/O 관리자를 호출하여 IRP를 디바이스 스택의 최상위 드라이버로 보냅니다.

  2. I/O 관리자는 상위 드라이버의 DispatchPnP 루틴을 호출합니다. 이 예제에서는 디바이스 스택에 드라이버가 두 개뿐이며(함수 드라이버와 부모 버스 드라이버) 함수 드라이버가 최상위 드라이버입니다.

  3. 함수 드라이버는 커널 모드 이벤트를 선언 및 초기화하고, 다음 하위 드라이버에 대한 스택 위치를 설정하고, 이 IRP에 대한 IoCompletion 루틴을 설정합니다.

    함수 드라이버는 IoCopyCurrentIrpStackLocationToNext 를 사용하여 스택 위치를 설정할 수 있습니다.

    IoSetCompletionRoutine 호출에서 함수 드라이버는 InvokeOnSuccess, InvokeOnErrorInvokeOnCancelTRUE로 설정하고 커널 모드 이벤트를 컨텍스트 매개 변수의 일부로 전달합니다.

  4. 함수 드라이버는 IRP를 처리하는 작업을 수행하기 전에 IoCallDriver 를 사용하여 IRP를 디바이스 스택 아래로 전달합니다.

  5. I/O 관리자는 해당 드라이버의 DispatchPnP 루틴을 호출하여 IRP를 디바이스 스택의 다음 하위 드라이버로 보냅니다.

  6. 이 예제의 다음으로 낮은 드라이버는 디바이스 스택에서 가장 낮은 드라이버인 부모 버스 드라이버입니다. 버스 드라이버는 해당 작업을 수행하여 디바이스를 시작합니다. 버스 드라이버는 Irp-IoStatus.Status>를 설정하고, 이 IRP와 관련된 경우 Irp-IoStatus.Information>를 설정하고, IoCompleteRequest를 호출하여 IRP를 완료합니다.

    버스 드라이버가 다른 드라이버 루틴을 호출하거나 I/O를 디바이스에 전송하여 시작하는 경우 버스 드라이버는 DispatchPnP 루틴에서 PnP IRP를 완료하지 않습니다. 대신 IoMarkIrpPending 으로 보류 중인 IRP를 표시하고 DispatchPnP 루틴에서 STATUS_PENDING 반환해야 합니다. 드라이버는 나중에 다른 드라이버 루틴(아마도 DPC 루틴)에서 IoCompleteRequest 를 호출합니다.

다음 그림에서는 디바이스 스택의 상위 드라이버가 연기된 IRP 처리를 다시 시작하는 예제의 두 번째 부분을 보여줍니다.

플러그 앤 플레이 irp 처리를 연기하는 다이어그램, 2부.

다음 노트는 이전 그림의 원을 그리는 숫자에 해당합니다.

  1. 버스 드라이버가 IoCompleteRequest를 호출하면 I/O 관리자는 상위 드라이버의 스택 위치를 검사하고 찾은 모든 IoCompletion 루틴을 호출합니다. 이 예제에서 I/O 관리자는 다음 상위 드라이버인 함수 드라이버에 대한 IoCompletion 루틴을 찾아 호출합니다.

  2. 함수 드라이버의 IoCompletion 루틴은 컨텍스트 매개 변수에 제공된 커널 모드 이벤트를 설정하고 STATUS_MORE_PROCESSING_REQUIRED 반환합니다.

    I/O 관리자가 현재 더 높은 드라이버에서 설정한 IoCompletion 루틴을 호출하지 않도록 IoCompletion 루틴은 STATUS_MORE_PROCESSING_REQUIRED 반환해야 합니다. IoCompletion 루틴은 이 상태 사용하여 완료를 포리스트에 추가하므로 드라이버의 DispatchPnP 루틴이 다시 제어할 수 있습니다. I/O 관리자는 이 드라이버의 DispatchPnP 루틴이 IRP를 완료하면 이 IRP에 대해 더 높은 드라이버의 IoCompletion 루틴 호출을 다시 시작합니다.

  3. I/O 관리자는 IRP 완료를 중지하고 IoCompleteRequest라는 루틴으로 컨트롤을 반환합니다. 이 예제에서는 버스 드라이버의 DispatchPnP 루틴입니다.

  4. 버스 드라이버는 STATUS_SUCCESS 또는 오류 상태 IRP 처리 결과를 나타내는 상태 있는 DispatchPnP 루틴에서 반환됩니다.

  5. IoCallDriver 는 호출자에게 컨트롤을 반환합니다. 이 예제에서는 함수 드라이버의 DispatchPnP 루틴입니다.

  6. 함수 드라이버의 DispatchPnP 루틴은 IRP 처리를 다시 시작합니다.

    IoCallDriver가 STATUS_PENDING 반환하는 경우 DispatchPnP 루틴은 IoCompletion 루틴이 호출되기 전에 실행을 다시 시작합니다. 따라서 DispatchPnP 루틴은 커널 이벤트가 IoCompletion 루틴에 의해 신호를 받을 때까지 기다려야 합니다. 이렇게 하면 모든 하위 드라이버가 완료될 때까지 DispatchPnP 루틴이 IRP 처리를 계속하지 않습니다.

    Irp-IoStatus.Status>가 오류로 설정된 경우 낮은 드라이버가 IRP에 실패했으며 함수 드라이버가 IRP를 계속 처리해서는 안 됩니다(필요한 정리 제외).

  7. 낮은 드라이버가 IRP를 성공적으로 완료하면 함수 드라이버가 IRP를 처리합니다.

    부모 버스 드라이버에서 먼저 처리되는 IRP의 경우 버스 드라이버는 일반적으로 Irp-IoStatus.Status>에서 성공적인 상태 설정하고 필요에 따라 Irp-IoStatus.Information>에서 값을 설정합니다. 함수 및 필터 드라이버는 IRP에 실패하지 않는 한 값을 IoStatus 에 그대로 둡니다.

    함수 드라이버의 DispatchPnP 루틴은 IoCompleteRequest 를 호출하여 IRP를 완료합니다. I/O 관리자는 I/O 완료 처리를 다시 시작합니다. 이 예제에서는 함수 드라이버 위에 필터 드라이버가 없으므로 호출할 IoCompletion 루틴이 더 이상 없습니다. IoCompleteRequest가 함수 드라이버 DispatchPnP 루틴에 컨트롤을 반환하면 DispatchPnP 루틴은 상태 반환합니다.

일부 IRP의 경우 함수 또는 필터 드라이버가 디바이스 스택을 백업하는 동안 IRP에 실패하면 PnP 관리자가 하위 드라이버에 알릴 수 있습니다. 예를 들어 함수 또는 필터 드라이버가 IRP_MN_START_DEVICE 실패하면 PnP 관리자는 디바이스 스택에 IRP_MN_REMOVE_DEVICE 보냅니다.