/*++
*******************************************************************************
* Copyright (c) 1999 - 2000 Digi International Inc., All Rights Reserved             
*                                                                             
* 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.                                                                 
*                                                                             
* Restricted Rights Legend                                                    
*                                                                             
* Use, duplication, or disclosure by the Government is subject to             
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in         
* Technical Data and Computer Software clause at DFARS 252.227-7031 or        
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -          
* Restricted Rights at 48 CFR 52.227-19, as applicable.                       
*                                                                             
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343          
*                                                                             
*******************************************************************************

Module Name:

   AsynCreate.c

Abstract:


Author:

   Sandy Haapala

--*/

#include "digiasyn.h"

#define ISDN_MESSAGE_ENCAP_SIZE  4

STATIC __inline VOID
CompletePendingIrp
(
	IN PASYN_PORT_EXT	pPortExt,
	IN NTSTATUS			Status
)
{
	// Complete the pending Create or Close IRP
	PIRP			pIrp;

	DigiAcquireSpinLock( &pPortExt->Lock );
	pIrp = pPortExt->pPendingIrp;
	pPortExt->pPendingIrp = NULL;   
	DigiReleaseSpinLock( &pPortExt->Lock );

	if ( pIrp != NULL )
	{   
		DigiDump( DIGICREATE|DIGICLOSE|DIGIFLOW, ("Port %i completing %p with status=0x%x \n",pPortExt->iSdxbPort,pIrp,Status));
		pIrp->IoStatus.Information = 0; 
		AsynIoCompleteRequest( pIrp, Status, NULL );
	}      
}      


STATIC VOID
CloseComplete
(
	IN HANDLE         HOwner,
	IN NTSTATUS       Status
)
{
	PASYN_PORT_EXT	pPortExt = (PASYN_PORT_EXT)HOwner;
	DigiDump(DIGICLOSE|DIGIFLOW, ("DigiAsyn!CloseComplete for Port=%d CurrentIrql=0x%x\n", pPortExt->iSdxbPort,KeGetCurrentIrql()));

	DigiAcquireSpinLock( &pPortExt->Lock );
	pPortExt->DsPortHandle = NULL;
	pPortExt->State = IdleState;
#if NT5_BUILD   
   if ( pPortExt->DevPower != PowerDeviceD3 )
#endif   
	{   
		DigiReleaseSpinLock( &pPortExt->Lock );
		CompletePendingIrp( pPortExt, STATUS_SUCCESS );
      
	}
#if NT5_BUILD   
   else
   {
		DigiReleaseSpinLock( &pPortExt->Lock );      
	}      
#endif   
}


VOID
AsynCloseSdxbPort
(
	IN PASYN_PORT_EXT pPortExt
)
{
	DigiDump(DIGICLOSE|DIGIFLOW, ("Entering DigiAsyn!CloseSdxbPort for Port=%d\n", pPortExt->iSdxbPort));
	DigiAcquireSpinLock( &pPortExt->Lock );

	if ( pPortExt->DsPortHandle )
	{   
		pPortExt->State = CloseInProgressState;
      
		SdxbFlush( pPortExt->DsPortHandle, SDXB_FLUSH_OUTPUT | SDXB_FLUSH_INPUT );                              
      
		{
			// Drop hardware flow control
			UCHAR	SavedMflow = pPortExt->SdxbValues.PortParams.mflow;
         pPortExt->SdxbValues.PortParams.mflow = 0;
			SdxbParam( pPortExt->DsPortHandle, &pPortExt->SdxbValues.PortParams );
         pPortExt->SdxbValues.PortParams.mflow = SavedMflow;
		}      

		// Drop modem signals
		if ( pPortExt->SdxbValues.Mout & (SDXB_DTR | SDXB_RTS))
			( void )SdxbMstat( pPortExt->DsPortHandle, 0, (UCHAR)(pPortExt->SdxbValues.Mout & ~(SDXB_DTR|SDXB_RTS)), TRUE );

		// Turn off break
#define BREAK_OFF	0
		(void)SdxbBreak( pPortExt->DsPortHandle, BREAK_OFF );

		// Wait for S/DXB to complete the previous requests...
		pPortExt->State = CloseOnFinalSyncState;      
	   pPortExt->Close.SyncSeq = ++pPortExt->SdxbValues.SyncSeqNum;
		DigiReleaseSpinLock( &pPortExt->Lock );
	   ( void )SdxbSync( pPortExt->DsPortHandle, pPortExt->Close.SyncSeq, TRUE );
	}      
	else
	{
		pPortExt->State = IdleState;
		DigiReleaseSpinLock( &pPortExt->Lock );
		CompletePendingIrp( pPortExt, STATUS_SUCCESS );
	}	
	DigiDump(DIGICLOSE|DIGIFLOW, ("Leaving DigiAsyn!CloseSdxbPort for Port=%d\n", pPortExt->iSdxbPort));
}


