URB를 제출하는 방법

이 항목에서는 특정 요청을 처리하기 위해 초기화된 URB를 USB 드라이버 스택에 제출하는 데 필요한 단계를 설명합니다.

클라이언트 드라이버는 IRP_MJ_INTERNAL_DEVICE_CONTROL 유형의 I/O 요청 패킷(IRP)으로 디바이스에 전달되는 I/O 제어 코드(IOCTL) 요청을 사용하여 디바이스와 통신합니다. select-configuration 요청과 같은 디바이스별 요청의 경우 요청은 IRP와 연결된 URB(USB 요청 블록)에 설명되어 있습니다. URB를 IRP와 연결하고 USB 드라이버 스택에 요청을 보내는 프로세스를 URB 제출이라고 합니다. URB를 제출하려면 클라이언트 드라이버가 IOCTL_INTERNAL_USB_SUBMIT_URB 디바이스 제어 코드로 사용해야 합니다. IOCTL은 클라이언트 드라이버가 디바이스 및 디바이스가 연결된 포트를 관리하는 데 사용하는 I/O 인터페이스를 제공하는 "내부" 제어 코드 중 하나입니다. 사용자 모드 애플리케이션은 해당 내부 I/O 인터페이스에 액세스할 수 없습니다. 커널 모드 드라이버에 대한 자세한 제어 코드는 USB 클라이언트 드라이버용 커널 모드 IOCTL을 참조하세요.

사전 준비 사항

USB(유니버설 직렬 버스) 드라이버 스택에 요청을 보내기 전에 클라이언트 드라이버는 요청 유형에 따라 URB 구조와 해당 구조체의 형식을 할당해야 합니다. 자세한 내용은 URL 할당 및 빌드 및모범 사례: URL 사용을 참조하세요.

지침

  1. IoAllocateIrp 루틴을 호출하여 URB에 대한 IRP를 할당합니다. IRP를 수신하는 디바이스 개체의 스택 크기를 제공해야 합니다. IoAttachDeviceToDeviceStack 루틴에 대한 이전 호출에서 해당 디바이스 개체에 대한 포인터를 받았습니다. 스택 크기는 DEVICE_OBJECT 구조체의 StackSize 멤버에 저장됩니다.

  2. IoGetNextIrpStackLocation을 호출하여 IRP의 첫 번째 스택 위치(IO_STACK_LOCATION)에 대한 포인터를 가져옵니다.

  3. IO_STACK_LOCATION 구조체의 MajorFunction 멤버를 IRP_MJ_INTERNAL_DEVICE_CONTROL 설정합니다.

  4. IO_STACK_LOCATION 구조체의 Parameters.DeviceIoControl.IoControlCode 멤버를 IOCTL_INTERNAL_USB_SUBMIT_URB 설정합니다.

  5. IO_STACK_LOCATION 구조체의 Parameters.Others.Argument1 멤버를 초기화된 URB 구조체의 주소로 설정합니다. IRP를 URB에 연결하려면 URB가 USBD_UrbAllocate, USBD_SelectConfigUrbAllocateAndBuild 또는 USBD_SelectInterfaceUrbAllocateAndBuild 할당된 경우에만 USBD_AssignUrbToIoStackLocation 호출할 수 있습니다.

  6. IoSetCompletionRoutineEx를 호출하여 완료 루틴을 설정합니다.

    URB를 비동기적으로 제출하는 경우 호출자가 구현한 완료 루틴 및 해당 컨텍스트에 대한 포인터를 전달합니다. 호출자는 완료 루틴에서 IRP를 해제합니다.

    IRP를 동기적으로 제출하는 경우 완료 루틴을 구현하고 IoSetCompletionRoutineEx 호출에서 해당 루틴에 대한 포인터를 전달합니다. 또한 호출에는 Context 매개 변수에서 초기화된 KEVENT 개체가 필요합니다. 완료 루틴에서 이벤트를 신호 상태로 설정합니다.

  7. IoCallDriver를 호출하여 채워진 IRP를 디바이스 스택의 다음 하위 디바이스 개체로 전달합니다. 동기 호출의 경우 IoCallDriver를 호출한 후 KeWaitForSingleObject 를 호출하여 이벤트 개체를 기다렸다가 이벤트 알림을 받습니다.

  8. IRP가 완료되면 IRP의 IoStatus.Status 멤버를 검사 결과를 평가합니다. IoStatus.Status가 STATUS_SUCCESS 요청이 성공했습니다.

USB 동기 제출

다음 예제에서는 URB를 동기적으로 제출하는 방법을 보여줍니다.

// The SubmitUrbSync routine submits an URB synchronously.
//
// Parameters:
//      DeviceExtension: Pointer to the caller's device extension. The
//                       device extension must have a pointer to
//                       the next lower device object in the device stacks.  
//
//      Irp: Pointer to an IRP allocated by the caller.
//
//      Urb: Pointer to an URB that is allocated by  USBD_UrbAllocate,
//           USBD_IsochUrbAllocate, USBD_SelectConfigUrbAllocateAndBuild,
//           or USBD_SelectInterfaceUrbAllocateAndBuild.

