/*
 ****************************************************************************
 * Copyright (c) 1990-99 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 File    : open.c
 *
 * Author         : P.Donovan
 *
 * Purpose        :
 *
 *      Open and Close dispatch routines for Asyn driver.
 *
 *
 *----------------------------------------------------------------------
 */

#include "header.h"

VOID AsynOpenComplete( IN HANDLE HOwner, IN HANDLE pStream,
                       IN ULONG ErrorCode, IN NTSTATUS Status);

/*
 *----------------------------------------------------------------------
 * Function:  AsynOpen.
 *
 * Purpose:
 *
 *    The IRP_MJ_CREATE dispatch routine for for a Port.
 *    Attempt to open a stream for the selected COM port and initialize the 
 *    port settings.
 *
 *
 *  Method:
 *    o Open a stream to S/DXB.
 *    o Build BUFFER_CMD, SET_PORT_CMD, MODEM_CMD, EVENT_CMD,
 *      LINE_ERROR_CMD, SEQ_NUM_CMD and SYNC_CMD
 *      and send these to the PutMsg Queue.
 *    o Post a Receive "type-ahead" buffer on the GetMsg Queue.
 *    o Set the IRP status to STATUS_PENDING, when the SYNC_RSP acknowledgement
 *      is received the status will be changed to STATUS_SUCCESS.
 *
 *
 * NOTES:   The Spin-Lock protecting the Port must be held prior to 
 *          calling this routine.
 *
 * Returns:
 *
 *    o STATUS_UNSUCCESS on error.
 *    o STATUS_PENDING otherwise.
 *
 *
 *----------------------------------------------------------------------
 */
NTSTATUS AsynPortOpen(
    IN PPORT_EXT        pPortExt,      // Comm port 
    IN PIRP             pIrp           // Associated work order
)
{
   NTSTATUS    Status = STATUS_SUCCESS;// Called function return value.
   PCARD_EXT   pCardExt;               // Driver specific information.
   ULONG       ErrorCode;
   HANDLE		DsHandle;


#if NT5_BUILD   
   // Verify that the port can be opened and that it is not already opened
   if ((pPortExt->PnpState != PNPSTATE_OK ) || (pPortExt->State != IdleState ))
   {   
      DigiDump( (DIGIOPEN), ("AsynPortOpen failed for port %x: STATUS_ACCESS_DENIED\n\tPnpState=%x State=%x",pPortExt->Port,pPortExt->PnpState,pPortExt->State));
      Status = STATUS_ACCESS_DENIED;
      goto PortOpenExit;
   }
#endif   
   pPortExt->OpenIrp = pIrp;

   //
   // Insert the Port from the Card activePorts queue.
   //
   pCardExt = pPortExt->CardExt;
#if REMOVED   
	// SAH - 6/29/99
   //  don't see where pCardExt->activePorts queue is ever used...
   InsertHeadList( &pCardExt->ActivePorts, &pPortExt->ActivePort);
#endif   
   pPortExt->State = OpeningState;
   pPortExt->DxbCount = 0;
   pCardExt->NbrActivePorts++;
   DigiDump(DIGIOPEN, ("ASYN!Open: Number of open ports=%d\n", pCardExt->NbrActivePorts));
   
#if NT5_BUILD
	DsHandle = pPortExt->DsHandle;
#else
	DsHandle = pCardExt->DsHandle;
#endif      
   if (pCardExt->IsdnCC == DXB_REGVAL_ASYN_ISDN_CC_ENABLED)
   {
	   typedef struct {
	      ULONG pnum ;
		  ULONG flags ;
	   } ISDN_CC_OPEN_T ;
	   ISDN_CC_OPEN_T TempOpenStructure ;

	   TempOpenStructure.pnum = pPortExt->Port ;
	   TempOpenStructure.flags = 0x01 ;	// isdn 
		
       Status = DsOpen( DsHandle,
                    SERIAL_STREAM_DEVNAME,
                    (HANDLE)pPortExt,
                    &TempOpenStructure,
                    sizeof (TempOpenStructure),
                    &pPortExt->DsPortHandle,
					&ErrorCode);
	   DigiDump(DIGIOPEN, ("ASYN!Open: ISDN Call Control is on.\n"));
   }
   else
   {
       Status = DsOpen( DsHandle,
                    SERIAL_STREAM_DEVNAME,
                    (HANDLE)pPortExt,
                    &pPortExt->Port,
                    sizeof (pPortExt->Port),
                    &pPortExt->DsPortHandle,
                    &ErrorCode);
   }
   DigiDump(DIGIOPEN, ("ASYN!Open: DsOpen Status=0x%x\n", Status));
   if (Status != STATUS_SUCCESS)
   {
   	pCardExt->NbrActivePorts--;
		pPortExt->State = IdleState;
      goto PortOpenExit;
   }
   AsynOpenComplete( pPortExt, pPortExt->DsPortHandle, ErrorCode, Status);
   Status = STATUS_PENDING;
   
PortOpenExit:
   if (Status != STATUS_PENDING)
   {
   	// Complete the IRP if there was an error.
      pIrp->IoStatus.Status = Status;
      pIrp->IoStatus.Information = 0;
      InsertTailList( &pPortExt->CompleteIrpQ, &pIrp->Tail.Overlay.ListEntry);
   }
   return (Status);
}

