/*
 ****************************************************************************
 * 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    : ioctl.c
 *
 * Author         : P.Donovan
 *
 * Purpose        :
 *
 *      IOCTL related stuff.
 *
 *----------------------------------------------------------------------
 */

#include "header.h"
#define min(a, b)  (((a) < (b)) ? (a) : (b))

/*
 *----------------------------------------------------------------------
 * Function:  AsynXnPutMsgComplete
 *
 * Purpose:
 *
 *    Put Message Completion routine called by DStreams Lib
 *
 *
 * Method:
 *     Not much to be done.  We simply free the buffer if it was ever allocated.
 *
 * Returns:
 *
 *    void
 *
 *
 *----------------------------------------------------------------------
 */
void
AsynXnPutMsgComplete(
   IN HANDLE HOwner,
   IN PVOID Context,
   IN PVOID PControlBuffer,
   IN ULONG ControlLength,
   IN PVOID PDataBuffer,
   IN ULONG DataLength,
   IN NTSTATUS Status
)
{
   DigiDump(DIGIIOCTL, ("ASYN!XnPutMsgComplete\n"));
	// if we allocated a buffer for this then we need to free it.
	if (Context != NULL)
		DigiFreeMem (Context) ;
}


/*
 *----------------------------------------------------------------------
 * Function:  AsynCancelXnGetIrp.
 *
 * Purpose:
 *
 *    Entry point for cancelling XnGet IRPs.
 *
 *
 * Method:
 *
 *    o Clear the IRPs Cancel routine
 *    o Mark the irp canceled.
 *    o Put the IRP in the completed IRP queue.
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
VOID
AsynCancelXnGetIrp(
   IN PDEVICE_OBJECT    pDevice,    // port device.
   IN PIRP              pIrp        // Associated work order.
)

{
   PPORT_EXT      pPortExt;         // Driver specific information.

   IoReleaseCancelSpinLock(pIrp->CancelIrql);

   DigiDump(DIGICANCEL|DIGIIRP, ("ASYN!CancelXnGetIrp: "));

   //
   // Get the driver specific data structures.
   //
   pPortExt = pDevice->DeviceExtension;
   DigiDump(DIGICANCEL|DIGIIRP, ("PortNo=0x%x IRP=0x%x\n", pPortExt->Port, pIrp));


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

   IoSetCancelRoutine( pIrp, NULL);

   pIrp->IoStatus.Information = 0;
   pIrp->IoStatus.Status = STATUS_CANCELLED;

   // The IRP must always be queued.  Remove it from that queue.
   RemoveEntryList( &pIrp->Tail.Overlay.ListEntry);

   //
   // Schedule the IRP for completion
   InsertTailList( &pPortExt->CompleteIrpQ, &pIrp->Tail.Overlay.ListEntry);

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

/*
 *----------------------------------------------------------------------
 *  Function:  AsynCancelIoctl
 *
 *  Purpose:
 *
 *      Provide for a cancel mechanism for SERIAL IOCTL functions which
 *      pend.
 *
 *
 *  Method:
 *
 *      o Remove the cancel function.
 *      o Nullify the waitMaskIrp or the immWrite IRP.
 *      o Complete the IRP.
 *
 * NOTES:
 *    The Port's Spin-lock must be held when this routine is called.
 *
 *  Returns:
 *
 *      o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
VOID
AsynCancelIoctl(
   IN PPORT_EXT         pPortExt,   // port 
   IN PIRP              pIrp           // Associated work order.
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   BOOLEAN     bWaitIrp = FALSE;

   DigiDump(DIGICANCEL|DIGIIOCTL, ("ASYN!CancelIoctl: "));

   DigiDump(DIGICANCEL|DIGIIOCTL, ("PortNo=0x%x IRP=0x%x\n", pPortExt->Port, pIrp));

   pIrp->IoStatus.Information = 0;

   if (pPortExt->WaitMaskIrp == pIrp)
   {
      DigiDump(DIGICANCEL|DIGIIOCTL, ("ASYN!CancelIoctl: Complete waitMaskIrp; no action\n"));
		// Vantive 2339: Set status to STATUS_CANCELLED instead of STATUS_SUCCESS
      //               Fixes port hang due to port never getting closed by TAPI.
      pIrp->IoStatus.Information = 0;
      pIrp->IoStatus.Status = STATUS_CANCELLED;
      pPortExt->WaitMaskIrp = NULL;
      bWaitIrp = TRUE;
   }
   //
   // See if this IRP spawned an associated PutMsg Event
   //
   pPutEvent = AsynUtilFindPutEvent( pPortExt, pIrp, &pPortExt->PutQ);

   if (pPutEvent)
   {
      if (pPutEvent->Command == CHAR_IMMED_CMD)
      {
         pPortExt->OpenStateMask &= ~ImmWriteMask;
      }
      ASSERT(pPutEvent->Reason != REASON_WRITE);

      DigiDump(DIGICANCEL|DIGIIOCTL, ("ASYN!CancelIoctl: pPut=0x%x Reason=0x%x State=0x%x\n",
               pPutEvent, pPutEvent->Reason, pPutEvent->State));

      if ( (pPutEvent->State == PutAwaitingCompletion) ||
           (pPutEvent->State == PutAwaitingCompletionResponseRxed) )
      {
         //
         // The DStreams still has not called the completion routine.
         // Complete the IRP, but we can't complete the PutEvent until DStreams
         // calls the completions routine.
         //
         pPutEvent->State = PutAborted;
         pPutEvent->Status = STATUS_UNSUCCESSFUL;
         pPutEvent->Irp = NULL;
      }
      else
      {
         // Put the Put Event descriptor back in the free pool
         RemoveEntryList( &pPutEvent->ListEntry);
			AsynFreeEntry( &pPortExt->PutEventPool, &pPutEvent->ListEntry );
      }
   }
   else
   {
      PLIST_ENTRY pEntry, pEntryNext;
      BOOLEAN fFoundIrp = FALSE;

      //
      // Never even got to generate a Put Event, 
      //
      pIrp->IoStatus.Information = 0;
   
   }
   
   //
   // Schedule the IRP for completion
   InsertTailList( &pPortExt->CompleteIrpQ, &pIrp->Tail.Overlay.ListEntry);
}

/*
 *----------------------------------------------------------------------
 *  Function:  AsynPurgeOutboundIrps
 *
 *  Purpose:
 *
 *      Purge the outbound IRPs (Writes & Ioctls)
 *
 *
 *  Method:
 *
 *      o Set each IRP status to Cancelled.
 *      o Put the IRP on the Completed Outbound Irp Queue
 *
 *
 *  Returns:
 *
 *      o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static VOID
AsynPurgeOutboundIrps(
   IN PPORT_EXT      pPortExt          // Port specific information.
)
{
   PLIST_ENTRY pEntry;                 // LIST_ENTRY intermediate value.   
   PIRP        pIrp;                   // Queued IRP.

   while (! IsListEmpty( &pPortExt->WriteQueue))
   {
      pEntry = RemoveHeadList( &pPortExt->WriteQueue);
      pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry);

      pIrp->IoStatus.Status = STATUS_CANCELLED;
      InsertTailList( &pPortExt->CompleteIrpQ, 
                      &pIrp->Tail.Overlay.ListEntry);

      DigiDump(DIGIPURGE|DIGICANCEL, ("Asyn!PurgeOutboundIrps: Port=%d Irp=0x%x\n", 
                           pPortExt->Port, pIrp));
   }

   //
   // Write timers will be cancelled when the associated Put Work item
   // is cancelled.  Don't do it here!
   {
      // This space is left blank intentionally!!!
   }
}

/*
 *----------------------------------------------------------------------
 *  Function:     AsynPurgeXnGetIrps
 *
 *  Purpose:
 *
 *      Purge the XnGet IRPs
 *
 *
 *  Method:
 *
 *      o Set each IRP status to Cancelled.
 *      o Call IoCompleteRequest for the IRP
 *
 *
 *  Returns:
 *
 *      o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static VOID
AsynPurgeXnGetIrps(
   IN PPORT_EXT      pPortExt          // Port specific information.
)
{
   PLIST_ENTRY pEntry;                 // LIST_ENTRY intermediate value.   
   PIRP        pIrp;                   // Queued IRP.
   KIRQL       CurrentIrql;            //Save prior irq level.
   PXN_GET_LIST pXnGetList ;

   while (! IsListEmpty( &pPortExt->XnGetQueue))
   {
      pEntry = RemoveHeadList( &pPortExt->XnGetQueue);
      pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry);

      DigiDump(DIGIREAD|DIGIPURGE|DIGICANCEL, ("Asyn!PurgeXnGetIrps: Port=%d Irp=0x%x\n", 
                                    pPortExt->Port, pIrp));
	  pIrp->IoStatus.Information = 0 ;
      pIrp->IoStatus.Status = STATUS_CANCELLED;
      InsertTailList( &pPortExt->CompleteIrpQ, &pIrp->Tail.Overlay.ListEntry);
   }
   //
   // if we hit before these could be completed then we need to clear the
   // xngetpktqueue
   while (! IsListEmpty( &pPortExt->XnGetPktQueue))
   {
      pEntry = RemoveHeadList( &pPortExt->XnGetPktQueue);
      pXnGetList = CONTAINING_RECORD(pEntry, XN_GET_LIST, List) ;
	  // start freeing
	  if (pXnGetList->XnGetEntry)
	  {
         if (pXnGetList->XnGetEntry->pData)
		    DigiFreeMem (pXnGetList->XnGetEntry->pData);
         if (pXnGetList->XnGetEntry->pControl)
		    DigiFreeMem (pXnGetList->XnGetEntry->pControl);
		 DigiFreeMem (pXnGetList->XnGetEntry);
	  }
      DigiFreeMem (pXnGetList) ; 
   }
}

/*
 *----------------------------------------------------------------------
 *  Function:  AsynPurgeReadIrps
 *
 *  Purpose:
 *
 *      Purge the Read IRPs
 *
 *
 *  Method:
 *
 *      o Set each IRP status to Cancelled.
 *      o Call IoCompleteRequest for the IRP
 *
 *
 *  Returns:
 *
 *      o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static VOID
AsynPurgeReadIrps(
   IN PPORT_EXT      pPortExt          // Port specific information.
)
{
   PLIST_ENTRY pEntry;                 // LIST_ENTRY intermediate value.   
   PIRP        pIrp;                   // Queued IRP.
   KIRQL       CurrentIrql;            //Save prior irq level.

   while (! IsListEmpty( &pPortExt->ReadQueue))
   {
      pEntry = RemoveHeadList( &pPortExt->ReadQueue);
      pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry);

      //
      // Cancel timer when active.
      //
      if (pIrp == pPortExt->TimedReadIrp)
      {
         pPortExt->TimedReadIrp = NULL;
         KeCancelTimer( &pPortExt->Timer.Read);

         //
         // Remove the Interval timer, too.
         //
         if (pPortExt->ReadIntervalActive > ReadIntervalIdle)
         {
            pPortExt->ReadIntervalActive = ReadIntervalIdle;
            pPortExt->ReadIntervalCount = 0;
            KeCancelTimer( &pPortExt->Timer.IntervalRead);
        }
      }

      DigiDump(DIGIREAD|DIGIPURGE|DIGICANCEL, ("Asyn!PurgeReadIrps: Port=%d Irp=0x%x\n", 
                                    pPortExt->Port, pIrp));

      pIrp->IoStatus.Status = STATUS_CANCELLED;
      InsertTailList( &pPortExt->CompleteIrpQ, &pIrp->Tail.Overlay.ListEntry);
   }
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetStats.
 *
 * Purpose:
 *
 *    Get the Performance Statistics.
 *
 *
 * Method:
 *
 *    o Update the Performance Statistics based on the PortExt structure.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS is returned.
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetStats(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated work order.
   IN ULONG             BufferSize,    // Output buffer size.
   OUT PASYN_SERIALPERF_STATS pStats        // Destination for Statistics.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL, ("ASYN!IoGetStats: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pStats))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto  IoctlExit;
   }

   //
   // Copy the complete structure.
   //
   *pStats = pPortExt->PerfStats;
   pIrp->IoStatus.Information = sizeof(*pStats);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoClearStats.
 *
 * Purpose:
 *
 *    Clear the Performance Statistics.
 *
 *
 * Method:
 *
 *    o Zero the Performance Statistics stored in the PortExt structure.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoClearStats(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp           // Associated work order.
)
{
   DigiDump(DIGIIOCTL, ("ASYN!IoClearStats: PortNo=0x%x\n", pPortExt->Port));

   // Zero the complete structure.
   RtlZeroMemory( &pPortExt->PerfStats, sizeof(ASYN_SERIALPERF_STATS) );
   pIrp->IoStatus.Information = 0;
   return STATUS_SUCCESS;
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSetBaudRate.
 *
 * Purpose:
 *
 *    Set the BAUD rate.
 *
 *
 * Method:
 *
 *    o Mark associated IRP as Pending.
 *    o Hold the Port's PortLock Lock.
 *    o Update the baud rate.
 *
 *
 * Returns:
 *
 *    o STATUS_PENDING is returned.
 *    o STATUS_BUFFER_TOO_SMALL on input buffer too small.
 *    o STATUS_INVALID_PARAMETER on Error.
 *
 *
 * Notes:
 *
 *    o When the DsPutMsgEx completion routine is called the IRP status is
 *      changed from PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoSetBaudRate(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN ULONG             BufferSize,    // Input buffer size.
   IN PSERIAL_BAUD_RATE pBaudRate      // BAUD rate to set.
)
{
   NTSTATUS    Status;                 // Call return value.
   PPUT_MSG_EV pPutEvent = NULL;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoSetBaudRate: PortNo=0x%x ", pPortExt->Port));

   if (BufferSize < sizeof(*pBaudRate))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }
   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("Baud=%d\n", pBaudRate->BaudRate));

   Status = STATUS_INVALID_PARAMETER;
   if ((pBaudRate->BaudRate > 1228800) || (pBaudRate->BaudRate < 4))
   {
      DigiDump(DIGIALL, ("ASYN!IoSetBaudRate: PortNo=0x%x InvalidBaud=%d!\n", 
         pPortExt->Port, pBaudRate->BaudRate));
      Status = STATUS_INVALID_PARAMETER;
      goto IoctlExit;
   }
   if (pBaudRate->BaudRate == pPortExt->SdxbValues.Speed)
   {
      Status = STATUS_SUCCESS;
      goto IoctlExit;
   }

   if (pPortExt->DxbCount > MAX_SDXB_IOCTL) {
      Status = STATUS_DEVICE_BUSY;  // What status code should I use? RFM
      goto IoctlExit;
   }


   pPortExt->SdxbValues.Speed = pBaudRate->BaudRate;

   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_BAUD_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }

   pPortExt->DxbCount++;
   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded,
                             PORT_PARAM_CMD, REASON_GEN_IOCTL, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildSetPortCmd);

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

IoctlExit:
   pIrp->IoStatus.Information = 0;

   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetBaudRate.
 *
 * Purpose:
 *
 *    Get the BAUD rate.
 *
 *
 * Method:
 *
 *    o Update the baud rate based on the PortExt structure.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS is returned.
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetBaudRate(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Work item.
   IN ULONG             BufferSize,    // Output buffer size.
   OUT PSERIAL_BAUD_RATE pBaudRate     // BAUD rate to set.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoGetBaudRate: PortNo=0x%x ", 
            pPortExt->Port));

   if (BufferSize < sizeof(*pBaudRate))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto  IoctlExit;
   }
   pBaudRate->BaudRate = pPortExt->SdxbValues.Speed;
        
   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("%d\n", pBaudRate->BaudRate));

   pIrp->IoStatus.Information = sizeof(*pBaudRate);

   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSetLineControl
 *
 * Purpose:
 *
 *    Set the parity, word size and stop bit parameters.
 *
 *
 * Method:
 *
 *    o Set the parity, word size and stop bit parameters.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_INVALID_PARAMETER, Error.
 *    o STATUS_PENDING, DsPutMsgEx called.
 *    o STATUS_SUCCESS, no need to send a command to S/DXB.
 *
 *
 * Notes:
 *    o The associated IRP is already marked as Pending.
 *    o The Port's PortLock Lock is held before calling this function.
 *
 *
 *----------------------------------------------------------------------
 */
