
/*
 * dxbloop.c
 *
 * Temporary file containting functions which have yet to be implemented.
 * As each function gets implemented, remove form this file and put in
 * appropriate file.
 *
 */


/*
 *----------------------------------------------------------------------
 *  System Include Files
 *----------------------------------------------------------------------
 */
#include <ntddk.h>
#include <ntddser.h>                    // Serial port definitions.

/*
 *----------------------------------------------------------------------
 *  Local Include Files
 *----------------------------------------------------------------------
 */
#include "ntdrv.h"
#include "digifile.h"
#include "config.h"
#include "sdxb.h"                      // S/DXB protocol defines and structures.
#include "asyn.h"                      // Driver control structures.
#include "dxbloop.h"                   // DStreams loopback simulation stuff.

#include "protofun.h"


DS_DEV Card;

NTSTATUS DsOpenDevice(
   IN PUNICODE_STRING   DeviceName,
   OUT PHANDLE          DeviceHandle
   )
{
   PDS_DEV     pCard;
   NTSTATUS    Status;
   NTSTATUS    LogMessage;          // Error log message

   pCard = &Card;
   *DeviceHandle = (HANDLE)NULL;
   KeInitializeSpinLock( &pCard->Lock);

   InitializeListHead( &pCard->StreamsQ);
#if 0
   InitializeListHead( &pCard->DataPool.List);
   Status = AsynAllocPool( &pCard->DataPool, DS_DEF_DATA_COUNT, sizeof(RX_BUF));
   if (! NT_SUCCESS(Status))
   {
      DigiDump( DIGIINIT, ("DS: DATA Pool Alloc FAILED!\n"));
      LogMessage = ASYN_NEED_MEMORY;
      return STATUS_UNSUCCESSFUL;
   }
#endif
   InitializeListHead( &pCard->PutPool.List);
   Status = AsynAllocPool( &pCard->PutPool, DS_DEF_PUT_COUNT, sizeof(PEND_PUT));
   if (! NT_SUCCESS(Status))
   {
      DigiDump( DIGIINIT, ("DS: Put Pool Alloc FAILED!\n"));
      LogMessage = ASYN_NEED_MEMORY;
      return STATUS_UNSUCCESSFUL;
   }

   InitializeListHead( &pCard->GetPool.List);
   Status = AsynAllocPool( &pCard->GetPool, DS_DEF_GET_COUNT, sizeof(PEND_GET));
   if (! NT_SUCCESS(Status))
   {
      DigiDump( DIGIINIT, ("DS: Get Pool Alloc FAILED!\n"));
      LogMessage = ASYN_NEED_MEMORY;
      return STATUS_UNSUCCESSFUL;
   }

   //
   // Start the Poll which will process traffic.
   //
   DigiDump( DIGIINIT, ("DS: About to create Poll \n"));
   pCard->InUse = 0;
   KeInitializeDpc( &pCard->PollFunction, DsStreamsPoll, pCard);
   KeInitializeTimer( &pCard->Poll);
   pCard->PollCycle = RtlConvertLongToLargeInteger( 10 * MILLISECOND * -1);
   KeSetTimer( &pCard->Poll, pCard->PollCycle, &pCard->PollFunction);

   DigiDump( DIGIINIT, ("DS: Poll is created\n"));

   *DeviceHandle = (HANDLE)pCard;
   return STATUS_SUCCESS;
}

NTSTATUS DsCloseDevice(
   IN HANDLE            DeviceHandle
   )
{
   THREAD_INFO *ThreadInfo;   // Worker thread data.
   PDS_DEV      pCard;

   pCard = &Card;

   DigiDump( DIGIUNLOAD | DIGIDXB, ("DS: About to delete DS Poll\n"));
   DigiDump( DIGIUNLOAD | DIGIDXB, ("DS: InUse=0x%x\n", pCard->InUse));
   KeCancelTimer( &pCard->Poll);
   KeRemoveQueueDpc( &pCard->PollFunction);
   DigiDump( DIGIUNLOAD | DIGIDXB, ("DS: After Removal InUse=0x%x\n", pCard->InUse));

#if 0
   AsynFreePool( &pCard->DataPool);
#endif
   AsynFreePool( &pCard->PutPool);
   AsynFreePool( &pCard->GetPool);

   pCard = NULL;
   return STATUS_SUCCESS;
}

/*
 *----------------------------------------------------------------------
 * Function:  DsStreamsPoll
 *
 * Purpose:
 *
 *    Poll thread which processes S/DXB traffic for all Open Streams.
 *
 *
 * Method:
 *
 *    o For each stream open do the following :
 *    o If there is a current GetMsg complete it if :
 *       1. The top of the Put Queue has an S/DXB response
 *       2. The top of the Put Queue has Data and the transmission time is
 *          expired.
 *    o Call the completion routines for PUTs, if the count of Data buffers
 *      queued is < DS_MAX_DATA_BUF_QUEUED.
 *
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 *----------------------------------------------------------------------
 */
