/*++
 ****************************************************************************
 * 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 Name:

   asynioctl.c

Abstract:

   This module contains the NT IRP_MJ_DEVICE_CONTROL handler routine.

Author:

   Sandy Haapala

--*/

#include "digiasyn.h"
#include "digidga.h"

// Temporary - used in place of uncoded routines!
STATIC __inline NTSTATUS
IoctlToDo
( 
	IN PASYN_PORT_EXT	pPortExt,
	IN	PIRP				pIrp 
)
{
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}

VOID PurgeCompletion(IN PASYN_PORT_EXT pPortExt, IN PIRP pIrp);

VOID
AsynIoctlOnSdxbSync
(
	IN	PASYN_PORT_EXT	pPortExt,
	IN USHORT			SyncSeq
)
{
	PASYN_SDXB		pSdxbExt = &pPortExt->SdxbValues;
	LIST_ENTRY 		CompleteList;

	DigiDump( DIGIIOCTL | DIGISDXBRSP | DIGIFLOW, ("Entering AsynIoctlOnSdxbSync\n") );

	InitializeListHead( &CompleteList );

	// Complete ioctl IRPs that were pending until this sync was received

	DigiAcquireSpinLock( &pSdxbExt->Lock );
	pSdxbExt->SyncSeqAck = SyncSeq;

	while ( !IsListEmpty( &pSdxbExt->PendingIrps) )
	{
		PIRP pIrp;
		PLIST_ENTRY pEntry = RemoveHeadList( &pSdxbExt->PendingIrps );

		pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
		ASSERT( pIrp->Type == IO_TYPE_IRP );
		if ( pIrp->IoStatus.Information <= SyncSeq || pSdxbExt->SyncSeqNum == SyncSeq )
		{
			IoSetCancelRoutine( pIrp, NULL );
			pIrp->IoStatus.Status = STATUS_SUCCESS;
			pIrp->IoStatus.Information = 0;
			InsertTailList(&CompleteList, &pIrp->Tail.Overlay.ListEntry);
		}
		else
		{
			InsertHeadList( &pSdxbExt->PendingIrps, &pIrp->Tail.Overlay.ListEntry );
			break;
		}         
	}      
	DigiReleaseSpinLock( &pSdxbExt->Lock );

	while ( !IsListEmpty( &CompleteList ))
	{ 
		PIRP pIrp;
		PLIST_ENTRY pEntry = RemoveHeadList(&CompleteList);

		pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
		if ((IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_PURGE)
			PurgeCompletion( pPortExt, pIrp );
      
		AsynIoCompleteRequest( pIrp, STATUS_SUCCESS, pPortExt );
	}   
}

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

Description

	Cancel routine for ioctl irps
   
--*/
{
	PASYN_PORT_EXT	pPortExt = pDeviceObject->DeviceExtension;
	PASYN_SDXB		pSdxbExt = &pPortExt->SdxbValues;

	IoReleaseCancelSpinLock( pIrp->CancelIrql );

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

	DigiAcquireSpinLock( &pSdxbExt->Lock );

	// Find the Irp on the pending queue
	if ( RemoveIrpFromQueue( &pSdxbExt->PendingIrps, pIrp ) )
	{
		DigiDump( DIGICANCEL|DIGIIOCTL , ("\tCancelling ioctl Irp: 0x%x\n", pIrp ));

		// The IRP is all ours now, let everything run.
		DigiReleaseSpinLock( &pSdxbExt->Lock );

		pIrp->IoStatus.Information = 0;
		AsynIoCompleteRequest( pIrp, STATUS_CANCELLED, pPortExt );
	}
	else      
	{
		DigiDump(DIGICANCEL|DIGIIOCTL, ("\tIrp:%x was not found.\n", pIrp));
		DigiReleaseSpinLock( &pSdxbExt->Lock );
	}
}


VOID
AsynCancelIoctlIrps
(
	IN PASYN_SDXB      pSdxbExt  
)
/*++

Description

	Called to cancel all of the ioctl irps on the pending queue.
   
--*/

{
	LIST_ENTRY CancelList;

	DigiDump( DIGICANCEL, ("Entering AsynCancelIoctlIrps\n") );

	InitializeListHead( &CancelList );

	DigiAcquireSpinLock( &pSdxbExt->Lock );   
   
	// Mark each of the IRPs as cancelled and place on a queue to
	// complete after the spinlock can be released.   
	while ( !IsListEmpty( &pSdxbExt->PendingIrps) )
	{
		PIRP pIrp;
		PLIST_ENTRY pEntry = RemoveHeadList( &CancelList );
		
		pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
		ASSERT( pIrp->Type == IO_TYPE_IRP );
		pIrp->Cancel = TRUE;
		IoSetCancelRoutine( pIrp, NULL );
		pIrp->IoStatus.Information = 0;
	   InsertTailList( &CancelList, &pIrp->Tail.Overlay.ListEntry );
	}      
	DigiReleaseSpinLock( &pSdxbExt->Lock );

	// Now complete each IRP on the cancel queue   
	while ( !IsListEmpty( &CancelList ))
	{ 
		PIRP pIrp;
		PLIST_ENTRY pEntry = RemoveHeadList(&CancelList);

		pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
		DigiDump( DIGIIOCTL|DIGICANCEL, ("AsynCancelIoctlIrps: completed IRP %x with status=STATUS_CANCELLED\n",pIrp));
		AsynIoCompleteRequest( pIrp, STATUS_CANCELLED, NULL );
	}   
}  


STATIC __inline VOID
QueueIoctl(
	IN	PASYN_SDXB	pSdxbExt,
	IN	PIRP			pIrp 
)
/*++

Description

	Called to mark a ioctl as pending and to put the irp on the ports's 
   pending queue.
   
      
--*/
{
	// Save sequence number for sync response handling
	pIrp->IoStatus.Information = pSdxbExt->SyncSeqNum;
	DigiDump( DIGIFLOW, ("DigiAsyn:QueueIoctl Irp pended: 0x%x\n",pIrp));
	IoSetCancelRoutine(pIrp, AsynCancelIoctl);
	IoMarkIrpPending(pIrp);
	DigiAcquireSpinLock( &pSdxbExt->Lock );
	InsertTailList(&pSdxbExt->PendingIrps, &pIrp->Tail.Overlay.ListEntry);
	DigiReleaseSpinLock( &pSdxbExt->Lock );
}  


STATIC NTSTATUS
SetBaudRate(
   IN PASYN_PORT_EXT    pPortExt,      // Port specific information.
   IN PIRP              pIrp           // Associated IRP
)
{
	NTSTATUS    Status;                 // Call return value.
	ULONG			BaudRate = ((PSERIAL_BAUD_RATE)(pIrp->AssociatedIrp.SystemBuffer))->BaudRate;

	DigiDump( DIGIIOCTL, ("DigiAsyn!SetBaudRate: Port=0x%x BaudRate=%d\n", pPortExt->iSdxbPort, BaudRate ));

// TODO: Are these defined in a firmware header file anywhere?   
#define SDXB_MAX_BAUDRATE	1228800
#define SDXB_MIN_BAUDRATE			4   
	if ( BaudRate > SDXB_MAX_BAUDRATE || BaudRate < SDXB_MIN_BAUDRATE )
	{
		Status = STATUS_INVALID_PARAMETER;
		goto Exit;
	}   

	{
		PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;

		if ( BaudRate == pSdxbExt->PortParams.speed )
		{
			// BaudRate is already set - nothing else to do.
			Status = STATUS_SUCCESS;
         goto Exit;
		}  
		pSdxbExt->PortParams.speed = BaudRate;
      
		// Send baudrate change to sdxb
		Status = SdxbParam( pPortExt->DsPortHandle, &pSdxbExt->PortParams );
		if ( NT_ERROR( Status ) )
		{
			goto Exit;
		}
      else
      {
			// Put IRP on pending queue
			QueueIoctl( pSdxbExt, pIrp );      
			// Request sync
			(void)SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
			// Irp will complete sometime later.
			Status = STATUS_PENDING;
		}
	}
Exit:
	return Status;	   
}


STATIC NTSTATUS
GetBaudRate
(
	IN PASYN_PORT_EXT    pPortExt,      // Port specific information.
	IN PIRP              pIrp
)   
{
	PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
   
	DigiDump(DIGIIOCTL, ("DigiAsyn!GetBaudRate for PortNo 0x%x = %d\n", pPortExt->iSdxbPort,pSdxbExt->PortParams.speed));
	((PSERIAL_BAUD_RATE)(pIrp->AssociatedIrp.SystemBuffer))->BaudRate = pSdxbExt->PortParams.speed;
	pIrp->IoStatus.Information = sizeof(SERIAL_BAUD_RATE);
      
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
SetQueueSize
(
	IN PASYN_PORT_EXT    pPortExt,      // Port specific information.
	IN PIRP              pIrp           // Associated IRP
)
{
   DigiDump( DIGIIOCTL, ("DigiAsyn!SetQueueSize: PortNo=0x%x In=0x%x Out=x%x\n", 
   				pPortExt->iSdxbPort,
					((PSERIAL_QUEUE_SIZE)pIrp->AssociatedIrp.SystemBuffer)->InSize,
               ((PSERIAL_QUEUE_SIZE)pIrp->AssociatedIrp.SystemBuffer)->OutSize));

	// Save insize for use in receive data flow control
	AsynReadSetInQueueSize( pPortExt, ((PSERIAL_QUEUE_SIZE)pIrp->AssociatedIrp.SystemBuffer)->InSize );

	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}   


NTSTATUS
SetLineControl
(
	IN PASYN_PORT_EXT    pPortExt,      // Port specific information.
	IN PIRP              pIrp           // Associated IRP
)
{
	NTSTATUS    Status = STATUS_SUCCESS;

	pIrp->IoStatus.Information = 0;
	{
		PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
		USHORT		ProposedCflags = SDXB_CREAD;
		USHORT      ProposedXflags = pSdxbExt->PortParams.xflags & ~(SDXB_XPAR | SDXB_XEDATA);
		USHORT      ProposedIflags = pSdxbExt->PortParams.iflags & ~(SDXB_INPCK);
		PSERIAL_LINE_CONTROL pLineControl = (PSERIAL_LINE_CONTROL)pIrp->AssociatedIrp.SystemBuffer;      

		if ( pLineControl->Parity != NO_PARITY )
		{
			ProposedIflags |= SDXB_INPCK;
			   
			switch (pLineControl->Parity)
			{         
				case ODD_PARITY:
					ProposedCflags |= SDXB_PARENB | SDXB_PARODD;
					break;
				case EVEN_PARITY:
					ProposedCflags |= SDXB_PARENB;
					break;
				case MARK_PARITY:
					if ( pSdxbExt->PortCapabilities & CVEC_MARK_PARITY )
					{
						ProposedCflags |= SDXB_PARENB | SDXB_PARODD;
						ProposedXflags |= SDXB_XPAR;
					}
					else
						Status = STATUS_INVALID_PARAMETER;                  
					break;
				case SPACE_PARITY:
					if ( pSdxbExt->PortCapabilities & CVEC_SPACE_PARITY )
					{
						ProposedCflags |= SDXB_PARENB;
						ProposedXflags |= SDXB_XPAR;
					}
					else
						Status = STATUS_INVALID_PARAMETER;                  
					break;
		      default:
					Status = STATUS_INVALID_PARAMETER;
			}

			if ( NT_ERROR( Status ))
				goto Exit;
            
			// If not doing Microsoft's LSRMST_INSERT or Line Error replacement, 
			//  then receive error chars unaltered.
			if (!(pSdxbExt->PortParams.xflags & (SDXB_XMSPAR | SDXB_XMSERC)))
			{
				ProposedXflags |= SDXB_XEDATA;
			}            
		}                     
      
		switch (pLineControl->WordLength)
		{
			case 5:
				ProposedCflags |= SDXB_CS5;
				break;
			case 6:
				ProposedCflags |= SDXB_CS6;
				break;
			case 7:
				ProposedCflags |= SDXB_CS7;
				break;
			case 8:
				ProposedCflags |= SDXB_CS8;
				break;
			default:
				Status = STATUS_INVALID_PARAMETER;
				goto Exit;
		}

		switch (pLineControl->StopBits)
		{
			case STOP_BIT_1:
				break;
			case STOP_BITS_1_5:
				if (pLineControl->WordLength != 5)
				{
					Status = STATUS_INVALID_PARAMETER;
					goto Exit;
				}
				else
					ProposedCflags |= SDXB_CSTOPB;
				break;
			case STOP_BITS_2:
				if (pLineControl->WordLength == 5)
				{
					Status = STATUS_INVALID_PARAMETER;
					goto Exit;
				}
				else
					ProposedCflags |= SDXB_CSTOPB;
				break;
			default:
				Status = STATUS_INVALID_PARAMETER;
				goto Exit;
	   }

	   DigiDump( DIGIIOCTL, ("DigiAsyn!SetLineControl: PortNo=0x%x xflags=%x Proposed=%x\n",   																		
   					pPortExt->iSdxbPort,pSdxbExt->PortParams.xflags,ProposedXflags ));

		DigiAcquireSpinLock( &pSdxbExt->Lock );

		// Are the new settings different than the current settings?
		if ((ProposedCflags == pSdxbExt->PortParams.cflags)
			&& (ProposedXflags == pSdxbExt->PortParams.xflags)
			&& (ProposedIflags == pSdxbExt->PortParams.iflags))
		{
			// Settings are the same - we're done
			Status = STATUS_SUCCESS;
			DigiReleaseSpinLock( &pSdxbExt->Lock );
         goto Exit;
		}
		else
		{

			pSdxbExt->PortParams.cflags = ProposedCflags;
			pSdxbExt->PortParams.xflags = ProposedXflags;
			pSdxbExt->PortParams.iflags = ProposedIflags;
			DigiReleaseSpinLock( &pSdxbExt->Lock );

			Status = SdxbParam( pPortExt->DsPortHandle, &pSdxbExt->PortParams );
			if ( NT_ERROR( Status ) )
			{
				goto Exit;
			}
			else
			{
				// Put IRP on pending queue
				QueueIoctl( pSdxbExt, pIrp );      
				// Override status.  Irp will complete sometime later after sync response.
				Status = STATUS_PENDING;
            // Request sync
				(void) SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
			}         
		}         
	}
   
Exit:
	return Status;	   
}

STATIC NTSTATUS
GetLineControl
(
	IN PASYN_PORT_EXT    pPortExt,      
	IN PIRP              pIrp
)   
{
	{
		PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
		PSERIAL_LINE_CONTROL pLineControl = (PSERIAL_LINE_CONTROL)pIrp->AssociatedIrp.SystemBuffer;
		USHORT			Cflags = pSdxbExt->PortParams.cflags;
		USHORT      	Xflags = pSdxbExt->PortParams.xflags;

		switch ( Cflags & SDXB_CSIZE )
		{
			case SDXB_CS5:
				pLineControl->WordLength = 5;
				break;
			case SDXB_CS6:
				pLineControl->WordLength = 6;
				break;
			case SDXB_CS7:
				pLineControl->WordLength = 7;
				break;
			case SDXB_CS8:
				pLineControl->WordLength = 8;
				break;
	   }
      
		switch ( Cflags & SDXB_CSTOPB )
		{
			case 0x00:
				pLineControl->StopBits = STOP_BIT_1;
				break;
			case SDXB_CSTOPB:
				pLineControl->StopBits = pLineControl->WordLength == 5 ? STOP_BITS_1_5 : STOP_BITS_2;					         		
				break;
		}
      
		if (!( Cflags & SDXB_PARENB))
		{
			pLineControl->Parity = NO_PARITY;
		}
		else
		{
			if (!( Xflags & SDXB_XPAR))
			{
				pLineControl->Parity = Cflags & SDXB_PARODD ? ODD_PARITY : EVEN_PARITY;
			}
			else
			{
				pLineControl->Parity = Cflags & SDXB_PARODD ? MARK_PARITY : SPACE_PARITY;
			}
		}
		pIrp->IoStatus.Information = sizeof(*pLineControl);
	}            
   
	return STATUS_SUCCESS;
}

STATIC NTSTATUS
SetBreakOn
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
#define BREAK_DURATION_INFINITE 0xffff
	(void)SdxbBreak( pPortExt->DsPortHandle, BREAK_DURATION_INFINITE );
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}   


STATIC NTSTATUS
SetBreakOff
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
#define BREAK_OFF	0
	(void)SdxbBreak( pPortExt->DsPortHandle, BREAK_OFF );
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}   


