Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1403 lines
36 KiB

/*--------------------------------------------------------------------------
*
* Copyright (C) Cyclades Corporation, 1997-2001.
* All rights reserved.
*
* Cyclades-Z Port Driver
*
* This file: cyzwrite.c
*
* Description: This module contains the code related to write
* operations in the Cyclades-Z Port driver.
*
* Notes: This code supports Windows 2000 and Windows XP,
* x86 and IA64 processors.
*
* Complies with Cyclades SW Coding Standard rev 1.3.
*
*--------------------------------------------------------------------------
*/
/*-------------------------------------------------------------------------
*
* Change History
*
*--------------------------------------------------------------------------
*
*
*--------------------------------------------------------------------------
*/
#include "precomp.h"
BOOLEAN
CyzGiveWriteToIsr(
IN PVOID Context
);
VOID
CyzCancelCurrentWrite(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
);
BOOLEAN
CyzGrabWriteFromIsr(
IN PVOID Context
);
BOOLEAN
CyzGrabXoffFromIsr(
IN PVOID Context
);
VOID
CyzCancelCurrentXoff(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
);
BOOLEAN
CyzGiveXoffToIsr(
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGESER,CyzProcessEmptyTransmit)
#pragma alloc_text(PAGESER,CyzWrite)
#pragma alloc_text(PAGESER,CyzStartWrite)
#pragma alloc_text(PAGESER,CyzGetNextWrite)
#pragma alloc_text(PAGESER,CyzGiveWriteToIsr)
#pragma alloc_text(PAGESER,CyzCancelCurrentWrite)
#pragma alloc_text(PAGESER,CyzGrabWriteFromIsr)
#pragma alloc_text(PAGESER,CyzGrabXoffFromIsr)
#pragma alloc_text(PAGESER,CyzCancelCurrentXoff)
#pragma alloc_text(PAGESER,CyzGiveXoffToIsr)
#endif
NTSTATUS
CyzWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*--------------------------------------------------------------------------
CyzWrite()
Routine Description: This is the dispatch routine for write. It
validates the parameters for the write request and if all is ok
then it places the request on the work queue.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value: If the io is zero length then it will return STATUS_SUCCESS,
otherwise this routine will return STATUS_PENDING.
--------------------------------------------------------------------------*/
{
PCYZ_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
NTSTATUS status;
CYZ_LOCKED_PAGED_CODE();
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzWrite(%X, %X)\n", DeviceObject,
Irp);
LOGENTRY(LOG_MISC, ZSIG_WRITE,
Extension->PortIndex+1,
IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length,
0);
if ((status = CyzIRPPrologue(Irp, Extension)) != STATUS_SUCCESS) {
if (status != STATUS_PENDING) {
CyzCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
}
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzWrite (1) %X\n", status);
return status;
}
CyzDbgPrintEx(CYZIRPPATH, "Dispatch entry for: %x\n", Irp);
if (CyzCompleteIfError(DeviceObject,Irp) != STATUS_SUCCESS) {
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzWrite (2) %X\n",
STATUS_CANCELLED);
return STATUS_CANCELLED;
}
Irp->IoStatus.Information = 0L;
//
// Quick check for a zero length write. If it is zero length
// then we are already done!
//
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length) {
//
// Well it looks like we actually have to do some
// work. Put the write on the queue so that we can
// process it when our previous writes are done.
//
status = CyzStartOrQueue(Extension, Irp, &Extension->WriteQueue,
&Extension->CurrentWriteIrp,
CyzStartWrite);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzWrite (3) %X\n", status);
return status;
} else {
Irp->IoStatus.Status = STATUS_SUCCESS;
CyzCompleteRequest(Extension, Irp, 0);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzWrite (4) %X\n",
STATUS_SUCCESS);
return STATUS_SUCCESS;
}
}
NTSTATUS
CyzStartWrite(
IN PCYZ_DEVICE_EXTENSION Extension
)
/*--------------------------------------------------------------------------
CyzStartWrite()
Routine Description: This routine is used to start off any write.
It initializes the Iostatus fields of the irp. It will set up any
timers that are used to control the write.
Arguments:
Extension - Points to the serial device extension
Return Value: This routine will return STATUS_PENDING for all writes
other than those that we find are cancelled.
--------------------------------------------------------------------------*/
{
PIRP NewIrp;
KIRQL OldIrql;
#ifdef POLL
KIRQL pollIrql;
#endif
LARGE_INTEGER TotalTime;
BOOLEAN UseATimer;
SERIAL_TIMEOUTS Timeouts;
BOOLEAN SetFirstStatus = FALSE;
NTSTATUS FirstStatus;
CYZ_LOCKED_PAGED_CODE();
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzStartWrite(%X)\n", Extension);
// LOGENTRY(LOG_MISC, ZSIG_START_WRITE,
// Extension->PortIndex+1,
// 0,
// 0);
do {
// If there is an xoff counter then complete it.
IoAcquireCancelSpinLock(&OldIrql);
// We see if there is a actually an Xoff counter irp.
//
// If there is, we put the write irp back on the head
// of the write list. We then kill the xoff counter.
// The xoff counter killing code will actually make the
// xoff counter back into the current write irp, and
// in the course of completing the xoff (which is now
// the current write) we will restart this irp.
if (Extension->CurrentXoffIrp) {
if (SERIAL_REFERENCE_COUNT(Extension->CurrentXoffIrp)) {
// The reference count is non-zero. This implies that
// the xoff irp has not made it through the completion
// path yet. We will increment the reference count
// and attempt to complete it ourseleves.
SERIAL_SET_REFERENCE(Extension->CurrentXoffIrp,
SERIAL_REF_XOFF_REF);
Extension->CurrentXoffIrp->IoStatus.Information = 0; // Added in build 2128
// The following call will actually release the
// cancel spin lock.
CyzTryToCompleteCurrent(
Extension,
CyzGrabXoffFromIsr,
OldIrql,
STATUS_SERIAL_MORE_WRITES,
&Extension->CurrentXoffIrp,
NULL,
NULL,
&Extension->XoffCountTimer,
NULL,
NULL,
SERIAL_REF_XOFF_REF
);
} else {
// The irp is well on its way to being finished.
// We can let the regular completion code do the
// work. Just release the spin lock.
IoReleaseCancelSpinLock(OldIrql);
}
} else {
IoReleaseCancelSpinLock(OldIrql);
}
UseATimer = FALSE;
// Calculate the timeout value needed for the
// request. Note that the values stored in the
// timeout record are in milliseconds. Note that
// if the timeout values are zero then we won't start
// the timer.
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
Timeouts = Extension->Timeouts;
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
if (Timeouts.WriteTotalTimeoutConstant ||
Timeouts.WriteTotalTimeoutMultiplier) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(
Extension->CurrentWriteIrp
);
UseATimer = TRUE;
// We have some timer values to calculate.
// Take care, we might have an xoff counter masquerading
// as a write.
TotalTime.QuadPart =
((LONGLONG)((UInt32x32To64(
(IrpSp->MajorFunction == IRP_MJ_WRITE)?
(IrpSp->Parameters.Write.Length):
(1),
Timeouts.WriteTotalTimeoutMultiplier
)
+ Timeouts.WriteTotalTimeoutConstant)))
* -10000;
}
// The irp may be going to the isr shortly. Now
// is a good time to initialize its reference counts.
SERIAL_INIT_REFERENCE(Extension->CurrentWriteIrp);
// We need to see if this irp should be canceled.
IoAcquireCancelSpinLock(&OldIrql);
if (Extension->CurrentWriteIrp->Cancel) {
IoReleaseCancelSpinLock(OldIrql);
Extension->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;
if (!SetFirstStatus) {
FirstStatus = STATUS_CANCELLED;
SetFirstStatus = TRUE;
}
} else {
if (!SetFirstStatus) {
// If we haven't set our first status, then
// this is the only irp that could have possibly
// not been on the queue. (It could have been
// on the queue if this routine is being invoked
// from the completion routine.) Since this
// irp might never have been on the queue we
// should mark it as pending.
IoMarkIrpPending(Extension->CurrentWriteIrp);
SetFirstStatus = TRUE;
FirstStatus = STATUS_PENDING;
}
// We give the irp to to the isr to write out.
// We set a cancel routine that knows how to
// grab the current write away from the isr.
//
// Since the cancel routine has an implicit reference
// to this irp up the reference count.
IoSetCancelRoutine(
Extension->CurrentWriteIrp,
CyzCancelCurrentWrite
);
SERIAL_SET_REFERENCE(Extension->CurrentWriteIrp,SERIAL_REF_CANCEL);
if (UseATimer) {
CyzSetTimer(
&Extension->WriteRequestTotalTimer,
TotalTime,
&Extension->TotalWriteTimeoutDpc,
Extension
);
// This timer now has a reference to the irp.
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_TOTAL_TIMER
);
}
#ifdef POLL
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
CyzGiveWriteToIsr(Extension);
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
#else
KeSynchronizeExecution(
Extension->Interrupt,
CyzGiveWriteToIsr,
Extension
);
#endif
IoReleaseCancelSpinLock(OldIrql);
break;
}
// Well the write was canceled before we could start it up.
// Try to get another.
CyzGetNextWrite(&Extension->CurrentWriteIrp, &Extension->WriteQueue,
&NewIrp, TRUE, Extension);
} while (NewIrp);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzStartWrite %X\n", FirstStatus);
return FirstStatus;
}
VOID
CyzGetNextWrite(
IN PIRP *CurrentOpIrp,
IN PLIST_ENTRY QueueToProcess,
IN PIRP *NewIrp,
IN BOOLEAN CompleteCurrent,
IN PCYZ_DEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine completes the old write as well as getting
a pointer to the next write.
The reason that we have have pointers to the current write
queue as well as the current write irp is so that this
routine may be used in the common completion code for
read and write.
Arguments:
CurrentOpIrp - Pointer to the pointer that points to the
current write irp.
QueueToProcess - Pointer to the write queue.
NewIrp - A pointer to a pointer to the irp that will be the
current irp. Note that this could end up pointing
to a null pointer. This does NOT necessaryly mean
that there is no current write. What could occur
is that while the cancel lock is held the write
queue ended up being empty, but as soon as we release
the cancel spin lock a new irp came in from
CyzStartWrite.
CompleteCurrent - Flag indicates whether the CurrentOpIrp should
be completed.
Return Value:
None.
--*/
{
CYZ_LOCKED_PAGED_CODE();
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzGetNextWrite(XXX)\n");
do {
//
// We could be completing a flush.
//
if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
== IRP_MJ_WRITE) {
KIRQL OldIrql;
ASSERT(Extension->TotalCharsQueued >=
(IoGetCurrentIrpStackLocation(*CurrentOpIrp)
->Parameters.Write.Length));
IoAcquireCancelSpinLock(&OldIrql);
Extension->TotalCharsQueued -=
IoGetCurrentIrpStackLocation(*CurrentOpIrp)
->Parameters.Write.Length;
IoReleaseCancelSpinLock(OldIrql);
} else if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
== IRP_MJ_DEVICE_CONTROL) {
KIRQL OldIrql;
#ifdef POLL
KIRQL pollIrql;
#endif
PIRP Irp;
PSERIAL_XOFF_COUNTER Xc;
IoAcquireCancelSpinLock(&OldIrql);
Irp = *CurrentOpIrp;
Xc = Irp->AssociatedIrp.SystemBuffer;
//
// We should never have a xoff counter when we
// get to this point.
//
ASSERT(!Extension->CurrentXoffIrp);
//
// We absolutely shouldn't have a cancel routine
// at this point.
//
ASSERT(!Irp->CancelRoutine);
//
// This could only be a xoff counter masquerading as
// a write irp.
//
Extension->TotalCharsQueued--;
//
// Check to see of the xoff irp has been set with success.
// This means that the write completed normally. If that
// is the case, and it hasn't been set to cancel in the
// meanwhile, then go on and make it the CurrentXoffIrp.
//
if (Irp->IoStatus.Status != STATUS_SUCCESS) {
//
// Oh well, we can just finish it off.
//
NOTHING;
} else if (Irp->Cancel) {
Irp->IoStatus.Status = STATUS_CANCELLED;
} else {
//
// Give it a new cancel routine, and increment the
// reference count because the cancel routine has
// a reference to it.
//
IoSetCancelRoutine(
Irp,
CyzCancelCurrentXoff
);
SERIAL_SET_REFERENCE(
Irp,
SERIAL_REF_CANCEL
);
//
// We don't want to complete the current irp now. This
// will now get completed by the Xoff counter code.
//
CompleteCurrent = FALSE;
//
// Give the counter to the isr.
//
Extension->CurrentXoffIrp = Irp;
#ifdef POLL
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
CyzGiveXoffToIsr(Extension);
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
#else
KeSynchronizeExecution(
Extension->Interrupt,
CyzGiveXoffToIsr,
Extension
);
#endif
//
// Start the timer for the counter and increment
// the reference count since the timer has a
// reference to the irp.
//
if (Xc->Timeout) {
LARGE_INTEGER delta;
delta.QuadPart = -((LONGLONG)UInt32x32To64(
1000,
Xc->Timeout
));
CyzSetTimer(
&Extension->XoffCountTimer,
delta,
&Extension->XoffCountTimeoutDpc,
Extension
);
SERIAL_SET_REFERENCE(
Irp,
SERIAL_REF_TOTAL_TIMER
);
}
}
IoReleaseCancelSpinLock(OldIrql);
}
//
// Note that the following call will (probably) also cause
// the current irp to be completed.
//
CyzGetNextIrp(
CurrentOpIrp,
QueueToProcess,
NewIrp,
CompleteCurrent,
Extension
);
if (!*NewIrp) {
KIRQL OldIrql;
#ifdef POLL
KIRQL pollIrql;
#endif
IoAcquireCancelSpinLock(&OldIrql);
#ifdef POLL
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
CyzProcessEmptyTransmit(Extension);
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
#else
KeSynchronizeExecution(
Extension->Interrupt,
CyzProcessEmptyTransmit,
Extension
);
#endif
IoReleaseCancelSpinLock(OldIrql);
break;
} else if (IoGetCurrentIrpStackLocation(*NewIrp)->MajorFunction
== IRP_MJ_FLUSH_BUFFERS) {
//
// If we encounter a flush request we just want to get
// the next irp and complete the flush.
//
// Note that if NewIrp is non-null then it is also
// equal to CurrentWriteIrp.
//
ASSERT((*NewIrp) == (*CurrentOpIrp));
(*NewIrp)->IoStatus.Status = STATUS_SUCCESS;
} else {
break;
}
} while (TRUE);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzGetNextWrite\n");
}
VOID
CyzCompleteWrite(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This routine is merely used to complete any write. It
assumes that the status and the information fields of
the irp are already correctly filled in.
Arguments:
Dpc - Not Used.
DeferredContext - Really points to the device extension.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
Return Value:
None.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzCompleteWrite(%X)\n",
Extension);
// LOGENTRY(LOG_MISC, ZSIG_WRITE_COMPLETE,
// Extension->PortIndex+1,
// 0,
// 0);
IoAcquireCancelSpinLock(&OldIrql);
CyzTryToCompleteCurrent(Extension, NULL, OldIrql, STATUS_SUCCESS,
&Extension->CurrentWriteIrp,
&Extension->WriteQueue, NULL,
&Extension->WriteRequestTotalTimer,
CyzStartWrite, CyzGetNextWrite,
SERIAL_REF_ISR);
CyzDpcEpilogue(Extension, Dpc);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzCompleteWrite\n");
}
BOOLEAN
CyzProcessEmptyTransmit(
IN PVOID Context
)
/*++
Routine Description:
This routine is used to determine if conditions are appropriate
to satisfy a wait for transmit empty event, and if so to complete
the irp that is waiting for that event. It also call the code
that checks to see if we should lower the RTS line if we are
doing transmit toggling.
NOTE: This routine is called by KeSynchronizeExecution.
NOTE: This routine assumes that it is called with the cancel
spinlock held.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = Context;
CYZ_LOCKED_PAGED_CODE();
if (Extension->IsrWaitMask && (Extension->IsrWaitMask & SERIAL_EV_TXEMPTY) &&
Extension->EmptiedTransmit && (!Extension->TransmitImmediate) &&
(!Extension->CurrentWriteIrp) && IsListEmpty(&Extension->WriteQueue)) {
Extension->HistoryMask |= SERIAL_EV_TXEMPTY;
if (Extension->IrpMaskLocation) {
*Extension->IrpMaskLocation = Extension->HistoryMask;
Extension->IrpMaskLocation = NULL;
Extension->HistoryMask = 0;
Extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
CyzInsertQueueDpc(
&Extension->CommWaitDpc,
NULL,
NULL,
Extension
);
}
Extension->CountOfTryingToLowerRTS++;
CyzPerhapsLowerRTS(Extension);
}
return FALSE;
}
BOOLEAN
CyzGiveWriteToIsr(
IN PVOID Context
)
/*++
Routine Description:
Try to start off the write by slipping it in behind
a transmit immediate char, or if that isn't available
and the transmit holding register is empty, "tickle"
the UART into interrupting with a transmit buffer
empty.
NOTE: This routine is called by KeSynchronizeExecution.
NOTE: This routine assumes that it is called with the
cancel spin lock held.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = Context;
//
// The current stack location. This contains all of the
// information we need to process this particular request.
//
PIO_STACK_LOCATION IrpSp;
CYZ_LOCKED_PAGED_CODE();
// LOGENTRY(LOG_MISC, ZSIG_GIVE_WRITE_TO_ISR,
// Extension->PortIndex+1,
// 0,
// 0);
IrpSp = IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp);
//
// We might have a xoff counter request masquerading as a
// write. The length of these requests will always be one
// and we can get a pointer to the actual character from
// the data supplied by the user.
//
if (IrpSp->MajorFunction == IRP_MJ_WRITE) {
Extension->WriteLength = IrpSp->Parameters.Write.Length;
Extension->WriteCurrentChar =
Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer;
} else {
Extension->WriteLength = 1;
Extension->WriteCurrentChar =
((PUCHAR)Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer) +
FIELD_OFFSET(
SERIAL_XOFF_COUNTER,
XoffChar
);
}
//
// The isr now has a reference to the irp.
//
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_ISR
);
//
// Check first to see if an immediate char is transmitting.
// If it is then we'll just slip in behind it when its
// done.
//
//Removed at 02/07/00 by Fanny. Polling routine will do the transmission.
if (!Extension->TransmitImmediate) {
//
// //
// // If there is no immediate char transmitting then we
// // will "re-enable" the transmit holding register empty
// // interrupt. The 8250 family of devices will always
// // signal a transmit holding register empty interrupt
// // *ANY* time this bit is set to one. By doing things
// // this way we can simply use the normal interrupt code
// // to start off this write.
// //
// // We've been keeping track of whether the transmit holding
// // register is empty so it we only need to do this
// // if the register is empty.
// //
//
//#if DBG
//{
// PUCHAR writeptr = Extension->WriteCurrentChar;
// int ivar;
// for (ivar=0; ivar< Extension->WriteLength; ivar++) {
// //DbgPrint("%c",writeptr[ivar]);
// LOGENTRY(LOG_MISC,ZSIG_TRANSMIT,0,0,writeptr[ivar]);
// }
// //DbgPrint("\n");
//}
//#endif
//if (Extension->HoldingEmpty) {
// enable transmit intr
CyzTxStart(Extension);
//}
}
//
// The rts line may already be up from previous writes,
// however, it won't take much additional time to turn
// on the RTS line if we are doing transmit toggling.
//
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_TRANSMIT_TOGGLE) {
CyzSetRTS(Extension);
}
return FALSE;
}
VOID
CyzCancelCurrentWrite(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine is used to cancel the current write.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP to be canceled.
Return Value:
None.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
CYZ_LOCKED_PAGED_CODE();
CyzTryToCompleteCurrent(
Extension,
CyzGrabWriteFromIsr,
Irp->CancelIrql,
STATUS_CANCELLED,
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
NULL,
&Extension->WriteRequestTotalTimer,
CyzStartWrite,
CyzGetNextWrite,
SERIAL_REF_CANCEL
);
}
VOID
CyzWriteTimeout(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This routine will try to timeout the current write.
Arguments:
Dpc - Not Used.
DeferredContext - Really points to the device extension.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
Return Value:
None.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzWriteTimeout(%X)\n",
Extension);
IoAcquireCancelSpinLock(&OldIrql);
CyzTryToCompleteCurrent(Extension, CyzGrabWriteFromIsr, OldIrql,
STATUS_TIMEOUT, &Extension->CurrentWriteIrp,
&Extension->WriteQueue, NULL,
&Extension->WriteRequestTotalTimer,
CyzStartWrite, CyzGetNextWrite,
SERIAL_REF_TOTAL_TIMER);
CyzDpcEpilogue(Extension, Dpc);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzWriteTimeout\n");
}
BOOLEAN
CyzGrabWriteFromIsr(
IN PVOID Context
)
/*++
Routine Description:
This routine is used to grab the current irp, which could be timing
out or canceling, from the ISR
NOTE: This routine is being called from KeSynchronizeExecution.
NOTE: This routine assumes that the cancel spin lock is held
when this routine is called.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
Always false.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = Context;
CYZ_LOCKED_PAGED_CODE();
//
// Check if the write length is non-zero. If it is non-zero
// then the ISR still owns the irp. We calculate the the number
// of characters written and update the information field of the
// irp with the characters written. We then clear the write length
// the isr sees.
//
if (Extension->WriteLength) {
//
// We could have an xoff counter masquerading as a
// write irp. If so, don't update the write length.
//
if (IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp)
->MajorFunction == IRP_MJ_WRITE) {
Extension->CurrentWriteIrp->IoStatus.Information =
IoGetCurrentIrpStackLocation(
Extension->CurrentWriteIrp
)->Parameters.Write.Length -
Extension->WriteLength;
} else {
Extension->CurrentWriteIrp->IoStatus.Information = 0;
}
//
// Since the isr no longer references this irp, we can
// decrement it's reference count.
//
SERIAL_CLEAR_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_ISR
);
Extension->WriteLength = 0;
if (Extension->ReturnStatusAfterFwEmpty) {
struct BUF_CTRL *buf_ctrl;
ULONG tx_get, tx_put;
Extension->ReturnWriteStatus = FALSE;
// Flush fw buffer and Startech FIFO.
CyzIssueCmd(Extension,C_CM_FLUSH_TX,0L,TRUE);
// Flush transmission buffer in the firmware
//buf_ctrl = Extension->BufCtrl;
//tx_put = CYZ_READ_ULONG(&buf_ctrl->tx_put);
//tx_get = CYZ_READ_ULONG(&buf_ctrl->tx_get);
//while (tx_put != tx_get) {
// tx_put = tx_get;
// CYZ_WRITE_ULONG(&buf_ctrl->tx_put,tx_put);
// tx_get = CYZ_READ_ULONG(&buf_ctrl->tx_get);
//}
}
}
return FALSE;
}
BOOLEAN
CyzGrabXoffFromIsr(
IN PVOID Context
)
/*++
Routine Description:
This routine is used to grab an xoff counter irp from the
isr when it is no longer masquerading as a write irp. This
routine is called by the cancel and timeout code for the
xoff counter ioctl.
NOTE: This routine is being called from KeSynchronizeExecution.
NOTE: This routine assumes that the cancel spin lock is held
when this routine is called.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
Always false.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = Context;
CYZ_LOCKED_PAGED_CODE();
if (Extension->CountSinceXoff) {
//
// This is only non-zero when there actually is a Xoff ioctl
// counting down.
//
Extension->CountSinceXoff = 0;
//
// We decrement the count since the isr no longer owns
// the irp.
//
SERIAL_CLEAR_REFERENCE(
Extension->CurrentXoffIrp,
SERIAL_REF_ISR
);
}
return FALSE;
}
VOID
CyzCompleteXoff(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This routine is merely used to truely complete an xoff counter irp. It
assumes that the status and the information fields of the irp are
already correctly filled in.
Arguments:
Dpc - Not Used.
DeferredContext - Really points to the device extension.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
Return Value:
None.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzCompleteXoff(%X)\n",
Extension);
IoAcquireCancelSpinLock(&OldIrql);
CyzTryToCompleteCurrent(Extension, NULL, OldIrql, STATUS_SUCCESS,
&Extension->CurrentXoffIrp, NULL, NULL,
&Extension->XoffCountTimer, NULL, NULL,
SERIAL_REF_ISR);
CyzDpcEpilogue(Extension, Dpc);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzCompleteXoff\n");
}
VOID
CyzTimeoutXoff(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This routine is merely used to truely complete an xoff counter irp,
if its timer has run out.
Arguments:
Dpc - Not Used.
DeferredContext - Really points to the device extension.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
Return Value:
None.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzTimeoutXoff(%X)\n", Extension);
IoAcquireCancelSpinLock(&OldIrql);
CyzTryToCompleteCurrent(Extension, CyzGrabXoffFromIsr, OldIrql,
STATUS_SERIAL_COUNTER_TIMEOUT,
&Extension->CurrentXoffIrp, NULL, NULL, NULL,
NULL, NULL, SERIAL_REF_TOTAL_TIMER);
CyzDpcEpilogue(Extension, Dpc);
CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzTimeoutXoff\n");
}
VOID
CyzCancelCurrentXoff(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine is used to cancel the current write.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP to be canceled.
Return Value:
None.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
CYZ_LOCKED_PAGED_CODE();
CyzTryToCompleteCurrent(
Extension,
CyzGrabXoffFromIsr,
Irp->CancelIrql,
STATUS_CANCELLED,
&Extension->CurrentXoffIrp,
NULL,
NULL,
&Extension->XoffCountTimer,
NULL,
NULL,
SERIAL_REF_CANCEL
);
}
BOOLEAN
CyzGiveXoffToIsr(
IN PVOID Context
)
/*++
Routine Description:
This routine starts off the xoff counter. It merely
has to set the xoff count and increment the reference
count to denote that the isr has a reference to the irp.
NOTE: This routine is called by KeSynchronizeExecution.
NOTE: This routine assumes that it is called with the
cancel spin lock held.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PCYZ_DEVICE_EXTENSION Extension = Context;
//
// The current stack location. This contains all of the
// information we need to process this particular request.
//
PSERIAL_XOFF_COUNTER Xc =
Extension->CurrentXoffIrp->AssociatedIrp.SystemBuffer;
CYZ_LOCKED_PAGED_CODE();
ASSERT(Extension->CurrentXoffIrp);
Extension->CountSinceXoff = Xc->Counter;
//
// The isr now has a reference to the irp.
//
SERIAL_SET_REFERENCE(
Extension->CurrentXoffIrp,
SERIAL_REF_ISR
);
return FALSE;
}