Cómo enviar un URB
En este tema se describen los pasos necesarios para enviar un URB inicializado a la pila de controladores USB para procesar una solicitud determinada.
Un controlador cliente se comunica con su dispositivo mediante solicitudes de código de control de E/S (IOCTL) que se entregan al dispositivo en paquetes de solicitud de E/S (IRP) de tipo IRP_MJ_INTERNAL_DEVICE_CONTROL. Para una solicitud específica del dispositivo, como una solicitud de configuración de selección, la solicitud se describe en un bloque de solicitudes USB (URB) asociado a un IRP. El proceso de asociar un URB con un IRP y enviar la solicitud a la pila del controlador USB se conoce como envío de un URB. Para enviar un URB, el controlador cliente debe usar IOCTL_INTERNAL_USB_SUBMIT_URB como código de control de dispositivo. El IOCTL es uno de los códigos de control "internos" que proporcionan una interfaz de E/S que un controlador de cliente usa para administrar su dispositivo y el puerto al que está conectado el dispositivo. Las aplicaciones en modo de usuario no tienen acceso a esas interfaces de E/S internas. Para obtener más códigos de control para controladores de modo kernel, consulte IoCTLs en modo kernel para controladores de cliente USB.
Requisitos previos
Antes de enviar una solicitud a la pila de controladores del Bus serie universal (USB), el controlador cliente debe asignar una estructura URB y dar formato a esa estructura en función del tipo de solicitud. Para obtener más información, consulte Asignación y creación de direcciones URL y procedimientos recomendados: Uso de direcciones URL.
Instrucciones
Asigne un IRP para el URB llamando a la rutina IoAllocateIrp . Debe proporcionar el tamaño de pila del objeto de dispositivo que recibe el IRP. Recibió un puntero a ese objeto de dispositivo en una llamada anterior a la rutina IoAttachDeviceToDeviceStack . El tamaño de la pila se almacena en el miembro StackSize de la estructura DEVICE_OBJECT .
Obtenga un puntero a la primera ubicación de pila (IO_STACK_LOCATION) del IRP llamando a IoGetNextIrpStackLocation.
Establezca el miembro MajorFunction de la estructura IO_STACK_LOCATIONen IRP_MJ_INTERNAL_DEVICE_CONTROL.
Establezca el miembro Parameters.DeviceIoControl.IoControlCode de la estructura IO_STACK_LOCATION en IOCTL_INTERNAL_USB_SUBMIT_URB.
Establezca el miembro Parameters.Others.Argument1 de la estructura IO_STACK_LOCATION en la dirección de la estructura URB inicializada. Para asociar el IRP al URB, también puede llamar a USBD_AssignUrbToIoStackLocation solo si el URB se asignó por USBD_UrbAllocate, USBD_SelectConfigUrbAllocateAndBuild o USBD_SelectInterfaceUrbAllocateAndBuild.
Establezca una rutina de finalización llamando a IoSetCompletionRoutineEx.
Si envía el URB de forma asincrónica, pase un puntero a la rutina de finalización implementada por el autor de la llamada y su contexto. El autor de la llamada libera el IRP en su rutina de finalización.
Si va a enviar irP de forma sincrónica, implemente una rutina de finalización y pase un puntero a esa rutina en la llamada a IoSetCompletionRoutineEx. La llamada también requiere un objeto KEVENT inicializado en el parámetro Context . En la rutina de finalización, establezca el evento en el estado señalado.
Llame a IoCallDriver para reenviar el IRP rellenado al siguiente objeto de dispositivo inferior de la pila de dispositivos. Para una llamada sincrónica, después de llamar a IoCallDriver, espere al objeto de evento llamando a KeWaitForSingleObject para obtener la notificación de eventos.
Después de completar el IRP, compruebe el miembro IoStatus.Status de IRP y evalúe el resultado. Si ioStatus.Status es STATUS_SUCCESS, la solicitud se realizó correctamente.
Envío sincrónico USB
En el ejemplo siguiente se muestra cómo enviar un URB de forma sincrónica.
// 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;
}
Envío asincrónico USB
En el ejemplo siguiente se muestra cómo enviar un URB de forma asincrónica.
// 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;
}