STATIC NTSTATUS
SendImmediateChar
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	USHORT			Estat;
	PASYN_SDXB		pSdxbExt = &pPortExt->SdxbValues;
	NTSTATUS 		Status;
   
	DigiAcquireSpinLock( &pSdxbExt->Lock );
	Estat = pSdxbExt->Estat;
	DigiReleaseSpinLock( &pSdxbExt->Lock );

	// Check for pending immediate write
	if ( Estat & SDXB_TXI )
	{
		Status = STATUS_INVALID_PARAMETER;
		goto Exit;
	}	      

	{
		PUCHAR pIrpBuffer = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer;

		Status = SdxbScimmed( pPortExt->DsPortHandle, *pIrpBuffer );

		if ( Status == STATUS_PENDING )
			Status = STATUS_SUCCESS;

		if ( NT_SUCCESS(Status) )
			pIrp->IoStatus.Information = 1;
	}         
Exit:   
	return Status;
}   


STATIC NTSTATUS
SetTimeouts
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	PSERIAL_TIMEOUTS pTimeouts = (PSERIAL_TIMEOUTS)pIrp->AssociatedIrp.SystemBuffer;      

	AsynSetReadTimeouts( &pPortExt->Read, pTimeouts );
	AsynSetWriteTimeout( &pPortExt->Write, pTimeouts );
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}   


STATIC NTSTATUS
GetTimeouts
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	PSERIAL_TIMEOUTS pTimeouts = (PSERIAL_TIMEOUTS)pIrp->AssociatedIrp.SystemBuffer;      

	AsynGetReadTimeouts( &pPortExt->Read, pTimeouts );
	AsynGetWriteTimeout( &pPortExt->Write, pTimeouts );
	pIrp->IoStatus.Information = sizeof( *pTimeouts );
	return STATUS_SUCCESS;
}   


