如何提交 URB
本主題描述將初始化的 URB 提交至 USB 驅動程式堆疊以處理特定要求所需的步驟。
用戶端驅動程式會使用 I/O 控制程式碼與裝置通訊, (IOCTL) 在 I/O 要求封包中傳遞至裝置的要求, (I/O 要求封包) 類型 為 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 和 最佳做法:使用 URB。
指示
呼叫 IoAllocateIrp 常式,為 URB 配置 IRP。 您必須提供接收 IRP 之裝置物件的堆疊大小。 您在先前呼叫 IoAttachDeviceToDeviceStack 常式中收到該裝置物件的指標。 堆疊大小會儲存在DEVICE_OBJECT結構的StackSize成員中。
藉由呼叫IoGetNextIrpStackLocation,取得 IRP 第一個堆疊位置的指標 (IO_STACK_LOCATION) 。
將IO_STACK_LOCATION結構的MajorFunction成員設定為IRP_MJ_INTERNAL_DEVICE_CONTROL。
將IO_STACK_LOCATION結構的Parameters.DeviceIoControl.IoControlCode成員設定為IOCTL_INTERNAL_USB_SUBMIT_URB。
將IO_STACK_LOCATION結構的Parameters.Others.Argument1成員設定為初始化URB結構的位址。 若要將 IRP 與 URB 產生關聯,您只能在URB 由USBD_UrbAllocate、USBD_SelectConfigUrbAllocateAndBuild或USBD_SelectInterfaceUrbAllocateAndBuild配置時呼叫USBD_AssignUrbToIoStackLocation。
藉由呼叫 IoSetCompletionRoutineEx來設定完成常式。
如果您以非同步方式提交 URB,請將指標傳遞至呼叫端實作的完成常式及其內容。 呼叫端會在其完成常式中釋放 IRP。
如果您要同步提交 IRP,請實作完成常式,並在呼叫 IoSetCompletionRoutineEx中傳遞該常式的指標。 呼叫也需要 CoNtext 參數中初始化的 KEVENT 物件。 在您的完成常式中,將事件設定為已發出訊號的狀態。
呼叫 IoCallDriver ,將填入的 IRP 轉送至裝置堆疊中的下一個較低裝置物件。 針對同步呼叫,在呼叫 IoCallDriver之後,呼叫 KeWaitForSingleObject 以等候事件物件,以取得事件通知。
完成 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;
}