VOID
DsStreamsPoll(
   IN PKDPC      pDpc,          // DPC object for PsExt PortServer.
   IN PDS_DEV    pCard,         // Async device instance extension
   IN PVOID      SystemArg1,    // System argument.
   IN PVOID      SystemArg2     // System argument.
)
{
   PDS_STREAM  pPort;                  // Stream
   PDS_STREAM  pPortNext;              // Stream
   KIRQL       CardIrql;               // Prior Adapter lock irq level.
   KIRQL       PortIrql;               // Prior Port lock irq level.
   PPEND_PUT   pPut, pPutFree;
   PPEND_GET   pGet, pGetFree;
   static ULONG PollCount = 0;

   UNREFERENCED_PARAMETER( pDpc);
   UNREFERENCED_PARAMETER( SystemArg1);
   UNREFERENCED_PARAMETER( SystemArg2);

   if (!(PollCount % 10000))
   {
      DigiDump( DIGIDXB, ("DS: Poll loop =0x%x\n", PollCount));
   }
   PollCount++;

   KeAcquireSpinLock( &pCard->Lock, &CardIrql);
   ASSERT(!pCard->InUse);
   pCard->InUse++;

   for ( pPort = (PDS_STREAM)pCard->StreamsQ.Flink;
        pPort != (PDS_STREAM)&pCard->StreamsQ; pPort = pPortNext)
   {
      KeReleaseSpinLock( &pCard->Lock, CardIrql);
      pPortNext = (PDS_STREAM)pPort->ListEntry.Flink;

      KeAcquireSpinLock( &pPort->Lock, &PortIrql);
      //
      // Can we satisfy GetMsg for this port?
      //
      if (!IsListEmpty(&pPort->GetQ))
      {
         if (!IsListEmpty(&pPort->PutQ))
         {
            pPut = (PPEND_PUT)pPort->PutQ.Flink;
            pGet = (PPEND_GET)pPort->GetQ.Flink;
            pPutFree = NULL;
            pGetFree = NULL;
            if (pPut->Type == SdxbType)
            {
               DigiDump( DIGIDXB, ("DS: Poll GET Port=%d S/DXB Completion; Count=0x%x\n",
                                    pPort->Port, pPort->BufferCount));
               pGet->CompletionRoutine( pGet->Hstream, pGet->Context,
                                        pPut->Sdxb.Buffer,
                                        pPut->Sdxb.Length,
                                        pGet->DataBuffer, 0,
                                        STATUS_SUCCESS);
               pPutFree = pPut;
               pGetFree = pGet;
            }
            else if (pPut->Type == DataType)
            {
               LARGE_INTEGER  CurrentTime;

               KeQuerySystemTime( &CurrentTime);
               if (pPort->DataEndTime.QuadPart <= CurrentTime.QuadPart)
               {
                  DigiDump( DIGIDXB, ("DS: Poll GET Port=%d Data Completion; Buf=0x%x Count=0x%x\n",
                                       pPort->Port, pGet->DataBuffer,
                                       pPort->BufferCount));
                  ASSERT(pPort->BufferCount);
                  if (pGet->DataBuffer)
                  {
                     RtlCopyMemory( pGet->DataBuffer, pPut->Data.Data,
                                    pPut->Data.Len);
                     pGet->CompletionRoutine( pGet->Hstream, pGet->Context,
                                              NULL, 0,
                                              pGet->DataBuffer,
                                              pPut->Data.Len,
                                              STATUS_SUCCESS);
                     pGetFree = pGet;
                  }
                  pPort->DataEndTime.LowPart = (ULONG)-1;
                  pPort->DataEndTime.HighPart = 0x0fffffff;
                  pPutFree = pPut;
               }
            }
            if (pPutFree)
            {
               DigiDump( DIGIDXB, ("DS: Poll Port=%d Freeing PUT=0x%x\n",
                                    pPort->Port, pPutFree));
               RemoveEntryList( &pPutFree->ListEntry);
               KeAcquireSpinLock( &pCard->Lock, &CardIrql);
#if 0
               if (pPutFree->Type == DataType)
               {
                  DigiDump( DIGIDXB, ("DS: Poll Port=%d Freeing DATA=0x%x\n",
                                       pPort->Port, pPutFree->Data));
                  RemoveEntryList( &pPutFree->Data->ListEntry);
                  AsynFreeEntry( &pPort->Device->DataPool,
                     &pPutFree->Data->ListEntry);
               }
#endif
               AsynFreeEntry( &pCard->PutPool, &pPutFree->ListEntry);
               pPort->BufferCount--;
               KeReleaseSpinLock( &pCard->Lock, CardIrql);
            }
            if (pGetFree)
            {
               DigiDump( DIGIDXB, ("DS: Poll Port=%d Freeing GET=0x%x\n",
                                    pPort->Port, pGetFree));
               RemoveEntryList( &pGetFree->ListEntry);
               KeAcquireSpinLock( &pCard->Lock, &CardIrql);
               AsynFreeEntry( &pCard->GetPool, &pGetFree->ListEntry);
               KeReleaseSpinLock( &pCard->Lock, CardIrql);
            }
            //
            // When things are busy (>DS_MAX_BUF_QUEUED) the Put Completion
            // routines will not get called when DsPutMsg() is called.
            // Instead they are pending, waiting for the activity to decrease.
            //
            if (!IsListEmpty(&pPort->PutQ))
            {
               PPEND_PUT      pPutNext;

               pPut = (PPEND_PUT)pPort->PutQ.Flink;
               for (pPut = (PPEND_PUT)pPort->PutQ.Flink;
                  pPut != (PPEND_PUT)&pPort->PutQ; pPut = pPutNext)
               {
                  pPutNext = (PPEND_PUT)pPut->ListEntry.Flink;

                  if (pPort->BufferCount >= DS_MAX_DATA_BUF_QUEUED)
                  {
                     break;
                  }

                  if (pPut->Type == PendingType)
                  {
                     BOOLEAN     bQueuePut;

                     bQueuePut = DsProcessPut( pPort, pPut);
                     DigiDump( DIGIDXB, ("DS: Poll Port=%d PUT Completion; Count=0x%x\n",
                                          pPort->Port, pPort->BufferCount));
                     pPut->Pending.CompletionRoutine( pPut->Pending.hStream,
                                                      pPut->Pending.Context,
                                                      pPut->Pending.pControlBuffer,
                                                      pPut->Pending.ControlLength,
                                                      pPut->Pending.pDataBuffer,
                                                      pPut->Pending.DataLength,
                                                      STATUS_SUCCESS);
                     if (!bQueuePut)
                     {
                        //
                        // The PutMsg does not need a response, so no need to
                        // keep the Put Descriptor hanging around.
                        //
                        DigiDump( DIGIDXB, ("DS: Poll Port=%d Freeing PUT=0x%x\n",
                                             pPort->Port, pPut));
                        RemoveEntryList( &pPut->ListEntry);
                        KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
                        AsynFreeEntry( &pPort->Device->PutPool, &pPut->ListEntry);
                        KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
                     }
                     else
                     {
                        pPort->BufferCount++;
                     }
                  }
                  pPut = (PPEND_PUT)pPort->PutQ.Flink;
                  if (pPutFree && (pPut->Type == DataType))
                  {
                     if ( (pPort->DataEndTime.LowPart == (ULONG)-1) &&
                          (pPort->DataEndTime.HighPart == 0x0fffffff))
                        DsCalcTxEnd( pPort->Speed, pPut->Data.Len, &pPort->DataEndTime);
                  }
               }
            }
         }
      }
      KeReleaseSpinLock( &pPort->Lock, PortIrql);

      KeAcquireSpinLock( &pCard->Lock, &CardIrql);
   }
   pCard->InUse--;
   KeReleaseSpinLock( &pCard->Lock, CardIrql);
   //
   // Schedule Poll thread again
   //
   (void) KeSetTimer( &pCard->Poll, pCard->PollCycle, &pCard->PollFunction);
}

