/*************************************************************************** Copyright (c) 1998 Microsoft Corporation Module Name: UTILS.C Abstract: Routines that don't fit anywhere else. Environment: kernel mode only Notes: THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Copyright (c) 1998 Microsoft Corporation. All Rights Reserved. Revision History: 12/23/97 : created Authors: Tom Green ****************************************************************************/ #include #include #include #include #include #include #include #include #ifdef WMI_SUPPORT #include #include #include #endif #include "usbser.h" #include "usbserpw.h" #include "serioctl.h" #include "utils.h" #include "debugwdm.h" #ifdef ALLOC_PRAGMA #if DBG #pragma alloc_text(PAGEUBS0, UsbSerLockPagableCodeSection) #endif #pragma alloc_text(PAGEUBS0, UsbSerGetRegistryKeyValue) #pragma alloc_text(PAGEUBS0, UsbSerUndoExternalNaming) #pragma alloc_text(PAGEUBS0, UsbSerDoExternalNaming) #pragma alloc_text(PAGEUBS0, StopDevice) #pragma alloc_text(PAGEUBS0, StartPerfTimer) #pragma alloc_text(PAGEUBS0, StopPerfTimer) #pragma alloc_text(PAGEUBS0, BytesPerSecond) #pragma alloc_text(PAGEUBS0, CallUSBD) #pragma alloc_text(PAGEUBS0, ConfigureDevice) #pragma alloc_text(PAGEUBS0, BuildRequest) // #pragma alloc_text(PAGEUBS0, BuildReadRequest) -- called from restartnotify #pragma alloc_text(PAGEUBS0, ClassVendorCommand) #pragma alloc_text(PAGEUBS0, StartRead) #pragma alloc_text(PAGEUBS0, StartNotifyRead) #pragma alloc_text(PAGEUBS0, UsbSerRestoreModemSettings) #pragma alloc_text(PAGEUBS0, StartDevice) #pragma alloc_text(PAGEUBS0, DeleteObjectAndLink) #pragma alloc_text(PAGEUBS0, RemoveDevice) // #pragma alloc_text(PAGEUSBS, CancelPendingWaitMasks) -- called from STOP #pragma alloc_text(PAGEUSBS, UsbSerTryToCompleteCurrent) #pragma alloc_text(PAGEUSBS, UsbSerGetNextIrp) #pragma alloc_text(PAGEUSBS, UsbSerStartOrQueue) #pragma alloc_text(PAGEUSBS, UsbSerCancelQueued) #pragma alloc_text(PAGEUSBS, UsbSerKillAllReadsOrWrites) #pragma alloc_text(PAGEUSBS, UsbSerKillPendingIrps) #pragma alloc_text(PAGEUSBS, UsbSerCompletePendingWaitMasks) #pragma alloc_text(PAGEUSBS, UsbSerProcessEmptyTransmit) #pragma alloc_text(PAGEUSBS, UsbSerCancelWaitOnMask) #endif // ALLOC_PRAGMA // we will support 256 devices, keep track of open slots here #define NUM_DEVICE_SLOTS 256 LOCAL BOOLEAN Slots[NUM_DEVICE_SLOTS]; LOCAL ULONG NumDevices; LOCAL PDEVICE_OBJECT GlobDeviceObject; USHORT RxBuffSize = RX_BUFF_SIZE; /************************************************************************/ /* UsbSerGetRegistryValues */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Gets values from the registry */ /* */ /* Arguments: */ /* */ /* Handle Handle to the opened registry key */ /* */ /* PKeyNameString ANSI string to the desired key */ /* */ /* KeyNameStringLength Length of the KeyNameString */ /* */ /* PData Buffer to place the key value in */ /* */ /* DataLength Length of the data buffer */ /* */ /* PDevExt - pointer to the device extension */ /* */ /* Return Value: */ /* */ /* STATUS_SUCCESS if all works, otherwise status of system call that */ /* went wrong. */ /* */ /************************************************************************/ NTSTATUS UsbSerGetRegistryKeyValue(IN HANDLE Handle, IN PWCHAR PKeyNameString, IN ULONG KeyNameStringLength, IN PVOID PData, IN ULONG DataLength) { UNICODE_STRING keyName; ULONG length; PKEY_VALUE_FULL_INFORMATION pFullInfo; NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; PAGED_CODE(); DEBUG_LOG_PATH("enter UsbSerGetRegistryKeyValue"); UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerGetRegistryKeyValue\n")); RtlInitUnicodeString(&keyName, PKeyNameString); length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength + DataLength; pFullInfo = DEBUG_MEMALLOC(PagedPool, length); if (pFullInfo) { status = ZwQueryValueKey(Handle, &keyName, KeyValueFullInformation, pFullInfo, length, &length); if (NT_SUCCESS(status)) { // // If there is enough room in the data buffer, // copy the output // if (DataLength >= pFullInfo->DataLength) { RtlCopyMemory(PData, ((PUCHAR)pFullInfo) + pFullInfo->DataOffset, pFullInfo->DataLength); } } DEBUG_MEMFREE(pFullInfo); } DEBUG_LOG_ERROR(status); DEBUG_LOG_PATH("exit UsbSerGetRegistryKeyValue"); DEBUG_TRACE3(("status (%08X)\n", status)); UsbSerSerialDump(USBSERTRACEOTH, ("UsbSerUndoExternalNaming\n")); if (PDevExt->SymbolicLinkName.Buffer && PDevExt->CreatedSymbolicLink) { IoDeleteSymbolicLink(&PDevExt->SymbolicLinkName); } if (PDevExt->SymbolicLinkName.Buffer != NULL) { DEBUG_MEMFREE(PDevExt->SymbolicLinkName.Buffer); RtlInitUnicodeString(&PDevExt->SymbolicLinkName, NULL); } if (PDevExt->DosName.Buffer != NULL) { DEBUG_MEMFREE(PDevExt->DosName.Buffer); RtlInitUnicodeString(&PDevExt->DosName, NULL); } if (PDevExt->DeviceName.Buffer != NULL) { RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP, PDevExt->DeviceName.Buffer); DEBUG_MEMFREE(PDevExt->DeviceName.Buffer); RtlInitUnicodeString(&PDevExt->DeviceName, NULL); } #ifdef WMI_SUPPORT if (PDevExt->WmiIdentifier.Buffer) { DEBUG_MEMFREE(PDevExt->WmiIdentifier.Buffer); PDevExt->WmiIdentifier.MaximumLength = PDevExt->WmiIdentifier.Length = 0; PDevExt->WmiIdentifier.Buffer = NULL; } #endif DEBUG_LOG_PATH("exit UsbSerUndoExternalNaming"); UsbSerSerialDump(USBSERTRACEOTH, ("UsbSerDoExternalNaming\n")); RtlZeroMemory(&linkName, sizeof(UNICODE_STRING)); linkName.MaximumLength = SYMBOLIC_NAME_LENGTH * sizeof(WCHAR); linkName.Buffer = DEBUG_MEMALLOC(PagedPool, linkName.MaximumLength + sizeof(WCHAR)); if (linkName.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto UsbSerDoExternalNamingError; } RtlZeroMemory(linkName.Buffer, linkName.MaximumLength + sizeof(WCHAR)); pRegName = DEBUG_MEMALLOC(PagedPool, SYMBOLIC_NAME_LENGTH * sizeof(WCHAR) + sizeof(WCHAR)); if (pRegName == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto UsbSerDoExternalNamingError; } status = IoOpenDeviceRegistryKey(PDevExt->PhysDeviceObject, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_READ, &keyHandle); if (status != STATUS_SUCCESS) { goto UsbSerDoExternalNamingError; } status = UsbSerGetRegistryKeyValue(keyHandle, L"PortName", sizeof(L"PortName"), pRegName, SYMBOLIC_NAME_LENGTH * sizeof(WCHAR)); if (status != STATUS_SUCCESS) { status = UsbSerGetRegistryKeyValue(keyHandle, L"Identifier", sizeof(L"Identifier"), pRegName, SYMBOLIC_NAME_LENGTH * sizeof(WCHAR)); if (status != STATUS_SUCCESS) { ZwClose(keyHandle); goto UsbSerDoExternalNamingError; } } ZwClose(keyHandle); #ifdef WMI_SUPPORT { ULONG bufLen; bufLen = wcslen(pRegName) * sizeof(WCHAR) + sizeof(UNICODE_NULL); PDevExt->WmiIdentifier.Buffer = DEBUG_MEMALLOC(PagedPool, bufLen); if (PDevExt->WmiIdentifier.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto UsbSerDoExternalNamingError; } RtlZeroMemory(PDevExt->WmiIdentifier.Buffer, bufLen); PDevExt->WmiIdentifier.Length = 0; PDevExt->WmiIdentifier.MaximumLength = (USHORT)bufLen - 1; RtlAppendUnicodeToString(&PDevExt->WmiIdentifier, pRegName); } #endif // // Create the "\\DosDevices\\" string // RtlAppendUnicodeToString(&linkName, L"\\"); RtlAppendUnicodeToString(&linkName, DEFAULT_DIRECTORY); RtlAppendUnicodeToString(&linkName, L"\\"); RtlAppendUnicodeToString(&linkName, pRegName); // // Allocate pool and save the symbolic link name in the device extension // PDevExt->SymbolicLinkName.MaximumLength = linkName.Length + sizeof(WCHAR); PDevExt->SymbolicLinkName.Buffer = DEBUG_MEMALLOC(PagedPool, PDevExt->SymbolicLinkName.MaximumLength); if (PDevExt->SymbolicLinkName.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto UsbSerDoExternalNamingError; } RtlZeroMemory(PDevExt->SymbolicLinkName.Buffer, PDevExt->SymbolicLinkName.MaximumLength); RtlAppendUnicodeStringToString(&PDevExt->SymbolicLinkName, &linkName); status = IoCreateSymbolicLink(&PDevExt->SymbolicLinkName, &PDevExt->DeviceName); if (status != STATUS_SUCCESS) { goto UsbSerDoExternalNamingError; } PDevExt->CreatedSymbolicLink = TRUE; PDevExt->DosName.Buffer = DEBUG_MEMALLOC(PagedPool, 64 + sizeof(WCHAR)); if (PDevExt->DosName.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto UsbSerDoExternalNamingError; } PDevExt->DosName.MaximumLength = 64 + sizeof(WCHAR); PDevExt->DosName.Length = 0; RtlZeroMemory(PDevExt->DosName.Buffer, PDevExt->DosName.MaximumLength); RtlAppendUnicodeToString(&PDevExt->DosName, pRegName); RtlZeroMemory(((PUCHAR)(&PDevExt->DosName.Buffer[0])) + PDevExt->DosName.Length, sizeof(WCHAR)); status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM", PDevExt->DeviceName.Buffer, REG_SZ, PDevExt->DosName.Buffer, PDevExt->DosName.Length + sizeof(WCHAR)); if (status != STATUS_SUCCESS) { goto UsbSerDoExternalNamingError; } UsbSerDoExternalNamingError:; // // Clean up error conditions // if (status != STATUS_SUCCESS) { if (PDevExt->DosName.Buffer != NULL) { DEBUG_MEMFREE(PDevExt->DosName.Buffer); PDevExt->DosName.Buffer = NULL; } if (PDevExt->CreatedSymbolicLink == TRUE) { IoDeleteSymbolicLink(&PDevExt->SymbolicLinkName); PDevExt->CreatedSymbolicLink = FALSE; } if (PDevExt->SymbolicLinkName.Buffer != NULL) { DEBUG_MEMFREE(PDevExt->SymbolicLinkName.Buffer); PDevExt->SymbolicLinkName.Buffer = NULL; } if (PDevExt->DeviceName.Buffer != NULL) { RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP, PDevExt->DeviceName.Buffer); } } // // Always clean up our temp buffers. // if (linkName.Buffer != NULL) { DEBUG_MEMFREE(linkName.Buffer); } if (pRegName != NULL) { DEBUG_MEMFREE(pRegName); } DEBUG_LOG_ERROR(status); DEBUG_LOG_PATH("exit UsbSerDoExternalNaming"); DEBUG_TRACE3(("status (%08X)\n", status)); UsbSerSerialDump(USBSERTRACEOTH, ("UsbSerAbortPipes (%08X)\n", PDevObj)); pDevExt = PDevObj->DeviceExtension; pUrb = DEBUG_MEMALLOC(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST)); if (pUrb != NULL) { pUrb->UrbHeader.Length = (USHORT)sizeof(struct _URB_PIPE_REQUEST); pUrb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE; pUrb->UrbPipeRequest.PipeHandle = pDevExt->DataInPipe; ntStatus = CallUSBD(PDevObj, pUrb); if (ntStatus != STATUS_SUCCESS) { goto UsbSerAbortPipesErr; } // // Wait for all the read IRPS to drain // UsbSerSerialDump(USBSERTRACERD, ("DataInCountw %08X @ %08X\n", pDevExt->PendingDataInCount, &pDevExt->PendingDataInCount)); // // Decrement for initial value // pendingIrps = InterlockedDecrement(&pDevExt->PendingDataInCount); if (pendingIrps) { DEBUG_TRACE1(("Abort DataIn Pipe\n")); UsbSerSerialDump(USBSERTRACEOTH, ("Waiting for DataIn Pipe\n")); KeWaitForSingleObject(&pDevExt->PendingDataInEvent, Executive, KernelMode, FALSE, NULL); } // // Reset counter // InterlockedIncrement(&pDevExt->PendingDataInCount); UsbSerSerialDump(USBSERTRACERD, ("DataInCountx %08X @ %08X\n", pDevExt->PendingDataInCount, &pDevExt->PendingDataInCount)); pUrb->UrbHeader.Length = (USHORT)sizeof(struct _URB_PIPE_REQUEST); pUrb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE; pUrb->UrbPipeRequest.PipeHandle = pDevExt->DataOutPipe; ntStatus = CallUSBD(PDevObj, pUrb); if (ntStatus != STATUS_SUCCESS) { goto UsbSerAbortPipesErr; } // // Wait for all the write irps to drain // // // Decrement for initial value // pendingIrps = InterlockedDecrement(&pDevExt->PendingDataOutCount); if (pendingIrps) { UsbSerSerialDump(USBSERTRACEOTH, ("Waiting for DataOut Pipe\n")); KeWaitForSingleObject(&pDevExt->PendingDataOutEvent, Executive, KernelMode, FALSE, NULL); } // // Reset counter // InterlockedIncrement(&pDevExt->PendingDataOutCount); pUrb->UrbHeader.Length = (USHORT)sizeof(struct _URB_PIPE_REQUEST); pUrb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE; pUrb->UrbPipeRequest.PipeHandle = pDevExt->NotificationPipe; ntStatus = CallUSBD(PDevObj, pUrb); // // Wait for all the notify irps to drain // // // Decrement for initial value // pendingIrps = InterlockedDecrement(&pDevExt->PendingNotifyCount); if (pendingIrps) { UsbSerSerialDump(USBSERTRACEOTH, ("Waiting for Notify Pipe\n")); KeWaitForSingleObject(&pDevExt->PendingNotifyEvent, Executive, KernelMode, FALSE, NULL); } // // // Die my darling, die. // // IoCancelIrp(pDevExt->NotifyIrp); // Reset counter // InterlockedIncrement(&pDevExt->PendingNotifyCount); UsbSerAbortPipesErr:; DEBUG_MEMFREE(pUrb); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD, ("DeviceExtension; NTSTATUS NtStatus = STATUS_SUCCESS; KEVENT Event; PVOID pPagingHandle; PAGED_CODE(); DEBUG_LOG_PATH("enter StartDevice"); DEBUG_TRACE1(("StartDevice\n")); // pass this down to the USB stack first KeInitializeEvent(&Event, NotificationEvent, FALSE); // // Initialize our DPC's // KeInitializeDpc(&DeviceExtension->TotalReadTimeoutDpc, UsbSerReadTimeout, DeviceExtension); KeInitializeDpc(&DeviceExtension->IntervalReadTimeoutDpc, UsbSerIntervalReadTimeout, DeviceExtension); KeInitializeDpc(&DeviceExtension->TotalWriteTimeoutDpc, UsbSerWriteTimeout, DeviceExtension); // // Initialize timers // KeInitializeTimer(&DeviceExtension->WriteRequestTotalTimer); KeInitializeTimer(&DeviceExtension->ReadRequestTotalTimer); KeInitializeTimer(&DeviceExtension->ReadRequestIntervalTimer); // // Store values into the extension for interval timing. // // // If the interval timer is less than a second then come // in with a short "polling" loop. // // For large (> then 2 seconds) use a 1 second poller. // DeviceExtension->ShortIntervalAmount.QuadPart = -1; DeviceExtension->LongIntervalAmount.QuadPart = -10000000; DeviceExtension->CutOverAmount.QuadPart = 200000000; KeInitializeEvent(&Event, SynchronizationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, UsbSerSyncCompletion, &Event, TRUE, TRUE, TRUE); NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, Irp); // wait for Irp to complete if status is pending if(NtStatus == STATUS_PENDING) { KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); } NtStatus = Irp->IoStatus.Status; if (!NT_SUCCESS(NtStatus)) { goto ExitStartDevice; } NtStatus = GetDeviceDescriptor(DeviceObject); if (!NT_SUCCESS(NtStatus)) { goto ExitStartDevice; } NtStatus = ConfigureDevice(DeviceObject); if (!NT_SUCCESS(NtStatus)) { goto ExitStartDevice; } // // Page in and lock necessary code // pPagingHandle = UsbSerLockPagableCodeSection(PAGEUSBSER_Function); // reset device ResetDevice(NULL, DeviceObject); // init stuff in device extension DeviceExtension->HandFlow.ControlHandShake = 0; DeviceExtension->HandFlow.FlowReplace = SERIAL_RTS_CONTROL; DeviceExtension->AcceptingRequests = TRUE; InitializeListHead(&DeviceExtension->ReadQueue); InitializeListHead(&DeviceExtension->ImmediateReadQueue); UsbSerDoExternalNaming(DeviceExtension); // clear DTR and RTS SetClrDtr(DeviceObject, FALSE); ClrRts(NULL, DeviceExtension); // kick off a read StartRead(DeviceExtension); // kick off a notification read StartNotifyRead(DeviceExtension); UsbSerUnlockPagableImageSection(pPagingHandle); ExitStartDevice:; if(NT_SUCCESS(NtStatus)) { DeviceExtension->DeviceState = DEVICE_STATE_STARTED; // try and idle the modem // UsbSerFdoSubmitIdleRequestIrp(DeviceExtension); } Irp->IoStatus.Status = NtStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT); DEBUG_LOG_PATH("exit StartDevice"); return NtStatus; } // StartDevice /************************************************************************/ /* StopDevice */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Take care of processing needed to stop device. */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* Irp - pointer to an I/O Request Packet */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS StopDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; NTSTATUS NtStatus = STATUS_SUCCESS; ULONG Size; PURB Urb; PAGED_CODE(); DEBUG_LOG_PATH("enter StopDevice"); DEBUG_TRACE1(("StopDevice\n")); UsbSerFetchBooleanLocked(&DeviceExtension->AcceptingRequests, FALSE, &DeviceExtension->ControlLock); CancelPendingWaitMasks(DeviceExtension); if(DeviceExtension->DeviceState == DEVICE_STATE_STARTED) { DEBUG_TRACE1(("AbortPipes\n")); UsbSerAbortPipes(DeviceObject); } DeviceExtension->DeviceState = DEVICE_STATE_STOPPED; if(DeviceExtension->PendingIdleIrp) { IoCancelIrp(DeviceExtension->PendingIdleIrp); } Size = sizeof(struct _URB_SELECT_CONFIGURATION); Urb = DEBUG_MEMALLOC(NonPagedPool, Size); if(Urb) { UsbBuildSelectConfigurationRequest(Urb, (USHORT) Size, NULL); NtStatus = CallUSBD(DeviceObject, Urb); DEBUG_TRACE3(("Device Configuration Closed status = (%08X) " "USB status = (%08X)\n", NtStatus, Urb->UrbHeader.Status)); DEBUG_MEMFREE(Urb); } else { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } DEBUG_LOG_PATH("exit StopDevice"); return NtStatus; } // StopDevice /************************************************************************/ /* RemoveDevice */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Take care of processing needed to remove device. */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* Irp - pointer to an I/O Request Packet */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS RemoveDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; NTSTATUS NtStatus = STATUS_SUCCESS; PVOID pPagingHandle; PAGED_CODE(); DEBUG_LOG_PATH("enter RemoveDevice"); DEBUG_TRACE1(("RemoveDevice\n")); // // Page in and lock necessary code // pPagingHandle = UsbSerLockPagableCodeSection(PAGEUSBSER_Function); UsbSerFetchBooleanLocked(&DeviceExtension->AcceptingRequests, FALSE, &DeviceExtension->ControlLock); CancelPendingWaitMasks(DeviceExtension); // // Cancel all pending USB transactions // if(DeviceExtension->DeviceState == DEVICE_STATE_STARTED) { DEBUG_TRACE1(("AbortPipes\n")); UsbSerAbortPipes(DeviceObject); } // // Once we set accepting requests to false, we shouldn't // have any more contention here -- if we do, we're dead // because we're freeing memory out from under our feet. // DEBUG_TRACE1(("Freeing Allocated Memory\n")); // free allocated notify URB if(DeviceExtension->NotifyUrb) { DEBUG_MEMFREE(DeviceExtension->NotifyUrb); DeviceExtension->NotifyUrb = NULL; } // free allocated Read URB if(DeviceExtension->ReadUrb) { DEBUG_MEMFREE(DeviceExtension->ReadUrb); DeviceExtension->ReadUrb = NULL; } // free allocated device descriptor if(DeviceExtension->DeviceDescriptor) { DEBUG_MEMFREE(DeviceExtension->DeviceDescriptor); DeviceExtension->DeviceDescriptor = NULL; } // free up read buffer if(DeviceExtension->ReadBuff) { DEBUG_MEMFREE(DeviceExtension->ReadBuff); DeviceExtension->ReadBuff = NULL; } if(DeviceExtension->USBReadBuff) { DEBUG_MEMFREE(DeviceExtension->USBReadBuff); DeviceExtension->USBReadBuff = NULL; } // free up notification buffer if(DeviceExtension->NotificationBuff) { DEBUG_MEMFREE(DeviceExtension->NotificationBuff); DeviceExtension->NotificationBuff = NULL; } DEBUG_TRACE1(("Undo Serial Name\n")); UsbSerUndoExternalNaming(DeviceExtension); // // Pass this down to the next driver IoCopyCurrentIrpStackLocationToNext(Irp); NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, Irp); DEBUG_TRACE1(("Detach Device\n")); // detach device from stack IoDetachDevice(DeviceExtension->StackDeviceObject); DEBUG_TRACE1(("DevExt (%08X) DevExt Size (%08X)\n", DeviceExtension, sizeof(DEVICE_EXTENSION))); DEBUG_TRACE1(("Delete Object and Link\n")); // delete device object and symbolic link DeleteObjectAndLink(DeviceObject); DEBUG_TRACE1(("Done Removing Device\n")); DEBUG_LOG_PATH("exit RemoveDevice"); UsbSerUnlockPagableImageSection(pPagingHandle); DeviceExtension->DeviceState = DEVICE_STATE_STOPPED; return NtStatus; } // RemoveDevice /************************************************************************/ /* CreateDeviceObject */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Take care of processing needed to create device obeject for */ /* device. */ /* */ /* Arguments: */ /* */ /* DriverObject - pointer to a driver object */ /* DeviceObject - pointer to a device object pointer */ /* DeviceName - pointer to a base name of device */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS CreateDeviceObject(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT *DeviceObject, IN PCHAR DeviceName) { ANSI_STRING DevName; ANSI_STRING LinkName; NTSTATUS NtStatus; UNICODE_STRING DeviceNameUnicodeString; UNICODE_STRING LinkNameUnicodeString; PDEVICE_EXTENSION DeviceExtension; CHAR DeviceLinkBuffer[NAME_MAX]; CHAR DeviceNameBuffer[NAME_MAX]; ULONG DeviceInstance; ULONG bufferLen; KIRQL OldIrql; DEBUG_LOG_PATH("enter CreateDeviceObject"); DEBUG_TRACE1(("CreateDeviceObject\n")); KeAcquireSpinLock(&GlobalSpinLock, &OldIrql); // let's get an instance for (DeviceInstance = 0; DeviceInstance < NUM_DEVICE_SLOTS; DeviceInstance++) { if (Slots[DeviceInstance] == FALSE) break; } KeReleaseSpinLock(&GlobalSpinLock, OldIrql); // check if we didn't have any empty slots if (DeviceInstance == NUM_DEVICE_SLOTS) NtStatus = STATUS_INVALID_DEVICE_REQUEST; else { // complete names of links and devices sprintf(DeviceLinkBuffer, "%s%s%03d", "\\DosDevices\\", DeviceName, DeviceInstance); sprintf(DeviceNameBuffer, "%s%s%03d", "\\Device\\", DeviceName, DeviceInstance); // init ANSI string with our link and device names RtlInitAnsiString(&DevName, DeviceNameBuffer); RtlInitAnsiString(&LinkName, DeviceLinkBuffer); DeviceNameUnicodeString.Length = 0; DeviceNameUnicodeString.Buffer = NULL; LinkNameUnicodeString.Length = 0; LinkNameUnicodeString.Buffer = NULL; *DeviceObject = NULL; // convert to UNICODE string NtStatus = RtlAnsiStringToUnicodeString(&DeviceNameUnicodeString, &DevName, TRUE); if(NT_SUCCESS(NtStatus)) { NtStatus = RtlAnsiStringToUnicodeString(&LinkNameUnicodeString, &LinkName, TRUE); if(NT_SUCCESS(NtStatus)) { DEBUG_TRACE3(("Create Device (%s)\n", DeviceNameBuffer)); // create the device object NtStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceNameUnicodeString, FILE_DEVICE_MODEM, 0, TRUE, DeviceObject); } else { goto CreateDeviceObjectError; } } else { goto CreateDeviceObjectError; } // created the device object O.K., create symbolic links, // attach device object, and fill in the device extension if (NT_SUCCESS(NtStatus)) { // create symbolic links DEBUG_TRACE3(("Create SymLink (%s)\n", DeviceLinkBuffer)); NtStatus = IoCreateUnprotectedSymbolicLink(&LinkNameUnicodeString, &DeviceNameUnicodeString); if (NtStatus != STATUS_SUCCESS) { goto CreateDeviceObjectError; } // get pointer to device extension DeviceExtension = (PDEVICE_EXTENSION) (*DeviceObject)->DeviceExtension; // let's zero out device extension RtlZeroMemory(DeviceExtension, sizeof(DEVICE_EXTENSION)); // save our strings // save link name strcpy(DeviceExtension->LinkName, DeviceLinkBuffer); bufferLen = RtlAnsiStringToUnicodeSize(&DevName); DeviceExtension->DeviceName.Length = 0; DeviceExtension->DeviceName.MaximumLength = (USHORT)bufferLen; DeviceExtension->DeviceName.Buffer = DEBUG_MEMALLOC(PagedPool, bufferLen); if (DeviceExtension->DeviceName.Buffer == NULL) { // // Skip out. We have worse problems than missing // the name. // NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto CreateDeviceObjectError; } else { RtlAnsiStringToUnicodeString(&DeviceExtension->DeviceName, &DevName, FALSE); // save physical device object DeviceExtension->PhysDeviceObject = *DeviceObject; DeviceExtension->Instance = DeviceInstance; // initialize spinlocks KeInitializeSpinLock(&DeviceExtension->ControlLock); // mark this device slot as in use and increment number // of devices KeAcquireSpinLock(&GlobalSpinLock, &OldIrql); Slots[DeviceInstance] = TRUE; NumDevices++; KeReleaseSpinLock(&GlobalSpinLock, OldIrql); DeviceExtension->IsDevice = TRUE; KeInitializeEvent(&DeviceExtension->PendingDataInEvent, SynchronizationEvent, FALSE); KeInitializeEvent(&DeviceExtension->PendingDataOutEvent, SynchronizationEvent, FALSE); KeInitializeEvent(&DeviceExtension->PendingNotifyEvent, SynchronizationEvent, FALSE); KeInitializeEvent(&DeviceExtension->PendingFlushEvent, SynchronizationEvent, FALSE); DeviceExtension->PendingDataInCount = 1; DeviceExtension->PendingDataOutCount = 1; DeviceExtension->PendingNotifyCount = 1; DeviceExtension->SanityCheck = SANITY_CHECK; } } CreateDeviceObjectError:; // free Unicode strings RtlFreeUnicodeString(&DeviceNameUnicodeString); RtlFreeUnicodeString(&LinkNameUnicodeString); // // Delete the devobj if there was an error // if (NtStatus != STATUS_SUCCESS) { if (*DeviceObject) { IoDeleteDevice(*DeviceObject); *DeviceObject = NULL; } } } // log an error if we got one DEBUG_LOG_ERROR(NtStatus); DEBUG_LOG_PATH("exit CreateDeviceObject"); DEBUG_TRACE3(("status (%08X)\n", NtStatus)); return NtStatus; } // CreateDeviceObject /************************************************************************/ /* CompleteIO */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Complete IO request and log IRP */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to device object. */ /* Irp - pointer to IRP. */ /* MajorFunction - major function of IRP. */ /* IoBuffer - buffer for data passed in and out of driver. */ /* BufferLen - length of buffer */ /* */ /* Return Value: */ /* */ /* VOID */ /* */ /************************************************************************/ VOID CompleteIO(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG MajorFunction, IN PVOID IoBuffer, IN ULONG_PTR BufferLen) { PDEVICE_EXTENSION DeviceExtension; DEBUG_LOG_PATH("enter CompleteIO"); // get pointer to device extension DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; // log IRP count and bytes processed in device extension DeviceExtension->IRPCount++; DeviceExtension->ByteCount = RtlLargeIntegerAdd(DeviceExtension->ByteCount, RtlConvertUlongToLargeInteger((ULONG)Irp->IoStatus .Information)); // make entry in IRP history table DEBUG_LOG_IRP_HIST(DeviceObject, Irp, MajorFunction, IoBuffer, (ULONG)BufferLen); // if we got here, must want to complete request on IRP IoCompleteRequest(Irp, IO_NO_INCREMENT); DEBUG_LOG_PATH("exit CompleteIO"); } // CompleteIO /************************************************************************/ /* DeleteObjectAndLink */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Deletes a device object and associated symbolic link */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to device object. */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS DeleteObjectAndLink(IN PDEVICE_OBJECT DeviceObject) { PDEVICE_EXTENSION DeviceExtension; UNICODE_STRING DeviceLinkUnicodeString; ANSI_STRING DeviceLinkAnsiString; NTSTATUS NtStatus; PAGED_CODE(); DEBUG_LOG_PATH("enter DeleteObjectAndLink"); DEBUG_TRACE1(("DeleteObjectAndLink\n")); // get pointer to device extension, we will get the symbolic link name // here DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; // get rid of the symbolic link RtlInitAnsiString(&DeviceLinkAnsiString, DeviceExtension->LinkName); NtStatus = RtlAnsiStringToUnicodeString(&DeviceLinkUnicodeString, &DeviceLinkAnsiString, TRUE); DEBUG_TRACE1(("Delete Symbolic Link\n")); IoDeleteSymbolicLink(&DeviceLinkUnicodeString); // clear out slot and decrement number of devices if(DeviceExtension->Instance < NUM_DEVICE_SLOTS) { UsbSerFetchBooleanLocked(&Slots[DeviceExtension->Instance], FALSE, &GlobalSpinLock); NumDevices--; if(!NumDevices) DEBUG_CHECKMEM(); } DEBUG_TRACE1(("Delete Device Object\n")); if(DeviceExtension->SanityCheck != SANITY_CHECK) { DEBUG_TRACE1(("Device Extension Scrozzled\n")); } // wait to do this till here as this triggers unload routine IoDeleteDevice(DeviceObject); DEBUG_TRACE1(("Done Deleting Device Object and Link\n")); DEBUG_LOG_PATH("exit DeleteObjectAndLink"); return NtStatus; } // DeleteObjectAndLink /************************************************************************/ /* StartPerfTimer */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Start perf timer for measuring bytes/second throughput */ /* */ /* Arguments: */ /* */ /* DeviceExtension - pointer to device extension for device */ /* */ /* Return Value: */ /* */ /* VOID */ /* */ /************************************************************************/ VOID StartPerfTimer(IN OUT PDEVICE_EXTENSION DeviceExtension) { PAGED_CODE(); // set up perf stuff if perf timing enabled if(DeviceExtension && DeviceExtension->PerfTimerEnabled) { // get current perf counter DeviceExtension->TimerStart = KeQueryPerformanceCounter(NULL); } } // StartPerfTimer /************************************************************************/ /* StopPerfTimer */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Stop perf timer for measuring bytes/second throughput */ /* */ /* Arguments: */ /* */ /* DeviceExtension - pointer to device extension for device */ /* BytesXfered - number of bytes tranferred this iteration */ /* */ /* Return Value: */ /* */ /* VOID */ /* */ /************************************************************************/ VOID StopPerfTimer(IN OUT PDEVICE_EXTENSION DeviceExtension, IN ULONG BytesXfered) { LARGE_INTEGER BytesThisTransfer; LARGE_INTEGER CurrentTime; LARGE_INTEGER TimeThisTransfer; PAGED_CODE(); if(DeviceExtension && DeviceExtension->PerfTimerEnabled) { // get updated time CurrentTime = KeQueryPerformanceCounter(NULL); // stop perf timing with system timer BytesThisTransfer = RtlConvertUlongToLargeInteger(BytesXfered); DeviceExtension->BytesXfered = RtlLargeIntegerAdd(DeviceExtension->BytesXfered, BytesThisTransfer); // now add the time it took to elapsed time TimeThisTransfer = RtlLargeIntegerSubtract(CurrentTime, DeviceExtension->TimerStart); DeviceExtension->ElapsedTime = RtlLargeIntegerAdd(DeviceExtension->ElapsedTime, TimeThisTransfer); } } // StopPerfTimer /************************************************************************/ /* BytesPerSecond */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Start perf timer for measuring bytes/second throughput */ /* */ /* Arguments: */ /* */ /* DeviceExtension - pointer to device extension for device */ /* */ /* Return Value: */ /* */ /* ULONG - bytes/second for device */ /* */ /************************************************************************/ ULONG BytesPerSecond(IN OUT PDEVICE_EXTENSION DeviceExtension) { ULONG Remainder; LARGE_INTEGER Result; LARGE_INTEGER TicksPerSecond; PAGED_CODE(); // get ticks per second from perf counter KeQueryPerformanceCounter(&TicksPerSecond); // scale the bytes xfered Result = RtlExtendedIntegerMultiply(DeviceExtension->BytesXfered, TicksPerSecond.LowPart); // Don't divide by 0 DeviceExtension->ElapsedTime.LowPart = (DeviceExtension->ElapsedTime.LowPart == 0L) ? 1 : DeviceExtension->ElapsedTime.LowPart; // lets get stats here Result = RtlExtendedLargeIntegerDivide(Result, DeviceExtension->ElapsedTime.LowPart, &Remainder); return Result.LowPart; } // BytesPerSecond /************************************************************************/ /* CallUSBD */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Call USB bus driver. */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* Urb - pointer to URB */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS CallUSBD(IN PDEVICE_OBJECT DeviceObject, IN PURB Urb) { NTSTATUS NtStatus = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; PIRP Irp; KEVENT Event; PIO_STACK_LOCATION NextStack; PAGED_CODE(); DEBUG_LOG_PATH("enter CallUSBD"); // issue a synchronous request KeInitializeEvent(&Event, SynchronizationEvent, FALSE); Irp = IoAllocateIrp(DeviceExtension->StackDeviceObject->StackSize, FALSE); if (Irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // Set the Irp parameters NextStack = IoGetNextIrpStackLocation(Irp); NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; NextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; NextStack->Parameters.Others.Argument1 = Urb; // Set the completion routine, which will signal the event IoSetCompletionRoutine(Irp, CallUSBD_SyncCompletionRoutine, &Event, TRUE, // InvokeOnSuccess TRUE, // InvokeOnError TRUE); // InvokeOnCancel DEBUG_LOG_PATH("Calling USB driver stack"); NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, Irp); DEBUG_LOG_PATH("Returned from calling USB driver stack"); // block on pending request if(NtStatus == STATUS_PENDING) { LARGE_INTEGER timeout; // Specify a timeout of 30 seconds to wait for this call to complete. // timeout.QuadPart = -10000 * 30000; NtStatus = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &timeout); if(NtStatus == STATUS_TIMEOUT) { NtStatus = STATUS_IO_TIMEOUT; // Cancel the Irp we just sent. // IoCancelIrp(Irp); // And wait until the cancel completes // KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); } else { NtStatus = Irp->IoStatus.Status; } } IoFreeIrp(Irp); DEBUG_LOG_PATH("exit CallUSBD"); return NtStatus; } // CallUSBD /************************************************************************/ /* CallUSBD_SyncCompletionRoutine */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Completion routine for USB sync request. */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS CallUSBD_SyncCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PKEVENT kevent; kevent = (PKEVENT) Context; KeSetEvent(kevent, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } // CallUSBD_SyncCompletionRoutine /************************************************************************/ /* GetDeviceDescriptor */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Get device descriptor for USB device. */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS GetDeviceDescriptor(IN PDEVICE_OBJECT DeviceObject) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; NTSTATUS NtStatus; PUSB_DEVICE_DESCRIPTOR DeviceDescriptor; PURB Urb; ULONG Size; ULONG UrbCDRSize; KIRQL OldIrql; DEBUG_LOG_PATH("enter GetDeviceDescriptor"); UrbCDRSize = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST); Urb = DEBUG_MEMALLOC(NonPagedPool, UrbCDRSize); if (Urb) { Size = sizeof(USB_DEVICE_DESCRIPTOR); DeviceDescriptor = DEBUG_MEMALLOC(NonPagedPool, Size); if (DeviceDescriptor) { UsbBuildGetDescriptorRequest(Urb, (USHORT)UrbCDRSize, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, DeviceDescriptor, NULL, Size, NULL); NtStatus = CallUSBD(DeviceObject, Urb); if (NT_SUCCESS(NtStatus)) { DEBUG_TRACE3(("Device Descriptor (%08X)\n", DeviceDescriptor)); DEBUG_TRACE3(("Length (%08X)\n", Urb->UrbControlDescriptorRequest .TransferBufferLength)); DEBUG_TRACE3(("Device Descriptor:\n")); DEBUG_TRACE3(("-------------------------\n")); DEBUG_TRACE3(("bLength (%08X)\n", DeviceDescriptor->bLength)); DEBUG_TRACE3(("bDescriptorType (%08X)\n", DeviceDescriptor->bDescriptorType)); DEBUG_TRACE3(("bcdUSB (%08X)\n", DeviceDescriptor->bcdUSB)); DEBUG_TRACE3(("bDeviceClass (%08X)\n", DeviceDescriptor->bDeviceClass)); DEBUG_TRACE3(("bDeviceSubClass (%08X)\n", DeviceDescriptor->bDeviceSubClass)); DEBUG_TRACE3(("bDeviceProtocol (%08X)\n", DeviceDescriptor->bDeviceProtocol)); DEBUG_TRACE3(("bMaxPacketSize0 (%08X)\n", DeviceDescriptor->bMaxPacketSize0)); DEBUG_TRACE3(("idVendor (%08X)\n", DeviceDescriptor->idVendor)); DEBUG_TRACE3(("idProduct (%08X)\n", DeviceDescriptor->idProduct)); DEBUG_TRACE3(("bcdDevice (%08X)\n", DeviceDescriptor->bcdDevice)); DEBUG_TRACE3(("iManufacturer (%08X)\n", DeviceDescriptor->iManufacturer)); DEBUG_TRACE3(("iProduct (%08X)\n", DeviceDescriptor->iProduct)); DEBUG_TRACE3(("iSerialNumber (%08X)\n", DeviceDescriptor->iSerialNumber)); DEBUG_TRACE3(("bNumConfigurations (%08X)\n", DeviceDescriptor->bNumConfigurations)); } } else { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } // save the device descriptor if (NT_SUCCESS(NtStatus)) { PVOID pOldDesc = NULL; ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); if (DeviceExtension->DeviceDescriptor) { pOldDesc = DeviceExtension->DeviceDescriptor; } DeviceExtension->DeviceDescriptor = DeviceDescriptor; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); if (pOldDesc != NULL) { DEBUG_MEMFREE(pOldDesc); } } else if (DeviceDescriptor) { DEBUG_MEMFREE(DeviceDescriptor); } DEBUG_MEMFREE(Urb); } else { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } DEBUG_LOG_PATH("exit GetDeviceDescriptor"); return NtStatus; } // GetDeviceDescriptor /************************************************************************/ /* ConfigureDevice */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Initializes USB device and selects configuration. */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS ConfigureDevice(IN PDEVICE_OBJECT DeviceObject) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; NTSTATUS NtStatus; PURB Urb; ULONG Size; ULONG UrbCDRSize; PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; ULONG NumConfigs; UCHAR Config; PAGED_CODE(); DEBUG_LOG_PATH("enter ConfigureDevice"); UrbCDRSize = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST); // first configure the device Urb = DEBUG_MEMALLOC(NonPagedPool, UrbCDRSize); if (Urb) { // there may be problems with the 82930 chip, so make this buffer bigger // to prevent choking Size = sizeof(USB_CONFIGURATION_DESCRIPTOR) + 256; // get the number of configurations NumConfigs = DeviceExtension->DeviceDescriptor->bNumConfigurations; // run through all of the configurations looking for a CDC device for (Config = 0; Config < NumConfigs; Config++) { // we will probably only do this once, maybe twice while (TRUE) { ConfigurationDescriptor = DEBUG_MEMALLOC(NonPagedPool, Size); if (ConfigurationDescriptor) { UsbBuildGetDescriptorRequest(Urb, (USHORT)UrbCDRSize, USB_CONFIGURATION_DESCRIPTOR_TYPE, Config, 0, ConfigurationDescriptor, NULL, Size, NULL); NtStatus = CallUSBD(DeviceObject, Urb); DEBUG_TRACE3(("Configuration Descriptor (%08X) " "Length (%08X)\n", ConfigurationDescriptor, Urb->UrbControlDescriptorRequest .TransferBufferLength)); } else { NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // see if we got enough data, we may get an error in URB because of // buffer overrun if (Urb->UrbControlDescriptorRequest.TransferBufferLength>0 && ConfigurationDescriptor->wTotalLength > Size) { // size of data exceeds current buffer size, so allocate correct // size Size = ConfigurationDescriptor->wTotalLength; DEBUG_MEMFREE(ConfigurationDescriptor); ConfigurationDescriptor = NULL; } else { break; } } if (NT_SUCCESS(NtStatus)) { NtStatus = SelectInterface(DeviceObject, ConfigurationDescriptor); DEBUG_MEMFREE(ConfigurationDescriptor); ConfigurationDescriptor = NULL; } else { DEBUG_MEMFREE(ConfigurationDescriptor); ConfigurationDescriptor = NULL; } // found a config we like if (NT_SUCCESS(NtStatus)) break; } DEBUG_MEMFREE(Urb); } else { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } DEBUG_LOG_PATH("exit ConfigureDevice"); return NtStatus; } // ConfigureDevice /************************************************************************/ /* SelectInterface */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Select interface for USB device. */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* ConfigurationDescriptor - pointer to config descriptor */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS SelectInterface(IN PDEVICE_OBJECT DeviceObject, IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) { PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; NTSTATUS NtStatus; PURB Urb; USHORT Size; ULONG Index; PUSBD_INTERFACE_INFORMATION Interfaces[2]; PUSBD_INTERFACE_INFORMATION Interface; PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor[2]; UCHAR AlternateSetting, InterfaceNumber; ULONG Pipe; KIRQL OldIrql; PUCHAR Temp; BOOLEAN FoundCommDevice = FALSE; DEBUG_LOG_PATH("enter SelectInterface"); Urb = USBD_CreateConfigurationRequest(ConfigurationDescriptor, &Size); if (Urb) { Temp = (PUCHAR) &Urb->UrbSelectConfiguration.Interface; for (InterfaceNumber = 0; InterfaceNumber < ConfigurationDescriptor->bNumInterfaces; InterfaceNumber++) { AlternateSetting = 0; InterfaceDescriptor[InterfaceNumber] = USBD_ParseConfigurationDescriptor(ConfigurationDescriptor, InterfaceNumber, AlternateSetting); Interfaces[InterfaceNumber] = (PUSBD_INTERFACE_INFORMATION) Temp; Interfaces[InterfaceNumber]->Length = GET_USBD_INTERFACE_SIZE(InterfaceDescriptor[InterfaceNumber] ->bNumEndpoints); Interfaces[InterfaceNumber]->InterfaceNumber = InterfaceDescriptor[InterfaceNumber]->bInterfaceNumber; Interfaces[InterfaceNumber]->AlternateSetting = InterfaceDescriptor[InterfaceNumber]->bAlternateSetting; for (Index = 0; Index < Interfaces[InterfaceNumber]->NumberOfPipes; Index++) { PUSBD_PIPE_INFORMATION PipeInformation; PipeInformation = &Interfaces[InterfaceNumber]->Pipes[Index]; if (USB_ENDPOINT_DIRECTION_IN(PipeInformation->EndpointAddress)) { // check for data in pipe if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK) { // set bulk pipe in max transfer size PipeInformation->MaximumTransferSize = USB_RX_BUFF_SIZE; } } else if (USB_ENDPOINT_DIRECTION_OUT(PipeInformation->EndpointAddress)) { // check for data out pipe if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK) { // set bulk pipe out max transfer size PipeInformation->MaximumTransferSize = MAXIMUM_TRANSFER_SIZE; } } } Temp += Interfaces[InterfaceNumber]->Length; } UsbBuildSelectConfigurationRequest(Urb, Size, ConfigurationDescriptor); NtStatus = CallUSBD(DeviceObject, Urb); if (NtStatus != STATUS_SUCCESS) { ExFreePool(Urb); goto ExitSelectInterface; } DEBUG_TRACE3(("Select Config Status (%08X)\n", NtStatus)); DeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle; for (InterfaceNumber = 0; InterfaceNumber < ConfigurationDescriptor->bNumInterfaces; InterfaceNumber++) { Interface = Interfaces[InterfaceNumber]; DEBUG_TRACE3(("---------\n")); DEBUG_TRACE3(("NumberOfPipes (%08X)\n", Interface->NumberOfPipes)); DEBUG_TRACE3(("Length (%08X)\n", Interface->Length)); DEBUG_TRACE3(("Alt Setting (%08X)\n", Interface->AlternateSetting)); DEBUG_TRACE3(("Interface Number (%08X)\n", Interface->InterfaceNumber)); DEBUG_TRACE3(("Class (%08X) SubClass (%08X) Protocol (%08X)\n", Interface->Class, Interface->SubClass, Interface->Protocol)); if (Interface->Class == USB_COMM_COMMUNICATION_CLASS_CODE) { FoundCommDevice = TRUE; DeviceExtension->CommInterface = Interface->InterfaceNumber; } for (Pipe = 0; Pipe < Interface->NumberOfPipes; Pipe++) { PUSBD_PIPE_INFORMATION PipeInformation; PipeInformation = &Interface->Pipes[Pipe]; DEBUG_TRACE3(("---------\n")); DEBUG_TRACE3(("PipeType (%08X)\n", PipeInformation->PipeType)); DEBUG_TRACE3(("EndpointAddress (%08X)\n", PipeInformation->EndpointAddress)); DEBUG_TRACE3(("MaxPacketSize (%08X)\n", PipeInformation->MaximumPacketSize)); DEBUG_TRACE3(("Interval (%08X)\n", PipeInformation->Interval)); DEBUG_TRACE3(("Handle (%08X)\n", PipeInformation->PipeHandle)); DEBUG_TRACE3(("MaximumTransferSize (%08X)\n", PipeInformation->MaximumTransferSize)); // now lets save pipe handles in device extension if (USB_ENDPOINT_DIRECTION_IN(PipeInformation->EndpointAddress)) { // check for data in pipe if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK) { PVOID pOldNotBuff = NULL; PVOID pOldReadBuff = NULL; PVOID pOldUSBReadBuff = NULL; PVOID pNewNotBuff = NULL; PVOID pNewReadBuff = NULL; PVOID pNewUSBReadBuff = NULL; DeviceExtension->RxMaxPacketSize = RxBuffSize; if (DeviceExtension->RxMaxPacketSize != 0) { pNewReadBuff = DEBUG_MEMALLOC(NonPagedPool, DeviceExtension->RxMaxPacketSize); } pNewNotBuff = DEBUG_MEMALLOC(NonPagedPool, NOTIFICATION_BUFF_SIZE); pNewUSBReadBuff = DEBUG_MEMALLOC(NonPagedPool, USB_RX_BUFF_SIZE); ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); DeviceExtension->DataInPipe = PipeInformation->PipeHandle; if (DeviceExtension->NotificationBuff) pOldNotBuff = DeviceExtension->NotificationBuff; if (DeviceExtension->ReadBuff) pOldReadBuff = DeviceExtension->ReadBuff; if (DeviceExtension->USBReadBuff) pOldUSBReadBuff = DeviceExtension->USBReadBuff; DeviceExtension->RxQueueSize = DeviceExtension->RxMaxPacketSize; DeviceExtension->CharsInReadBuff = 0; DeviceExtension->CurrentReadBuffPtr = 0; DeviceExtension->ReadBuff = pNewReadBuff; DeviceExtension->USBReadBuff = pNewUSBReadBuff; DeviceExtension->NotificationBuff = pNewNotBuff; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); if (pOldNotBuff != NULL) { DEBUG_MEMFREE(pOldNotBuff); } if (pOldReadBuff != NULL) { DEBUG_MEMFREE(pOldReadBuff); } if (pOldUSBReadBuff != NULL) { DEBUG_MEMFREE(pOldUSBReadBuff); } } // check for notification pipe else if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_INTERRUPT) DeviceExtension->NotificationPipe = PipeInformation->PipeHandle; } else { // check for data out pipe if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK) DeviceExtension->DataOutPipe = PipeInformation->PipeHandle; } } DEBUG_TRACE3(("Data Out (%08X) Data In (%08X) Notification (%08X)\n", DeviceExtension->DataOutPipe, DeviceExtension->DataInPipe, DeviceExtension->NotificationPipe)); } ExFreePool(Urb); } else { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } if (!FoundCommDevice) NtStatus = STATUS_NO_SUCH_DEVICE; ExitSelectInterface:; DEBUG_LOG_PATH("exit SelectInterface"); return NtStatus; } // SelectInterface /************************************************************************/ /* BuildRequest */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Build a Urb for a USB request */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* Irp - pointer to Irp */ /* PipeHandle - USB pipe handle */ /* Read - transfer direction */ /* */ /* Return Value: */ /* */ /* Pointer to URB */ /* */ /************************************************************************/ PURB BuildRequest(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN USBD_PIPE_HANDLE PipeHandle, IN BOOLEAN Read) { ULONG Size; ULONG Length; PURB Urb; PAGED_CODE(); DEBUG_LOG_PATH("enter BuildRequest"); // length of buffer Length = MmGetMdlByteCount(Irp->MdlAddress); // allocate and zero Urb Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); Urb = DEBUG_MEMALLOC(NonPagedPool, Size); if (Urb) { RtlZeroMemory(Urb, Size); Urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) Size; Urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; Urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle; Urb->UrbBulkOrInterruptTransfer.TransferFlags = Read ? USBD_TRANSFER_DIRECTION_IN : 0; // use an MDL Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = Irp->MdlAddress; Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = Length; // short packet is not treated as an error. Urb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK; // no linkage for now Urb->UrbBulkOrInterruptTransfer.UrbLink = NULL; } DEBUG_LOG_PATH("exit BuildRequest"); return Urb; } // BuildRequest /************************************************************************/ /* BuildReadRequest */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Build a Urb for a USB read request */ /* */ /* Arguments: */ /* */ /* Urb - pointer to URB */ /* Buffer - pointer to data buffer */ /* Length - length of data buffer */ /* PipeHandle - USB pipe handle */ /* Read - transfer direction */ /* */ /* Return Value: */ /* */ /* VOID */ /* */ /************************************************************************/ VOID BuildReadRequest(PURB Urb, PUCHAR Buffer, ULONG Length, IN USBD_PIPE_HANDLE PipeHandle, IN BOOLEAN Read) { ULONG Size; // PAGED_CODE(); DEBUG_LOG_PATH("enter BuildReadRequest"); Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); // zero Urb RtlZeroMemory(Urb, Size); Urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) Size; Urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; Urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle; Urb->UrbBulkOrInterruptTransfer.TransferFlags = Read ? USBD_TRANSFER_DIRECTION_IN : 0; // we are using a tranfsfer buffer instead of an MDL Urb->UrbBulkOrInterruptTransfer.TransferBuffer = Buffer; Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = Length; Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; // short packet is not treated as an error. Urb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK; // no linkage for now Urb->UrbBulkOrInterruptTransfer.UrbLink = NULL; DEBUG_LOG_PATH("exit BuildReadRequest"); } // BuildReadRequest /************************************************************************/ /* ClassVendorCommand */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Issue class or vendor specific command */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to a device object */ /* Request - request field of class/vendor specific command */ /* Value - value field of class/vendor specific command */ /* Index - index field of class/vendor specific command */ /* Buffer - pointer to data buffer */ /* BufferLen - data buffer length */ /* Read - data direction flag */ /* Class - True if Class Command, else vendor command */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS ClassVendorCommand(IN PDEVICE_OBJECT DeviceObject, IN UCHAR Request, IN USHORT Value, IN USHORT Index, IN PVOID Buffer, IN OUT PULONG BufferLen, IN BOOLEAN Read, IN ULONG ComType) { NTSTATUS NtStatus; PURB Urb; ULONG Size; ULONG Length; PAGED_CODE(); DEBUG_LOG_PATH("enter VendorCommand"); // length of buffer passed in Length = BufferLen ? *BufferLen : 0; Size = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST); // allocate memory for the Urb Urb = DEBUG_MEMALLOC(NonPagedPool, Size); if (Urb) { UsbBuildVendorRequest(Urb, ComType == USBSER_CLASS_COMMAND ? URB_FUNCTION_CLASS_INTERFACE : URB_FUNCTION_VENDOR_DEVICE, (USHORT) Size, Read ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT, 0, Request, Value, Index, Buffer, NULL, Length, NULL); NtStatus = CallUSBD(DeviceObject, Urb); // get length of buffer if (BufferLen) *BufferLen = Urb->UrbControlVendorClassRequest.TransferBufferLength; DEBUG_MEMFREE(Urb); } else { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } DEBUG_LOG_PATH("exit VendorCommand"); return NtStatus; } // ClassVendorCommand /************************************************************************/ /* CancelPendingWaitMasks */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Cancels any wait masks in progress. */ /* */ /* Arguments: */ /* */ /* DeviceExtension - pointer to a device extension */ /* */ /* Return Value: */ /* */ /* VOID */ /* */ /************************************************************************/ VOID CancelPendingWaitMasks(IN PDEVICE_EXTENSION DeviceExtension) { KIRQL OldIrql; PIRP CurrentMaskIrp; DEBUG_LOG_PATH("enter CancelPendingWaitMasks"); UsbSerSerialDump(USBSERTRACEOTH, (">CancelPendingWaitMasks\n")); ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); CurrentMaskIrp = DeviceExtension->CurrentMaskIrp; // mark current pending wait mask as cancelled if(CurrentMaskIrp) { DeviceExtension->CurrentMaskIrp = NULL; CurrentMaskIrp->IoStatus.Status = STATUS_CANCELLED; CurrentMaskIrp->IoStatus.Information = 0; IoSetCancelRoutine(CurrentMaskIrp, NULL); RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); IoCompleteRequest(CurrentMaskIrp, IO_NO_INCREMENT); DEBUG_TRACE1(("CancelPendingWaitMask\n")); } else { RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); } DEBUG_LOG_PATH("exit CancelPendingWaitMasks"); UsbSerSerialDump(USBSERTRACEOTH, ("StartRead\n")); // get stack size for Irp and allocate one that we will use to keep // read requests going StackSize = (CCHAR)(DeviceExtension->StackDeviceObject->StackSize + 1); ReadIrp = IoAllocateIrp(StackSize, FALSE); if (ReadIrp) { // get size of Urb and allocate Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); ReadUrb = DEBUG_MEMALLOC(NonPagedPool, Size); if (ReadUrb) { KeInitializeEvent(&DeviceExtension->ReadEvent, NotificationEvent, FALSE); // save these to be freed when not needed UsbSerFetchPVoidLocked(&DeviceExtension->ReadIrp, ReadIrp, &DeviceExtension->ControlLock); UsbSerFetchPVoidLocked(&DeviceExtension->ReadUrb, ReadUrb, &DeviceExtension->ControlLock); RestartRead(DeviceExtension); } } UsbSerSerialDump(USBSERTRACERD, ("RestartRead\n")); do { StartAnotherRead = FALSE; ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); if(!DeviceExtension->ReadInProgress && DeviceExtension->CharsInReadBuff <= LOW_WATER_MARK && DeviceExtension->AcceptingRequests) { StartAnotherRead = TRUE; DeviceExtension->ReadInProgress = TRUE; DeviceExtension->ReadInterlock = START_READ; } RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); if(StartAnotherRead) { ReadIrp = DeviceExtension->ReadIrp; ReadUrb = DeviceExtension->ReadUrb; BuildReadRequest(ReadUrb, DeviceExtension->USBReadBuff, USB_RX_BUFF_SIZE, DeviceExtension->DataInPipe, TRUE); // set Irp up for a submit Urb IOCTL NextStack = IoGetNextIrpStackLocation(ReadIrp); NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; NextStack->Parameters.Others.Argument1 = ReadUrb; NextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; // completion routine will take care of updating buffers and counters IoSetCompletionRoutine(ReadIrp,ReadCompletion, DeviceExtension, TRUE, TRUE, TRUE); DEBUG_TRACE1(("StartRead\n")); InterlockedIncrement(&DeviceExtension->PendingDataInCount); UsbSerSerialDump(USBSERTRACERD, ("DataInCounty %08X @ %08X\n", DeviceExtension->PendingDataInCount, &DeviceExtension->PendingDataInCount)); NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, ReadIrp); DEBUG_TRACE1(("Read Status (%08X)\n", NtStatus)); if(!NT_SUCCESS(NtStatus)) { if(InterlockedDecrement(&DeviceExtension->PendingDataInCount) == 0) { KeSetEvent(&DeviceExtension->PendingDataInEvent, IO_NO_INCREMENT, FALSE); UsbSerSerialDump(USBSERTRACERD, ("DataInCountz %08X @ %08X\n", DeviceExtension->PendingDataInCount, &DeviceExtension->PendingDataInCount)); } } ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); if(DeviceExtension->ReadInterlock == IMMEDIATE_READ) { StartAnotherRead = TRUE; } else { StartAnotherRead = FALSE; } DeviceExtension->ReadInterlock = END_READ; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); } }while(StartAnotherRead) DEBUG_LOG_PATH("exit RestartRead"); UsbSerSerialDump(USBSERTRACERD, ("StartNotifyRead\n")); // get stack size for Irp and allocate one that we will use to keep // notification requests going StackSize = (CCHAR)(DeviceExtension->StackDeviceObject->StackSize + 1); NotifyIrp = IoAllocateIrp(StackSize, FALSE); if (NotifyIrp) { // get size of Urb and allocate Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); NotifyUrb = DEBUG_MEMALLOC(NonPagedPool, Size); if (NotifyUrb) { // save these to be freed when not needed UsbSerFetchPVoidLocked(&DeviceExtension->NotifyIrp, NotifyIrp, &DeviceExtension->ControlLock); UsbSerFetchPVoidLocked(&DeviceExtension->NotifyUrb, NotifyUrb, &DeviceExtension->ControlLock); RestartNotifyRead(DeviceExtension); } } DEBUG_LOG_PATH("exit StartNotifyRead"); UsbSerSerialDump(USBSERTRACERD, ("RestartNotifyRead\n")); NotifyUrb = DeviceExtension->NotifyUrb; NotifyIrp = DeviceExtension->NotifyIrp; if(DeviceExtension->AcceptingRequests) { BuildReadRequest(NotifyUrb, DeviceExtension->NotificationBuff, NOTIFICATION_BUFF_SIZE, DeviceExtension->NotificationPipe, TRUE); // set Irp up for a submit Urb IOCTL NextStack = IoGetNextIrpStackLocation(NotifyIrp); NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; NextStack->Parameters.Others.Argument1 = NotifyUrb; NextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; // completion routine will take care of updating buffers and counters IoSetCompletionRoutine(NotifyIrp, NotifyCompletion, DeviceExtension, TRUE, TRUE, TRUE); DEBUG_TRACE1(("Start NotifyRead\n")); InterlockedIncrement(&DeviceExtension->PendingNotifyCount); NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, NotifyIrp); if (!NT_SUCCESS(NtStatus)) { if (InterlockedDecrement(&DeviceExtension->PendingNotifyCount) == 0) { KeSetEvent(&DeviceExtension->PendingNotifyEvent, IO_NO_INCREMENT, FALSE); } } DEBUG_TRACE1(("Status (%08X)\n", NtStatus)); } DEBUG_LOG_PATH("exit RestartNotifyRead"); UsbSerSerialDump(USBSERTRACERD, ("ReadCompletion(%08X)\n", Irp)); ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); Urb = DeviceExtension->ReadUrb; Count = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength; if (NT_SUCCESS(Irp->IoStatus.Status) && (DeviceExtension->CurrentDevicePowerState == PowerDeviceD0)) { DeviceExtension->HistoryMask |= SERIAL_EV_RXCHAR | SERIAL_EV_RX80FULL; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); // // Scan for RXFLAG char if needed // if(DeviceExtension->IsrWaitMask & SERIAL_EV_RXFLAG) { ULONG i; for(i = 0; i < Count; i++) { if(*((PUCHAR)(DeviceExtension->USBReadBuff + i)) == DeviceExtension->SpecialChars.EventChar) { DeviceExtension->HistoryMask |= SERIAL_EV_RXFLAG; break; } } } PutData(DeviceExtension, Count); // got some data, let's see if we can satisfy any queued reads CheckForQueuedReads(DeviceExtension); DEBUG_TRACE1(("ReadCompletion (%08X)\n", DeviceExtension->CharsInReadBuff)); ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); DeviceExtension->ReadInProgress = FALSE; if(DeviceExtension->ReadInterlock == END_READ) { DeviceExtension->ReadInterlock = IMMEDIATE_READ; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); RestartRead(DeviceExtension); } else { DeviceExtension->ReadInterlock = IMMEDIATE_READ; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); } } else { // // the device is not accepting requests, so signal anyone who // cancelled this or is waiting for it to stop // DeviceExtension->ReadInterlock = IMMEDIATE_READ; DeviceExtension->ReadInProgress = FALSE; DeviceExtension->AcceptingRequests = FALSE; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); KeSetEvent(&DeviceExtension->ReadEvent, 1, FALSE); DEBUG_TRACE1(("RC Irp Status (%08X)\n", Irp->IoStatus.Status)); } // // Notify everyone if this is the last IRP // if (InterlockedDecrement(&DeviceExtension->PendingDataInCount) == 0) { UsbSerSerialDump(USBSERTRACEOTH, ("DataIn pipe is empty\n")); KeSetEvent(&DeviceExtension->PendingDataInEvent, IO_NO_INCREMENT, FALSE); } UsbSerSerialDump(USBSERTRACERD, ("DataInCount %08X @ %08X\n", DeviceExtension->PendingDataInCount, &DeviceExtension->PendingDataInCount)); DEBUG_LOG_PATH("exit ReadCompletion"); UsbSerSerialDump(USBSERTRACERD, ("GetData\n")); ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); BufferLen = min(DeviceExtension->CharsInReadBuff, BufferLen); if(BufferLen) { count = min(BufferLen, (DeviceExtension->RxMaxPacketSize - DeviceExtension->CurrentReadBuffPtr)); memcpy(Buffer, &DeviceExtension->ReadBuff[DeviceExtension->CurrentReadBuffPtr], count); Buffer += count; DeviceExtension->CurrentReadBuffPtr += count; DeviceExtension->CharsInReadBuff -= count; DeviceExtension->NumberNeededForRead -= count; BufferLen -= count; *NewCount += count; // if there is still something left in the buffer, then we wrapped if(BufferLen) { memcpy(Buffer, DeviceExtension->ReadBuff, BufferLen); DeviceExtension->CurrentReadBuffPtr = BufferLen; DeviceExtension->CharsInReadBuff -= BufferLen; DeviceExtension->NumberNeededForRead -= BufferLen; *NewCount += BufferLen; } } DEBUG_TRACE2(("Count (%08X) CharsInReadBuff (%08X)\n", count, DeviceExtension->CharsInReadBuff)); #if DBG if (UsbSerSerialDebugLevel & USBSERDUMPRD) { ULONG i; DbgPrint("RD: "); for (i = 0; i < count; i++) { DbgPrint("%02x ", Buffer[i] & 0xFF); } DbgPrint("\n\n"); } #endif RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); RestartRead(DeviceExtension); DEBUG_LOG_PATH("exit GetData"); UsbSerSerialDump(USBSERTRACERD, ("ControlLock, &OldIrql); // get current pointer into circular buffer BuffPtr = (DeviceExtension->CharsInReadBuff + DeviceExtension->CurrentReadBuffPtr) % DeviceExtension->RxMaxPacketSize; // figure out amount to copy into read buffer, in case we would right past end of buffer count = min(BufferLen, (DeviceExtension->RxMaxPacketSize - BuffPtr)); memcpy(&DeviceExtension->ReadBuff[BuffPtr], DeviceExtension->USBReadBuff, count); // update counters BufferLen -= count; DeviceExtension->CharsInReadBuff += count; DeviceExtension->ReadByIsr += count; // if there is still something left in the buffer, then we wrapped if(BufferLen) { // count still holds the amount copied from buffer on first copy // and BufferLen holds the amount remaining to copy memcpy(DeviceExtension->ReadBuff, &DeviceExtension->USBReadBuff[count], BufferLen); DeviceExtension->CharsInReadBuff += BufferLen; DeviceExtension->ReadByIsr += BufferLen; } RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); } } // PutData VOID UsbSerRundownIrpRefs(IN PIRP *PpCurrentOpIrp, IN PKTIMER IntervalTimer OPTIONAL, IN PKTIMER TotalTimer OPTIONAL, IN PDEVICE_EXTENSION PDevExt) /*++ Routine Description: This routine runs through the various items that *could* have a reference to the current read/write. It try's to kill the reason. If it does succeed in killing the reason it will decrement the reference count on the irp. NOTE: This routine assumes that it is called with the cancel spin lock held. Arguments: PpCurrentOpIrp - Pointer to a pointer to current irp for the particular operation. IntervalTimer - Pointer to the interval timer for the operation. NOTE: This could be null. TotalTimer - Pointer to the total timer for the operation. NOTE: This could be null. PDevExt - Pointer to device extension Return Value: None. --*/ { // PAGED_CODE(); UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerRundownIrpRefs(%08X)\n", *PpCurrentOpIrp)); // // This routine is called with the cancel spin lock held // so we know only one thread of execution can be in here // at one time. // // // First we see if there is still a cancel routine. If // so then we can decrement the count by one. // if ((*PpCurrentOpIrp)->CancelRoutine) { USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_CANCEL); IoSetCancelRoutine(*PpCurrentOpIrp, NULL); } if (IntervalTimer) { // // Try to cancel the operations interval timer. If the operation // returns true then the timer did have a reference to the // irp. Since we've canceled this timer that reference is // no longer valid and we can decrement the reference count. // // If the cancel returns false then this means either of two things: // // a) The timer has already fired. // // b) There never was an interval timer. // // In the case of "b" there is no need to decrement the reference // count since the "timer" never had a reference to it. // // In the case of "a", then the timer itself will be coming // along and decrement it's reference. Note that the caller // of this routine might actually be the this timer, but it // has already decremented the reference. // if (KeCancelTimer(IntervalTimer)) { USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_INT_TIMER); } } if (TotalTimer) { // // Try to cancel the operations total timer. If the operation // returns true then the timer did have a reference to the // irp. Since we've canceled this timer that reference is // no longer valid and we can decrement the reference count. // // If the cancel returns false then this means either of two things: // // a) The timer has already fired. // // b) There never was an total timer. // // In the case of "b" there is no need to decrement the reference // count since the "timer" never had a reference to it. // // // If we have an escape char event pending, we can't overstuff, // so subtract one from the length // // In the case of "a", then the timer itself will be coming // along and decrement it's reference. Note that the caller // of this routine might actually be the this timer, but it // has already decremented the reference. // if (KeCancelTimer(TotalTimer)) { USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_TOTAL_TIMER); } } // USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_RXBUFFER); UsbSerSerialDump(USBSERTRACEOTH, ("UsbSerTryToCompleteCurrent(%08X)\n", *PpCurrentOpIrp)); // // We can decrement the reference to "remove" the fact // that the caller no longer will be accessing this irp. // USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, RefType); // // Try to run down all other references to this irp. // UsbSerRundownIrpRefs(PpCurrentOpIrp, PIntervalTimer, PTotalTimer, PDevExt); // // See if the ref count is zero after trying to kill everybody else. // if (!USBSER_REFERENCE_COUNT(*PpCurrentOpIrp)) { PIRP pNewIrp; // // The ref count was zero so we should complete this // request. // // The following call will also cause the current irp to be // completed. // (*PpCurrentOpIrp)->IoStatus.Status = StatusToUse; if (StatusToUse == STATUS_CANCELLED) { (*PpCurrentOpIrp)->IoStatus.Information = 0; } if (PGetNextIrp) { RELEASE_CANCEL_SPINLOCK(PDevExt, IrqlForRelease); (*PGetNextIrp)(PpCurrentOpIrp, PQueue, &pNewIrp, Complete, PDevExt); if (pNewIrp) { Starter(PDevExt); } } else { PIRP pOldIrp = *PpCurrentOpIrp; // // There was no get next routine. We will simply complete // the irp. We should make sure that we null out the // pointer to the pointer to this irp. // *PpCurrentOpIrp = NULL; RELEASE_CANCEL_SPINLOCK(PDevExt, IrqlForRelease); if (Complete) { IoCompleteRequest(pOldIrp, IO_SERIAL_INCREMENT); } } } else { RELEASE_CANCEL_SPINLOCK(PDevExt, IrqlForRelease); UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR, ("Current IRP still has reference of %08X\n", ((UINT_PTR)((IoGetCurrentIrpStackLocation((*PpCurrentOpIrp))-> Parameters.Others.Argument4))))); } UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR, ("CheckForQueuedReads\n")); ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &oldIrql); if ((DeviceExtension->CurrentReadIrp != NULL) && (USBSER_REFERENCE_COUNT(DeviceExtension->CurrentReadIrp) & USBSER_REF_RXBUFFER)) { RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql); DEBUG_TRACE3(("Reading 0x%x\n", DeviceExtension->NumberNeededForRead)); charsRead = GetData(DeviceExtension, ((PUCHAR)(DeviceExtension->CurrentReadIrp ->AssociatedIrp.SystemBuffer)) + (IoGetCurrentIrpStackLocation(DeviceExtension ->CurrentReadIrp)) ->Parameters.Read.Length - DeviceExtension->NumberNeededForRead, DeviceExtension->NumberNeededForRead, &DeviceExtension->CurrentReadIrp->IoStatus.Information); ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &oldIrql); // // See if this read is complete // if (DeviceExtension->NumberNeededForRead == 0) { DEBUG_TRACE3(("USBSER: Completing read\n")); if(DeviceExtension->CurrentReadIrp) { DeviceExtension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS; } // // Mark the read as completed and try to service the next one // DeviceExtension->CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE; #if DBG if (UsbSerSerialDebugLevel & USBSERDUMPRD) { ULONG i; ULONG count; if (DeviceExtension->CurrentReadIrp->IoStatus.Status == STATUS_SUCCESS) { count = (ULONG)DeviceExtension->CurrentReadIrp->IoStatus.Information; } else { count = 0; } DbgPrint("RD2: A(%08X) G(%08X) I(%08X)\n", IoGetCurrentIrpStackLocation(DeviceExtension->CurrentReadIrp) ->Parameters.Read.Length, count, DeviceExtension->CurrentReadIrp); for (i = 0; i < count; i++) { DbgPrint("%02x ", *(((PUCHAR)DeviceExtension->CurrentReadIrp ->AssociatedIrp.SystemBuffer) + i) & 0xFF); } if (i == 0) { DbgPrint("NULL (%08X)\n", DeviceExtension->CurrentReadIrp ->IoStatus.Status); } DbgPrint("\n\n"); } #endif UsbSerTryToCompleteCurrent(DeviceExtension, oldIrql, STATUS_SUCCESS, &DeviceExtension->CurrentReadIrp, &DeviceExtension->ReadQueue, &DeviceExtension->ReadRequestIntervalTimer, &DeviceExtension->ReadRequestTotalTimer, UsbSerStartRead, UsbSerGetNextIrp, USBSER_REF_RXBUFFER, TRUE); ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &oldIrql); } } if (DeviceExtension->IsrWaitMask & SERIAL_EV_RXCHAR) { DeviceExtension->HistoryMask |= SERIAL_EV_RXCHAR; } if (DeviceExtension->CurrentMaskIrp != NULL) { pWaitMask = (PULONG)DeviceExtension->CurrentMaskIrp-> AssociatedIrp.SystemBuffer; // // Process events // if (DeviceExtension->IsrWaitMask & DeviceExtension->HistoryMask) { PIRP pMaskIrp; DEBUG_TRACE3(("Completing events\n")); *pWaitMask = DeviceExtension->HistoryMask; DeviceExtension->HistoryMask = 0; pMaskIrp = DeviceExtension->CurrentMaskIrp; pMaskIrp->IoStatus.Information = sizeof(ULONG); pMaskIrp->IoStatus.Status = STATUS_SUCCESS; DeviceExtension->CurrentMaskIrp = NULL; IoSetCancelRoutine(pMaskIrp, NULL); RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql); IoCompleteRequest(pMaskIrp, IO_SERIAL_INCREMENT); } else { RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql); } } else { RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql); } DEBUG_LOG_PATH("exit CheckForQueuedReads"); UsbSerSerialDump(USBSERTRACERD, ("UsbSerGetNextIrp(%08)\n", *PpCurrentOpIrp)); ACQUIRE_CANCEL_SPINLOCK(PDevExt, &oldIrql); pOldIrp = *PpCurrentOpIrp; #if DBG if (pOldIrp != NULL) { if (CompleteCurrent) { ASSERT(pOldIrp->CancelRoutine == NULL); } } #endif // // Check to see if there is a new irp to start up // if (!IsListEmpty(PQueueToProcess)) { PLIST_ENTRY pHeadOfList; pHeadOfList = RemoveHeadList(PQueueToProcess); *PpCurrentOpIrp = CONTAINING_RECORD(pHeadOfList, IRP, Tail.Overlay.ListEntry); IoSetCancelRoutine(*PpCurrentOpIrp, NULL); } else { *PpCurrentOpIrp = NULL; } *PpNextIrp = *PpCurrentOpIrp; RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql); // // Complete the current one if so requested // if (CompleteCurrent) { if (pOldIrp != NULL) { IoCompleteRequest(pOldIrp, IO_SERIAL_INCREMENT); } } DEBUG_LOG_PATH("Exit UsbSerGetNextIrp"); UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR, ("UsbSerStartOrQueue(%08X)\n", PIrp)); ACQUIRE_CANCEL_SPINLOCK(PDevExt, &oldIrql); if (IsListEmpty(PQueue) && (*PPCurrentIrp == NULL)) { // // Nothing pending -- start the new irp // *PPCurrentIrp = PIrp; RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql); status = Starter(PDevExt); DEBUG_LOG_PATH("Exit UsbSerStartOrQueue(1)"); UsbSerSerialDump(USBSERTRACEOTH, ("Cancel) { // // The IRP was apparently cancelled. Complete it. // RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql); PIrp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(PIrp, IO_NO_INCREMENT); DEBUG_LOG_PATH("Exit UsbSerStartOrQueue(2)"); UsbSerSerialDump(USBSERTRACEOTH, ("IoStatus.Status = STATUS_PENDING; IoMarkIrpPending(PIrp); InsertTailList(PQueue, &PIrp->Tail.Overlay.ListEntry); IoSetCancelRoutine(PIrp, UsbSerCancelQueued); RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql); DEBUG_LOG_PATH("Exit UsbSerStartOrQueue(3)"); UsbSerSerialDump(USBSERTRACEOTH, ("DeviceExtension; PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(PIrp); USBSER_ALWAYS_LOCKED_CODE(); DEBUG_LOG_PATH("Enter UsbSerCancelQueued"); UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerCancelQueued(%08X)\n", PIrp)); // // The irp was cancelled -- remove it from the queue // PIrp->IoStatus.Status = STATUS_CANCELLED; PIrp->IoStatus.Information = 0; RemoveEntryList(&PIrp->Tail.Overlay.ListEntry); RELEASE_CANCEL_SPINLOCK(pDevExt, PIrp->CancelIrql); IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT); DEBUG_LOG_PATH("Exit UsbSerCancelQueued"); UsbSerSerialDump(USBSERTRACEOTH, ("DeviceExtension; USBSER_ALWAYS_LOCKED_CODE(); UsbSerSerialDump(USBSERTRACERD | USBSERTRACEWR, (">UsbSerKillAllReadsOrWrites(%08X)\n", *PpCurrentOpIrp)); // // We acquire the cancel spin lock. This will prevent the // irps from moving around. // ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql); // // Clean the list from back to front. // while (!IsListEmpty(PQueueToClean)) { PIRP pCurrentLastIrp = CONTAINING_RECORD(PQueueToClean->Blink, IRP, Tail.Overlay.ListEntry); RemoveEntryList(PQueueToClean->Blink); cancelRoutine = pCurrentLastIrp->CancelRoutine; pCurrentLastIrp->CancelIrql = cancelIrql; pCurrentLastIrp->CancelRoutine = NULL; pCurrentLastIrp->Cancel = TRUE; cancelRoutine(PDevObj, pCurrentLastIrp); ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql); } // // The queue is clean. Now go after the current if // it's there. // if (*PpCurrentOpIrp) { cancelRoutine = (*PpCurrentOpIrp)->CancelRoutine; (*PpCurrentOpIrp)->Cancel = TRUE; // // If the current irp is not in a cancelable state // then it *will* try to enter one and the above // assignment will kill it. If it already is in // a cancelable state then the following will kill it. // if (cancelRoutine) { (*PpCurrentOpIrp)->CancelRoutine = NULL; (*PpCurrentOpIrp)->CancelIrql = cancelIrql; // // This irp is already in a cancelable state. We simply // mark it as canceled and call the cancel routine for // it. // cancelRoutine(PDevObj, *PpCurrentOpIrp); } else { RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql); } } else { RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql); } UsbSerSerialDump(USBSERTRACERD | USBSERTRACEWR, ("DeviceExtension; KIRQL cancelIrql; USBSER_ALWAYS_LOCKED_CODE(); UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerKillPendingIrps\n")); // // Kill all reads; we do not queue writes // UsbSerKillAllReadsOrWrites(PDevObj, &pDevExt->ReadQueue, &pDevExt->CurrentReadIrp); // // Get rid of any pending waitmasks // ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql); if (pDevExt->CurrentMaskIrp != NULL) { PDRIVER_CANCEL cancelRoutine; cancelRoutine = pDevExt->CurrentMaskIrp->CancelRoutine; pDevExt->CurrentMaskIrp->Cancel = TRUE; ASSERT(cancelRoutine); if (cancelRoutine) { pDevExt->CurrentMaskIrp->CancelRoutine = NULL; pDevExt->CurrentMaskIrp->CancelIrql = cancelIrql; cancelRoutine(PDevObj, pDevExt->CurrentMaskIrp); } else { RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql); } }else { RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql); } // // Cancel any pending wait-wake irps // if (pDevExt->PendingWakeIrp != NULL) { IoCancelIrp(pDevExt->PendingWakeIrp); pDevExt->PendingWakeIrp = NULL; } UsbSerSerialDump(USBSERTRACEOTH, ("CompletePendingWaitMasks\n")); ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &cancelIrql); ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql); CurrentMaskIrp = DeviceExtension->CurrentMaskIrp; if (CurrentMaskIrp) { CurrentMaskIrp->IoStatus.Status = STATUS_SUCCESS; CurrentMaskIrp->IoStatus.Information = sizeof(ULONG); *((PULONG)CurrentMaskIrp->AssociatedIrp.SystemBuffer) = 0; DeviceExtension->CurrentMaskIrp = NULL; IoSetCancelRoutine(CurrentMaskIrp, NULL); } // complete the queued IRP if needed if (CurrentMaskIrp) { RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); RELEASE_CANCEL_SPINLOCK(DeviceExtension, cancelIrql); IoCompleteRequest(CurrentMaskIrp, IO_NO_INCREMENT); DEBUG_TRACE1(("CompletePendingWaitMask\n")); } else { RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql); RELEASE_CANCEL_SPINLOCK(DeviceExtension, cancelIrql); } DEBUG_LOG_PATH("exit CompletePendingWaitMasks"); UsbSerSerialDump(USBSERTRACEOTH, ("HistoryMask |= SERIAL_EV_TXEMPTY; if (PDevExt->IsrWaitMask & SERIAL_EV_TXEMPTY) { PIRP pMaskIrp; DEBUG_TRACE3(("Completing events\n")); ACQUIRE_CANCEL_SPINLOCK(PDevExt, &oldIrql); if (PDevExt->CurrentMaskIrp != NULL) { pWaitMask = (PULONG)PDevExt->CurrentMaskIrp-> AssociatedIrp.SystemBuffer; *pWaitMask = PDevExt->HistoryMask; PDevExt->HistoryMask = 0; pMaskIrp = PDevExt->CurrentMaskIrp; pMaskIrp->IoStatus.Information = sizeof(ULONG); pMaskIrp->IoStatus.Status = STATUS_SUCCESS; PDevExt->CurrentMaskIrp = NULL; IoSetCancelRoutine(pMaskIrp, NULL); RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql); IoCompleteRequest(pMaskIrp, IO_SERIAL_INCREMENT); } else { RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql); } } } VOID UsbSerCancelWaitOnMask(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp) /*++ Routine Description: This function is used as a cancel routine for WaitOnMask IRPs. Arguments: PDevObj - Pointer to Device Object PIrp - Pointer to IRP that is being canceled; must be the same as the current mask IRP. Return Value: VOID --*/ { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension; USBSER_ALWAYS_LOCKED_CODE(); UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerCancelWaitOnMask(%08X)\n", PIrp)); ASSERT(pDevExt->CurrentMaskIrp == PIrp); PIrp->IoStatus.Status = STATUS_CANCELLED; PIrp->IoStatus.Information = 0; pDevExt->CurrentMaskIrp = NULL; RELEASE_CANCEL_SPINLOCK(pDevExt, PIrp->CancelIrql); IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT); UsbSerSerialDump(USBSERTRACEOTH, ("ControlLock, &oldIrql); ioWorkItem = DeviceExtension->IoWorkItem; DeviceExtension->IoWorkItem = NULL; RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, oldIrql); IoFreeWorkItem(ioWorkItem); RestartNotifyRead(DeviceExtension); } // USBER_RestartNotifyReadWorkItem