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.
 
 
 
 
 
 

1261 lines
40 KiB

/*-------------------------------------------------------------------
| write.c -
1-22-99 - add missing IoReleaseCancelSpinLock() to CompleteWrite(),
missing since 1-18-99 changes. kpb.
1-18-99 - Adjust sync lock for write packets to avoid bugchecks with
wait on tx option. kpb
9-25-98 - bugfix, immediate write could drop 1 byte due to faulty
txport buffer check.
Copyright 1993-98 Comtrol Corporation. All rights reserved.
|--------------------------------------------------------------------*/
#include "precomp.h"
/************************************************************************
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.
************************************************************************/
NTSTATUS
SerialWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
NTSTATUS Status;
BOOLEAN acceptingIRPs;
acceptingIRPs = SerialIRPPrologue(Extension);
if (acceptingIRPs == FALSE) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
SerialCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
return STATUS_NO_SUCH_DEVICE;
}
if (Extension->DeviceType == DEV_BOARD)
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
SerialCompleteRequest (Extension, Irp, IO_NO_INCREMENT);
return STATUS_NOT_SUPPORTED;
};
#ifdef TRACE_PORT
if (Extension->TraceOptions)
{
if (Extension->TraceOptions & 1) // trace messages
{
Tprintf("Write, Len:%d",
(ULONG) IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length);
// dump data into the trace buffer in a hex or ascii dump format
TraceDump(Extension,
Irp->AssociatedIrp.SystemBuffer,
IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length,0);
}
else if (Extension->TraceOptions & 4) // trace output data
{
TracePut(
Irp->AssociatedIrp.SystemBuffer,
IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length);
}
}
#endif
if (Extension->ErrorWord)
{
if (SerialCompleteIfError( DeviceObject, Irp ) != STATUS_SUCCESS)
{
ExtTrace(Extension,D_Error, " ErrorSet!");
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)
{
//------ 10-22-96, start code addition to speed up 1-byte writes
#define WRT_LEN (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length)
#ifdef S_VS
// vs1000 code
if (!Extension->port_config->WaitOnTx) // have to let ISR handle physical end detect
{
if (WRT_LEN < OUT_BUF_SIZE)
{
// ISR is locked out since it only handles IRP's queued up
// any other contention problems?
//extension->lock_out_other_tasks = 1;
if ((Extension->CurrentWriteIrp == NULL) // no current write
&& (IsListEmpty(&Extension->WriteQueue))) // no queued up output data
{
// if room in hardware
// bug: kpb, 9-22-98
//if ( (ULONG)(OUT_BUF_SIZE-PortGetTxCnt(Extension->Port)) >= WRT_LEN)
if ( (ULONG)(PortGetTxRoom(Extension->Port)) > WRT_LEN)
{
// Send it all ,WriteTxBlk will chk fifo
q_put(&Extension->Port->QOut,
(PUCHAR)(Irp->AssociatedIrp.SystemBuffer),
WRT_LEN);
Extension->ISR_Flags |= TX_NOT_EMPTY; // use to detect fifo empty
Extension->OurStats.TransmittedCount += WRT_LEN;
++Extension->sent_packets;
Irp->IoStatus.Information = WRT_LEN;
ExtTrace(Extension,D_Ioctl, " ,IMMED. WRITE");
Irp->IoStatus.Status = STATUS_SUCCESS;
SerialCompleteRequest(Extension, Irp, 0);
return STATUS_SUCCESS;
}
}
} // if (WRT_LEN < OUT_BUF_SIZE)
} // if (!Extension->port_config->WaitOnTx)
#else
// rocketport code
if (!Extension->port_config->WaitOnTx) // have to let ISR handle physical end detect
{
if (WRT_LEN <= MAXTX_SIZE)
{
// ISR is locked out since it only handles IRP's queued up
if ((Extension->CurrentWriteIrp == NULL) // no current write
&& (IsListEmpty(&Extension->WriteQueue))) // no queued up output data
{
// if room in hardware
if ( (ULONG)(MAXTX_SIZE-sGetTxCnt(Extension->ChP)) >= WRT_LEN)
{
if (Extension->Option & OPTION_RS485_SOFTWARE_TOGGLE)
{
if ((Extension->DTRRTSStatus & SERIAL_RTS_STATE) == 0)
{
sSetRTS(Extension->ChP);
Extension->DTRRTSStatus |= SERIAL_RTS_STATE;
}
}
// Send it all ,WriteTxBlk will chk fifo
sWriteTxBlk(Extension->ChP,
(PUCHAR)(Irp->AssociatedIrp.SystemBuffer),
WRT_LEN);
Extension->ISR_Flags |= TX_NOT_EMPTY; // use to detect fifo empty
Extension->OurStats.TransmittedCount += WRT_LEN;
++Extension->sent_packets;
Irp->IoStatus.Information = WRT_LEN;
ExtTrace(Extension,D_Ioctl, " ,IMMED. WRITE");
++Extension->sent_packets;
Irp->IoStatus.Status = STATUS_SUCCESS;
SerialCompleteRequest(Extension, Irp, 0);
return STATUS_SUCCESS;
}
}
} // if (WRT_LEN <= MAXTX_SIZE)
} // if (!Extension->port_config->WaitOnTx)
#endif // rocketport code
//------ 10-22-96, end code addition to speed up 1-byte writes
// Put the write on the queue so that we can
// process it when our previous writes are done.
++Extension->sent_packets;
Status = SerialStartOrQueue(
Extension,
Irp,
&Extension->WriteQueue,
&Extension->CurrentWriteIrp,
SerialStartWrite
);
if (Status == STATUS_PENDING)
{
ExtTrace(Extension,D_Ioctl, " ,PENDING");
}
return Status;
}
else // if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
SerialCompleteRequest(Extension, Irp, 0 );
return STATUS_SUCCESS;
}
}
/***************************************************************************
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.
***************************************************************************/
NTSTATUS
SerialStartWrite(
IN PSERIAL_DEVICE_EXTENSION Extension
)
{
PIRP NewIrp;
KIRQL OldIrql;
LARGE_INTEGER TotalTime;
BOOLEAN UseATimer;
SERIAL_TIMEOUTS Timeouts;
BOOLEAN SetFirstStatus = FALSE;
NTSTATUS FirstStatus;
do {
// If there is an xoff counter then complete it.
#ifdef REMOVED // Will not suppor Xoff Counter IRP
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
);
//
// The following call will actually release the
// cancel spin lock.
//
SerialTryToCompleteCurrent(
Extension,
SerialGrabXoffFromIsr,
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);
}
#endif //REMOVED
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;
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);
ExtTrace(Extension,D_Ioctl, " (write canceled)");
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,
SerialCancelCurrentWrite
);
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_CANCEL
);
if(UseATimer)
{
//ExtTrace(Extension,D_Ioctl, " (total timer used)");
KeSetTimer(
&Extension->WriteRequestTotalTimer,
TotalTime,
&Extension->TotalWriteTimeoutDpc
);
// This timer now has a reference to the irp.
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_TOTAL_TIMER
);
}
#ifdef NEW_WRITE_SYNC_LOCK
// write some data now
SyncUp(Driver.InterruptObject,
&Driver.TimerLock,
SerialGiveWriteToIsr,
Extension);
#else
SerialGiveWriteToIsr(Extension);
#endif
IoReleaseCancelSpinLock(OldIrql);
break;
}
// Well the write was canceled before we could start it up.
// Try to get another.
SerialGetNextWrite(
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
&NewIrp,
TRUE,
Extension
);
} while (NewIrp);
return FirstStatus;
}
/****************************************************************************
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
SerialStartWrite.
CompleteCurrent - Flag indicates whether the CurrentOpIrp should
be completed.
Return Value:
None.
****************************************************************************/
VOID
SerialGetNextWrite(
IN PIRP *CurrentOpIrp,
IN PLIST_ENTRY QueueToProcess,
IN PIRP *NewIrp,
IN BOOLEAN CompleteCurrent,
PSERIAL_DEVICE_EXTENSION Extension
)
{
// LARGE_INTEGER charTime; // 100 ns ticks per char, related to baud rate
// LARGE_INTEGER WaitTime; // Actual time req'd for buffer to drain
// PSERIAL_DEVICE_EXTENSION Extension = CONTAINING_RECORD(
// QueueToProcess,
// SERIAL_DEVICE_EXTENSION,
// WriteQueue
// );
do {
// We could be completing a flush.
if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
== IRP_MJ_WRITE)
{ //------- normal write block
KIRQL OldIrql;
// assert that our TotalCharsQueued var is not screwed up.
MyAssert(Extension->TotalCharsQueued >=
(IoGetCurrentIrpStackLocation(*CurrentOpIrp)
->Parameters.Write.Length));
IoAcquireCancelSpinLock(&OldIrql);
// increment our character count
Extension->TotalCharsQueued -=
IoGetCurrentIrpStackLocation(*CurrentOpIrp)
->Parameters.Write.Length;
IoReleaseCancelSpinLock(OldIrql);
}
else if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
== IRP_MJ_DEVICE_CONTROL)
{ //------- xoffcounter nonsense
KIRQL OldIrql;
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);
// If CurrentXoffIrp is not equal to null, this
// implies that this is the "second" time around
// for this irp, which implies that we should really
// be completing it this time.
//
// 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,
SerialCancelCurrentXoff
);
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;
//KeSynchronizeExecution(
// Driver.Interrupt,
// SerialGiveXoffToIsr,
// Extension
// );
SerialGiveXoffToIsr(Extension);
//
// 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
));
KeSetTimer(
&Extension->XoffCountTimer,
delta,
&Extension->XoffCountTimeoutDpc
);
SERIAL_SET_REFERENCE(Irp,SERIAL_REF_TOTAL_TIMER);
} // timeout
}
IoReleaseCancelSpinLock(OldIrql);
}
//
// Note that the following call will (probably) also cause
// the current irp to be completed.
//
SerialGetNextIrp(
CurrentOpIrp,
QueueToProcess,
NewIrp,
CompleteCurrent,
Extension
);
if (!*NewIrp) {
KIRQL OldIrql;
IoAcquireCancelSpinLock(&OldIrql);
//KeSynchronizeExecution(
// Extension->Interrupt,
// SerialProcessEmptyTransmit,
// Extension
// );
//SerialProcessEmptyTransmit();
IoReleaseCancelSpinLock(OldIrql);
break;
}
else if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction ==
IRP_MJ_FLUSH_BUFFERS )
{ //------ flush operation
// If flush, wait for Tx FIFO to empty before completing
ExtTrace(Extension,D_Ioctl, "(end flush write)");
#ifdef S_RK
#ifdef COMMENT_OUT
(took this out 9-22-97 - kpb)
// Calculate 100ns ticks to delay for each character
// Negate for call to KeDelay...
charTime = RtlLargeIntegerNegate(SerialGetCharTime(Extension));
// While Tx FIFO and Tx Shift Register aren't empty
while ( (sGetChanStatusLo(Extension->ChP) & DRAINED) != DRAINED )
{ WaitTime = RtlExtendedIntegerMultiply(charTime,
sGetTxCnt(Extension->ChP)
);
KeDelayExecutionThread(KernelMode,FALSE,&WaitTime);
}
#endif
#endif
//
// 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);
}
/********************************************************************
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.
*********************************************************************/
VOID
SerialCompleteWrite(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
{
PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
IoAcquireCancelSpinLock(&OldIrql);
// make sure no one else grabbed it first, since ISR will
// set this flag to 2 to indicate it is ours to end.
if (Extension->WriteBelongsToIsr == 2)
{
Extension->WriteBelongsToIsr = 0;
SerialTryToCompleteCurrent(
Extension,
NULL,
OldIrql,
STATUS_SUCCESS,
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
NULL,
&Extension->WriteRequestTotalTimer,
SerialStartWrite,
SerialGetNextWrite,
SERIAL_REF_ISR
);
}
else
{
IoReleaseCancelSpinLock(OldIrql);
}
}
/******************************************************************************
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.
******************************************************************************/
BOOLEAN
SerialGiveWriteToIsr(
IN PVOID Context
)
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
ULONG wCount;
ULONG room;
ULONG write_cnt;
ULONG OurWriteLength;
// The current stack location. This contains all of the
// information we need to process this particular request.
PIO_STACK_LOCATION IrpSp;
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)
{
//------ start code addition to avoid tx-lag, see if we can start
// sending data to hardware immediately
OurWriteLength = IrpSp->Parameters.Write.Length;
write_cnt = 0; // use this as a flag as well if we will write() now.
if ((IsListEmpty(&Extension->WriteQueue))) // no queued up output data
{
// the startwrite routine added CurrentWriteIrp which is us,
// so we are the only Write Irp in existence. This is important
// so that isr.c is not trying to serve up off our irp que too.
//// [bug, 3-28-98, kpb] room = sGetTxCnt(Extension->ChP);
#ifdef S_RK
room = (ULONG)(MAXTX_SIZE-sGetTxCnt(Extension->ChP));
#else
room = (ULONG) PortGetTxRoom(Extension->Port);
#endif
if (room > 10) // we have room in the hardware
{
// at this point we have some non-trivial amount of space in
// the hardware buffer to put some data.
write_cnt = IrpSp->Parameters.Write.Length;
}
if (write_cnt) // we are going to write() now
{
#ifdef S_RK
// toggle rts if needed
if (Extension->Option & OPTION_RS485_SOFTWARE_TOGGLE)
{
if ((Extension->DTRRTSStatus & SERIAL_RTS_STATE) == 0)
{
sSetRTS(Extension->ChP);
Extension->DTRRTSStatus |= SERIAL_RTS_STATE;
}
}
// Send as much as possible,WriteTxBlk will chk fifo
wCount = sWriteTxBlk(Extension->ChP,
(PUCHAR)(Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer),
write_cnt);
#else
// Send it all ,WriteTxBlk will chk fifo
if (write_cnt > room) // limit to what we have space for
wCount = room;
else wCount = write_cnt;
q_put(&Extension->Port->QOut,
(PUCHAR)(Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer),
wCount);
#endif
Extension->OurStats.TransmittedCount += wCount;
Extension->CurrentWriteIrp->IoStatus.Information += wCount;
// following used to detect fifo empty, semaphore
// which passes control to the ISR routine.
OurWriteLength = (IrpSp->Parameters.Write.Length - wCount);
Extension->ISR_Flags |= TX_NOT_EMPTY;
// and gives this write to ISR
ExtTrace(Extension,D_Ioctl, " , Immed Part Write");
} // write() it out
} // no queue write packets
//------ 1-08-98, end code addition to avoid tx-lag
//add irp to queue, give isr.c the write irp to finish
Extension->WriteLength = OurWriteLength;
if (Extension->port_config->WaitOnTx)
{
// then definitely let ISR finish it, ISR must wait for tx-fifo to drain
Extension->WriteBelongsToIsr = 1;
}
else
{
if (OurWriteLength == 0)
{
// its done, finish it off
Extension->WriteBelongsToIsr = 2;
KeInsertQueueDpc( &Extension->CompleteWriteDpc, NULL, NULL );
}
else
Extension->WriteBelongsToIsr = 1;
}
}
else
{
// !!!!! WHAT is a xoff counter????
// An xoff-counter is something the virtual 16450 uart driver uses
// to send an xoff, it sends an xoff and also starts a timer for
// what purpose I am not sure. Tried some code where it just sends
// an xoff without the timer, this seemed to work ok, but the sent
// xoff should be synced up with the other outgoing data packets in
// the order received from the app.
//It's an xoff counter......
Extension->WriteLength = 1;
Extension->WriteCurrentChar =
((PUCHAR)Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer) +
FIELD_OFFSET(SERIAL_XOFF_COUNTER, XoffChar);
}
//endwr:
// The isr now has a reference to the irp.
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_ISR
);
return FALSE;
}
/****************************************************************************
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.
*****************************************************************************/
VOID
SerialCancelCurrentWrite(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
ExtTrace(Extension,D_Ioctl, "(cancel cur-write)");
SerialTryToCompleteCurrent(
Extension,
SerialGrabWriteFromIsr,
Irp->CancelIrql,
STATUS_CANCELLED,
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
NULL,
&Extension->WriteRequestTotalTimer,
SerialStartWrite,
SerialGetNextWrite,
SERIAL_REF_CANCEL
);
}
/***************************************************************************
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.
***************************************************************************/
VOID
SerialWriteTimeout(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
{
PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
ExtTrace(Extension,D_Ioctl, "(write-timeout)");
IoAcquireCancelSpinLock(&OldIrql);
SerialTryToCompleteCurrent(
Extension,
SerialGrabWriteFromIsr,
OldIrql,
STATUS_TIMEOUT,
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
NULL,
&Extension->WriteRequestTotalTimer,
SerialStartWrite,
SerialGetNextWrite,
SERIAL_REF_TOTAL_TIMER
);
}
/***************************************************************************
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.
***************************************************************************/
BOOLEAN
SerialGrabWriteFromIsr(
IN PVOID Context
)
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
#ifdef NEW_WAIT
ULONG in_q;
#endif
#ifdef NEW_WAIT
if (Extension->WriteBelongsToIsr != 0)
{
// isr owns irp, or it is has queued dpc to complete it.
// reset this flag to take back from the isr.
Extension->WriteBelongsToIsr = 0;
// 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 = 0;
}
else
{
SERIAL_CLEAR_REFERENCE(Extension->CurrentWriteIrp,
SERIAL_REF_ISR);
Extension->WriteLength = 0;
if (Extension->port_config->WaitOnTx)
{
// want to report how many characters are "stuck", or did
// not really make it out the port if a timeout occurs.
#ifdef S_RK
in_q = sGetTxCnt(Extension->ChP);
#else
// may have add in box cout too?
in_q = PortGetTxCnt(Extension->Port);
#endif
if (Extension->CurrentWriteIrp->IoStatus.Information >= in_q)
Extension->CurrentWriteIrp->IoStatus.Information -= in_q;
}
}
}
#else
// 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;
}
SERIAL_CLEAR_REFERENCE(Extension->CurrentWriteIrp,
SERIAL_REF_ISR);
Extension->WriteLength = 0;
}
#endif
return FALSE;
}
// Xoff Counter code: UNUSED
/*-----------------------------------------------------------------
SerialGrabXoffFromIsr -
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.
|-----------------------------------------------------------------*/
BOOLEAN
SerialGrabXoffFromIsr(
IN PVOID Context
)
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
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;
}
/******************************************************************************
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.
******************************************************************************/
VOID
SerialCompleteXoff(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
{
PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
IoAcquireCancelSpinLock(&OldIrql);
SerialTryToCompleteCurrent(
Extension,
NULL,
OldIrql,
STATUS_SUCCESS,
&Extension->CurrentXoffIrp,
NULL,
NULL,
&Extension->XoffCountTimer,
NULL,
NULL,
SERIAL_REF_ISR
);
}
/*------------------------------------------------------------------
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.
-------------------------------------------------------------------*/
VOID
SerialTimeoutXoff(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
{
PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
IoAcquireCancelSpinLock(&OldIrql);
SerialTryToCompleteCurrent(
Extension,
SerialGrabXoffFromIsr,
OldIrql,
STATUS_SERIAL_COUNTER_TIMEOUT,
&Extension->CurrentXoffIrp,
NULL,
NULL,
NULL,
NULL,
NULL,
SERIAL_REF_TOTAL_TIMER
);
}
/*---------------------------------------------------------------
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.
----------------------------------------------------------------*/
VOID
SerialCancelCurrentXoff(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
SerialTryToCompleteCurrent(
Extension,
SerialGrabXoffFromIsr,
Irp->CancelIrql,
STATUS_CANCELLED,
&Extension->CurrentXoffIrp,
NULL,
NULL,
&Extension->XoffCountTimer,
NULL,
NULL,
SERIAL_REF_CANCEL
);
}
/*------------------------------------------------------------------------
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.
------------------------------------------------------------------------*/
BOOLEAN
SerialGiveXoffToIsr(
IN PVOID Context
)
{
PSERIAL_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;
Extension->CountSinceXoff = Xc->Counter;
// The isr now has a reference to the irp.
SERIAL_SET_REFERENCE(
Extension->CurrentXoffIrp,
SERIAL_REF_ISR
);
return FALSE;
}