NTSTATUS DsOpen(
   IN HANDLE      hDevice,
   IN PSZ         pszStreamDeviceName,
   IN HANDLE      hOwner,
   IN PVOID       Data,
   IN ULONG       Length,
   OUT PHANDLE    phStream,
   OUT PULONG     pErrorCode
   )
{
   PDS_STREAM  pPort;
   KIRQL       CardIrql;
   PPORT_EXT   pPortExt = (PPORT_EXT)hOwner;

   if ( !hDevice || !hOwner)
   {
      DigiDump(DIGIALL, ("***DS: DsOpen hDevice=0x%x hOwner=0x%x\n",
               hDevice, hOwner));
      ASSERT(hDevice && hOwner);
      return STATUS_UNSUCCESSFUL;
   }
   pPort = (PDS_STREAM)DigiAllocMem( NonPagedPool, sizeof(DS_STREAM));
   if (pPort)
   {
      pPort->Device = (PDS_DEV)hDevice;
      InitializeListHead( &pPort->PutQ);
      InitializeListHead( &pPort->GetQ);
      KeInitializeSpinLock( &pPort->Lock);
      pPort->Speed = 9600;
      pPort->BufferCount = 0;
      pPort->PortExt = pPortExt;
      pPort->Port = pPortExt->Port;
      pPort->Sseq = 0;
      pPort->DataEndTime.LowPart = (ULONG)-1;
      pPort->DataEndTime.HighPart = 0x0fffffff;
      KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
      InsertTailList( &((PDS_DEV)hDevice)->StreamsQ, &pPort->ListEntry);
      KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
   }
   *phStream = (PHANDLE)pPort;
   return STATUS_SUCCESS;
}