VOID
AsynCloseOnSdxbSync
(
	IN PASYN_PORT_EXT	pPortExt,
	IN USHORT			SyncSeq
)
{
#if DBG
	{
		LARGE_INTEGER	CurrentSystemTime;
		KeQuerySystemTime( &CurrentSystemTime );
		DigiDump(DIGICLOSE|DIGIFLOW, ("AsynCloseOnSdxbSync for Port=%d at %u:%u\n", 
		     				pPortExt->iSdxbPort,CurrentSystemTime.HighPart,CurrentSystemTime.LowPart));
	}            
#endif      
	DigiAcquireSpinLock( &pPortExt->Lock );
   switch ( pPortExt->State )
   {
   	case CloseWaitTxDrainState:
      	// Verify that this is the sync we expected
      	if ( pPortExt->Close.SyncSeq == SyncSeq )  
			{
				KeCancelTimer( &pPortExt->Close.Timer );
            DigiReleaseSpinLock( &pPortExt->Lock );
				DigiDump(DIGICLOSE|DIGIFLOW, ("Cancelled close timer for Port=%d\n", pPortExt->iSdxbPort));
            AsynCloseSdxbPort( pPortExt );
			}  
			else   
				DigiReleaseSpinLock( &pPortExt->Lock );              
         break;
		case CloseOnFinalSyncState:
			// Discard any received data
			AsynPurgeRxBuffers( pPortExt );

			// Stop both high and low priority RxEngines
			{
				HANDLE hLowPriority = pPortExt->hRxEngine_LowPriority;
				HANDLE hHighPriority = pPortExt->hRxEngine_HighPriority;
			   
				pPortExt->hRxEngine_LowPriority = NULL;
				pPortExt->hRxEngine_HighPriority = NULL;

				DigiReleaseSpinLock( &pPortExt->Lock );
			   
				if ( hLowPriority )
					RxEngineStop( hLowPriority );
				if ( hHighPriority )
					RxEngineStop( hHighPriority );   
			}

			( void )DsPurge( pPortExt->DsPortHandle, DS_PURGE_ALL);

			// Free the DStreams resources for this port
			DsCloseEx( pPortExt->DsPortHandle, CloseComplete);
         break;
		default:
			DigiReleaseSpinLock( &pPortExt->Lock );              
	}         
}   


VOID
AsynCloseTimedOut
(
	IN PKDPC       pPortDpc,      // DPC object for Port
	IN PASYN_PORT_EXT
					 	pPortExt,  		// Pointer to the port structure of the device extension
	IN PVOID       SystemArg1,    // System argument.
	IN PVOID       SystemArg2     // System argument.
)
/*++

Description

	DPC for the close timer.
   
--*/
{
	UNREFERENCED_PARAMETER( pPortDpc);
	UNREFERENCED_PARAMETER( SystemArg1);
	UNREFERENCED_PARAMETER( SystemArg2);
   
#if DBG   
	{
		LARGE_INTEGER	CurrentSystemTime;
		KeQuerySystemTime( &CurrentSystemTime );
		DigiDump(DIGICLOSE|DIGIFLOW, ("DigiAsyn!AsynCloseTimedOut for Port=%d at %u:%u\n",
		      				pPortExt->iSdxbPort,CurrentSystemTime.HighPart,CurrentSystemTime.LowPart));
	}                        
#endif                        
	if ( pPortExt->State == CloseWaitTxDrainState )
	{   
		SdxbFlush( pPortExt->DsPortHandle, SDXB_FLUSH_OUTPUT | SDXB_FLUSH_INPUT );                              
		AsynCloseSdxbPort( pPortExt );
	}      
}



