
/*
 ****************************************************************************
 * 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 :  trace.c
 *
 * Author      : P. Donovan
 *
 * Purpose     :
 *
 *      Trace debugging support for dgamode.exe application
 *
 *
 *----------------------------------------------------------------------
 */

#include "header.h"

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoCard
 *
 * Purpose:
 *
 *    IOCTL commands for the controller including Trace commands.
 *
 *
 *  Method:
 *    Complete the Trace command.
 *
 *
 *  Returns:
 *
 *    o STATUS_SUCCESS everything good.
 *    o STATUS_CANCELLED on shutdown.
 *    o STATUS_UNSUCCESSFUL on error.
 *    o STATUS_PENDING otherwise.
 *
 *
 *----------------------------------------------------------------------
 */
NTSTATUS AsynIoCard(
    IN PDEVICE_OBJECT   pDevice,       // Controller
    IN PIRP             pIrp           // Associated work order
)
{
   PCARD_EXT   pCardExt;               // Driver specific information.
   PPORT_EXT   pPortExt;               // Port specific information.
   KIRQL       CurrentIrql;            // Save prior irq level.
   NTSTATUS    Status;                 // Called function return value.
   SDXB_CMD    sdxbCmd;                // S/DXB command structure.
   USHORT      cmdLen;                 // Length of S/DXB command.
   PPUT_MSG_EV pPutEvent;
   NTSTATUS    putMsgStatus;
   PIO_STACK_LOCATION
               pIrpSp;                 // Current IRP stack position.

   /*
    * Get the driver specific data structures.
    */
   pCardExt = pDevice->DeviceExtension;

   pIrp->IoStatus.Information = 0;
   pIrpSp = IoGetCurrentIrpStackLocation( pIrp);
   Status = STATUS_SUCCESS;
   if (pCardExt->State == ShutdownState)
   {
      Status = STATUS_CANCELLED;
   }
   else
   {
      switch( pIrpSp->Parameters.DeviceIoControl.IoControlCode )
      {
#if 0
         case IOCTL_DIGI_GET_CONTROLLER_PERF_DATA:
         {
            if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONTROLLER_PERF_DATA))
            {
               Status = STATUS_BUFFER_TOO_SMALL;
            }
            else
            {
               memcpy(pIrp->AssociatedIrp.SystemBuffer, &ControllerExt->PerfData, sizeof(ControllerExt->PerfData));
               pIrp->IoStatus.Information = sizeof(ControllerExt->PerfData);
            }
            break;
         }
         case IOCTL_DIGI_GET_PORT_PERF_DATA:
         {
            struct PORT_DATA
            {
               ULONG ComNumber;
               PORT_PERF_DATA PerfData;
            } *PortData;

            if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(struct PORT_DATA)*ControllerExt->NumberOfPorts)
            {
               Status = STATUS_BUFFER_TOO_SMALL;
            }
            else
            {
               PDEVICE_OBJECT DeviceObject = ControllerExt->HeadDeviceObject;
               ULONG BytesReturned = 0;

               PortData = (struct PORT_DATA *)pIrp->AssociatedIrp.SystemBuffer;
               pIrp->IoStatus.Information = 0;

               while (DeviceObject)
               {
                  USHORT Tin, Tout, Tmax;
                  PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;

                  if (BytesReturned+sizeof(struct PORT_DATA) > pIrpSp->Parameters.DeviceIoControl.OutputBufferLength)
                  {
                     DigiDump( DIGIALL, ("ASYN!IoCard: Trying to overflow ioctl buffer. BAD.") );
                     Status = STATUS_BUFFER_TOO_SMALL;
                     break;
                  }

                  // Gather some data directly from the fep.

                  Tin = DeviceExt->Channel.Info.tin;
                  Tmax = DeviceExt->Channel.Info.tmax;

                  EnableWindow( ControllerExt, DeviceExt->Channel.InfoAddr.Window );
                  Tout = READ_CHANNEL_USHORT( DeviceExt, tout );
                  DisableWindow( ControllerExt );

                  DeviceExt->Perf.Data.SendBufferSize = Tmax;
                  DeviceExt->Perf.Data.BytesInSendBuffer = (Tin - Tout) & Tmax;

                  PortData->ComPort = DeviceExt->ComNumber;
                  PortData->PerfData = DeviceExt->Perf.Data;

                  BytesReturned += sizeof(struct PORT_DATA);
                  PortData++;

                  DeviceObject = DeviceExt->NextDeviceObject;
               }
               if (Status==STATUS_SUCCESS)
               {
                  pIrp->IoStatus.Information = BytesReturned;
               }
            }
            break;
         }
         case IOCTL_FEP_KME_READ:
         {
            PFEP_KME_READ pRead = pIrp->AssociatedIrp.SystemBuffer;

            if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof(FEP_KME_READ) ||
                pIrpSp->Parameters.DeviceIoControl.OutputBufferLength<pRead->Length)
            {
               Status = STATUS_INVALID_PARAMETER;
            }
            else
            {
               FEP_KME_READ Read = *pRead;
               PUCHAR pBuffer = (PUCHAR)pRead;

               UCHAR Window = (UCHAR)(Read.Address / ControllerExt->WindowSize);
               ULONG Offset = Read.Address - Window * ControllerExt->WindowSize;

               pIrp->IoStatus.Information = Read.Length;

               EnableWindow(ControllerExt, Window);
               if (Offset+Read.Length > ControllerExt->WindowSize)
               {
                  ULONG ThisRead = ControllerExt->WindowSize - Offset;

                  READ_ADAPTER_BUFFER(ControllerExt->VirtualAddress+Offset, 
                                      pBuffer, 
                                      ThisRead);
                  Read.Length -= ThisRead;
                  Offset = 0;
                  DisableWindow(ControllerExt);
                  EnableWindow(ControllerExt, (UCHAR)(Window+1));
               }
               READ_ADAPTER_BUFFER(ControllerExt->VirtualAddress+Offset, 
                                   pBuffer, 
                                   Read.Length);
               DisableWindow(ControllerExt);
            }
            break;
         }
         case IOCTL_FEP_KME_WRITE:
         {
            PFEP_KME_WRITE pWrite = pIrp->AssociatedIrp.SystemBuffer;
            if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof(FEP_KME_WRITE) ||
                pIrpSp->Parameters.DeviceIoControl.InputBufferLength<pWrite->Length+sizeof(FEP_KME_WRITE))
            {
               Status = STATUS_INVALID_PARAMETER;
            }
            else
            {
               PUCHAR pBuffer = (PUCHAR)&pWrite[1];

               UCHAR Window = (UCHAR)(pWrite->Address / ControllerExt->WindowSize);
               ULONG Offset = pWrite->Address - Window * ControllerExt->WindowSize;

               pIrp->IoStatus.Information = pWrite->Length;

               EnableWindow(ControllerExt, Window);
               if (Offset+pWrite->Length > ControllerExt->WindowSize)
               {
                  ULONG ThisWrite = ControllerExt->WindowSize - Offset;

                  WRITE_ADAPTER_BUFFER(ControllerExt->VirtualAddress+Offset, 
                                      pBuffer, 
                                      ThisWrite);
                  pWrite->Length -= ThisWrite;
                  Offset = 0;
                  DisableWindow(ControllerExt);
                  EnableWindow(ControllerExt, (UCHAR)(Window+1));
               }
               WRITE_ADAPTER_BUFFER(ControllerExt->VirtualAddress+Offset, 
                                   pBuffer, 
                                   pWrite->Length);
               DisableWindow(ControllerExt);
            }
            break;
         }
         case IOCTL_DIGI_REPORT_CONTROLLER_STATUS:
         {

            ReportControllerStatus(ControllerExt);
            break;
         }