STATIC NTSTATUS
SetDtr
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	NTSTATUS    Status;                 // Call return value.

	{
		PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;

		DigiAcquireSpinLock( &pSdxbExt->Lock );   	

		if (  ( pSdxbExt->Handflow.ControlHandShake & SERIAL_DTR_MASK ) == SERIAL_DTR_HANDSHAKE )
		{	
			Status = STATUS_INVALID_PARAMETER;
			DigiReleaseSpinLock( &pSdxbExt->Lock );
			goto Exit;
		}         

		if ( pPortExt->PnpModemDetectHandle != NULL )
			// DTR high could signal that PnP device detection is in progress.
			PnpModemDtrHi( pPortExt->PnpModemDetectHandle );

		if ( pSdxbExt->Mout & SDXB_DTR )
		{   
			// DTR is already set - nothing more to do here.
			Status = STATUS_SUCCESS;
			DigiReleaseSpinLock( &pSdxbExt->Lock );
			goto Exit;
		}

		// Set DTR   
		pSdxbExt->Mout |= SDXB_DTR;
		DigiReleaseSpinLock( &pSdxbExt->Lock );

		// Send the modem request followed by a sync request
		Status = SdxbMstat( pPortExt->DsPortHandle, SDXB_DTR|SDXB_RTS|SDXB_CTS|SDXB_DSR|SDXB_RI|SDXB_DCD, pSdxbExt->Mout, FALSE );
		if ( NT_ERROR( Status ) )
      {
			goto Exit;
		}
      else
      {               
			// Put IRP on pending queue
			QueueIoctl( pSdxbExt, pIrp );      
			// Override status.  Irp will complete sometime later.
			Status = STATUS_PENDING;
			// Request sync
			(void) SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
		} 
	}	
Exit:   
	if ( Status != STATUS_PENDING )
		pIrp->IoStatus.Information = 0;
	return Status;
}


STATIC NTSTATUS
ClearDtr
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	NTSTATUS    Status;                 // Call return value.
	PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;

	DigiAcquireSpinLock( &pSdxbExt->Lock );   	

	if (  ( pSdxbExt->Handflow.ControlHandShake & SERIAL_DTR_MASK ) == SERIAL_DTR_HANDSHAKE )
	{	
		Status = STATUS_INVALID_PARAMETER;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}         

	if ( pPortExt->PnpModemDetectHandle != NULL )
		// DTR low could signal that PnP device detection is in progress.
		PnpModemDtrLo( pPortExt->PnpModemDetectHandle );

	if ( !(pSdxbExt->Mout & SDXB_DTR) )
	{   
		// Already cleared - nothing more to do here   
		Status = STATUS_SUCCESS;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}

	// Clear DTR   
	pSdxbExt->Mout &= ~SDXB_DTR;
	DigiReleaseSpinLock( &pSdxbExt->Lock );

	// Send the modem request followed by a sync request
	Status = SdxbMstat( pPortExt->DsPortHandle, SDXB_DTR|SDXB_RTS|SDXB_CTS|SDXB_DSR|SDXB_RI|SDXB_DCD, pSdxbExt->Mout, FALSE );
	if ( NT_ERROR( Status ) )
   {
		goto Exit;
	}            
   else
   {
		// Put IRP on pending queue
		QueueIoctl( pSdxbExt, pIrp );      
		// Override status.  Irp will complete after sync response.
		Status = STATUS_PENDING;
		// Request sync
		(void) SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
	}    
Exit:
	if ( Status != STATUS_PENDING )
		pIrp->IoStatus.Information = 0;

	return Status;
}


STATIC NTSTATUS
IoResetDevice
( 
	IN PASYN_PORT_EXT    pPortExt,      // Port specific information.
	IN PIRP              pIrp           // Associated IRP
)
{
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
SetRts
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	NTSTATUS    Status;                 // Call return value.

	PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;

	DigiAcquireSpinLock( &pSdxbExt->Lock );   	

	if ( ( pSdxbExt->Handflow.ControlHandShake & SERIAL_RTS_MASK ) == SERIAL_RTS_HANDSHAKE ||
		  ( pSdxbExt->Handflow.ControlHandShake & SERIAL_RTS_MASK ) == SERIAL_TRANSMIT_TOGGLE )
	{	
   	Status = STATUS_INVALID_PARAMETER;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}         
   
	if ( pPortExt->PnpModemDetectHandle != NULL)
	{
		// RTS high could signal that PnP device detection is in progress.
		PnpModemRtsHi( pPortExt->PnpModemDetectHandle );
		if ( PnpModemIsEnumerated( pPortExt->PnpModemDetectHandle ))
		{
			// Successful PnP detection: Get the modem id string and insert it into the receive buffer queue
			PRX_BUF	pRxBuf = DigiAllocMem( NonPagedPool, pPortExt->SdxbValues.PortParams.maxlen + sizeof(RX_BUF));
			if ( pRxBuf != NULL )
			{
				ULONG	cbData = pPortExt->SdxbValues.PortParams.maxlen;
						         
				if (STATUS_SUCCESS == PnpModemGetId( (PCHAR)pRxBuf+sizeof(RX_BUF), &cbData ))
				{
					DigiDump(DIGIPNP,("DigiAsyn:SetRts Retrieved pnp modem string of length 0x%x\n", cbData));
					if ( cbData!=0 )
						AsynOnData( pPortExt, pRxBuf, cbData );
					else
						DigiFreeMem( pRxBuf );                  
				}   					            
			}               
		}
	}         
   
   
	if ( pSdxbExt->Mout & SDXB_RTS )
	{   
		Status = STATUS_SUCCESS;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}

	// Set RTS   
	pSdxbExt->Mout |= SDXB_RTS;
	DigiReleaseSpinLock( &pSdxbExt->Lock );
   
	// Send the modem request followed by a sync request
	Status = SdxbMstat( pPortExt->DsPortHandle, SDXB_DTR|SDXB_RTS|SDXB_CTS|SDXB_DSR|SDXB_RI|SDXB_DCD, pSdxbExt->Mout, FALSE );
	if ( NT_ERROR( Status ) )
   {
		goto Exit;
	}            
   else
   {
		// Put IRP on pending queue
		QueueIoctl( pSdxbExt, pIrp );      
		// Override status.  Irp will complete after sync response.
		Status = STATUS_PENDING;
		// Request sync
		(void) SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
	}    
     
Exit:
	if ( Status != STATUS_PENDING )
		pIrp->IoStatus.Information = 0;

	return Status;
}


STATIC NTSTATUS
ClearRts
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)
{
	NTSTATUS    Status;                 // Call return value.
	PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;

	DigiAcquireSpinLock( &pSdxbExt->Lock );   	

	if ( ( pSdxbExt->Handflow.ControlHandShake & SERIAL_RTS_MASK ) == SERIAL_RTS_HANDSHAKE ||
		  ( pSdxbExt->Handflow.ControlHandShake & SERIAL_RTS_MASK ) == SERIAL_TRANSMIT_TOGGLE )
	{	
		Status = STATUS_INVALID_PARAMETER;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}         
   
	if ( pPortExt->PnpModemDetectHandle != NULL)
		// RTS low could signal that PnP device detection is in progress.
		PnpModemRtsLo( pPortExt->PnpModemDetectHandle );

	if ( !(pSdxbExt->Mout & SDXB_RTS) )
	{   
		Status = STATUS_SUCCESS;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}

	// Clear RTS   
	pSdxbExt->Mout &= ~SDXB_RTS;
	DigiReleaseSpinLock( &pSdxbExt->Lock );
   
	// Send the modem request followed by a sync request
	Status = SdxbMstat( pPortExt->DsPortHandle, SDXB_DTR|SDXB_RTS|SDXB_CTS|SDXB_DSR|SDXB_RI|SDXB_DCD, pSdxbExt->Mout, FALSE );
	if ( NT_ERROR( Status ) )
   {
		goto Exit;
	}            
   else
   {
		// Put IRP on pending queue
		QueueIoctl( pSdxbExt, pIrp );      
		// Override status.  Irp will complete after sync response.
		Status = STATUS_PENDING;
		// Request sync
		(void) SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
	}    
	      
Exit:
	if ( Status != STATUS_PENDING )
		pIrp->IoStatus.Information = 0;
      
	return Status;
}