STATIC NTSTATUS PortClose
(
	IN PASYN_PORT_EXT 	pPortExt,
	IN PIRP 					pIrp
)
/*++

Description:


Parameters:


--*/
{
	NTSTATUS Status = STATUS_PENDING;
   
	DigiAcquireSpinLock( &pPortExt->Lock );
   if ( pPortExt->PnpModemDetectHandle != NULL )
	{
		PnpModemCleanup( pPortExt->PnpModemDetectHandle );
		pPortExt->PnpModemDetectHandle = NULL;
	}   
	pPortExt->Wait.Mask = 0;
   
#if NT5_BUILD   
   if ( pPortExt->DevPower == PowerDeviceD3 )
	{
		// Device is in a powered down state. Succeed the close request   
		if ( pPortExt->State == OpenState_PowerD3 )  
      {
			Status = STATUS_SUCCESS;
         pPortExt->State = IdleState_PowerD3;
		}               
      else
      {
			// Power processing is in progress - pend the close IRP for now...      
			pPortExt->pPendingIrp = pIrp;
			IoMarkIrpPending( pIrp );      
		}
		DigiReleaseSpinLock( &pPortExt->Lock );      
		goto PortCloseExit;               
	}   
#endif    
   pPortExt->pPendingIrp = pIrp;
   pPortExt->State = CloseWaitTxDrainState;
	DigiReleaseSpinLock ( &pPortExt->Lock );
	IoMarkIrpPending( pIrp );
      
   switch ( pPortExt->Close.TxDrainTimeout )
   {
   	case 0: 
      	// No timeout: purge transmit data now
			SdxbFlush( pPortExt->DsPortHandle, SDXB_FLUSH_OUTPUT | SDXB_FLUSH_INPUT );                              
         ( void )DsPurge( pPortExt->DsPortHandle, DS_PURGE_WRITE | DS_PURGE_PUTMSG );
         break;
		case MAXULONG:
      	// Wait forever for transmit data to drain
         //   TODO: Revisit this - have code watch for progress??
         break;
      default:
   	{
      	// set timeout for transmit data to drain. If timeout expires, the transmit data will be purged.
      	LARGE_INTEGER	Timeout;
         Timeout.QuadPart = (LONGLONG)pPortExt->Close.TxDrainTimeout * ONE_MILLISECOND;
#if DBG
			{
				LARGE_INTEGER	CurrentSystemTime;
            KeQuerySystemTime( &CurrentSystemTime );
			   DigiDump(DIGICLOSE|DIGIFLOW, ("Set close timer for Port=%d at %u:%u\n", 
            				pPortExt->iSdxbPort,CurrentSystemTime.HighPart,CurrentSystemTime.LowPart));
			}            
#endif      
         KeSetTimer( &pPortExt->Close.Timer, Timeout, &pPortExt->Close.DPC );
      }         
	}         
   
	// Request sync notification when transmit queue is empty
   pPortExt->Close.SyncSeq = ++pPortExt->SdxbValues.SyncSeqNum;
   ( void )SdxbSync( pPortExt->DsPortHandle, pPortExt->Close.SyncSeq, TRUE );
#if NT5_BUILD   
PortCloseExit:   
#endif
	return Status;
}



NTSTATUS AsynDeviceClose
(
	IN PDEVICE_OBJECT 	pDeviceObject,
	IN PIRP             	pIrp        
)
{
	NTSTATUS    Status = STATUS_SUCCESS;
	DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceClose: Entering\n"));

	// Check for close of port versus controller
	switch ( pDeviceObject->DeviceType )
	{
		case FILE_DEVICE_CONTROLLER:
		{
			// Always succeed close for the controller
			Status = STATUS_SUCCESS;
			break;
		}         
		case FILE_DEVICE_SERIAL_PORT:
		{
			PASYN_PORT_EXT	pPortExt = pDeviceObject->DeviceExtension;
	        DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceClose: Irp=%p for Port=%d\n",pIrp,pPortExt->iSdxbPort));
			if ( pPortExt->Type != ASYN_TYPE_PORT_EXT )
			{
				Status = STATUS_UNSUCCESSFUL;
			}            
			else
			{
				Status = PortClose( pPortExt, pIrp );
			}            
			break;
		}   
		case FILE_DEVICE_UNKNOWN:  // This is for the dga global object
		{
			Status = STATUS_SUCCESS;
			break;
		}
		default:
			Status = STATUS_UNSUCCESSFUL;
	}               

	if ( Status != STATUS_PENDING )
	{
		pIrp->IoStatus.Information = 0;
		AsynIoCompleteRequest( pIrp, Status, NULL );
	}      
	DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceClose: leaving with Status=0x%x \n",Status));
	return Status;
}   