NTSTATUS DsClose(
   IN HANDLE      hStream
   )
{
   KIRQL       CardIrql;

   if (hStream)
   {
      PDS_STREAM     pPort = hStream;

      KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
      RemoveEntryList( &((PDS_STREAM)hStream)->ListEntry);
      while (!IsListEmpty( &pPort->GetQ))
      {
         PPEND_GET pGet;
         pGet = (PPEND_GET)RemoveHeadList( &pPort->GetQ);
         DigiDump( DIGIDXB, ("DS: DsClose Port=%d Freeing GET=0x%x\n",
                              pPort->Port, pGet));
         AsynFreeEntry( &pPort->Device->GetPool, &pGet->ListEntry);
      }
      while (!IsListEmpty( &pPort->PutQ))
      {
         PPEND_PUT pPut;
         pPut = (PPEND_PUT)RemoveHeadList( &pPort->PutQ);
         DigiDump( DIGIDXB, ("DS: DsClose Port=%d Freeing GET=0x%x\n",
                              pPort->Port, pPut));
         AsynFreeEntry( &pPort->Device->PutPool, &pPut->ListEntry);
      }
      KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
      DigiFreeMem( (PVOID)hStream);
   }
   return STATUS_SUCCESS;
}

NTSTATUS DsPurge(
   IN HANDLE hStream,
   IN ULONG PurgeFlags
   )
{
   return STATUS_SUCCESS;
}

//
// DsGetMsgEx()
//
// If there is data or control info in the Put queue then we can complete the
// Get Message request in this context.
// Else queue the GetMsg work item as a PEND_GET descriptor.
//

