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

#include "header.h"

/*
 *----------------------------------------------------------------------
 * Function:  AsynCheckWaitEvents.
 *
 * Purpose:
 *
 *    Match up the current Modem and Async notification events with the
 *    current WaitMask.
 *
 *
 * Method:
 *
 *    o Check each NT SERIAL_EV_XXXXX item against driver stored status.
 *    o Complete the WaitMaskIrp when a pending event occurs.
 *
 *
 * Returns:
 *
 *    o Nothing is returned.
 *
 *
 * Note:
 *
 *    o The Port SPINLOCK is held during this call.
 *
 *
 *
 *----------------------------------------------------------------------
 q*/
VOID
AsynCheckWaitEvents(
    IN PPORT_EXT   pPortExt            // Port structure
)
{
   PIRP        pIrp;                  // Complete pending waitMask IRP.
   ULONG       WaitMask;              // Port wait mask value.
   ULONG       WaitEvent;             // Composite of events that occurred
                                      // and are being waited upon.
   ULONG       HistoryMask;             // Composite of events that occurred.
   KIRQL       OldIrql;               // Save prior irq level.

   DigiDump(DIGIWAIT, ("ASYN!CheckWaitEvents: Port=%d Mask=0x%x History=0x%x\n", 
                        pPortExt->Port, pPortExt->WaitMask, pPortExt->HistoryMask));

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

   if ( WaitEvent && pPortExt->WaitMaskIrp)
   {
      pIrp = pPortExt->WaitMaskIrp;
      pPortExt->WaitMaskIrp = NULL;
      pPortExt->HistoryMask = 0;

      DigiDump(DIGIWAIT, ("ASYN!CheckWaitEvents: COMPLETED Wait IRP on events:%X IRP=0x%x\n", 
          WaitEvent, pIrp));

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

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

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoSetWaitMask.
 *
 * Purpose:
 *
 *    Enable Modem and Async event notification.
 *
 *
 * Method:
 *
 *    o Figure out the required settings for the new wait mask.
 *    o Update the current S/DXB notification events.
 *    o Send the updated notification events to the S/DXB.
 *    o Send the updated IFLAGS to the S/DXB.
 *    o Request updated modem status.
 *
 *
 * Returns:
 *
 *    o STATUS_PENDING is normally returned.
 *    o STATUS_BUFFER_TOO_SMALL on input buffer too small.
 *    o STATUS_INVALID_PARAMETER on Error.
 *
 *
 * Notes:
 *
 *
 *
 *----------------------------------------------------------------------
 */
NTSTATUS
AsynIoSetWaitMask(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Associated IRP
   IN ULONG             BufferSize,    // Input buffer size.
   IN PULONG            pWaitMask      // Wait mask to set
)
{
   PCARD_EXT   pCardExt;               // Driver specific information.
   NTSTATUS    Status = STATUS_SUCCESS;// Call return value.
   PPUT_MSG_EV pPutEvent = NULL;
   BOOLEAN     bBuildPortParam = FALSE;
   BOOLEAN     bBuildLineErr = FALSE;
   ULONG       OldMask;
   PIRP        pIrpRecorded = NULL;

   DigiDump(DIGIIOCTL | DIGIWAIT, ("ASYN!IoSetWaitMask PortNo=0x%x CurMask=0x%x NewMask=0x%x History=0x%x\n",
                        pPortExt->Port, pPortExt->WaitMask, *pWaitMask, pPortExt->HistoryMask));

   if (BufferSize < sizeof(*pWaitMask))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }
   //
   // Verify the mask values.
   //
   if (*pWaitMask & ~(SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY |
                      SERIAL_EV_CTS | SERIAL_EV_DSR | SERIAL_EV_RLSD |
                      SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING |
                      SERIAL_EV_PERR | SERIAL_EV_RX80FULL | SERIAL_EV_EVENT1 |
                      SERIAL_EV_EVENT2) )
   {
       Status = STATUS_INVALID_PARAMETER;
       goto IoctlExit;
   }

   if (pPortExt->DxbCount > MAX_SDXB_IOCTL) {
      Status = STATUS_DEVICE_BUSY;
      goto IoctlExit;
   }

   //
   // Let's see what we need to set (if anything) in S/DXB.
   //
   OldMask = pPortExt->WaitMask;
   pPortExt->WaitMask = *pWaitMask;
   pPortExt->HistoryMask &= pPortExt->WaitMask;

   //
   // Complete prior IRP waiting on waitMask events.
   //
   if (pPortExt->WaitMaskIrp)
   {
      PIRP     pOldIrp;

      DigiDump(DIGIWAIT, ("ASYN!IoSetWaitMask: RESET:complete prior IRP=0x%x\n",
                           pPortExt->WaitMaskIrp));

      pOldIrp = pPortExt->WaitMaskIrp;
      pPortExt->WaitMaskIrp = NULL;

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

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

   //
   // Is a PORT-PARAM-CMD necessary ?
   //
   if ( (OldMask & SERIAL_EV_RXFLAG)
        != (*pWaitMask & SERIAL_EV_RXFLAG) )
   {
      if (!(pPortExt->CardExt->RunTimeOptions & RUNTIME_RXFLAG_IN_DRIVER))
      {
         if (*pWaitMask & SERIAL_EV_RXFLAG)
            pPortExt->SdxbValues.Xflags |= SDXB_XSCR;
         else
            pPortExt->SdxbValues.Xflags &= ~SDXB_XSCR;
         bBuildPortParam = TRUE;
      }
   }

   //
   // Is a MODEM-CONTROL-CMD necessary ?
   //
   if ( (OldMask & (SERIAL_EV_CTS | SERIAL_EV_DSR |
                    SERIAL_EV_RLSD | SERIAL_EV_RING))
        != (*pWaitMask & (SERIAL_EV_CTS | SERIAL_EV_DSR |
                          SERIAL_EV_RLSD | SERIAL_EV_RING)) )
   {
      // It's not necessary to generate a Modem-Control-Cmd, since we are
      // always monitoring changes in these signals.

      // This space let blank intentionally!
   }
   //
   // Is a LINE_ERR-CMD necessary ?
   //
   if ( (OldMask & (SERIAL_EV_BREAK | SERIAL_EV_ERR))
        != (*pWaitMask & (SERIAL_EV_BREAK | SERIAL_EV_ERR)) )
   {
      if (*pWaitMask & (SERIAL_EV_BREAK | SERIAL_EV_ERR))
         pPortExt->SdxbValues.Ltime = SDXB_LTIME_DEF;
      else
         pPortExt->SdxbValues.Ltime = SDXB_LTIME_OFF;
      bBuildLineErr = TRUE;
   }
   if ( !bBuildPortParam && !bBuildLineErr)
   {
      goto IoctlExit;
   }
   pCardExt = pPortExt->CardExt;

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

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

   if (bBuildPortParam) {
      pPortExt->DxbCount++;

      Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                                SYNC_CMD, REASON_SET_WAIT, DS_PRIORITY_LOW, 
                                PutBuilding, AsynBuildSetPortCmd);

      if (Status != STATUS_PENDING) {
      	 pPortExt->DxbCount--;
         goto IoctlExitPutCleanup;
      }
   }
   if (bBuildLineErr)
   {
      pPortExt->DxbCount++;
      // NOTE: the parameters :- pIrp, SYNC_CMD, and REASON_SET_WAIT are not
      // necessary if bBuildPortParam is TRUE and a pPutEvent has already been
      // allocated by AsynPutEventMsg(),  they will simply be ignored by 
      // AsynPutEventMsg().
      Status = AsynPutEventMsg( pPortExt, &pPutEvent, pIrpRecorded, 
                                SYNC_CMD, REASON_SET_WAIT, DS_PRIORITY_LOW, 
                                PutBuilding, AsynBuildLineErrorCmd);
      if (Status != STATUS_PENDING)
      {
      	 pPortExt->DxbCount--;
         goto IoctlExitPutCleanup;
      }
   }
   //
   // Sync command, a convenient tag which tells us when SET_WAIT 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:
   if (pPutEvent && (Status != STATUS_PENDING))
   {
      AsynPutEventCleanup( &pPutEvent);
   }
   if (pPutEvent && (Status == STATUS_PENDING) && !pIrpRecorded)
   {
      Status = STATUS_SUCCESS;
   }
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoGetWaitMask
 *
 * Purpose:
 *
 *    Return the current wait mask.
 *
 *
 * Method:
 *
 *    o Return the current wait mask.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
NTSTATUS
AsynIoGetWaitMask(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl Wait request to perform.
   IN ULONG             BufferSize,    // Output buffer size.
   OUT PULONG           pWaitMask     // Wait mask to report.
)
{
   NTSTATUS    Status;                 // Call return value.

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

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

   *pWaitMask = pPortExt->WaitMask;
   
   pIrp->IoStatus.Information = sizeof(*pWaitMask);
   Status = STATUS_SUCCESS;

IoctlExit:
   return (Status);
}

/*
 *----------------------------------------------------------------------
 * Function:  AsynIoWaitOnMask
 *
 * Purpose:
 *
 *    Wait for a WaitMask event to occur.
 *
 *
 * Method:
 *
 *    o Complete Prior WaitMaskIrp.
 *    o Set a Wait IRP for a WaitMask event to occur.
 *
 *
 * Returns:
 *
 *    o STATUS_BUFFER_TOO_SMALL on buffer too small.
 *    o STATUS_SUCCESS on WaitMask is zero.
 *    o STATUS_PENDING is normally returned.
 *
 *
 *----------------------------------------------------------------------
 */
NTSTATUS
AsynIoWaitOnMask(
   IN PPORT_EXT         pPortExt,      // Port specific information.
   IN PIRP              pIrp,          // Ioctl Wait request to perform.
   IN ULONG             BufferSize     // Size of supplied buffer.
)
{
   NTSTATUS    Status;                 // Call return value.
   PIRP        pOldIrp;                // Prior Waiting IRP.

   DigiDump(DIGIIOCTL | DIGIWAIT, ("ASYN!IoWaitOnMask: PortNo=0x%x IRP=0x%x History=0x%x\n", 
            pPortExt->Port, pIrp, pPortExt->HistoryMask));

   if (BufferSize < sizeof(ULONG))
   {
      Status = STATUS_BUFFER_TOO_SMALL;
      goto IoctlExit;
   }
   //
   // Return immediately if the WaitMask is not set!
   //
   if ( (pPortExt->WaitMaskIrp) ||
        (pPortExt->WaitMask == 0) )
   {
      pIrp->IoStatus.Information = sizeof(ULONG);
      *((ULONG *) pIrp->AssociatedIrp.SystemBuffer) = 0;
      Status = STATUS_INVALID_PARAMETER;
      goto IoctlExit;
   }
   //
   // Can the Wait be satisfied immediately
   //
   if (pPortExt->HistoryMask & pPortExt->WaitMask)
   {
      pIrp->IoStatus.Information = sizeof(ULONG);
      Status = STATUS_SUCCESS;
      *((ULONG *) pIrp->AssociatedIrp.SystemBuffer) = 
         pPortExt->HistoryMask & pPortExt->WaitMask;
      pPortExt->HistoryMask = 0;
   }
   else
   {
      pPortExt->WaitMaskIrp = pIrp;
      Status = STATUS_PENDING;
   }

IoctlExit:
   return (Status);
}