VOID
AsynOpenOnSdxbSync
(
	IN	PASYN_PORT_EXT	pPortExt,
	IN USHORT			SyncSeq
)
{
	NTSTATUS Status;

	// Start the RxEngine for normal priority (data/control) messages. 
	Start_LowPriority_RxEngine( pPortExt );
	// if successful, set port state to OPEN
	DigiAcquireSpinLock( &pPortExt->Lock );
	if ( pPortExt->hRxEngine_LowPriority )
	{
		pPortExt->State = OpenState;
		DigiReleaseSpinLock( &pPortExt->Lock );
		Status = STATUS_SUCCESS;
		CompletePendingIrp( pPortExt, Status );
	}	       
	else
	{
		// Error starting the RxEngine
		pPortExt->State = IdleState;
		DigiReleaseSpinLock( &pPortExt->Lock );
		AsynLogRxEngineFailed( &pPortExt->PortName );
		AsynCloseSdxbPort( pPortExt );
		Status = STATUS_UNSUCCESSFUL;
	}
}

/*++

Description:

	Initialize/restore the S/DXB settings after a port open

--*/
NTSTATUS AsynInitializeSdxbPort
(
	IN PASYN_PORT_EXT 	pPortExt,
	IN BOOLEAN 				fPowerStateChange
)   
{

	NTSTATUS 	Status;
	PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
	HANDLE		DsPortHandle = pPortExt->DsPortHandle;

	// Request Buffer Size
	Status = SdxbBufsize( DsPortHandle );
	if ( NT_ERROR( Status ) )
		goto Exit;
      
	// Request Port Capabilities
	Status = SdxbPcap( DsPortHandle );
	if ( NT_ERROR( Status ) )
		goto Exit;
      
	// Set the port parameters
	Status = SdxbParam( DsPortHandle, &pSdxbExt->PortParams );
	if ( NT_ERROR( Status ) )
		goto Exit;
   
	// Get current modem settings.
	pPortExt->SdxbValues.Mout |= SDXB_DTR|SDXB_RTS;
	Status = SdxbMstat( DsPortHandle, pPortExt->SdxbValues.Mint, pPortExt->SdxbValues.Mout, TRUE );
	if ( NT_ERROR( Status ) )
		goto Exit;
   
		// Request Event reporting for all event types
		{
		USHORT Events = SDXB_TXB | SDXB_TXI | SDXB_RXB | SDXB_IPS | SDXB_OPU | SDXB_OPS | 
	                            SDXB_OPX | SDXB_OPH | SDXB_IPU | SDXB_IPS;
		Status = SdxbEvent( DsPortHandle, Events );     
		if ( NT_ERROR( Status ) )
			goto Exit;
	}
   
	// Line Error Control
	// TODO: Reset error counters...
	Status = SdxbLerr( DsPortHandle, pPortExt->SdxbValues.Ltime );
	if ( NT_ERROR( Status ) )
		goto Exit;

	// Sequence Number Control
	Status = SdxbSeq( DsPortHandle, pPortExt->SdxbValues.Rmax, pPortExt->SdxbValues.Rtime,
									pPortExt->SdxbValues.Tmax, pPortExt->SdxbValues.Ttime, pPortExt->SdxbValues.Icdtime );
	if ( NT_ERROR( Status ) )
		goto Exit;

	// Flush all input and output buffers
	Status = SdxbFlush( DsPortHandle, SDXB_FLUSH_OUTPUT | SDXB_FLUSH_INPUT );                              
	if ( NT_ERROR( Status ) )
		goto Exit;

	if ( !fPowerStateChange )
   	// No need for a sync when restoring the power state
		( void )SdxbSync( DsPortHandle, pPortExt->SdxbValues.SyncSeqNum, FALSE );

Exit:
	return Status;
}   


