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

1541 lines
48 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:
dispatch.c
Abstract:
This module contains the NT dispatch routines used in the DriverEntry
function call.
Revision History:
* $Log: /Components/Windows/NT/Async/FEP5/dispatch.c $
*
* 1 3/04/96 12:07p Stana
* Most driver entry points: SerialRead, SerialWrite, SerialFlush,
* SerialIoControl, SerialCreate, SerialCleanup, SerialClose.
*
* Revision 1.43.2.11 1995/12/15 16:01:28 dirkh
* Resume transmission on close when XON/XOFF is configured.
*
* Revision 1.43.2.10 1995/11/28 13:26:08 dirkh
* Adopt common header file.
* Remove NOTIMPLEMENTED flag from DosMode messages.
*
* Revision 1.43.2.9 1995/11/09 13:48:02 dirkh
* Cache last requested port speed in device extension (allows many IOCTL_SERIAL_SET_BAUD_RATE to be ignored). This allows SetCommState to configure a dead port with non-empty transmit buffers (e.g., to clear flow control) without hanging...
*
* Revision 1.43.2.8 1995/10/27 18:08:36 dirkh
* IOCTL_SERIAL_IMMEDIATE_CHAR must set IoStatus.Information to MAXULONG for StartWriteRequest.
*
* Revision 1.43.2.7 1995/10/19 13:20:20 dirkh
* Initialize DevExt->ReceiveNotificationLimit for RX80FULL.
* Under RAS, don't change queue size (via SetupComm / IOCTL_SERIAL_SET_QUEUE_SIZE).
* Fill in more info (still not complete) for ClearCommError / IOCTL_SERIAL_GET_COMMSTATUS.
* SetSerialHandflow changes:
* {
* Undo changes from previous revision. (TERMINAL and RAS dump core.)
* Do not change flow control limits under RAS.
* Adjust DevExt->ReceiveNotificationLimit when flow control limits are recalculated.
* Don't bother FEP if CTS/DSR/DCD hardware flow control settings have not changed.
* }
*
* Revision 1.43.2.6 1995/10/09 12:40:46 dirkh
* Rework SetSerialHandflow (advertise end-relative XoffLimit, scale limits *only* when necessary).
*
* Revision 1.43.2.5 1995/10/04 18:38:42 dirkh
* General: Fix debug messages and simplify register reads.
* Verify IOCTL_SERIAL_SET_BAUD_RATE has taken in hardware only in checked build.
* Simplify IOCTL_SERIAL_SET_TIMEOUTS and IOCTL_SERIAL_[GS]ET_CHARS.
*
* Revision 1.43.2.4 1995/09/21 14:11:58 dirkh
* General changes:
* {
* ControllerExt->ModemSignalTable contains explicit values, not bit numbers.
* Use Flush*Buffer whenever possible.
* Clarify interface to SetXFlag (.Mask is only for disabling, .Src is only for enabling).
* }
* Move CancelCurrentFlush and DeferredFlushBuffers to write.c.
* Incorporate StartFlushRequest into StartWriteRequest.
* Simplify SerialCreate and SerialClose.
* Incorporate DigiPurgeRequest into SerialIoControl and simplify code.
* Queue IOCTL_SERIAL_XOFF_COUNTER onto DevExt->WriteQueue via DigiStartIrpRequest.
* Simplify SetXFlag.
* DrainTransmit inspects every 10ms for up to a total of 30 seconds (not 5 minutes).
*
* Revision 1.43.2.3 1995/09/05 17:04:40 dirkh
* StartFlushRequest: Release IoCancel spin lock before calculating timeout.
* DeferredFlushBuffers: Set timer under spin lock to ensure reference count integrity (vs. outstanding timers) in RundownIrpRefs.
* CancelCurrentFlush: Eliminate unused ControllerExt variable.
*
* Revision 1.43.2.2 1995/09/05 13:34:14 dirkh
* Fully realize "fast RAS" flush IRPs (affects SerialWrite, StartFlush, DeferredFlush, CancelFlush, & DigiTryToCompleteIrp; add new routine FreeDigiIrp).
* SerialRead and SerialWrite initialize Irp->IoStatus.Information to MAXULONG to mark them as "not started."
* DeferredFlushBuffers locks DevExt->ControlAccess *before* accessing DevExt->WriteQueue.
* DeferredFlushBuffers delays only 1ms (not 100ms) until next try.
* DigiStartIrpRequest knows how to queue IMMEDIATE_CHAR.
* NBytesInRecvBuffer returns USHORT.
*
* Revision 1.43 1995/04/12 14:36:52 rik
* Changed self-inserted flushes to use the standard method of queue/dequeue
* items.
*
* Fixed problem with not turning on memory when in DosMode and trying to
* count # of received bytes.
*
* Revision 1.42 1994/12/09 14:22:49 rik
* #if Int32x32 back to RtlLarge for NT 3.1 release
*
* Revision 1.41 1994/11/28 21:49:53 rik
* Changed UInt32x32To64 to Int32x32To64 per IBM's request.
*
* Revision 1.40 1994/11/28 09:15:45 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.39 1994/09/13 09:51:17 rik
* Added IOCTL which Microsoft's RAS will call to indicate it is the
* application above our serial driver. This affectivly disables DOSMode so
* we won't report parity errors and we will read data in blocks instead of
* character by character.
*
* If the RAS Ioctl was sent down, then we will also alter the transmit path
* so we insert a bogus flush file buffer IRP onto the write queue. This
* emulates better a network controller which doesn't give a transmit
* complete until the data is actually out on the wire.
*
* Revision 1.38 1994/08/25 20:41:08 rik
* Gave wrong Xoff limit value when a user set flow control limits. Fix so
* I give the correctly calculated value.
*
* Revision 1.37 1994/08/24 13:14:49 rik
* Explicitly clear RTS and DTR flow control if the user isn't requesting
* RTS or DTR flow control.
*
* Moved enabling/disabling flow control before modem lines are set/cleared
* because the request will be ignored by the controller if RTS or DTR
* flow control is enabled.
*
* Revision 1.36 1994/08/18 14:13:03 rik
* Added private RAS ioctl which will ignore SERIAL_EV_ERR notification. This
* has the effect of doing reads in blocks instead of one character at a time.
*
* Revision 1.35 1994/08/03 23:46:36 rik
* Added debug transmit tracing
*
* Optimized RXFLAG and RXCHAR events.
*
* Added 50, 200 baud and 1.5 stop bit support.
*
* Changed queue size implementation such that we always return success.
* I keep track of the requested queue sizes and xon/xoff limits and
* give the controller the same ratio for the limits.
*
* Revision 1.34 1994/06/18 12:42:55 rik
* Updated the DigiLogError calls to include Line # so it is easier to
* determine where the error occurred.
*
* Revision 1.33 1994/05/18 00:37:57 rik
* Updated to include 230000 as a possible baud rate.
*
* Revision 1.32 1994/05/11 13:35:30 rik
* Fixed problem with Transmit Immediate character.
* Put in delay for toggling modem lines.
*
* Revision 1.31 1994/04/19 14:56:34 rik
* Moved a line of code so a port will be marked as closing more towards the
* beginning of a close request.
*
* Revision 1.30 1994/04/10 14:51:22 rik
* Cleanup compiler warnings.
*
* Revision 1.29 1994/04/10 14:15:43 rik
* Deleted code which reset a channels tbusy flag to 0.
*
* Added code to "futz" with the XoffLimit if an app is trying to set an
* Xoff Limit which is lower than the Xon limit. This appears to be a problem
* for some Win16 app's because of the screwed up semantics of the
* XoffLimit.
*
* Revision 1.28 1994/03/16 14:35:59 rik
* Made changes to better support flushing requests. Also created a
* function which will determine when a transmit queue has been drained,
* which can be called at request time.
*
* Revision 1.27 1994/03/05 00:05:27 rik
* Deleted some commented out code.
*
* Revision 1.26 1994/02/23 03:44:29 rik
* Changed so the controllers firmware can be downloaded from a binary file.
* This releases some physical memory was just wasted previously.
*
* Also updated so when compiling with a Windows NT OS and tools release greater
* than 528, then pagable code is compiled into the driver. This greatly
* reduced the size of in memory code, especially the hardware specific
* miniports.
*
* Revision 1.25 1993/12/03 13:17:32 rik
* Added error log for when trying to change baud rate while the transmit buffer is non-empty.
*
* Corrected errors with the DOSMODE being set and unset to the incorrect va.
*
* Revision 1.24 1993/10/15 10:20:05 rik
* Fixed problme with EV_RXFLAG notification.
*
* Revision 1.23 1993/09/30 17:34:47 rik
* Put in a temporary solution for allowing the controller to drain its
* transmit buffer before the port is actually closed and flushed. This
* was causing problems with serial printing.
*
* Revision 1.22 1993/09/29 18:00:31 rik
* Corrected a problem which showed up with RAS on a PC/8i controller.
* For some unknown reason, RAS would send down a write request with
* a NULL pointer for its buffer, and a write length of 0. I moved the
* test for a write length of 0 before the test for the NULL buffer pointer
* which seems to have corrected the problem.
*
* Revision 1.21 1993/09/24 16:39:36 rik
* Put in a better check for baud rate not being set properly down on the
* controller.
*
* Revision 1.20 1993/09/01 11:01:15 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.19 1993/08/27 09:38:37 rik
* Added support for the FEP5 Events RECEIVE_BUFFER_OVERRUN and
* UART_RECEIVE_OVERRUN. There previously weren't being handled.
*
* Revision 1.18 1993/07/16 10:21:54 rik
* Fixed problem where turning off notification fo SERIAL_EV_ERR resulted in
* disabling LSRMST mode and visa versa.
*
* Revision 1.17 1993/07/03 09:25:41 rik
* Added more information to a debugging output statement.
*
* Revision 1.16 1993/06/25 09:22:46 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.15 1993/06/16 10:07:56 rik
* Changed how the XoffLim entry of the HandFlow was being used. The correct
* interpretation is to subtract XoffLim from the size of the receive
* queue, and use the result as the # of bytes to receive before sending
* and XOFF.
*
* Revision 1.14 1993/06/06 14:06:33 rik
* Turned on the appropriate paramaters on the controller to better support
* Break, parity and framing errors.
*
* Revision 1.13 1993/05/18 04:59:12 rik
* Added support for Flushing. This was overlooked previously.
*
* Revision 1.12 1993/05/09 09:12:34 rik
* Changed which name is printed out on Debugging output.
*
* Commented out the check for SET_QUEUE_SIZE. It now will always return
* TRUE regardless of the size request.
*
* Revision 1.11 1993/04/05 18:57:28 rik
* Changed so set wait mask will not call the startup routine for waits.
* There is no need since no more than one wiat IRP can be outstanding at
* any given time.
*
* Revision 1.10 1993/03/05 06:07:50 rik
* I corrected a problem with how I wasn't keeping track of the output lines
* (DTR, RTS). I now update the DeviceExt->BestModem value when I change
* one of the output modem signals.
*
* Added/rearranged debugging output for better tracking.
*
* Revision 1.9 1993/03/02 13:04:05 rik
* Added new debugging entries for "things" which are not yet implemented.
*
* Revision 1.8 1993/02/26 21:13:49 rik
* Discovered that I am suppose to start tracking modem signal changes from
* the time I receive a SET_WAIT_MASK, not when I receive a WAIT_ON_MASK. This
* makes the changes to deal with this.
*
* Revision 1.7 1993/02/25 19:04:31 rik
* Added debugging output. Cleanup on device close by canceling all outstanding
* IRPs. Changed how more of the modem state signals were handled.
*
* Revision 1.6 1993/02/04 12:18:11 rik
* Fixed CTS flow control problem. I hadn't implemented output flow control
* for CTS, DSR, or DCD.
*
* Revision 1.5 1993/01/22 12:32:15 rik
* *** empty log message ***
*
* Revision 1.4 1992/12/10 16:04:32 rik
* Added support for a lot of IOCTLs.
*
* Revision 1.3 1992/11/12 12:47:50 rik
* Updated to properly report and set baud rates.
*
* Revision 1.2 1992/10/28 21:46:05 rik
* Updated to support better IOCTL commands. Added support for reading.
*
* Revision 1.1 1992/10/19 11:24:45 rik
* Initial revision
*
--*/
#include "header.h"
#ifndef _DISPATCH_DOT_C
# define _DISPATCH_DOT_C
static char RCSInfo_DispatchDotC[] = "$Header: /Components/Windows/NT/Async/FEP5/dispatch.c 1 3/04/96 12:07p Stana $";
#endif
#define DIGI_IOCTL_DBGOUT 0x00000001
#define DIGI_IOCTL_TRACE 0x00000002
#define DIGI_IOCTL_DBGBREAK 0x00000003
typedef struct _DIGI_IOCTL_
{
ULONG dwCommand;
ULONG dwBufferLength;
CHAR Char[1024];
} DIGI_IOCTL, *PDIGI_IOCTL;
//
// Dispatch Helper functions
//
void DrainTransmit( PDIGI_CONTROLLER_EXTENSION ControllerExt,
PDIGI_DEVICE_EXTENSION DeviceExt,
PIRP Irp );
NTSTATUS
FreeDigiIrp( PDEVICE_OBJECT deviceObject, PIRP irp, PVOID context )
{
#ifdef IO_ALLOC_IRP_WORKS
IoFreeIrp( irp );
#else
DigiFreeMem( irp );
#endif
// Prevent further cleanup by I/O Manager.
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
SerialWrite( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
This is the dispatch routine for write. It validates the parameters
for the write request and if all is ok and there are no outstanding
write requests, it then checks to see if there is enough room for the
data in the controllers Tx buffer. It places the request on the work
queue if it can't place all the data in the controllers buffer, or there
is an outstanding write request.
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, or the result
of trying to write the data to the waiting controller.
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
NTSTATUS Status;
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_WRITE );
if (DeviceObject==ControllerExt->ControllerDeviceObject)
{
/*
** No writes allowed to controller.
*/
Irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_ACCESS_VIOLATION;
}
if( DeviceExt->DeviceState != DIGI_DEVICE_STATE_OPEN )
{
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_CANCELLED;
}
InterlockedIncrement(&ControllerExt->PerfData.WriteRequests);
InterlockedIncrement(&DeviceExt->PerfData.WriteRequests);
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIIRP|DIGIFLOW|DIGIWRITE), ("Entering SerialWrite: port = %s\tIRP = 0x%x\t%u:%u\n",
DeviceExt->DeviceDbgString, Irp, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
//
// Check first for a write length of 0, then for a NULL buffer!
//
if( IoGetCurrentIrpStackLocation( Irp )->Parameters.Write.Length == 0 )
{
//
// We assume a write of 0 length is valid. Just complete the request
// and return.
//
Irp->IoStatus.Information = 0L;
Irp->IoStatus.Status = STATUS_SUCCESS;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
DigiDump( DIGITXTRACE, ("port %s requested a zero length write\n",
DeviceExt->DeviceDbgString) );
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIFLOW|DIGIWRITE), ("Exiting SerialWrite: port = %s\t%u:%u\n",
DeviceExt->DeviceDbgString, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
return( STATUS_SUCCESS );
}
if( Irp->AssociatedIrp.SystemBuffer == NULL )
{
//
// This is most definitely a No No!
//
DigiDump( DIGIERRORS, ("SerialWrite - Invalid Irp->AssociatedIrp.SystemBuffer!\n") );
Irp->IoStatus.Information = 0L;
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( DIGIFLOW, ("Exiting SerialWrite: port = %s ret = %d\t%u:%u\n",
DeviceExt->DeviceDbgString, STATUS_INVALID_PARAMETER, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
return( STATUS_INVALID_PARAMETER );
}
//
// Mark the IRP as being "not started."
// StartWriteRequest will set this field to zero.
// WriteTxBuffer updates the field to the number of bytes written.
//
Irp->IoStatus.Information = MAXULONG;
DigiDump( DIGIWRITE, (" #bytes to write: %d\n",
IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length) );
#if DBG
if( DigiDebugLevel & DIGITXTRACE )
{
PUCHAR Temp = Irp->AssociatedIrp.SystemBuffer;
ULONG i;
DigiDump( DIGITXTRACE, ("TXTRACE: %s: TxLength = %d (0x%x)",
DeviceExt->DeviceDbgString,
IoGetCurrentIrpStackLocation( Irp )->Parameters.Write.Length,
IoGetCurrentIrpStackLocation( Irp )->Parameters.Write.Length) );
for( i = 0;
i < IoGetCurrentIrpStackLocation( Irp )->Parameters.Write.Length;
i++ )
{
if( (i & 15) == 0 )
DigiDump( DIGITXTRACE, ( "\n\t") );
DigiDump( DIGITXTRACE, ( "-%02x", Temp[i]) );
}
DigiDump( DIGITXTRACE, ("\n") );
}
#endif
//
// If we are doing RAS, then we wait for any data in
// the transmit queue to be put on the wire.
//
if( DeviceExt->SpecialFlags & DIGI_SPECIAL_FLAG_FAST_RAS )
{
PIRP NewIrp;
//#undef IO_ALLOC_IRP_WORKS, at least under 3.5 (807) checked build (IoFreeIrp reports corrupted memory)
#ifdef IO_ALLOC_IRP_WORKS
NewIrp = IoAllocateIrp( 2, FALSE );
#else
NewIrp = DigiAllocMem( NonPagedPool, IoSizeOfIrp( 2 ) );
#endif
if( NewIrp )
{
PIO_STACK_LOCATION NewIrpSp;
IoInitializeIrp( NewIrp, IoSizeOfIrp( 2 ), 2 );
IoSetCompletionRoutine( NewIrp, FreeDigiIrp, NULL, TRUE, TRUE, TRUE );
IoSetNextIrpStackLocation( NewIrp );
NewIrpSp = IoGetCurrentIrpStackLocation( NewIrp );
NewIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS;
NewIrpSp->DeviceObject = DeviceObject;
DigiDump( 0, ("Inserting flush irp 0x%x on %s\n", NewIrp, DeviceExt->DeviceDbgString) );
(VOID) DigiStartIrpRequest( ControllerExt, DeviceExt,
&DeviceExt->WriteQueue, NewIrp,
StartWriteRequest );
}
}
Status = DigiStartIrpRequest( ControllerExt, DeviceExt,
&DeviceExt->WriteQueue, Irp,
StartWriteRequest );
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIFLOW|DIGIWRITE), ("Exiting SerialWrite: port = %s\t%u:%u\n",
DeviceExt->DeviceDbgString, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
return( Status );
} // end SerialWrite
NTSTATUS
SerialFlush( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
NTSTATUS Status;
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_FLUSH_BUFFERS );
if( DeviceExt->DeviceState != DIGI_DEVICE_STATE_OPEN )
{
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_CANCELLED;
}
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIIRP|DIGIFLOW), ("Entering SerialFlush: port = %s\tIRP = 0x%x\t%u:%u\n",
DeviceExt->DeviceDbgString, Irp,
CurrentSystemTime.HighPart,
CurrentSystemTime.LowPart) );
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
Status = DigiStartIrpRequest( DeviceExt->ParentControllerExt, DeviceExt,
&DeviceExt->WriteQueue, Irp,
StartWriteRequest );
DigiDump( DIGIFLOW, ("Exiting SerialFlush: port = %s\n",
DeviceExt->DeviceDbgString) );
return( Status );
} // end SerialFlush
PIRP CurrentReadIrp = NULL;
UCHAR StackCount;
UCHAR CurrentLocation;
NTSTATUS
SerialRead( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
This is the dispatch routine for read. It validates the parameters
for the read request and if all is ok and there are not outstanding
read request, it will read waiting data from the controller. Otherwise,
it will queue the request, and wait for the incoming data.
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 the status returned by the actual controller
read routine.
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_READ );
if (DeviceObject==DeviceExt->ParentControllerExt->ControllerDeviceObject)
{
/*
** No reads allowed to controller.
*/
Irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_ACCESS_VIOLATION;
}
if( DeviceExt->DeviceState != DIGI_DEVICE_STATE_OPEN )
{
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_CANCELLED;
}
InterlockedIncrement(&DeviceExt->ParentControllerExt->PerfData.ReadRequests);
InterlockedIncrement(&DeviceExt->PerfData.ReadRequests);
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIIRP|DIGIFLOW|DIGIREAD), ("Entering SerialRead: port = %s\tIRP = 0x%x\t%u:%u\n",
DeviceExt->DeviceDbgString, Irp, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
//
// Quick check for a zero length read. If it is zero length
// then we are already done!
//
if( IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length )
{
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
NTSTATUS Status;
//
// Mark the IRP as being "not started."
// StartReadRequest will set this field to zero.
// ReadRxBuffer updates the field to the number of bytes read.
//
Irp->IoStatus.Information = MAXULONG;
DigiDump( DIGIREAD, (" #bytes to read: %d\n",
IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length) );
#if 0 // XXXX
{
const void *debugAddr = &Irp->RequestorMode; // to catch changes to StackCount and CurrentLocation
const int mask = ~(DR3_MASK_IN_DR7);
const int value = (L3_IN_DR7 | G3_IN_DR7 | LEN3_EQUALS_4_IN_DR7 | WRITE_DR3_IN_DR7);
ASSERT( ((LONG)debugAddr & 3) == 0 );
SetIntelRegister( dr3, debugAddr );
AffectIntelRegister( dr7, mask, value );
}
#endif
Status = DigiStartIrpRequest( ControllerExt, DeviceExt,
&DeviceExt->ReadQueue, Irp,
StartReadRequest );
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting SerialRead: port = %s\t%u:%u\n",
DeviceExt->DeviceDbgString, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
return( Status );
}
else
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIFLOW|DIGIREAD), ("Exiting SerialRead: port = %s\t%u:%u\n",
DeviceExt->DeviceDbgString, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
return STATUS_SUCCESS;
}
} // end SerialRead
NTSTATUS
SerialCreate( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
NTSTATUS Status=STATUS_SUCCESS;
PFEP_CHANNEL_STRUCTURE ChInfo;
DIGI_XFLAG IFlag;
USHORT Rmax, Tmax, Rhigh;
KIRQL OldIrql;
UCHAR MStatSet, MStatClear, HFlowSet, HFlowClear;
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
if (DeviceObject==ControllerExt->ControllerDeviceObject)
{
/*
** All controller opens succeed.
*/
DigiDump( (DIGIIRP|DIGIFLOW|DIGICREATE), ("ControllerCreate\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIIRP|DIGIFLOW|DIGICREATE), ("Entering SerialCreate: port = %s\tIRP = 0x%x\t%u:%u\n",
DeviceExt->DeviceDbgString, Irp, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_CREATE );
InterlockedIncrement(&ControllerExt->PerfData.OpenRequests);
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
DeviceExt->DeviceState = DIGI_DEVICE_STATE_OPEN;
DeviceExt->WaitMask = 0L;
DeviceExt->HistoryWait = 0L;
DeviceExt->TotalCharsQueued = 0L;
DeviceExt->EscapeChar = 0;
DeviceExt->SpecialFlags = 0;
DeviceExt->UnscannedRXFLAGPosition = MAXULONG;
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
//
// Okay, lets make sure the port on the controller is in a known
// state.
//
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
DeviceExt->ChannelInfo.Offset);
FlushTransmitBuffer( ControllerExt, DeviceExt );
FlushReceiveBuffer( ControllerExt, DeviceExt );
// Set default flow control limits.
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
Rmax = READ_REGISTER_USHORT( &ChInfo->rmax );
Tmax = READ_REGISTER_USHORT( &ChInfo->tmax );
DisableWindow( ControllerExt );
DeviceExt->XonLimit = (((LONG)Rmax + 1) / 4);
Rhigh = Rmax - (USHORT)DeviceExt->XonLimit;
DeviceExt->XoffLimit = DeviceExt->XonLimit; // We preserve XoffLimit in Win32 semantics
WriteCommandWord( DeviceExt, SET_RCV_LOW, (USHORT)DeviceExt->XonLimit );
WriteCommandWord( DeviceExt, SET_RCV_HIGH, Rhigh );
WriteCommandWord( DeviceExt, SET_TX_LOW, (USHORT)((Tmax + 1) / 4) );
//
// Based on 10ms polling frequency and 115.2Kbps bit rate,
// we acquire up to 115.2 bytes per polling iteration.
// Thus, to avoid flow control, we need notification up to
// two polling iterations prior to reaching XoffLimit.
// However, we need at least 50 bytes to make the interaction worthwhile.
// DH Calculate limit dynamically based on communication characteristics.
//
if( Rhigh > 2*115 + 50 )
DeviceExt->ReceiveNotificationLimit = Rhigh - 2*115;
else
if( Rmax >= 100 )
DeviceExt->ReceiveNotificationLimit = 50;
else // shouldn't happen
DeviceExt->ReceiveNotificationLimit = Rmax / 2;
//
// Initialize requested queue sizes.
//
DeviceExt->RequestedQSize.InSize = (ULONG)(Rmax + 1);
DeviceExt->RequestedQSize.OutSize = (ULONG)(Tmax + 1);
//
// Set where RxChar and RxFlag were last seen in the buffer to a
// bogus value so we catch the condition where the 1st character in
// is RxFlag, and so we give notification for the 1st character
// received.
//
DeviceExt->PreviousRxChar = MAXULONG;
DeviceExt->UnscannedRXFLAGPosition = MAXULONG;
//
// Set the Xon & Xoff characters for this device to default values.
//
WriteCommandBytes( DeviceExt, SET_XON_XOFF_CHARACTERS,
DEFAULT_XON_CHAR, DEFAULT_XOFF_CHAR );
MStatClear = MStatSet = 0;
HFlowClear = HFlowSet = 0;
//
// We have some RTS flow control to worry about.
//
// Don't forget that flow control is sticky across
// open requests.
//
if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_RTS_HANDSHAKE )
{
//
// This is normal RTS input flow control
//
HFlowSet |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
}
else if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_RTS_CONTROL )
{
//
// We need to make sure RTS is asserted when certain 'things'
// occur, or when we are in a certain state.
//
MStatSet |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
}
else if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_TRANSMIT_TOGGLE )
{
}
else
{
//
// RTS Control Mode is in a Disabled state.
//
MStatClear |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
}
//
// We have some DTR flow control to worry about.
//
// Don't forget that flow control is sticky across
// open requests.
//
if( (DeviceExt->ControlHandShake & SERIAL_DTR_MASK) ==
SERIAL_DTR_HANDSHAKE )
{
//
// This is normal DTR input flow control
//
HFlowSet |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
}
else if( (DeviceExt->ControlHandShake & SERIAL_DTR_MASK) ==
SERIAL_DTR_CONTROL )
{
//
// We need to make sure DTR is asserted when certain 'things'
// occur, or when we are in a certain state.
//
MStatSet |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
}
else
{
//
// DTR Control Mode is in a Disabled state.
//
MStatClear |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
}
//
// CTS, DSR, and DCD output handshaking is sticky across OPEN requests.
//
if( (DeviceExt->ControlHandShake & SERIAL_CTS_HANDSHAKE) )
{
HFlowSet |= ControllerExt->ModemSignalTable[CTS_SIGNAL];
}
else
{
HFlowClear |= ControllerExt->ModemSignalTable[CTS_SIGNAL];
}
if( (DeviceExt->ControlHandShake & SERIAL_DSR_HANDSHAKE) )
{
HFlowSet |= ControllerExt->ModemSignalTable[DSR_SIGNAL];
}
else
{
HFlowClear |= ControllerExt->ModemSignalTable[DSR_SIGNAL];
}
if( (DeviceExt->ControlHandShake & SERIAL_DCD_HANDSHAKE) )
{
HFlowSet |= ControllerExt->ModemSignalTable[DCD_SIGNAL];
}
else
{
HFlowClear |= ControllerExt->ModemSignalTable[DCD_SIGNAL];
}
//
// Make sure we enable/disable flow controls before trying to
// explicitly set/clear modem control lines.
//
if( HFlowSet || HFlowClear )
{
DeviceExt->WriteOnlyModemSignalMask = (~HFlowSet) &
(ControllerExt->ModemSignalTable[DTR_SIGNAL]|ControllerExt->ModemSignalTable[RTS_SIGNAL]);
WriteCommandBytes( DeviceExt, SET_HDW_FLOW_CONTROL,
HFlowSet, HFlowClear );
}
if( MStatSet || MStatClear )
{
DeviceExt->CurrentModemSignals |= MStatSet;
DeviceExt->CurrentModemSignals &= ~MStatClear;
DeviceExt->WriteOnlyModemSignalValue |= MStatSet;
DeviceExt->WriteOnlyModemSignalValue &= ~MStatClear;
WriteCommandBytes( DeviceExt, SET_MODEM_LINES,
MStatSet, MStatClear );
}
//
// Make sure we get break notification through the event queue to
// begin with.
//
IFlag.Mask = (USHORT)(~( IFLAG_PARMRK | IFLAG_INPCK | IFLAG_DOSMODE ));
IFlag.Src = IFLAG_BRKINT;
IFlag.Command = SET_IFLAGS;
SetXFlag( DeviceExt, &IFlag );
//
// Okay, were done, lets get the heck out of dodge.
//
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0L;
//
// We do this check here to make sure the controller has had
// a chance to catch up. Running on fast machines doesn't always
// give the controller a chance.
//
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
if( READ_REGISTER_USHORT( &ChInfo->rlow )
== 0 )
DigiDump( DIGIINIT, ("ChInfo->rlow == 0\n"));
if( READ_REGISTER_USHORT( &ChInfo->rhigh )
== 0 )
DigiDump( DIGIINIT, ("ChInfo->rhigh == 0\n"));
if( READ_REGISTER_USHORT( &ChInfo->tlow )
== 0 )
DigiDump( DIGIINIT, ("ChInfo->tlow == 0\n"));
//
// Enable IDATA so we get notified when new data has arrived.
//
WRITE_REGISTER_UCHAR( &ChInfo->idata, TRUE );
DisableWindow( ControllerExt );
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
InterlockedIncrement(&ControllerExt->PerfData.OpenPorts);
DigiDump( (DIGIFLOW|DIGICREATE), ("Exiting SerialCreate: port = %s\n",
DeviceExt->DeviceDbgString) );
return( STATUS_SUCCESS );
} // end SerialCreate
NTSTATUS
SerialClose( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
UCHAR ClearSignals;
#if DBG
LARGE_INTEGER CurrentSystemTime;
#endif
if (DeviceObject==DeviceExt->ParentControllerExt->ControllerDeviceObject)
{
/*
** No reads allowed to controller.
*/
DigiDump( (DIGIIRP|DIGIFLOW), ("ControllerClose\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
InterlockedIncrement(&ControllerExt->PerfData.CloseRequests);
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( (DIGIFLOW|DIGIIRP), ("Entering SerialClose: port = %s\tIRP = 0x%x\t%u:%u\n",
DeviceExt->DeviceDbgString, Irp, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_CLOSE );
//
// Mark the port as closing so new IRPs will be rejected by dispatch routines.
// Note that event processing must not clear the transmit buffer so we don't lose data.
//
DeviceExt->DeviceState = DIGI_DEVICE_STATE_CLOSED;
DigiCancelIrpQueue( DeviceObject, &DeviceExt->WriteQueue );
DigiCancelIrpQueue( DeviceObject, &DeviceExt->ReadQueue );
DigiCancelIrpQueue( DeviceObject, &DeviceExt->WaitQueue );
//
// We put this after the canceling of the Irp queues because all
// writes should have been completed before this routine was called.
//
DrainTransmit( ControllerExt, DeviceExt, Irp );
// If XOFF'ed, let the transmitter recover.
if( DeviceExt->FlowReplace & SERIAL_AUTO_TRANSMIT )
WriteCommandWord( DeviceExt, RESUME_TX, 0 );
//
// Indicate we don't want to notify anyone.
//
DeviceExt->WaitMask = 0L;
//
// Disable hardware flow control and force RTS and DTR low.
//
ClearSignals = ControllerExt->ModemSignalTable[RTS_SIGNAL]
| ControllerExt->ModemSignalTable[DTR_SIGNAL];
DeviceExt->WriteOnlyModemSignalMask = ClearSignals;
WriteCommandBytes( DeviceExt, SET_HDW_FLOW_CONTROL, 0, ClearSignals );
DeviceExt->CurrentModemSignals &= ~ClearSignals;
DeviceExt->WriteOnlyModemSignalValue = 0;
WriteCommandBytes( DeviceExt, SET_MODEM_LINES, 0, ClearSignals );
Irp->IoStatus.Status = STATUS_SUCCESS;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
InterlockedDecrement(&ControllerExt->PerfData.OpenPorts);
#if DBG
KeQuerySystemTime( &CurrentSystemTime );
#endif
DigiDump( DIGIFLOW, ("Exiting SerialClose: port = %s\t%u:%u\n",
DeviceExt->DeviceDbgString, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
return( STATUS_SUCCESS );
} // end SerialClose
NTSTATUS
SerialCleanup( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_CLEANUP );
if (DeviceObject==ControllerExt->ControllerDeviceObject)
{
/*
** All controller opens succeed.
*/
DigiDump( (DIGIIRP|DIGIFLOW), ("ControllerCleanup\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
DigiDump( (DIGIFLOW|DIGIIRP), ("Entering SerialCleanup: port = %s\tIRP = 0x%x\n",
DeviceExt->DeviceDbgString, Irp) );
//
// Mark the port as cleaning up so new IRPs will be rejected by dispatch routines.
// Note that event processing must not modify buffers...
//
DeviceExt->DeviceState = DIGI_DEVICE_STATE_CLEANUP;
DigiCancelIrpQueue( DeviceObject, &DeviceExt->WriteQueue );
DigiCancelIrpQueue( DeviceObject, &DeviceExt->ReadQueue );
DigiCancelIrpQueue( DeviceObject, &DeviceExt->WaitQueue );
// Not clear what state we're in now, but SerialClose will take care of things anyway.
DeviceExt->DeviceState = DIGI_DEVICE_STATE_OPEN;
Irp->IoStatus.Status = STATUS_SUCCESS;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
DigiDump( DIGIFLOW, ("Exiting SerialCleanup: port = %s\n",
DeviceExt->DeviceDbgString) );
return( STATUS_SUCCESS );
}
NTSTATUS
SerialQueryInformation( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
DigiDump( (DIGIFLOW|DIGIIRP), ("Entering SerialQueryInformation: port = %s\tIRP = 0x%x\n",
DeviceExt->DeviceDbgString, Irp) );
Irp->IoStatus.Status = STATUS_SUCCESS;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
DigiDump( DIGIFLOW, ("Exiting SerialQueryInformation: port = %s\n",
DeviceExt->DeviceDbgString) );
return( STATUS_SUCCESS );
}
NTSTATUS
SerialSetInformation( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
DigiDump( (DIGIFLOW|DIGIIRP), ("Entering SerialSetInformation: port = %s\tIRP = 0x%x\n",
DeviceExt->DeviceDbgString, Irp) );
Irp->IoStatus.Status = STATUS_SUCCESS;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
DigiDump( DIGIFLOW, ("Exiting SerialSetInformation: port = %s\n",
DeviceExt->DeviceDbgString) );
return( STATUS_SUCCESS );
}
NTSTATUS
SerialQueryVolumeInformation( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
DigiDump( (DIGIFLOW|DIGIIRP), ("Entering SerialQueryVolumeInformation: port = %s\tIRP = 0x%x\n",
DeviceExt->DeviceDbgString, Irp) );
Irp->IoStatus.Status = STATUS_SUCCESS;
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
DigiDump( DIGIFLOW, ("Exiting SerialQueryVolumeInformation: port = %s\n",
DeviceExt->DeviceDbgString) );
return( STATUS_SUCCESS );
}
VOID
DrainTransmit( PDIGI_CONTROLLER_EXTENSION ControllerExt,
PDIGI_DEVICE_EXTENSION DeviceExt,
PIRP Irp )
/*++
Routine Description:
We do the necessary checks to determine if the controller has
transmitted all the data it has been given.
The check basically is:
if( CIN == COUT
TIN == TOUT
TBusy == 0 )
transmit buffer is empty.
NOTE: Care should be taken when using this function, and at
what dispatch level it is being called from. I don't do any
synch'ing with the WriteQueue in the DeviceObject. So it is
potentially possible that data could keep getting put on the
controller while the function is waiting for it to drain.
Arguments:
ControllerExt - a pointer to this devices controllers extension.
DeviceObject - a pointer to this devices object.
Irp - Pointer to the current Irp request whose context this function
is being called. This allows us to determine if the Irp
has been cancelled.
Return Value:
--*/
{
PFEP_CHANNEL_STRUCTURE ChInfo;
PCOMMAND_STRUCT CommandQ;
COMMAND_STRUCT CmdStruct;
UCHAR TBusy;
ULONG count;
USHORT OrgTout, Tin, Tout;
TIME DelayInterval;
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
DeviceExt->ChannelInfo.Offset);
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
Tin = READ_REGISTER_USHORT( &ChInfo->tin );
Tout = READ_REGISTER_USHORT( &ChInfo->tout );
TBusy = READ_REGISTER_UCHAR( &ChInfo->tbusy );
DisableWindow( ControllerExt );
OrgTout = Tout;
//
// Get the command queue info
//
CommandQ = ((PCOMMAND_STRUCT)(ControllerExt->VirtualAddress + FEP_CIN));
EnableWindow( ControllerExt, ControllerExt->Global.Window );
READ_REGISTER_BUFFER_UCHAR( (PUCHAR)CommandQ,
(PUCHAR)&CmdStruct,
sizeof(CmdStruct) );
DisableWindow( ControllerExt );
//
// Delay for 10 milliseconds
//
#if rmm < 807
DelayInterval = RtlConvertLongToLargeInteger( -10 * 10000 );
#else
DelayInterval.QuadPart = -10 * 10000;
#endif
count = 0;
while( ((Tin != Tout) ||
(TBusy) ||
(CmdStruct.cmHead != CmdStruct.cmTail)) &&
!Irp->Cancel )
{
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL ); // not DPC, or KeDelay won't ever return
KeDelayExecutionThread( KernelMode,
FALSE,
&DelayInterval );
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
Tin = READ_REGISTER_USHORT( &ChInfo->tin );
Tout = READ_REGISTER_USHORT( &ChInfo->tout );
TBusy = READ_REGISTER_UCHAR( &ChInfo->tbusy );
DisableWindow( ControllerExt );
EnableWindow( ControllerExt, ControllerExt->Global.Window );
READ_REGISTER_BUFFER_UCHAR( (PUCHAR)CommandQ,
(PUCHAR)&CmdStruct,
sizeof(CmdStruct) );
DisableWindow( ControllerExt );
if( Tout != OrgTout )
{
count = 0;
OrgTout = Tout;
}
if( count++ > 2500 )
{
//
// We have waited for 25 seconds and haven't seen the transmit
// buffer change. Assume we are in a deadlock flow control state
// and exit!
//
//
// We go ahead and flush the transmit queue because a close
// may be following soon, and we don't want it to have to
// wait again. Basically, it had its chance to drain.
//
FlushTransmitBuffer( ControllerExt, DeviceExt );
break;
}
}
} // end DrainTransmit
USHORT NBytesInRecvBuffer( PDIGI_CONTROLLER_EXTENSION ControllerExt,
PDIGI_DEVICE_EXTENSION DeviceExt )
/*++
Routine Description:
Determine the number of actual bytes in the receive buffer. This routine
takes into account DOSMODE on the controller.
Arguments:
ControllerExt - pointer to the controller extension information
assosicated with DeviceExt.
DeviceExt - pointer to the device specific information.
Return Value:
Number of bytes in the receive buffer.
--*/
{
PUCHAR ControllerBuffer;
PFEP_CHANNEL_STRUCTURE ChInfo;
USHORT AmountInQueue;
USHORT Rin, Rout, Rmax;
USHORT DosMode;
UCHAR ReceivedByte, SecondReceivedByte;
ControllerBuffer = ControllerExt->VirtualAddress + DeviceExt->RxSeg.Offset;
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 );
DosMode = READ_REGISTER_USHORT( &ChInfo->iflag );
DisableWindow( ControllerExt );
DosMode &= IFLAG_DOSMODE;
if( !DosMode )
{
AmountInQueue = Rin - Rout;
if( (SHORT)AmountInQueue < 0)
AmountInQueue += (Rmax + 1);
return( AmountInQueue );
}
AmountInQueue = 0;
EnableWindow( ControllerExt, DeviceExt->RxSeg.Window );
DigiDump( DIGIIOCTL,
(" NRecvRoutine: Rin = 0x%x, Rout = 0x%x\n",
Rin,
Rout) );
while( Rout != Rin )
{
ReceivedByte = READ_REGISTER_UCHAR( (ControllerBuffer + Rout) );
Rout++;
Rout &= Rmax;
AmountInQueue++;
DigiDump( DIGIIOCTL,
(" NRecvByte = 0x%x, Rout = 0x%x\n",
ReceivedByte,
Rout) );
//
// We need to process out DigiBoard specific 0xFF.
//
if( ReceivedByte == 0xFF )
{
//
// We have some special processing to do!
//
//
// Is there a second character available??
//
if( Rout == Rin )
{
//
// The second character isn't available!
//
AmountInQueue--;
DigiDump( DIGIIOCTL,
(" NRecvRoutine, 2nd byte not available!\n" ) );
break;
}
else
{
//
// Get the 2nd characters
//
SecondReceivedByte = READ_REGISTER_UCHAR( (ControllerBuffer + Rout) );
Rout++;
Rout &= Rmax;
if( SecondReceivedByte == 0xFF )
{
//
// We actually received a 0xFF in the data stream.
//
DigiDump( DIGIIOCTL,
(" NRecvRoutine, Actually recv'ed 0xFF\n" ) );
continue;
}
else
{
//
// This is Line Status information. Is the last
// character available??
//
if( Rin == Rout )
{
//
// The 3rd byte isn't available
//
AmountInQueue--;
DigiDump( DIGIIOCTL,
(" NRecvRoutine, 3rd byte not available!\n" ) );
break;
}
Rout++;
Rout &= Rmax;
}
}
}
}
DisableWindow( ControllerExt );
DigiDump( DIGIIOCTL,
(" NRecvRoutine, return RecvBytes = %d!\n",
AmountInQueue ) );
return( AmountInQueue );
} // end NBytesInRecvBuffer