NTSTATUS
AsynInitSDXBDevice( 
	IN PPORT_EXT	pPortExt,
	IN PIRP			pIrp
)

{
	NTSTATUS 	Status;
   PPUT_MSG_EV pPutEvent = NULL;
   PRX_BUF     pRxBuf;                 // Type-ahead Receive buffer.
   
	// Build and send the commands to initialize/restore the S/DXB settings after a port open

   //
   // Get Buffer Size
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrp, 
                             SYNC_CMD, REASON_OPEN, DS_PRIORITY_LOW,
                             PutBuilding, AsynBuildBufferSizeCmd);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }
   //
   // Port Paramaters
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutBuilding, AsynBuildSetPortCmd);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }

   //
   // Port Modem Control Status, get current settings.
   //
   pPortExt->SdxbValues.Mflags = SDXB_IMMED;
	pPortExt->SdxbValues.Mout |= SDXB_DTR|SDXB_RTS;
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutBuilding, AsynBuildModemCmd);
   //
   // Restore the default flags settings to generate only on a delta.
   //
   pPortExt->SdxbValues.Mflags = 0;
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }

   //
   // Event reporting
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutBuilding, AsynBuildEventCmd);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }

   //
   // Line Error Control
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutBuilding, AsynBuildLineErrorCmd);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }

   //
   // Sequence Number Control
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                            CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutBuilding, AsynBuildSeqNumberCmd);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }

   //
   // Build and send the necessary S/DXB commands.
   //
   pPortExt->SdxbValues.FlushFlags = SDXB_FLUSH_OUTPUT | SDXB_FLUSH_INPUT; 
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutBuilding, AsynBuildFlush);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }

   //
   // Sync command, a convenient tag which tells us when open S/DXB
   // commands are complete.
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutAwaitingCompletion, AsynBuildSyncCmd);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }
   //
   // Post a receive "type-ahead" buffer on the GetMsg queue.
   //
   pRxBuf = (PRX_BUF)AsynAllocEntry( &pPortExt->ReadBufferPool, &Status);
   if (!pRxBuf)
   {
      goto InitSDXBExit;
   }
   // The buffer is empty at the moment
   pRxBuf->Size = DEF_RX_BUF_SIZE;
   pRxBuf->Len = 0;
   pRxBuf->Start = 0;

   pPortExt->RxBufCount++;
   InsertTailList( &pPortExt->RxQ, &pRxBuf->ListEntry);
#ifdef DBG
   pPortExt->Debug.LastGetMsgData = 
                      &pRxBuf->Data[pRxBuf->Start + pRxBuf->Len];
   pPortExt->Debug.LastGetMsgLen = 
                      pRxBuf->Size - (pRxBuf->Start + pRxBuf->Len); 