//static 
NTSTATUS
AsynIoSetLineControl(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN ULONG             BufferSize,    // Input buffer size.
   IN PSERIAL_LINE_CONTROL
                        pLineControl   // Control Information.
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   NTSTATUS    Status = STATUS_SUCCESS;// Call return value.
   USHORT      ProposedCflags;
   USHORT      ProposedXflags;
   USHORT      ProposedIflags;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL, ("ASYN!IoSetLineControl: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pLineControl))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto  IoctlExit;
   }

   if (pPortExt->DxbCount > MAX_SDXB_IOCTL) {
      Status = STATUS_DEVICE_BUSY;  // What status code should I use? RFM
      goto IoctlExit;
   }

   //
   // Reset changeable xflag and cflag bits.
   //
   ProposedCflags = SDXB_CREAD;
   ProposedXflags = pPortExt->SdxbValues.Xflags & ~(SDXB_XPAR | SDXB_XEDATA);
   ProposedIflags = pPortExt->SdxbValues.Iflags & ~(SDXB_INPCK);

   switch (pLineControl->Parity)
   {
      case NO_PARITY:
         break;
      case ODD_PARITY:
         ProposedCflags |= SDXB_PARENB | SDXB_PARODD;
         ProposedIflags |= SDXB_INPCK;
         //
         // If not doing Microsoft's LSRMST_INSERT or Line Error 
         // replacement , then receive error chars unaltered.
         if (!(pPortExt->SdxbValues.Xflags & (SDXB_XMSPAR | SDXB_XMSERC)))
         {
            ProposedXflags |= SDXB_XEDATA;
         }
         break;
      case EVEN_PARITY:
         ProposedCflags |= SDXB_PARENB;
         ProposedIflags |= SDXB_INPCK;
         //
         // If not doing Microsoft's LSRMST_INSERT or Break or Line Error 
         // replacement , then receive error chars unaltered.
         if (!(pPortExt->SdxbValues.Xflags & (SDXB_XMSPAR | SDXB_XMSERC)))
         {
            ProposedXflags |= SDXB_XEDATA;
         }
         break;
      case MARK_PARITY:
         ProposedCflags |= SDXB_PARENB | SDXB_PARODD;
         ProposedXflags |= SDXB_XPAR;
         ProposedIflags |= SDXB_INPCK;
         //
         // If not doing Microsoft's LSRMST_INSERT or Break or Line Error 
         // replacement , then receive error chars unaltered.
         if (!(pPortExt->SdxbValues.Xflags & (SDXB_XMSPAR | SDXB_XMSERC)))
         {
            ProposedXflags |= SDXB_XEDATA;
         }
         break;
      case SPACE_PARITY:
         ProposedCflags |= SDXB_PARENB;
         ProposedXflags |= SDXB_XPAR;
         ProposedIflags |= SDXB_INPCK;
         //
         // If not doing Microsoft's LSRMST_INSERT or Break or Line Error 
         // replacement , then receive error chars unaltered.
         if (!(pPortExt->SdxbValues.Xflags & (SDXB_XMSPAR | SDXB_XMSERC)))
         {
            ProposedXflags |= SDXB_XEDATA;
         }
         break;
      default:
         Status = STATUS_INVALID_PARAMETER;
         goto  IoctlExit;
   }

   switch (pLineControl->WordLength)
   {
      case 5:
         ProposedCflags |= SDXB_CS5;
         break;
      case 6:
         ProposedCflags |= SDXB_CS6;
         break;
      case 7:
         ProposedCflags |= SDXB_CS7;
         break;
      case 8:
         ProposedCflags |= SDXB_CS8;
         break;
      default:
         Status = STATUS_INVALID_PARAMETER;
         goto  IoctlExit;
   }

   switch (pLineControl->StopBits)
   {
      case STOP_BIT_1:
         break;
      case STOP_BITS_1_5:
         if (pLineControl->WordLength != 5)
         {
            Status = STATUS_INVALID_PARAMETER;
            goto  IoctlExit;
         }
         else
            ProposedCflags |= SDXB_CSTOPB;
         break;
      case STOP_BITS_2:
         if (pLineControl->WordLength == 5)
         {
            Status = STATUS_INVALID_PARAMETER;
            goto  IoctlExit;
         }
         else
            ProposedCflags |= SDXB_CSTOPB;
         break;
      default:
         Status = STATUS_INVALID_PARAMETER;
         goto  IoctlExit;
   }


   //
   // All the setup values have been set, do we need to update S/DXB module?
   //
   if ((ProposedCflags != pPortExt->SdxbValues.Cflags)
       || (ProposedXflags != pPortExt->SdxbValues.Xflags)
       || (ProposedIflags != pPortExt->SdxbValues.Iflags))
   {

      pPortExt->SdxbValues.Cflags = ProposedCflags;
      pPortExt->SdxbValues.Xflags = ProposedXflags;
      pPortExt->SdxbValues.Iflags = ProposedIflags;

      if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_LINE_USE_PENDING_IRP )
      {
         pIrpRecorded = pIrp;
      }
      pPortExt->DxbCount++;

		KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
      Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                                PORT_PARAM_CMD, REASON_GEN_IOCTL, DS_PRIORITY_LOW, 
                                PutAwaitingCompletion, AsynBuildSetPortCmd);
      KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
   }
IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetLineControl
 *
 * Purpose:
 *
 *    Return the port's configured parity, data width, and stop bits.
 *
 *
 * Method:
 *
 *    o Return the port's configured parity, data width, and stop bits.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetLineControl(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl work to perform.
   IN ULONG             BufferSize,    // Output buffer size.
   OUT PSERIAL_LINE_CONTROL
                        pLineControl   // Control information.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL, ("ASYN!IoGetLineControl: PortNo=0x%x\n",
                        pPortExt->Port));

   Status = STATUS_SUCCESS;

   if (BufferSize < sizeof(*pLineControl))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto  IoctlExit;
   }

   switch (pPortExt->SdxbValues.Cflags & (SDXB_CSIZE))
   {
      case SDXB_CS5:
           pLineControl->WordLength = 5;
           break;
      case SDXB_CS6:
           pLineControl->WordLength = 6;
           break;
      case SDXB_CS7:
           pLineControl->WordLength = 7;
           break;
      case SDXB_CS8:
           pLineControl->WordLength = 8;
           break;
   }

   switch (pPortExt->SdxbValues.Cflags & SDXB_CSTOPB)
   {
      case 0x00:
           pLineControl->StopBits = STOP_BIT_1;
           break;
      case SDXB_CSTOPB:
           if (pLineControl->WordLength == 5)
               pLineControl->StopBits = STOP_BITS_1_5;
           else
               pLineControl->StopBits = STOP_BITS_2;
           break;
   }

   if (!(pPortExt->SdxbValues.Cflags & SDXB_PARENB))
   {
      pLineControl->Parity = NO_PARITY;
   }
   else
   {
      if (!(pPortExt->SdxbValues.Xflags & SDXB_XPAR))
      {
         if (pPortExt->SdxbValues.Cflags & SDXB_PARODD)
         {
            pLineControl->Parity = ODD_PARITY;
         }
         else
         {
            pLineControl->Parity = EVEN_PARITY;
         }
      }
      else
      {
         if (pPortExt->SdxbValues.Cflags & SDXB_PARODD)
         {
            pLineControl->Parity = MARK_PARITY;
         }
         else
         {
            pLineControl->Parity = SPACE_PARITY;
         }
      }
   }
   pIrp->IoStatus.Information = sizeof(*pLineControl);
IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSetBreak.
 *
 * Purpose:
 *
 *    Set/Clear Break.
 *
 *
 * Method:
 *
 *    o Mark associated IRP as Pending.
 *    o Hold the Port's Spin Lock.
 *    o Generate an S/DXB break command.
 *    o Release the Port's Spin Lock.
 *
 *
 * Returns:
 *
 *    o STATUS_PENDING is returned.
 *    o STATUS_INVALID_PARAMETER on Error.
 *
 *
 * Notes:
 *
 *    o When the DsPutMsgEx completion routine is called the IRP status is
 *      changed from PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoSetBreak(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN BOOLEAN           BreakOn        // TRUE if Break is on.
)
{
   NTSTATUS    Status;                 // Call return value.
   PPUT_MSG_EV pPutEvent = NULL;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL, ("ASYN!IoSetBreak: PortNo=0x%x\n", pPortExt->Port));

   if ( (BreakOn && (pPortExt->OpenStateMask & BreakOnMask)) || 
        (!BreakOn && !(pPortExt->OpenStateMask & BreakOnMask)) )
   {
      Status = STATUS_SUCCESS;
      goto  IoctlExit;
   }

	//	if (pPortExt->DxbCount > MAX_SDXB_IOCTL) {
   //   Status = STATUS_DEVICE_BUSY;  // What status code should I use? RFM
   //   goto IoctlExit;
   // }

   
   if (BreakOn)
      pPortExt->OpenStateMask |= BreakOnMask;
   else
      pPortExt->OpenStateMask &= ~BreakOnMask;

   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_BREAK_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }

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

   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                             BREAK_CMD, REASON_GEN_IOCTL, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildBreakCmd);
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSendChar.
 *
 * Purpose:
 *
 *    Sends a single character immediately.
 *
 *
 * Method:
 *
 *    o Mark associated IRP as Pending.
 *    o Hold the Port's Spin Lock.
 *    o Generate an S/DXB SEND_IMMED_CHAR command.
 *    o Release the Port's Spin Lock.
 *
 *
 * Returns:
 *
 *    o STATUS_PENDING is returned.
 *    o STATUS_INVALID_PARAMETER on Error.
 *
 *
 * Notes:
 *
 *    o When the DsPutMsgEx completion routine is called the IRP status is
 *      changed from PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoSendChar(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN ULONG             BufferSize,    // Input buffer size.
   IN PUCHAR            pChar          // Char to send
)
{
   NTSTATUS    Status;                 // Call return value.
   PPUT_MSG_EV pPutEvent = NULL;

   DigiDump(DIGIIOCTL | DIGIIOCTL1 | DIGIALL, ("ASYN!IoSendChar: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pChar))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }
   //
   // Illegal request when already processing an immediate character OR
   // a BREAK character write.
   //
   if ((pPortExt->OpenStateMask & ImmWriteMask) || 
       (pPortExt->OpenStateMask & BreakOnMask))
   {
       Status = STATUS_INVALID_PARAMETER;
       goto IoctlExit;
   }


   pIrp->IoStatus.Information = 1;

   pPortExt->OpenStateMask |= ImmWriteMask;
   pPortExt->SendImmChar = *pChar;

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

   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrp, 
                             SDXB_SCIMMED, REASON_SEND_IMMED, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildImmedCharCmd);

   //
   // Update Performance Statistics as necessary
   //
   pPortExt->PerfStats.TransmittedCount++;

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

   //
   // The S/DXB Event(SDXB_TXI) response will notify when this command 
   // should complete
   //
IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSetTimeouts.
 *
 * Purpose:
 *
 *    Set the Timeout settings
 *
 *
 * Method:
 *
 *    o Set the timeout settings based on the supplied settings.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS is returned.
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoSetTimeouts(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated work order.
   IN ULONG             BufferSize,    // Output buffer size.
   IN PSERIAL_TIMEOUTS  pTimeouts      // Set these timeout values.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL, ("ASYN!IoSetTimeouts: PortNo=0x%x\n", pPortExt->Port));
   if (BufferSize < sizeof(*pTimeouts))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto  IoctlExit;
   }
#ifdef DBG
   if ( (pTimeouts->ReadIntervalTimeout != pPortExt->TimeoutValues.ReadIntervalTimeout) ||
        (pTimeouts->ReadTotalTimeoutMultiplier != pPortExt->TimeoutValues.ReadTotalTimeoutMultiplier) ||
        (pTimeouts->ReadTotalTimeoutConstant != pPortExt->TimeoutValues.ReadTotalTimeoutConstant) ||
        (pTimeouts->WriteTotalTimeoutMultiplier != pPortExt->TimeoutValues.WriteTotalTimeoutMultiplier) ||
        (pTimeouts->WriteTotalTimeoutConstant != pPortExt->TimeoutValues.WriteTotalTimeoutConstant) )
   {
      DigiDump(DIGITIMEOUT1, ("ASYN!IoSetTimeouts: PortNo=0x%x Read Int:0x%x Mul:0x%x Con:0x%x - Write Mul:0x%x Con:0x%x\n", 
                             pPortExt->Port,
                             pTimeouts->ReadIntervalTimeout,
                             pTimeouts->ReadTotalTimeoutMultiplier,
                             pTimeouts->ReadTotalTimeoutConstant,
                             pTimeouts->WriteTotalTimeoutMultiplier,
                             pTimeouts->WriteTotalTimeoutConstant));
   }
#endif
   pPortExt->TimeoutValues = *pTimeouts;

   Status = STATUS_SUCCESS;

IoctlExit:
   pIrp->IoStatus.Information = 0;
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetTimeouts.
 *
 * Purpose:
 *
 *    Get the Timeout settings
 *
 *
 * Method:
 *
 *    o Get the timeout settings based on the PortExt structure.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS is returned.
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetTimeouts(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl work to perform.
   IN ULONG             BufferSize,    // Output buffer size.
   OUT PSERIAL_TIMEOUTS pTimeouts      // return timeout values.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL|DIGITIMEOUT, ("ASYN!IoGetTimeouts: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pTimeouts))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto  IoctlExit;
   }
   *pTimeouts = pPortExt->TimeoutValues;
   pIrp->IoStatus.Information = sizeof(*pTimeouts);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoDtr.
 *
 * Purpose:
 *
 *    Set or Clear the DTR signal.
 *
 *
 * Method:
 *
 *    o Mark associated IRP as Pending.
 *    o Hold the Port's PortLock Lock.
 *    o Set or Clear the DTR signal.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS no signal change necessary.
 *    o STATUS_INVALID_PARAMETER on Error.
 *    o STATUS_PENDING attempting to change signal.
 *
 *
 * Notes:
 *
 *    o When an S/DXB response is received the IRP status is changed from
 *      PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoDtr(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN BOOLEAN           SetDtr         // TRUE for Set DTR
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   NTSTATUS    Status;                 // Call return value.
   IRP_REASON  Reason;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoDtr: PortNo=0x%x Dtr=%d\n", 
                         pPortExt->Port, SetDtr));

   if ( (pPortExt->Handflow.ControlHandShake & SERIAL_DTR_MASK) ==
         SERIAL_DTR_HANDSHAKE )
   {
      Status = STATUS_INVALID_PARAMETER;
      goto IoctlExit;
   }
   if (SetDtr)
   {
      if ( pPortExt->PnpModemDetectHandle != NULL )
      	// DTR high could signal that PnP device detection is in progress.
      	PnpModemDtrHi( pPortExt->PnpModemDetectHandle );
   
      if ( (pPortExt->SdxbValues.Mout & SDXB_DTR) || 
         (pPortExt->SdxbValues.Mstat & SDXB_DTR))
      {
         pPortExt->SdxbValues.Mout |= SDXB_DTR;
         Status = STATUS_SUCCESS;
         goto IoctlExit;
      }
   }
   else
   {
      if ( pPortExt->PnpModemDetectHandle != NULL )
      	// DTR low could signal that PnP device detection is in progress.
      	PnpModemDtrLo( pPortExt->PnpModemDetectHandle );
         
      if ( !(pPortExt->SdxbValues.Mout & SDXB_DTR) || 
         !(pPortExt->SdxbValues.Mstat & SDXB_DTR))
      {
         pPortExt->SdxbValues.Mout &= ~SDXB_DTR;
         Status = STATUS_SUCCESS;
         goto IoctlExit;
      }
   }

   if (SetDtr)
   {
      pPortExt->SdxbValues.Mout |= SDXB_DTR;
      Reason = REASON_SET_DTR;
   }
   else
   {
      pPortExt->SdxbValues.Mout &= ~SDXB_DTR;
      Reason = REASON_CLR_DTR;
   }
  
   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_SIGNALS_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }

	KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                             SYNC_CMD, Reason, DS_PRIORITY_LOW, 
                             PutBuilding, AsynBuildModemCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE, REASON_NONE, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildSyncCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