NTSTATUS DsGetMsgEx(
   IN HANDLE   hStream,
   IN ULONG    ControlLength,
   IN PUCHAR   pDataBuffer,
   IN ULONG    DataLength,
   IN HANDLE   Context,
   IN UCHAR    Priority,
   IN PUTGETMSG_COMPLETION_ROUTINE CompletionRoutine
   )
{
   ULONG       RxLength;
   PDS_STREAM  pPort = (PDS_STREAM)hStream;
   KIRQL       CardIrql;
   KIRQL       PortIrql;
   PPEND_PUT   pPut, pPutFree = NULL;
   PPEND_GET   pGet;
   NTSTATUS    Status = STATUS_SUCCESS;

   DigiDump( DIGIDXB, ("DS: DsGetMsg: Port=%d pD=0x%x\n",
                        ((PPORT_EXT)Context)->Port, pDataBuffer));
   if (!hStream)
   {
      DigiDump( DIGIALL, ("***DS: DsGetMsg: Null stream \n"));
      return STATUS_UNSUCCESSFUL;
   }

   KeAcquireSpinLock( &pPort->Lock, &PortIrql);

   if (!IsListEmpty( &pPort->PutQ))
   {
      pPut = (PPEND_PUT)pPort->PutQ.Flink;
      if (pPut->Type == DataType)
      {
         LARGE_INTEGER  CurrentTime;

         KeQuerySystemTime( &CurrentTime);
         if (pPort->DataEndTime.QuadPart <= CurrentTime.QuadPart)
         {
            if (DataLength > pPut->Data.Len)
               RxLength = pPut->Data.Len;
            else
               RxLength = DataLength;
            if (pDataBuffer)
            {
               RtlCopyMemory( pDataBuffer, pPut->Data.Data + pPut->Data.Start,
                              RxLength);
               CompletionRoutine( hStream, Context, NULL, 0,
                                  pDataBuffer, RxLength, STATUS_SUCCESS);
               DigiDump( DIGIDXB, ("DS: GET Port=%d Data Completion; Count=0x%x\n",
                                    pPort->Port, pPort->BufferCount));
            }
            else
            {
               DigiDump( DIGIDXB, ("DS: GET Port=%d Data Completion; Count=0x%x but no Rx Buffer\n",
                                    pPort->Port, pPort->BufferCount));
               goto  DsGetMsgInsert;
            }
            pPort->DataEndTime.LowPart = (ULONG)-1;
            pPort->DataEndTime.HighPart = 0x0fffffff;
            if (RxLength == pPut->Data.Len)
            {
               pPutFree = pPut;
            }
            else
            {
               pPut->Data.Len -= RxLength;
               pPut->Data.Start += RxLength;
            }
         }
         else
         {
            //DbgBreakPoint();
            goto  DsGetMsgInsert;
         }
      }
      else if (pPut->Type == SdxbType)
      {
         DigiDump( DIGIDXB, ("DS: GET S/DXB Completion Port=%d Count=0x%x\n",
                              pPort->Port, pPort->BufferCount));
         DigiDump( DIGIDXB, ("DS: Put len=0x%x [0x%x] [0x%x]\n",
                             pPut->Sdxb.Length, ((PULONG)pPut->Sdxb.Buffer)[0],
                             ((PULONG)pPut->Sdxb.Buffer)[1]));
         CompletionRoutine( hStream, Context,
                            pPut->Sdxb.Buffer, pPut->Sdxb.Length,
                            pDataBuffer, 0, STATUS_SUCCESS);
         pPutFree = pPut;
      }
      else
      {
         DigiDump( DIGIALL, ("DS: *** Hey why are we pending Port=%d pPut=0x%x\n",
                              pPort->Port, pPut));
         DbgBreakPoint();
      }
      if (pPutFree)
      {
         DigiDump( DIGIDXB, ("DS: DsGetMsg Port=%d Freeing PUT=0x%x\n",
                              pPort->Port, pPutFree));
         RemoveEntryList( &pPutFree->ListEntry);
         KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
#if 0
         if (pPutFree->Type == DataType)
         {
            DigiDump( DIGIDXB, ("DS: DsGetMsg Port=%d Freeing DATA=0x%x\n",
                                 pPort->Port, pPutFree->Data));
            RemoveEntryList( &pPutFree->Data->ListEntry);
            AsynFreeEntry( &pPort->Device->DataPool, &pPutFree->Data->ListEntry);
         }
#endif
         AsynFreeEntry( &pPort->Device->PutPool, &pPutFree->ListEntry);
         pPort->BufferCount--;
         KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
      }
      //
      // When things are busy (>DS_MAX_BUF_QUEUED) the Put Completion
      // routines will not get called when DsPutMsg() is called.
      // Instead they are pending, waiting for the activity to decrease.
      //
      if (!IsListEmpty(&pPort->PutQ))
      {
         PPEND_PUT      pPutNext;

         pPut = (PPEND_PUT)pPort->PutQ.Flink;
         for (pPut = (PPEND_PUT)pPort->PutQ.Flink;
            pPut != (PPEND_PUT)&pPort->PutQ; pPut = pPutNext)
         {
            pPutNext = (PPEND_PUT)pPut->ListEntry.Flink;

            if (pPort->BufferCount >= DS_MAX_DATA_BUF_QUEUED)
            {
               break;
            }

            if (pPut->Type == PendingType)
            {
               BOOLEAN     bQueuePut;

               //DbgBreakPoint();
               bQueuePut = DsProcessPut( pPort, pPut);
               DigiDump( DIGIDXB, ("DS: GetMsg Port=%d PUT Completion; Count=0x%x\n",
                                    pPort->Port, pPort->BufferCount));
               pPut->Pending.CompletionRoutine( pPut->Pending.hStream,
                                                pPut->Pending.Context,
                                                pPut->Pending.pControlBuffer,
                                                pPut->Pending.ControlLength,
                                                pPut->Pending.pDataBuffer,
                                                pPut->Pending.DataLength,
                                                STATUS_SUCCESS);
               if (!bQueuePut)
               {
                  //
                  // The PutMsg does not need a response, so no need to
                  // keep the Put Descriptor hanging around.
                  //
                  DigiDump( DIGIDXB, ("DS: GetMsg Port=%d Freeing PUT=0x%x\n",
                                       pPort->Port, pPut));
                  RemoveEntryList( &pPut->ListEntry);
                  KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
                  AsynFreeEntry( &pPort->Device->PutPool, &pPut->ListEntry);
                  KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
               }
               else
               {
                  pPort->BufferCount++;
               }
            }
         }
         pPut = (PPEND_PUT)pPort->PutQ.Flink;
         if (pPutFree && (pPut->Type == DataType))
         {
            if ( (pPort->DataEndTime.LowPart == (ULONG)-1) &&
                 (pPort->DataEndTime.HighPart == 0x0fffffff))
               DsCalcTxEnd( pPort->Speed, pPut->Data.Len, &pPort->DataEndTime);
         }

      }
   }
   else
   {
DsGetMsgInsert:
      KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
      pGet = (PPEND_GET)AsynAllocEntry( &pPort->Device->GetPool, &Status);
      KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
      DigiDump( DIGIDXB, ("DS: Poll Port=%d Alloc GET=0x%x\n", pPort->Port, pGet));

      pGet->Hstream = hStream;
      pGet->ControlLength = ControlLength;
      pGet->DataBuffer = pDataBuffer;
      pGet->DataLength = DataLength;
      pGet->Context = Context;
      pGet->Priority = Priority;
      pGet->CompletionRoutine = CompletionRoutine;
      DigiDump( DIGIDXB, ("***DS: DsGetMsg: Port=%d A pending message outstanding\n",
                           pPort->Port));
      InsertTailList( &pPort->GetQ, &pGet->ListEntry);
   }
   KeReleaseSpinLock( &pPort->Lock, PortIrql);

   return (Status);
}