#endif   // DBG
   DigiDump(DIGIOPEN, ("ASYN!Open: Port=%d, calling DsGetMsgExt\n", pPortExt->Port));
   Status = DsGetMsgEx( pPortExt->DsPortHandle,
//                      MAX ((sizeof(SDXB_CMD)), (sizeof (SDXB_COMP_RSP)) ),
					  sizeof (union ISDN_primitives) + ISDN_MESSAGE_ENCAP_SIZE,
                      &pRxBuf->Data[pRxBuf->Start + pRxBuf->Len],
                      pRxBuf->Size - (pRxBuf->Start + pRxBuf->Len),
                      (HANDLE)pPortExt,
                      DS_PRIORITY_ANY,
                      AsynGetMsgComplete);
   if (Status != STATUS_PENDING)
   {
      goto InitSDXBExit;
   }
InitSDXBExit:
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
	return Status;
   
}     

/*
 *----------------------------------------------------------------------
 * Function:  AsynOpenComplete.
 *
 * Purpose:
 *
 *    Completion routine for DsOpenEx
 *
 *
 * Method:
 *
 *    o Send startup S/DXB commands.
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static VOID
AsynOpenComplete(
   IN HANDLE         HOwner,
   IN HANDLE         pStream,
   IN ULONG          ErrorCode,
   IN NTSTATUS       Status
)
{
   PPORT_EXT   pPortExt;
   PCARD_EXT   pCardExt;
   KIRQL       CurrentIrql;
   PIRP        pIrp;

   DigiDump(DIGIOPEN, ("ASYN!OpenComplete: \n"));

   pPortExt = (PPORT_EXT)HOwner;
   if (!pPortExt)
   {
      PORT_ERROR(pPortExt);
      goto OpenExit;
   }

#if OPEN_EX
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
#endif   // OPEN_EX

   pPortExt->DsPortHandle = pStream;
   pCardExt = pPortExt->CardExt;

   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);

   pPortExt->SdxbValues.Tout = 0;
   pPortExt->SyncOutSequence = 0;
   pPortExt->SyncInSequence = 0;

   pPortExt->TxBytesPending = 0;

   pPortExt->SdxbValues.Estat = 0;

   ASSERT(pPortExt->OpenIrp);

   if (!(pIrp = pPortExt->OpenIrp))
   {
      PORT_ERROR(pPortExt);
      goto OpenExit;
   }
   pPortExt->OpenIrp = NULL;

   if (Status != STATUS_SUCCESS)
   {
      DigiDump(DIGIOPEN, ("*** ASYN!OpenComplete: port=%d Open FAILED "
                          "Status=0x%x Error=0x%x\n", pPortExt->Port, Status, ErrorCode));
      goto OpenExit;
   }

   //
   // BAUD rate and start/stop bits are maintained across opens.
   //


   //
   // Hardware flow control is maintained across opens.
   //
   // Therefore, mflow is not reinitialized.
   //
   pPortExt->SdxbValues.Mstat = 0;
   pPortExt->SdxbValues.Mlast = 0;

   pPortExt->SdxbValues.Estat = 0;
   pPortExt->SdxbValues.Elast = 0;

   pPortExt->EscapeChar = 0;

   //
   // Reinitialize error counters
   pPortExt->SdxbValues.Norun = 0;
   pPortExt->SdxbValues.Noflow = 0;
   pPortExt->SdxbValues.Nframe = 0;
   pPortExt->SdxbValues.Nparity = 0;
   pPortExt->SdxbValues.Nbreak = 0;
   pPortExt->SdxbValues.NorunLast = 0;
   pPortExt->SdxbValues.NoflowLast = 0;
   pPortExt->SdxbValues.NframeLast = 0;
   pPortExt->SdxbValues.NparityLast = 0;
   pPortExt->SdxbValues.NbreakLast = 0;

   pPortExt->SdxbValues.Pset = 0;
   pPortExt->SdxbValues.Preset = 0;

   pPortExt->TotalRxBytesBuffered = 0;
   pPortExt->TotalReadBytes = 0;
   pPortExt->TotalWriteBytes = 0;

   pPortExt->CloseIrp = NULL;

   pPortExt->WaitMask = 0;
   pPortExt->HistoryMask = 0;

   pPortExt->ReadIntervalActive = ReadIntervalIdle;
   pPortExt->ReadIntervalCount = 0;

   pPortExt->OpenStateMask = 0;
	Status = AsynInitSDXBDevice( pPortExt, pIrp );   
   
   if ( pPortExt->IsModem )
   {
   	pPortExt->PnpModemDetectHandle = PnpModemInit();
      DigiDump( (DIGIOPEN), ("AsynPortOpen PnpModemDetect initialized %x:\n",pPortExt->PnpModemDetectHandle));
   }
   else
   	pPortExt->PnpModemDetectHandle = NULL;
	
OpenExit:
   if (pPortExt)
   {
      KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
   }
   
   if (pPortExt)
   {
      if (Status != STATUS_PENDING)
      {
#if REMOVED      
			// SAH - 6/29/99
		   //  don't see where pCardExt->activePorts queue is ever used...
      
         //
         // Remove the Port from the Card activePorts queue.
         //
         RemoveEntryList( &pPortExt->ActivePort);
#endif      
         KeAcquireSpinLock( &pCardExt->CardLock, &CurrentIrql);
         pCardExt->NbrActivePorts--;
         KeReleaseSpinLock( &pCardExt->CardLock, CurrentIrql);
         pPortExt->State = IdleState;
      }
#if OPEN_EX
      KeReleaseSpinLock(&pPortExt->PortLock, pPortExt->PortIrql);
   }
   if (Status != STATUS_PENDING)
   {
      if (pIrp)
      {
         pIrp->IoStatus.Status = Status;
         IoCompleteRequest(pIrp, IO_NO_INCREMENT);
         DigiDump(DIGIIRP, ("ASYN!Open: Complete=0x%x Status\n", pIrp, Status));
      }
   }
   else
   {
      //
      // If the status is PENDING, the Irp has already been marked
      // appropriately.
      // Nothing more to do.
#endif   // OPEN_EX
   }
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynPortClose.
 *
 * Purpose:
 *
 *    The IRP_MJ_CLOSE dispatch routine for a Port.  
 *    Attempt to close a stream for the selected COM port.
 *
 *
 * Method:
 *    o If transmit is pending and "completeWhenSent" is FALSE wait for an
 *      interval of "CloseTimeOut" before continuing.
 *    o Complete all pended IRPs
 *    o Clear the modem signals by building a MODEM_CMD S/DXB command and place
 *      in the PutMsg Queue.
 *    o Set the IRP status to STATUS_SUCCESS and return.
 *
 *
 * NOTES:   The Spin-Lock protecting the Port must be held prior to 
 *          calling this routine.
 *
 *
 * Returns:
 *
 *    
 *----------------------------------------------------------------------
 */