IoctlExitPutCleanup:
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }

   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoResetDevice
 *
 * Purpose:
 *
 *    Reset the device, this has no meaning for serial ports.
 *
 *
 * Method:
 *
 *    o Just return
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoResetDevice( 
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp           // Associated IRP
   )
{
   NTSTATUS    Status = STATUS_SUCCESS;

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

   pIrp->IoStatus.Information = 0;
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoRts.
 *
 * Purpose:
 *
 *    Set or Clear the RTS signal.
 *
 *
 * Method:
 *
 *    o Mark associated IRP as Pending.
 *    o Hold the Port's PortLock Lock.
 *    o Set or Clear the RTS signal.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS no signal change necessary.
 *    o STATUS_INVALID_PARAMETER on Error.
 *    o STATUS_PENDING attempting to change signal.
 *
 *
 * Notes:
 *
 *    o When an S/DXB response is received the IRP status is changed from
 *      PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoRts(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN BOOLEAN           SetRts         // TRUE for Set RTS
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   NTSTATUS    Status;                 // Call return value.
   IRP_REASON  Reason;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoRts: PortNo=0x%x Rts=%d\n", 
                         pPortExt->Port, SetRts));
   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoRts: HandShake=0x%x\n", 
                         pPortExt->Handflow.ControlHandShake));
   if( (pPortExt->Handflow.ControlHandShake & SERIAL_RTS_MASK) == 
            SERIAL_RTS_HANDSHAKE ||
       (pPortExt->Handflow.ControlHandShake & SERIAL_RTS_MASK) == 
            SERIAL_TRANSMIT_TOGGLE )
   {
      DigiDump( DIGIIOCTL, ("ASYN!IoRts: Returning STATUS_INVALID_PARAMETER\n") );
      Status = STATUS_INVALID_PARAMETER;
      goto IoctlExit;
   }
   if (SetRts)
   {
      if ( pPortExt->PnpModemDetectHandle != NULL)
      {
      	// RTS high could signal that PnP device detection is in progress.
      	PnpModemRtsHi( pPortExt->PnpModemDetectHandle );
         if ( PnpModemIsEnumerated( pPortExt->PnpModemDetectHandle ))
         {
	      	// Successful PnP detection: Get the modem id string and insert it into the receive buffer queue
         	NTSTATUS Status;
            
				PRX_BUF	pRxBuf = (PRX_BUF)AsynAllocEntry( &pPortExt->ReadBufferPool, &Status);
		      if (Status == STATUS_SUCCESS)
	   	   {
            	ULONG	Length = 0;
               
	   	      pRxBuf->Size = DEF_RX_BUF_SIZE;
		         pRxBuf->Len = 0;
		         pRxBuf->Start = 0;
               
               Length = DEF_RX_BUF_SIZE;
               if (STATUS_SUCCESS == PnpModemGetId( &pRxBuf->Data[0], &Length ))
               {
					   DigiDump(DIGIPNP , ("ASYN!IoRts: Retrieved pnp modem string of length 0x%x\n", Length));
	               
	               AsynProcessData( pPortExt, &pRxBuf->Data[0], Length, pRxBuf );
	               
	               if (( pRxBuf->Len == 0 ) || ( pPortExt->RxBufCount > 1 ) || ((PRX_BUF)pPortExt->RxQ.Blink)->Len != 0 )
		               // Discard the PnP id buffer if it is empty or if any data is already in our receive queue
							AsynFreeEntry( &pPortExt->ReadBufferPool, &pRxBuf->ListEntry );
						else         
	               {  
	               	// Otherwise, insert the PnP id buffer at the front of the receive queue
				         pPortExt->RxBufCount++;
				         InsertHeadList( &pPortExt->RxQ, &pRxBuf->ListEntry);
						}
               }   					            
				}               
         }
		}         
   
      if ( (pPortExt->SdxbValues.Mout & SDXB_RTS) || 
         (pPortExt->SdxbValues.Mstat & SDXB_RTS))
      {
         pPortExt->SdxbValues.Mout |= SDXB_RTS;
         Status = STATUS_SUCCESS;
         goto IoctlExit;
      }
   }
   else
   {
      if ( pPortExt->PnpModemDetectHandle != NULL)
      	// RTS low could signal that PnP device detection is in progress.
      	PnpModemRtsLo( pPortExt->PnpModemDetectHandle );
         
      if ( !(pPortExt->SdxbValues.Mout & SDXB_RTS) || 
         !(pPortExt->SdxbValues.Mstat & SDXB_RTS))
      {
         pPortExt->SdxbValues.Mout &= ~SDXB_RTS;
         Status = STATUS_SUCCESS;
         goto IoctlExit;
      }
   }
   if (SetRts)
   {
      pPortExt->SdxbValues.Mout |= SDXB_RTS;
      Reason = REASON_SET_RTS;
   }
   else
   {
      pPortExt->SdxbValues.Mout &= ~SDXB_RTS;
      Reason = REASON_CLR_RTS;
   }

#if 0
// SAH: 9/30/99 - Removed - why was this code duplicated from above?
   if (SetRts)
   {
      pPortExt->SdxbValues.Mout |= SDXB_RTS;
      Reason = REASON_SET_RTS;
   }
   else
   {
      pPortExt->SdxbValues.Mout &= ~SDXB_RTS;
      Reason = REASON_CLR_RTS;
   }
#endif
   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_SIGNALS_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }
	KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);

   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                             SYNC_CMD, Reason, DS_PRIORITY_LOW, 
                             PutBuilding, AsynBuildModemCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE, REASON_NONE, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildSyncCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
IoctlExitPutCleanup:
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetDtrRts
 *
 * Purpose:
 *
 *    Return the current DTR & RTS Modem Signals.
 *
 *
 * Method:
 *
 *    o Return the Port structure's DTR & RTS Mstat values.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetDtrRts(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN ULONG             BufferSize,    // Size of output buffer.
   OUT PULONG           pDtrRtsState   // Destination for modem status.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoGetDtrRts: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pDtrRtsState))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto  IoctlExit;
   }

   *pDtrRtsState = 0;

   if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
       *pDtrRtsState |= SERIAL_DTR_STATE;

   if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
       *pDtrRtsState |= SERIAL_RTS_STATE;

   pIrp->IoStatus.Information = sizeof(*pDtrRtsState);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoXonXoff.
 *
 * Purpose:
 *
 *    Simulate an Xon or an Xoff.
 *
 *
 * Method:
 *
 *    o Mark associated IRP as Pending.
 *    o Hold the Port's PortLock Lock.
 *    o Simulate an Xon or Xoff as appropriate.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS no signal change necessary.
 *    o STATUS_INVALID_PARAMETER on Error.
 *    o STATUS_PENDING attempting to change signal.
 *
 *
 * Notes:
 *
 *    o When an S/DXB response is received the IRP status is changed from
 *      PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoXonXoff(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN BOOLEAN           SimulateXon    // TRUE for Simulating Xon
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   NTSTATUS    Status;                 // Call return value.
   IRP_REASON  Reason;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL, ("ASYN!IoXonXoff: PortNo=0x%x Xon=%d\n", 
                         pPortExt->Port, SimulateXon));

   if (pPortExt->DxbCount > (MAX_SDXB_IOCTL/2)) {
      Status = STATUS_DEVICE_BUSY;  // what status code should I really use? RFM
      goto IoctlExit;
   }

   pPortExt->DxbCount++;

   if (SimulateXon)
   {
      pPortExt->SdxbValues.Pset &= ~SDXB_OPS;
      pPortExt->SdxbValues.Preset |= SDXB_OPS;
      Reason = REASON_UNPAUSE;
   }
   else
   {
      pPortExt->SdxbValues.Pset |= SDXB_OPS;
      pPortExt->SdxbValues.Preset &= ~SDXB_OPS;
      Reason = REASON_PAUSE;
   }

   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_XONXOFF_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }

   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                             PAUSE_CMD, Reason, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildPauseCmd);
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

IoctlExit:

   pIrp->IoStatus.Information = 0;

   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoPurge.
 *
 * Purpose:
 *
 *    Cleans up the serial interface.
 *
 *
 * Method:
 *
 *    o Purge queued transmit IRPs.
 *    o Purge queued Receive IRPs.
 *    o Flush transmit buffer.
 *    o Flush receive buffer.
 *
 *
 * 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
AsynIoPurge(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl request to perform.
   IN ULONG             BufferSize,    // Input buffer size.
   IN PULONG            pPurgeFlag     // Bitmap of purge request.
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   NTSTATUS    Status = STATUS_SUCCESS;// Call return value.
   PLIST_ENTRY pEntry, pEntryNext;
   UCHAR       FlushFlag;
   PIRP        pIrpRecorded = NULL;
   BOOLEAN fCalledInternally = pIrp == NULL ? TRUE : FALSE;

   if (BufferSize < sizeof(*pPurgeFlag))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }
   if ((*pPurgeFlag == 0) ||
       (*pPurgeFlag & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT |
                        SERIAL_PURGE_TXCLEAR | SERIAL_PURGE_RXCLEAR)))
   {
       DigiDump(DIGIPURGE, ("***ASYN!IoPurge: PortNo=%d Invalid Flag=0x%x\n", 
                  pPortExt->Port, *pPurgeFlag));
       Status = STATUS_INVALID_PARAMETER;
       goto IoctlExit;
   }
#ifdef DBG
   if (*pPurgeFlag == (SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT |
                       SERIAL_PURGE_TXCLEAR | SERIAL_PURGE_RXCLEAR))
   {
      DigiDump(DIGIALL, ("ASYN!IoPurge: Port=%d Flag=0x%x\n", 
                        pPortExt->Port, *pPurgeFlag));
   }
#endif
   DigiDump(DIGIPURGE, ("ASYN!IoPurge: Port=%d Flag=0x%x\n", 
                        pPortExt->Port, *pPurgeFlag));
   FlushFlag = 0;

   //
   // Complete all queued Transmit IRPs.
   //
   if (*pPurgeFlag & SERIAL_PURGE_TXABORT)
   {
#if 0 
		// Code caused problem with making RAS connection - Vantive 3147: Sempra Trading
      // NOP'd for now, since this code was added for Comapi32 test compliance.
      // Will revisit in the digiasyn driver rewrite. SAH 11/5/99  
      FlushFlag |= SDXB_FLUSH_OUTPUT;
      pPortExt->OpenStateMask |= TxDataPurgeMask;
#endif
      if (pPortExt->OpenStateMask & ImmWriteMask)
      {
         pPortExt->OpenStateMask &= ~ImmWriteMask;

         //
         // Even though we are forcing the Immediate Char to complete before 
         // we get an ack from the S/DXB, since this command is sent 
         // out-of-band it probably did succeed.  
         // Therefore return STATUS_SUCCESS.
         //
         DigiDump(DIGIPURGE | DIGIALL, ("ASYN!IoPurge: Complete IMMED PortNo=0x%x\n", 
            pPortExt->Port));
         AsynFindAndCompletePut( pPortExt, CHAR_IMMED_CMD, STATUS_SUCCESS);
      }

      // FOO, What about the PutQueue??

      // Purge the Queued Irps
      AsynPurgeOutboundIrps( pPortExt);
      
      KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
      //
      // DsPurge must be called with the PortLock released, 
      // since the PutMsg Completion routine will acquire this lock.
      Status = DsPurge( pPortExt->DsPortHandle, DS_PURGE_WRITE | DS_PURGE_PUTMSG);
      KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
      
      if (Status != STATUS_SUCCESS)
      {
         // For some strange reason Purge failed.
         PORT_ERROR(pPortExt);
      }
   }
   //
   // Complete all queued Receive IRPs.
   //
   if (*pPurgeFlag & SERIAL_PURGE_RXABORT)
   {
      pPortExt->OpenStateMask |= RxDataPurgeMask;
      FlushFlag |= SDXB_FLUSH_INPUT;
      AsynPurgeReadIrps( pPortExt);
   }

   //
   // Complete any XnGet queued IRPs.
   //
   AsynPurgeXnGetIrps( pPortExt );

   //
   // Flush the Transmit buffer.
   //
   if (*pPurgeFlag & SERIAL_PURGE_TXCLEAR)
   {
      FlushFlag |= SDXB_FLUSH_OUTPUT;
   }

   //
   // Flush the Receive buffers.
   //
   if (*pPurgeFlag & SERIAL_PURGE_RXCLEAR)
   {
      //
      // Remove any data in the type-ahead buffer
      //
      // Keep only the last Receive Buffer in the type-ahead pool.
      // Purge all data in last buffer by setting the adding the 
      // len to the start.
      //
      DigiDump(DIGIPURGE, ("ASYN!IoPurge: Port=0x%x TypeAhead=0x%x\n",
                          pPortExt->Port, pPortExt->TotalRxBytesBuffered));
      pPortExt->TotalRxBytesBuffered = 0;

      pEntry = pPortExt->RxQ.Flink;
      while ((pEntry != &pPortExt->RxQ) && (pEntry != pPortExt->RxQ.Blink))
      {
         // Free unnecessary buffers.
         // Only one is needed when the type-ahead buffers are flushed.
         pEntryNext = pEntry->Flink;

         RemoveEntryList( &((PRX_BUF)pEntry)->ListEntry);
         AsynFreeEntry( &pPortExt->ReadBufferPool, pEntry);
         pEntry = pEntryNext;
         pPortExt->RxBufCount--;
      }
      if (pEntry == pPortExt->RxQ.Flink)
      {
         PRX_BUF  pRxBuf;

         pRxBuf = (PRX_BUF)pEntry;
         pRxBuf->Len = 0;
      }
      else 
      {
         PORT_ERROR(pPortExt);
         DigiDump(DIGIALL, ("*** ASYN!IoPurge: There should be One Entry in the RxQ\n"));
      }

      //
      // Ignore Data in the Receive for this port
      //
      pPortExt->OpenStateMask |= RxDataPurgeMask;
      FlushFlag |= SDXB_FLUSH_INPUT;
   }
   if (FlushFlag)
   {
   
      //
      // Generate a Flush S/DXB command
      //
      pPortExt->SdxbValues.FlushFlags = FlushFlag;
      if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_FLUSH_USE_PENDING_IRP )
      {
         pIrpRecorded = pIrp;
      }
      //
      // NOTE: This must be sent HI-Priority.  If it's sent LOW-Priority and
      // the Tx-Stream is blocked, then the purge will never be seen.
      //
      KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);

      Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded,
                                SYNC_CMD, REASON_PURGE, DS_PRIORITY_HIGH, 
                                PutBuilding, AsynBuildFlush);
      if (Status != STATUS_PENDING)
      {
         goto IoctlExitPutCleanup;
      }
      //
      // Sync command, a convenient tag which tells us when PURGE S/DXB
      // is complete.
      //
      // NOTE: Send this LOW-Priority.  This will guarantee that the flush
      // and all other commands have completed.
      //
      Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                                CMD_NONE, REASON_NONE, DS_PRIORITY_LOW,
                                PutAwaitingCompletion, AsynBuildSyncCmd);
      if (Status != STATUS_PENDING)
      {
         goto IoctlExitPutCleanup;
      }
