/*-- Copyright (c) 1998, 1999 Microsoft Corporation Module Name: urb.c Abstract: Environment: Kernel mode only. Notes: --*/ #include "usbverfy.h" #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, UsbVerify_CheckReplacedUrbs) #endif #define COMPUTE_SIZE ((USHORT) 0xFFFF) USHORT FunctionToUrbLength[] = { /* URB_FUNCTION_SELECT_CONFIGURATION 0x0000 */ COMPUTE_SIZE, // sizeof(struct _URB_SELECT_CONFIGURATION), /* URB_FUNCTION_SELECT_INTERFACE 0x0001 */ COMPUTE_SIZE, // sizeof(struct _URB_SELECT_INTERFACE), /* URB_FUNCTION_ABORT_PIPE 0x0002 */ sizeof(struct _URB_PIPE_REQUEST), /* URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL 0x0003 */ sizeof(struct _URB_FRAME_LENGTH_CONTROL), /* URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL 0x0004 */ sizeof(struct _URB_FRAME_LENGTH_CONTROL), /* URB_FUNCTION_GET_FRAME_LENGTH 0x0005 */ sizeof(struct _URB_GET_FRAME_LENGTH), /* URB_FUNCTION_SET_FRAME_LENGTH 0x0006 */ sizeof(struct _URB_SET_FRAME_LENGTH), /* URB_FUNCTION_GET_CURRENT_FRAME_NUMBER 0x0007 */ sizeof(struct _URB_GET_CURRENT_FRAME_NUMBER), /* URB_FUNCTION_CONTROL_TRANSFER 0x0008 */ sizeof(struct _URB_CONTROL_TRANSFER), /* URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 0x0009 */ sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), /* URB_FUNCTION_ISOCH_TRANSFER 0x000A */ COMPUTE_SIZE, // sizeof(struct _URB_ISOCH_TRANSFER), /* URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 0x000B */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ), /* URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE 0x000C */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ), /* URB_FUNCTION_SET_FEATURE_TO_DEVICE 0x000D */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_SET_FEATURE_TO_INTERFACE 0x000E */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_SET_FEATURE_TO_ENDPOINT 0x000F */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE 0x0010 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE 0x0011 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT 0x0012 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_GET_STATUS_FROM_DEVICE 0x0013 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ), /* URB_FUNCTION_GET_STATUS_FROM_INTERFACE 0x0014 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ), /* URB_FUNCTION_GET_STATUS_FROM_ENDPOINT 0x0015 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ), /* URB_FUNCTION_RESERVED0 0x0016 */ 0x0, /* URB_FUNCTION_VENDOR_DEVICE 0x0017 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_VENDOR_INTERFACE 0x0018 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_VENDOR_ENDPOINT 0x0019 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_CLASS_DEVICE 0x001A */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_CLASS_INTERFACE 0x001B */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_CLASS_ENDPOINT 0x001C */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_RESERVED 0x001D */ 0x0, /* URB_FUNCTION_RESET_PIPE 0x001E */ sizeof(struct _URB_PIPE_REQUEST ), /* URB_FUNCTION_CLASS_OTHER 0x001F */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_VENDOR_OTHER 0x0020 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ), /* URB_FUNCTION_GET_STATUS_FROM_OTHER 0x0021 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ), /* URB_FUNCTION_CLEAR_FEATURE_TO_OTHER 0x0022 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_SET_FEATURE_TO_OTHER 0x0023 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ), /* URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT 0x0024 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ), /* URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT 0x0025 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ), /* URB_FUNCTION_GET_CONFIGURATION 0x0026 */ sizeof(struct _URB_CONTROL_GET_CONFIGURATION_REQUEST ), /* URB_FUNCTION_GET_INTERFACE 0x0027 */ sizeof(struct _URB_CONTROL_GET_INTERFACE_REQUEST ), /* URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE 0x0028 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ), /* URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE 0x0029 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ), }; #define FUNCTION_TO_URB_LENGTH_MAX (sizeof(FunctionToUrbLength) / sizeof(USHORT)) USHORT UsbVerify_DetermineInterfaceSize( PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension, PURB Urb, PUSBD_INTERFACE_INFORMATION Interface ) { USHORT minimumLength; // // This assert is an iffy one. It is possible that a device can // set a zero BW interface which could be either an alternate setting // that contains 1 EP with a MaxPacketSize of 0 or an 0 EP interface. // Right now we'll leave this in and just choke on most streaming // devices that rachet down on shutting off. // // NOTE: This assert needs to be revisited for further determination // of its validity. At a minimum, the above behavior should // be documented somewhere. // UsbVerify_ASSERT(Interface->NumberOfPipes > 0, DeviceExtension->Self, NULL, Urb); // // Calculate the minimum required length of the interface field. This must // equal the sizeof interface plus any room for the pipe information. // We subtract the sizeof(USBD_PIPE_INFORMATION) from the interface // size since an interface doesn't have to have any pipes but // sizeof(USBD_INTERFACE_INFORMATION) returns the size of one pipe. // minimumLength = sizeof(USBD_INTERFACE_INFORMATION) - sizeof(USBD_PIPE_INFORMATION) + Interface -> NumberOfPipes * sizeof(USBD_PIPE_INFORMATION); if (Interface->Length) { // // Check that the reported interface length is at least the size // of the minimum length. // // NOTE: Need to find a better way to report the mimimum length // to the user. This is the same problem as with // verifying the overall URB length. // // NOTE: Should this be an == instead of >= to perform tighter // restrictions. Doing so would help verify that the // length field is accurate and not just some bogus // number that happened to be bigger than minimum length. // Are there reasons a client may legally specify a larger // size? // UsbVerify_ASSERT(Interface->Length >= minimumLength, DeviceExtension->Self, NULL, Urb); return Interface->Length; } else { // // Size of the Interface field // return (minimumLength); } } USHORT UsbVerify_DetermineUrbLength( PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension, PURB Urb ) { USHORT length, function; function = Urb->UrbHeader.Function; if (function < FUNCTION_TO_URB_LENGTH_MAX) { length = FunctionToUrbLength[function]; if (length != 0 && length != COMPUTE_SIZE) { return length; } } else { UsbVerify_Print(DeviceExtension, PRINT_ALWAYS, ("Uknnown function 0x%x with length of 0!\n", (ULONG) function)); return (USHORT) 0x0; } // // Check to see if the user is allowed to send this URB // if (length == 0) { UsbVerify_Assert("Client driver is not allowed to send this urb!", DeviceExtension->Self, NULL, Urb, NULL); return 0; } // // length == COMPUTE_SIZE. This means we need to do some math to figure // out the size that is required // switch (function) { case URB_FUNCTION_SELECT_CONFIGURATION: // // The UrbSelectConfiguration size should be at least equal to // sizeof(UrbSelectConfiguration). This would be true only when // the configuration is being deselected (ConfigurationDescriptor == // NULL). Otherwise, calculate the variable length portion that is // the interface information. Since, sizeof(UrbSelectConfiguration) // contains the size of the Interface field, subtract that off because // the value returned by UsbVerify_DeterminteInterfaceSize contains // the size of the Interface field as well as the memory hanging // on after it. // length = (USHORT) (sizeof(Urb->UrbSelectConfiguration)); if (NULL != Urb->UrbSelectConfiguration.ConfigurationDescriptor) { length -= sizeof(Urb->UrbSelectConfiguration.Interface); length += UsbVerify_DetermineInterfaceSize(DeviceExtension, Urb, &Urb->UrbSelectConfiguration.Interface); } UsbVerify_Print(DeviceExtension, PRINT_URB_INFO, ("select config computed size = 0x%x bytes\n", (ULONG) length)); break; case URB_FUNCTION_SELECT_INTERFACE: // // The sizeof UrbSelectInterface contains the size of the Interface // field. We want to get subtract that off because the value returned by // UsbVerify_DeterminteInterfaceSize contains the size of the Interface // field as well as the memory hanging on after it // length = (USHORT) (sizeof(Urb->UrbSelectInterface) - sizeof(Urb->UrbSelectInterface.Interface)) + UsbVerify_DetermineInterfaceSize(DeviceExtension, Urb, &Urb->UrbSelectInterface.Interface); UsbVerify_Print(DeviceExtension, PRINT_URB_INFO, ("select interface computed size = 0x%x bytes\n", (ULONG) length)); break; case URB_FUNCTION_ISOCH_TRANSFER: UsbVerify_ASSERT(Urb->UrbIsochronousTransfer.NumberOfPackets > 0, DeviceExtension->Self, NULL, Urb); length = sizeof(Urb->UrbIsochronousTransfer) + (sizeof(Urb->UrbIsochronousTransfer.IsoPacket[0]) * (Urb->UrbIsochronousTransfer.NumberOfPackets - 1)); UsbVerify_Print(DeviceExtension, PRINT_URB_INFO, ("isoch transfer computed size = 0x%x bytes\n", (ULONG) length)); break; default: UsbVerify_Print(DeviceExtension, PRINT_URB_ERROR, ("need to add a case for funciton 0x%x\n", (ULONG) function)); DbgBreakPoint(); length = 0; } return length; } VOID UsbVerify_PreProcessUrb( PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension, PIRP Irp, PURB *OriginalUrb ) { PURB urb, newUrb = NULL; USHORT expectedUrbLength; urb = URB_FROM_IRP(Irp); *OriginalUrb = NULL; // // All URBs must definitely be sent only after START_DEVICE has been sent // down and up the stack. Most USB clients do not handle SURPRISE_REMOVE, // they will continue to send requests in that state. // UsbVerify_ASSERT((DeviceExtension->VerifyState == Started) || (DeviceExtension->VerifyState == SurpriseRemoved), DeviceExtension->Self, Irp, urb); UsbVerify_ASSERT(DeviceExtension->PowerState == PowerDeviceD0, DeviceExtension->Self, Irp, urb); if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_TEST_MEMORY)) { // // NOTE implement this // // MmProbeForWrite () ? // ExQueryPoolBlockSize () ? // any Mm function to verify the pool? } // // Check the length that the calling driver passed in to the urb // function. We should be able to determine the expected length in // all cases and the length field should match that length. // expectedUrbLength = UsbVerify_DetermineUrbLength(DeviceExtension, urb); UsbVerify_Print(DeviceExtension, PRINT_URB_INFO, ("expected urb length = 0x%x bytes\n", expectedUrbLength)); UsbVerify_ASSERT(urb->UrbHeader.Length == expectedUrbLength, DeviceExtension->Self, Irp, urb); if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_REPLACE_URBS)) { if (0 == expectedUrbLength) { goto LowMemory; } // // Can't use AllocStruct because the urb size can be easily be bigger // then sizeof(URB) // newUrb = (PURB) ExAllocatePool (NonPagedPool, expectedUrbLength); if (newUrb != NULL) { // // Copy the contents into the new urb and then fill the original with // USB_VERIFY_FULL. We will copy back the contents when we are done // in the completion routine. // RtlCopyMemory(newUrb, urb, expectedUrbLength); newUrb -> UrbHeader.Length = expectedUrbLength; RtlFillMemory(urb, expectedUrbLength, USB_VERIFY_FILL); // // Replace the caller supplied URB with ours // URB_FROM_IRP(Irp) = newUrb; *OriginalUrb = urb; UsbVerify_Print(DeviceExtension, PRINT_URB_INFO, ("Replacing urb\n" "\t Original URB 0x%x\n" "\t Verify URB 0x%x\n" "\t in IRP 0x%x on device 0x%x\n", urb, newUrb, Irp, DeviceExtension->Self)); } else { LowMemory: // // No memory! Behave as if the replace urb flag is not set // InterlockedIncrement(&DeviceExtension->ReplaceUrbsLowMemoryCount); } } if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_TRACK_URBS)) { PUSB_VERIFY_TRACK_URB verify; PLIST_ENTRY entry; KIRQL irql; // // Make sure the client is reusing the same URB in two different IRPs. // According to JD, some driver writers actually try to do this! // UsbVerify_LockUrbList(DeviceExtension, irql); for (entry = DeviceExtension->UrbList.Flink; entry != &DeviceExtension->UrbList; /* increment handled before we free the struct */ ) { verify = CONTAINING_RECORD(entry, USB_VERIFY_TRACK_URB, Link); // // Any previous client sent URBs will be in verify->Urb (we are NOT // replacing URBs( or in verify->OriginalUrb (we ARE replacing URBs) // UsbVerify_ASSERT(verify->Urb != urb && verify->OriginalUrb != urb, DeviceExtension->Self, Irp, urb); entry = entry->Flink; } UsbVerify_UnlockUrbList(DeviceExtension, irql); verify = AllocStruct(NonPagedPool, USB_VERIFY_TRACK_URB, 1); if (verify) { RtlZeroMemory(verify, sizeof(*verify)); verify->Irp = Irp; if (newUrb) { verify->Urb = newUrb; verify->OriginalUrb = urb; } else { verify->Urb = urb; verify->OriginalUrb = NULL; } KeQueryTickCount(&verify->ArrivalTime); UsbVerify_LockUrbList(DeviceExtension, irql); InsertTailList(&DeviceExtension->UrbList, &verify->Link); UsbVerify_UnlockUrbList(DeviceExtension, irql); } else { InterlockedIncrement(&DeviceExtension->UrbListLowMemoryCount); } } InterlockedIncrement(&DeviceExtension->UrbListCount); } VOID UsbVerify_PostProcessUrb( PDEVICE_OBJECT DeviceObject, PIRP Irp, PURB OldUrb ) { PUSB_VERIFY_DEVICE_EXTENSION devExt; PURB urb; ULONG urbListCount; devExt = GetExtension(DeviceObject); // // This urb may be an urb we sent in place of OldUrb, if OldUrb != NULL // urb = URB_FROM_IRP(Irp); // // We always keep (at least) a count of pending urbs // urbListCount = InterlockedDecrement(&devExt->UrbListCount); UsbVerify_ASSERT(urbListCount >= 0, devExt->Self, Irp, urb); if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_TRACK_URBS)) { PUSB_VERIFY_TRACK_URB verify; PLIST_ENTRY entry; KIRQL irql; BOOLEAN found = FALSE; UsbVerify_LockUrbList(devExt, irql); for (entry = devExt->UrbList.Flink; entry != &devExt->UrbList; entry = entry->Flink) { verify = CONTAINING_RECORD(entry, USB_VERIFY_TRACK_URB, Link); if (verify->Irp == Irp) { UsbVerify_ASSERT(urb == verify->Urb, devExt->Self, Irp, urb); RemoveEntryList(&verify->Link); found = TRUE; break; } } UsbVerify_UnlockUrbList(devExt, irql); if (found) { ExFreePool(verify); } else { ULONG urbListLowMemoryCount; // // We go through the troule of assigning the return value to a local // variable so it will be easier to inspect this value in the debugger // if we need to view this value // urbListLowMemoryCount = InterlockedDecrement(&devExt->UrbListLowMemoryCount); UsbVerify_ASSERT(urbListLowMemoryCount >= 0, devExt->Self, Irp, urb); } } if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_REPLACE_URBS)) { if (ARGUMENT_PRESENT(OldUrb)) { PUCHAR buffer, bufferEnd; buffer = (PUCHAR) OldUrb; bufferEnd = buffer + urb->UrbHeader.Length; // // Make sure the caller hasn't scribbled on the URB. // // NTOE: is there a way for us to mark the memory as READ ONLY? // for ( ; buffer < bufferEnd; buffer++) { UsbVerify_ASSERT(*buffer == USB_VERIFY_FILL, devExt->Self, Irp, OldUrb); } UsbVerify_Print(devExt, PRINT_URB_INFO, ("returning original URB\n" "\t Original URB 0x%x\n" "\t Verify URB 0x%x\n" "\t in IRP 0x%x on device 0x%x\n", OldUrb, urb, Irp, devExt->Self)); // // Copy the contents from our replacement URB into the caller's URB // NOTE: the length field in the urb that we use in this copy // is the calculated expected urb length. There may // be problems if the client was expecting more data in the // original urb or had not allocated enough space to hold // everything. // RtlCopyMemory(OldUrb, urb, urb->UrbHeader.Length); // // We are done with our replacement URB. Free it and set up urb to // point to valid pool. // ExFreePool(urb); urb = OldUrb; // // This is necessary because we are more often than not going to play // with this urb after this function call. The current URB in the IRP // has just been freed, so we need to put the old and valid one back // URB_FROM_IRP(Irp) = OldUrb; } else { ULONG replaceUrbsLowMemoryCount; // // We go through the troule of assigning the return value to a local // variable so it will be easier to inspect this value in the debugger // if we need to view this value // replaceUrbsLowMemoryCount = InterlockedDecrement(&devExt->ReplaceUrbsLowMemoryCount); UsbVerify_ASSERT(replaceUrbsLowMemoryCount >= 0, devExt->Self, Irp, urb); } } if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_TEST_MEMORY)) { // // NOTE: implement this // // MmProbeForWrite () ? // ExQueryPoolBlockSize () ? // any Mm function to verify the pool? } // // NOTE: should we check anything else on the way back up? Pipes for instance? // } VOID UsbVerify_FreePendingUrbsList( PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension ) { PUSB_VERIFY_TRACK_URB verify; PLIST_ENTRY entry; ULONG urbListCount, urbListLowMemoryCount; KIRQL irql; // // We go through the trouble of assigning the return value to a local // variable so it will be easier to inspect this value in the debugger // if we need to view this value // // // NOTE: Looks like alot of clients don't cancel any of their pending requests // until after they have the remove device. Perhaps this should be // guarded by a VerifyFlags (instead of the #if 0) ? // urbListCount = InterlockedCompareExchange(&DeviceExtension->UrbListCount, 0, 0); UsbVerify_ASSERT(urbListCount == 0, DeviceExtension->Self, NULL, NULL); urbListLowMemoryCount = InterlockedCompareExchange(&DeviceExtension->UrbListLowMemoryCount, 0, 0); UsbVerify_ASSERT(urbListLowMemoryCount == 0, DeviceExtension->Self, NULL, NULL); if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_TRACK_URBS)) { UsbVerify_LockUrbList(DeviceExtension, irql); for (entry = DeviceExtension->UrbList.Flink; entry != &DeviceExtension->UrbList; /* increment handled before we free the struct */ ) { verify = CONTAINING_RECORD(entry, USB_VERIFY_TRACK_URB, Link); UsbVerify_ASSERT(verify->Urb != NULL && verify->Irp != NULL, // "pended URB after remove device", DeviceExtension->Self, verify->Irp, verify->Urb); UsbVerify_LogError(IDS_STALE_URB, DeviceExtension, verify->Irp, verify->Urb); entry = entry->Flink; RemoveEntryList(&verify->Link); ExFreePool(verify); } UsbVerify_UnlockUrbList(DeviceExtension, irql); } } VOID UsbVerify_CheckReplacedUrbs( PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension ) { ULONG replaceUrbsLowMemoryCount; PAGED_CODE(); // // We go through the troule of assigning the return value to a local // variable so it will be easier to inspect this value in the debugger // if we need to view this value // replaceUrbsLowMemoryCount = InterlockedCompareExchange(&DeviceExtension->ReplaceUrbsLowMemoryCount, 0, 0); UsbVerify_ASSERT(replaceUrbsLowMemoryCount == 0, DeviceExtension->Self, NULL, NULL); } NTSTATUS UsbVerify_UrbComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Sanity check an URB on the way back up --*/ { PUSB_VERIFY_DEVICE_EXTENSION devExt = GetExtension(DeviceObject); UsbVerify_PostVerifyUrb(DeviceObject, Irp); UsbVerify_PostProcessUrb(DeviceObject, Irp, (PURB) Context); // // We are done with the irp, return the status code in the irp so the irp // will progress up the stack // UsbVerify_Print(devExt, PRINT_URB_NOISE, ("UrbComplete, returning 0x%x\n", Irp->IoStatus.Status)); if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } // // the IO completion code looks for STATUS_MORE_PROCESSING_REQUIRED, so any // other value will do (don't waste valuable cycles dereffing // Irp->IoStatus.Status) // return STATUS_SUCCESS; } NTSTATUS UsbVerify_SendUrbSynchronously( PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension, PIRP Irp, PURB OldUrb ) { KEVENT event; NTSTATUS status; IoCopyCurrentIrpStackLocationToNext(Irp); KeInitializeEvent(&event, SynchronizationEvent, FALSE ); // // Don't use UsbVerify_UrbComplete because its context must be an PURB, not // a PKEVENT // IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) UsbVerify_Complete, &event, TRUE, TRUE, TRUE); // No need for Cancel status = IoCallDriver(DeviceExtension->TopOfStack, Irp); if (STATUS_PENDING == status) { KeWaitForSingleObject( &event, Executive, // Waiting for reason of a driver KernelMode, // Waiting in kernel mode FALSE, // No allert NULL); // No timeout status = Irp->IoStatus.Status; } UsbVerify_PostProcessUrb(DeviceExtension->Self, Irp, OldUrb); return status; }