NTSTATUS AsynPortClose(
    IN PPORT_EXT        pPortExt,   // Comm port 
    IN PIRP             pIrp        // Associated work order
)
{
   NTSTATUS    Status = STATUS_SUCCESS;// Called function return value.
   PCARD_EXT   pCardExt;               // Driver specific information.
   PPUT_MSG_EV pPutEvent = NULL;
   LARGE_INTEGER
               CloseTimeout;

   DigiDump(DIGICLOSE, ("ASYN!PortClose: Port=%d\n", pPortExt->Port));

   pPortExt->State = ClosingState;
   pPortExt->CloseIrp = pIrp;
   pCardExt = pPortExt->CardExt;
   
	if ( pPortExt->PnpModemDetectHandle != NULL )
   {
		PnpModemCleanup( pPortExt->PnpModemDetectHandle);
      pPortExt->PnpModemDetectHandle = NULL;
	}      

   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   
   if ( !IsListEmpty( &pPortExt->PutQ) && (!pPortExt->CompleteWhenSent) )
   {
      // Either data or commands are pending.

      // Transmit IRPs may be returned before all data leaves the hardware
      // thus since the user thinks the data is sent wait some time to allow
      // the data to transmit before continuing with the close.

      Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrp, 
                                SYNC_CMD, REASON_CLOSE, DS_PRIORITY_LOW,
                                PutAwaitingCompletion, AsynBuildSyncCmd);
      if (Status != STATUS_PENDING)
      {
         goto PortCloseExit;
      }

      // Schedule the close timeout.
      CloseTimeout.QuadPart = Int32x32To64( pPortExt->CloseTimeOut, -1*MILLISECOND);
      KeSetTimer(&pPortExt->Timer.Close, CloseTimeout, &pPortExt->Timer.CloseDpc);
   }
   else
   {
      UCHAR Mout;          // Proposed new mout
      UCHAR PreservedMout; // copy of old mout

      // Always lower the DTR and RTS modem signals.
      // But record their current settings, they will be used as the default 
      // settings for the next open.

      // Build the new mout
      PreservedMout = Mout = pPortExt->SdxbValues.Mout;
      Mout &= ~(SDXB_DTR | SDXB_RTS);

      // Only update if necessary
      if ( (pPortExt->OpenStateMask & BreakOnMask) ||
           (Mout != pPortExt->SdxbValues.Mout) )
      {
         //
         // Turn off break
         //
         if (pPortExt->OpenStateMask & BreakOnMask)
         {
            pPortExt->OpenStateMask &= ~BreakOnMask;
            Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrp, 
                                      BREAK_CMD, REASON_CLOSE, DS_PRIORITY_LOW, 
                                      PutBuilding, AsynBuildBreakCmd);
            if (Status != STATUS_PENDING)
            {
               goto PortCloseExit;
            }
         }
   
         //
         // Lower modem signals
         //
         if (Mout != pPortExt->SdxbValues.Mout)
         {
            pPortExt->SdxbValues.Mout = Mout;
            Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrp,
                                      SYNC_CMD, REASON_CLOSE, DS_PRIORITY_LOW,
                                      PutBuilding, AsynBuildModemCmd);
            pPortExt->SdxbValues.Mout = PreservedMout;
            if (Status != STATUS_PENDING)
            {
               goto PortCloseExit;
            }
         }

         //
         // Sync command, a convenient tag which tells us when close S/DXB
         // commands are complete.
         //
         Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                                   CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                                   PutAwaitingCompletion, AsynBuildSyncCmd);
         if (Status != STATUS_PENDING)
         {
            goto PortCloseExit;
         }
         // Schedule the close timeout.
         CloseTimeout.QuadPart = Int32x32To64( pPortExt->CloseTimeOut, -1*MILLISECOND);
         KeSetTimer( &pPortExt->Timer.Close, CloseTimeout, &pPortExt->Timer.CloseDpc);
      }
      else
      {
         // Modem signals are already cleared
         // We can complete IRP
         KeAcquireSpinLock(&pPortExt->PortLock, &pPortExt->PortIrql);
         AsynCloseRsp( pPortExt);
         KeReleaseSpinLock(&pPortExt->PortLock, pPortExt->PortIrql);
         //Status = STATUS_SUCCESS;
         Status = STATUS_PENDING;
      }
   }

