Como enviar um URB
Este tópico descreve as etapas necessárias para enviar um URB inicializado para a pilha de driver USB para processar uma solicitação específica.
Um driver cliente se comunica com seu dispositivo usando solicitações de IOCTL (código de controle de E/S) que são entregues ao dispositivo em IRPs (pacotes de solicitação de E/S) do tipo IRP_MJ_INTERNAL_DEVICE_CONTROL. Para uma solicitação específica do dispositivo, como uma solicitação de configuração de seleção, a solicitação é descrita em um URB (Bloco de Solicitação USB) associado a um IRP. O processo de associar um URB a um IRP e enviar a solicitação para a pilha de driver USB é chamado de enviar um URB. Para enviar um URB, o driver do cliente deve usar IOCTL_INTERNAL_USB_SUBMIT_URB como o código de controle do dispositivo. O IOCTL é um dos códigos de controle "internos" que fornecem uma interface de E/S que um driver de cliente usa para gerenciar seu dispositivo e a porta à qual o dispositivo está conectado. Os aplicativos de modo de usuário não têm acesso a essas interfaces de E/S internas. Para obter mais códigos de controle para drivers de modo kernel, consulte IOCTLs do modo Kernel para drivers de cliente USB.
Pré-requisitos
Antes de enviar uma solicitação para a pilha de driver do Barramento Serial Universal (USB), o driver cliente deve alocar uma estrutura URB e formatar essa estrutura dependendo do tipo de solicitação. Para obter mais informações, consulte Alocando e criando URBs e práticas recomendadas: usando URBs.
Instruções
Aloque um IRP para o URB chamando a rotina IoAllocateIrp . Você deve fornecer o tamanho da pilha do objeto de dispositivo que recebe o IRP. Você recebeu um ponteiro para esse objeto de dispositivo em uma chamada anterior para a rotina IoAttachDeviceToDeviceStack . O tamanho da pilha é armazenado no membro StackSize da estrutura DEVICE_OBJECT .
Obtenha um ponteiro para o primeiro local de pilha do IRP (IO_STACK_LOCATION) chamando IoGetNextIrpStackLocation.
Defina o membro MajorFunction da estrutura IO_STACK_LOCATION como IRP_MJ_INTERNAL_DEVICE_CONTROL.
Defina o membro Parameters.DeviceIoControl.IoControlCode da estrutura IO_STACK_LOCATION como IOCTL_INTERNAL_USB_SUBMIT_URB.
Defina o membro Parameters.Others.Argument1 da estrutura IO_STACK_LOCATION para o endereço da estrutura URB inicializada. Para associar o IRP ao URB, você poderá chamar USBD_AssignUrbToIoStackLocation somente se o URB tiver sido alocado por USBD_UrbAllocate, USBD_SelectConfigUrbAllocateAndBuild ou USBD_SelectInterfaceUrbAllocateAndBuild.
Defina uma rotina de conclusão chamando IoSetCompletionRoutineEx.
Se você enviar o URB de forma assíncrona, passe um ponteiro para a rotina de conclusão implementada pelo chamador e seu contexto. O chamador libera o IRP em sua rotina de conclusão.
Se você estiver enviando o IRP de forma síncrona, implemente uma rotina de conclusão e passe um ponteiro para essa rotina na chamada para IoSetCompletionRoutineEx. A chamada também requer um objeto KEVENT inicializado no parâmetro Context . Em sua rotina de conclusão, defina o evento como o estado sinalizado.
Chame IoCallDriver para encaminhar o IRP preenchido para o próximo objeto de dispositivo inferior na pilha do dispositivo. Para uma chamada síncrona, depois de chamar IoCallDriver, aguarde o objeto de evento chamando KeWaitForSingleObject para obter a notificação de evento.
Após a conclusão do IRP, marcar o membro IoStatus.Status do IRP e avalie o resultado. Se ioStatus.Status for STATUS_SUCCESS, a solicitação foi bem-sucedida.
Envio síncrono de USB
O exemplo a seguir mostra como enviar um URB de forma síncrona.
// 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;
}
Envio assíncrono usb
O exemplo a seguir mostra como enviar um URB de forma assíncrona.
// 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;
}