NTSTATUS
AsynResetPortSettings
(
	IN PASYN_PORT_EXT pPortExt
)
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
   
   if ( pPortExt == NULL )
   	goto Exit;
      
	if ( pPortExt->Type != ASYN_TYPE_PORT_EXT )
   	goto Exit;      

   // NOTE: BAUD rate, start/stop bits and hardware flow control are maintained across opens.
   {
		PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
      
      DigiAcquireSpinLock( &pSdxbExt->Lock );
      
      pSdxbExt->Mstat = 0;
      pSdxbExt->Estat = 0;
      
	   // Reinitialize error counters 
      pSdxbExt->Norun = 0;
      pSdxbExt->Noflow = 0;
      pSdxbExt->Nframe = 0;
      pSdxbExt->Nparity = 0;
      pSdxbExt->Nbreak = 0;

		// Restart sequence number
		pSdxbExt->SyncSeqNum = 0;
		pSdxbExt->SyncSeqAck = 0;     
      
      // Reset error counters
      pSdxbExt->CommErr = 0;
	   RtlZeroMemory(&pSdxbExt->PerfStats, sizeof(pSdxbExt->PerfStats));

      DigiReleaseSpinLock( &pSdxbExt->Lock );
   }

   // Reset read specific variables 
   AsynRead_OnCreate( &pPortExt->Read );
   // Reset Write specific variables
   pPortExt->Write.Sequence.CurrentCountOut = 0;
   pPortExt->Write.Sequence.CurrentCountIn = 0;
   pPortExt->Write.Sequence.CurrentCountReference = 0;
   
   // Reset Wait mask and history 
   {
   	PASYN_WAIT pWaitExt = &pPortExt->Wait;
      
      pWaitExt->pPendingIrp = NULL;
      pWaitExt->History = 0;
      pWaitExt->Mask = 0;
	}      
   
   // Start the high priority RxEngine for control-only messages
   {
		Start_HighPriority_RxEngine( pPortExt )
		if ( !pPortExt->hRxEngine_HighPriority )
      {
			AsynLogRxEngineFailed( &pPortExt->PortName );
         Status = STATUS_UNSUCCESSFUL;
         goto Exit;
      }
   }
   
	// Initialize/restore the S/DXB settings 
   Status = AsynInitializeSdxbPort( pPortExt, FALSE );
   if ( NT_ERROR( Status ))
   	goto Exit;
   
   // Initialize PnpModem Detection routine (for HLC ports)
   if ( pPortExt->IsModem )
   {
   	pPortExt->PnpModemDetectHandle = PnpModemInit();
   }
   else
   	pPortExt->PnpModemDetectHandle = NULL;
Exit:	
   DigiDump(DIGICREATE, ("AsynResetPortSettings for Port=%i and Irp=%p completed with status = %x\n",pPortExt->iSdxbPort,pPortExt->pPendingIrp,Status));
	return Status;   
}


STATIC NTSTATUS PortCreate
(
	IN PASYN_PORT_EXT 	pPortExt,
	IN PIRP 					pIrp
)

/*++

Description:


Parameters:


--*/
{
	NTSTATUS Status;
	ULONG		ErrorCode = 0;

	DigiAcquireSpinLock( &pPortExt->Lock );
	// Verify that the port can be opened and that it is not already opened
#if NT5_BUILD
	if ( pPortExt->State != IdleState && pPortExt->State != IdleState_PowerD3 )
#else   
	if ( pPortExt->State != IdleState )
#endif   
	{
		DigiDump( DIGICREATE, ("DigiAsyn!PortCreate: Port %d not in IdleState - state %d\n",pPortExt->iSdxbPort,pPortExt->State ));   
		Status = STATUS_ACCESS_DENIED;
		DigiReleaseSpinLock( &pPortExt->Lock );
		goto Exit;      	
	}   
   
#if NT5_BUILD
	// Make sure the device is currently powered on
	if ( pPortExt->DevPower != PowerDeviceD0 )
	{
		DigiDump( DIGICREATE|DIGIPOWER, ("DigiAsyn!PortCreate: Port %d is not in Power state D0 - marking IRP pending\n",pPortExt->iSdxbPort));        
      pPortExt->pPendingIrp = pIrp;
      pPortExt->State = OpeningState_PowerD3;
		DigiReleaseSpinLock( &pPortExt->Lock );      
		Status = STATUS_PENDING;
		IoMarkIrpPending( pIrp );
		goto Exit;
	}		            
#endif   

	// Set port state to indicate that Open is in progress
	pPortExt->State = OpenInProgressState;
	DigiReleaseSpinLock( &pPortExt->Lock );      

	// TODO: Code DsOpen for ISDN call control (Acotec) option
	Status = DsOpen( pPortExt->DsHandle, "SERIAL", (HANDLE)pPortExt, &pPortExt->iSdxbPort,
						sizeof (pPortExt->iSdxbPort), &pPortExt->DsPortHandle, &ErrorCode);

	if (NT_ERROR( Status ))
	{
		DigiDump( DIGICREATE|DIGIERROR,("DigiAsyn!PortCreate DsOpen failed with Status=%x ErrorCode=%x\n",Status,ErrorCode));
		AsynLogDsOpenFailed( &pPortExt->PortName );
		DigiAcquireSpinLock( &pPortExt->Lock );
		pPortExt->State = IdleState;
		DigiReleaseSpinLock( &pPortExt->Lock );
	}
	else
	{
		// Irp will be completed later, after a sync response from s/dxb
		DigiAcquireSpinLock( &pPortExt->Lock );
		pPortExt->pPendingIrp = pIrp;
		DigiReleaseSpinLock( &pPortExt->Lock );
		IoMarkIrpPending( pIrp );
		if (NT_ERROR( AsynResetPortSettings( pPortExt )))
		{
			AsynCloseSdxbPort( pPortExt );
		}         
		Status = STATUS_PENDING;
	}
Exit:   
	return Status;
}