#endif   // 0
         case IOCTL_DIGI_GET_CONTROLLER_DATA:
            if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CARD_STATUS))
            {
               Status = STATUS_BUFFER_TOO_SMALL;
            }
            else
            {
               PCARD_STATUS pCard = (PCARD_STATUS)pIrp->AssociatedIrp.SystemBuffer;
               PDEVICE_OBJECT pPort;
               PLIST_ENTRY    pList;

               pCard->DeviceExt = pCardExt;
               pCard->State = pCardExt->State;
               pCard->Type = 0;
               pCard->NumberOfPorts = pCardExt->NbrActivePorts;
               RtlFillMemory( pCard->OpenPorts, MAX_DMODE_PORTS, 0);
               for (pList = pCardExt->PortList.Flink; pList != &pCardExt->PortList; 
                  pList = pList->Flink)
               {
                  pPortExt = CONTAINING_RECORD( pList, PORT_EXT, PortEntry);
                  if (pPortExt->State != IdleState)
                  {
                     pCard->OpenPorts[pPortExt->Port] = (UCHAR)pPortExt->ComNumber;
                  }
               }
               pIrp->IoStatus.Information = sizeof(CARD_STATUS);
            }
            break;

         case IOCTL_DIGI_GET_PORT_STATUS:
            if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof(ULONG) ||
                pIrpSp->Parameters.DeviceIoControl.OutputBufferLength<sizeof(PORT_STATUS))
            {
               Status = STATUS_INVALID_PARAMETER;
            }
            else
            {
               ULONG ComPort = *(PULONG)pIrp->AssociatedIrp.SystemBuffer;
               PPORT_STATUS Port = (PPORT_STATUS)pIrp->AssociatedIrp.SystemBuffer;
               PPORT_EXT      pPortExt;
               PLIST_ENTRY    pList;

               for (pList = pCardExt->PortList.Flink; pList != &pCardExt->PortList; 
                    pList = pList->Flink)
               {
                  pPortExt = CONTAINING_RECORD( pList, PORT_EXT, PortEntry);
                  if (pPortExt->ComNumber == ComPort)
                  {
                     AsynGetDeviceStatus( pPortExt, Port);
                     Status = STATUS_SUCCESS;
                     pIrp->IoStatus.Information = sizeof(PORT_STATUS);
                     break;
                  }
               }
            }

            break;

         case IOCTL_DIGI_GET_PORT_TRACE:
            if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof(ULONG))
            {
               Status = STATUS_INVALID_PARAMETER;
            }
            else
            {
               ULONG ComPort = *(PULONG)pIrp->AssociatedIrp.SystemBuffer;
               PPORT_EXT      pPortExt;
               PLIST_ENTRY    pList;

               Status = STATUS_NO_SUCH_DEVICE;

               for (pList = pCardExt->PortList.Flink; pList != &pCardExt->PortList; 
                    pList = pList->Flink)
               {
                  pPortExt = CONTAINING_RECORD( pList, PORT_EXT, PortEntry);
                  if (pPortExt->ComNumber == ComPort)
                  {
                     KIRQL OldIrql;
                     pIrp->IoStatus.Status = STATUS_PENDING;
                     pIrp->IoStatus.Information = 0;
                     IoMarkIrpPending( pIrp);
                     KeAcquireSpinLock(&pPortExt->PortLock, &OldIrql);

                     InsertTailList( &pPortExt->Trace.Queue,
                                     &pIrp->Tail.Overlay.ListEntry);

                     if (!pPortExt->Trace.TimeoutPending)
                     {
                        pPortExt->Trace.TimeoutPending = TRUE;
                        KeSetTimer( &pPortExt->Trace.Timer,
                                    pPortExt->Trace.Timeout,
                                    &pPortExt->Trace.Dpc );
                     }
                     KeReleaseSpinLock( &pPortExt->PortLock, OldIrql);
                     return STATUS_PENDING;
                  }
               }
            }
            break;

         case IOCTL_DIGI_CONTROLLER_SPECIAL:
            if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof(ULONG))
            {
               Status = STATUS_INVALID_PARAMETER;
            }
            else
            {
               PDIGI_IOCTL DigiIoctl = (PDIGI_IOCTL)pIrp->AssociatedIrp.SystemBuffer;
   
               DigiDump( DIGIIOCTL, ("ASYN!IoCard: IOCTL_DIGI_SPECIAL:\n") );
   
               if( DigiIoctl )
               {
                  switch( DigiIoctl->dwCommand )
                  {
                     case DIGI_IOCTL_DBGOUT:
                     {
                        DigiDump( (DIGIALL), ("ASYN!IoCard: %s", DigiIoctl->Char) );
                        break;
                     }
   
                     case DIGI_IOCTL_TRACE:
                     {
                        PULONG NewTraceLevel = (PULONG)&DigiIoctl->Char[0];
   
                        DigiDump( (DIGIALL), ("ASYN!IoCard: Setting DigiDebugLevel = 0x%x\n",
                                              *NewTraceLevel ) );
                        gDebugMask = *NewTraceLevel;
                        break;
                     }
   
                     case DIGI_IOCTL_DBGBREAK:
                     {
                        DbgBreakPoint();
                        break;
                     }
                  }
               }
               Status = STATUS_SUCCESS;
            }
            break;

         default:
            DigiDump( DIGIALL, ("*** ASYN!IoCard: INVALID IOCTL PARAMETER 0x%x\n",
                      pIrpSp->Parameters.DeviceIoControl.IoControlCode) );
            Status = STATUS_INVALID_PARAMETER;
            break;
      }
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynGetDeviceStatus
 *
 * Purpose:
 *
 *    Get a snapshot of the Port device status.
 *
 *
 *  Method:
 *    Fill in the status data structure.
 *
 *
 *  Returns:
 *    VOID
 *
 *
 *----------------------------------------------------------------------
 */
