/*
 ****************************************************************************
 * Copyright (c) 1999-2000 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:  asynwait.c
 *
 * Author      : Sandy Haapala	  
 *
 * Purpose     :
 *
 *
 *----------------------------------------------------------------------
 */

#include "digiasyn.h"

VOID
AsynCancelWait(
	IN	PDEVICE_OBJECT 	pDeviceObject,
	IN PIRP              pIrp       
)
/*++

Description

	Cancel routine for wait irps
   
--*/
{
    PASYN_PORT_EXT	pPortExt = pDeviceObject->DeviceExtension;
    PASYN_WAIT		pWaitExt = &pPortExt->Wait;

    IoReleaseCancelSpinLock( pIrp->CancelIrql );

    DigiDump(DIGICANCEL|DIGIWAIT, ("AsynCancelWait: Port=0x%x IRP=0x%x\n",
                                   pPortExt->iSdxbPort, pIrp));

    DigiAcquireSpinLock( &pWaitExt->Lock );

    if ( pIrp == pWaitExt->pPendingIrp )
    {
        DigiDump( DIGICANCEL|DIGIWAIT , ("\tCancelling wait Irp: 0x%x\n", pIrp ));
        GetAsynPendingIrp(&pWaitExt->pPendingIrp);
        // The IRP is all ours now, let everything run.
        DigiReleaseSpinLock( &pWaitExt->Lock );
        pIrp->IoStatus.Information = 0;
        AsynIoCompleteRequest( pIrp, STATUS_CANCELLED, pPortExt );
    }
    else      
    {
        DigiDump(DIGICANCEL|DIGIWAIT|DIGIERROR, ("\tIrp:%x was not found.\n", pIrp));
        DigiReleaseSpinLock( &pWaitExt->Lock );
    }
}


VOID AsynCancelWaitIrp
(
	IN PASYN_PORT_EXT  pPortExt
)
{
    PIRP pIrp;
    PASYN_WAIT pWaitExt = &pPortExt->Wait;
   
    DigiAcquireSpinLock( &pWaitExt->Lock );
    pIrp = GetAsynPendingIrp(&pWaitExt->pPendingIrp);
    DigiReleaseSpinLock( &pWaitExt->Lock );

    DigiDump(DIGICANCEL|DIGIWAIT, 
             ("AsynCancelWaitIrp: Port=0x%x Pending Irp=0x%x\n",
              pPortExt->iSdxbPort, pIrp));

    if ( pIrp != NULL )
    {
        IoSetCancelRoutine( pIrp, NULL );
        pIrp->IoStatus.Information = 0;
        AsynIoCompleteRequest( pIrp, STATUS_CANCELLED, pPortExt );
    }
}


