// immediat.c // // // Requires DDK Only // File created on 2/2/2005 // #include "pch.h" #ifdef CPR_WMI_TRACE #include "immediat.tmh" #endif /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialStartImmediate // starts IOCTL_SERIAL_IMMEDIATE_CHAR request // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // Status // NTSTATUS CprSerialStartImmediate( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { NTSTATUS status; PIRP irp; KIRQL oldIrql; BOOLEAN setTimer; SERIAL_TIMEOUTS timeouts; LARGE_INTEGER totalTime; CprDebugPrint(DeviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"++"); // get timeout setting CprAcquireSerialSpinLock(DeviceExtension, &oldIrql); timeouts = DeviceExtension->Timeouts; CprReleaseSerialSpinLock(DeviceExtension, oldIrql); irp = DeviceExtension->ImmediateCharIrp; if (timeouts.WriteTotalTimeoutConstant || timeouts.WriteTotalTimeoutMultiplier) { setTimer = TRUE; // we have only one character totalTime.QuadPart = (1*timeouts.WriteTotalTimeoutMultiplier + timeouts.WriteTotalTimeoutConstant)*(10000); totalTime = RtlLargeIntegerNegate(totalTime); } else { setTimer = FALSE; } DeviceExtension->ImmediateCharIrpRef = 0; CprAcquireSerialSpinLock(DeviceExtension, &oldIrql); IoSetCancelRoutine(irp, CprSerialCancelImmediate); if (irp->Cancel && IoSetCancelRoutine(irp, NULL)) { DeviceExtension->ImmediateCharIrp = NULL; --DeviceExtension->PendingWriteCount; CprReleaseSerialSpinLock(DeviceExtension, oldIrql); status = STATUS_CANCELLED; irp->IoStatus.Status = status; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); } else { DeviceExtension->ImmediateCharIrpRef |= CPR_IRPREF_CANCEL; if (setTimer) { KeSetTimer(&DeviceExtension->ImmediateCharTimer, totalTime, &DeviceExtension->ImmediateCharTimeoutDpc); DeviceExtension->ImmediateCharIrpRef |= CPR_IRPREF_TIMER; } DeviceExtension->TxImmediate = TRUE; DeviceExtension->ImmediateChar = *(PUCHAR)irp->AssociatedIrp.SystemBuffer; DeviceExtension->ImmediateCharIrpRef |= CPR_IRPREF_IO; // we are going to return STATUS_PENDING, so mark irp as pending IoMarkIrpPending(irp); status = STATUS_PENDING; // if transmit engine is in idle state, restart it if ((DeviceExtension->WriteLength == 0) && DeviceExtension->TxIdle) { CprUartStartWrite(&DeviceExtension->Uart); } CprReleaseSerialSpinLock(DeviceExtension, oldIrql); } CprDebugPrint(DeviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"--"); return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialStopImmediateChar // Stops outstanding immediate char request // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // VOID CprSerialStopImmediateChar( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { CprDebugPrint(DeviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"++"); // if immediate character is not transmitted yet, // cancel it if (DeviceExtension->TxImmediate) { DeviceExtension->TxImmediate = FALSE; DeviceExtension->ImmediateCharIrpRef &= ~CPR_IRPREF_IO; } CprDebugPrint(DeviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialCancelImmediate // Cancel routine for outstanding immediate char request // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // The IOCTL_SERIAL_IMMEDIATE_CHAR IRP to cancel // // Return Value: // None // VOID CprSerialCancelImmediate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCPR_DEVICE_EXTENSION deviceExtension; KIRQL oldIrql; BOOLEAN complete; BOOLEAN atDispatch = FALSE; // 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(oldIrql); if (KeGetCurrentIrql() == DISPATCH_LEVEL) { atDispatch = TRUE; CprAcquireSerialSpinLockAtDpcLevel(deviceExtension); } else { atDispatch = FALSE; CprAcquireSerialSpinLock(deviceExtension, &oldIrql); } // mark that cancel routine has run deviceExtension->ImmediateCharIrpRef &= ~CPR_IRPREF_CANCEL; // check if we set a timer if (deviceExtension->ImmediateCharIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->ImmediateCharTimer)) { // timer is canceled deviceExtension->ImmediateCharIrpRef &= ~CPR_IRPREF_TIMER; } } // try to stop immediate char io CprSerialStopImmediateChar(deviceExtension); // check if we are done with this immediate char irp if (deviceExtension->ImmediateCharIrpRef == 0) { complete = TRUE; deviceExtension->ImmediateCharIrp = NULL; --deviceExtension->PendingWriteCount; CprCheckForEmptyTransmitEvent(deviceExtension); } else { complete = FALSE; } if (atDispatch == TRUE) { CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); } else { CprReleaseSerialSpinLock(deviceExtension, oldIrql); } if (complete) { Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); } CprDebugPrint(deviceExtension, DBG_IO, DBG_WARN, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialImmediateCharTimeout // Immediate char request timeout callback // // Arguments: // IN Dpc // ImmediateCharTimeoutDpc // // IN Context // our device extension // // IN Unused1 // unused // // IN Unused2 // unused // // Return Value: // None // VOID CprSerialImmediateCharTimeout( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_DEVICE_EXTENSION deviceExtension; KIRQL oldIrql; PIRP irp; BOOLEAN complete; // 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->ImmediateCharIrpRef &= ~CPR_IRPREF_TIMER; irp = deviceExtension->ImmediateCharIrp; // try to remove cancel routine if (deviceExtension->ImmediateCharIrpRef & CPR_IRPREF_CANCEL) { if (IoSetCancelRoutine(irp, NULL)) { deviceExtension->ImmediateCharIrpRef &= ~CPR_IRPREF_CANCEL; } } // try to stop immediate char io CprSerialStopImmediateChar(deviceExtension); // check if we are done with this immediate char irp if (deviceExtension->ImmediateCharIrpRef == 0) { complete = TRUE; deviceExtension->ImmediateCharIrp = NULL; --deviceExtension->PendingWriteCount; CprCheckForEmptyTransmitEvent(deviceExtension); } else { complete = FALSE; } // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (complete) { irp->IoStatus.Status = STATUS_TIMEOUT; IoCompleteRequest(irp, IO_NO_INCREMENT); } CprDebugPrint(deviceExtension,DBG_IO, DBG_WARN, __FUNCTION__"--"); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialImmediateCharComplete // Immediate char completion request callback // // Arguments: // IN Dpc // ImmediateCharCompleteDpc // // IN Context // our device extension // // IN Unused1 // unused // // IN Unused2 // unused // // Return Value: // None // VOID CprSerialImmediateCharComplete( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_DEVICE_EXTENSION deviceExtension; KIRQL oldIrql; PIRP irp; BOOLEAN complete; // 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->ImmediateCharIrpRef &= ~CPR_IRPREF_IO; irp = deviceExtension->ImmediateCharIrp; // try to remove cancel routine if (deviceExtension->ImmediateCharIrpRef & CPR_IRPREF_CANCEL) { if (IoSetCancelRoutine(irp, NULL)) { deviceExtension->ImmediateCharIrpRef &= ~CPR_IRPREF_CANCEL; } } // check if we set a timer if (deviceExtension->ImmediateCharIrpRef & CPR_IRPREF_TIMER) { if (KeCancelTimer(&deviceExtension->ImmediateCharTimer)) { // timer is canceled deviceExtension->ImmediateCharIrpRef &= ~CPR_IRPREF_TIMER; } } // check if we are done with this immediate char irp if (deviceExtension->ImmediateCharIrpRef == 0) { complete = TRUE; deviceExtension->ImmediateCharIrp = NULL; --deviceExtension->PendingWriteCount; CprCheckForEmptyTransmitEvent(deviceExtension); } else { complete = FALSE; } // CprReleaseSerialSpinLockFromDpcLevel(deviceExtension); CprReleaseSerialSpinLock(deviceExtension, oldIrql); if (complete) { irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(irp, IO_NO_INCREMENT); } CprDebugPrint(deviceExtension, DBG_IO, DBG_TRACE, __FUNCTION__"--"); return; }