//
// Function:   DsPutMsg
//
//
// If Data
//    - Get pre-allocated PUT descriptor
//    - If buffers-queued < DS_MAX_DATA_BUF_QUEUED
//       - Copy data to a preallocated data buffer
//       - Increment buffers-queued count
//       - Call completion routine
//    - Else
//       - Copy parameters to PUT descriptor
//       - Mark PUT descriptor as completion routine pending.
//    - Queue PUT descriptor
// Else must be control data
//    - Get pre-allocated PUT descriptor
//    - Build appropriate S/DXB response
//    - Call completion routine
//    - Queue PUT descriptor
//

NTSTATUS DsPutMsgEx(
   IN HANDLE   hStream,
   IN PUCHAR   pControlBuffer,
   IN ULONG    ControlLength,
   IN PUCHAR   pDataBuffer,
   IN ULONG    DataLength,
   IN HANDLE   Context,
   IN UCHAR    Priority,
   IN PUTGETMSG_COMPLETION_ROUTINE CompletionRoutine
   )
{
   PPEND_GET   pGet;
   PPEND_PUT   pPut;
   PDS_STREAM  pPort = (PDS_STREAM)hStream;
   NTSTATUS    Status = STATUS_SUCCESS;
   KIRQL       PortIrql, CardIrql;

   DigiDump( DIGISDXB, ("SDXB: tx Port=%d ", pPort->Port));
   if (pDataBuffer)
   {
      DigiDump( DIGISDXB, ("DATA len=0x%x\n", DataLength));
   }
   else if (pControlBuffer)
   {
      PUCHAR pStr;

      switch (((PSDXB_CMD_HEAD)pControlBuffer)->Ctype)
      {
         case SYNC_SDXB:
            DigiDump( DIGISDXB, ("#=0x%x ",
                     ((PSDXB_CMD)pControlBuffer)->U.Sync.Sseq));
            pStr = "SYNC";
            break;
         case BUFFER_SIZE_SDXB:
            pStr = "BUF_SIZE";
            break;
         case MODEM_CTRL_SDXB:
            pStr = "MODM_CTRL";
            break;
         case EVENT_SDXB:
            pStr = "EVENT";
            break;
         case LINE_ERR_SDXB:
            pStr = "LINE_ERR";
            break;
         case PORT_CAP_SDXB:
            pStr = "PORT_CAP";
            break;
         case PORT_PARAM_SDXB:
            pStr = "PORT_PAR";
            break;
         case SEQ_NUM_SDXB:
            pStr = "SEQ_NUM";
            break;
         case CHAR_IMMED_SDXB:
            pStr = "IMMED";
            break;
         case WATERMARKS_SDXB:
            pStr = "WATERMARK";
            break;
         case BREAK_SDXB:
            pStr = "BREAK";
            break;
         case FLUSH_SDXB:
            pStr = "FLUSH";
            break;
         case PAUSE_SDXB:
            pStr = "PAUSE";
            break;
         case SPECIAL_CHAR_SDXB:
            pStr = "SPX_CHAR";
            break;
         case FRAME_EXCEPT_SDXB:
            pStr = "FRAME_EXC";
            break;
         case PROTOCOL_ERR_SDXB:
            pStr = "PROTO_ERR";
            break;
         default:
            pStr = "***UNKNOWN";
            break;
      }
      DigiDump( DIGISDXB, ("%s\n", pStr));
   }

   if (!hStream)
   {
      DigiDump( DIGIALL, ("***DS: DsPutMsg: Null stream \n"));
      ASSERT(!hStream);
      ASSERT(pPort->Device == &Card);
   }

   KeAcquireSpinLock( &pPort->Lock, &PortIrql);

   KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
   pPut = (PPEND_PUT)AsynAllocEntry( &pPort->Device->PutPool, &Status);
   DigiDump( DIGIDXB, ("DS: DsPutMsg Port=%d Alloc PUT=0x%x\n", pPort->Port, pPut));
   KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);

   pPut->Pending.hStream = hStream;
   pPut->Pending.pControlBuffer = pControlBuffer;
   pPut->Pending.ControlLength = ControlLength;
   pPut->Pending.pDataBuffer = pDataBuffer;
   pPut->Pending.DataLength = DataLength;
   pPut->Pending.Context = Context;
   pPut->Pending.Priority = Priority;
   pPut->Pending.CompletionRoutine = CompletionRoutine;

   if (pPort->BufferCount < DS_MAX_DATA_BUF_QUEUED)
   {
      BOOLEAN     bQueuePut;

      bQueuePut = DsProcessPut( pPort, pPut);
      DigiDump( DIGIDXB, ("DS: PUT Port=%d Completion; Count=0x%x\n",
                           pPort->Port, pPort->BufferCount));
      CompletionRoutine( hStream, Context, pControlBuffer, ControlLength,
                         pDataBuffer, DataLength, STATUS_SUCCESS);
      if (!bQueuePut)
      {
         //
         // The PutMsg does not need a response, so no need to keep the
         // Put Descriptor hanging around.
         //
         KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
         DigiDump( DIGIDXB, ("DS: PutMsg Port=%d Freeing PUT=0x%x\n", pPort->Port, pPut));
         AsynFreeEntry( &pPort->Device->PutPool, &pPut->ListEntry);
         KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
      }
      else
      {
         pPort->BufferCount++;
         if ((pPort->BufferCount == 1) && (pPut->Type == DataType))
         {
            if ( (pPort->DataEndTime.LowPart == (ULONG)-1) &&
                 (pPort->DataEndTime.HighPart == 0x0fffffff))
               DsCalcTxEnd( pPort->Speed, pPut->Data.Len, &pPort->DataEndTime);
         }
         DigiDump( DIGIDXB, ("DS: PutMsg Port=%d Inserting PUT=0x%x\n",
                              pPort->Port, pPut));
         InsertTailList( &pPort->PutQ, &pPut->ListEntry);
      }
   }
   else
   {
      pPut->Type = PendingType;
      DigiDump( DIGIDXB, ("DS: PutMsg Port=%d Inserting PUT=0x%x Pending\n", pPort->Port, pPut));
      InsertTailList( &pPort->PutQ, &pPut->ListEntry);
   }
   KeReleaseSpinLock( &pPort->Lock, PortIrql);
   return (Status);
}

