URB の送信方法
このトピックでは、特定の要求を処理するために初期化された URB を USB ドライバー スタックに送信する手順について説明しています。
クライアント ドライバーは、IRP_MJ_INTERNAL_DEVICE_CONTROL 型の I/O 要求パケット (IRP) でデバイスに配信される I/O 制御コード (IOCTL) 要求を使用して、そのデバイスと通信します。 構成選択要求などのデバイス固有の要求の場合、要求は IRP に関連付けられている USB 要求ブロック (URB) で記述されます。 URB を IRP に関連付け、USB ドライバー スタックに要求を送信するプロセスは、URB の送信と呼ばれます。 URB を送信するには、クライアント ドライバーがデバイス制御コードとして IOCTL_INTERNAL_USB_SUBMIT_URB を使用する必要があります。 IOCTL は、クライアント ドライバーがそのデバイスとデバイスの接続先ポートを管理するために使用する I/O インターフェイスを提供する "内部" 制御コードの 1 つです。 ユーザー モード アプリケーションは、これらの内部 I/O インターフェイスにアクセスすることができません。 カーネル モード ドライバーの他の制御コードについては、「USB クライアント ドライバーのカーネル モード IOCTL」をご覧ください。
前提条件
ユニバーサル シリアル バス (USB) ドライバー スタックに要求を送信する前に、クライアント ドライバーは、要求の種類に応じて URB 構造とその構造の形式を割り当てる必要があります。 詳しくは、「URB の割り当てと構築」および「ベスト プラクティス: URL の使用」をご覧ください。
手順
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;
}