//      CompletionRoutine: Completion routine.
//
// Return Value:
//
//      NTSTATUS  

NTSTATUS SubmitUrbSync( PDEVICE_EXTENSION DeviceExtension,
                       PIRP Irp,
                       PURB Urb,  
                       PIO_COMPLETION_ROUTINE SyncCompletionRoutine)  

{

    NTSTATUS  ntStatus;  
    KEVENT    kEvent;

    PIO_STACK_LOCATION nextStack;

    // Get the next stack location.
    nextStack = IoGetNextIrpStackLocation(Irp);  

    // Set the major code.
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;  

    // Set the IOCTL code for URB submission.
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;  

    // Attach the URB to this IRP.
    // The URB must be allocated by USBD_UrbAllocate, USBD_IsochUrbAllocate,
    // USBD_SelectConfigUrbAllocateAndBuild, or USBD_SelectInterfaceUrbAllocateAndBuild.
    USBD_AssignUrbToIoStackLocation (DeviceExtension->UsbdHandle, nextStack, Urb);

    KeInitializeEvent(&kEvent, NotificationEvent, FALSE);

    ntStatus = IoSetCompletionRoutineEx ( DeviceExtension->NextDeviceObject,  
        Irp,  
        SyncCompletionRoutine,  
        (PVOID) &kEvent,  
        TRUE,
        TRUE,
        TRUE);

    if (!NT_SUCCESS(ntStatus))
    {
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "IoSetCompletionRoutineEx failed. \n" ));
        goto Exit;
    }

    ntStatus = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);  

    if (ntStatus == STATUS_PENDING)
    {
        KeWaitForSingleObject ( &kEvent,
            Executive,
            KernelMode,
            FALSE,
            NULL);
    }

    ntStatus = Irp->IoStatus.Status;

Exit:

    if (!NT_SUCCESS(ntStatus))
    {
        // We hit a failure condition,
        // We will free the IRP

        IoFreeIrp(Irp);
        Irp = NULL;
    }


    return ntStatus;
}

// The SyncCompletionRoutine routine is the completion routine
// for the synchronous URB submit request.
//
// Parameters:
//
//      DeviceObject: Pointer to the device object.
//      Irp:          Pointer to an I/O Request Packet.
//      CompletionContext: Context for the completion routine.
//
// Return Value:
//
//      NTSTATUS  

NTSTATUS SyncCompletionRoutine ( PDEVICE_OBJECT DeviceObject,
                                PIRP           Irp,
                                PVOID          Context)
{
    PKEVENT kevent;

    kevent = (PKEVENT) Context;

    if (Irp->PendingReturned == TRUE)
    {
        KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
    }

    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Request completed. \n" ));


    return STATUS_MORE_PROCESSING_REQUIRED;
}

USB 비동기 제출

다음 예제에서는 URB를 비동기적으로 제출하는 방법을 보여줍니다.

// The SubmitUrbASync routine submits an URB asynchronously.
//
// Parameters:
//
// Parameters:
//      DeviceExtension: Pointer to the caller's device extension. The
//                       device extension must have a pointer to
//                       the next lower device object in the device stacks.  
//
//      Irp: Pointer to an IRP allocated by the caller.
//
//      Urb: Pointer to an URB that is allocated by  USBD_UrbAllocate,
//           USBD_IsochUrbAllocate, USBD_SelectConfigUrbAllocateAndBuild,
//           or USBD_SelectInterfaceUrbAllocateAndBuild.

//      CompletionRoutine: Completion routine.
//
//      CompletionContext: Context for the completion routine.
//
//
// Return Value:
//
//      NTSTATUS

NTSTATUS SubmitUrbASync ( PDEVICE_EXTENSION DeviceExtension,
                         PIRP Irp,
                         PURB Urb,  
                         PIO_COMPLETION_ROUTINE CompletionRoutine,  
                         PVOID CompletionContext)  
{
    // Completion routine is required if the URB is submitted asynchronously.
    // The caller's completion routine releases the IRP when it completes.


    NTSTATUS ntStatus = -1;  

    PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);  

    // Attach the URB to this IRP.
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;  

    // Attach the URB to this IRP.
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;  

    // Attach the URB to this IRP.
    (void) USBD_AssignUrbToIoStackLocation (DeviceExtension->UsbdHandle, nextStack, Urb);  

    // Caller's completion routine will free the irp when it completes.
    ntStatus = IoSetCompletionRoutineEx ( DeviceExtension->NextDeviceObject,
        Irp,  
        CompletionRoutine,  
        CompletionContext,  
        TRUE,
        TRUE,
        TRUE);

    if (!NT_SUCCESS(ntStatus))
    {
        goto Exit;
    }

    (void) IoCallDriver(DeviceExtension->NextDeviceObject, Irp);

Exit:
    if (!NT_SUCCESS(ntStatus))
    {
        // We hit a failure condition,
        // We will free the IRP

        IoFreeIrp(Irp);
        Irp = NULL;
    }

    return ntStatus;
}

USB 디바이스에 요청 보내기