IoctlExitPutCleanup:
      KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
   }
	
IoctlExit:
	if ( !fCalledInternally )
		pIrp->IoStatus.Information = 0;
      
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSetChars.
 *
 * Purpose:
 *
 *    Set the NT special characters.
 *
 *
 * Method:
 *
 *    o If the special characters have not changed; return.
 *    o Mark associated IRP as Pending.
 *    o Hold the Port's PortLock Lock.
 *    o Change the Special characters settings and convey to S/DXB.
 *    o Mark the end of setting Special characters with a SYNC cmd.
 *    o Release the Port's PortLock Lock.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS no change necessary.
 *    o STATUS_BUFFER_TOO_SMALL on supplied buffer is too small.
 *    o STATUS_INVALID_PARAMETER on Error.
 *    o STATUS_PENDING attempting to change characters in S/DXB.
 *
 *
 * Notes:
 *
 *    o When an S/DXB SYNC response is received the IRP status is changed from
 *      PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoSetChars(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl request to perform.
   IN ULONG             BufferSize,    // Input buffer size.
   IN PSERIAL_CHARS     pChars         // Eof Error Break Event Xon Xoff
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   NTSTATUS    Status;                 // Call return value.
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL, ("ASYN!IoSetChars: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pChars))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }
   if ((pChars->XonChar == pChars->XoffChar) && (pChars->XonChar != 0))
   {
      Status = STATUS_INVALID_PARAMETER;
      goto IoctlExit;
   }

   if (pPortExt->DxbCount > MAX_SDXB_IOCTL) {
      Status = STATUS_DEVICE_BUSY;  // what status code should I really use? RFM
      goto IoctlExit;
   }

   //
   // Do not allow a null character to be BOTH the xon and the xoff character.
   //
   if (( pChars->XonChar == pChars->XoffChar) && (pChars->XonChar == 0))
   {
      Status = STATUS_SUCCESS;
      goto IoctlExit;
   }

   //
   // Have the special characters changed?
   //
   if ( (pChars->XonChar == pPortExt->SpecialChars.XonChar) &&
        (pChars->XoffChar == pPortExt->SpecialChars.XoffChar) &&
        (pChars->ErrorChar == pPortExt->SpecialChars.ErrorChar) &&
        (pChars->BreakChar == pPortExt->SpecialChars.BreakChar) &&
        (pChars->EventChar == pPortExt->SpecialChars.EventChar) )
   {
      // Pickup Eof, even though we currently don't do anything with it.
      pPortExt->SpecialChars = *pChars;
      Status = STATUS_SUCCESS;
      goto IoctlExit;
   }

      //
   // Parity/Framing error replacement char
   //
   if (pPortExt->Handflow.FlowReplace & SERIAL_ERROR_CHAR)
   {
      pPortExt->SdxbValues.Xflags |= SDXB_XMSERC;
      pPortExt->SdxbValues.Xflags &= ~SDXB_XEDATA;
   }
   else
   {
      pPortExt->SdxbValues.Xflags &= ~SDXB_XMSERC;
      pPortExt->SdxbValues.Xflags |= SDXB_XEDATA;
   }

   //
   // Break replacement char
   //
   if (pPortExt->Handflow.FlowReplace & SERIAL_ERROR_BREAK)
   {
      pPortExt->SdxbValues.Xflags |= SDXB_XMSBRC;
   }
   else
   {
      pPortExt->SdxbValues.Xflags &= ~SDXB_XMSBRC;
   }

   //
   // We need to tell S/DXB about the new characters.
   //
   pPortExt->SpecialChars = *pChars;

   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_CHARS_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }
   pPortExt->DxbCount++;
	KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                             SYNC_CMD, REASON_SET_CHARS, DS_PRIORITY_LOW, 
                             PutBuilding, AsynBuildSetPortCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
   //
   // Sync command, a convenient tag which tells us when SET_CHARS S/DXB
   // commands are complete.
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW,
                             PutAwaitingCompletion, AsynBuildSyncCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
IoctlExitPutCleanup:
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetChars
 *
 * Purpose:
 *
 *    Return the current special characters.
 *
 *
 * Method:
 *
 *    o Return the current special characters.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetChars(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl work to perform.
   IN ULONG             BufferSize,    // Size of output buffer.
   OUT PSERIAL_CHARS    pChars         // Special characters
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL, ("ASYN!IoGetChars: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pChars))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto IoctlExit;
   }
   *pChars = pPortExt->SpecialChars;
   pIrp->IoStatus.Information = sizeof(*pChars);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSetHandflow.
 *
 * Purpose:
 *
 *    Set the handshaking and flow control parameters.
 *
 *
 * Method:
 *
 *    o Sanity check the handshake and flow control parameters.
 *    o If the special characters have not changed; return.
 *    o Hold the Port's PortLock Lock.
 *    o Mark associated IRP as Pending.
 *    o Change the Handshaking and flow control settings and convey to S/DXB.
 *    o Mark the end of setting Special characters with a SYNC cmd.
 *    o Release the Port's PortLock Lock.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS no change necessary.
 *    o STATUS_BUFFER_TOO_SMALL on supplied buffer is too small.
 *    o STATUS_INVALID_PARAMETER on Error.
 *    o STATUS_PENDING attempting to change characters in S/DXB.
 *
 *
 * Notes:
 *
 *    o When an S/DXB SYNC response is received the IRP status is changed from
 *      PENDING to SUCCESS and the IRP is completed.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoSetHandflow(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl request to perform.
   IN ULONG             BufferSize,    // Input buffer size.
   IN PSERIAL_HANDFLOW  pHandflow      // Handshaking and Flow control.
)
{
   PPUT_MSG_EV pPutEvent = NULL;
   NTSTATUS    Status = STATUS_SUCCESS;// Call return value.
   USHORT      Iflags;                 // New iflags value.
   UCHAR       Mflow;                  // Modem flow control.
   UCHAR       Mout;                   // Modem signals.
   BOOLEAN     BuildPortParam = FALSE;
   BOOLEAN     BuildModemCtrl = FALSE;
   ULONG       ProposedFlow, ProposedHandshake;
   ULONG       CurrentFlow, CurrentHandshake;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL, ("ASYN!IoSetHandflow: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pHandflow))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }
   if ((pHandflow->ControlHandShake & SERIAL_CONTROL_INVALID) ||
      ((pHandflow->ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_MASK) ||
      (pHandflow->FlowReplace & SERIAL_FLOW_INVALID) ||

      //
      // Insure sane Xon/Xoff limits.
      //
      (pHandflow->XonLimit < 0) || (pHandflow->XoffLimit < 0) ||

      //
      // Don't allow NT's error replacement AND line/modem status insertion.
      //
      ((pPortExt->EscapeChar) && (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)))
   {
      Status = STATUS_INVALID_PARAMETER;
      goto IoctlExit;
   }

   if (pPortExt->DxbCount > MAX_SDXB_IOCTL) {
      Status = STATUS_DEVICE_BUSY;  // What status code should I use? RFM
      goto IoctlExit;
   }

   //
   // Let's see what we need to set (if anything) in S/DXB.
   //

   // Make life a little easier by assigning some shorthand variables.
   ProposedHandshake = pHandflow->ControlHandShake;
   CurrentHandshake = pPortExt->Handflow.ControlHandShake;
   ProposedFlow = pHandflow->FlowReplace;
   CurrentFlow = pPortExt->Handflow.FlowReplace;

   DigiDump(DIGIIOCTL, ("ASYN!IoSetHandflow: newHand=0x%x curHand=0x%x\n", 
                        ProposedHandshake, CurrentHandshake));
   DigiDump(DIGIIOCTL, ("ASYN!IoSetHandflow: newFlow=0x%x curFlow=0x%x\n", 
                        ProposedFlow, CurrentFlow));
   //
   // Is a PORT-PARAM-CMD necessary ?
   //
   if ( ((ProposedHandshake & SERIAL_DTR_MASK & SERIAL_DTR_HANDSHAKE) !=
          (CurrentHandshake & SERIAL_DTR_MASK & SERIAL_DTR_HANDSHAKE)) ||
        ((ProposedFlow & SERIAL_RTS_MASK & SERIAL_RTS_HANDSHAKE) !=
          (CurrentFlow & SERIAL_RTS_MASK & SERIAL_RTS_HANDSHAKE)) ||
        ((ProposedHandshake & SERIAL_OUT_HANDSHAKEMASK) !=
          (CurrentHandshake & SERIAL_OUT_HANDSHAKEMASK)) ||
        ((ProposedFlow & SERIAL_AUTO_TRANSMIT) !=
          (CurrentFlow & SERIAL_AUTO_TRANSMIT)) ||
        ((ProposedFlow & SERIAL_AUTO_RECEIVE) !=
          (CurrentFlow & SERIAL_AUTO_RECEIVE)) ||
        ((ProposedFlow & SERIAL_ERROR_CHAR) !=
          (CurrentFlow & SERIAL_ERROR_CHAR))  ||
        ((ProposedFlow & SERIAL_NULL_STRIPPING) !=
          (CurrentFlow & SERIAL_NULL_STRIPPING)) 
	)
   {
      BuildPortParam = TRUE;
   }

   //
   // Is a MODEM-CTRL-CMD necessary ?
   //
   if ( ((ProposedHandshake & SERIAL_DTR_MASK & SERIAL_DTR_CONTROL) !=
          (CurrentHandshake & SERIAL_DTR_MASK & SERIAL_DTR_CONTROL)) ||
        ((ProposedFlow & SERIAL_RTS_MASK & SERIAL_RTS_CONTROL) !=
          (CurrentFlow & SERIAL_RTS_MASK & SERIAL_RTS_CONTROL)) )
   {
      BuildModemCtrl = TRUE;
   }
   if ( !BuildPortParam && !BuildModemCtrl)
   {
      // Nothing to do, we can leave
      goto IoctlExit;
   }

   //
   // Save the new values
   //
   pPortExt->Handflow.ControlHandShake = ProposedHandshake;
   pPortExt->Handflow.FlowReplace = ProposedFlow;

   
   //
   // Update the Port settings
   //
   Mflow = 0;
   // Preserve setting which are not being changed.
   Mout = pPortExt->SdxbValues.Mout;

   Iflags = pPortExt->SdxbValues.Iflags & ~(SDXB_IXON | SDXB_IXOFF);
   if ((ProposedFlow & SERIAL_RTS_MASK) == SERIAL_RTS_HANDSHAKE)
   {
      Mflow |= SDXB_RTS;
   }
   else if ((ProposedFlow & SERIAL_RTS_MASK) == SERIAL_RTS_CONTROL)
   {
      Mout |= SDXB_RTS;
   }

   if ((ProposedHandshake & SERIAL_DTR_MASK) == SERIAL_DTR_HANDSHAKE)
   {
      Mflow |= SDXB_DTR;
   }
   else if ((ProposedHandshake & SERIAL_DTR_MASK) == SERIAL_DTR_CONTROL)
   {
      Mout |= SDXB_DTR;
   }

   if (ProposedHandshake & SERIAL_CTS_HANDSHAKE)
   {
      Mflow |= SDXB_CTS;
   }

   if (ProposedHandshake & SERIAL_DSR_HANDSHAKE)
   {
      Mflow |= SDXB_DSR;
   }

   if (ProposedHandshake & SERIAL_DCD_HANDSHAKE)
   {
      Mflow |= SDXB_DCD;
   }

   //
   // Turn on software flow control for transmit.
   //
   if (ProposedFlow & SERIAL_AUTO_TRANSMIT)
   {
      Iflags |= SDXB_IXON;
   }

   //
   // Turn on software flow control for receive.
   //
   if (ProposedFlow & SERIAL_AUTO_RECEIVE)
   {
      Iflags |= SDXB_IXOFF;
   }
   //
   // Parity/Framing error replacement char
   //
   if (ProposedFlow & SERIAL_ERROR_CHAR)
   {
      pPortExt->SdxbValues.Xflags |= SDXB_XMSERC;
      pPortExt->SdxbValues.Xflags &= ~SDXB_XEDATA;
   }
   else
   {
      pPortExt->SdxbValues.Xflags &= ~SDXB_XMSERC;
      pPortExt->SdxbValues.Xflags |= SDXB_XEDATA;
   }
   //
   // Break replacement char
   //
   if (pPortExt->Handflow.FlowReplace & SERIAL_ERROR_BREAK)
   {
      pPortExt->SdxbValues.Xflags |= SDXB_XMSBRC;
   }
   else
   {
      pPortExt->SdxbValues.Xflags &= ~SDXB_XMSBRC;
   }
   
	//
	// strip NULLs
	//
   if (ProposedFlow & SERIAL_NULL_STRIPPING) {
		pPortExt->SdxbValues.Xflags |= SDXB_XSTRIP;
	}
	else {
		pPortExt->SdxbValues.Xflags &= ~SDXB_XSTRIP;
	}
	   
   pPortExt->SdxbValues.Mflow = Mflow;
   pPortExt->SdxbValues.Mout = Mout;
   pPortExt->SdxbValues.Iflags = Iflags;

   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_HANDFLOW_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }
	KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);

   if (BuildPortParam)
   {
      pPortExt->DxbCount++;
      Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                                SYNC_CMD, REASON_SET_HANDFLOW, DS_PRIORITY_LOW, 
                                PutBuilding, AsynBuildSetPortCmd);
      if (Status != STATUS_PENDING)
      {
         goto IoctlExitPutCleanup;
      }
   }
   if (BuildModemCtrl)
   {
		pPortExt->DxbCount++;
      Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                                SYNC_CMD, REASON_SET_HANDFLOW, DS_PRIORITY_LOW, 
                                PutBuilding, AsynBuildModemCmd);
      if (Status != STATUS_PENDING)
      {
         goto IoctlExitPutCleanup;
      }
   }
   //
   // Sync command, a convenient tag which tells us when SET_HANDFLOW S/DXB
   // commands are complete.
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                            CMD_NONE,REASON_NONE, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildSyncCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
IoctlExitPutCleanup:
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetHandflow
 *
 * Purpose:
 *
 *    Return the handshaking and flow control parameters.
 *
 *
 * Method:
 *
 *    o Return the handshaking and flow control parameters.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetHandflow(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl work to perform.
   IN ULONG             BufferSize,    // Size of output buffer.
   OUT PSERIAL_HANDFLOW pHandflow      // Flow control and handshaking.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL, ("ASYN!IoGetHandflow: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pHandflow))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto IoctlExit;
   }

   *pHandflow = pPortExt->Handflow;

   pIrp->IoStatus.Information = sizeof(*pHandflow);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetModemStatus
 *
 * Purpose:
 *
 *    Return the current Modem Signals.
 *
 *
 * Method:
 *
 *    o Return the Port structure's Mstat values.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetModemStatus(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl work to perform.
   IN ULONG             BufferSize,    // Size of output buffer.
   OUT PULONG           pModemStatus   // Destination for modem status.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoGetModemStatus: PortNo=0x%x ", pPortExt->Port));

   if (BufferSize < sizeof(*pModemStatus))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto IoctlExit;
   }

   *pModemStatus = 0;

   if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
       *pModemStatus |= SERIAL_CTS_STATE;

   if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
       *pModemStatus |= SERIAL_DSR_STATE;

   if (pPortExt->SdxbValues.Mstat & SDXB_RI)
       *pModemStatus |= SERIAL_RI_STATE;

   if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
       *pModemStatus |= SERIAL_DCD_STATE;

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("State=0x%x\n", *pModemStatus));

   pIrp->IoStatus.Information = sizeof(*pModemStatus);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetCommStatus
 *
 * Purpose:
 *
 *    Return the most recent comm status.
 *
 *
 * Method:
 *
 *    o Return the Port structure's Communications status.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetCommStatus(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated request.
   IN ULONG             BufferSize,    // Size of output buffer.
   OUT PSERIAL_STATUS   pCommStatus    // Destination for Comm status.
)
{
   NTSTATUS    Status;                 // Call return value.
   PPUT_MSG_EV pPutEvent = NULL;

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoGetCommStatus: PortNo=0x%x ", pPortExt->Port));

   if (BufferSize < sizeof(*pCommStatus))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto IoctlExit;
   }

   pCommStatus->Errors = 0;

   if (pPortExt->CommErr.Break)
   {
      pPortExt->CommErr.Break = 0;
      pCommStatus->Errors |= SERIAL_ERROR_BREAK;
   }
   if (pPortExt->CommErr.Framing)
   {
      pPortExt->CommErr.Framing = 0;
      pCommStatus->Errors |= SERIAL_ERROR_FRAMING;
   }
   if (pPortExt->CommErr.Overrun)
   {
      pPortExt->CommErr.Overrun = 0;
      pCommStatus->Errors |= SERIAL_ERROR_OVERRUN;
   }
   if (pPortExt->CommErr.Parity)
   {
      pPortExt->CommErr.Parity = 0;
      pCommStatus->Errors |= SERIAL_ERROR_PARITY;
   }
   pCommStatus->HoldReasons = 0;
   if ((pPortExt->Handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
       !(pPortExt->SdxbValues.Mstat & SDXB_CTS))
   {
      pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
   }
   if ((pPortExt->Handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) &&
       !(pPortExt->SdxbValues.Mstat & SDXB_DSR))
   {
      pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
   }
   if ((pPortExt->Handflow.ControlHandShake & SERIAL_DCD_HANDSHAKE) &&
       !(pPortExt->SdxbValues.Mstat & SDXB_DCD))
   {
      pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
   }
   if (pPortExt->SdxbValues.Estat & SDXB_TXF)
   {
      pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;
   }

   if (pPortExt->SdxbValues.Estat & (SDXB_OPS | SDXB_OPH) )
   {
      pCommStatus->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;
   }
   if ((pPortExt->WaitMask & SERIAL_EV_BREAK) &&
       (pPortExt->SdxbValues.Estat & SDXB_TXB))
   {
      pCommStatus->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;
   }
   pCommStatus->AmountInInQueue = pPortExt->TotalRxBytesBuffered;
   pCommStatus->AmountInOutQueue = pPortExt->TxBytesPending;

   //
   // Must include the immediate char (out of band data) in this
   // calculation.
   //
   if (pPortExt->OpenStateMask & ImmWriteMask)
   {
      pCommStatus->AmountInOutQueue++;
      pCommStatus->WaitForImmediate = TRUE;
   }
   else
   {
      pCommStatus->WaitForImmediate = FALSE;
   }

   pPortExt->SdxbValues.LerrFlags = SDXB_CLEAR;

	// Clear SDXB line error statistics only if errors were reported.
   //  Some applications use GetCommStatus to poll for amount of data on the rx/tx queues.
   //  No need to forward a command to SDXB on every poll.
	if ( pCommStatus->Errors != 0 )
   {

	   if (pPortExt->DxbCount < MAX_SDXB_IOCTL) 
      {
		   //
		   // Clear the line error statistics with SDXB_LERR,
		   // but only if our SDXB_COUNT isn't too high
		   //
	      pPortExt->DxbCount++;
	      KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
	      Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
	                             CMD_NONE,REASON_LERR, DS_PRIORITY_LOW,
	                             PutBuilding, AsynBuildLineErrorCmd);
	      KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
	   }
	}
   pCommStatus->EofReceived = FALSE;

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("InQ=%d OutQ=%d Hold=0x%x Errors=0x%x\n", 
      pCommStatus->AmountInInQueue, pCommStatus->AmountInOutQueue, 
      pCommStatus->HoldReasons, pCommStatus->Errors));

   pIrp->IoStatus.Information = sizeof(*pCommStatus);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetProperties
 *
 * Purpose:
 *
 *    Return the Port capabilities.
 *
 *
 * Method:
 *
 *    o Provide all possible baud rates and configurations.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoGetProperties(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated request.
   IN ULONG             BufferSize,    // Size of output buffer.
   OUT PSERIAL_COMMPROP pProperties    // Destination for Comm Properties.
)
{
   NTSTATUS    Status;                 // Call return value.

   DigiDump(DIGIIOCTL | DIGIIOCTL1, ("ASYN!IoGetProperties: PortNo=0x%x\n", 
         pPortExt->Port));

   if (BufferSize < sizeof(*pProperties))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto IoctlExit;
   }

   RtlZeroMemory( pProperties, sizeof(*pProperties));

   pProperties->PacketLength = sizeof(*pProperties);
   pProperties->PacketVersion = 2;
   pProperties->ServiceMask = SERIAL_SP_SERIALCOMM;
   pProperties->Reserved1 = 0;

   pProperties->MaxTxQueue = pPortExt->SdxbValues.Outsize;
   pProperties->MaxRxQueue = pPortExt->SdxbValues.Insize;

   pProperties->CurrentTxQueue = pPortExt->SdxbValues.Outsize;
   pProperties->CurrentRxQueue = pPortExt->SdxbValues.Insize;

   pProperties->SettableBaud = 0;

   pProperties->MaxBaud = SERIAL_BAUD_USER;

   pProperties->ProvSubType = SERIAL_SP_RS232;
   pProperties->ProvCapabilities = SERIAL_PCF_DTRDSR |
                                   SERIAL_PCF_RTSCTS |
                                   SERIAL_PCF_CD     |
                                   SERIAL_PCF_PARITY_CHECK |
                                   SERIAL_PCF_XONXOFF |
                                   SERIAL_PCF_SETXCHAR |
                                   SERIAL_PCF_TOTALTIMEOUTS |
                                   SERIAL_PCF_INTTIMEOUTS |
                                   SERIAL_PCF_SPECIALCHARS;

   pProperties->SettableParams = SERIAL_SP_PARITY |
                                 SERIAL_SP_BAUD |
                                 SERIAL_SP_DATABITS |
                                 SERIAL_SP_STOPBITS |
                                 SERIAL_SP_HANDSHAKING |
                                 SERIAL_SP_PARITY_CHECK |
                                 SERIAL_SP_CARRIER_DETECT;

   pProperties->SettableBaud |= SERIAL_BAUD_075 |
                                SERIAL_BAUD_110 |
                                SERIAL_BAUD_134_5 |
                                SERIAL_BAUD_150 |
                                SERIAL_BAUD_300 |
                                SERIAL_BAUD_600 |
                                SERIAL_BAUD_1200 |
                                SERIAL_BAUD_1800 |
                                SERIAL_BAUD_2400 |
                                SERIAL_BAUD_4800 |
                                SERIAL_BAUD_7200 |
                                SERIAL_BAUD_9600 |
                                SERIAL_BAUD_14400 |
                                SERIAL_BAUD_19200 |
                                SERIAL_BAUD_38400 |
                                SERIAL_BAUD_57600 |
                                SERIAL_BAUD_115200 |
                                SERIAL_BAUD_USER;

   pProperties->SettableData = SERIAL_DATABITS_5 |
                               SERIAL_DATABITS_6 |
                               SERIAL_DATABITS_7 |
                               SERIAL_DATABITS_8;

   pProperties->SettableStopParity = SERIAL_STOPBITS_10 |
                                     SERIAL_STOPBITS_15 |
                                     SERIAL_STOPBITS_20 |
                                     SERIAL_PARITY_NONE |
                                     SERIAL_PARITY_ODD  |
                                     SERIAL_PARITY_EVEN |
                                     SERIAL_PARITY_MARK |
                                     SERIAL_PARITY_SPACE;

   pIrp->IoStatus.Information = sizeof(*pProperties);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoLsrmstInsert
 *
 * Purpose:
 *
 *    Establish NT's error reporting escape character; similar to Digi's
 *    DOSMODE implementation.
 *
 *
 * Method:
 *
 *    o Send the updated notification events to the PortServer.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoLsrmstInsert( 
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated request.
   IN ULONG             BufferSize,    // Size of input buffer.
   OUT PUCHAR           pEscapeChar    // Escape character to use.
)
{
   NTSTATUS    Status;        // Call return value.
   PPUT_MSG_EV pPutEvent = NULL;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL, ("ASYN!IoLsrmstInsert: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pEscapeChar))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }

   if (*pEscapeChar)
   {
       if ((*pEscapeChar == pPortExt->SpecialChars.XoffChar) ||
           (*pEscapeChar == pPortExt->SpecialChars.XonChar) ||
           (pPortExt->Handflow.FlowReplace & SERIAL_ERROR_CHAR))
       {
          Status = STATUS_INVALID_PARAMETER;
          goto IoctlExit;
       }
   }

   if (pPortExt->DxbCount > MAX_SDXB_IOCTL) {
      Status = STATUS_DEVICE_BUSY;  // What status code should I use? RFM
      goto IoctlExit;
   }

   //
   // We need to tell S/DXB about the new escape character.
   //


   //
   // Update the Port settings
   //
   if (*pEscapeChar)
   {
      pPortExt->SdxbValues.Xflags |= SDXB_XMSPAR | SDXB_XMODEM;
      pPortExt->SdxbValues.Iflags |= SDXB_PARMRK;

      // Turn off errored-data-placed-in-input-stream-without-modification
      pPortExt->SdxbValues.Xflags &= ~SDXB_XEDATA;
   }
   else
   {
      pPortExt->SdxbValues.Xflags &= ~(SDXB_XMSPAR | SDXB_XMODEM);
      pPortExt->SdxbValues.Iflags &= ~SDXB_PARMRK;

      // Turn on errored-data-placed-in-input-stream-without-modification
      if (!(pPortExt->SdxbValues.Xflags & (SDXB_XMSPAR | SDXB_XMSERC))) {
         pPortExt->SdxbValues.Xflags |= SDXB_XEDATA;
      }
   }
   pPortExt->EscapeChar = *pEscapeChar;
   pPortExt->SpecialChars.BreakChar = *pEscapeChar;

   if ( pPortExt->CardExt->RunTimeOptions & RUNTIME_SET_LSMSR_USE_PENDING_IRP )
   {
      pIrpRecorded = pIrp;
   }
   pPortExt->DxbCount++;
   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                             SYNC_CMD, REASON_SET_LSRMST, DS_PRIORITY_LOW, 
                             PutBuilding, AsynBuildSetPortCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }

   //
   // Sync command, a convenient tag which tells us when SET_LSRMST S/DXB
   // commands are complete.
   //
   Status = AsynPutEventMsg( pPortExt, &pPutEvent, NULL, 
                             CMD_NONE,REASON_NONE, DS_PRIORITY_LOW, 
                             PutAwaitingCompletion, AsynBuildSyncCmd);
   if (Status != STATUS_PENDING)
   {
      goto IoctlExitPutCleanup;
   }
IoctlExitPutCleanup:
   KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);