STATIC NTSTATUS
GetDtrRts
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)   
{
	PULONG	  pDtrRtsState = pIrp->AssociatedIrp.SystemBuffer;
	PASYN_SDXB pSdxbExt = &pPortExt->SdxbValues;
		
	*pDtrRtsState = 0;

	DigiAcquireSpinLock( &pSdxbExt->Lock );

	if ( pSdxbExt->Mstat & SDXB_DTR )
		*pDtrRtsState |= SERIAL_DTR_STATE;

	if ( pSdxbExt->Mstat & SDXB_RTS )
		*pDtrRtsState |= SERIAL_RTS_STATE;

	DigiReleaseSpinLock( &pSdxbExt->Lock );       

	pIrp->IoStatus.Information = sizeof(*pDtrRtsState);
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
SetXon
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)   
{
	(void)SdxbPause( pPortExt->DsPortHandle, 0, SDXB_OPS );
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
SetXoff
(
	IN	PASYN_PORT_EXT		pPortExt,
	IN	PIRP					pIrp
)   
{
	(void)SdxbPause( pPortExt->DsPortHandle, SDXB_OPS, 0 );
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;
}

STATIC NTSTATUS
SetChars
(
	IN PASYN_PORT_EXT		pPortExt,
	IN PIRP					pIrp
)
{
	NTSTATUS				Status = STATUS_SUCCESS;
	PSERIAL_CHARS pNewChars = pIrp->AssociatedIrp.SystemBuffer;
	PASYN_SDXB			pSdxbExt = &pPortExt->SdxbValues;

	DigiAcquireSpinLock( &pSdxbExt->Lock );
#if TODO
	// Make sure the xon and xoff characters aren't the same
	// as the escape character.
	if ( pSdxbExt->EscapeChar &&
		 (pSdxbExt->EscapeChar == pNewChars->XonChar ||
		  pSdxbExt->EscapeChar == pNewChars->XonChar ))
	{
		Status = STATUS_INVALID_PARAMETER;
		DigiReleaseSpinLock( &pSdxbExt->Lock );      
		goto Exit;
	}                    
#endif
	// save EofChar, though we do nothing with it
	pSdxbExt->EofChar = pNewChars->EofChar;
	if ( (pSdxbExt->PortParams.xon == pNewChars->XonChar) &&
 		 (pSdxbExt->PortParams.xoff == pNewChars->XoffChar) &&
 		 (pSdxbExt->PortParams.mser == pNewChars->ErrorChar) &&
 		 (pSdxbExt->PortParams.msbr == pNewChars->BreakChar) &&
 		 (pSdxbExt->PortParams.spcr == pNewChars->EventChar) )
	{
		// No change - we're done... 
		DigiReleaseSpinLock( &pSdxbExt->Lock );      
		goto Exit;
	}
   
	// Update SDXB with the new characters.
	pSdxbExt->PortParams.xon = pNewChars->XonChar;
	pSdxbExt->PortParams.xoff = pNewChars->XoffChar;
	pSdxbExt->PortParams.mser = pNewChars->ErrorChar;
	pSdxbExt->PortParams.msbr = pNewChars->BreakChar;
	pSdxbExt->PortParams.spcr = pNewChars->EventChar;
   
	// Parity/Framing error replacement char
	if (pSdxbExt->Handflow.FlowReplace & SERIAL_ERROR_CHAR)
	{
		pSdxbExt->PortParams.xflags |= SDXB_XMSERC;
		pSdxbExt->PortParams.xflags &= ~SDXB_XEDATA;
	}
	else
	{
		pSdxbExt->PortParams.xflags &= ~SDXB_XMSERC;
		pSdxbExt->PortParams.xflags |= SDXB_XEDATA;
	}

   //
   // Break replacement char
   //
	if (pSdxbExt->Handflow.FlowReplace & SERIAL_ERROR_BREAK)
	{
		pSdxbExt->PortParams.xflags |= SDXB_XMSBRC;
	}
	else
	{
		pSdxbExt->PortParams.xflags &= ~SDXB_XMSBRC;
	}
	DigiReleaseSpinLock( &pSdxbExt->Lock );
   
	Status = SdxbParam( pPortExt->DsPortHandle, &pSdxbExt->PortParams );
	if ( NT_ERROR( Status ) )
	{
		goto Exit;
	}
   // Put IRP on pending queue
   QueueIoctl( pSdxbExt, pIrp );
   
	// Override status.  Irp will complete after sync responxe
	Status = STATUS_PENDING;

	// Request sync
	(void) SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );

Exit:
	return Status;   

}   

STATIC NTSTATUS
GetChars
(
	IN PASYN_PORT_EXT    pPortExt,      
	IN PIRP              pIrp
)   
{
	PSERIAL_CHARS pChars = pIrp->AssociatedIrp.SystemBuffer;
	PASYN_SDXB			pSdxbExt = &pPortExt->SdxbValues;
	pIrp->IoStatus.Information = sizeof(*pChars);
	DigiAcquireSpinLock( &pSdxbExt->Lock );
	pChars->EofChar = pSdxbExt->EofChar;
	pChars->XonChar = pSdxbExt->PortParams.xon;
	pChars->XoffChar = pSdxbExt->PortParams.xoff;
	pChars->ErrorChar = pSdxbExt->PortParams.mser;
	pChars->BreakChar = pSdxbExt->PortParams.msbr;
	pChars->EventChar = pSdxbExt->PortParams.spcr;
	DigiReleaseSpinLock( &pSdxbExt->Lock );
	return STATUS_SUCCESS;
}


NTSTATUS
AsynUpdatePortHandflow
(
	IN HANDLE		DsPortHandle,
	IN PASYN_SDXB	pSdxbExt
)
{
	NTSTATUS	Status;
   
	// Update the Port settings
	UCHAR 	Mflow = 0;

	// Preserve setting which are not being changed.
	UCHAR 	Mout = pSdxbExt->Mout;
	USHORT   Iflags = pSdxbExt->PortParams.iflags & ~(SDXB_IXON | SDXB_IXOFF);
	USHORT	Xflags = pSdxbExt->PortParams.xflags;
	ULONG		FlowReplace = pSdxbExt->Handflow.FlowReplace;
	ULONG		ControlHandShake = pSdxbExt->Handflow.ControlHandShake;

	switch ( FlowReplace & SERIAL_RTS_MASK )
	{
		case SERIAL_RTS_HANDSHAKE:
			Mflow |= SDXB_RTS;
			break;
		case SERIAL_RTS_CONTROL:
			Mout |= SDXB_RTS;
			break;
		// TODO: does firmware support RTS toggle            
		case SERIAL_TRANSMIT_TOGGLE:
		default:            	                        
			Mout &= ~SDXB_RTS;
			break;
	}

	switch ( ControlHandShake & SERIAL_DTR_MASK )
	{
		case SERIAL_DTR_HANDSHAKE:
			Mflow |= SDXB_DTR;
			break;
		case SERIAL_DTR_CONTROL:
			Mout |= SDXB_DTR;
			break;
		default:
			Mout &= ~SDXB_DTR;
			break;
	}
   
	if ( ControlHandShake & SERIAL_CTS_HANDSHAKE )
		Mflow |= SDXB_CTS;

	if ( ControlHandShake & SERIAL_DSR_HANDSHAKE )
		Mflow |= SDXB_DSR;

	if ( ControlHandShake & SERIAL_DCD_HANDSHAKE )
		Mflow |= SDXB_DCD;

	if ( FlowReplace & SERIAL_AUTO_TRANSMIT )
		Iflags |= SDXB_IXON;
   
	if ( FlowReplace & SERIAL_AUTO_RECEIVE )
		Iflags |= SDXB_IXOFF;         
      
	if ( FlowReplace & SERIAL_ERROR_CHAR )
	{
		Xflags |= SDXB_XMSERC;
		Xflags &= ~SDXB_XEDATA;
	}      	         
	else
	{
		Xflags &= ~SDXB_XMSERC;
		Xflags |= SDXB_XEDATA;
	}

	// Break replacement char
	if ( FlowReplace & SERIAL_ERROR_BREAK )
		Xflags |= SDXB_XMSBRC;
	else
		Xflags &= ~SDXB_XMSBRC;

	if ( FlowReplace & SERIAL_NULL_STRIPPING )
		Xflags |= SDXB_XSTRIP;
	else
		Xflags &= ~SDXB_XSTRIP;                  

	pSdxbExt->PortParams.iflags = Iflags;
	pSdxbExt->PortParams.xflags = Xflags;
	pSdxbExt->PortParams.mflow = Mflow;
	pSdxbExt->Mout = Mout;         
	                  
	Status = SdxbParam( DsPortHandle, &pSdxbExt->PortParams );
	if ( NT_ERROR( Status ) )
	{
      goto Exit;
	}
   
	Status = SdxbMstat( DsPortHandle, SDXB_DTR|SDXB_RTS|SDXB_CTS|SDXB_DSR|SDXB_RI|SDXB_DCD, pSdxbExt->Mout, FALSE );
   
Exit:
	return Status;   

}	   

