mirror of https://github.com/lianthony/NT4.0
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.
967 lines
32 KiB
967 lines
32 KiB
/*++
|
|
|
|
*****************************************************************************
|
|
* *
|
|
* 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:
|
|
|
|
write.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the NT routines responsible for writing data
|
|
to a Digi controller running FEPOS 5 program.
|
|
|
|
Revision History:
|
|
|
|
* $Log: /Components/Windows/NT/Async/FEP5/write.c $
|
|
*
|
|
* 1 3/04/96 12:22p Stana
|
|
* Write data to be transmitted to a DigiBoard FEP Tx Buffer.
|
|
* Revision 1.21.3.5 1995/12/14 12:26:16 dirkh
|
|
* Only set IEMPTY after data has been transferred.
|
|
* Only set ILOW if out of room (PENDING).
|
|
* Set these flags *after* updating TIN.
|
|
*
|
|
* Revision 1.21.3.4 1995/11/28 12:50:10 dirkh
|
|
* Adopt common header file.
|
|
*
|
|
* Revision 1.21.3.3 1995/09/25 16:29:34 dirkh
|
|
* Remove fastcall from IsTransmitterIdle (not efficient on non-Intel).
|
|
*
|
|
* Revision 1.21.3.2 1995/09/20 17:28:06 dirkh
|
|
* WriteTxBuffer changes:
|
|
* {
|
|
* Add IOCTL_SERIAL_XOFF_COUNTER support (return SUCCESS only if transmitted & followed by WRITE or XOFF_COUNTER).
|
|
* Use Status (not CompleteBufferSent) to indicate IRP completion.
|
|
* Always set IEMPTY and ILOW exactly once (move to DigiServiceEvent later?).
|
|
* }
|
|
* StartWriteRequest changes:
|
|
* {
|
|
* Add IOCTL_SERIAL_XOFF_COUNTER support (maintain DevExt->pXoffCounter).
|
|
* Incorporate StartFlushRequest functionality.
|
|
* }
|
|
* Move DeferredFlushBuffers and DigiCancelCurrentFlush here from dispatch.c.
|
|
* DigiWriteTimeout purges the transmit buffer.
|
|
*
|
|
* Revision 1.21.3.1 1995/09/05 13:32:16 dirkh
|
|
* Simplify WriteTxBuffer interface.
|
|
* Eliminate unnecessary DevExt->WriteTxBufferCnt.
|
|
* StartWriteRequest changes:
|
|
* {
|
|
* Replace WriteOffset with WriteQueue->Irp->IoStatus.Information.
|
|
* Minimize spin lock windows, especially IoCancel.
|
|
* Calculate timeout only when necessary.
|
|
* Set bFirstStatus = FALSE on loop iteration.
|
|
* }
|
|
* DigiCancelCurrentWrite: Minimize IoCancel spin lock window.
|
|
* DigiWriteTimeout gets spin lock at DISPATCH_LEVEL.
|
|
*
|
|
* Revision 1.21 1994/12/09 14:23:08 rik
|
|
* #if Int32x32 back to RtlLarge for NT 3.1 release
|
|
*
|
|
* Revision 1.20 1994/11/28 09:17:25 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.19 1994/09/13 09:50:46 rik
|
|
* Added debug tracking output for cancel irps.
|
|
*
|
|
* Revision 1.18 1994/08/03 23:26:36 rik
|
|
* Cleanup how I determine how data needs to be read from the controller.
|
|
*
|
|
* Changed dbg output msg to use string name of port instead of unicode
|
|
* string name. Unicode strings cause NT to bugcheck at DPC level.
|
|
*
|
|
* Revision 1.17 1994/05/11 13:43:25 rik
|
|
* Added support for transmit immediate character.
|
|
*
|
|
* Revision 1.16 1994/04/10 14:14:47 rik
|
|
* Deleted code which resets the tbusy flag in a channel structure to 0.
|
|
*
|
|
* Revision 1.15 1994/03/16 14:32:32 rik
|
|
* Changed how Flush requests are handled.
|
|
* deleted commented out code.
|
|
*
|
|
* Revision 1.14 1994/02/23 03:44:48 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.13 1993/09/07 14:29:10 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.12 1993/09/01 11:02:54 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.11 1993/07/03 09:31:32 rik
|
|
* Added fix for problem of how I was enabling iempty and ilow. I was
|
|
* potentially writting the values to the transmit buffer, which caused
|
|
* CRC (ie data corruption) in the transmitted data.
|
|
*
|
|
* Revision 1.10 1993/06/06 14:14:50 rik
|
|
* Corrected an uninitalized DeviceExt variable.
|
|
* Added a check for where an Irp's CancelRoutine is NULL. If it isn't null,
|
|
* I print a message a do a breakpoint to stop execution.
|
|
*
|
|
* Revision 1.9 1993/05/18 05:23:39 rik
|
|
* Added support for flushing buffers.
|
|
*
|
|
* Fixed problem with not releasing the device extension spinlock BEFORE
|
|
* calling IoComplete request.
|
|
*
|
|
* Revision 1.8 1993/05/09 09:38:00 rik
|
|
* Changed so the Startwrite routine will return the first status it actually
|
|
* comes up with.
|
|
*
|
|
* Changed the name used in debugging output.
|
|
*
|
|
* Revision 1.7 1993/03/08 07:10:17 rik
|
|
* Added more debugging information for better flow debugging.
|
|
*
|
|
* Revision 1.6 1993/02/25 19:13:05 rik
|
|
* Added better debugging support for tracing IRPs and write requests individly.
|
|
*
|
|
* Revision 1.5 1993/01/22 12:46:36 rik
|
|
* *** empty log message ***
|
|
*
|
|
* Revision 1.4 1992/12/10 16:24:22 rik
|
|
* Changed DigiDump messages.
|
|
*
|
|
* Revision 1.3 1992/11/12 12:54:24 rik
|
|
* Basically re-wrote to better support timeouts, and multi-processor machines.
|
|
*
|
|
* Revision 1.2 1992/10/28 21:51:50 rik
|
|
* Made changes to better support writing to the controller.
|
|
*
|
|
* Revision 1.1 1992/10/19 11:25:16 rik
|
|
* Initial revision
|
|
*
|
|
|
|
--*/
|
|
|
|
|
|
#include "header.h"
|
|
|
|
#ifndef _WRITE_DOT_C
|
|
# define _WRITE_DOT_C
|
|
static char RCSInfo_WriteDotC[] = "$Header: /Components/Windows/NT/Async/FEP5/write.c 1 3/04/96 12:22p Stana $";
|
|
#endif
|
|
|
|
/****************************************************************************/
|
|
/* Local Prototypes */
|
|
/****************************************************************************/
|
|
VOID DigiCancelCurrentFlush( PDEVICE_OBJECT DeviceObject, PIRP Irp );
|
|
VOID DigiCancelCurrentWrite( PDEVICE_OBJECT DeviceObject, PIRP Irp );
|
|
|
|
|
|
NTSTATUS WriteTxBuffer( IN PDIGI_DEVICE_EXTENSION DeviceExt )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
PUCHAR tx, WriteBuffer;
|
|
USHORT Tin, Tout, Tmax;
|
|
USHORT TxSize, TxWrite;
|
|
LONG nBytesWritten = 0;
|
|
PLIST_ENTRY WriteQueue;
|
|
NTSTATUS Status = STATUS_PENDING;
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWRITE), ("Entering WriteTxBuffer\n") );
|
|
|
|
WriteQueue = &DeviceExt->WriteQueue;
|
|
Irp = CONTAINING_RECORD( WriteQueue->Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
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 );
|
|
Tmax = READ_REGISTER_USHORT( &ChInfo->tmax );
|
|
DisableWindow( ControllerExt );
|
|
|
|
DigiDump( DIGIWRITE, (" Tin = 0x%hx\tTout = 0x%hx\n"
|
|
" WrReq = 0x%x\tWriteOffset = 0x%x\tLength = 0x%x\n",
|
|
Tin, Tout,
|
|
IrpSp->Parameters.Write.Length, Irp->IoStatus.Information,
|
|
IrpSp->Parameters.Write.Length - Irp->IoStatus.Information) );
|
|
|
|
// Determine how much room is available in this device's Tx Queue.
|
|
TxSize = (Tout - Tin - 1) & Tmax;
|
|
if( TxSize == 0 )
|
|
goto WriteTxBufferExit;
|
|
|
|
// Determine how much to write (i.e., TxWrite = MIN( write data, buffer space ))
|
|
// and whether we complete the write request (i.e., TxWrite == write data).
|
|
if( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL )
|
|
{
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR )
|
|
{
|
|
DigiDump( DIGIWRITE, ("WriteTxBuffer is transmitting IMMEDIATE_CHAR\n") );
|
|
WriteBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
|
|
}
|
|
else
|
|
{
|
|
DigiDump( (DIGIWRITE|DIGIDIAG1), ("WriteTxBuffer is transmitting XOFF_COUNTER\n") );
|
|
ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER );
|
|
ASSERT( DeviceExt->pXoffCounter == Irp->AssociatedIrp.SystemBuffer );
|
|
WriteBuffer = &DeviceExt->pXoffCounter->XoffChar;
|
|
}
|
|
TxWrite = 1;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ULONG remainder;
|
|
|
|
ASSERT( IrpSp->MajorFunction == IRP_MJ_WRITE );
|
|
|
|
WriteBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
|
|
WriteBuffer += Irp->IoStatus.Information;
|
|
|
|
ASSERT( Irp->IoStatus.Information <= IrpSp->Parameters.Write.Length );
|
|
remainder = IrpSp->Parameters.Write.Length - Irp->IoStatus.Information;
|
|
|
|
if( remainder <= (ULONG)TxSize )
|
|
{
|
|
TxWrite = (USHORT)remainder;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
TxWrite = TxSize; // Write as much as will fit.
|
|
}
|
|
}
|
|
ASSERT( TxWrite <= Tmax );
|
|
|
|
// Set up where we are going to start writing to the controller.
|
|
tx = (PUCHAR)( ControllerExt->VirtualAddress +
|
|
DeviceExt->TxSeg.Offset +
|
|
Tin );
|
|
|
|
// Handle circular buffer wrapping.
|
|
if( ((LONG)Tin + (LONG)TxWrite) > (LONG)Tmax )
|
|
{
|
|
USHORT Temp;
|
|
|
|
Temp = Tmax - Tin + 1;
|
|
ASSERT( Temp <= Tmax );
|
|
|
|
DigiDump( DIGIWRITE, (" tx:0x%x\tWriteBuffer:0x%x\tTemp:%hd\n",
|
|
tx, WriteBuffer, Temp) );
|
|
|
|
ASSERT( (tx + Temp) <= (ControllerExt->VirtualAddress +
|
|
DeviceExt->TxSeg.Offset +
|
|
ControllerExt->WindowSize) );
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->TxSeg.Window );
|
|
WRITE_REGISTER_BUFFER_UCHAR( tx, WriteBuffer, Temp );
|
|
DisableWindow( ControllerExt );
|
|
|
|
// Fix up all the values.
|
|
nBytesWritten += Temp;
|
|
WriteBuffer += Temp;
|
|
TxWrite -= Temp;
|
|
ASSERT( TxWrite <= Tmax );
|
|
|
|
tx = (PUCHAR)( ControllerExt->VirtualAddress +
|
|
DeviceExt->TxSeg.Offset );
|
|
}
|
|
|
|
DigiDump( DIGIWRITE, ("DigiBoard: tx:0x%x\tWriteBuffer:0x%x\tTxWrite:%hd\n",
|
|
tx, WriteBuffer, TxWrite) );
|
|
|
|
ASSERT( (tx + TxWrite) <= (ControllerExt->VirtualAddress +
|
|
DeviceExt->TxSeg.Offset +
|
|
ControllerExt->WindowSize) );
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->TxSeg.Window );
|
|
WRITE_REGISTER_BUFFER_UCHAR( tx, WriteBuffer, TxWrite );
|
|
DisableWindow( ControllerExt );
|
|
|
|
nBytesWritten += TxWrite;
|
|
|
|
Tin = (Tin + nBytesWritten) & Tmax;
|
|
|
|
DigiDump( DIGIWRITE, ("--------- BytesWritten = %d\tNew Tin = 0x%hx\n", nBytesWritten, Tin) );
|
|
|
|
if( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL )
|
|
{
|
|
ASSERT( nBytesWritten == 1 );
|
|
ASSERT( Status == STATUS_SUCCESS ); // otherwise, we would have jumped to WriteTxBufferExit
|
|
Irp->IoStatus.Information = nBytesWritten;
|
|
|
|
// If a transmitted XOFF_COUNTER is followed by a WRITE or XOFF_COUNTER,
|
|
// then let the transmitted XOFF_COUNTER complete.
|
|
if( DeviceExt->pXoffCounter )
|
|
{
|
|
ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER );
|
|
|
|
if( WriteQueue->Flink->Flink == WriteQueue ) // only XOFF_COUNTER on queue
|
|
{
|
|
Status = STATUS_PENDING;
|
|
}
|
|
else // two or more IRPs on queue
|
|
{
|
|
PIRP SecondIrp = CONTAINING_RECORD( WriteQueue->Flink->Flink, IRP, Tail.Overlay.ListEntry );
|
|
PIO_STACK_LOCATION SecondIrpSp = IoGetCurrentIrpStackLocation( SecondIrp );
|
|
|
|
if( SecondIrpSp->MajorFunction == IRP_MJ_WRITE
|
|
|| ( SecondIrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER
|
|
&& SecondIrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL
|
|
)
|
|
)
|
|
{
|
|
DigiDump( (DIGIWRITE|DIGIDIAG1), ("WriteTxBuffer is completing XOFF_COUNTER.\n") );
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
if( Status == STATUS_PENDING )
|
|
{
|
|
DigiDump( (DIGIWRITE|DIGIDIAG1), ("WriteTxBuffer transmitted XOFF_COUNTER, but completion is pending.\n") );
|
|
}
|
|
} // XOFF_COUNTER absorption
|
|
}
|
|
else
|
|
{
|
|
ASSERT( IrpSp->MajorFunction == IRP_MJ_WRITE );
|
|
Irp->IoStatus.Information += nBytesWritten;
|
|
}
|
|
|
|
DeviceExt->TotalCharsQueued -= nBytesWritten;
|
|
|
|
WriteTxBufferExit:
|
|
|
|
if( nBytesWritten )
|
|
{
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
// Beware: (tin == tout) implies empty buffer.
|
|
ASSERT( Tin != READ_REGISTER_USHORT( &ChInfo->tout ) );
|
|
WRITE_REGISTER_USHORT( &ChInfo->tin, Tin );
|
|
// If we ran out of space, request notification for when space is available.
|
|
if( Status == STATUS_PENDING )
|
|
WRITE_REGISTER_UCHAR( &ChInfo->ilow, TRUE );
|
|
WRITE_REGISTER_UCHAR( &ChInfo->iempty, TRUE );
|
|
DisableWindow( ControllerExt );
|
|
}
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWRITE), ("Exiting WriteTxBuffer\n") );
|
|
|
|
return( Status );
|
|
} // end WriteTxBuffer
|
|
|
|
BOOLEAN
|
|
IsTransmitterIdle( PDIGI_CONTROLLER_EXTENSION ControllerExt,
|
|
PDIGI_DEVICE_EXTENSION DeviceExt )
|
|
{
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
UCHAR Tbusy;
|
|
USHORT Tin, Tout;
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
Tbusy = READ_REGISTER_UCHAR( &ChInfo->tbusy );
|
|
Tin = READ_REGISTER_USHORT( &ChInfo->tin );
|
|
Tout = READ_REGISTER_USHORT( &ChInfo->tout );
|
|
DisableWindow( ControllerExt );
|
|
|
|
return !Tbusy && Tin == Tout;
|
|
}
|
|
|
|
NTSTATUS StartWriteRequest( IN PDIGI_CONTROLLER_EXTENSION ControllerExt,
|
|
IN PDIGI_DEVICE_EXTENSION DeviceExt,
|
|
IN PKIRQL pOldIrql )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine assumes the head of the DeviceExt->WriteQueue 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 write request.
|
|
|
|
DeviceExt - a pointer to the device extension associated with this write
|
|
request.
|
|
|
|
pOldIrql - a pointer to the IRQL associated with the current spin lock
|
|
of the device extension.
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY WriteQueue = &DeviceExt->WriteQueue;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN bFirstStatus = TRUE;
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWRITE), ("Entering StartWriteRequest: port = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
|
|
ASSERT( !IsListEmpty( WriteQueue ) );
|
|
|
|
do
|
|
{
|
|
PIRP Irp = CONTAINING_RECORD( WriteQueue->Flink, IRP, Tail.Overlay.ListEntry );
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
KIRQL OldCancelIrql;
|
|
|
|
if( IrpSp->MajorFunction == IRP_MJ_FLUSH_BUFFERS )
|
|
{
|
|
if( IsTransmitterIdle( ControllerExt, DeviceExt ) )
|
|
{
|
|
BOOLEAN discoveredIrp;
|
|
CHAR boost;
|
|
|
|
// Clear DigiCancelQueuedIrp, if set.
|
|
IoAcquireCancelSpinLock( &OldCancelIrql );
|
|
IoSetCancelRoutine( Irp, NULL );
|
|
IoReleaseCancelSpinLock( OldCancelIrql );
|
|
|
|
DigiRemoveIrp( WriteQueue );
|
|
discoveredIrp = !IsListEmpty( WriteQueue );
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
|
|
|
|
ExInterlockedAddUlong(&DeviceExt->ParentControllerExt->PerfData.BytesWritten,
|
|
Irp->IoStatus.Information,
|
|
&DeviceExt->ParentControllerExt->PerfLock);
|
|
|
|
ExInterlockedAddUlong(&DeviceExt->PerfData.BytesWritten,
|
|
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;
|
|
}
|
|
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.
|
|
}
|
|
else // Transmitter is busy.
|
|
{
|
|
LARGE_INTEGER DelayInterval;
|
|
|
|
DIGI_INIT_REFERENCE( Irp );
|
|
|
|
IoAcquireCancelSpinLock( &OldCancelIrql );
|
|
IoSetCancelRoutine( Irp, DigiCancelCurrentFlush );
|
|
// Increment because the CancelRoutine knows about this IRP.
|
|
DIGI_INC_REFERENCE( Irp );
|
|
// Increment because the timer routine knows about this IRP.
|
|
DIGI_INC_REFERENCE( Irp );
|
|
IoReleaseCancelSpinLock( OldCancelIrql );
|
|
|
|
// Delay for 1 milliseconds
|
|
#if rmm < 807
|
|
DelayInterval = RtlConvertLongToLargeInteger( 1 * -10000 );
|
|
#else
|
|
DelayInterval.QuadPart = Int32x32To64( 1, -10000 );
|
|
#endif
|
|
|
|
KeSetTimer( &DeviceExt->FlushBuffersTimer,
|
|
DelayInterval,
|
|
&DeviceExt->FlushBuffersDpc );
|
|
|
|
if( bFirstStatus )
|
|
Status = STATUS_PENDING;
|
|
|
|
break;
|
|
}
|
|
} // IRP_MJ_FLUSH_BUFFERS
|
|
|
|
if( Irp->IoStatus.Information != MAXULONG )
|
|
{
|
|
// Someone else has already started the IRP.
|
|
break;
|
|
}
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoAcquireCancelSpinLock( &OldCancelIrql );
|
|
IoSetCancelRoutine( Irp, NULL );
|
|
IoReleaseCancelSpinLock( OldCancelIrql );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER
|
|
&& IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL )
|
|
{
|
|
ASSERT( DeviceExt->pXoffCounter == NULL );
|
|
DeviceExt->pXoffCounter = Irp->AssociatedIrp.SystemBuffer;
|
|
DeviceExt->XcPreview = 0;
|
|
}
|
|
|
|
if( WriteTxBuffer( DeviceExt ) == STATUS_PENDING )
|
|
{
|
|
//
|
|
// Quick check to make sure this Irp hasn't been cancelled.
|
|
//
|
|
IoAcquireCancelSpinLock( &OldCancelIrql );
|
|
if( Irp->Cancel )
|
|
{
|
|
BOOLEAN discoveredIrp;
|
|
|
|
IoReleaseCancelSpinLock( OldCancelIrql );
|
|
|
|
DigiRemoveIrp( WriteQueue );
|
|
discoveredIrp = !IsListEmpty( WriteQueue );
|
|
if( DeviceExt->pXoffCounter )
|
|
DeviceExt->pXoffCounter = NULL;
|
|
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
|
|
{
|
|
ULONG WriteTotalTimeoutConstant, WriteTotalTimeoutMultiplier;
|
|
|
|
DIGI_INIT_REFERENCE( Irp );
|
|
// Increment because the CancelRoutine knows about this Irp.
|
|
DIGI_INC_REFERENCE( Irp );
|
|
IoSetCancelRoutine( Irp, DigiCancelCurrentWrite );
|
|
IoReleaseCancelSpinLock( OldCancelIrql );
|
|
|
|
//
|
|
// Get the current timeout values, we can access this now
|
|
// because we are under the protection of the spin lock already.
|
|
//
|
|
if( DeviceExt->pXoffCounter )
|
|
{
|
|
WriteTotalTimeoutConstant = DeviceExt->pXoffCounter->Timeout;
|
|
WriteTotalTimeoutMultiplier = 0; // so IrpSp->Parameters.Write.Length won't kill us
|
|
}
|
|
else
|
|
{
|
|
WriteTotalTimeoutConstant = DeviceExt->Timeouts.WriteTotalTimeoutConstant;
|
|
WriteTotalTimeoutMultiplier = DeviceExt->Timeouts.WriteTotalTimeoutMultiplier;
|
|
}
|
|
|
|
if( WriteTotalTimeoutConstant
|
|
|| WriteTotalTimeoutMultiplier )
|
|
{
|
|
LARGE_INTEGER TotalTime;
|
|
|
|
//
|
|
// We have some timer values to calculate.
|
|
//
|
|
// Take care, we might have an xoff counter masquerading
|
|
// as a write.
|
|
//
|
|
|
|
DigiDump( DIGIWRITE, (" Should use total timer.\n"
|
|
" WriteTotalTimeoutMultiplier = 0x%x\n"
|
|
" WriteTotalTimeoutConstant = 0x%x\n",
|
|
WriteTotalTimeoutMultiplier,
|
|
WriteTotalTimeoutConstant ) );
|
|
|
|
#if rmm < 807
|
|
TotalTime = RtlEnlargedUnsignedMultiply(
|
|
IrpSp->Parameters.Write.Length,
|
|
WriteTotalTimeoutMultiplier );
|
|
|
|
TotalTime = RtlLargeIntegerAdd( TotalTime,
|
|
RtlConvertUlongToLargeInteger(
|
|
WriteTotalTimeoutConstant ) );
|
|
|
|
TotalTime = RtlExtendedIntegerMultiply( TotalTime, -10000 );
|
|
#else
|
|
TotalTime.QuadPart =
|
|
((LONGLONG)((UInt32x32To64(
|
|
(IrpSp->MajorFunction == IRP_MJ_WRITE)?
|
|
(IrpSp->Parameters.Write.Length):
|
|
(1),
|
|
WriteTotalTimeoutMultiplier
|
|
)
|
|
+ WriteTotalTimeoutConstant)))
|
|
* -10000;
|
|
#endif
|
|
|
|
DigiDump( DIGIWRITE, (" TotalTime = 0x%x:%.8x\n",
|
|
TotalTime.HighPart, TotalTime.LowPart ) );
|
|
|
|
if( !DeviceExt->pXoffCounter )
|
|
{
|
|
//
|
|
// We should readjust the total timer for any characters
|
|
// which may already be in the buffer.
|
|
//
|
|
}
|
|
|
|
DIGI_INC_REFERENCE( Irp );
|
|
KeSetTimer( &DeviceExt->WriteRequestTotalTimer,
|
|
TotalTime, &DeviceExt->TotalWriteTimeoutDpc );
|
|
}
|
|
|
|
if( bFirstStatus )
|
|
Status = STATUS_PENDING;
|
|
|
|
break;
|
|
} // not canceled
|
|
|
|
ASSERT( FALSE ); // not reached
|
|
|
|
}
|
|
else // WriteTxBuffer == SUCCESS
|
|
{
|
|
BOOLEAN discoveredIrp;
|
|
CHAR boost;
|
|
|
|
DigiDump( DIGIWRITE, (" completing write request on 1st attempt\n"
|
|
" #bytes completing = %d\n",
|
|
Irp->IoStatus.Information ) );
|
|
|
|
//
|
|
// We have completed the Irp before from the start so no
|
|
// timers or cancels were associated with it.
|
|
//
|
|
ExInterlockedAddUlong(&DeviceExt->ParentControllerExt->PerfData.BytesWritten,
|
|
Irp->IoStatus.Information,
|
|
&DeviceExt->ParentControllerExt->PerfLock);
|
|
|
|
ExInterlockedAddUlong(&DeviceExt->PerfData.BytesWritten,
|
|
Irp->IoStatus.Information,
|
|
&DeviceExt->PerfLock);
|
|
|
|
|
|
DigiRemoveIrp( WriteQueue );
|
|
discoveredIrp = !IsListEmpty( WriteQueue );
|
|
if( DeviceExt->pXoffCounter )
|
|
{
|
|
DeviceExt->pXoffCounter = NULL;
|
|
boost = IO_SERIAL_INCREMENT; // probably DOS, so boost it even if it's immediate
|
|
Irp->IoStatus.Status = STATUS_SERIAL_MORE_WRITES;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
|
|
|
|
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( WriteQueue ) );
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWRITE), ("Exiting StartWriteRequest: port = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
return( Status );
|
|
} // end StartWriteRequest
|
|
|
|
VOID DeferredFlushBuffers( PKDPC Dpc,
|
|
PVOID Context,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2 )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the transmitter is initially busy, StartWriteRequest sets a timer to call this routine.
|
|
Effectively, this routine polls the transmitter until it is idle.
|
|
|
|
Arguments:
|
|
|
|
Context - pointer to the Device Extension information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDIGI_DEVICE_EXTENSION DeviceExt = Context;
|
|
PLIST_ENTRY WriteQueue = &DeviceExt->WriteQueue;
|
|
PIRP Irp;
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWAIT), ("Entering DeferredFlushBuffers.\n") );
|
|
|
|
KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
|
|
|
|
Irp = CONTAINING_RECORD( WriteQueue->Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
|
|
ASSERT( IoGetCurrentIrpStackLocation( Irp )->MajorFunction == IRP_MJ_FLUSH_BUFFERS );
|
|
|
|
if( IsTransmitterIdle( DeviceExt->ParentControllerExt, DeviceExt ) )
|
|
{
|
|
KIRQL OldIrql = DISPATCH_LEVEL;
|
|
|
|
DigiTryToCompleteIrp( DeviceExt,
|
|
&OldIrql,
|
|
STATUS_SUCCESS,
|
|
WriteQueue,
|
|
NULL,
|
|
&DeviceExt->FlushBuffersTimer,
|
|
StartWriteRequest );
|
|
}
|
|
else
|
|
{
|
|
TIME DelayInterval;
|
|
|
|
//
|
|
// We haven't drained yet. Just reset the timer.
|
|
//
|
|
|
|
// Delay for 1 milliseconds
|
|
#if rmm < 807
|
|
DelayInterval = RtlConvertLongToLargeInteger( 1 * -10000 );
|
|
#else
|
|
DelayInterval.QuadPart = Int32x32To64( 1, -10000 );
|
|
#endif
|
|
|
|
KeSetTimer( &DeviceExt->FlushBuffersTimer,
|
|
DelayInterval,
|
|
&DeviceExt->FlushBuffersDpc );
|
|
|
|
KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
|
|
}
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWAIT), ("Exiting DeferredFlushBuffers.\n") );
|
|
|
|
} // end DeferredFlushBuffers
|
|
|
|
VOID DigiCancelCurrentWrite( PDEVICE_OBJECT DeviceObject, PIRP Irp )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to cancel the current write.
|
|
|
|
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|DIGIWRITE), ("Entering DigiCancelCurrentWrite: port = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
|
|
DigiDump( DIGICANCELIRP, ( "Canceling write Irp! 0x%x\n",
|
|
Irp ) );
|
|
|
|
//
|
|
// We flush the transmit queue because if the request was
|
|
// cancelled, then the data just doesn't matter.
|
|
//
|
|
FlushTransmitBuffer( DeviceExt->ParentControllerExt, DeviceExt );
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
|
|
STATUS_CANCELLED, &DeviceExt->WriteQueue,
|
|
NULL,
|
|
&DeviceExt->WriteRequestTotalTimer,
|
|
StartWriteRequest );
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWRITE), ("Exiting DigiCancelCurrentWrite: port = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
} // end DigiCancelCurrentWrite
|
|
|
|
VOID DigiCancelCurrentFlush( PDEVICE_OBJECT DeviceObject, PIRP Irp )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The Flush Irp is being cancelled. We need to flush the transmit
|
|
buffer on the controller so the DrainTransmit routine will finally
|
|
return. Otherwise, the routine wouldn't return until possibly
|
|
some long time later.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
PDIGI_DEVICE_EXTENSION DeviceExt;
|
|
KIRQL OldIrql;
|
|
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
|
|
DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
DigiDump( (DIGIFLOW|DIGICANCELIRP), ("Canceling CurrentFlush Irp 0x%x\n",
|
|
Irp) );
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
DigiTryToCompleteIrp( DeviceExt,
|
|
&OldIrql,
|
|
STATUS_CANCELLED,
|
|
&DeviceExt->WriteQueue,
|
|
NULL,
|
|
&DeviceExt->FlushBuffersTimer,
|
|
StartWriteRequest );
|
|
|
|
} // end DigiCancelCurrentFlush
|
|
|
|
VOID DigiWriteTimeout( IN PKDPC Dpc, IN PVOID DeferredContext,
|
|
IN PVOID SystemContext1, IN PVOID SystemContext2 )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to complete a write 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;
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWRITE), ("Entering DigiWriteTimeout: port = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(SystemContext1);
|
|
UNREFERENCED_PARAMETER(SystemContext2);
|
|
|
|
KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
|
|
|
|
FlushTransmitBuffer( DeviceExt->ParentControllerExt, DeviceExt );
|
|
|
|
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
|
|
DeviceExt->pXoffCounter ? STATUS_SERIAL_COUNTER_TIMEOUT : STATUS_TIMEOUT,
|
|
&DeviceExt->WriteQueue,
|
|
NULL,
|
|
&DeviceExt->WriteRequestTotalTimer,
|
|
StartWriteRequest );
|
|
|
|
DigiDump( (DIGIFLOW|DIGIWRITE), ("Exiting DigiWriteTimeout: port = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
} // end DigiWriteTimeout
|
|
|