VOID AsynSatisfyWait
( 
	IN PASYN_PORT_EXT pExt,
	IN ULONG EventReason,
	IN BOOLEAN fCompleteWait		// If TRUE, complete the Wait IRP now, even if no events have occurred.
)
/*++

Routine Description:

	This routine will complete the current Wait on mask, if the WaitQueue
	is not empty.

Return Value:

	None.

--*/
{
    PASYN_WAIT	pWaitExt = &pExt->Wait;

    DigiDump( DIGIFLOW|DIGIWAIT, ("Port %d: Entering SatisfyWait: Wait mask = 0x%x\tEventReason = 0x%x\n",
                                  pExt->iSdxbPort, pWaitExt->Mask, EventReason ));
	              
    DigiAcquireSpinLock( &pWaitExt->Lock );                 

    if ( EventReason )
        pWaitExt->History |= (pWaitExt->Mask & EventReason);
   
    if (pWaitExt->History || fCompleteWait)
    {
        PIRP pIrp = GetAsynPendingIrp(&pWaitExt->pPendingIrp);

        if (pIrp)
        {
            // Complete the pending wait irp
            *(PULONG)pIrp->AssociatedIrp.SystemBuffer = pWaitExt->History;
#if DBG
            DigiDump( DIGIWAIT, ("   Wait event satisfied because: %x\n", pWaitExt->History) );
            if ( pWaitExt->History & SERIAL_EV_RXCHAR ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_RXCHAR\n"));
            if ( pWaitExt->History & SERIAL_EV_RXFLAG ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_RXFLAG\n"));
            if ( pWaitExt->History & SERIAL_EV_TXEMPTY ) DigiDump( DIGIWAIT, ("\tSERIAL_EV_TXEMPTY\n"));
            if ( pWaitExt->History & SERIAL_EV_CTS ) 		DigiDump( DIGIWAIT, ("\tSERIAL_EV_CTS\n"));
            if ( pWaitExt->History & SERIAL_EV_DSR ) 		DigiDump( DIGIWAIT, ("\tSERIAL_EV_DSR\n"));
            if ( pWaitExt->History & SERIAL_EV_RLSD ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_RLSD\n"));
            if ( pWaitExt->History & SERIAL_EV_BREAK ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_BREAK\n"));
            if ( pWaitExt->History & SERIAL_EV_ERR ) 		DigiDump( DIGIWAIT, ("\tSERIAL_EV_ERR\n"));
            if ( pWaitExt->History & SERIAL_EV_RING ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_RING\n"));
            if ( pWaitExt->History & SERIAL_EV_PERR ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_PERR\n"));
            if ( pWaitExt->History & SERIAL_EV_RING ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_RING\n"));
            if ( pWaitExt->History & SERIAL_EV_RX80FULL )DigiDump( DIGIWAIT, ("\tSERIAL_EV_RX80FULL\n"));
            if ( pWaitExt->History & SERIAL_EV_EVENT1 ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_EVENT1\n"));
            if ( pWaitExt->History & SERIAL_EV_EVENT2 ) 	DigiDump( DIGIWAIT, ("\tSERIAL_EV_EVENT2\n"));
#endif

            pWaitExt->History = 0;
            DigiReleaseSpinLock( &pWaitExt->Lock );  


            IoSetCancelRoutine( pIrp, NULL );
            pIrp->IoStatus.Information = sizeof( ULONG );
            AsynIoCompleteRequest( pIrp, STATUS_SUCCESS, pExt );

            return;
        }
    }      

    DigiReleaseSpinLock( &pWaitExt->Lock ); 
}  // end SatisfyWait


NTSTATUS
AsynWaitOnMask
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PASYN_WAIT pWaitExt = &pPortExt->Wait;   

    DigiAcquireSpinLock( &pWaitExt->Lock );

    if ( pWaitExt->pPendingIrp )
    {
        DigiDump( DIGIERROR|DIGIWAIT|DIGIIOCTL, ("DigiError: *** Already have a IOCTL_SERIAL_WAIT_ON_MASK Irp %x\n",
                                                 pWaitExt->pPendingIrp ));
        Status = STATUS_INVALID_PARAMETER;
        goto UnLock;                  
    }
		
    if ( pWaitExt->Mask == 0 )   	  
    {
        DigiDump( DIGIERROR|DIGIWAIT|DIGIIOCTL, ("DigiError: *** Wait Mask is 0\n" ));
        Status = STATUS_INVALID_PARAMETER;
        goto UnLock;                  
    }      

   
    // Can the Wait be satisfied immediately ??
    if (pWaitExt->History & pWaitExt->Mask)
    {
        DigiDump( DIGIWAIT, ("DigiAsyn:WaitOnMask: Wait event satisfied because: %x\n", pWaitExt->History & pWaitExt->Mask) );
        Status = STATUS_SUCCESS;
        *(PULONG)pIrp->AssociatedIrp.SystemBuffer = pWaitExt->History & pWaitExt->Mask;
        pIrp->IoStatus.Information = sizeof( ULONG );
        pWaitExt->History = 0;
    }
    else
    {
        DigiDump( DIGIWAIT|DIGIFLOW, ("DigiAsyn:WaitOnMask Wait Irp pended: 0x%x\n",pIrp));
        Status = STATUS_PENDING;
        IoSetCancelRoutine(pIrp, AsynCancelWait);
        IoMarkIrpPending(pIrp);
        SetAsynPendingIrp(&pWaitExt->pPendingIrp,pIrp);
    }   
 UnLock:
    DigiReleaseSpinLock( &pWaitExt->Lock );
   
    return Status;
}   
      

NTSTATUS
AsynSetWaitMask
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)   
{
    ULONG NewMask = *((ULONG *)pIrp->AssociatedIrp.SystemBuffer);
    ULONG OldMask;
    NTSTATUS Status = STATUS_SUCCESS;

    pIrp->IoStatus.Information = 0;

    // Verify settings
    if ( NewMask & ~(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))
    {
        DigiDump( DIGIWAIT, ("    Invalid Wait mask, 0x%x\n", NewMask) );
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    // Complete the pending IOCTL_SERIAL_WAIT_ON_MASK IRP (if any)
    AsynSatisfyWait( pPortExt, 0, TRUE );

    {
        PASYN_WAIT pWaitExt = &pPortExt->Wait;
        DigiAcquireSpinLock( &pWaitExt->Lock );

        OldMask = pWaitExt->Mask;
      
        // Save the new setting   
        pWaitExt->Mask = NewMask;
        pWaitExt->History &= NewMask;
        DigiReleaseSpinLock( &pWaitExt->Lock );
    }      

    // If the mask has changed, adjust the event parameters for S/DXB

    if (( OldMask & SERIAL_EV_TXEMPTY ) != ( NewMask & SERIAL_EV_TXEMPTY ))
        AsynWaitTxEmpty( &pPortExt->Write,(BOOLEAN)( NewMask & SERIAL_EV_TXEMPTY ? TRUE : FALSE ));
		      
    if (( OldMask & SERIAL_EV_RXFLAG ) != ( NewMask & SERIAL_EV_RXFLAG ))
    {
        PASYN_SDXB pSdxbExt = &pPortExt->SdxbValues;
        DigiAcquireSpinLock( &pSdxbExt->Lock );
        if ( NewMask & SERIAL_EV_RXFLAG )
            // Enable special character recognition in the firmware. 
            //  The special character will be the last data byte in the buffer previously received.
            pSdxbExt->PortParams.xflags |= SDXB_XSCR|SDXB_XSCRAFTER;
        else
            pSdxbExt->PortParams.xflags &= ~SDXB_XSCR|SDXB_XSCRAFTER;
        DigiReleaseSpinLock( &pSdxbExt->Lock );
        ( void ) SdxbParam( pPortExt->DsPortHandle, &pSdxbExt->PortParams );
    }
   
 Exit:   
    return Status;
}

NTSTATUS
AsynGetWaitMask
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	pIrp->IoStatus.Information = sizeof(ULONG);
	*((ULONG *)pIrp->AssociatedIrp.SystemBuffer) = pPortExt->Wait.Mask;
	return STATUS_SUCCESS;
}