PortCloseExit:
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

   if (Status != STATUS_PENDING)
   {
      pIrp->IoStatus.Status = Status;
      InsertTailList( &pPortExt->CompleteIrpQ, 
                      &pIrp->Tail.Overlay.ListEntry);
   }
   pIrp->IoStatus.Status = Status;

   DigiDump(DIGIFLOW, ("ASYN!PortClose: Status=0x%x Irp=0x%x IrpStat=0x%x\n", 
               Status, pIrp, pIrp->IoStatus.Status));
   return (Status);
}
/*
 *----------------------------------------------------------------------
 * Function:  AsynCloseTimedOut.
 *
 * Purpose:
 *
 *    Failed to get a Close-Rsp.
 *
 *
 * Method:
 *
 *    o Force the completion of a close.
 *
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
VOID AsynCloseTimedOut(
   IN PKDPC      pPortDpc,       // DPC object for Port.
   IN PPORT_EXT  pPortExt,       // Port specific information.
   IN PVOID      SystemArg1,     // System argument.
   IN PVOID      SystemArg2      // System argument.
)
{
   DigiDump(DIGICLOSE, ("ASYN!CloseTimedOut: \n"));

   UNREFERENCED_PARAMETER(pPortDpc);
   UNREFERENCED_PARAMETER(SystemArg1);
   UNREFERENCED_PARAMETER(SystemArg2);

   //
   // Force a close.
   // Complete any outstanding IRPs.
   //
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
   AsynCloseRsp( pPortExt);

   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
}


/*
 *----------------------------------------------------------------------
 * Function:  AsynCancelClose.
 *
 * Purpose:
 *
 *    Cancel an active close.
 *
 *
 * Method:
 *
 *    o Force the completion of a close.
 *
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
VOID
AsynCancelClose(
    IN PPORT_EXT         pPortExt,   // port 
    IN PIRP              pIrp           // Associated work order.
)
{
   DigiDump(DIGICANCEL|DIGICLOSE, ("ASYN!CancelClose: \n"));

   pIrp->IoStatus.Information = 0;

   //
   // Complete the close operation immediately.
   //
   if (pPortExt->CloseIrp == pIrp)
   {
      (void) AsynCloseRsp(pPortExt);
   }

   // Do not schedule the Close IRP for completion just yet, 
   // Allow the DsCloseEx() routine to call the completion routine.
   // Nothing more to do!
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynCloseComplete.
 *
 * Purpose:
 *
 *    Completion routine for DsCloseEx.
 *
 *
 * Method:
 *
 *    o Set the port state to IdleState.
 *
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static VOID
AsynCloseComplete(
   IN HANDLE         HOwner,
   IN NTSTATUS       Status
)
{
   PPORT_EXT         pPortExt;
   PCARD_EXT         pCardExt;
   KIRQL             CurrentIrql;

   DigiDump(DIGICLOSE, ("ASYN!CloseComplete: "));

   pPortExt = (PPORT_EXT)HOwner;
   DigiDump(DIGICLOSE, ("Port=%d\n", pPortExt->Port));

   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

   //
   // It is now safe to free the Receive Buffer Queue
   //
   pCardExt = pPortExt->CardExt;
   while (!IsListEmpty( &pPortExt->RxQ))
   {
      PRX_BUF     pRxBuf;

      DigiDump(DIGICLOSE,("ASYN!CloseComplete: RxQ=0x%x count=0x%x\n",
                         pPortExt->RxQ.Flink, pPortExt->RxBufCount));
      if (pPortExt->RxQ.Flink == 0)
      {
         DbgBreakPoint();
      }
      pRxBuf = (PRX_BUF)RemoveHeadList( &pPortExt->RxQ);
      if (pRxBuf->Size == DEF_RX_BUF_SIZE)
      {
         AsynFreeEntry( &pPortExt->ReadBufferPool, &pRxBuf->ListEntry);
      }
      else
      {
         AsynFreeEntry( &pPortExt->VariableReadBufferPool, &pRxBuf->ListEntry);
      }
      pPortExt->RxBufCount--;
   }

   ASSERT(pPortExt->RxBufCount == 0);

   //
   // The Cancel routines for all IRPs should have been called by now.
   // Also, any outstanding Puts should have completed or been cancelled.
   //
   // Check the Put queue again, and make sure there is nothing on it.
   //
   while (!IsListEmpty(&pPortExt->PutQ))
   {
      PPUT_MSG_EV pPutEvent;

      pPutEvent = (PPUT_MSG_EV)RemoveHeadList( &pPortExt->PutQ);

      switch (pPutEvent->Reason)
      {
         case REASON_CLOSE:
            // There is a CLOSE still in the Put Message queue, this probably 
            // means that the Close has timed out.
            // Clear the Irp pointer in the Put Event so that the IRP is not 
            // completed twice.  Later this routine will explicitly complete 
            // the pPortExt->CloseIrp.
            //
            DigiDump(DIGICLOSE, ("ASYN!CloseComplete: CLOSE Response Irp=0x%x Command=0x%x Reason =0x%x\n",
                                 pPutEvent->Irp, pPutEvent->Command, pPutEvent->Reason));
            pPutEvent->Irp = 0;
            Status = STATUS_SUCCESS;
            break;
         default:
            DigiDump(DIGIALL, ("*** ASYN!CloseComplete: pPut=0x%x Irp=0x%x Reason=0x%x\n",
                               pPutEvent, pPutEvent->Irp, pPutEvent->Reason));
            PORT_ERROR(pPortExt);
            DigiDump(DIGIALL, ("*** ASYN!CloseComplete DsPurge FAILED!\n"));

            if (pPortExt->TimedWritePutEvent == pPutEvent)
            {
               pPortExt->TimedWritePutEvent = NULL;
               KeCancelTimer( &pPortExt->Timer.Write);
            }
            Status = STATUS_UNSUCCESSFUL;
            break;
      }
      if (pPutEvent->Irp)
      {
         PIRP        pIrp;

         IoAcquireCancelSpinLock( &CurrentIrql);
         pIrp = pPutEvent->Irp;
         pPutEvent->Irp = NULL;
         IoSetCancelRoutine(pIrp, NULL);
         IoReleaseCancelSpinLock( CurrentIrql);

         if (pPutEvent->Reason == REASON_CLOSE)
            pIrp->IoStatus.Status = Status;
         else
            pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
         pIrp->IoStatus.Information = 0;
         DigiDump(DIGICLOSE, ("ASYN!CloseComplete: Port=%d PutQ pEvent=0x%x CompleteIrp=0x%x.\n", 
                              pPortExt->Port, pPutEvent, pIrp));

         if (pPortExt->PortBusy)
         {
            KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
            InsertTailList( &pPortExt->CompleteIrpQ, 
                            &pIrp->Tail.Overlay.ListEntry);
            KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
         }
         else
         {
            KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
            AsynCompleteIrp( pPortExt, pIrp);
            KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
         }
      }
      AsynFreeEntry( &pPortExt->PutEventPool, &pPutEvent->ListEntry);
   }
   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
  
#if REMOVED
	// SAH - 6/29/99
   //  don't see where pCardExt->activePorts queue is ever used...
   
   //
   // Remove the Port from the Card activePorts queue.
   //
   RemoveEntryList(&pPortExt->ActivePort);
#endif
   KeAcquireSpinLock(&pCardExt->CardLock, &CurrentIrql);
   pCardExt->NbrActivePorts--;
   KeReleaseSpinLock(&pCardExt->CardLock, CurrentIrql);

   //
   // The port can be reused.
   //
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
   pPortExt->State = IdleState;
   
   if ( pPortExt->CloseIrp != NULL )
   {
	   pPortExt->CloseIrp->IoStatus.Status = STATUS_SUCCESS;

	   if (pPortExt->PortBusy)
	   {
	      DigiDump(DIGICLOSE, ("ASYN!CloseComplete: Port=%d B=%d Queue CloseIrp=0x%x.\n", 
	                           pPortExt->Port, pPortExt->PortBusy, pPortExt->CloseIrp));

	      InsertTailList( &pPortExt->CompleteIrpQ, 
	                      &pPortExt->CloseIrp->Tail.Overlay.ListEntry);
	   }
	   else
	   {
	      DigiDump(DIGICLOSE, ("ASYN!CloseComplete: Port=%d B=%d CloseIrp=0x%x.\n", 
	                           pPortExt->Port, pPortExt->PortBusy, pPortExt->CloseIrp));

	      KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);

	      AsynCompleteIrp( pPortExt, pPortExt->CloseIrp);
		   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
	   }
	   pPortExt->CloseIrp = 0;
   }
   
   AsynFreePool( &pPortExt->ReadBufferPool);
   AsynFreePool( &pPortExt->VariableReadBufferPool);
   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynCloseRsp.
 *
 * Purpose:
 *
 *    The port is in a state where we can free it's resources.
 *
 *
 * Method:
 *
 *    o Force the completion of a close.
 *    o Free the resources in use.
 *    o Call DsCloseEx() routine.
 *
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
VOID
AsynCloseRsp(
   IN PPORT_EXT   pPortExt           // Port specific information.
)
{
   PIRP        pIrp;       // IRP to complete.
   KIRQL       CurrentIrql;
   NTSTATUS    Status = STATUS_SUCCESS;

   DigiDump(DIGICLOSE, ("ASYN!CloseRsp: \n"));

   //
   // If we are not in the closing state just ignore.
   // This most likely means that the CloseTimeout and the 
   // S/DXB SYNC (Close Response) are colliding.  We only need one of them to 
   // close the port.
   //
   if (pPortExt->State != ClosingState)
   {
      return;
   }
   pPortExt->State = ClosingAwaitingCompleteState;
   //
   // Release the IRP pended upon the waitMask event(s).
   //
   if (pPortExt->WaitMaskIrp)
   {
       DigiDump(DIGICLOSE, ("ASYN!CloseRsp: complete waitMaskIrp; no action\n"));

       IoAcquireCancelSpinLock( &CurrentIrql);
       pIrp = pPortExt->WaitMaskIrp;
       pPortExt->WaitMaskIrp = NULL;
       IoSetCancelRoutine(pIrp, NULL);
       IoReleaseCancelSpinLock( CurrentIrql);

       pIrp->IoStatus.Information = sizeof(ULONG);
       pIrp->IoStatus.Status = STATUS_SUCCESS;
       *((ULONG *) pIrp->AssociatedIrp.SystemBuffer) = 0;

       KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
       IoCompleteRequest( pIrp, IO_NO_INCREMENT);
       KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
   }

   KeCancelTimer( &pPortExt->Timer.Close);

   //
   // Do not require the timer DPCs any more.
   //
   DigiDump(DIGICLOSE, ("ASYN!CloseRsp: removing dpc queues.\n"));
   KeRemoveQueueDpc( &pPortExt->Timer.WriteDpc);
   KeRemoveQueueDpc( &pPortExt->Timer.ReadDpc);
   KeRemoveQueueDpc( &pPortExt->Timer.IntervalReadDpc);
   KeRemoveQueueDpc( &pPortExt->Timer.CloseDpc);

   // 
   // Calling DsPurge is like calling DsPutMsg in that the PutMsg Completion 
   // may get called.  Thus set the PutMsg Busy flag and release the Port Lock.
   // Completion routines will be called with status == STATUS_CANCELLED
   //
   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   Status = DsPurge( pPortExt->DsPortHandle, DS_PURGE_ALL);
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

   if (Status != STATUS_SUCCESS)
   {
      PORT_ERROR(pPortExt);
      DigiDump(DIGIALL, ("*** ASYN!CloseRsp: DsPurge FAILED Status=0x%x!\n", Status));
   }

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

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoCleanup.
 *
 * Purpose:
 *
 *    Cleans up the serial interface.
 *
 *
 * Method:
 *
 *    o Cancel Wait IRP
 *    o Cancel write and read IRPs.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS no change necessary.
 *    o STATUS_BUFFER_TOO_SMALL on supplied buffer is too small.
 *    o STATUS_PENDING attempting to change characters in S/DXB.
 *
 *
 * Notes:
 *    o Called with the PortLock spin-lock held.
 *
 *----------------------------------------------------------------------
 */
