/*
 ****************************************************************************
 * 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 ));
		pWaitExt->pPendingIrp = NULL;
		// 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;
   
	DigiDump(DIGICANCEL|DIGIWAIT, ("AsynCancelWaitIrp: Port=0x%x Pending Irp=0x%x\n",
																	pPortExt->iSdxbPort, pWaitExt->pPendingIrp));

	DigiAcquireSpinLock( &pWaitExt->Lock );
	pIrp = pWaitExt->pPendingIrp;
	pWaitExt->pPendingIrp = NULL;
	DigiReleaseSpinLock( &pWaitExt->Lock );

	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->pPendingIrp && ( pWaitExt->History || fCompleteWait ) )
	{
		// Complete the pending wait irp
		PIRP pIrp = pWaitExt->pPendingIrp;

		pWaitExt->pPendingIrp = NULL;
		*(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 );
	}      
	else
	{
		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);
		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;
}