STATIC NTSTATUS
SetHandflow
(
	IN PASYN_PORT_EXT    pPortExt,      
	IN PIRP              pIrp
)   
{
	NTSTATUS				Status = STATUS_SUCCESS;
	PSERIAL_HANDFLOW 	pHandflow = pIrp->AssociatedIrp.SystemBuffer;
	PASYN_SDXB			pSdxbExt = &pPortExt->SdxbValues;

	// Make sure that there are no invalid bits set in the control and handshake.
	if( (pHandflow->ControlHandShake & SERIAL_CONTROL_INVALID) ||  
		 (pHandflow->ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_MASK )
	{
		Status = STATUS_INVALID_PARAMETER;
		DigiDump( DIGIIOCTL, ("      Invalid ControlHandShake, returning STATUS_INVALID_PARAMETER\n!") );
		goto Exit;
	}
	if( pHandflow->FlowReplace & SERIAL_FLOW_INVALID )
	{
		Status = STATUS_INVALID_PARAMETER;
		DigiDump( DIGIIOCTL, ("      Invalid FlowReplace, returning STATUS_INVALID_PARAMETER!\n") );
		goto Exit;
	}

	// Insure sane Xon/Xoff limits.
	if ((pHandflow->XonLimit < 0) || (pHandflow->XoffLimit < 0))
	{
		Status = STATUS_INVALID_PARAMETER;
		goto Exit;
	}

	DigiAcquireSpinLock( &pSdxbExt->Lock );
#if TODO   
	// Don't allow NT's error replacement AND line/modem status insertion.
	if ((pSdxbExt->EscapeChar) && (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)))
	{
		Status = STATUS_INVALID_PARAMETER;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}
#endif
	// Continue only if settings have changed
	if ( ( pHandflow->ControlHandShake == pSdxbExt->Handflow.ControlHandShake ) &&
			( pHandflow->FlowReplace == pSdxbExt->Handflow.FlowReplace ) &&
			( pHandflow->XonLimit == pSdxbExt->Handflow.XonLimit ) &&
			( pHandflow->XoffLimit == pSdxbExt->Handflow.XoffLimit ) )
	{
		Status = STATUS_SUCCESS;
		DigiReleaseSpinLock( &pSdxbExt->Lock );
		goto Exit;
	}

	pSdxbExt->Handflow = *pHandflow;
	DigiReleaseSpinLock( &pSdxbExt->Lock );
   
   Status = AsynUpdatePortHandflow( pPortExt->DsPortHandle, pSdxbExt );
   
   if ( NT_ERROR(Status) )
	{
      goto Exit;
	}   		   

	// Put IRP on pending queue
	QueueIoctl( pSdxbExt, pIrp );      
      
	// Override status.  Irp will complete after sync response
	Status = STATUS_PENDING;
	(void) SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
   
Exit:
	return Status;   
}


STATIC NTSTATUS
GetHandflow
(
	IN PASYN_PORT_EXT    pPortExt,      
	IN PIRP              pIrp
)   
{
	PSERIAL_HANDFLOW pHandflow = pIrp->AssociatedIrp.SystemBuffer;
	*pHandflow = pPortExt->SdxbValues.Handflow;
	pIrp->IoStatus.Information = sizeof(*pHandflow);
   
   // Note: since we return the handflow settings from a saved structure - there's potential
   // for the SDXB settings to be out-of-sync. So set the handflow based on the saved settings.
   DigiAcquireSpinLock( &pPortExt->SdxbValues.Lock );
   AsynUpdatePortHandflow( pPortExt->DsPortHandle, &pPortExt->SdxbValues );
   DigiReleaseSpinLock( &pPortExt->SdxbValues.Lock );
   
	return STATUS_SUCCESS;
}