IoctlExit:
   pIrp->IoStatus.Information = 0;
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoConfigSize
 *
 * Purpose:
 *
 *    Return the Config Size
 *
 *
 * Method:
 *
 *    o Return zero.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsynIoConfigSize(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated request.
   IN ULONG             BufferSize,    // Size of output buffer.
   OUT PULONG           pConfigSize    // Destination for Config Size.
)
{
   NTSTATUS    Status = STATUS_SUCCESS;   // Call return value.

   DigiDump(DIGIIOCTL, ("ASYN!IoConfigSize: PortNo=0x%x\n", pPortExt->Port));

   if (BufferSize < sizeof(*pConfigSize))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      pIrp->IoStatus.Information = 0;
      goto IoctlExit;
   }
   pIrp->IoStatus.Information = sizeof(ULONG);
   pIrp->IoStatus.Status = STATUS_SUCCESS;

   *pConfigSize = 0;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsyncSdxbXnGet
 *
 * Purpose:
 *
 *    Get the ISDN Call Control Response
 *
 *
 * Method:
 *
 *    o 
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS is returned.
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsyncSdxbXnGet(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   OUT PIRP             pIrp,          // Associated work order.
   IN ULONG             BufferSize,    // Output buffer size.
   OUT PXN_GET 			pXnGet        // 
)
{
   NTSTATUS    Status;                 // Call return value.
   PLIST_ENTRY pEntry;                 // LIST_ENTRY intermediate value.   
   PXN_GET_LIST pXnGetList;            // Queued Xn_Get message
   KIRQL       OldIrql ;

   DigiDump(DIGIIOCTL, ("ASYNC!SdxbXnGet: PortNo=0x%x\n", pPortExt->Port));
   // lock down the buffers right away.
   if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pXnGet->pControl, 
	                        pXnGet->cbMaxControl, &pXnGet->pProviderControl ) ) )
	  goto XnGetIoctlExit;

   if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pXnGet->pData, 
	                        pXnGet->cbMaxData, &pXnGet->pProviderData ) ) )
	  goto XnGetIoctlExit;

   // is xn_get list empty?
   //   yes, then queue the get and return pending.
   //   nope, then pop one off the front of the list and return.
   if (! IsListEmpty (&pPortExt->XnGetPktQueue))
   {
      // nope
	  // let's pop the queue and return this baby
	  pEntry = RemoveHeadList( &pPortExt->XnGetPktQueue);
      pXnGetList = CONTAINING_RECORD( pEntry, XN_GET_LIST, List);

	  if (pXnGetList != NULL)
	  {
	     if (pXnGetList->XnGetEntry != NULL)
		 {
	        // copy data
			if ((pXnGetList->XnGetEntry->cbControl > pXnGet->cbMaxControl) ||
  		        (pXnGetList->XnGetEntry->cbData > pXnGet->cbMaxData) )
			{
	            pIrp->IoStatus.Information = 0 ;
				pIrp->IoStatus.Status = Status = STATUS_BUFFER_TOO_SMALL;
			}
			else
			{
		        RtlCopyMemory( pXnGet->pProviderControl, pXnGetList->XnGetEntry->pControl, 
	            	           pXnGetList->XnGetEntry->cbControl);
		        pXnGet->cbControl = pXnGetList->XnGetEntry->cbControl ;
        	    RtlCopyMemory (pXnGet->pProviderData, pXnGetList->XnGetEntry->pData, 
	        	               pXnGetList->XnGetEntry->cbData);
		        pXnGet->cbData = pXnGetList->XnGetEntry->cbData ;

    	        pIrp->IoStatus.Information = sizeof( XN_GET ) ;
				pIrp->IoStatus.Status = Status = STATUS_SUCCESS;
			}
            // let's free our memory.
	        if (pXnGetList->XnGetEntry->pControl)
	           DigiFreeMem (pXnGetList->XnGetEntry->pControl);
	        if (pXnGetList->XnGetEntry->pData)
	           DigiFreeMem (pXnGetList->XnGetEntry->pData);
   	        DigiFreeMem (pXnGetList->XnGetEntry) ;
		 }
		 else
		 {
            pIrp->IoStatus.Information = 0 ;
			pIrp->IoStatus.Status = Status = STATUS_UNSUCCESSFUL;
		 }
		 // free the get list
	     DigiFreeMem (pXnGetList) ;
      }
	  else
	     Status = STATUS_UNSUCCESSFUL ;
	 
   }
   else
   {
	  InsertTailList (&pPortExt->XnGetQueue, &pIrp->Tail.Overlay.ListEntry);
//      IoSetCancelRoutine( pIrp, AsynCancelXnGetIrp);
//      IoMarkIrpPending( pIrp );
      Status = STATUS_PENDING;
   }