BOOLEAN
DsProcessPut(
   IN PDS_STREAM     pPort,
   IN PPEND_PUT      pPut
)
{
   PSYNC_RSP_SDXB          sync;
   PBUFFER_SIZE_RSP_SDXB   bufferSize;
   PMODEM_CTRL_RSP_SDXB    modemCtrl;
   PEVENT_RSP_SDXB         event;
   PLINE_ERR_RSP_SDXB      lineErr;
   PPORT_PARAM_RSP_SDXB    portParam;
   PSEQ_NUM_RSP_SDXB       seqNum;
   PVOID                   rspBuffer;
   KIRQL                   CardIrql;
   ULONG                   CmdLength;
   NTSTATUS                Status;
   BOOLEAN                 bQueuePut = TRUE;

   if (pPut->Pending.pDataBuffer)
   {
#if 0
      KeAcquireSpinLock( &pPort->Device->Lock, &CardIrql);
      pPut->Data = (PRX_BUF)AsynAllocEntry( &pPort->Device->DataPool, &Status);
      DigiDump( DIGIDXB, ("DS: Process Port=%d Alloc DATA=0x%x\n",
                           pPort->Port, pPut->Data));
      KeReleaseSpinLock( &pPort->Device->Lock, CardIrql);
      RtlCopyMemory( pPut->Data->Data, pPut->Pending.pDataBuffer,
                     pPut->Pending.DataLength);
      pPut->Data->Len = pPut->Pending.DataLength;
      pPut->Data->Start = 0;
#else
      RtlCopyMemory( pPut->Data.Data, pPut->Pending.pDataBuffer,
                     pPut->Pending.DataLength);
      pPut->Data.Len = pPut->Pending.DataLength;
      pPut->Data.Start = 0;
#endif
      pPut->Type = DataType;
   }
   else
   {
      PUCHAR   pControlBuffer;

      pPut->Type = SdxbType;
      CmdLength = 0;
      pControlBuffer = pPut->Pending.pControlBuffer;

      switch (((PSDXB_CMD_HEAD)pControlBuffer)->Ctype)
      {
         case SYNC_SDXB:
            CmdLength = sizeof(SYNC_RSP_SDXB);
            sync = (PSYNC_RSP_SDXB)&pPut->Sdxb;
            sync->Ctype = SYNC_SDXB;
            sync->Flags = 0;
            sync->Sseq = ((struct SYNC_SDXB_CMD *)pControlBuffer)->Sseq;
//            DigiDump( DIGISDXB, ("FOO P=%d #=0x%x add=0x%x\n",
//                                 pPort->Port, sync->Sseq, sync));
            if ((sync->Sseq) && (sync->Sseq <= pPort->Sseq))
            {
               DbgBreakPoint();
            }
            pPort->Sseq = sync->Sseq;
            break;
         case BUFFER_SIZE_SDXB:
            CmdLength = sizeof(BUFFER_SIZE_RSP_SDXB);
            bufferSize = (PBUFFER_SIZE_RSP_SDXB)&pPut->Sdxb;
            bufferSize->Ctype = BUFFER_SIZE_SDXB;
            bufferSize->Insize = 2 * 1024;
            bufferSize->Outsize = 1 * 1024;
            break;
         case MODEM_CTRL_SDXB:
            CmdLength = sizeof(MODEM_CTRL_RSP_SDXB);
            modemCtrl = (PMODEM_CTRL_RSP_SDXB)&pPut->Sdxb;
            modemCtrl->Ctype = MODEM_CTRL_SDXB;
            modemCtrl->Flags = 0;
            modemCtrl->Mstat = ((struct MODEM_CONTROL_SDXB_CMD *)pControlBuffer)->Mout;
            break;
         case EVENT_SDXB:
            CmdLength = sizeof(EVENT_RSP_SDXB);
            event = (PEVENT_RSP_SDXB)&pPut->Sdxb;
            event->Ctype = EVENT_SDXB;
            event->Flags = 0;
            event->Estat = 0;
            break;
         case LINE_ERR_SDXB:
            CmdLength = sizeof(LINE_ERR_RSP_SDXB);
            lineErr = (PLINE_ERR_RSP_SDXB)&pPut->Sdxb;
            lineErr->Ctype = LINE_ERR_SDXB;
            lineErr->Flags = 0;
            lineErr->Norun = 0;
            lineErr->Noflow = 0;
            lineErr->Nframe = 0;
            lineErr->Nparity = 0;
            lineErr->Nbreak = 0;
            break;
         case PORT_CAP_SDXB:
            bQueuePut = FALSE;
            break;
         case PORT_PARAM_SDXB:
            CmdLength = sizeof(PORT_PARAM_RSP_SDXB);
            portParam = (PPORT_PARAM_RSP_SDXB)&pPut->Sdxb;
            portParam->Ctype = PORT_PARAM_SDXB;
            portParam->Cflags = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Cflags;
            portParam->Iflags = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Iflags;
            portParam->Oflags = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Oflags;
            portParam->Xflags = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Xflags;
            portParam->Fflags = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Fflags;
            portParam->Txaccm = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Txaccm;
            portParam->Rxaccm = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Rxaccm;
            portParam->Speed = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Speed;
            portParam->Mflow = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Mflow;
            portParam->Mctrl = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Mctrl;
            portParam->Xon = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Xon;
            portParam->Xoff = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Xoff;
            portParam->Xxon = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Xxon;
            portParam->Xxoff = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Xxoff;
            portParam->Lnext = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Lnext;
            portParam->Spcr = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Spcr;
            portParam->Mspesc = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->MspEsc;
            portParam->Idel = ((struct PORT_PARAMETERS_SDXB_CMD *)pControlBuffer)->Idel;
            pPort->Speed = portParam->Speed;
            break;
         case SEQ_NUM_SDXB:
            CmdLength = sizeof(SEQ_NUM_RSP_SDXB);
            seqNum = (PSEQ_NUM_RSP_SDXB)&pPut->Sdxb;
            seqNum->Ctype = SEQ_NUM_SDXB;
            seqNum->Rin = 0;
            seqNum->Tout = 0;
            break;
         case CHAR_IMMED_SDXB:
         case WATERMARKS_SDXB:
         case BREAK_SDXB:
         case FLUSH_SDXB:
         case PAUSE_SDXB:
         case SPECIAL_CHAR_SDXB:
         case FRAME_EXCEPT_SDXB:
         case PROTOCOL_ERR_SDXB:
         default:
            bQueuePut = FALSE;
            break;
      }
      pPut->Sdxb.Length = CmdLength;
   }
   return bQueuePut;
}

VOID
DsCalcTxEnd(
   IN ULONG             Speed,
   IN ULONG             Length,
   OUT PLARGE_INTEGER   pStamp

)
{
   LARGE_INTEGER     Time;

   KeQuerySystemTime( &Time);
   pStamp->QuadPart = (((MILLISECOND * 10000)/Speed) * Length + Time.QuadPart);
   DigiDump( DIGIDXB, ("DS: TimeStamp=0x%x\n", pStamp->QuadPart));
}
