如何提交 URB

本主题介绍将初始化的 URB 提交到 USB 驱动程序堆栈以处理特定请求所需的步骤。

客户端驱动程序使用 I/O 控制代码 (IOCTL) IOCTL 与设备通信,这些请求在 I/O 请求数据包 (IRP) 类型为 IRP_MJ_INTERNAL_DEVICE_CONTROL。 对于特定于设备的请求(如选择配置请求),与 IRP 关联的 USB 请求块 (URB) 中介绍了该请求。 将 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_UrbAllocateUSBD_SelectConfigUrbAllocateAndBuildUSBD_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 设备发送请求