NTSTATUS AsynDeviceCreate
(
	IN PDEVICE_OBJECT pDeviceObject,
	IN PIRP pIrp
)

/*++

Description:


Parameters:


--*/
{
	NTSTATUS    Status = STATUS_UNSUCCESSFUL;

	DigiDump( DIGICREATE|DIGIFLOW, ("AsynDeviceCreate: Entering Irp=%p\n",pIrp));

	// Check for open of port versus controller
	switch ( pDeviceObject->DeviceType )
	{
		case FILE_DEVICE_CONTROLLER:
		{
			// Always succeed create for the controller
			Status = STATUS_SUCCESS;
			break;
		}         
		case FILE_DEVICE_SERIAL_PORT:
		{
			PASYN_PORT_EXT	pPortExt = pDeviceObject->DeviceExtension;
	        DigiDump( DIGICREATE|DIGIFLOW, ("AsynDeviceCreate: Irp=%p for Port=%d\n",pIrp,pPortExt->iSdxbPort));

			if ( pPortExt == NULL || pPortExt->Type != ASYN_TYPE_PORT_EXT )
			{
				Status = STATUS_UNSUCCESSFUL;
			}            
			else
			{
				Status = PortCreate( pPortExt, pIrp );
			}            
			break;
		}
		case FILE_DEVICE_UNKNOWN:           // This is for the access of the dgamode global object
		{
			Status = STATUS_SUCCESS;
			break;
		}
		default:
			Status = STATUS_UNSUCCESSFUL;
	}               

	DigiDump( DIGICREATE|DIGIFLOW, ("AsynDeviceCreate: Irp=%p leaving with Status=0x%x \n",pIrp,Status));
	if ( Status != STATUS_PENDING)
	{
		pIrp->IoStatus.Information = 0;
		AsynIoCompleteRequest( pIrp, Status, NULL );
	}
	return Status;
}


NTSTATUS AsynDeviceCleanup
(
	IN PDEVICE_OBJECT pDeviceObject,
	IN PIRP           pIrp        
)
{
	NTSTATUS    Status;
	DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceCleanup: Entering\n"));

	switch ( pDeviceObject->DeviceType )
	{
		case FILE_DEVICE_CONTROLLER:
		{
			// Always succeed cleanup for the controller
			Status = STATUS_SUCCESS;
			break;
		}         
		case FILE_DEVICE_SERIAL_PORT:
		{
			PASYN_PORT_EXT	pPortExt = pDeviceObject->DeviceExtension;
			if ( pPortExt->Type != ASYN_TYPE_PORT_EXT )
			{
				Status = STATUS_UNSUCCESSFUL;
			}            
			else
			{
				// Cancel pending irps
				AsynCancelReadIrps( pPortExt );
				AsynCancelIoctlIrps(&pPortExt->SdxbValues);
				AsynCancelWaitIrp ( pPortExt );
            AsynWritePurge( pPortExt, SERIAL_PURGE_TXABORT );
				Status = STATUS_SUCCESS;
			}            
			break;
		}   
		default:
			Status = STATUS_UNSUCCESSFUL;
	}               
   
	if ( pIrp != NULL )
	{   
		pIrp->IoStatus.Information = 0;
		AsynIoCompleteRequest( pIrp, Status, NULL );
		DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceCleanup: leaving with Status=0x%x \n",Status));
	}      
	return Status;
}   