STATIC NTSTATUS
GetModemStatus
(
	IN PASYN_PORT_EXT    pPortExt,      
	IN PIRP              pIrp
)   
{
	PULONG	  pModemStatus = pIrp->AssociatedIrp.SystemBuffer;
	UCHAR Mstat = pPortExt->SdxbValues.Mstat;

	*pModemStatus = 0;

	if (Mstat & SDXB_CTS)
		*pModemStatus |= SERIAL_CTS_STATE;

	if (Mstat & SDXB_DSR)
		*pModemStatus |= SERIAL_DSR_STATE;

	if (Mstat & SDXB_RI)
		*pModemStatus |= SERIAL_RI_STATE;

	if (Mstat & SDXB_DCD)
		*pModemStatus |= SERIAL_DCD_STATE;

	DigiDump(DIGIIOCTL, ("\tModem Status=0x%x\n", *pModemStatus));

	pIrp->IoStatus.Information = sizeof(*pModemStatus);
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
GetCommStatus
(
	IN PASYN_PORT_EXT		pPortExt,      
	IN PIRP              pIrp
)
{
	PSERIAL_STATUS	  	pCommStatus = pIrp->AssociatedIrp.SystemBuffer;
	PASYN_SDXB			pSdxbExt = &pPortExt->SdxbValues;

	pCommStatus->Errors = pSdxbExt->CommErr;
	pSdxbExt->CommErr = 0;

	pCommStatus->HoldReasons = 0;

	if ((pSdxbExt->Handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
	 !(pSdxbExt->Mstat & SDXB_CTS))
	{
		pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
	}

	if ((pSdxbExt->Handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) &&
	 !(pSdxbExt->Mstat & SDXB_DSR))
	{
		pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
	}

	if ((pSdxbExt->Handflow.ControlHandShake & SERIAL_DCD_HANDSHAKE) &&
	 !(pSdxbExt->Mstat & SDXB_DCD))
	{
		pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
	}

	if (pSdxbExt->Estat & SDXB_TXF)
	{
		pCommStatus->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;
	}

	if (pSdxbExt->Estat & (SDXB_OPS | SDXB_OPH) )
	{
		pCommStatus->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;
	}

	pCommStatus->AmountInInQueue = pPortExt->Read.cbRxQ;
	pCommStatus->AmountInOutQueue = AsynWriteCommStatus( &pPortExt->Write );
   
#if TODO   
	if ((pPortExt->WaitMask & SERIAL_EV_BREAK) &&
		(pPortExt->SdxbValues.Estat & SDXB_TXB))
	{
		pCommStatus->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;
	}
#endif   

	if ( pSdxbExt->Estat & SDXB_TXI )
	{
		// Immediate character is pending
		pCommStatus->WaitForImmediate = TRUE;
		pCommStatus->AmountInOutQueue++;
	}      
	else      
	{
		pCommStatus->WaitForImmediate = FALSE;
	}      

	pCommStatus->EofReceived = FALSE;

	DigiDump(DIGIIOCTL, ("\tInQ=%d OutQ=%d Hold=0x%x Errors=0x%x\n", 
	pCommStatus->AmountInInQueue, pCommStatus->AmountInOutQueue, 
	pCommStatus->HoldReasons, pCommStatus->Errors));

	pIrp->IoStatus.Information = sizeof(*pCommStatus);
	return STATUS_SUCCESS;
}


STATIC VOID
PurgeCompletion
(
	IN PASYN_PORT_EXT		pPortExt,
	IN PIRP					pIrp
)
{
	ULONG PurgeBits;
	NTSTATUS Status = STATUS_SUCCESS;

	PurgeBits = *((ULONG *)(pIrp->AssociatedIrp.SystemBuffer));

	if ( PurgeBits & SERIAL_PURGE_RXCLEAR )
		AsynPurgeRxBuffers( pPortExt );
      
}   


STATIC NTSTATUS
Purge
(
	IN PASYN_PORT_EXT		pPortExt,
	IN PIRP    				pIrp
)
{
	ULONG PurgeBits;
	NTSTATUS Status = STATUS_SUCCESS;

	PurgeBits = *((ULONG *)(pIrp->AssociatedIrp.SystemBuffer));

	if ( PurgeBits == 0 || 
		(PurgeBits & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR | SERIAL_PURGE_RXCLEAR) ) )
	{
		Status = STATUS_INVALID_PARAMETER;
		DigiDump( DIGIIOCTL, ("      Invalid Purge request, returning STATUS_INVALID_PARAMETER\n!") );
		goto Exit;
	}

	AsynWritePurge( pPortExt, PurgeBits );
   
	if( PurgeBits & SERIAL_PURGE_RXABORT )
		AsynCancelReadIrps( pPortExt );

	if( PurgeBits & SERIAL_PURGE_RXCLEAR )
	{
		PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
		UCHAR			PurgeFlags = 0;

		if ( PurgeBits & SERIAL_PURGE_TXCLEAR )
			PurgeFlags |= SDXB_FLUSH_OUTPUT;

		if ( PurgeBits & SERIAL_PURGE_RXCLEAR )
		   //Purge S/DXB first, then on completion, purge this driver's receive buffers.
			PurgeFlags |= SDXB_FLUSH_INPUT;        

		Status = SdxbFlush( pPortExt->DsPortHandle, PurgeFlags );          
		if (NT_ERROR(Status))
      {
			goto Exit;
		}	   
		// Put IRP on pending queue
		QueueIoctl( pSdxbExt, pIrp );      
		
		// Irp will be completed on the sync response
		( void )SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
		Status = STATUS_PENDING;
	}      

Exit:
	return Status;            
	
}

STATIC NTSTATUS
GetProperties
(
   IN PASYN_PORT_EXT    pPortExt,      
   IN PIRP              pIrp
)   
{
	PSERIAL_COMMPROP 	pProperties = (PSERIAL_COMMPROP)pIrp->AssociatedIrp.SystemBuffer;
	PASYN_SDXB			pSdxbExt = &pPortExt->SdxbValues;

	DigiDump(DIGIIOCTL, ("DigiAsyn!GetProperties: PortNo=0x%x\n", pPortExt->iSdxbPort));

	RtlZeroMemory( pProperties, sizeof(*pProperties));

	pProperties->PacketLength = sizeof(*pProperties);
	pProperties->PacketVersion = 2;
	pProperties->ServiceMask = SERIAL_SP_SERIALCOMM;

	pProperties->MaxTxQueue = 0;	// zero indicates that no maximum value is imposed by this driver.
	pProperties->MaxRxQueue = 0;	// zero indicates that no maximum value is imposed

	pProperties->CurrentTxQueue = 0;	// zero indicates that the value is unavailable;
	pProperties->CurrentRxQueue = pPortExt->Read.cbMaxRxQ;	

	pProperties->MaxBaud = SERIAL_BAUD_USER;

	pProperties->ProvSubType = SERIAL_SP_RS232;

	pProperties->ProvCapabilities = SERIAL_PCF_DTRDSR |
											SERIAL_PCF_RTSCTS |
											SERIAL_PCF_CD     |
											SERIAL_PCF_PARITY_CHECK |
											SERIAL_PCF_XONXOFF |
											SERIAL_PCF_SETXCHAR |
											SERIAL_PCF_TOTALTIMEOUTS |
											SERIAL_PCF_INTTIMEOUTS |
											SERIAL_PCF_SPECIALCHARS;

	pProperties->SettableParams = SERIAL_SP_PARITY |
											SERIAL_SP_BAUD |
											SERIAL_SP_DATABITS |
											SERIAL_SP_STOPBITS |
											SERIAL_SP_HANDSHAKING |
											SERIAL_SP_PARITY_CHECK |
											SERIAL_SP_CARRIER_DETECT;

	pProperties->SettableBaud =  SERIAL_BAUD_075 |
											SERIAL_BAUD_110 |
											SERIAL_BAUD_134_5 |
											SERIAL_BAUD_150 |
											SERIAL_BAUD_300 |
											SERIAL_BAUD_600 |
											SERIAL_BAUD_1200 |
											SERIAL_BAUD_1800 |
											SERIAL_BAUD_2400 |
											SERIAL_BAUD_4800 |
											SERIAL_BAUD_7200 |
											SERIAL_BAUD_9600 |
											SERIAL_BAUD_14400 |
											SERIAL_BAUD_19200 |
											SERIAL_BAUD_38400 |
											SERIAL_BAUD_56K |
											SERIAL_BAUD_57600 |
											SERIAL_BAUD_115200 |
											SERIAL_BAUD_128K |
											SERIAL_BAUD_USER;

	pProperties->SettableData = SERIAL_DATABITS_5 |
											SERIAL_DATABITS_6 |
											SERIAL_DATABITS_7 |
											SERIAL_DATABITS_8;

	pProperties->SettableStopParity = SERIAL_STOPBITS_10 |
											SERIAL_STOPBITS_15 |
											SERIAL_STOPBITS_20 |
											SERIAL_PARITY_NONE |
											SERIAL_PARITY_ODD  |
											SERIAL_PARITY_EVEN ;
                                     
	if ( pSdxbExt->PortCapabilities & CVEC_SPACE_PARITY )
		pProperties->SettableStopParity |= SERIAL_PARITY_MARK;
      
	if ( pSdxbExt->PortCapabilities & CVEC_SPACE_PARITY )
		pProperties->SettableStopParity |= SERIAL_PARITY_SPACE;

	pIrp->IoStatus.Information = sizeof(*pProperties);

	return STATUS_SUCCESS;
}


STATIC NTSTATUS
LsrmstInsert
(
	IN PASYN_PORT_EXT		pPortExt,      
	IN PIRP					pIrp
)   
{
	UCHAR			EscapeChar = *(PUCHAR)pIrp->AssociatedIrp.SystemBuffer;
	PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
	NTSTATUS		Status = STATUS_SUCCESS;
   
	pIrp->IoStatus.Information = sizeof(UCHAR);

	DigiAcquireSpinLock( &pSdxbExt->Lock );

	if ( EscapeChar )
	{
		// Escape character must be different than the XON & XOFF characters and
		// can't already be doing error replacement
		if ( EscapeChar == pSdxbExt->PortParams.xon || EscapeChar == pSdxbExt->PortParams.xoff ||  	
				pSdxbExt->Handflow.FlowReplace & SERIAL_ERROR_CHAR )
		{
			Status = STATUS_INVALID_PARAMETER;
			pIrp->IoStatus.Information = 0;
			DigiReleaseSpinLock( &pSdxbExt->Lock );
			goto Exit;
		}                  
		DigiDump( DIGIIOCTL | DIGIFLOW, ("\tEnable LSRMST Insertion\n") );
      
		// Enable LSRMST insertion
		pSdxbExt->PortParams.xflags |= SDXB_XMSPAR | SDXB_XMODEM;
		pSdxbExt->PortParams.xflags &= ~SDXB_XEDATA;
		pSdxbExt->PortParams.iflags |= SDXB_PARMRK;
	}
	else
	{
		DigiDump( DIGIIOCTL | DIGIFLOW, ("\tDisable LSRMST Insertion\n") );
		// Disable LSRMST insertion   	
		pSdxbExt->PortParams.xflags &= ~(SDXB_XMSPAR | SDXB_XMODEM);
		pSdxbExt->PortParams.iflags &= ~SDXB_PARMRK;
		if (!pSdxbExt->PortParams.xflags & SDXB_XMSERC )
			pSdxbExt->PortParams.xflags |= SDXB_XEDATA;
	}
	pSdxbExt->PortParams.mspesc = EscapeChar;
#if 0
	pSdxbExt->PortParams.breakchar = EscapeChar;
#endif      
	DigiReleaseSpinLock( &pSdxbExt->Lock );
   
	Status = SdxbParam( pPortExt->DsPortHandle, &pSdxbExt->PortParams );
	if ( NT_ERROR( Status ) )
	{
		goto Exit;
	}
   
	// Put IRP on pending queue
	QueueIoctl( pSdxbExt, pIrp );      
	// Override status.  Irp will complete after sync response
	Status = STATUS_PENDING;

	// Request sync
	Status = SdxbSync( pPortExt->DsPortHandle, ++pSdxbExt->SyncSeqNum, FALSE );
Exit:   
	return Status;
}


STATIC NTSTATUS
ConfigSize
(
	IN PASYN_PORT_EXT		pPortExt,      
	IN PIRP					pIrp
)   
{
	*(PULONG)pIrp->AssociatedIrp.SystemBuffer = 0;
	pIrp->IoStatus.Information = sizeof(ULONG);
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
GetStats
(
IN PASYN_PORT_EXT    pPortExt,      
IN PIRP              pIrp
)   
{
	PSERIALPERF_STATS pStats = pIrp->AssociatedIrp.SystemBuffer;
   pPortExt->SdxbValues.PerfStats.TransmittedCount = AsynWriteGetWriteStatus( &pPortExt->Write );
	*pStats = pPortExt->SdxbValues.PerfStats;
	pIrp->IoStatus.Information = sizeof(*pStats);
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
ClearStats
(
	IN PASYN_PORT_EXT    pPortExt,      
	IN PIRP              pIrp
)
{
	PASYN_SDXB		pSdxbExt = &pPortExt->SdxbValues;

	DigiAcquireSpinLock( &pSdxbExt->Lock );
	RtlZeroMemory( &pSdxbExt->PerfStats, sizeof pSdxbExt->PerfStats );
	DigiReleaseSpinLock( &pSdxbExt->Lock ); 
   
	AsynWriteClearStats( &pPortExt->Write );
  
	pIrp->IoStatus.Information = 0;
	return STATUS_SUCCESS;	
}


NTSTATUS
ProcessIoctl
(
	IN PASYN_PORT_EXT   	pPortExt,      // Port specific information.
	IN PIRP        		pIrp           // Write request to perform.
)
{
	NTSTATUS       Status = STATUS_SUCCESS;	// Called function return value.
	ULONG Ioctl = 
				(IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.IoControlCode;

	LARGE_INTEGER Time = {0};

typedef NTSTATUS 
	(*PASYN_IOCTL) ( 
		IN PASYN_PORT_EXT 		pExt,
		IN PIRP						pIrp);

	PASYN_IOCTL IoctlFunction;
	ULONG			ExpectedBufferLength = 0;
	ULONG			IrpBufferLength = 0;
   
#if DBG
	KeQuerySystemTime( &Time );
#endif
	DigiDump( DIGIFLOW|DIGIIOCTL,
		( "DigiAsyn!ProcessIoctl: Enter, time 0x%08x%08x, Port %S, pIrp 0x%08x %s\n", 
		Time.HighPart, Time.LowPart,pPortExt->PortName.Buffer, pIrp,
		Ioctl == IOCTL_SERIAL_SET_BAUD_RATE  ? "IOCTL_SERIAL_SET_BAUD_RATE" :
		Ioctl == IOCTL_SERIAL_SET_QUEUE_SIZE ? "IOCTL_SERIAL_SET_QUEUE_SIZE" :
		Ioctl == IOCTL_SERIAL_SET_LINE_CONTROL ? "IOCTL_SERIAL_SET_LINE_CONTROL" :
		Ioctl == IOCTL_SERIAL_SET_BREAK_ON  ? "IOCTL_SERIAL_SET_BREAK_ON" :
		Ioctl == IOCTL_SERIAL_SET_BREAK_OFF ? "IOCTL_SERIAL_SET_BREAK_OFF" :
		Ioctl == IOCTL_SERIAL_IMMEDIATE_CHAR ? "IOCTL_SERIAL_IMMEDIATE_CHAR" :
		Ioctl == IOCTL_SERIAL_SET_TIMEOUTS ? "IOCTL_SERIAL_SET_TIMEOUTS" :
		Ioctl == IOCTL_SERIAL_GET_TIMEOUTS ? "IOCTL_SERIAL_GET_TIMEOUTS" :
		Ioctl == IOCTL_SERIAL_SET_DTR ? "IOCTL_SERIAL_SET_DTR" :
		Ioctl == IOCTL_SERIAL_CLR_DTR ? "IOCTL_SERIAL_CLR_DTR" :
		Ioctl == IOCTL_SERIAL_RESET_DEVICE ? "IOCTL_SERIAL_RESET_DEVICE" : 
		Ioctl == IOCTL_SERIAL_SET_RTS ? "IOCTL_SERIAL_SET_RTS" :
		Ioctl == IOCTL_SERIAL_CLR_RTS ? "IOCTL_SERIAL_CLR_RTS" : 
		Ioctl == IOCTL_SERIAL_GET_DTRRTS ? "IOCTL_SERIAL_GET_DTRRTS" :
		Ioctl == IOCTL_SERIAL_SET_XOFF ? "IOCTL_SERIAL_SET_XOFF" :
		Ioctl == IOCTL_SERIAL_SET_XON ? "IOCTL_SERIAL_SET_XON" :
		Ioctl == IOCTL_SERIAL_LSRMST_INSERT ? "IOCTL_SERIAL_LSRMST_INSERT" :
		Ioctl == IOCTL_SERIAL_GET_WAIT_MASK ? "IOCTL_SERIAL_GET_WAIT_MASK" :
		Ioctl == IOCTL_SERIAL_SET_WAIT_MASK ? "IOCTL_SERIAL_SET_WAIT_MASK" :
		Ioctl == IOCTL_SERIAL_WAIT_ON_MASK ? "IOCTL_SERIAL_WAIT_ON_MASK" :
		Ioctl == IOCTL_SERIAL_PURGE ? "IOCTL_SERIAL_PURGE" :
		Ioctl == IOCTL_SERIAL_GET_BAUD_RATE ? "IOCTL_SERIAL_GET_BAUD_RATE" :
		Ioctl == IOCTL_SERIAL_GET_LINE_CONTROL ? "IOCTL_SERIAL_GET_LINE_CONTROL" :
		Ioctl == IOCTL_SERIAL_GET_CHARS ? "IOCTL_SERIAL_GET_CHARS" :
		Ioctl == IOCTL_SERIAL_SET_CHARS ? "IOCTL_SERIAL_SET_CHARS" :
		Ioctl == IOCTL_SERIAL_GET_HANDFLOW ? "IOCTL_SERIAL_GET_HANDFLOW" :
		Ioctl == IOCTL_SERIAL_SET_HANDFLOW ? "IOCTL_SERIAL_SET_HANDFLOW" : 
		Ioctl == IOCTL_SERIAL_GET_MODEMSTATUS ? "IOCTL_SERIAL_GET_MODEMSTATUS" :
		Ioctl == IOCTL_SERIAL_GET_COMMSTATUS ? "IOCTL_SERIAL_GET_COMMSTATUS" :
		Ioctl == IOCTL_SERIAL_XOFF_COUNTER ? "IOCTL_SERIAL_XOFF_COUNTER" :
		Ioctl == IOCTL_SERIAL_GET_PROPERTIES ? "IOCTL_SERIAL_GET_PROPERTIES" :
		Ioctl == IOCTL_SERIAL_CONFIG_SIZE ? "IOCTL_SERIAL_CONFIG_SIZE" :
		Ioctl == IOCTL_SERIAL_SET_HANDFLOW ? "IOCTL_SERIAL_SET_HANDFLOW" : 
		Ioctl == IOCTL_DIGI_SDXB_XN_PUT ? "IOCTL_DIGI_SDXB_XN_PUT" :
		Ioctl == IOCTL_DIGI_SDXB_XN_GET ? "IOCTL_DIGI_SDXB_XN_GET" : "Unknown" ) );
      
	switch ( Ioctl )
	{
      
		case IOCTL_SERIAL_SET_BAUD_RATE:
			IoctlFunction = SetBaudRate;
			ExpectedBufferLength = sizeof( SERIAL_BAUD_RATE );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_GET_BAUD_RATE:
			IoctlFunction = GetBaudRate;
			ExpectedBufferLength = sizeof( SERIAL_BAUD_RATE );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_SET_QUEUE_SIZE:
			IoctlFunction = SetQueueSize;
			ExpectedBufferLength = sizeof( SERIAL_QUEUE_SIZE );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_SET_LINE_CONTROL:
			IoctlFunction = SetLineControl;
			ExpectedBufferLength = sizeof( SERIAL_LINE_CONTROL );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_GET_LINE_CONTROL:
			IoctlFunction = GetLineControl;
			ExpectedBufferLength = sizeof( SERIAL_LINE_CONTROL );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_SET_BREAK_ON:
			IoctlFunction = SetBreakOn;      
			break;

		case IOCTL_SERIAL_SET_BREAK_OFF:
			IoctlFunction = SetBreakOff;      
			break;

		case IOCTL_SERIAL_IMMEDIATE_CHAR:
			IoctlFunction = SendImmediateChar;      
			ExpectedBufferLength = sizeof( UCHAR );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
         if ( IrpBufferLength != ExpectedBufferLength )
				Status = STATUS_INVALID_PARAMETER;         
			break;

		case IOCTL_SERIAL_SET_TIMEOUTS:
			IoctlFunction = SetTimeouts;      
			ExpectedBufferLength = sizeof( SERIAL_TIMEOUTS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_GET_TIMEOUTS:
			IoctlFunction = GetTimeouts;      
			ExpectedBufferLength = sizeof( SERIAL_TIMEOUTS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_SET_DTR:
			IoctlFunction = SetDtr;      
			break;

		case IOCTL_SERIAL_CLR_DTR:
			IoctlFunction = ClearDtr;      
			break;

		case IOCTL_SERIAL_RESET_DEVICE:
			IoctlFunction = IoResetDevice;
			break;

		case IOCTL_SERIAL_SET_RTS:
			IoctlFunction = SetRts;      
			break;

		case IOCTL_SERIAL_CLR_RTS:
			IoctlFunction = ClearRts;      
			break;

		case IOCTL_SERIAL_GET_DTRRTS:
			IoctlFunction = GetDtrRts;      
			ExpectedBufferLength = sizeof( ULONG );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_SET_XON:
			IoctlFunction = SetXon;      
			break;

		case IOCTL_SERIAL_SET_XOFF:
			IoctlFunction = SetXoff;      
			break;

		case IOCTL_SERIAL_SET_CHARS:
			IoctlFunction = SetChars;      
			ExpectedBufferLength = sizeof( SERIAL_CHARS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_GET_CHARS:
			IoctlFunction = GetChars;      
			ExpectedBufferLength = sizeof( SERIAL_CHARS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_SET_HANDFLOW:
			IoctlFunction = SetHandflow;      
			ExpectedBufferLength = sizeof( SERIAL_HANDFLOW );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_GET_HANDFLOW:
			IoctlFunction = GetHandflow;      
			ExpectedBufferLength = sizeof( SERIAL_HANDFLOW );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_GET_MODEMSTATUS:
			IoctlFunction = GetModemStatus;      
			ExpectedBufferLength = sizeof( ULONG );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_GET_COMMSTATUS:
			IoctlFunction = GetCommStatus;
			ExpectedBufferLength = sizeof( SERIAL_STATUS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_GET_PROPERTIES:
			IoctlFunction = GetProperties;
			ExpectedBufferLength = sizeof( SERIAL_COMMPROP );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_LSRMST_INSERT:
			IoctlFunction = LsrmstInsert;      
			ExpectedBufferLength = sizeof( UCHAR );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_CONFIG_SIZE:
			IoctlFunction = ConfigSize;      
			ExpectedBufferLength = sizeof( ULONG );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_SET_WAIT_MASK:
			IoctlFunction = AsynSetWaitMask;      
			ExpectedBufferLength = sizeof( ULONG );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;
         
		case IOCTL_SERIAL_GET_WAIT_MASK:
			IoctlFunction = AsynGetWaitMask;
			ExpectedBufferLength = sizeof( ULONG );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_WAIT_ON_MASK:
			IoctlFunction = AsynWaitOnMask;      
			ExpectedBufferLength = sizeof( ULONG );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_PURGE:
			IoctlFunction = Purge;      
			ExpectedBufferLength = sizeof( ULONG );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		case IOCTL_SERIAL_GET_STATS:
			IoctlFunction = GetStats;      
			ExpectedBufferLength = sizeof( SERIALPERF_STATS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_CLEAR_STATS:
			IoctlFunction = ClearStats;      
			break;

		case IOCTL_DIGI_SDXB_XN_PUT:
			//   		IoctlFunction = SdxbXnPut;
			IoctlFunction = IoctlToDo;
			break;

		case IOCTL_DIGI_SDXB_XN_GET:
			//   		IoctlFunction = SdxbXnGet;
			IoctlFunction = IoctlToDo;
			break;

		default:
			DigiDump( DIGIERROR, ("*** DigiAsyn!ProcessIoctl: Unknown req=0x%x\n", Ioctl ));
			Status = STATUS_INVALID_PARAMETER;
			break;
	}
   
	if ( NT_ERROR( Status ))
		goto Exit;

	if ( IrpBufferLength < ExpectedBufferLength )
	{	
		DigiDump( DIGIERROR, ("*** DigiAsyn!ProcessIoctl: InputBufferLength=0x%x ExpectedBufferLength=0x%x\n", (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength , ExpectedBufferLength ));
		Status = STATUS_BUFFER_TOO_SMALL;
		goto Exit;
	}
	Status = IoctlFunction( pPortExt, pIrp );
Exit:   
#ifdef DBG
	DigiDump( DIGIIOCTL, ("ASYN!ProcessIoctl: Exit ret=0x%x\n", Status));
	if ( (Status != STATUS_SUCCESS) && (Status != STATUS_PENDING))
	{
		DigiDump( DIGIERROR, ("*** ASYN!ProcessIoctl: Exit ret=0x%x Ioctl=0x%x\n", Status, Ioctl ));
	}
#endif
	if ( NT_ERROR( Status ))
	{	
		pIrp->IoStatus.Information = 0;
	}      
	if ( Status != STATUS_PENDING )
	{
		AsynIoCompleteRequest( pIrp, Status, pPortExt );
	}      
	return ( Status );
}

NTSTATUS
AsynDeviceIoControl(
	IN DEVICE_OBJECT    *pDeviceObject,  
	IN IRP              *pIrp          
)
{
	NTSTATUS    Status = STATUS_UNSUCCESSFUL;
	PASYN_PORT_EXT	pPortExt = pDeviceObject->DeviceExtension;

	// Check for port versus controller
	switch ( pDeviceObject->DeviceType )
	{
		case FILE_DEVICE_SERIAL_PORT:
		{
			if ( pPortExt->Type != ASYN_TYPE_PORT_EXT )
			{
				Status = STATUS_UNSUCCESSFUL;
				pIrp->IoStatus.Information = 0;
				AsynIoCompleteRequest( pIrp, Status, NULL );
			}            
			else
			{
				// Check if port is open before continuing
				if ( pPortExt->State != OpenState )
				{
	         	Status = STATUS_CANCELLED;
	            pIrp->IoStatus.Information = 0;
	            AsynIoCompleteRequest( pIrp, Status, NULL );
				}            
	         else
					Status = ProcessIoctl( pPortExt, pIrp );
			}            
			break;
		}
		case FILE_DEVICE_CONTROLLER:
		case FILE_DEVICE_UNKNOWN:      
		{
			Status = DigiDgaIoctl( pDeviceObject, pIrp );
			break;
		}
		default:
		{
			pIrp->IoStatus.Information = 0;
			AsynIoCompleteRequest( pIrp, Status, pPortExt );
			break;
		}
	}               
	return Status;

}

#if NT5_BUILD
STATIC NTSTATUS
BasicSettings(
   IN PASYN_PORT_EXT    pPortExt,      // Port specific information.
   IN PIRP              pIrp           // Associated IRP
)
{
	SERIAL_BASIC_SETTINGS NewSettings;
	PSERIAL_BASIC_SETTINGS pBasic;
   PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;

	//
	// Everything is 0 -- timeouts and flow control.  If additional features are added, 
	// this zero memory method may not work.
	//
	RtlZeroMemory(&NewSettings, sizeof(SERIAL_BASIC_SETTINGS));

	//
	// Save the current settings
	//
	pIrp->IoStatus.Information = sizeof(SERIAL_BASIC_SETTINGS);
	pBasic = (PSERIAL_BASIC_SETTINGS)pIrp->AssociatedIrp.SystemBuffer;
   
	AsynGetReadTimeouts( &pPortExt->Read, &pBasic->Timeouts );
	AsynGetWriteTimeout( &pPortExt->Write, &pBasic->Timeouts );
      
   * &pBasic->HandFlow = pSdxbExt->Handflow;
	//
	// Update with the new settings
	//
   AsynSetReadTimeouts( &pPortExt->Read, &NewSettings.Timeouts );
	AsynSetWriteTimeout( &pPortExt->Write, &NewSettings.Timeouts );
   
	DigiAcquireSpinLock( &pSdxbExt->Lock );
	pSdxbExt->Handflow = *(PSERIAL_HANDFLOW)&NewSettings.HandFlow;
	(void)AsynUpdatePortHandflow( pPortExt->DsPortHandle, pSdxbExt );
	DigiReleaseSpinLock( &pSdxbExt->Lock );
   
	return STATUS_SUCCESS;
}


STATIC NTSTATUS
RestoreSettings(
   IN PASYN_PORT_EXT    pPortExt,      // Port specific information.
   IN PIRP              pIrp           // Associated IRP
)
{
   PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;
	PSERIAL_BASIC_SETTINGS pBasic = (PSERIAL_BASIC_SETTINGS)pIrp->AssociatedIrp.SystemBuffer;
         
	// Restore the timeouts
	AsynSetReadTimeouts( &pPortExt->Read, &pBasic->Timeouts );
	AsynSetWriteTimeout( &pPortExt->Write, &pBasic->Timeouts );

	// Restore the flow control settings
	DigiAcquireSpinLock( &pSdxbExt->Lock );
	pSdxbExt->Handflow = *(PSERIAL_HANDFLOW)&pBasic->HandFlow;
   (void)AsynUpdatePortHandflow( pPortExt->DsPortHandle, pSdxbExt );
	DigiReleaseSpinLock( &pSdxbExt->Lock );
   
	return STATUS_SUCCESS;
}


NTSTATUS
ProcessInternalIoctl
(
	IN PASYN_PORT_EXT   	pPortExt,      // Port specific information.
	IN PIRP        		pIrp           // Write request to perform.
)
{
typedef NTSTATUS 
	(*PASYN_IOCTL) ( 
		IN PASYN_PORT_EXT 		pExt,
		IN PIRP						pIrp);

	PASYN_IOCTL IoctlFunction;
	ULONG			ExpectedBufferLength = 0;
	ULONG			IrpBufferLength = 0;

	NTSTATUS       Status = STATUS_SUCCESS;	// Called function return value.
	ULONG Ioctl = 
				(IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.IoControlCode;

	LARGE_INTEGER Time = {0};


	switch ( Ioctl )
	{
      
		case IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS:
			IoctlFunction = BasicSettings;
			ExpectedBufferLength = sizeof( SERIAL_BASIC_SETTINGS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.OutputBufferLength;
			break;

		case IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS:
			IoctlFunction = RestoreSettings;
			ExpectedBufferLength = sizeof( SERIAL_BASIC_SETTINGS );
			IrpBufferLength = (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength;
			break;

		default:
			DigiDump( DIGIERROR, ("*** DigiAsyn!ProcessInternalIoctl: Unknown req=0x%x\n", Ioctl ));
			Status = STATUS_INVALID_PARAMETER;
			break;
	}
   
	if ( NT_ERROR( Status ))
		goto Exit;

	if ( IrpBufferLength < ExpectedBufferLength )
	{	
		DigiDump( DIGIERROR, ("*** DigiAsyn!ProcessInternalIoctl: InputBufferLength=0x%x ExpectedBufferLength=0x%x\n", (IoGetCurrentIrpStackLocation( pIrp ))->Parameters.DeviceIoControl.InputBufferLength , ExpectedBufferLength ));
		Status = STATUS_BUFFER_TOO_SMALL;
		goto Exit;
	}
	Status = IoctlFunction( pPortExt, pIrp );
   
Exit:   
	if ( NT_ERROR( Status ))
	{	
		DigiDump( DIGIERROR, ("*** ASYN!ProcessInternalIoctl: Exit ret=0x%x Ioctl=0x%x\n", Status, Ioctl ));
		pIrp->IoStatus.Information = 0;
	}      
	if ( Status != STATUS_PENDING )
	{
		AsynIoCompleteRequest( pIrp, Status, pPortExt );
	}      
	return ( Status );
}



NTSTATUS
AsynDeviceInternalIoControl(
	IN DEVICE_OBJECT    *pDevice,  
	IN IRP              *pIrp          
)
{
	NTSTATUS    Status = STATUS_UNSUCCESSFUL;

	pIrp->IoStatus.Information = 0;
   
	// Check for a valid device object
	if ( pDevice == NULL || pDevice->DeviceType != FILE_DEVICE_SERIAL_PORT)
	{
		DigiDump(DIGIIOCTL, ("*** AsynDeviceRead: unknown device =0x%x Type=x0%x\n", 
																	pDevice, pDevice->DeviceType));
		AsynIoCompleteRequest( pIrp, Status, NULL );
		goto Exit;
	}      

	{
		PASYN_PORT_EXT	pPortExt = pDevice->DeviceExtension;

	   if ( pPortExt->Type != ASYN_TYPE_PORT_EXT )
		{
			AsynIoCompleteRequest( pIrp, Status, NULL );
         goto Exit;
		}
	   
	   if ( pPortExt->State != OpenState )
		{            
			Status = STATUS_CANCELLED;
			AsynIoCompleteRequest( pIrp, Status, pPortExt );
         goto Exit;
		}
		
		Status = ProcessInternalIoctl( pPortExt, pIrp );
	}   
Exit:   
	return Status;
}
#endif