NT4/private/ntos/dd/digibrd/fep5/read.c
2020-09-30 17:12:29 +02:00

1830 lines
62 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
*****************************************************************************
* *
* This software contains proprietary and confidential information of *
* *
* Digi International Inc. *
* *
* By accepting transfer of this copy, Recipient agrees to retain this *
* software in confidence, to prevent disclosure to others, and to make *
* no use of this software other than that for which it was delivered. *
* This is an unpublished copyrighted work of Digi International Inc. *
* Except as permitted by federal law, 17 USC 117, copying is strictly *
* prohibited. *
* *
*****************************************************************************
Module Name:
read.c
Abstract:
This module contains the NT routines responsible for reading data
from a Digi controller running FEPOS 5 program.
Revision History:
* $Log: /Components/Windows/NT/Async/FEP5/read.c $
*
* 1 3/04/96 12:20p Stana
* Module is responsible for reading data from a DigiBoard intelligent
* controller through the FEP 5 interface.
* Revision 1.31.2.6 1995/12/14 12:26:08 dirkh
* Set IDATA *after* updating ROUT.
*
* Revision 1.31.2.5 1995/11/28 12:49:06 dirkh
* Adopt common header file.
*
* Revision 1.31.2.4 1995/10/25 15:18:38 dirkh
* DigiIntervalReadTimeout uses NBytesInRecvBuffer to determine whether IRP can be completed.
*
* Revision 1.31.2.3 1995/09/19 12:55:28 dirkh
* Add IOCTL_SERIAL_XOFF_COUNTER support to ReadRxBuffer (decrement .Counter, don't double count "previewed" bytes when there was no read IRP -- see DigiServiceEvent).
*
* Revision 1.31.2.2 1995/09/06 10:56:18 dirkh
* Fix debug messages.
*
* Revision 1.31.2.1 1995/09/05 13:31:52 dirkh
* General: Minimize use of IoCancel spin lock.
* Simplify ReadRxBuffer interface.
* StartReadRequest changes:
* {
* Replace DevExt->ReadOffset with RQ->Irp->IoStatus.Information.
* Irp->IoStatus.Information is set to MAXULONG when IRP has not been started.
* Calculates timeouts only when necessary.
* Continue starting IRPs if the current one is canceled.
* Set bFirstStatus on loop iteration.
* }
* DigiReadTimeout and DigiReadIntervalTimeout acquire spin locks at DISPATCH_LEVEL.
*
* Revision 1.31 1995/04/06 17:43:14 rik
* Changed the ProcessSlowRead to KISS (Keep It Simple Stupid). Now I only
* read the number of valid data character from the controller.
* This fixed a problem with WinCim, a Win-16 application.
*
* Revision 1.30 1994/12/09 14:23:04 rik
* #if Int32x32 back to RtlLarge for NT 3.1 release
*
* Revision 1.29 1994/11/28 09:17:03 rik
* Made corrections for PowerPC port.
* Optimized the polling loop for determining which port needs servicing.
* Changed from using RtlLarge math functions to direct 64-bit manipulation,
* per Microsoft's request.
*
* Revision 1.28 1994/09/13 09:48:01 rik
* Added debug tracking output for cancel irps.
*
* Revision 1.27 1994/08/18 14:07:03 rik
* No longer use ProcessSlowRead for EV_RXFLAG events.
* Updated where last character was read in from on the controller.
*
* Revision 1.26 1994/08/10 19:12:33 rik
* Added port name to debug string.
*
* Revision 1.25 1994/08/03 23:29:58 rik
* optimized determining when RX_FLAG comes in.
*
* changed dbg string from unicode to c string.
*
* Revision 1.24 1994/02/17 18:03:15 rik
* Deleted some commented out code.
* Fixed possible buffer alignment problem when using an EPC which
* can have 32K of buffer.
*
* Revision 1.23 1994/01/31 13:55:50 rik
* Updated to fix problems with Win16 apps and DOS mode apps. Win16 apps
* appear to be working properly, but DOS mode apps still have some sort
* of problem.
*
* Revision 1.22 1993/12/03 13:19:31 rik
* Fixed problem with reading DosMode value from wrong place on the
* controller.
*
* Revision 1.21 1993/10/15 10:22:33 rik
* Added new function which scans the controllers buffer for a special character.
* This is used primarily for EV_RXFLAG notification.
*
* Revision 1.20 1993/10/06 11:04:04 rik
* Fixed a problem with how ProcessSlowRead was parsing the input data stream.
* Previously, if the last character of the read was a 0xFF, it would
* return without determining if the next character was a 0xFF. Also, fixed
* problem with counters being off under certain circumstances.
*
* Revision 1.19 1993/09/30 16:01:20 rik
* Fixed problem with processing DOSMODE. Previously, I would eat a 0xFF, there
* it were in the actual data stream.
*
* Revision 1.18 1993/09/07 14:28:54 rik
* Ported necessary code to work properly with DEC Alpha Systems running NT.
* This was primarily changes to accessing the memory mapped controller.
*
* Revision 1.17 1993/09/01 11:02:50 rik
* Ported code over to use READ/WRITE_REGISTER functions for accessing
* memory mapped data. This is required to support computers which don't run
* in 32bit mode, such as the DEC Alpha which runs in 64 bit mode.
*
* Revision 1.16 1993/07/16 10:25:03 rik
* Fixed problem with NULL_STRIPPING.
*
* Revision 1.15 1993/07/03 09:34:03 rik
* Added simple fix for LSRMST missing modem status events when there wasn't
* a read buffer available to place the change into the buffer.
*
* Added some debugging information which will only be turned on if the
* #define CONFIRM_CONTROLLER_ACCESS is defined.
*
* Revision 1.14 1993/06/25 09:24:53 rik
* Added better support for the Ioctl LSRMT. It should be more accurate
* with regard to Line Status and Modem Status information with regard
* to the actual data being received.
*
* Revision 1.13 1993/06/14 14:43:37 rik
* Corrected a problem with reference count in the read interval timeout
* routine. Also moved where a spinlock was being released because of
* an invalid assumption of the state of the spinlock at its location.
*
* Revision 1.12 1993/06/06 14:53:36 rik
* Added better support for errors such as Breaks, parity, and framing.
*
* Tightened up a possible window in the DigiCancelCurrentRead function
* with regards to spinlocks.
*
* Revision 1.11 1993/05/20 16:18:52 rik
* Started to added support for monitoring data stream for line status register
* problems.
*
* Revision 1.10 1993/05/18 05:17:35 rik
* Implemented new timeouts, used primarily for the OS/2 subsystem.
*
* Changed total timeout to take into effect the number of bytes all ready
* received.
*
* Changed the interval timeout to be more accurate for longer timeout delays.
*
* Revision 1.9 1993/05/09 09:29:10 rik
* Changed the device name printed out in debugging output.
*
* Started to keep track of what the first status to it can be returned.
*
* Revision 1.8 1993/03/08 07:14:18 rik
* Added more debugging information for better flow debugging.
*
* Revision 1.7 1993/03/01 16:04:35 rik
* Changed a debugging output message.
*
* Revision 1.6 1993/02/25 19:11:29 rik
* Added better debugging for tracing read requests.
*
* Revision 1.5 1993/02/04 12:25:00 rik
* Updated DigiDump to include DIGIREAD parameter in certain circumstances.
*
* Revision 1.4 1993/01/22 12:45:46 rik
* *** empty log message ***
*
* Revision 1.3 1992/12/10 16:22:02 rik
* Changed DigiDump messages.
*
* Revision 1.2 1992/11/12 12:53:07 rik
* Changed to better support timeouts and multi-processor platfor
* basically rewrote how I now do the reads.
*
* Revision 1.1 1992/10/28 21:40:50 rik
* Initial revision
*
--*/
#include "header.h"
#ifndef _READ_DOT_C
# define _READ_DOT_C
static char RCSInfo_ReadDotC[] = "$Header: /Components/Windows/NT/Async/FEP5/read.c 1 3/04/96 12:20p Stana $";
#endif
/****************************************************************************/
/* Local Prototypes */
/****************************************************************************/
NTSTATUS ProcessSlowRead( IN PDIGI_DEVICE_EXTENSION DeviceExt,
IN PUCHAR ReadBuffer,
IN USHORT ReadBufferMax,
IN PUSHORT BytesReadFromController,
IN PUSHORT BytesPlacedInReadBuffer,
IN USHORT Rout,
IN USHORT RxRead,
IN USHORT Rmax,
IN PKIRQL OldIrql );
VOID DigiCancelCurrentRead( PDEVICE_OBJECT DeviceObject, PIRP Irp );
NTSTATUS ReadRxBuffer( IN PDIGI_DEVICE_EXTENSION DeviceExt,
IN PKIRQL pOldIrql )
/*++
Routine Description:
Will read data from the device receive buffer on a Digi controller. If
there is no data in the receive queue, it will queue up the request
until the buffer is full, or a time out occurs.
Arguments:
DeviceExt - a pointer to the device object associated with this read
request.
pOldIrql - Pointer to KIRQL used to acquire the device extensions
spinlock
MapRegisterBase - NULL, not used
Context - NULL pointer, not used
Return Value:
STATUS_SUCCESS - Was able to complete the current read request.
STATUS_PENDING - Was unable to complete the current read request.
--*/
{
PDIGI_CONTROLLER_EXTENSION ControllerExt;
PIRP Irp;
PFEP_CHANNEL_STRUCTURE ChInfo;
PUCHAR rx, ReadBuffer;
USHORT Rin, Rout, Rmax;
USHORT RxSize, RxRead;
USHORT nBytesRead = 0;
PIO_STACK_LOCATION IrpSp;
BOOLEAN IrpFulFilled = FALSE;
NTSTATUS Status = STATUS_PENDING;
PLIST_ENTRY ReadQueue = &DeviceExt->ReadQueue;
Irp = CONTAINING_RECORD( ReadQueue->Flink,
IRP,
Tail.Overlay.ListEntry );
DigiDump( (DIGIREAD|DIGIFLOW), (" Entering ReadRxBuffer: port = %s\tIRP = 0x%x\n",
DeviceExt->DeviceDbgString, Irp) );
ControllerExt = (PDIGI_CONTROLLER_EXTENSION)(DeviceExt->ParentControllerExt);
//
// Always update the LastReadTime variable in the device extensions.
// We only get here for two reasons:
//
// 1) Initial read request time. LastReadTime will be updated again
// just before the interval timer is set.
//
// 2) We are notified by the controller that there is data available on
// the controller.
//
KeQuerySystemTime( &DeviceExt->LastReadTime );
IrpSp = IoGetCurrentIrpStackLocation( Irp );
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
DeviceExt->ChannelInfo.Offset);
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
Rout = READ_REGISTER_USHORT( &ChInfo->rout );
Rin = READ_REGISTER_USHORT( &ChInfo->rin );
Rmax = READ_REGISTER_USHORT( &ChInfo->rmax );
DisableWindow( ControllerExt );
DigiDump( DIGIREAD, (" Rin = 0x%hx\tRout = 0x%hx\n"
" RdReq = 0x%x\tReadOffset = 0x%x\tLength = 0x%x\n",
Rin, Rout,
IrpSp->Parameters.Read.Length, Irp->IoStatus.Information,
IrpSp->Parameters.Read.Length - Irp->IoStatus.Information ) );
//
// First look and see if there is data available. If there isn't enough
// data to satisify the request, then place this request on the queue.
//
RxSize = NBytesInRecvBuffer( ControllerExt, DeviceExt );
if( RxSize == 0 )
goto ReadRxBufferExit;
//
// Now determine how much we should read from the controller. We should
// read enough to fulfill this Irp's read request, up to the size of
// the controllers buffer or the amount of data available.
//
if( IrpSp->Parameters.Read.Length - Irp->IoStatus.Information
<= (ULONG)RxSize )
{
//
// We can fulfill this read request.
//
IrpFulFilled = TRUE;
RxRead = (USHORT)(IrpSp->Parameters.Read.Length - Irp->IoStatus.Information);
}
else
{
IrpFulFilled = FALSE;
RxRead = RxSize;
}
ASSERT( RxRead <= Rmax );
//
// Set up where we are going to start reading from the controller.
//
rx = (PUCHAR)( ControllerExt->VirtualAddress +
DeviceExt->RxSeg.Offset +
Rout );
ReadBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer + Irp->IoStatus.Information;
if( DeviceExt->EscapeChar ||
(DeviceExt->FlowReplace & SERIAL_NULL_STRIPPING) ||
(DeviceExt->WaitMask & SERIAL_EV_ERR) )
{
USHORT BytesReadFromController, BytesPlacedInReadBuffer;
//
// We are forced to do a slow, one byte at a time read from
// the controller.
//
ProcessSlowRead( DeviceExt, ReadBuffer,
(USHORT)(IrpSp->Parameters.Read.Length - Irp->IoStatus.Information),
&BytesReadFromController,
&BytesPlacedInReadBuffer,
Rout, RxRead, Rmax, pOldIrql );
nBytesRead = BytesReadFromController;
ASSERT( BytesPlacedInReadBuffer <=
(IrpSp->Parameters.Read.Length - Irp->IoStatus.Information) );
Irp->IoStatus.Information += BytesPlacedInReadBuffer;
if( Irp->IoStatus.Information == IrpSp->Parameters.Read.Length )
{
IrpFulFilled = TRUE;
}
}
else
{
//
// All right, we can try to move data across at hopefully
// lightning speed!
//
//
// We need to determine if the read wraps our Circular
// buffer. RxSize should be the amount of buffer space which needs to be
// read, So we need to figure out if it will wrap.
//
// Do we need to check RXFLAG here??? SWA
if( (Rout + RxRead) > Rmax )
{
USHORT Temp;
//
// Yep, we need to wrap.
//
Temp = Rmax - Rout + 1;
ASSERT( Temp <= Rmax );
DigiDump( DIGIREAD, (" rx:0x%x\tReadBuffer:0x%x\tTemp:%hd...Wrapping circular buffer....\n",
rx, ReadBuffer, Temp) );
EnableWindow( ControllerExt, DeviceExt->RxSeg.Window );
READ_REGISTER_BUFFER_UCHAR( rx, ReadBuffer, Temp );
DisableWindow( ControllerExt );
DigiDump( DIGIINFO, (" %s\n", ReadBuffer) );
// Fix up all the values.
nBytesRead += Temp;
ReadBuffer += Temp;
RxRead -= Temp;
ASSERT( RxRead <= Rmax );
rx = (PUCHAR)( ControllerExt->VirtualAddress +
DeviceExt->RxSeg.Offset );
}
DigiDump( DIGIREAD, (" rx:0x%x\tReadBuffer:0x%x\tRxRead:%hd\n",
rx, ReadBuffer, RxRead) );
EnableWindow( ControllerExt, DeviceExt->RxSeg.Window );
READ_REGISTER_BUFFER_UCHAR( rx, ReadBuffer, RxRead );
DisableWindow( ControllerExt );
DigiDump( DIGIINFO, (" %s\n", ReadBuffer) );
nBytesRead += RxRead;
Irp->IoStatus.Information += nBytesRead;
}
DeviceExt->PreviousRxChar = (ULONG)Rin;
// Update the Rx Pointer.
Rout = (Rout + nBytesRead) & Rmax;
DigiDump( DIGIREAD, (" BytesRead = %d\tNew Rout = 0x%hx\n", nBytesRead, Rout) );
if( IrpFulFilled )
{
//
// We have completed this irp.
//
Status = STATUS_SUCCESS;
goto ReadRxBufferExit;
}
ReadRxBufferExit:
if( nBytesRead )
{
PSERIAL_XOFF_COUNTER Xc;
//
// We actually read some data, update the downstairs pointer.
//
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
WRITE_REGISTER_USHORT( &ChInfo->rout, Rout );
DisableWindow( ControllerExt );
// The act of receiving data might complete an XOFF_COUNTER on the WriteQueue.
Xc = DeviceExt->pXoffCounter;
if( Xc )
{
if( nBytesRead <= DeviceExt->XcPreview )
{
DeviceExt->XcPreview -= nBytesRead;
// DigiServiceEvent(IDATA) already decremented Xc->Counter.
}
else
{
USHORT credit = nBytesRead - DeviceExt->XcPreview;
if( credit < Xc->Counter )
{
Xc->Counter -= credit;
}
else
{
// XOFF_COUNTER is complete.
#if DBG
Xc->Counter = 0; // Looks a little nicer...
#endif
DigiDump( (DIGIWRITE|DIGIDIAG1), ("ReadRxBuffer is completing XOFF_COUNTER\n") );
DigiTryToCompleteIrp( DeviceExt, pOldIrql, STATUS_SUCCESS,
&DeviceExt->WriteQueue, NULL, &DeviceExt->WriteRequestTotalTimer, StartWriteRequest );
KeAcquireSpinLock( &DeviceExt->ControlAccess, pOldIrql );
}
}
}
} // nBytesRead
//
// If we came through this routine, then lets ask to be notified when
// there is data in this devices receive buffer.
//
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
WRITE_REGISTER_UCHAR( &ChInfo->idata, TRUE );
DisableWindow( ControllerExt );
DigiDump( (DIGIFLOW|DIGIREAD), (" Exiting ReadRxBuffer: port = %s\n",
DeviceExt->DeviceDbgString) );
return( Status );
} // end ReadRxBuffer
NTSTATUS ProcessSlowRead( IN PDIGI_DEVICE_EXTENSION DeviceExt,
IN PUCHAR pReadBuffer,
IN USHORT ReadBufferMax,
IN PUSHORT pBytesReadFromController,
IN PUSHORT pBytesPlacedInReadBuffer,
IN USHORT Rout,
IN USHORT RxRead,
IN USHORT Rmax,
IN PKIRQL pOldIrql )
/*++
Routine Description:
This routine will read one byte of data at a time from the controller
and process it according to the current settings and events
requested. This function would be called to process anything which
needs to have the incoming data stream looked at.
Arguments:
DeviceExt - a pointer to the device extension associated with this read
request.
ReadBuffer - pointer to where we place the incoming data, usually
the read IRP buffer.
ReadBufferMax - the maximum number of bytes which can be placed
in the ReadBuffer.
BytesReadFromController - pointer which will show how many bytes
were actually read from the controller.
BytesPlacedInReadBuffer - pointer which indicates how many bytes
are placed into the read buffer.
Rout - value from controller's memory for Rout
RxRead - number of bytes we should read from the controller and
place in the ReadBuffer.
This value should not be larger than Rmax.
Rmax - value from ports Channel Info structure for Rmax.
Return Value:
STATUS_SUCCESS - always returns
--*/
{
PDIGI_CONTROLLER_EXTENSION ControllerExt;
UCHAR LineStatus, ReceivedErrorChar, SpecialChar;
PFEP_CHANNEL_STRUCTURE ChInfo;
PUCHAR pControllerBuffer;
ULONG EventReason;
USHORT DosMode, Rin;
unsigned int BytesReadFromController=0, BytesPlacedInReadBuffer=0;
#define BETWEEN_RING_POINTERS(out, in, max, p) ((((p)-(out))&(max)) < (((in)-(out))&(max)))
DigiDump( (DIGISLOWREAD|DIGIREAD|DIGIFLOW), (" Entering ProcessSlowRead: port = %s\n",
DeviceExt->DeviceDbgString) );
DigiDump( (DIGISLOWREAD|DIGIREAD), (" ReadBufferMax = 0x%hx, Rout = 0x%hx, RxRead = 0x%hx, Rmax = 0x%hx\n",
ReadBufferMax, Rout, RxRead, Rmax) );
ControllerExt = (PDIGI_CONTROLLER_EXTENSION)(DeviceExt->ParentControllerExt);
pControllerBuffer = ControllerExt->VirtualAddress + DeviceExt->RxSeg.Offset;
EventReason = 0;
SpecialChar = DeviceExt->SpecialChars.EventChar;
if( DeviceExt->PreviousMSRByte && DeviceExt->EscapeChar )
{
if( (BytesPlacedInReadBuffer + 4) > ReadBufferMax )
return( STATUS_SUCCESS );
*pReadBuffer++ = DeviceExt->EscapeChar;
BytesPlacedInReadBuffer++;
*pReadBuffer++ = SERIAL_LSRMST_MST;
BytesPlacedInReadBuffer++;
*pReadBuffer++ = (UCHAR)(DeviceExt->PreviousMSRByte);
DigiDump( (DIGISLOWREAD|DIGIERRORS), (" PreviousMSRByte = 0x%x\n",
DeviceExt->PreviousMSRByte) );
DeviceExt->PreviousMSRByte = 0;
}
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
DeviceExt->ChannelInfo.Offset);
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
DosMode = READ_REGISTER_USHORT( &ChInfo->iflag );
Rin = READ_REGISTER_USHORT( &ChInfo->rin );
DisableWindow( ControllerExt );
DosMode &= IFLAG_DOSMODE;
EnableWindow( ControllerExt, DeviceExt->RxSeg.Window );
while( RxRead-- )
{
UCHAR ReceivedByte;
ReceivedByte = READ_REGISTER_UCHAR( (pControllerBuffer + Rout) );
Rout++;
Rout &= Rmax;
BytesReadFromController++;
DigiDump( DIGISLOWREAD, (" ReceivedByte = 0x%x, Rout = 0x%x, RxRead = 0x%x, DosMode = 0x%x\n",
ReceivedByte, Rout, RxRead, DosMode) );
if( ReceivedByte != 0xFF || !DosMode )
{
//
// This is just normal data, do any processing
// of the data necessary.
//
ProcessValidData:
if( (DeviceExt->WaitMask & SERIAL_EV_RXCHAR) &&
(DeviceExt->PreviousRxChar != (ULONG)Rin) )
{
EventReason |= SERIAL_EV_RXCHAR;
}
if( (DeviceExt->WaitMask & SERIAL_EV_RXFLAG) &&
(ReceivedByte == SpecialChar) &&
(DeviceExt->UnscannedRXFLAGPosition == MAXULONG ||
BETWEEN_RING_POINTERS((USHORT)DeviceExt->UnscannedRXFLAGPosition, Rin, Rmax, Rout)) )
{
DigiDump( (DIGISLOWREAD|DIGIEVENT), (" SERIAL_EV_RXFLAG satisfied!!\n") );
EventReason |= SERIAL_EV_RXFLAG;
}
if( (DeviceExt->FlowReplace & SERIAL_NULL_STRIPPING) &&
(!ReceivedByte) )
{
continue;
}
if( DeviceExt->EscapeChar &&
(DeviceExt->EscapeChar == ReceivedByte) )
{
//
// We have received the same character as our escape character
//
DigiDump( DIGISLOWREAD, (" EscapeChar == ReceivedByte!\n") );
if( BytesPlacedInReadBuffer >= ReadBufferMax )
break;
*pReadBuffer++ = ReceivedByte;
BytesPlacedInReadBuffer++;
if( BytesPlacedInReadBuffer >= ReadBufferMax )
break;
*pReadBuffer++ = SERIAL_LSRMST_ESCAPE;
BytesPlacedInReadBuffer++;
}
else
{
//
// Place the character in the read buffer.
//
if( BytesPlacedInReadBuffer >= ReadBufferMax )
break;
*pReadBuffer++ = ReceivedByte;
BytesPlacedInReadBuffer++;
}
}
else
{
//
// We have at least two more bytes of data available on
// the controller.
//
// AND, the data is Line Status information.
//
LineStatus = READ_REGISTER_UCHAR( (pControllerBuffer + Rout) );
Rout++;
Rout &= Rmax;
BytesReadFromController++;
DigiDump( DIGISLOWREAD, (" LineStatus = 0x%x, Rout = 0x%x, RxRead = 0x%x\n",
LineStatus, Rout, RxRead) );
if( LineStatus == 0xFF )
{
//
// We actually received the byte 0xFF. Place it in the
// read buffer.
//
DigiDump( DIGISLOWREAD, (" Received actual 0xFF in data stream!\n") );
goto ProcessValidData;
}
else
{
//
// There is actually a Line Status byte waiting for
// us to proecess it.
//
ReceivedErrorChar = READ_REGISTER_UCHAR( (pControllerBuffer + Rout) );
Rout++;
Rout &= Rmax;
BytesReadFromController++;
DigiDump( DIGISLOWREAD, (" ReceivedErrorChar = 0x%x, Rout = 0x%x, RxRead = 0x%x\n",
ReceivedErrorChar, Rout, RxRead) );
//
// Process the LineStatus information
//
if( LineStatus & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT |
SERIAL_LSR_DR) )
{
//
// There is a Line Status Error
//
if( DeviceExt->EscapeChar )
{
DigiDump( DIGISLOWREAD, (" LSRMST_INSERT mode is ON!\n") );
//
// IOCTL_SERIAL_LSRMST_INSERT mode has been turned on, so we have
// to look at every character from the controller
//
if( (BytesPlacedInReadBuffer + 1) > ReadBufferMax )
break;
*pReadBuffer++ = DeviceExt->EscapeChar;
BytesPlacedInReadBuffer++;
if( (BytesPlacedInReadBuffer + 1) > ReadBufferMax )
break;
*pReadBuffer++ = SERIAL_LSRMST_LSR_DATA;
BytesPlacedInReadBuffer++;
if( (BytesPlacedInReadBuffer + 1) > ReadBufferMax )
break;
*pReadBuffer++ = LineStatus;
BytesPlacedInReadBuffer++;
if( (BytesPlacedInReadBuffer + 1) > ReadBufferMax )
break;
*pReadBuffer++ = ReceivedErrorChar;
BytesPlacedInReadBuffer++;
}
if( LineStatus & SERIAL_LSR_OE )
{
EventReason |= SERIAL_EV_ERR;
DeviceExt->ErrorWord |= SERIAL_ERROR_OVERRUN;
InterlockedIncrement(&DeviceExt->PerfData.SerialOverrunErrorCount);
if( DeviceExt->FlowReplace & SERIAL_ERROR_CHAR )
{
if( (BytesPlacedInReadBuffer + 1) > ReadBufferMax )
break;
*pReadBuffer++ = DeviceExt->SpecialChars.ErrorChar;
BytesPlacedInReadBuffer++;
}
}
if( LineStatus & SERIAL_LSR_BI )
{
EventReason |= SERIAL_EV_BREAK;
DeviceExt->ErrorWord |= SERIAL_ERROR_BREAK;
if( DeviceExt->FlowReplace & SERIAL_BREAK_CHAR )
{
if( (BytesPlacedInReadBuffer + 1) > ReadBufferMax )
break;
*pReadBuffer++ = DeviceExt->SpecialChars.ErrorChar;
BytesPlacedInReadBuffer++;
}
}
else
{
//
// Framing errors and parity errors should only count
// when there isn't a break.
//
if( (LineStatus & SERIAL_LSR_PE) ||
(LineStatus & SERIAL_LSR_FE) )
{
EventReason |= SERIAL_EV_ERR;
if( LineStatus & SERIAL_LSR_PE )
{
DeviceExt->ErrorWord |= SERIAL_ERROR_PARITY;
InterlockedIncrement(&DeviceExt->PerfData.ParityErrorCount);
}
if( LineStatus & SERIAL_LSR_FE )
{
DeviceExt->ErrorWord |= SERIAL_ERROR_FRAMING;
InterlockedIncrement(&DeviceExt->PerfData.FrameErrorCount);
}
if( DeviceExt->FlowReplace & SERIAL_ERROR_CHAR )
{
if( (BytesPlacedInReadBuffer + 1) > ReadBufferMax )
break;
*pReadBuffer++ = DeviceExt->SpecialChars.ErrorChar;
BytesPlacedInReadBuffer++;
}
}
}
if( DeviceExt->ControlHandShake & SERIAL_ERROR_ABORT )
{
//
// Since there was a line status error indicated, we
// are expected to flush our buffers, and cancel
// all current read and write requests.
//
}
}
}
}
}
DisableWindow( ControllerExt );
DigiDump( (DIGIREAD|DIGISLOWREAD), (" BytesPlacedInReadBuffer = 0x%x, BytesReadFromController = 0x%x\n",
BytesPlacedInReadBuffer, BytesReadFromController) );
if (DeviceExt->WaitMask&SERIAL_EV_RXFLAG)
DeviceExt->UnscannedRXFLAGPosition = Rout;
*pBytesPlacedInReadBuffer = (USHORT)BytesPlacedInReadBuffer;
*pBytesReadFromController = (USHORT)BytesReadFromController;
if( EventReason )
{
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
DigiSatisfyEvent( ControllerExt, DeviceExt, EventReason );
KeAcquireSpinLock( &DeviceExt->ControlAccess, pOldIrql );
}
DigiDump( (DIGISLOWREAD|DIGIREAD|DIGIFLOW), (" Exiting ProcessSlowRead: port = %s\n",
DeviceExt->DeviceDbgString) );
return( STATUS_SUCCESS );
} // end ProcessSlowRead
NTSTATUS StartReadRequest( IN PDIGI_CONTROLLER_EXTENSION ControllerExt,
IN PDIGI_DEVICE_EXTENSION DeviceExt,
IN PKIRQL pOldIrql )
/*++
Routine Description:
This routine assumes the head of the DeviceExt->ReadQueue is the current
Irp to process. We will try to process as many of the Irps as
possible until we exhaust the list, or we can't complete the
current Irp.
NOTE: I assume the DeviceExt->ControlAccess spin lock is acquired
before this routine is called.
Arguments:
ControllerExt - a pointer to the controller extension associated with
this read request.
DeviceExt - a pointer to the device extension associated with this read
request.
pOldIrql - a pointer to the IRQL associated with the current spin lock
of the device extension.
Return Value:
--*/
{
PLIST_ENTRY ReadQueue = &DeviceExt->ReadQueue;
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN bFirstStatus = TRUE;
DigiDump( (DIGIFLOW|DIGIREAD), ("Entering StartReadRequest: port = %s\n",
DeviceExt->DeviceDbgString) );
ASSERT( !IsListEmpty( ReadQueue ) );
do
{
PIRP Irp;
KIRQL OldCancelIrql;
NTSTATUS ReadStatus;
Irp = CONTAINING_RECORD( ReadQueue->Flink,
IRP,
Tail.Overlay.ListEntry );
if( Irp->IoStatus.Information != MAXULONG )
{
// Someone else already started this IRP.
break;
}
IoAcquireCancelSpinLock( &OldCancelIrql );
IoSetCancelRoutine( Irp, NULL );
// Kluge alert! Rik changed this so that the polling loop wouldn't see
// this IRP as PENDING and complete it. ReadStatus needs to be set to
// some other value before we return from this function, or it will
// hang up the reads. The right fix was to patch the spinlock hole that
// allowed the IRP to be completed twice, but that won't happen 'til
// I get time. (heavy sigh) -SWA
DeviceExt->ReadStatus = SERIAL_COMPLETE_READ_PROCESSING;
IoReleaseCancelSpinLock( OldCancelIrql );
Irp->IoStatus.Information = 0;
DeviceExt->PreviousReadCount = 0;
ReadStatus = ReadRxBuffer( DeviceExt, pOldIrql );
DigiDump( DIGIREAD, (" Initial ReadRxBuffer status = 0x%x\n"
" Irp->IoStatus.Information = %u\n",
ReadStatus,
Irp->IoStatus.Information) );
if( ReadStatus == STATUS_PENDING )
{
BOOLEAN UseTotalTimer = FALSE;
BOOLEAN UseIntervalTimer = FALSE;
ULONG ReadIntervalTimeout, ReadTotalTimeoutConstant, ReadTotalTimeoutMultiplier;
//
// Calculate the timeout value needed for the request. Note that
// the values stored in the timeout record are in milliseconds.
//
// Get the current timeout values, we can access this now be
// cause we are under the protection of the spin lock all ready.
//
ReadIntervalTimeout = DeviceExt->Timeouts.ReadIntervalTimeout;
ReadTotalTimeoutConstant = DeviceExt->Timeouts.ReadTotalTimeoutConstant;
ReadTotalTimeoutMultiplier = DeviceExt->Timeouts.ReadTotalTimeoutMultiplier;
if( ReadIntervalTimeout == MAXULONG )
{
//
// We need to do special 'return quickly' stuff here.
//
// 1) If both constant and multiplier are
// 0 then we return immediately with whatever
// we've got, even if it was zero.
//
// 2) If constant and multiplier are not MAXULONG
// then return immediately if any characters
// are present, but if nothing is there, then
// use the timeouts as specified.
//
// 3) If multiplier is MAXULONG then do as in
// "2" but return when the first character
// arrives.
//
if( ReadTotalTimeoutConstant == 0
&& ReadTotalTimeoutMultiplier == 0 )
{
//
// 1) If both constant and multiplier are
// 0 then we return immediately with whatever
// we've got, even if it was zero.
//
DigiDump( (DIGIFLOW|DIGIREAD), (" Should return immediately, even if zero bytes.\n") );
goto CompleteIrp;
}
else if( ReadTotalTimeoutConstant != MAXULONG )
{
if( ReadTotalTimeoutMultiplier != MAXULONG )
{
//
// 2) If constant and multiplier are not MAXULONG
// then return immediately if any characters
// are present, but if nothing is there, then
// use the timeouts as specified.
//
DigiDump( (DIGIFLOW|DIGIREAD), (" Return if bytes available, otherwise, use normal timeouts.\n") );
if( Irp->IoStatus.Information != 0 )
goto CompleteIrp;
UseTotalTimer = TRUE;
}
else // ReadTotalTimeoutMultiplier == MAXULONG
{
//
// 3) If multiplier is MAXULONG then do as in
// "2" but return when the first character
// arrives.
//
DigiDump( (DIGIFLOW|DIGIREAD), (" Return if bytes available, otherwise, wait for 1 byte to arrive.\n") );
if( Irp->IoStatus.Information != 0 )
goto CompleteIrp;
// Force immediate completion on next byte.
IoGetCurrentIrpStackLocation( Irp )->Parameters.Read.Length = 1;
UseTotalTimer = TRUE;
ReadTotalTimeoutMultiplier = 0; // MAXULONG would mess things up
}
}
// DH else ???? (SERIAL.SYS doesn't handle this case, either.)
}
else // ReadIntervalTimeout != MAXULONG
{
//
// Calculate interval timeout.
//
if( ReadIntervalTimeout )
{
UseIntervalTimer = TRUE;
DigiDump( (DIGIFLOW|DIGIREAD), (" Should use interval timer.\n") );
DigiDump( (DIGIFLOW|DIGIREAD), (" ReadIntervalTimeout = 0x%x\n",
ReadIntervalTimeout ) );
#if rmm < 807
DeviceExt->IntervalTime = RtlEnlargedIntegerMultiply( ReadIntervalTimeout, -10000 );
#else
DeviceExt->IntervalTime.QuadPart = Int32x32To64(ReadIntervalTimeout, -10000);
#endif
}
//
// If both the multiplier and the constant are
// zero then don't do any total timeout processing.
//
if( ReadTotalTimeoutMultiplier ||
ReadTotalTimeoutConstant )
{
UseTotalTimer = TRUE;
}
} // ReadIntervalTimeout != ULONG
//
// Head Irp is still being processed.
//
// Quick check to make sure this Irp hasn't been cancelled.
IoAcquireCancelSpinLock( &OldCancelIrql );
if( Irp->Cancel )
{
BOOLEAN discoveredIrp;
IoReleaseCancelSpinLock( OldCancelIrql );
DigiRemoveIrp( ReadQueue );
discoveredIrp = !IsListEmpty( ReadQueue );
DeviceExt->ReadStatus = SERIAL_COMPLETE_READ_CANCEL;
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
if( bFirstStatus )
Status = STATUS_CANCELLED;
KeAcquireSpinLock( &DeviceExt->ControlAccess, pOldIrql );
if( discoveredIrp )
goto StartNextIrp;
else
break; // DigiStartIrpRequest will detect the empty Q and start the next IRP.
}
else // not canceled
{
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
DIGI_INIT_REFERENCE( Irp );
//
// Increment because the CancelRoutine knows about this IRP.
//
DIGI_INC_REFERENCE( Irp );
IoSetCancelRoutine( Irp, DigiCancelCurrentRead );
IoReleaseCancelSpinLock( OldCancelIrql );
DeviceExt->ReadStatus = STATUS_PENDING;
DigiDump( DIGIREAD, (" unable to satisfy read at request time.\n") );
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( DIGIREAD, (" Start Read SystemTime = %u:%u\n",
CurrentSystemTime.HighPart,
CurrentSystemTime.LowPart) );
if( UseTotalTimer )
{
LARGE_INTEGER TotalTime;
//
// We should readjust the total timer for any characters
// which may have been available.
//
#if rmm < 807
TotalTime = RtlEnlargedUnsignedMultiply(
IoGetCurrentIrpStackLocation( Irp )->Parameters.Read.Length -
Irp->IoStatus.Information,
ReadTotalTimeoutMultiplier );
TotalTime = RtlLargeIntegerAdd(
TotalTime,
RtlConvertUlongToLargeInteger(
ReadTotalTimeoutConstant ) );
TotalTime = RtlExtendedIntegerMultiply(
TotalTime, -10000 );
#else
TotalTime.QuadPart = ((LONGLONG)(UInt32x32To64(
IoGetCurrentIrpStackLocation( Irp )->Parameters.Read.Length -
Irp->IoStatus.Information,
ReadTotalTimeoutMultiplier ) +
ReadTotalTimeoutConstant)) *
-10000;
#endif
DigiDump( DIGIREAD, (" Should use Read total timer.\n"
" Read MultiplierVal = %u\n"
" Read Constant = %u\n"
" Read TotalTime = 0x%x:%.8x\n",
ReadTotalTimeoutMultiplier, ReadTotalTimeoutConstant,
TotalTime.HighPart, TotalTime.LowPart) );
DIGI_INC_REFERENCE( Irp );
KeSetTimer( &DeviceExt->ReadRequestTotalTimer,
TotalTime, &DeviceExt->TotalReadTimeoutDpc );
}
if( UseIntervalTimer )
{
DigiDump( DIGIREAD, (" Should use Read interval timer.\n"
" Read interval time = 0x%x:%.8x\n",
DeviceExt->IntervalTime.HighPart,
DeviceExt->IntervalTime.LowPart) );
DIGI_INC_REFERENCE( Irp );
KeQuerySystemTime( &DeviceExt->LastReadTime );
KeSetTimer( &DeviceExt->ReadRequestIntervalTimer,
DeviceExt->IntervalTime,
&DeviceExt->IntervalReadTimeoutDpc );
}
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting StartReadRequest: port = %s\n",
DeviceExt->DeviceDbgString) );
if( bFirstStatus )
Status = STATUS_PENDING;
return( Status );
} // not canceled
ASSERT( FALSE ); // not reached
}
else // ReadStatus == STATUS_SUCCESS
{
BOOLEAN discoveredIrp;
CHAR boost;
CompleteIrp:;
DigiDump( DIGIREAD, (" completing read request on 1st attempt\n"
" #bytes completing = %d\n",
Irp->IoStatus.Information ) );
#if DBG
if( DigiDebugLevel & DIGIRXTRACE )
{
PUCHAR Temp;
ULONG i;
Temp = Irp->AssociatedIrp.SystemBuffer;
DigiDump( DIGIRXTRACE, ("Read buffer contains: %s",
DeviceExt->DeviceDbgString) );
for( i = 0;
i < Irp->IoStatus.Information;
i++ )
{
if( (i & 15) == 0 )
DigiDump( DIGIRXTRACE, ( "\n\t") );
DigiDump( DIGIRXTRACE, ( "-%02x", Temp[i]) );
}
DigiDump( DIGIRXTRACE, ("\n") );
}
#endif
//
// We have completed the Irp before from the start so no
// timers or cancels were associcated with it.
//
DigiRemoveIrp( ReadQueue );
discoveredIrp = !IsListEmpty( ReadQueue );
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
ExInterlockedAddUlong(&DeviceExt->ParentControllerExt->PerfData.BytesRead,
Irp->IoStatus.Information,
&DeviceExt->ParentControllerExt->PerfLock);
ExInterlockedAddUlong(&DeviceExt->PerfData.BytesRead,
Irp->IoStatus.Information,
&DeviceExt->PerfLock);
if( Irp->IoStatus.Status == STATUS_SUCCESS )
{
boost = IO_NO_INCREMENT; // never pending, so no boost
}
else
{
boost = IO_SERIAL_INCREMENT;
ASSERT( Irp->IoStatus.Status == STATUS_PENDING );
Irp->IoStatus.Status = STATUS_SUCCESS;
}
ASSERT(Irp->IoStatus.Information<=IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length);
DigiIoCompleteRequest( Irp, boost );
if( bFirstStatus )
Status = STATUS_SUCCESS;
KeAcquireSpinLock( &DeviceExt->ControlAccess, pOldIrql );
if( discoveredIrp )
goto StartNextIrp;
else
break; // DigiStartIrpRequest will detect the empty Q and start the next IRP.
}
ASSERT( FALSE ); // not reached
StartNextIrp:;
bFirstStatus = FALSE;
} while( !IsListEmpty( ReadQueue ) );
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting StartReadRequest: port = %s\n",
DeviceExt->DeviceDbgString) );
return( Status );
} // end StartReadRequest
VOID DigiReadTimeout( IN PKDPC Dpc, IN PVOID DeferredContext,
IN PVOID SystemContext1, IN PVOID SystemContext2 )
/*++
Routine Description:
This routine is used to complete a read because its total
timer has expired.
Arguments:
Dpc - Not Used.
DeferredContext - Really points to the device extension.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
Return Value:
None.
--*/
{
KIRQL OldIrql = DISPATCH_LEVEL;
PDIGI_DEVICE_EXTENSION DeviceExt=DeferredContext;
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
DigiDump( (DIGIFLOW|DIGIREAD), ("Entering DigiReadTimeout: port = %s\n",
DeviceExt->DeviceDbgString) );
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( DIGIREAD, (" Total Read Timeout, SystemTime = %u:%u\n", CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
DeviceExt->ReadStatus = SERIAL_COMPLETE_READ_TOTAL;
DigiDump( DIGIREAD, (" Total Read Timeout!\n") );
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
STATUS_TIMEOUT, &DeviceExt->ReadQueue,
&DeviceExt->ReadRequestIntervalTimer,
&DeviceExt->ReadRequestTotalTimer,
StartReadRequest );
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting DigiReadTimeout: port = %s\n",
DeviceExt->DeviceDbgString) );
} // end DigiReadTimeout
VOID DigiIntervalReadTimeout( IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2 )
/*++
Routine Description:
This routine is used timeout the request if the time between
characters exceed the interval time. A global is kept in
the device extension that records the count of characters read
the last the last time this routine was invoked (This dpc
will resubmit the timer if the count has changed). If the
count has not changed then this routine will attempt to complete
the irp. Note the special case of the last count being zero.
The timer isn't really in effect until the first character is
read.
Arguments:
Dpc - Not Used.
DeferredContext - Really points to the device extension.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
Return Value:
None.
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeferredContext;
KIRQL OldIrql = DISPATCH_LEVEL;
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
DigiDump( (DIGIFLOW|DIGIREAD), ("Entering DigiIntervalReadTimeout: port = %s\n",
DeviceExt->DeviceDbgString) );
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( DIGIREAD, (" Interval Read Timeout, SystemTime = %u:%u\n",
CurrentSystemTime.HighPart,
CurrentSystemTime.LowPart) );
KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
if( DeviceExt->ReadStatus == SERIAL_COMPLETE_READ_TOTAL )
{
//
// This value is only set by the total
// timer to indicate that it has fired.
// If so, then we should simply try to complete.
//
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
STATUS_TIMEOUT, &DeviceExt->ReadQueue,
&DeviceExt->ReadRequestIntervalTimer,
&DeviceExt->ReadRequestTotalTimer,
StartReadRequest );
}
else if( DeviceExt->ReadStatus == SERIAL_COMPLETE_READ_COMPLETE )
{
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
STATUS_SUCCESS, &DeviceExt->ReadQueue,
&DeviceExt->ReadRequestIntervalTimer,
&DeviceExt->ReadRequestTotalTimer,
StartReadRequest );
}
else if( DeviceExt->ReadStatus == SERIAL_COMPLETE_READ_CANCEL )
{
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
STATUS_CANCELLED, &DeviceExt->ReadQueue,
&DeviceExt->ReadRequestIntervalTimer,
&DeviceExt->ReadRequestTotalTimer,
StartReadRequest );
}
else
{
//
// We may actually need to timeout. If there aren't any more
// characters available on the controller, then we
// kill this read.
//
// If the read offset is not zero, then the interval timer has
// actually started.
//
PIRP Irp;
Irp = CONTAINING_RECORD( DeviceExt->ReadQueue.Flink,
IRP,
Tail.Overlay.ListEntry );
DigiDump( DIGIREAD, (" ReadOffset = %d\n", Irp->IoStatus.Information) );
if( Irp->IoStatus.Information )
{
if( Irp->IoStatus.Information != DeviceExt->PreviousReadCount )
{
DigiDump( DIGIREAD, (" Read Interval timeout reset because data has been read from controller since last timeout\n") );
//
// Characters have arrived since our last time out, and
// we have read them from the controller, so just reset
// the timer.
//
DeviceExt->PreviousReadCount = Irp->IoStatus.Information;
KeSetTimer( &DeviceExt->ReadRequestIntervalTimer,
DeviceExt->IntervalTime,
&DeviceExt->IntervalReadTimeoutDpc );
KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
}
else
{
//
// We potentially have a valid Interval Timeout. We need to
// look at the controllers receive buffer pointers to
// see if there is enough data to satify this request.
//
USHORT RxSize;
#if DBG
USHORT Rin, Rout;
PFEP_CHANNEL_STRUCTURE ChInfo;
#endif
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
PIO_STACK_LOCATION IrpSp;
//
// We could try looking down at the controller and see
// if there are any characters waiting.?????
//
DigiDump( DIGIREAD, (" Possible Read Interval Timeout!\n"
"There might be enough data on the controller!!\n") );
#if DBG
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
DeviceExt->ChannelInfo.Offset);
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
Rout = READ_REGISTER_USHORT( &ChInfo->rout );
Rin = READ_REGISTER_USHORT( &ChInfo->rin );
DisableWindow( ControllerExt );
#endif
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DigiDump( DIGIREAD, (" Rin = 0x%hx\tRout = 0x%hx\n"
" RdReq = 0x%x\tReadOffset = 0x%x\tLength = 0x%x\n",
Rin, Rout,
IrpSp->Parameters.Read.Length, Irp->IoStatus.Information,
IrpSp->Parameters.Read.Length - Irp->IoStatus.Information ) );
RxSize = NBytesInRecvBuffer( ControllerExt, DeviceExt );
if( (IrpSp->Parameters.Read.Length - Irp->IoStatus.Information)
> (ULONG)RxSize )
{
LARGE_INTEGER CurrentTime;
//
// We can't satisfy this read request, so determine if there
// really is a timeout.
//
KeQuerySystemTime( &CurrentTime );
#if rmm < 807
if (RtlLargeIntegerGreaterThanOrEqualTo(
RtlLargeIntegerSubtract( CurrentTime,
DeviceExt->LastReadTime ),
DeviceExt->IntervalTime ))
#else
if( (CurrentTime.QuadPart - DeviceExt->LastReadTime.QuadPart) >=
DeviceExt->IntervalTime.QuadPart )
#endif
{
DigiDump( DIGIREAD, (" Read real Interval timeout!!\n") );
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
STATUS_TIMEOUT, &DeviceExt->ReadQueue,
&DeviceExt->ReadRequestIntervalTimer,
&DeviceExt->ReadRequestTotalTimer,
StartReadRequest );
}
else
{
DigiDump( DIGIREAD, (" Resetting read interval timeout, 1st char not received\n") );
KeSetTimer( &DeviceExt->ReadRequestIntervalTimer,
DeviceExt->IntervalTime,
&DeviceExt->IntervalReadTimeoutDpc );
KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
}
}
else
{
//
// There are enough characters on the controller to
// satisfy this request, so lets do it!
//
NTSTATUS Status;
ASSERT( !IsListEmpty( &DeviceExt->ReadQueue ) );
// Re-use timeout reference count for to hold IRP across lock drop in ReadRxBuffer.
Status = ReadRxBuffer( DeviceExt, &OldIrql );
if( Status == STATUS_SUCCESS )
{
DigiDump( (DIGIFLOW|DIGIREAD), (" Read interval successfully completing Irp\n"
" #bytes completing = %d\n",
Irp->IoStatus.Information ) );
DeviceExt->ReadStatus = SERIAL_COMPLETE_READ_COMPLETE;
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
STATUS_SUCCESS, &DeviceExt->ReadQueue,
&DeviceExt->ReadRequestIntervalTimer,
&DeviceExt->ReadRequestTotalTimer,
StartReadRequest );
}
else
{
//
// We really shouldn't go through this path because
// we did a check before we called ReadRxBuffer to
// make sure there was enough data to complete
// this request!
//
DigiDump( (DIGIFLOW|DIGIREAD), (" Read interval timeout, with enough data to complete. We shouldn't be here!!!\n") );
ASSERT( Status == STATUS_SUCCESS );
DIGI_DEC_REFERENCE( Irp );
KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
}
}
}
}
else
{
//
// The timer doesn't start until we get the first character.
//
DigiDump( DIGIREAD, (" Resetting read interval timeout, 1st char not received\n") );
KeSetTimer( &DeviceExt->ReadRequestIntervalTimer,
DeviceExt->IntervalTime,
&DeviceExt->IntervalReadTimeoutDpc );
KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
}
}
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting DigiIntervalReadTimeout: port = %s\n",
DeviceExt->DeviceDbgString) );
}
VOID DigiCancelCurrentRead( PDEVICE_OBJECT DeviceObject, PIRP Irp )
/*++
Routine Description:
This routine is used to cancel the current read.
NOTE: The global cancel spin lock is acquired, so don't forget
to release it before returning.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP to be cancelled.
Return Value:
None.
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt;
KIRQL OldIrql;
IoReleaseCancelSpinLock( Irp->CancelIrql );
ASSERT( Irp->CancelRoutine == NULL );
DeviceExt = DeviceObject->DeviceExtension;
DigiDump( (DIGIFLOW|DIGIREAD), ("Entering DigiCancelCurrentRead: port = %s\n",
DeviceExt->DeviceDbgString) );
DigiDump( (DIGICANCELIRP|DIGIREAD), ( "Canceling read Irp! 0x%x\n",
Irp ) );
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
DeviceExt->ReadStatus = SERIAL_COMPLETE_READ_CANCEL;
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
STATUS_CANCELLED, &DeviceExt->ReadQueue,
&DeviceExt->ReadRequestIntervalTimer,
&DeviceExt->ReadRequestTotalTimer,
StartReadRequest );
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting DigiCancelCurrentRead: port = %s\n",
DeviceExt->DeviceDbgString) );
} // end DigiCancelCurrentRead
BOOLEAN ScanReadBufferForSpecialCharacter( IN PDIGI_DEVICE_EXTENSION DeviceExt,
IN UCHAR SpecialChar )
/*++
Routine Description:
SpinLock for this DeviceExt is assumed taken on entry.
Arguments:
DeviceExt - a pointer to the device object associated with this read
request.
SpecialChar - charater to check if in the read buffer.
Return Value:
TRUE - SpecialChar was found in the read buffer.
FALSE - SpecialChar was not found in the read buffer.
--*/
{
PDIGI_CONTROLLER_EXTENSION ControllerExt;
PUCHAR ControllerBuffer;
PFEP_CHANNEL_STRUCTURE ChInfo;
USHORT Rin, Rout, Rmax;
USHORT DosMode;
BOOLEAN Status=FALSE;
UCHAR ReceivedByte, SecondReceivedByte;
DigiDump( (DIGIREAD|DIGIFLOW), ("Entering ScanReadBufferForSpecialCharacter: port = %s, SpecialChar = 0x%hx\n",
DeviceExt->DeviceDbgString,
(USHORT)SpecialChar) );
ControllerExt = (PDIGI_CONTROLLER_EXTENSION)(DeviceExt->ParentControllerExt);
ControllerBuffer = ControllerExt->VirtualAddress + DeviceExt->RxSeg.Offset;
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
DeviceExt->ChannelInfo.Offset);
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
Rin = READ_REGISTER_USHORT( &ChInfo->rin );
Rmax = READ_REGISTER_USHORT( &ChInfo->rmax );
if( DeviceExt->UnscannedRXFLAGPosition == MAXULONG )
Rout = READ_REGISTER_USHORT( &ChInfo->rout );
else
{
if( DeviceExt->UnscannedRXFLAGPosition == Rin )
{
DisableWindow( ControllerExt );
return FALSE;
}
Rout = (USHORT)DeviceExt->UnscannedRXFLAGPosition;
}
DosMode = READ_REGISTER_USHORT( &ChInfo->iflag );
DisableWindow( ControllerExt );
DosMode &= IFLAG_DOSMODE;
DigiDump( DIGIREAD, (" Rin = 0x%hx\tRout = 0x%hx\tDosMode = 0x%hx\n",
Rin, Rout, DosMode) );
EnableWindow( ControllerExt, DeviceExt->RxSeg.Window );
if( !DosMode )
{
#if 0
for( ; Rout != Rin; ++Rout, Rout &= Rmax )
{
ReceivedByte = READ_REGISTER_UCHAR( ControllerBuffer + Rout );
if( ReceivedByte == SpecialChar )
{
Status = TRUE;
break;
}
}
#else
UCHAR Buf[256];
char *p = NULL;
ULONG i;
while (Rout != Rin)
{
ULONG ReadSize;
if (Rout < Rin)
{
ReadSize = (sizeof(Buf)<Rin-Rout) ? sizeof(Buf) : Rin-Rout;
}
else
{
ReadSize = (sizeof(Buf)<Rmax-Rout+1) ? sizeof(Buf) : Rmax-Rout+1;
}
READ_REGISTER_BUFFER_UCHAR( ControllerBuffer + Rout, Buf, ReadSize );
for (i=0; i<ReadSize && Buf[i]!=SpecialChar; i++);
if (i<ReadSize)
{
Rout += (USHORT)i;
Status = TRUE;
break;
}
Rout += (USHORT)ReadSize;
Rout &= Rmax;
}
#endif
}
else // DosMode
{
for( ; Rout != Rin; ++Rout, Rout &= Rmax )
{
ReceivedByte = READ_REGISTER_UCHAR( ControllerBuffer + Rout );
if( ReceivedByte != 0xff )
{
if( ReceivedByte == SpecialChar )
{
Status = TRUE;
break;
}
}
else
{
//
// If the 2nd character of the 0xff sequence is also 0xff, fall through
// pointing to the 2nd. Otherwise, we don't care what the 3rd char
// is, so skip it. If there insufficient bytes in the ring for a
// full 0xff sequence, point to the 0xff so we will begin scanning
// there on the next iteration.
//
if ((Rin-Rout)&Rmax>=2)
{
Rout++;
Rout &= Rmax;
SecondReceivedByte = READ_REGISTER_UCHAR( ControllerBuffer + Rout );
if (SecondReceivedByte==0xff)
{
if (SecondReceivedByte==SpecialChar)
{
Status = TRUE;
break;
}
}
else
{
Rout++;
Rout &= Rmax;
if (Rin==Rout)
{
Rout -= 2;
Rout &= Rmax;
ASSERT(READ_REGISTER_UCHAR( ControllerBuffer + Rout )==0xFF);
break;
}
}
}
else
{
--Rout;
Rout &= Rmax;
ASSERT(READ_REGISTER_UCHAR( ControllerBuffer + Rout )==0xFF);
break;
}
}
}
}
DisableWindow( ControllerExt );
if( Status )
{
DigiDump( (DIGIREAD|DIGIEVENT), (" Found specialchar 0x%x at 0x.4%x in read buffer.\n", SpecialChar, Rout) );
Rout++;
Rout &= Rmax;
}
DeviceExt->UnscannedRXFLAGPosition = Rout;
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting ScanReadBufferForSpecialCharacter: port = %s\n",
DeviceExt->DeviceDbgString) );
return( Status );
} // end ScanReadBufferForSpecialCharacter