/**********************************************************************************************
**
**	NTSTATUS  AsynCancelFlushRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp )
**
**	Purpose: For some reason the user sent down a request to flush the buffers, but has
**		now decided that flushing the buffers is not as important as before.  So this
**		function is a get out of jail card that the user can inact.
**
***********************************************************************************************/
NTSTATUS AsynCancelFlushRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP  pIrp )
{

	PASYN_PORT_EXT		pExt = (PASYN_PORT_EXT)DeviceObject->DeviceExtension;
	PASYN_WRITE			pWrite = &pExt->Write;
	NTSTATUS			status = STATUS_SUCCESS;

	IoReleaseCancelSpinLock( pIrp->CancelIrql );

	DigiAcquireSpinLock( &pWrite->Lock ); // Since we are clearing the flush for the tx engine

	if ( RemoveIrpFromQueue( &pWrite->Sequence.FlushList, pIrp ) )
	{
		DigiDump( DIGICANCEL , ("\tCancelling flush Irp: 0x%x\n", pIrp ));

		// The IRP is all ours now, let everything run.
		DigiReleaseSpinLock( &pWrite->Lock );

		pIrp->IoStatus.Information = 0;
		DigiDump( DIGICANCEL, ("AsynCancelRead: completed IRP %x with status=STATUS_CANCELLED read.length=%x \n",pIrp,pIrp->IoStatus.Information));
		AsynIoCompleteRequest( pIrp, STATUS_CANCELLED, NULL );
		status = STATUS_CANCELLED;
	}
	else      
	{
		DigiDump(DIGICANCEL, ("\tIrp:%x was not found.\n", pIrp));
		DigiReleaseSpinLock( &pWrite->Lock );
		status = STATUS_UNSUCCESSFUL;
	}

	return status;
}
/**********************************************************************************************
**
**	NTSTATUS  AsynDeviceFlushBuffer( IN PDEVICE_OBJECT  DeviceObject, IN PIRP  pIrp )
**
**	Purpose: Is to wait till all of the buffers have been flushed and then return 
**			status success. If we go past a certain time then we will return a
**			status failed.
**
***********************************************************************************************/
NTSTATUS AsynDeviceFlushBuffers
( 
	IN PDEVICE_OBJECT DeviceObject,
	IN PIRP           pIrp 
)
{
	NTSTATUS			status = STATUS_SUCCESS;
	PASYN_PORT_EXT		pExt = (PASYN_PORT_EXT)DeviceObject->DeviceExtension;
	PASYN_WRITE			pWrite = &pExt->Write;
	BOOLEAN				waitEmpty = FALSE;
	LARGE_INTEGER		maxWait;


	DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceFlushBuffers: Entering\n"));
	maxWait.QuadPart = 1000 * 10000 * -1; // This will wait a max of one second ( the wait is in increments of 100 nanoseconds ) must be neg.

	if (DeviceObject->DeviceType != FILE_DEVICE_SERIAL_PORT)
	{
		DigiDump(DIGICLOSE|DIGIFLOW, ("*** AsynDeviceRead: unknown device =0x%x Type=x0%x\n", 
																	DeviceObject, DeviceObject->DeviceType));
		status = STATUS_UNSUCCESSFUL;
		goto EXIT;
	} 
	if ( pWrite->Sequence.CurrentCountIn == pWrite->Sequence.CurrentCountOut ) // Then the buffer is empty
		goto EXIT;
	

	if ( pWrite->Sequence.CurrentCountIn > pWrite->Sequence.CurrentCountOut )
	{

		IoMarkIrpPending( pIrp );

		IoSetCancelRoutine( pIrp, AsynCancelFlushRequest );
		DigiAcquireSpinLock( &pWrite->Lock );
		pIrp->IoStatus.Status = pWrite->Sequence.CurrentCountIn;
		InsertTailList( &pWrite->Sequence.FlushList, &pIrp->Tail.Overlay.ListEntry );
		status = STATUS_PENDING;
		DigiReleaseSpinLock( &pWrite->Lock );

		return status;
	}

EXIT:
	pIrp->IoStatus.Information = 0;
	AsynIoCompleteRequest( pIrp, status, NULL );
	DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceFlsuhBuffers: leaving with Status=0x%x \n",status));
	return status;
}   
