/*
 ****************************************************************************
 * 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
 *
 ****************************************************************************
 */

#include "header.h"

/*
 *----------------------------------------------------------------------
 * Function:  AsynPutMsgComplete
 *
 * Purpose:
 *
 *    Put Message Completion routine called by DStreams Lib
 *
 *
 * Method:
 *    o Decrement the Completion call reference count.
 *    o if the reference goes to zero
 *       o if we entered at IRQL < DISPATCH_LEVEL
 *          o Complete the IRP
 *       o else 
 *          o Put the IRP in the CompleteWhenLoweredIrqlQueue
 *       o if PutEventState == Awaiting-Completion
 *          o Set PutEventState = Awaiting-Response
 *       o if PutEventState == Awaiting-CompletionResponseRxed
 *          o Free Put event
 *
 * Returns:
 *
 *    void
 *
 *
 *----------------------------------------------------------------------
 */
void
AsynPutMsgComplete(
   IN HANDLE HOwner,
   IN PVOID Context,
   IN PVOID PControlBuffer,
   IN ULONG ControlLength,
   IN PVOID PDataBuffer,
   IN ULONG DataLength,
   IN NTSTATUS Status
)
{
   PPUT_MSG_EV pPutEvent;
   PPORT_EXT   pPortExt;
   PIRP        pCompleteIrp = (PIRP)NULL;  // Irp to complete

   pPutEvent = (PPUT_MSG_EV)Context;

   ASSERT(pPutEvent);
#ifdef DBG
   ASSERT(pPutEvent->EntryState == EntryAllocated);
#endif
   ASSERT(pPutEvent->PutMsgRef);

   pPortExt = pPutEvent->PortExt;

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

   DigiDump(DIGIFLOW|DIGIFLOW1, ("AsynPutMsgComplete: pPutEvent=0x%x DataBuf=0x%x Port=%d\n",
                       pPutEvent, PDataBuffer, pPortExt->Port));
   switch (Status)
   {
      case STATUS_SUCCESS:
         // All is good so far.
         break;
      case STATUS_CANCELLED:
         // We must have requested a Purge
         pPutEvent->Status = Status;
         if (pPutEvent->Irp)
         {
            // Strange, if we sent a purge we should have already completed
            // the associated IRP.
            PORT_ERROR(pPortExt);
         }
         break;
      default:
         // Error ocurred.
         pPutEvent->Status = Status;
         if (pPutEvent->Irp)
         {
            // Complete any associated IRP with an error status
            pCompleteIrp = pPutEvent->Irp;
            pCompleteIrp->IoStatus.Status = Status;
            pPutEvent->Irp = (PIRP)NULL;
         }
         break;

   }
   // Regardless of what happened, if the PutEvent descriptor is registered
   // with other PutMsg entries we need to keep it until the completion
   // routines for those other entries are called.
   // Thus, just decrement the reference by one.
   pPutEvent->PutMsgRef--;

   if ( !pPutEvent->PutMsgRef)
   {
      //
      // No more references
      //

      //
      // Some IRPs can be completed at this stage, others will have to
      // wait for a response from the S/DXB before completing the IRP.
      //
      switch (pPutEvent->State)
      {
         case PutBuilding:
            break;

         case PutAwaitingCompletion:
         case PutAwaitingCompletionResponseRxed:
            DigiDump(DIGIFLOW, ("AsynPutMsgComplete: pPutEvent=0x%x Status=0x%x No more ref\n",
               pPutEvent, pPutEvent->Status));
            if ( (pPutEvent->Reason == REASON_GEN_IOCTL) ||
                 (pPutEvent->Status != STATUS_SUCCESS))
            {
               if (pPutEvent->Irp)
               {
                  pCompleteIrp = pPutEvent->Irp;
                  pCompleteIrp->IoStatus.Status = pPutEvent->Status;
                  pPutEvent->Irp = (PIRP)NULL;
               }
               pPutEvent->State = PutEnded;
            }
            else if ( (pPutEvent->Reason == REASON_PAUSE) ||
                      (pPutEvent->Reason == REASON_UNPAUSE) )
            {
               if (pPutEvent->Irp)
               {
                  pCompleteIrp = pPutEvent->Irp;
                  pCompleteIrp->IoStatus.Status = pPutEvent->Status;
                  pPutEvent->Irp = (PIRP)NULL;
               }
               pPutEvent->State = PutEnded;
               if (pPortExt->DxbCount > 0)
                  pPortExt->DxbCount--;    // ioctl overflow protection
            }
            //
            // If "CompleteWhenSent == FALSE", notify the user application when
            // all the data has been successfully transferred to the adapter.
            // In fact the adapter has all the data NOW!
            else if (pPutEvent->Reason == REASON_WRITE)
            {
               if (pPutEvent->State == PutAwaitingCompletion)
               {
                  pPutEvent->State = PutAwaitingResponse;
                  if ((pPortExt->CompleteWhenSent == FALSE) && (pPutEvent->Irp))
                  {
                     pCompleteIrp = pPutEvent->Irp;
                     pPutEvent->Irp = (PIRP)NULL;
                     pCompleteIrp->IoStatus.Information =
                        IoGetCurrentIrpStackLocation(pCompleteIrp)->Parameters.Write.Length;
                     pCompleteIrp->IoStatus.Status = pPutEvent->Status;

                     if (pPutEvent == pPortExt->TimedWritePutEvent)
                     {
                        pPortExt->TimedWritePutEvent = NULL;
                        KeCancelTimer( &pPortExt->Timer.Write);
                     }
                  }
               }
               else
               {
                  // The data has already been sent
                  pPutEvent->State = PutEnded;
                  if (pPutEvent->Irp)
                  {
                     pCompleteIrp = pPutEvent->Irp;
                     pPutEvent->Irp = (PIRP)NULL;
                     pCompleteIrp->IoStatus.Information =
                        IoGetCurrentIrpStackLocation(pCompleteIrp)->Parameters.Write.Length;
                     pCompleteIrp->IoStatus.Status = pPutEvent->Status;

                     if (pPutEvent == pPortExt->TimedWritePutEvent)
                     {
                        pPortExt->TimedWritePutEvent = NULL;
                        KeCancelTimer( &pPortExt->Timer.Write);
                     }
                  }
               }
            }
            else
            {
               if (pPutEvent->State == PutAwaitingCompletion)
               {
                  pPutEvent->State = PutAwaitingResponse;
               }
               else
               {
                  pPutEvent->State = PutEnded;
                  if (pPutEvent->Irp)
                  {
                     pCompleteIrp = pPutEvent->Irp;
                     pCompleteIrp->IoStatus.Status = pPutEvent->Status;
                     pPutEvent->Irp = (PIRP)NULL;
                  }
               }
            }
            break;
         case PutAwaitingResponse:
         default:
            PORT_ERROR(pPortExt);
            if (pPutEvent->Irp)
            {
               pCompleteIrp = pPutEvent->Irp;
               pCompleteIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
               pPutEvent->Irp = (PIRP)NULL;
            }
            pPutEvent->State = PutEnded;
            break;
      }
      if (pPutEvent->State == PutEnded)
      {
         RemoveEntryList( &pPutEvent->ListEntry);
         AsynFreeEntry( &pPortExt->PutEventPool, &pPutEvent->ListEntry);
         pPutEvent = NULL;
      }
   }

   if (pCompleteIrp)
   {
      InsertTailList( &pPortExt->CompleteIrpQ, 
                      &pCompleteIrp->Tail.Overlay.ListEntry);
      if (pPortExt->PortBusy)
      {
         // Invoked from DsPutMsgEx() context.
         // Cannot complete the IRP until out of this context.
         // It's already in the Queue of pending completion IRPs, 
         // nothing more to do.
      }
      else
      {
         DigiDump(DIGIFLOW|DIGIFLOW1, ("AsynPutMsgComplete: Calling EventLoop P=%d\n",
                                       pPortExt));
         DigiDump(DIGIDISPATCH, ("ASYN!PutMsgComplete: +++[%d]Entry P=%d Irp=0x%x\n", 
                     pPortExt->PortBusy, pPortExt->Port, pCompleteIrp));
         AsynSchedulerLoop( pPortExt);
         DigiDump(DIGIDISPATCH, ("ASYN!PutMsgComplete: ---[%d]Exit P=%d\n", 
                        pPortExt->PortBusy, pPortExt->Port));
      }
   }
   KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynGetMsgComplete
 *
 * Purpose:
 *
 *    Get Message Completion routine called by DStreams Lib
 *
 *
 * Method:
 *    o Schedule the driver thread to process the event.
 *
 * Returns:
 *
 *    void
 *
 *
 *----------------------------------------------------------------------
 */
void
AsynGetMsgComplete(
   IN HANDLE HOwner,
   IN PVOID Context,
   IN PVOID pControlBuffer,
   IN ULONG ControlLength,
   IN PVOID pDataBuffer,
   IN ULONG DataLength,
   IN NTSTATUS Status
)
{
   PGET_MSG_EV pGetEvent;                 // Get Event generated.
   PPORT_EXT   pPortExt;
   NTSTATUS    LocalStatus;
   PXN_GET_LIST pXnGetListEntry ;

   pPortExt = (PPORT_EXT)Context;

   DigiDump(DIGIFLOW, ("AsynGetMsgComplete: pPortExt=0x%x ", pPortExt));
   if (!pPortExt)
   {
      DigiDump(DIGIALL, ("\n*** AsynGetMsgComplete: pPortExt=0x0 status=0x%x\n",
                            Status));
      DbgBreakPoint();
      return;
   }
   DigiDump(DIGIFLOW, ("Port=%d\n", pPortExt->Port));

   if (Status == STATUS_CANCELLED)
   {
      // Closing, nothing to do
      if (pPortExt->State == ClosingAwaitingCompleteState)
      {
         return;
      }
   }
   
   // If this is an Xn_Get we need to setup special handling of it.
   if ((ControlLength > 0 ) && (((PSDXB_CMD_HEAD)pControlBuffer)->Ctype == SDXB_ISDN))
   {
DigiDump(DIGIFLOW, ("yayaya\n"));
	  // check that our control length is properly sized.
      // alloc our queue structure.
	  // alloc members
	  // copy memory.
	  // queue on list
	  if (ControlLength < ISDN_MESSAGE_ENCAP_SIZE)
	  {
         PORT_ERROR(pPortExt);
         DigiDump(DIGIALL, ("*** AsynGetMsgComplete: ISDN CC packet improperly formed! "
                            "port=0x%x \n", pPortExt->Port));
		 return ;
	  }
	  else
	     ControlLength -= ISDN_MESSAGE_ENCAP_SIZE ;  // we need to strip the 
													 // ISDN_MESSAGE_ENCAP_SIZE header

      pXnGetListEntry = (PXN_GET_LIST) DigiAllocMem (NonPagedPool, sizeof(XN_GET_LIST)) ;
	  if (pXnGetListEntry != NULL)
	  {
	     pXnGetListEntry->XnGetEntry = (PXN_GET) DigiAllocMem (NonPagedPool, sizeof(XN_GET)) ;
		 if (pXnGetListEntry->XnGetEntry != NULL)
		 {
	        pXnGetListEntry->XnGetEntry->pControl = (PVOID) DigiAllocMem (NonPagedPool, ControlLength) ;
			if (pXnGetListEntry->XnGetEntry->pControl != NULL)
			{
	           pXnGetListEntry->XnGetEntry->pData = pDataBuffer ; // let's reuse this.
	           RtlCopyMemory (pXnGetListEntry->XnGetEntry->pControl, (PVOID)((ULONG)pControlBuffer+ISDN_MESSAGE_ENCAP_SIZE), ControlLength) ;
	           pXnGetListEntry->XnGetEntry->cbControl = ControlLength ;
			   // allocate data 
			   if (DataLength != 0)
			   {
		           pXnGetListEntry->XnGetEntry->pData = (PVOID) DigiAllocMem (NonPagedPool, DataLength) ;
				   if (pXnGetListEntry->XnGetEntry->pData != NULL)
				   {
   	                  RtlCopyMemory (pXnGetListEntry->XnGetEntry->pData, pDataBuffer, DataLength) ;
	                  pXnGetListEntry->XnGetEntry->cbData = DataLength ;
				   }
			   }
			   else
			   {
			       pXnGetListEntry->XnGetEntry->pData = NULL ;
	               pXnGetListEntry->XnGetEntry->cbData = 0 ;
			   }
DigiDump(DIGIFLOW, ("complete: pXnGetlist clen %x dlen %x\n", pXnGetListEntry->XnGetEntry->cbControl, pXnGetListEntry->XnGetEntry->cbData));
     
               KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
               InsertTailList (&pPortExt->XnGetPktQueue, &pXnGetListEntry->List);
			}
		 }
      }
	  else
	  {
         PORT_ERROR(pPortExt);
         DigiDump(DIGIALL, ("*** AsynGetMsgComplete: Ran out of Xn Get Events for "
                            "port=0x%x \n", pPortExt->Port));
		 return ;
	  }
      //
      // We just killed the last of these so post the next GetMsg
      //
      if ((pPortExt->State == OpeningState) || 
          (pPortExt->State == OpenState) ||
          (pPortExt->State == ClosingState))
      {
         AsynPostGetMsg( pPortExt, STATUS_SUCCESS);
      }
      else
      {
         DigiDump(DIGITHREAD, 
                ("*** ASYN!XnGetResponses DON'T PortGet Port=%d State=0x%x\n",
                 pPortExt->Port, pPortExt->State));
      }
	  // okay, 
	  // we jump to the code at the bottom.  This checks whether or not we 
	  // can call the asynschedulerloop.
   }
   else
   {
      //
      // We have a Command response from S/DXB
      // Build a Get Message Event

      pGetEvent = (PGET_MSG_EV)AsynAllocEntry( &pPortExt->GetEventPool, &LocalStatus);
   
      if (LocalStatus != STATUS_SUCCESS)
      {
         PORT_ERROR(pPortExt);
         DigiDump(DIGIALL, ("*** AsynGetMsgComplete: Ran out of Get Events for "
                            "port=0x%x \n", pPortExt->Port));
         return;
      }

      pGetEvent->PortExt = pPortExt;
      pGetEvent->Response = 0;
      pGetEvent->RspLength = 0;
      pGetEvent->Status = Status;
      if (pControlBuffer && ControlLength)
      {
         pGetEvent->RspLength = AsynCompressSdxbRsp( pPortExt, pControlBuffer,
                                                     ControlLength,
                                                     &pGetEvent->Rsp);
      }
      else
      {
         pGetEvent->RspLength = 0;
      }
      pGetEvent->PData = pDataBuffer;
      pGetEvent->DataLength = DataLength;

      KeAcquireSpinLock( &pPortExt->PortLock, &pPortExt->PortIrql);
      InsertTailList( &pPortExt->SdxbRspQ, &pGetEvent->ListEntry);
   }

   // things are queued either on the getevent queue or on the xnget queue.  Either
   // way we are ready to process.
   if (pPortExt->PortBusy)
   {
      DigiDump(DIGIFLOW, ("AsynGetMsgComplete: Deferred\n"));
      KeReleaseSpinLock( &pPortExt->PortLock, pPortExt->PortIrql);
   }
   else
   {
      DigiDump(DIGIFLOW, ("AsynGetMsgComplete: Calling EventLoop P=%d\n", 
                            pPortExt->Port));
      DigiDump(DIGIDISPATCH, ("ASYN!GetMsgComplete: +++[%d]Entry P=%d\n", 
                  pPortExt->PortBusy, pPortExt->Port));
      AsynSchedulerLoop( pPortExt);
      DigiDump(DIGIDISPATCH, ("ASYN!GetMsgComplete: ---[%d]Exit P=%d\n", 
                        pPortExt->PortBusy, pPortExt->Port));

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

/*
 *----------------------------------------------------------------------
 * Function:  AsynPutMsgCompleteFlush
 *
 * Purpose:
 *
 *    Put Message Completion routine for Flushing data buffers.
 *
 *
 * Method:
 *    o Do nothing.

 * Returns:
 *
 *    void
 *
 *
 *----------------------------------------------------------------------
 */
void
AsynPutMsgCompleteFlush(
   IN HANDLE HOwner,
   IN PVOID Context,
   IN PVOID PControlBuffer,
   IN ULONG ControlLength,
   IN PVOID PDataBuffer,
   IN ULONG DataLength,
   IN NTSTATUS Status
)
{
   DigiDump(DIGIFLOW, ("AsynPutMsgCompleteFlush\n"));
}