NTSTATUS
AsynCleanup(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp           // Ioctl request to perform.
)
{
   NTSTATUS    Status = STATUS_SUCCESS;// Call return value.
   ULONG       PurgeFlag;
   PIRP        pWaitIrp;

   DigiDump(DIGICLOSE, ("ASYN!Cleanup: Port=0x%x Irp=0x%x\n",
                                    pPortExt->Port, pIrp));
   //
   // Complete IRP waiting on waitMask events.
   //
   if (pPortExt->WaitMaskIrp)
   {
      DigiDump(DIGIPURGE | DIGIWAIT |DIGIALL, ("ASYN!Cleanup: Port=0x%x WaitMaskIrp=0x%x\n",
                                       pPortExt->Port, pPortExt->WaitMaskIrp));

      pWaitIrp = pPortExt->WaitMaskIrp;
      pPortExt->WaitMaskIrp = NULL;
      pWaitIrp->IoStatus.Information = sizeof(ULONG);
      pWaitIrp->IoStatus.Status = STATUS_CANCELLED;
      *((ULONG *) pWaitIrp->AssociatedIrp.SystemBuffer) = 0;

      InsertTailList( &pPortExt->CompleteIrpQ, 
                      &pWaitIrp->Tail.Overlay.ListEntry);
   }

   PurgeFlag = SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | 
               SERIAL_PURGE_TXCLEAR | SERIAL_PURGE_RXCLEAR;

   Status = AsynIoPurge( pPortExt, pIrp, sizeof(PurgeFlag), &PurgeFlag);
   if (pIrp != NULL && Status != STATUS_PENDING)
   {
      pIrp->IoStatus.Status = Status;
      InsertTailList( &pPortExt->CompleteIrpQ, 
                      &pIrp->Tail.Overlay.ListEntry);
   }
   return (Status);
}