VOID AsynGetDeviceStatus(
   PPORT_EXT      pPortExt, 
   PPORT_STATUS   pPortStatus
)
{
   KIRQL CurrentIrql;
   PLIST_ENTRY ListEntry;
   ULONG i;
   extern UCHAR Version[];

   RtlFillMemory(pPortStatus, sizeof(PORT_STATUS), 0);

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

   pPortStatus->Version = *(PULONG)Version;
   pPortStatus->ComPort = pPortExt->Port;
   pPortStatus->DeviceExt = pPortExt;
   pPortStatus->State =  pPortExt->State;
   pPortStatus->Busy =  pPortExt->PortBusy;

   pPortStatus->Mstat = pPortExt->SdxbValues.Mstat;
   pPortStatus->Mint = pPortExt->SdxbValues.Mint;

   pPortStatus->Ttime = (USHORT)pPortExt->SdxbValues.Ttime;
   pPortStatus->Tmax = (USHORT)pPortExt->SdxbValues.Tmax;

   pPortStatus->Estat = pPortExt->SdxbValues.Estat;
   pPortStatus->Eint= pPortExt->SdxbValues.Eint;
   pPortStatus->Mstat = pPortExt->SdxbValues.Mstat;
   pPortStatus->Mint = pPortExt->SdxbValues.Mint;

   pPortStatus->Iflag = pPortExt->SdxbValues.Iflags;
   pPortStatus->Oflag = pPortExt->SdxbValues.Oflags;
   pPortStatus->Cflag = pPortExt->SdxbValues.Cflags;
   pPortStatus->Xflag = pPortExt->SdxbValues.Xflags;

   pPortStatus->Xon = pPortExt->SpecialChars.XonChar;
   pPortStatus->Xoff = pPortExt->SpecialChars.XoffChar;
   pPortStatus->Mflow = pPortExt->SdxbValues.Mflow;

   pPortStatus->SyncOutNum = pPortExt->SyncOutSequence;
   pPortStatus->SyncInNum = pPortExt->SyncInSequence;
   pPortStatus->Speed = pPortExt->SdxbValues.Speed;

   pPortStatus->OpenStateMask = (USHORT)pPortExt->OpenStateMask;

   pPortStatus->Ltime = pPortExt->SdxbValues.Ltime;
   pPortStatus->Norun = (USHORT)pPortExt->SdxbValues.Norun;
   pPortStatus->Noflow = (USHORT)pPortExt->SdxbValues.Noflow;
   pPortStatus->Nframe = (USHORT)pPortExt->SdxbValues.Nframe;
   pPortStatus->Nparity =(USHORT) pPortExt->SdxbValues.Nparity;
   pPortStatus->Nbreak = (USHORT)pPortExt->SdxbValues.Nbreak;
   pPortStatus->PortError.Total = (USHORT)pPortExt->PortError.Total;
   pPortStatus->PortError.LineNumber = (USHORT)pPortExt->PortError.LineNumber;
   RtlCopyMemory( pPortStatus->PortError.FileName, 
                  pPortExt->PortError.FileName, PORT_ERROR_FILE_SIZE);

   pPortStatus->Tout = pPortExt->SdxbValues.Tout;
   pPortStatus->TxBytesPending = pPortExt->TxBytesPending;

   pPortStatus->TotalRxBytesBuffered = pPortExt->TotalRxBytesBuffered;
   pPortStatus->TotalWriteBytes = pPortExt->TotalWriteBytes;
   pPortStatus->TotalReadBytes = pPortExt->TotalReadBytes;
   pPortStatus->RxBufCount = pPortExt->RxBufCount;

   for (ListEntry = pPortExt->DispatchQ.Flink, i=0;
        ListEntry != &pPortExt->DispatchQ && i<MAX_IRPS;
        ListEntry = ListEntry->Flink, i++)
   {
      PIRP Irp = pPortStatus->DispatchQ.Irp[i] = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
      pPortStatus->DispatchQ.IrpStatus[i] = Irp->IoStatus.Information;
   }
   for (ListEntry = pPortExt->ReadQueue.Flink, i=0;
        ListEntry != &pPortExt->ReadQueue && i<MAX_IRPS;
        ListEntry = ListEntry->Flink, i++)
   {
      PIRP Irp = pPortStatus->Read.Irp[i] = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
      pPortStatus->Read.IrpStatus[i] = Irp->IoStatus.Information;
   }
   pPortStatus->Read.Timeout.Interval = pPortExt->TimeoutValues.ReadIntervalTimeout;
   pPortStatus->Read.Timeout.Total = pPortExt->TimeoutValues.ReadTotalTimeoutConstant;
   pPortStatus->Read.Timeout.Multiplier = pPortExt->TimeoutValues.ReadTotalTimeoutMultiplier;

   pPortStatus->Read.Timeout.Irp = pPortExt->TimedReadIrp;

   for (ListEntry = pPortExt->WriteQueue.Flink, i=0;
        ListEntry != &pPortExt->WriteQueue && i<MAX_IRPS;
        ListEntry = ListEntry->Flink, i++)
   {
      PIRP Irp = pPortStatus->Write.Irp[i] = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
      pPortStatus->Write.IrpStatus[i] = Irp->IoStatus.Information;
   }

   for (ListEntry = pPortExt->CompleteIrpQ.Flink, i=0;
        ListEntry != &pPortExt->CompleteIrpQ && i<MAX_IRPS;
        ListEntry = ListEntry->Flink, i++)
   {
      PIRP Irp = pPortStatus->CompleteOutboundIrp.Irp[i] = 
            CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
      pPortStatus->CompleteOutboundIrp.IrpStatus[i] = Irp->IoStatus.Information;
   }

   pPortStatus->Write.Timeout.Total = 
                  pPortExt->TimeoutValues.WriteTotalTimeoutConstant;
   pPortStatus->Write.Timeout.Multiplier = 
                  pPortExt->TimeoutValues.WriteTotalTimeoutMultiplier;

   if (pPortExt->TimedWritePutEvent)
      pPortStatus->Write.Timeout.Irp = pPortExt->TimedWritePutEvent->Irp;
   else
      pPortStatus->Write.Timeout.Irp = NULL;
   pPortStatus->Wait.History = pPortExt->HistoryMask;
   pPortStatus->Wait.Mask = pPortExt->WaitMask;
   pPortStatus->Wait.Irp = pPortExt->WaitMaskIrp;

   for (ListEntry = pPortExt->RxQ.Flink, i=0;
        ListEntry != &pPortExt->RxQ && i<MAX_DMODE_RX_BUFFER;
        ListEntry = ListEntry->Flink, i++)
   {
      PRX_BUF     pRxBuf = (PRX_BUF)ListEntry;
      pPortStatus->RxQ.Ptr[i] = pRxBuf;
      pPortStatus->RxQ.Size[i] = (USHORT)pRxBuf->Size;
      pPortStatus->RxQ.Start[i] = (USHORT)pRxBuf->Start;
      pPortStatus->RxQ.Len[i] = (USHORT)pRxBuf->Len;
   }

   for (ListEntry = pPortExt->PutQ.Flink, i=0;
        ListEntry != &pPortExt->PutQ && i<MAX_DMODE_PUT_EVENT;
        ListEntry = ListEntry->Flink, i++)
   {
      PPUT_MSG_EV pPutEvent = (PPUT_MSG_EV)ListEntry;
      pPortStatus->PutQ.Put[i] = pPutEvent;
      pPortStatus->PutQ.Command[i] = (USHORT)pPutEvent->Command;
      pPortStatus->PutQ.Irp[i] = pPutEvent->Irp;
      pPortStatus->PutQ.Reason[i] = (USHORT)pPutEvent->Reason;
      pPortStatus->PutQ.State[i] = (USHORT)pPutEvent->State;
   }

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

/*
 *----------------------------------------------------------------------
 * Function:   AsynTraceTimeout 
 *
 * Purpose:
 *
 *    We use this function to complete a pending TRACE ioctl every 500ms, if 
 *    one is pending.
 *
 *
 *  Method:
 *    Complete any Irps on the trace queue and schedule this DPC if we
 *    are still tracing.
 *
 *
 *  Returns:
 *    VOID
 *
 *----------------------------------------------------------------------
 */
VOID
AsynTraceTimeout( 
   IN PKDPC       Dpc,
   IN PVOID       DeferredContext,
   IN PVOID       SystemContext1,
   IN PVOID       SystemContext2 
)
{
   PPORT_EXT      pPortExt = (PPORT_EXT)DeferredContext;
   KIRQL          OldIrql;
   PIRP           pIrp = NULL;

   KeAcquireSpinLock( &pPortExt->PortLock, &OldIrql);
   if (!IsListEmpty( &pPortExt->Trace.Queue))
   {
      PLIST_ENTRY ListEntry = RemoveHeadList( &pPortExt->Trace.Queue);
      pIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
   }
   if (!IsListEmpty( &pPortExt->Trace.Queue))
   {
      KeSetTimer( &pPortExt->Trace.Timer,
                  pPortExt->Trace.Timeout,
                  &pPortExt->Trace.Dpc );
   }
   else
   {
      pPortExt->Trace.TimeoutPending = FALSE;
   }
   KeReleaseSpinLock( &pPortExt->PortLock, OldIrql);

   if (pIrp)
   {
      pIrp->IoStatus.Status = STATUS_SUCCESS;
      IoCompleteRequest( pIrp, IO_NO_INCREMENT );
   }
}

void AsynTraceWriteIrp(
   PPORT_EXT      pPortExt, 
   PIRP           pIrp
)
{
   PCARD_EXT      pCardExt = pPortExt->CardExt;
   KIRQL          OldIrql;
   PUCHAR         pIrpBuffer;
   PIRP           pTraceIrp = NULL;
   PIRP           pIrpToComplete = NULL;
   PLIST_ENTRY    ListEntry;
   ULONG          IrpSpace;
   PIO_STACK_LOCATION pIrpSp;

//   KeAcquireSpinLock(&pPortExt->Trace.Lock, &OldIrql);

   if (!IsListEmpty(&pPortExt->Trace.Queue))
   {
      ListEntry = RemoveHeadList( &pPortExt->Trace.Queue);
      pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
      pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
      pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer + pTraceIrp->IoStatus.Information;
      IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength -
                     pTraceIrp->IoStatus.Information;

      if (pTraceIrp->IoStatus.Information && 
          IrpSpace < pIrp->IoStatus.Information+sizeof(WRITE_TRACE)+sizeof(TRACE_HEADER))
      {
         pIrpToComplete = pTraceIrp;
         pTraceIrp = NULL;

         // There's not enough room in this IRP.  Get the next one.

         if (!IsListEmpty(&pPortExt->Trace.Queue))
         {
            ListEntry = RemoveHeadList( &pPortExt->Trace.Queue);
            pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
            pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
            pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer;
            IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
         }
      }

      if (pTraceIrp)
      {
         WRITE_TRACE *pWrite;
         TRACE_HEADER *pHdr;
         ULONG BytesToCopy;

         pHdr = (TRACE_HEADER*)pIrpBuffer;
         pIrpBuffer += sizeof(TRACE_HEADER);
         IrpSpace -= sizeof(TRACE_HEADER);
         pWrite = (WRITE_TRACE*)pIrpBuffer;
         pIrpBuffer += sizeof(WRITE_TRACE);
         IrpSpace -= sizeof(WRITE_TRACE);

         pHdr->FunctionType = TRACETYPE_WRITE;
         KeQuerySystemTime(&pHdr->Time);

         pHdr->ModemStatus = 0;
         if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
            pHdr->ModemStatus |= 0x02;
         if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
            pHdr->ModemStatus |= 0x10;
         if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
            pHdr->ModemStatus |= 0x01;
         if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
            pHdr->ModemStatus |= 0x20;
         if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
            pHdr->ModemStatus |= 0x80;
         if (pPortExt->SdxbValues.Mstat & SDXB_RI)
            pHdr->ModemStatus |= 0x40;

         pHdr->Length = pIrp->IoStatus.Information;
         pWrite->Irp = (ULONG)pIrp;
         pWrite->Status = pIrp->IoStatus.Status;

         BytesToCopy = MIN( pIrp->IoStatus.Information, IrpSpace);
         RtlCopyMemory( pIrpBuffer, pIrp->AssociatedIrp.SystemBuffer, BytesToCopy);

         pTraceIrp->IoStatus.Information = pTraceIrp->IoStatus.Information + 
                                          sizeof(TRACE_HEADER) +
                                          sizeof(WRITE_TRACE) +
                                          BytesToCopy;

         InsertHeadList( &pPortExt->Trace.Queue, &pTraceIrp->Tail.Overlay.ListEntry);
      }
   }

//   KeReleaseSpinLock(&pPortExt->Trace.Lock, OldIrql);

   if (pIrpToComplete)
   {
      pIrpToComplete->IoStatus.Status = STATUS_SUCCESS;
      IoCompleteRequest( pIrpToComplete, IO_NO_INCREMENT);
   }
}

void AsynTraceReadIrp(
   PPORT_EXT      pPortExt, 
   PIRP           pIrp)
{
   PCARD_EXT      pCardExt = pPortExt->CardExt;
   KIRQL          OldIrql;
   PUCHAR         pIrpBuffer;
   PIRP           pTraceIrp = NULL, pIrpToComplete = NULL;
   PLIST_ENTRY    ListEntry;
   ULONG          IrpSpace;
   PIO_STACK_LOCATION pIrpSp;

//   KeAcquireSpinLock(&pPortExt->Trace.Lock, &OldIrql);

   if (!IsListEmpty(&pPortExt->Trace.Queue))
   {
      ListEntry = RemoveHeadList( &pPortExt->Trace.Queue);
      pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
      pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
      pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer + pTraceIrp->IoStatus.Information;
      IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength -
                 pTraceIrp->IoStatus.Information;

      if (pTraceIrp->IoStatus.Information && 
          IrpSpace<pIrp->IoStatus.Information+sizeof(READ_TRACE)+sizeof(TRACE_HEADER))
      {
         pIrpToComplete = pTraceIrp;
         pTraceIrp = NULL;

         // There's not enough room in this IRP.  Get the next one.

         if (!IsListEmpty(&pPortExt->Trace.Queue))
         {
            ListEntry = RemoveHeadList(&pPortExt->Trace.Queue);
            pTraceIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
            pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
            pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer;
            IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
         }
      }

      if (pTraceIrp)
      {
         READ_TRACE     *pRead;
         TRACE_HEADER   *pHdr;
         ULONG          BytesToCopy;

         pHdr = (TRACE_HEADER*)pIrpBuffer;
         pIrpBuffer += sizeof(TRACE_HEADER);
         IrpSpace -= sizeof(TRACE_HEADER);
         pRead = (READ_TRACE*)pIrpBuffer;
         pIrpBuffer += sizeof(READ_TRACE);
         IrpSpace -= sizeof(READ_TRACE);

         pHdr->FunctionType = TRACETYPE_READ;
         KeQuerySystemTime( &pHdr->Time);

         pHdr->ModemStatus = 0;
         if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
            pHdr->ModemStatus |= 0x02;
         if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
            pHdr->ModemStatus |= 0x10;
         if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
            pHdr->ModemStatus |= 0x01;
         if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
            pHdr->ModemStatus |= 0x20;
         if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
            pHdr->ModemStatus |= 0x80;
         if (pPortExt->SdxbValues.Mstat & SDXB_RI)
            pHdr->ModemStatus |= 0x40;

         pHdr->Length = pIrp->IoStatus.Information;
         pRead->Irp = (ULONG)pIrp;
         pRead->Status = pIrp->IoStatus.Status;

         BytesToCopy = MIN(pIrp->IoStatus.Information, IrpSpace);
         RtlCopyMemory( pIrpBuffer, pIrp->AssociatedIrp.SystemBuffer, BytesToCopy);

         pTraceIrp->IoStatus.Information = pTraceIrp->IoStatus.Information + 
                                          sizeof(TRACE_HEADER) +
                                          sizeof(READ_TRACE) +
                                          BytesToCopy;

         InsertHeadList( &pPortExt->Trace.Queue, &pTraceIrp->Tail.Overlay.ListEntry);
      }
   }

//   KeReleaseSpinLock(&pPortExt->Trace.Lock, OldIrql);

   if (pIrpToComplete)
   {
      pIrpToComplete->IoStatus.Status = STATUS_SUCCESS;
      IoCompleteRequest(pIrpToComplete, IO_NO_INCREMENT);
   }
}

void AsynTraceIoctlIrp(
   PPORT_EXT   pPortExt, PIRP pIrp, BOOLEAN In, BOOLEAN Out)
{
   PCARD_EXT   pCardExt = pPortExt->CardExt;
   KIRQL       OldIrql;
   PUCHAR      pIrpBuffer;
   PIRP        pTraceIrp=NULL, pIrpToComplete = NULL;
   PLIST_ENTRY ListEntry;
   ULONG       IrpSpace, SpaceNeeded;
   PIO_STACK_LOCATION pIrpSp, pTraceIrpSp;

   ASSERT(!(In && Out));

//   KeAcquireSpinLock(&pPortExt->Trace.Lock, &OldIrql);

   if (!IsListEmpty(&pPortExt->Trace.Queue))
   {
      ListEntry = RemoveHeadList(&pPortExt->Trace.Queue);
      pTraceIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
      pTraceIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
      pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer + 
                     pTraceIrp->IoStatus.Information;
      IrpSpace = pTraceIrpSp->Parameters.DeviceIoControl.OutputBufferLength -
                 pTraceIrp->IoStatus.Information;

      SpaceNeeded = sizeof(IOCTL_TRACE)+sizeof(TRACE_HEADER);

      pIrpSp = IoGetCurrentIrpStackLocation( pIrp );

      if (In)
      {
         SpaceNeeded += pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
      }
      else if (Out)
      {
         SpaceNeeded += pIrp->IoStatus.Information;
      }

      if (pTraceIrp->IoStatus.Information && IrpSpace<SpaceNeeded)
      {
         pIrpToComplete = pTraceIrp;
         pTraceIrp = NULL;

         // There's not enough room in this IRP.  Get the next one.

         if (!IsListEmpty( &pPortExt->Trace.Queue))
         {
            ListEntry = RemoveHeadList( &pPortExt->Trace.Queue);
            pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
            pTraceIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
            pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer;
            IrpSpace = pTraceIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
         }
      }

      if (pTraceIrp)
      {
         IOCTL_TRACE    *pIoctl;
         TRACE_HEADER   *pHdr;
         ULONG          BytesToCopy;

         pHdr = (TRACE_HEADER*)pIrpBuffer;
         pIrpBuffer += sizeof(TRACE_HEADER);
         IrpSpace -= sizeof(TRACE_HEADER);
         pIoctl = (IOCTL_TRACE*)pIrpBuffer;
         pIrpBuffer += sizeof(IOCTL_TRACE);
         IrpSpace -= sizeof(IOCTL_TRACE);

         pHdr->FunctionType = TRACETYPE_IOCTL;
         KeQuerySystemTime( &pHdr->Time);

         pHdr->ModemStatus = 0;
         if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
            pHdr->ModemStatus |= 0x02;
         if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
            pHdr->ModemStatus |= 0x10;
         if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
            pHdr->ModemStatus |= 0x01;
         if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
            pHdr->ModemStatus |= 0x20;
         if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
            pHdr->ModemStatus |= 0x80;
         if (pPortExt->SdxbValues.Mstat & SDXB_RI)
            pHdr->ModemStatus |= 0x40;

         if (In)
         {
            pHdr->Length = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
         }
         else if (Out)
         {
            pHdr->Length = pIrp->IoStatus.Information;
         }
   
         pIoctl->Irp = (ULONG)pIrp;
         pIoctl->Status = pIrp->IoStatus.Status;
         pIoctl->IoctlType = pIrpSp->Parameters.DeviceIoControl.IoControlCode;

         BytesToCopy = MIN(pHdr->Length, IrpSpace);
         RtlCopyMemory( pIrpBuffer, pIrp->AssociatedIrp.SystemBuffer, BytesToCopy);

         pTraceIrp->IoStatus.Information = pTraceIrp->IoStatus.Information + 
                                             sizeof(TRACE_HEADER) +
                                             sizeof(IOCTL_TRACE) +
                                             BytesToCopy;

         InsertHeadList( &pPortExt->Trace.Queue, &pTraceIrp->Tail.Overlay.ListEntry);
      }
   }

//   KeReleaseSpinLock(&pPortExt->Trace.Lock, OldIrql);

   if (pIrpToComplete)
   {
      pIrpToComplete->IoStatus.Status = STATUS_SUCCESS;
      IoCompleteRequest( pIrpToComplete, IO_NO_INCREMENT);
   }
}

void AsynTraceModemStatus(
   PPORT_EXT   pPortExt)
{
   PCARD_EXT   pCardExt = pPortExt->CardExt;
   KIRQL       OldIrql;
   PUCHAR      pIrpBuffer;
   PIRP        pTraceIrp=NULL, pIrpToComplete = NULL;
   PLIST_ENTRY ListEntry;
   ULONG       IrpSpace, SpaceNeeded;
   PIO_STACK_LOCATION pTraceIrpSp;

//   KeAcquireSpinLock(&pPortExt->Trace.Lock, &OldIrql);

   if (!IsListEmpty(&pPortExt->Trace.Queue))
   {
      ListEntry = RemoveHeadList(&pPortExt->Trace.Queue);
      pTraceIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
      pTraceIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
      pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer + 
                     pTraceIrp->IoStatus.Information;
      IrpSpace = pTraceIrpSp->Parameters.DeviceIoControl.OutputBufferLength -
                 pTraceIrp->IoStatus.Information;

      SpaceNeeded = sizeof(TRACE_HEADER);

      if (pTraceIrp->IoStatus.Information && IrpSpace<SpaceNeeded)
      {
         pIrpToComplete = pTraceIrp;
         pTraceIrp = NULL;

         // There's not enough room in this IRP.  Get the next one.

         if (!IsListEmpty( &pPortExt->Trace.Queue))
         {
            ListEntry = RemoveHeadList( &pPortExt->Trace.Queue);
            pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
            pTraceIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
            pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer;
            IrpSpace = pTraceIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
         }
      }

      if (pTraceIrp)
      {
         TRACE_HEADER   *pHdr;

         pHdr = (TRACE_HEADER*)pIrpBuffer;
         pIrpBuffer += sizeof(TRACE_HEADER);
         IrpSpace -= sizeof(TRACE_HEADER);

         pHdr->FunctionType = TRACETYPE_MODEM_STATUS;
         KeQuerySystemTime( &pHdr->Time);

         pHdr->ModemStatus = 0;
         if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
            pHdr->ModemStatus |= 0x02;
         if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
            pHdr->ModemStatus |= 0x10;
         if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
            pHdr->ModemStatus |= 0x01;
         if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
            pHdr->ModemStatus |= 0x20;
         if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
            pHdr->ModemStatus |= 0x80;
         if (pPortExt->SdxbValues.Mstat & SDXB_RI)
            pHdr->ModemStatus |= 0x40;

         pHdr->Length = 0;

         pTraceIrp->IoStatus.Information = pTraceIrp->IoStatus.Information + 
                                             sizeof(TRACE_HEADER);

         InsertHeadList( &pPortExt->Trace.Queue, &pTraceIrp->Tail.Overlay.ListEntry);
      }
   }

//   KeReleaseSpinLock(&pPortExt->Trace.Lock, OldIrql);

   if (pIrpToComplete)
   {
      pIrpToComplete->IoStatus.Status = STATUS_SUCCESS;
      IoCompleteRequest( pIrpToComplete, IO_NO_INCREMENT);
   }
}

