// write.c // // // Requires DDK Only // File created on 2/2/2005 // #include "pch.h" #ifdef CPR_WMI_TRACE #include "write.tmh" #endif /////////////////////////////////////////////////////////////////////////////////////////////////// // CprWriteDispatch // Handles incoming write requests // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // The write IRP to handle // // Return Value: // NT status code // NTSTATUS CprWriteDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCPR_DEVICE_EXTENSION deviceExtension; NTSTATUS status; POWER_STATE powerState; __try { deviceExtension = (PCPR_DEVICE_EXTENSION)DeviceObject->DeviceExtension; CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"++. IRP %p", Irp); // check the device error condition status = CprCheckForError(deviceExtension, Irp); if (!NT_SUCCESS(status)) { return status; } Irp->IoStatus.Information = 0; if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length) { if (deviceExtension->OpenHandleCount == 0) { status = STATUS_CONNECTION_DISCONNECTED; IoSetCancelRoutine(Irp, NULL); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { status = CprWriteData(Irp, deviceExtension); } } else { status = STATUS_SUCCESS; IoSetCancelRoutine(Irp, NULL); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } CprDebugPrint2(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); } __except( EXCEPTION_EXECUTE_HANDLER ) { status = STATUS_INTERNAL_ERROR; } return status; } NTSTATUS CprWriteData( IN PIRP Irp, IN PCPR_DEVICE_EXTENSION deviceExtension ) { NTSTATUS status = STATUS_SUCCESS; KIRQL OldIrql; PCPR_TDI_SOCKET Socket; void FillMsgBuf(char *msg, char *buf, int amountToWrite); CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"++. IRP %p", Irp); // Clear the area where we save the TdiIrp in Irp //TDI_IRP_REFERENCE(Irp) = NULL; //deviceExtension->socket->WriteTdiIrp = NULL; CprAcquireSerialSpinLock(deviceExtension, &OldIrql); deviceExtension->WaitEvents &= ~SERIAL_EV_TXEMPTY; // check if we set a transmit complete timer #ifdef BUFFER_STATE #else if (deviceExtension->TxEmptyRef == CPR_TXEMPTY_DPC) { if (KeCancelTimer(&deviceExtension->TxEmptyTimer)) { // timer is canceled deviceExtension->TxEmptyRef = CPR_TXEMPTY_CLEAR; } } #endif //if (deviceExtension->TxEmptyVerifyPending) //{ deviceExtension->TxEmptyVerifyPending = FALSE; //deviceExtension->TxEmptyRef = CPR_TXEMPTY_CANCEL; //} CprReleaseSerialSpinLock(deviceExtension, OldIrql); if (LoggingEvent) { char msg[CPR_EVENT_MSG_SIZE]; FillMsgBuf(msg, (char*)Irp->AssociatedIrp.SystemBuffer, IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length); _CprLogEvent( CPR_EVENT_TYPE_WRITE, CPR_EVENT_SUB_TYPE_NONE, deviceExtension, STATUS_SUCCESS, msg); } // if (deviceExtension->WaitOnWrite == 0) //if (TRUE) //{ CprDebugPrint3(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, "Queueing Write App Irp 0x%08X Buf 0x%08X Len %d", Irp, Irp->AssociatedIrp.SystemBuffer, IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length); status = CprQueueIrp(&deviceExtension->WriteQueue, Irp, TRUE); //} //else //{ // CprAcquireSerialSpinLock(deviceExtension, &OldIrql); // Socket = deviceExtension->socket; // KeClearEvent(&Socket->WaitWriteEvent); // Irp->UserEvent = &Socket->WaitWriteEvent; // CprReleaseSerialSpinLock(deviceExtension, OldIrql); // CprDebugPrint3(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, // "Queueing Write App Irp 0x%08X Buf 0x%08X Len %d", // Irp, Irp->AssociatedIrp.SystemBuffer, // IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length); // status = CprQueueIrp(&deviceExtension->WriteQueue, Irp, FALSE); // if (status == STATUS_PENDING) // { // KeWaitForSingleObject(&Socket->WaitWriteEvent, Executive, KernelMode, FALSE, NULL); // status = Socket->IoStatus.Status; // Irp->IoStatus.Status = status; // IoSetCancelRoutine(Irp, NULL); // IoCompleteRequest(Irp, IO_NO_INCREMENT); // } // Irp->UserEvent = NULL; //} CprDebugPrint2(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // WriteQueueStartIo // StartIo routine for WriteQueue IRP queue, processes serialized // requests // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // The WriteQueue IRP to process // // Return Value: // None // // If we are in buffering mode and the size of the buffer in the IRP is // greater than the size of the Uart TxBuffer then override the buffer mode // logic and use the IRP write path. // VOID WriteQueueStartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCPR_DEVICE_EXTENSION deviceExtension; NTSTATUS status; PIO_STACK_LOCATION irpStack; ULONG writeLength; BOOLEAN setTimer; SERIAL_TIMEOUTS timeouts; LARGE_INTEGER totalTime; PIRP xoffIrp; PCPR_UART Uart; KIRQL oldIrql; // Get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)DeviceObject->DeviceExtension; CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"++. IRP %p", Irp); irpStack = IoGetCurrentIrpStackLocation(Irp); if (irpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) { CprStartNext(&deviceExtension->WriteQueue); // cancel the Flush Buffers timeout routine KeCancelTimer(&deviceExtension->FlushBuffersTimer); IoSetCancelRoutine(Irp, NULL); Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return; } // check Xoff counter IRP //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); CprAcquireSerialSpinLock(deviceExtension, &oldIrql); if (deviceExtension->XoffIrp != NULL) { // cancel Xoff io CprSerialStopXoff(deviceExtension); // cancel Xoff timer if (deviceExtension->XoffIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->XoffTimer)) { deviceExtension->XoffIrpRef &= ~CPR_IRPREF_TIMER; } } // remove cancel routine if (deviceExtension->XoffIrpRef & CPR_IRPREF_CANCEL) { if (IoSetCancelRoutine(deviceExtension->XoffIrp, NULL)) { deviceExtension->XoffIrpRef &= ~CPR_IRPREF_CANCEL; } } } if ((deviceExtension->XoffIrp != NULL) && (deviceExtension->XoffIrpRef == 0)) { xoffIrp = deviceExtension->XoffIrp; deviceExtension->XoffIrp = NULL; } else { xoffIrp = NULL; } timeouts = deviceExtension->Timeouts; // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (xoffIrp != NULL) { xoffIrp->IoStatus.Status = STATUS_SERIAL_MORE_WRITES; xoffIrp->IoStatus.Information = 0; IoSetCancelRoutine(xoffIrp, NULL); IoCompleteRequest(xoffIrp, IO_NO_INCREMENT); } // Get the write buffer length writeLength = (irpStack->MajorFunction == IRP_MJ_WRITE) ? irpStack->Parameters.Write.Length : 1; // check if there is a write request timeout if (timeouts.WriteTotalTimeoutConstant || timeouts.WriteTotalTimeoutMultiplier) { setTimer = TRUE; // calculate write request timeout totalTime.QuadPart = (UInt32x32To64(writeLength, timeouts.WriteTotalTimeoutMultiplier) + timeouts.WriteTotalTimeoutConstant)* 10000; totalTime = RtlLargeIntegerNegate(totalTime); } else { setTimer = FALSE; } //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); CprAcquireSerialSpinLock(deviceExtension, &oldIrql); deviceExtension->WriteIrpRef = 0; // set completion routine and check if IRP was already canceled IoSetCancelRoutine(Irp, CprSerialCancelCurrentWrite); if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL)) { // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); // IRP was canceled, so complete it Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_CANCELLED; #ifdef USE_WAIT_ON_WRITES if (deviceExtension->WaitOnWrite) { if (Irp->UserEvent) { KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE); } } else #endif { IoSetCancelRoutine(Irp, NULL); IoCompleteRequest(Irp, IO_NO_INCREMENT); CprStartNext(&deviceExtension->WriteQueue); } } else { // record that write irp has cancel routine deviceExtension->WriteIrpRef |= CPR_IRPREF_CANCEL; // if there is write timeout, then start the timer if (setTimer) { KeSetTimer(&deviceExtension->WriteTimer, totalTime, &deviceExtension->WriteTimeoutDpc); // record that write irp has timer routine deviceExtension->WriteIrpRef |= CPR_IRPREF_TIMER; } if ((deviceExtension->BufferWrites == FALSE) || (deviceExtension->WriteQueue.CurrentIrp != NULL)) { ASSERT(deviceExtension->WriteLength == 0); // record write length, so our IO knows how much to transmit deviceExtension->WriteLength = writeLength; // Check for negative numbers. WriteLength is unsigned // DAG TODO: What are the consequences for ignoring this issue. // WriteLength should never be negative. if ((int)deviceExtension->WriteLength < 0) { deviceExtension->WriteLength = 0; writeLength = 0; } } //if (deviceExtension->BufferWrites) // CprDumpBufPtrs(deviceExtension, __FUNCTION__" Before"); deviceExtension->PendingWriteCount += writeLength; // DAG-TX-EMPTY //deviceExtension->PendingWriteCountTxEmpty += writeLength; if (irpStack->MajorFunction == IRP_MJ_WRITE) { // this is a normal write request CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, "*** WRITE REQUEST of %d", writeLength); Uart = &deviceExtension->Uart; if ((deviceExtension->BufferWrites) && (deviceExtension->WriteQueue.CurrentIrp == NULL)) { ULONG txBytesAtEnd; ULONG txBytesAtBeg; ULONG bytesToWrite; PUCHAR inBuf; inBuf = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; if (Uart->TxWritePtr >= Uart->TxReadPtr) { // First, write data to end of buffer txBytesAtEnd = (ULONG)(Uart->TxEndBuffer - Uart->TxWritePtr); txBytesAtBeg = (ULONG)(Uart->TxReadPtr - Uart->TxBuffer); // Need to leave room for one wasted byte // because an increment could make the // write == read and that is the empty state if (txBytesAtBeg != 0) txBytesAtBeg--; } else { txBytesAtEnd = (ULONG)(Uart->TxReadPtr - Uart->TxWritePtr) - 1; txBytesAtBeg = 0; } // Check for TxReadPtr at the end of the buffer if (Uart->TxReadPtr == Uart->TxEndBuffer) Uart->TxReadPtr = Uart->TxBuffer; bytesToWrite = writeLength; if (bytesToWrite > txBytesAtEnd) bytesToWrite = txBytesAtEnd; if (bytesToWrite > 0) { // Copy write data to Uart transmit buffer RtlCopyMemory(Uart->TxWritePtr, inBuf, bytesToWrite); Uart->TxWritePtr += bytesToWrite; } // Now see if we need to write the data to the beginning of the buffer if ((bytesToWrite < writeLength) && (txBytesAtBeg != 0)) { // More data to write inBuf = inBuf + bytesToWrite; bytesToWrite = writeLength - bytesToWrite; if (bytesToWrite > txBytesAtBeg) bytesToWrite = txBytesAtBeg; if (bytesToWrite > 0) { // If we were at the end of the buffer, // wrap around. if (Uart->TxWritePtr == Uart->TxEndBuffer) Uart->TxWritePtr = Uart->TxBuffer; // Copy Tsdu data to Uart receive buffer RtlCopyMemory(Uart->TxWritePtr, inBuf, bytesToWrite); Uart->TxWritePtr += bytesToWrite; } } //CprDumpBufPtrs(deviceExtension, __FUNCTION__" After"); // We have the data, we can finish this IRP Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = writeLength; IoSetCancelRoutine(Irp, NULL); IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { deviceExtension->WriteBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; } } else { // this is xoff counter requests deviceExtension->WriteBuffer = ((PUCHAR)Irp->AssociatedIrp.SystemBuffer) + FIELD_OFFSET(SERIAL_XOFF_COUNTER, XoffChar); } // record that IO processing knows about current write deviceExtension->WriteIrpRef |= CPR_IRPREF_IO; // check if Transmit engine in an idle state. // If it is then restart it if (deviceExtension->TxIdle && !deviceExtension->TxImmediate) { if (deviceExtension->BufferWrites && (deviceExtension->WriteQueue.CurrentIrp == NULL) && (deviceExtension->Uart.TxSendEndPtr > deviceExtension->Uart.TxSendBegPtr)) { // Already in progress, do not write. } else { deviceExtension->TxWriteInProgress = TRUE; CprUartStartWrite(&deviceExtension->Uart); } } // if we are doing transmit toggling, make sure RTS line is up #ifdef REMOVE_FOR_TESTING_WITH_GRID_CONNECT // GridConnect had FlowReplace = 0xC0 if ((deviceExtension->SerialHandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { CprSerialSetRTS(deviceExtension); } #endif // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); } CprDebugPrint5(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, __FUNCTION__ " TxBuffer %p, TxReadPtr %p, TxWritePtr %p, TxSendBegPtr %p, TxSendEndPtr %p", Uart->TxBuffer, Uart->TxReadPtr, Uart->TxWritePtr, Uart->TxSendBegPtr, Uart->TxSendEndPtr); CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"--. IRP %p", Irp); return; } VOID WriteQueueStatusChange( IN PDEVICE_OBJECT DeviceObject, ULONG CurrentIrpStatus ) { PCPR_DEVICE_EXTENSION deviceExtension; // Get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(deviceExtension); if (deviceExtension) deviceExtension->WriteIrpStatus = CurrentIrpStatus; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialStopCurrentWrite // Stops outstanding write io request // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // VOID CprSerialStopCurrentWrite( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { PIRP irp; PIO_STACK_LOCATION irpStack; CprDebugPrint(DeviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"++"); // check if write io is still in progress if (DeviceExtension->WriteLength > 0) { irp = DeviceExtension->WriteQueue.CurrentIrp; CprDebugPrint1(DeviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, "irp %p", irp); if (irp != NULL) { irpStack = IoGetCurrentIrpStackLocation(irp); // if the irp is write request update how much // data was transmitted. if (irpStack->MajorFunction == IRP_MJ_WRITE) { CprCancelTdiIrps(DeviceExtension); //BOOLEAN cancelResult = 0; //PCPR_TDI_SOCKET socket = DeviceExtension->socket; // PIRP tdiIrp = NULL; ////tdiIrp = (PIRP)InterlockedExchangePointer((PVOID*)irp->Tail.Overlay.DriverContext, NULL); ////tdiIrp = (PIRP)TDI_IRP_REFERENCE(irp); ////TDI_IRP_REFERENCE(irp) = NULL; //if (socket && socket->WriteTdiIrp) //{ // tdiIrp = socket->WriteTdiIrp; // socket->WriteTdiIrp = NULL; //} // // CprDebugPrint1(DeviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, // "CprSerialStopCurrentWrite: tdiIrp %p", tdiIrp); // if ((g_DebugArea & (DBG_WRITE | DBG_IO)) && (DBG_INFO <= g_DebugLevel)) //{ // CprDumpBuffer("CprSerialStopCurrentWrite", // DeviceExtension->WriteBuffer, DeviceExtension->WriteLength); //} //if (tdiIrp != NULL) //{ // ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // CprReleaseSerialSpinLockFromDpcLevel(DeviceExtension); // cancelResult = IoCancelIrp(tdiIrp); // CprAcquireSerialSpinLockAtDpcLevel(DeviceExtension); // DeviceExtension->Cancelled = TRUE; // if (!cancelResult) // { // // Need to wait on an event from the completion routine // // How do I see if the connection is broken // if (tdiIrp->MdlAddress != NULL) // { // IoFreeMdl(tdiIrp->MdlAddress); // tdiIrp->MdlAddress = NULL; // } // CprTdiFreeIrp(socket, tdiIrp); // CprTdiRelease(socket); // } //} // CprDebugPrint3(DeviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, // "userIrp %p, tdiIrp %p cancelResult %d", irp, tdiIrp, cancelResult); irp->IoStatus.Information = 0; } else { // this is xoff counter irp irp->IoStatus.Information = 0; } } else { CprDebugPrint(DeviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, __FUNCTION__" ================ irp is NULL"); } if ((DeviceExtension->BufferWrites) && (DeviceExtension->WriteQueue.CurrentIrp == NULL)) { if (DeviceExtension->Uart.TxSendEndPtr == DeviceExtension->Uart.TxEndBuffer) DeviceExtension->Uart.TxSendEndPtr = DeviceExtension->Uart.TxBuffer; DeviceExtension->Uart.TxReadPtr = DeviceExtension->Uart.TxSendEndPtr; DeviceExtension->Uart.TxSendBegPtr = DeviceExtension->Uart.TxSendEndPtr; DeviceExtension->PendingWriteCount -= DeviceExtension->Uart.TxTdiWriteLength; //DAG-TX-EMPTY //DeviceExtension->PendingWriteCountTxEmpty -= DeviceExtension->Uart.TxTdiWriteLength; DeviceExtension->Uart.TxRFC2217Additional = 0; } else { // IO will not touch current write irp if WriteLength is 0 DeviceExtension->PendingWriteCount -= DeviceExtension->WriteLength; // DAG-TX-EMPTY //DeviceExtension->PendingWriteCountTxEmpty -= DeviceExtension->WriteLength; //DeviceExtension->WriteCompleteCount -= DeviceExtension->WriteLength; DeviceExtension->WriteLength = 0; //DeviceExtension->PendingWriteCount = 0; //DeviceExtension->WriteCompleteCount = 0; } DeviceExtension->WriteIrpRef &= ~CPR_IRPREF_IO; } CprDebugPrint(DeviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialCancelCurrentWrite // Cancel routine for outstanding write io request // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // The WriteQueue IRP to cancel // // Return Value: // None // VOID CprSerialCancelCurrentWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCPR_DEVICE_EXTENSION deviceExtension; KIRQL oldIrql; BOOLEAN startNext; BOOLEAN atDispatch = FALSE; // get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)DeviceObject->DeviceExtension; CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_WARN, __FUNCTION__"++ IRP %p", Irp); if (Irp->IoStatus.Status == STATUS_CANCELLED) { IoReleaseCancelSpinLock(Irp->CancelIrql); return; } // drop global cancel lock IoReleaseCancelSpinLock(Irp->CancelIrql); IoSetCancelRoutine(Irp, NULL); if (KeGetCurrentIrql() == DISPATCH_LEVEL) { atDispatch = TRUE; CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); } else { atDispatch = FALSE; CprAcquireSerialSpinLock(deviceExtension, &oldIrql); } // mark that cancel routine has run deviceExtension->WriteIrpRef &= ~CPR_IRPREF_CANCEL; // check if we set a timer if (deviceExtension->WriteIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->WriteTimer)) { // timer is canceled deviceExtension->WriteIrpRef &= ~CPR_IRPREF_TIMER; } } // try to stop write io CprSerialStopCurrentWrite(deviceExtension); //if ((deviceExtension->UseRFC2217 == FALSE) && // (deviceExtension->PendingWriteCount == 0)) //{ CprCheckForEmptyTransmitEvent(deviceExtension); //} if (atDispatch == TRUE) { CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); } else { CprReleaseSerialSpinLock(deviceExtension, oldIrql); } CprDebugPrint3(deviceExtension, DBG_READ | DBG_IO, DBG_TRACE, __FUNCTION__" Releasing Write App Irp 0x%08X Buf 0x%08X, Info %d", Irp, Irp->AssociatedIrp.SystemBuffer, Irp->IoStatus.Information); #ifdef USE_WAIT_ON_WRITES if (deviceExtension->WaitOnWrite) { if (Irp->UserEvent) { KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE); } } else #endif { Irp->IoStatus.Status = STATUS_CANCELLED; IoSetCancelRoutine(Irp, NULL); IoCompleteRequest(Irp, IO_NO_INCREMENT); } CprDebugPrint(deviceExtension, DBG_WRITE | DBG_IO, DBG_WARN, __FUNCTION__"--"); return; } VOID CprSerialWriteTimeout( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_DEVICE_EXTENSION deviceExtension; PIRP irp; BOOLEAN startNext; KIRQL oldIrql; // get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)Context; CprDebugPrint(deviceExtension, DBG_IO, DBG_WARN, __FUNCTION__"++"); //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); CprAcquireSerialSpinLock(deviceExtension, &oldIrql); //deviceExtension->Connected = FALSE; // record the fact that timeout callback has run deviceExtension->WriteIrpRef &= ~CPR_IRPREF_TIMER; irp = deviceExtension->WriteQueue.CurrentIrp; CprDebugPrint1(deviceExtension, DBG_IO, DBG_INFO, "IRP %p", irp); if (irp != NULL) { // try to remove cancel routine if (deviceExtension->WriteIrpRef & CPR_IRPREF_CANCEL) { if (IoSetCancelRoutine(irp, NULL)) { deviceExtension->WriteIrpRef &= ~CPR_IRPREF_CANCEL; } } // try to stop write io CprSerialStopCurrentWrite(deviceExtension); deviceExtension->WriteQueue.CurrentIrp = NULL; CprReleaseSerialSpinLock(deviceExtension, oldIrql); // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); #ifdef USE_WAIT_ON_WRITES if (deviceExtension->WaitOnWrite) { if (irp->UserEvent) { KeSetEvent(irp->UserEvent, IO_NO_INCREMENT, FALSE); } } else #endif { irp->IoStatus.Status = STATUS_TIMEOUT; IoSetCancelRoutine(irp, NULL); IoCompleteRequest(irp, IO_NO_INCREMENT); } } else { CprDebugPrint(deviceExtension, DBG_IO, DBG_INFO, __FUNCTION__" ================ irp is NULL"); if (deviceExtension->WriteIrpRef & CPR_IRPREF_CANCEL) { deviceExtension->WriteIrpRef &= ~CPR_IRPREF_CANCEL; } // check if we are done with this write irp if (deviceExtension->WriteIrpRef == 0) { startNext = TRUE; } else { startNext = FALSE; } // Do we check for WriteIrpRef == 0 instead?? // //if ((deviceExtension->PendingWriteCount == 0) && // (((deviceExtension->UseRFC2217 == TRUE) && (deviceExtension->TransmitEmpty == TX_EMPTY_SERVER)) || // (deviceExtension->UseRFC2217 == FALSE))) //{ CprCheckForEmptyTransmitEvent(deviceExtension); //} //CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (startNext) { CprStartNext(&deviceExtension->WriteQueue); } } CprDebugPrint(deviceExtension, DBG_IO, DBG_WARN, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialWriteComplete // Write completion request callback // // Arguments: // IN Dpc // WriteCompleteDpc // // IN Context // our device extension // // IN Unused1 // unused // // IN Unused2 // unused // // Return Value: // None // VOID CprSerialWriteComplete( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_DEVICE_EXTENSION deviceExtension; PIRP irp; PIO_STACK_LOCATION irpStack = NULL; BOOLEAN startNext = FALSE; PSERIAL_XOFF_COUNTER xoffCount; LARGE_INTEGER timeout; BOOLEAN releaseIrp = FALSE; KIRQL oldIrql; // get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)Context; // ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); CprAcquireSerialSpinLock(deviceExtension, &oldIrql); // record the fact that io is completed deviceExtension->WriteIrpRef &= ~CPR_IRPREF_IO; irp = deviceExtension->WriteQueue.CurrentIrp; CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"++ IRP %p", irp); if (irp != NULL) { irpStack = IoGetCurrentIrpStackLocation(irp); if (irpStack == NULL) { CprDebugPrint2(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, __FUNCTION__" DevExt %p PendingWriteCount %d", deviceExtension, deviceExtension->PendingWriteCount); } else { CprDebugPrint3(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, __FUNCTION__" DevExt %p Len %d PendingWriteCount %d", deviceExtension, irpStack->Parameters.Write.Length, deviceExtension->PendingWriteCount); } CprDebugPrint1(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, "IRP->STATUS %x", irp->IoStatus.Status); // try to remove cancel routine if (deviceExtension->WriteIrpRef & CPR_IRPREF_CANCEL) { if (IoSetCancelRoutine(irp, NULL)) { releaseIrp = TRUE; deviceExtension->WriteIrpRef &= ~CPR_IRPREF_CANCEL; } } // check if we set a timer if (deviceExtension->WriteIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->WriteTimer)) { // timer is canceled deviceExtension->WriteIrpRef &= ~CPR_IRPREF_TIMER; } } // check if we are done with this write irp if (deviceExtension->WriteIrpRef == 0) { startNext = TRUE; if (irpStack != NULL) { if (irpStack->MajorFunction == IRP_MJ_WRITE) { // DAG: 10/05/06 // if doing RFC2217, we don't want to update // PendingWriteCount until we get a transmit empty. // We need to check for Transmit empty elsewhere // because we want to confirm the transmit empty // by querying the device server. // Note: When a transmit empty comes in request. // Also, if a COMMSTATUS request comes in // test to see what state we are in and // launch some processing in the background // that can, hopefully, update some fields // before the next COMSTATUS IOCTL. // if ((deviceExtension->UseRFC2217 == FALSE) || (deviceExtension->TransmitEmpty == TRANSMIT_EMPTY_CPR)) { deviceExtension->PendingWriteCount -= irpStack->Parameters.Write.Length; } } else if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { xoffCount = (PSERIAL_XOFF_COUNTER)irp->AssociatedIrp.SystemBuffer; --deviceExtension->PendingWriteCount; deviceExtension->XoffIrpRef = 0; IoSetCancelRoutine(irp, CprSerialCancelCurrentXoff); if (irp->Cancel && IoSetCancelRoutine(irp, NULL)) { irp->IoStatus.Status = STATUS_CANCELLED; } else { deviceExtension->XoffIrpRef |= CPR_IRPREF_CANCEL; deviceExtension->XoffIrp = irp; deviceExtension->XoffCount = xoffCount->Counter; deviceExtension->XoffIrpRef |= CPR_IRPREF_IO; if (xoffCount->Timeout != 0) { timeout.QuadPart = UInt32x32To64(-1000, xoffCount->Timeout); KeSetTimer(&deviceExtension->XoffTimer, timeout, &deviceExtension->XoffTimeoutDpc); deviceExtension->XoffIrpRef |= CPR_IRPREF_TIMER; } irp = NULL; } } } } else { startNext = FALSE; } } else if ((deviceExtension->BufferWrites) && (deviceExtension->WriteQueue.CurrentIrp == NULL)) { startNext = TRUE; // check if we set a timer if (deviceExtension->WriteIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->WriteTimer)) { // timer is canceled deviceExtension->WriteIrpRef &= ~CPR_IRPREF_TIMER; } } if ((deviceExtension->UseRFC2217 == FALSE) || (deviceExtension->TransmitEmpty == TRANSMIT_EMPTY_CPR)) { deviceExtension->PendingWriteCount -= deviceExtension->Uart.TxSerialWriteLength; } deviceExtension->Uart.TxSerialWriteLength = 0; } else { CprDebugPrint(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, __FUNCTION__" ================ irp is NULL"); startNext = TRUE; if (deviceExtension->WriteIrpRef & CPR_IRPREF_CANCEL) { deviceExtension->WriteIrpRef &= ~CPR_IRPREF_CANCEL; } // check if we set a timer if (deviceExtension->WriteIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->WriteTimer)) { // timer is canceled deviceExtension->WriteIrpRef &= ~CPR_IRPREF_TIMER; } } deviceExtension->PendingWriteCount -= deviceExtension->WriteLength; // May be a problem setting to 0, need more testing. //deviceExtension->PendingWriteCount = 0; } // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (startNext) { if (irp && releaseIrp) { if (irpStack == NULL) { CprDebugPrint3(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, __FUNCTION__" Releasing Write App Irp 0x%08X Buf 0x%08x Info %d", irp, irp->AssociatedIrp.SystemBuffer, irp->IoStatus.Information); } else { CprDebugPrint4(deviceExtension, DBG_WRITE | DBG_IO, DBG_INFO, __FUNCTION__" Releasing Write App Irp 0x%08X Buf 0x%08x Len %d Info %d", irp, irp->AssociatedIrp.SystemBuffer, irpStack->Parameters.Write.Length, irp->IoStatus.Information); } #ifdef USE_WAIT_ON_WRITES if (deviceExtension->WaitOnWrite) { if (((deviceExtension->UseRFC2217 == FALSE) || (deviceExtension->TransmitEmpty == TRANSMIT_EMPTY_CPR)) && irp->UserEvent) { KeSetEvent(irp->UserEvent, IO_NO_INCREMENT, FALSE); } } else #endif if ((deviceExtension->BufferWrites == FALSE) || (deviceExtension->WriteQueue.CurrentIrp != NULL)) { irp->IoStatus.Status = STATUS_SUCCESS; IoSetCancelRoutine(irp, NULL); IoCompleteRequest(irp, IO_NO_INCREMENT); } } #ifdef USE_WAIT_ON_WRITES if (!deviceExtension->WaitOnWrite) #endif CprStartNext(&deviceExtension->WriteQueue); } CprAcquireSerialSpinLock(deviceExtension, &oldIrql); CprCheckForEmptyTransmitEvent(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); CprDebugPrint2(deviceExtension, DBG_WRITE | DBG_IO, DBG_TRACE, __FUNCTION__"-- DevExt %p PendingWriteCount %d", deviceExtension, deviceExtension->PendingWriteCount); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialStopXoff // Stops outstanding xoff counter request // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // VOID CprSerialStopXoff( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { CprDebugPrint(DeviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"++"); if (DeviceExtension->XoffCount != 0) { DeviceExtension->XoffCount = 0; DeviceExtension->XoffIrpRef &= ~CPR_IRPREF_IO; } CprDebugPrint(DeviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialCancelCurrentXoff // Cancel routine for outstanding xoff count request // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // The IOCTL_SERIAL_XOFF_COUNTER IRP to cancel // // Return Value: // None // VOID CprSerialCancelCurrentXoff( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCPR_DEVICE_EXTENSION deviceExtension; KIRQL oldIrql; BOOLEAN complete; // get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)DeviceObject->DeviceExtension; CprDebugPrint(deviceExtension, DBG_IO, DBG_WARN, __FUNCTION__"++"); // drop global cancel lock //oldIrql = Irp->CancelIrql; IoReleaseCancelSpinLock(Irp->CancelIrql); CprAcquireSerialSpinLock(deviceExtension, &oldIrql); // mark that cancel routine has run deviceExtension->XoffIrpRef &= ~CPR_IRPREF_CANCEL; // check if we set a timer if (deviceExtension->XoffIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->XoffTimer)) { // timer is canceled deviceExtension->XoffIrpRef &= ~CPR_IRPREF_TIMER; } } // try to stop xoff counter CprSerialStopXoff(deviceExtension); // check we done with this xoff counter irp if (deviceExtension->XoffIrpRef == 0) { complete = TRUE; deviceExtension->XoffIrp = NULL; } else { complete = FALSE; } CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (complete) { Irp->IoStatus.Status = STATUS_CANCELLED; IoSetCancelRoutine(Irp, NULL); IoCompleteRequest(Irp, IO_NO_INCREMENT); } CprDebugPrint(deviceExtension, DBG_IO, DBG_WARN, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialXoffTimeout // xoff counter timeout callback // // Arguments: // IN Dpc // XoffTimeoutDpc // // IN Context // our device extension // // IN Unused1 // unused // // IN Unused2 // unused // // Return Value: // None // VOID CprSerialXoffTimeout( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_DEVICE_EXTENSION deviceExtension; PIRP irp; BOOLEAN complete; KIRQL oldIrql; // get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)Context; CprDebugPrint(deviceExtension, DBG_IO, DBG_WARN, __FUNCTION__"++"); //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); CprAcquireSerialSpinLock(deviceExtension, &oldIrql); // record the fact that timeout callback has run deviceExtension->XoffIrpRef &= ~CPR_IRPREF_TIMER; irp = deviceExtension->XoffIrp; // try to remove cancel routine if (deviceExtension->XoffIrpRef & CPR_IRPREF_CANCEL) { if (IoSetCancelRoutine(irp, NULL)) { deviceExtension->XoffIrpRef &= ~CPR_IRPREF_CANCEL; } } // try to stop xoff counter CprSerialStopXoff(deviceExtension); // check we done with this xoff counter irp if (deviceExtension->XoffIrpRef == 0) { complete = TRUE; deviceExtension->XoffIrp = NULL; } else { complete = FALSE; } // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (complete) { irp->IoStatus.Status = STATUS_SERIAL_COUNTER_TIMEOUT; IoSetCancelRoutine(irp, NULL); IoCompleteRequest(irp, IO_NO_INCREMENT); } CprDebugPrint(deviceExtension, DBG_IO, DBG_WARN, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialXoffComplete // xoff counter completion request callback // // Arguments: // IN Dpc // XoffCompleteDpc // // IN Context // our device extension // // IN Unused1 // unused // // IN Unused2 // unused // // Return Value: // None // VOID CprSerialXoffComplete( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_DEVICE_EXTENSION deviceExtension; PIRP irp; BOOLEAN complete; KIRQL oldIrql; // get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)Context; CprDebugPrint(deviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"++"); //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); CprAcquireSerialSpinLock(deviceExtension, &oldIrql); // record the fact that io is completed deviceExtension->XoffIrpRef &= ~CPR_IRPREF_IO; irp = deviceExtension->XoffIrp; // try to remove cancel routine if (deviceExtension->XoffIrpRef & CPR_IRPREF_CANCEL) { if (IoSetCancelRoutine(irp, NULL)) { deviceExtension->XoffIrpRef &= ~CPR_IRPREF_CANCEL; } } // check if we set a timer if (deviceExtension->XoffIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->XoffTimer)) { // timer is canceled deviceExtension->XoffIrpRef &= ~CPR_IRPREF_TIMER; } } // check we done with this xoff counter irp if (deviceExtension->XoffIrpRef == 0) { complete = TRUE; deviceExtension->XoffIrp = NULL; } else { complete = FALSE; } // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (complete) { irp->IoStatus.Status = STATUS_SUCCESS; IoSetCancelRoutine(irp, NULL); IoCompleteRequest(irp, IO_NO_INCREMENT); } CprDebugPrint(deviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFlushBuffersDispatch // Handles IRP_MJ_FLUSH_BUFFERS requests // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // The IRP_MJ_FLUSH_BUFFERS IRP to handle // // Return Value: // NT status code // NTSTATUS CprFlushBuffersDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCPR_DEVICE_EXTENSION deviceExtension; NTSTATUS status; POWER_STATE powerState; __try { deviceExtension = (PCPR_DEVICE_EXTENSION)DeviceObject->DeviceExtension; CprDebugPrint1(deviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"++. IRP %p", Irp); // check the device error condition status = CprCheckForError(deviceExtension, Irp); if (!NT_SUCCESS(status)) { return status; } // Cancel any previous timers KeCancelTimer(&deviceExtension->FlushBuffersTimer); // Could become false if in listen mode and // there is a pending write that will never go out // because there is no connection and therefore // IsDeviceEnabled is FALSE. if (deviceExtension->IsDeviceEnabled == FALSE) { // Need to set a timeout routine // If it times out, then remove the current IRP // in the write queue, clean out the queue // then complete the IRP. KeSetTimer( &deviceExtension->FlushBuffersTimer, deviceExtension->FlushBuffersTimeout, &deviceExtension->FlushBuffersTimeoutDpc); } status = CprQueueIrp(&deviceExtension->WriteQueue, Irp, TRUE); CprDebugPrint2(deviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); } __except( EXCEPTION_EXECUTE_HANDLER ) { status = STATUS_INTERNAL_ERROR; } return status; } VOID CprFlushBuffersTimeout( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_DEVICE_EXTENSION deviceExtension; PIRP xoffIrp; // get our device extension deviceExtension = (PCPR_DEVICE_EXTENSION)Context; CprDebugPrint(deviceExtension, DBG_IO, DBG_VERB, __FUNCTION__"++"); CprQueueCancelAll(&deviceExtension->WriteQueue); CprDebugPrint(deviceExtension, DBG_IO, DBG_VERB, __FUNCTION__"--"); return; }