/*++
*******************************************************************************
* 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 = GetAsynPendingIrp(&pPortExt->pPendingIrp);
    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 );
    }      
    else {
        SRAPRINT(DBGLVL_SUMMARY,("Port %x, no pending Irp\n",pPortExt));
    }
}      

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

VOID
AsynClosePortImmediate(
    IN PASYN_PORT_EXT pPortExt
    )
{    
    HANDLE hLowPriority;
    HANDLE hHighPriority;

    DigiAcquireSpinLock( &pPortExt->Lock );

    // Discard any received data
    AsynPurgeRxBuffers( pPortExt );

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

    pPortExt->hRxEngine_LowPriority = NULL;
    pPortExt->hRxEngine_HighPriority = NULL;

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

    // Free the DStreams resources for this port
    DsCloseEx( pPortExt->DsPortHandle, CloseComplete );
}

VOID
AsynCancelPendingIrp(
    IN PASYN_PORT_EXT pPortExt
    )
{
    PIRP        pIrp;

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

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

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:
        DigiReleaseSpinLock( &pPortExt->Lock );

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

        // Close port now
        AsynClosePortImmediate(pPortExt);

        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...      
            SetAsynPendingIrp(&pPortExt->pPendingIrp,pIrp);
            IoMarkIrpPending( pIrp );      
        }
        DigiReleaseSpinLock( &pPortExt->Lock );      
        goto PortCloseExit;               
    }   
#endif    

    DigiReleaseSpinLock ( &pPortExt->Lock );
      
    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 );

        Status = STATUS_SUCCESS;
        break;
    case MAXULONG:
      	// Wait forever for transmit data to drain
        // Do we really want to do this? NO - specify a max timeout value of 2 seconds.
    {

        LARGE_INTEGER	Timeout;
        DigiAcquireSpinLock( &pPortExt->Lock );
        SetAsynPendingIrp(&pPortExt->pPendingIrp,pIrp);
        pPortExt->State = CloseWaitTxDrainState;
        DigiReleaseSpinLock ( &pPortExt->Lock );

        IoMarkIrpPending( pIrp );
  
        Timeout.QuadPart = (LONGLONG)2000 * ONE_MILLISECOND;
        KeSetTimer( &pPortExt->Close.Timer, Timeout, &pPortExt->Close.DPC );
    }
    break;
    default:
    {
      	// set timeout for transmit data to drain. If timeout expires, the transmit data will be purged.
      	LARGE_INTEGER	Timeout;
		
        DigiAcquireSpinLock( &pPortExt->Lock );
        SetAsynPendingIrp(&pPortExt->pPendingIrp,pIrp);
        pPortExt->State = CloseWaitTxDrainState;
        DigiReleaseSpinLock ( &pPortExt->Lock );

        IoMarkIrpPending( pIrp );
        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. Only do this if we've requested a tx data 
    // drain timeout above.

    if (pPortExt->Close.TxDrainTimeout == 0)
    {
        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;

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",pDeviceObject,pIrp,FALSE));

    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;
        if ( pPortExt->Type != ASYN_TYPE_PORT_EXT )
        {
            Status = STATUS_UNSUCCESSFUL;
        }            
        else
        {
            // Stratus (BA 12Nov01)
            PASYN_PORT_EXT pPortExt = pDeviceObject->DeviceExtension;
            if (pPortExt->State == OpenState)
            {
                Status = PortClose( pPortExt, pIrp );
            }
            else
            {
                DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceClose: Caught invalid attempt to close device!\n"));
                Status = STATUS_SUCCESS;
            }
            // End Stratus (BA 12Nov01)

            if (Status == STATUS_PENDING) {
                // Now Irp is queued - check if adapter has failed and if so
                // cancel all outstanding IO
                // NOTE: This is done AFTER queueing the Irp for a very good
                //       reason! To check before in a synchronized way, we'd
                //       have to acquire and hold the device extension spinlock
                //       across the queue call - I dont like holding multiple
                //       locks because it's an invitation to deadlock city.
                //       Instead, we take the optimistic approach and just check
                //       after the fact. No need to acquire the spinlock to do
                //       the check because the place that SETS the flag also 
                //       cancels outstanding IO
                // NOTE ALSO: This check does NOT affect whether or not STATUS_PENDING
                //            is returned to the caller - we ALWAYS do this.
                AsynCheckForPortFailure(pPortExt);
            }
        }            
        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
AsynCancelAllPendingIo(
    PASYN_PORT_EXT pPortExt,
    IN NTSTATUS IOStatus
    )
{

    SRAPRINT(DBGLVL_SUMMARY,("Port %x, Cancel all pending IO!\n",pPortExt));

    AsynCancelReadIrps( pPortExt );
    AsynCancelIoctlIrps(&pPortExt->SdxbValues);
    AsynCancelWaitIrp( pPortExt );
    AsynCancelPendingIrp( pPortExt );
    AsynWritePurge( pPortExt, SERIAL_PURGE_TXABORT );
}

VOID
AsynOnCleanupSdxb(
    IN HANDLE Context,
    IN NTSTATUS IOStatus
    )
{
    PASYN_PORT_EXT pPortExt = (PASYN_PORT_EXT)Context;

    SRAPRINT(DBGLVL_ALWAYS,("Port %x, Cleanup port called!\n",pPortExt));

    // Save indication that device has failed. Port spin lock is used
    // to synchronize access
    DigiAcquireSpinLock( &pPortExt->Lock );
    pPortExt->DeviceStateFlags |= PNP_DEVICE_FAILED;
    DigiReleaseSpinLock( &pPortExt->Lock );

    // XXX: Should we call IoInvalidateDeviceState??? This would guarantee
    // XXX: that we'll find out about the removal - on the other hand, we
    // XXX: have other mechanism that should do this - no point in duplicating

    AsynCancelAllPendingIo(pPortExt,IOStatus);
}

VOID
AsynCheckForPortFailure(
    PASYN_PORT_EXT pPortExt
    )
{
    if (pPortExt->DeviceStateFlags & PNP_DEVICE_FAILED)
        AsynCancelAllPendingIo(pPortExt,STATUS_IO_DEVICE_ERROR);
}

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));        
        SetAsynPendingIrp(&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 );
        SetAsynPendingIrp(&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));

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",pDeviceObject,pIrp,FALSE));


    // 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;
        if ( pPortExt == NULL || pPortExt->Type != ASYN_TYPE_PORT_EXT )
        {
            Status = STATUS_UNSUCCESSFUL;
        }            
        else if (pPortExt->DeviceStateFlags & PNP_DEVICE_FAILED)
        {
            Status = STATUS_IO_DEVICE_ERROR;
        }
        else
        {
            Status = PortCreate( pPortExt, pIrp );
        }            

        if (Status == STATUS_PENDING) {
            // Now Irp is queued - check if adapter has failed and if so
            // cancel all outstanding IO
            // NOTE: This is done AFTER queueing the Irp for a very good
            //       reason! To check before in a synchronized way, we'd
            //       have to acquire and hold the device extension spinlock
            //       across the queue call - I dont like holding multiple
            //       locks because it's an invitation to deadlock city.
            //       Instead, we take the optimistic approach and check in
            //       an unsynchronized manner before and again after the fact. 
            //       No need to acquire the spinlock to do the check because 
            //       the place that SETS the flag also cancels outstanding IO
            // NOTE ALSO: This check does NOT affect whether or not STATUS_PENDING
            //            is returned to the caller - we ALWAYS do this.
            AsynCheckForPortFailure(pPortExt);
        }
        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"));

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",pDeviceObject,pIrp,FALSE));

    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
            AsynCancelAllPendingIo(pPortExt,STATUS_CANCELLED);
            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"));

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",DeviceObject,pIrp,FALSE));

    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, ("*** AsynDeviceFlushBuffers: unknown device =0x%x Type=x0%x\n", 
                                      DeviceObject, DeviceObject->DeviceType));
        status = STATUS_UNSUCCESSFUL;
        goto EXIT;
    } 
    // has port failed?
    if (pExt->DeviceStateFlags & PNP_DEVICE_FAILED)
    {
        status = STATUS_IO_DEVICE_ERROR;
        DigiDump( DIGICLOSE|DIGIFLOW, ("In AsynDeviceFlushBuffers port has failed\n" ));
        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 );
        DigiReleaseSpinLock( &pWrite->Lock );

        status = STATUS_PENDING;
    }

 EXIT:
    if (status == STATUS_PENDING) {
        // Now Irp is queued - check if adapter has failed and if so
        // cancel all outstanding IO
        // NOTE: This is done AFTER queueing the Irp for a very good
        //       reason! To check before in a synchronized way, we'd
        //       have to acquire and hold the device extension spinlock
        //       across the queue call - I dont like holding multiple
        //       locks because it's an invitation to deadlock city.
        //       Instead, we take the optimistic approach and check in
        //       an unsynchronized manner before and again after the fact. 
        //       No need to acquire the spinlock to do the check because 
        //       the place that SETS the flag also cancels outstanding IO
        // NOTE ALSO: This check does NOT affect whether or not STATUS_PENDING
        //            is returned to the caller - we ALWAYS do this.
        AsynCheckForPortFailure(pExt);
    }
    else {
        pIrp->IoStatus.Information = 0;
        AsynIoCompleteRequest( pIrp, status, NULL );
    }

    DigiDump( DIGICLOSE|DIGIFLOW, ("AsynDeviceFlsuhBuffers: leaving with Status=0x%x \n",status));

    return status;
}   