XnGetIoctlExit :
   if ( Status != STATUS_PENDING )
   {
	  DigiUnlockBuffers( pIrp );
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsyncSdxbXnPut
 *
 * Purpose:
 *
 *    Send the ISDN Call Control Request
 *
 *
 * Method:
 *
 *    o We allocate a new packet if there is any data to be sent
 *    o If there is just a control portion then we do not have to
 *    o We call DsPutMsgEx
 *    o We then return STATUS_SUCCESS.
 *    o The callback routine frees the allocated buffer.
 *
 *
 * Returns:
 *
 *    o STATUS_SUCCESS is returned.
 *
 *
 *----------------------------------------------------------------------
 */
static NTSTATUS
AsyncSdxbXnPut(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated work order.
   IN ULONG             BufferSize,    // packet buffer size.
   IN PXN_PUT 			pXnPut         // packet to be sent to board.
)
{
   NTSTATUS    Status;                 // Call return value.
   PVOID	   pData ;				   // 
   PVOID	   pControl ;			   //
#define ISDN_MESSAGE_ENCAP_SIZE 4
   unsigned char *pTmp ;

   DigiDump(DIGIIOCTL, ("ASYNC!SdxbXnPut: PortNo=0x%x\n", pPortExt->Port));

   // if we aren't open then this is silly
   // or if we havent opened this with isdncc then we shouldn't be doing this.
   if ((pPortExt->State != OpenState) ||
	   (pPortExt->CardExt->IsdnCC != DXB_REGVAL_ASYN_ISDN_CC_ENABLED))
   {
      Status = STATUS_UNSUCCESSFUL;
      goto XnPutIoctlExit;
   }
   
   // lock down the buffers right away.
   if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pXnPut->pControl, 
	                        pXnPut->cbControl, &pXnPut->pProviderControl ) ) )
	  goto XnPutIoctlExit;

   if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pXnPut->pData, 
	                        pXnPut->cbData, &pXnPut->pProviderData ) ) )
	  goto XnPutIoctlExit;

   //
   // We are going to return success so we need to know if we have to alloc
   // a buffer for the data portion of this packet.
   //
   if (pXnPut->cbData > 0)
   {
	  // we need a place to copy this
	  pData = (PVOID) DigiAllocMem (NonPagedPool, pXnPut->cbData);
	  // copy
	  RtlCopyMemory (pData, pXnPut->pProviderData, pXnPut->cbData) ;
   }
   else
      pData = NULL ;

   //
   // We also need a new control message buffer so that we can add
   // the ISDN MESSAGE ENCAPSULATION ENVELOPE (aka "80 00 00 00") to
   // the front of the message.
   //
   // we need a place to copy this
   pControl = (PVOID) DigiAllocMem (NonPagedPool, 
                            pXnPut->cbControl+ISDN_MESSAGE_ENCAP_SIZE) ;

   pTmp = pControl ;
   // copy 
   *pTmp = SDXB_ISDN ; pTmp++ ;
   *pTmp = 0x00 ; pTmp++ ;
   *pTmp = 0x00 ; pTmp++ ;
   *pTmp = 0x00 ; pTmp++ ;

    if (pXnPut->cbControl > 0)
      RtlCopyMemory (pTmp, pXnPut->pProviderControl, pXnPut->cbControl);

   // try to send it.
   Status = DsPutMsgEx( pPortExt->DsPortHandle, 
      					(PUCHAR)pControl, 	pXnPut->cbControl+ISDN_MESSAGE_ENCAP_SIZE, 
  	  				    (PUCHAR)pData, pXnPut->cbData,
                        (HANDLE)pData, DS_PRIORITY_HIGH, AsynXnPutMsgComplete);

   // the control portion allways gets copied.
   DigiFreeMem(pControl);

   // if we had an error we need to free any allocated memory and return unsuccessful
   if (NT_ERROR(Status))
   {
      if (pData != NULL)
		DigiFreeMem (pData) ;
	  Status = STATUS_UNSUCCESSFUL ;
   }
   else
      Status = STATUS_SUCCESS ;

XnPutIoctlExit:
   pIrp->IoStatus.Information = 0;
   if ( Status != STATUS_PENDING )
   {
	  DigiUnlockBuffers( pIrp );
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:   AsynIoctl.
 *
 * Purpose:
 *
 *    Perform the IOCTL operation.
 *
 *
 * Method:
 *
 *    o Perform the decode of the IOCTL and call the appropriate function.
 *    o If a DsPutMsgEx is necessary, then mark the IRP as Pending BEFORE
 *      calling DsPutMsgEx.  This is to allow for the case of the PutMsg
 *      completion routine being called in-line and the completion routine
 *      completing the IRP.  Thus when we return from DsPutMsgEx the IRP may
 *      have already completed.
 *
 *
 * Returns:
 *
 *    o Status of the IOCTL result is returned.
 *
 *
 * NOTE:
 *    o If a status other than STATUS_PENDING is returned, the calling
 *      routine will complete the IRP.
 *----------------------------------------------------------------------
 */
NTSTATUS
AsynIoctl(
   IN PPORT_EXT   pPortExt,      // Port specific information.
   IN PIRP        pIrp           // Write request to perform.
)
{
   NTSTATUS       Status;        // Called function return value.
   PIO_STACK_LOCATION      pIrpSp;        // Current IRP stack position.

   pIrpSp = IoGetCurrentIrpStackLocation(pIrp);

   // Unfortunately the "Parameters.DeviceIoControl" field is not a defined
   // struct type, thus we use a "#define" instead of a variable.
#define PDEVIO      (pIrpSp->Parameters.DeviceIoControl)

   DigiDump( DIGIIOCTL, ("ASYN!Ioctl: Code req=0x%x\n", PDEVIO.IoControlCode));


   switch (PDEVIO.IoControlCode)
   {
	  case IOCTL_DIGI_SDXB_XN_PUT:
		 Status =
			AsyncSdxbXnPut (pPortExt, pIrp, PDEVIO.InputBufferLength,
							(PXN_PUT)pIrp->AssociatedIrp.SystemBuffer);
         break ;

	  case IOCTL_DIGI_SDXB_XN_GET:
		 Status =
			AsyncSdxbXnGet (pPortExt, pIrp, PDEVIO.OutputBufferLength,
							(PXN_GET)pIrp->AssociatedIrp.SystemBuffer);
         break ;
      case IOCTL_FAST_RAS:
         // This IOCTL was invented by Microsoft to help Digi optimize the FEP5
         // based drivers running under RAS.  The S/DXB driver no longer needs 
         // this IOCTL, since FEP5's "DosMode" is obsolete.
         Status = STATUS_SUCCESS;
         break;

      case IOCTL_SERIAL_SET_BAUD_RATE:
         Status = 
            AsynIoSetBaudRate( pPortExt, pIrp, PDEVIO.InputBufferLength,
                               (PSERIAL_BAUD_RATE)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_BAUD_RATE:

         Status = 							 
            AsynIoGetBaudRate( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                               (PSERIAL_BAUD_RATE)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_SET_QUEUE_SIZE:
         DigiDump( DIGIIOCTL, ("ASYN!Ioctl: SET_QUEUE_SIZE Port=%d In=0x%x Out=0x%x\n", 
                  pPortExt->Port, 
                  ((PSERIAL_QUEUE_SIZE)pIrp->AssociatedIrp.SystemBuffer)->InSize,
                  ((PSERIAL_QUEUE_SIZE)pIrp->AssociatedIrp.SystemBuffer)->OutSize));
         if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
             sizeof(SERIAL_QUEUE_SIZE))
         {
            Status = STATUS_BUFFER_TOO_SMALL;
         }
         else
         {
            Status = STATUS_SUCCESS;
         }
         break;

      case IOCTL_SERIAL_SET_LINE_CONTROL:
         Status = 
            AsynIoSetLineControl( pPortExt, pIrp, PDEVIO.InputBufferLength,
                                  (PSERIAL_LINE_CONTROL)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_LINE_CONTROL:
         Status = AsynIoGetLineControl( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                        (PSERIAL_LINE_CONTROL)
                                          pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_SET_BREAK_ON:
         Status = AsynIoSetBreak( pPortExt, pIrp, TRUE);
         break;

      case IOCTL_SERIAL_SET_BREAK_OFF:
         Status = AsynIoSetBreak( pPortExt, pIrp, FALSE);
         break;
      
      case IOCTL_SERIAL_IMMEDIATE_CHAR:
         Status = AsynIoSendChar( pPortExt, pIrp, PDEVIO.InputBufferLength,
                                  pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_SET_TIMEOUTS:
         Status = AsynIoSetTimeouts( pPortExt, pIrp, PDEVIO.InputBufferLength,
                                     (PSERIAL_TIMEOUTS)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_TIMEOUTS:
         Status = AsynIoGetTimeouts( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                     (PSERIAL_TIMEOUTS)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_SET_DTR:
         Status = AsynIoDtr( pPortExt, pIrp, TRUE);
         break;

      case IOCTL_SERIAL_CLR_DTR:
         Status = AsynIoDtr( pPortExt, pIrp, FALSE);
         break;

      case IOCTL_SERIAL_RESET_DEVICE:
         Status = AsynIoResetDevice( pPortExt, pIrp);
         break;

      case IOCTL_SERIAL_SET_RTS:
         Status = AsynIoRts( pPortExt, pIrp, TRUE);
         break;

      case IOCTL_SERIAL_CLR_RTS:
         Status = AsynIoRts( pPortExt, pIrp, FALSE);
         break;

      case IOCTL_SERIAL_GET_DTRRTS:
         Status = AsynIoGetDtrRts( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                   (PULONG)pIrp->AssociatedIrp.SystemBuffer);
         break;
      
      case IOCTL_SERIAL_SET_XON:
         Status = AsynIoXonXoff( pPortExt, pIrp, TRUE);
         break;

      case IOCTL_SERIAL_SET_XOFF:
         Status = AsynIoXonXoff(pPortExt, pIrp, FALSE);
         break;

      case IOCTL_SERIAL_SET_WAIT_MASK:
         Status = AsynIoSetWaitMask( pPortExt, pIrp, PDEVIO.InputBufferLength,
                                     (PULONG)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_WAIT_MASK:
         Status = AsynIoGetWaitMask( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                     (PULONG)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_WAIT_ON_MASK:
         Status = AsynIoWaitOnMask( pPortExt, pIrp,
                                       PDEVIO.OutputBufferLength);
         break;

      case IOCTL_SERIAL_PURGE:
         Status = AsynIoPurge( pPortExt, pIrp, PDEVIO.InputBufferLength,
                               (PULONG)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_SET_CHARS:
         Status = AsynIoSetChars( pPortExt, pIrp, PDEVIO.InputBufferLength, 
                                  (PSERIAL_CHARS)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_CHARS:
         Status = AsynIoGetChars( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                  (PSERIAL_CHARS)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_SET_HANDFLOW:
         Status = AsynIoSetHandflow( pPortExt, pIrp, PDEVIO.InputBufferLength,
                                     (PSERIAL_HANDFLOW)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_HANDFLOW:
         Status = AsynIoGetHandflow( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                     (PSERIAL_HANDFLOW)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_MODEMSTATUS:
         Status = AsynIoGetModemStatus( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                        (PULONG)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_COMMSTATUS:
         Status = AsynIoGetCommStatus( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                       (PSERIAL_STATUS)
                                          pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_PROPERTIES:
         Status = AsynIoGetProperties( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                       (PSERIAL_COMMPROP)
                                          pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_LSRMST_INSERT:
         Status = AsynIoLsrmstInsert( pPortExt, pIrp, PDEVIO.InputBufferLength,
                                      (PUCHAR)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_CONFIG_SIZE:
         Status = AsynIoConfigSize( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                    (PULONG)pIrp->AssociatedIrp.SystemBuffer);
         break;

      case IOCTL_SERIAL_GET_STATS:
         Status = AsynIoGetStats( pPortExt, pIrp, PDEVIO.OutputBufferLength,
                                  (PASYN_SERIALPERF_STATS)
                                       pIrp->AssociatedIrp.SystemBuffer);
         break;
      case IOCTL_SERIAL_CLEAR_STATS:
         Status = AsynIoClearStats( pPortExt, pIrp);
         break;
         //
         // No idea what this request wants!
         //
      default:
         DigiDump( DIGIALL, ("*** ASYN!Ioctl: Unknown req=0x%x\n",
                   pIrpSp->Parameters.DeviceIoControl.IoControlCode ));
         Status = STATUS_INVALID_PARAMETER;
         pIrp->IoStatus.Information = 0;
         break;
   }
   DigiDump( DIGIIOCTL, ("ASYN!Ioctl: Exit ret=0x%x\n", Status));

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

#ifdef DBG
   if ( (Status != STATUS_SUCCESS) && (Status != STATUS_PENDING))
   {
      DigiDump( DIGIALL, ("*** ASYN!Ioctl: Exit ret=0x%x Ioctl=0x%x\n", 
                          Status, PDEVIO.IoControlCode));
   }
#endif

#undef PDEVIO

   return (Status);
}






