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

   DigiDga.c

Abstract:

   This module contains functions that will handle ioctls that will come in
   to get or give information to the controller. This is for Dga mode for 
   tracing, port status, controller data, ect.  I am trying to make this file
   portable so that all products will be able to use this.

Author:

   Dave E. Jones

--*/
#include	"digiasyn.h"
#include	"DigiDga.h"
#include	<ntddk.h>

int sprintf(char *s, const char *format, ...);

/*******************************************************************************************
**
**	VOID  DigiDgaIoctl( PDEVICE_OBJECT DeviceObject, PIRP Irp )
**
**	Purpose: I got an ioctl for the controller object, this is the function that will
**			handle the ioctl and send it packing.
**
**	Given: The device object for the controller, and the Irp that was sent down.
**
**	Returns: Nothing
**
********************************************************************************************/
NTSTATUS   DigiDgaIoctl( PDEVICE_OBJECT DeviceObject, PIRP Irp )
{
	PIO_STACK_LOCATION		currentIrpStack = IoGetCurrentIrpStackLocation( Irp );
	NTSTATUS				status = STATUS_SUCCESS;
	PDEVICE_OBJECT			desiredObject;
	ULONG					comPort;
	PASYN_PORT_EXT			pPortExt;


	switch ( currentIrpStack->Parameters.DeviceIoControl.IoControlCode )
	{
		case IOCTL_DIGI_CONTROLLER_SPECIAL:
		{
			//This will set up the current flag for the Trace mode of the statics mode.
			status = STATUS_INVALID_DEVICE_REQUEST;
			Irp->IoStatus.Status = status;
			AsynIoCompleteRequest( Irp, status, NULL );
			break;
		}
		case IOCTL_DIGI_GET_CONTROLLER_DATA:
		{
			// First determine if the passed in settings are correct.
			if ( currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( CARD_STATUS ) )
			{
				status = STATUS_INVALID_BUFFER_SIZE;
				Irp->IoStatus.Information = sizeof( CARD_STATUS );
				AsynIoCompleteRequest( Irp, status, NULL );
				break;
			}
			status = GetControllerData( DeviceObject, Irp );
			break;
		}
		case IOCTL_DIGI_GET_PORT_STATUS:
		{
			if ( (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) ||
				 (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PORT_STATUS)) )
			{
				status = STATUS_INVALID_BUFFER_SIZE;
				Irp->IoStatus.Status = status;
				Irp->IoStatus.Information = sizeof( PORT_STATUS );
				IoCompleteRequest( Irp, IO_NO_INCREMENT );
				break;
			}
			comPort = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
			desiredObject = GetPortDeviceObject( DeviceObject->DriverObject, comPort );
			if ( !desiredObject )
			{
				status = STATUS_TOO_MANY_SECRETS;
				Irp->IoStatus.Status = status;
				Irp->IoStatus.Information = 0;
				IoCompleteRequest( Irp, IO_NO_INCREMENT );
				break;
			}

			AsynGetDeviceStatus( desiredObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer );
			Irp->IoStatus.Status = STATUS_SUCCESS;
			Irp->IoStatus.Information = sizeof( PORT_STATUS );
			IoCompleteRequest( Irp, IO_NO_INCREMENT );          // TO DO:  is actually get the status.
			break;
		}
		case IOCTL_DIGI_GET_PORT_TRACE:
		{
			// If the irp is a trace, first check size of buffer, then mark pending and queue to list.
			if ( currentIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof( ULONG ) )
			{
				status = STATUS_INVALID_BUFFER_SIZE;
				Irp->IoStatus.Status = status;
				Irp->IoStatus.Information = sizeof( ULONG );
				IoCompleteRequest( Irp, IO_NO_INCREMENT );
				break;
			}

			comPort = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
			desiredObject = GetPortDeviceObject( DeviceObject->DriverObject, comPort );
			if ( !desiredObject )
			{
				status = STATUS_TOO_MANY_SECRETS;
				Irp->IoStatus.Status = status;
				Irp->IoStatus.Information = 0;
				IoCompleteRequest( Irp, IO_NO_INCREMENT );
				break;
			}
			// Now we have a good Irp and a good object queue the little sucker to the list.

			IoMarkIrpPending( Irp );
			pPortExt = (PASYN_PORT_EXT)desiredObject->DeviceExtension;
			DigiAcquireSpinLock( &pPortExt->Trace.TraceLock );

			if ( IsListEmpty( &pPortExt->Trace.TraceQueue ) )
			{
				InsertTailList( &pPortExt->Trace.TraceQueue, &Irp->Tail.Overlay.ListEntry );
				KeSetTimer( &pPortExt->Trace.TraceTimer, pPortExt->Trace.Timeout, &pPortExt->Trace.TraceDpc );
			}
			else
				InsertTailList( &pPortExt->Trace.TraceQueue, &Irp->Tail.Overlay.ListEntry );

			DigiReleaseSpinLock( &pPortExt->Trace.TraceLock );
			status = STATUS_PENDING;
			break;
			
		}
		default:
			status = STATUS_INVALID_DEVICE_REQUEST;
			Irp->IoStatus.Information = 0;
			AsynIoCompleteRequest( Irp, status, NULL );
			break;

	}
	return status;
}

/*********************************************************************************************************
**
**	NTSTATUS  GetControllerData( PDEVICE_OBJECT DeviceObject, PIRP Irp )
**
**	Purpose: This is a driver specific function that will extract data from the device extension
**		to fill in the buffer to return the irp with.
**
**	Given: The device object, for the device extension, and also the irp so that we can fill in the
**		  buffer with the correct data.
**
**	Returns: Status 
**
*********************************************************************************************************/
NTSTATUS GetControllerData( PDEVICE_OBJECT DeviceObject, PIRP Irp )
{
	Irp->IoStatus.Information = 0;
	AsynIoCompleteRequest( Irp, STATUS_INVALID_DEVICE_REQUEST, NULL );
	return STATUS_INVALID_DEVICE_REQUEST;
	// This request is not valid for this driver.  The CARD_STATUS struct has nothing 
	// to do with the type of data that we store in the device extension.
}

/*************************************************************************************************************
**
**	PDEVICE_OBJECT	GetPortDeviceObject(	PDRIVER_OBJECT		DriverObject )
**
**	Purpose:  I need to get the device object that the request was for.  This function will walk the driver
**		list until I find the correct object.  Then I will return that object to the caller for futher
**		processing.  This would be a good function for a shut down request maybe.
**
**************************************************************************************************************/
PDEVICE_OBJECT GetPortDeviceObject( PDRIVER_OBJECT DriverObject, ULONG ComPort )
{
	PDEVICE_OBJECT		comObject;
	PASYN_PORT_EXT		pExt;
	ANSI_STRING			checkString;
	UNICODE_STRING		unicodeCheck;
	CCHAR				buffer[32];


	sprintf( buffer, "Com%d", ComPort );
	RtlInitAnsiString( &checkString, buffer );
	RtlAnsiStringToUnicodeString( &unicodeCheck, &checkString, TRUE );

	// Now walk the driver list until I get the correct device object
	
	comObject = DriverObject->DeviceObject;

	while ( comObject )
	{
		if ( comObject->DeviceType != FILE_DEVICE_SERIAL_PORT )
		{
			comObject = comObject->NextDevice;
			continue;
		}

		// Here we get the port extension and check the comport number
		pExt = comObject->DeviceExtension;
		
		if ( !RtlCompareUnicodeString( &unicodeCheck, &pExt->PortName, TRUE ) )
			break;

		comObject = comObject->NextDevice;
	}

	RtlFreeUnicodeString( &unicodeCheck );
	return comObject;
}
/************************************************************************************************
**
**	void AsynTraceWriteIrp( PASYN_PORT_EXT pPortExt, PIRP pIrp )
**
**	Purpose: To return information to the caller on a write irp that was passed through.
**
**
*************************************************************************************************/
void AsynTraceWriteIrp( PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    PUCHAR					pIrpBuffer;
    PIRP					pTraceIrp = NULL;
    PIRP					pIrpToComplete = NULL;
    PLIST_ENTRY				ListEntry;
    ULONG_PTR				IrpSpace;
    PIO_STACK_LOCATION		pIrpSp;

	WRITE_TRACE		*pWrite;
    TRACE_HEADER	*pHdr;
    ULONG_PTR		BytesToCopy;


    if ( IsListEmpty( &pPortExt->Trace.TraceQueue ) )
		return;

    ListEntry = RemoveHeadList( &pPortExt->Trace.TraceQueue);
    pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
    pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
    pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer + pTraceIrp->IoStatus.Information;
    IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength - pTraceIrp->IoStatus.Information;

    if ( IrpSpace < pIrp->IoStatus.Information+sizeof(WRITE_TRACE)+sizeof(TRACE_HEADER) )
    {
		pTraceIrp->IoStatus.Status = STATUS_SUCCESS;
		IoCompleteRequest( pTraceIrp, IO_NO_INCREMENT );
		pTraceIrp = NULL;

         // There's not enough room in this IRP.  Get the next one.

         if (!IsListEmpty(&pPortExt->Trace.TraceQueue))
         {
            ListEntry = RemoveHeadList( &pPortExt->Trace.TraceQueue);
            pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
            pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
            pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer;
            IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
         }
		 else
			 return;
	}

    if ( !pTraceIrp )
		return;

    pHdr = (TRACE_HEADER*)pIrpBuffer;
    pIrpBuffer += sizeof(TRACE_HEADER);
    IrpSpace -= sizeof(TRACE_HEADER);
    pWrite = (WRITE_TRACE*)pIrpBuffer;
    pIrpBuffer += sizeof(WRITE_TRACE);
    IrpSpace -= sizeof(WRITE_TRACE);

    pHdr->FunctionType = TRACETYPE_WRITE;
    KeQuerySystemTime(&pHdr->Time);

	pHdr->ModemStatus = 0;
    if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
		pHdr->ModemStatus |= 0x02;
    if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
        pHdr->ModemStatus |= 0x10;
    if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
        pHdr->ModemStatus |= 0x01;
	if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
        pHdr->ModemStatus |= 0x20;
    if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
        pHdr->ModemStatus |= 0x80;
    if (pPortExt->SdxbValues.Mstat & SDXB_RI)
        pHdr->ModemStatus |= 0x40;

    pHdr->Length = pIrp->IoStatus.Information;
    pWrite->Irp = (ULONG_PTR)pIrp;
    pWrite->Status = pIrp->IoStatus.Status;

    BytesToCopy = MIN( pIrp->IoStatus.Information, IrpSpace);
    RtlCopyMemory( pIrpBuffer, pIrp->AssociatedIrp.SystemBuffer, BytesToCopy);

    pTraceIrp->IoStatus.Information = pTraceIrp->IoStatus.Information + 
                                          sizeof(TRACE_HEADER) +
                                          sizeof(WRITE_TRACE) +
                                          BytesToCopy;

    InsertHeadList( &pPortExt->Trace.TraceQueue, &pTraceIrp->Tail.Overlay.ListEntry);
  
}
/**************************************************************************************************
**
**	void AsynTraceReadIrp( PASYN_PORT_EXT pPortExt, PIRP pIrp )
**
**	Purpose: This will trace the read ioctls, this is very similiar to the write counter part.
**
**
***************************************************************************************************/
void AsynTraceReadIrp( PASYN_PORT_EXT pPortExt, PIRP pIrp)
{
    PUCHAR         pIrpBuffer;
    PIRP           pTraceIrp = NULL, pIrpToComplete = NULL;
    PLIST_ENTRY    ListEntry;
    ULONG_PTR      IrpSpace;
    PIO_STACK_LOCATION pIrpSp;

	READ_TRACE     *pRead;
    TRACE_HEADER   *pHdr;
    ULONG_PTR      BytesToCopy;

 
    if ( IsListEmpty(&pPortExt->Trace.TraceQueue) )
		return;

   
    ListEntry = RemoveHeadList( &pPortExt->Trace.TraceQueue);
    pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
    pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
    pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer + pTraceIrp->IoStatus.Information;
    IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength - pTraceIrp->IoStatus.Information;

    if ( IrpSpace < pIrp->IoStatus.Information+sizeof(READ_TRACE)+sizeof(TRACE_HEADER) )
    {

        pTraceIrp->IoStatus.Status = STATUS_SUCCESS; 
 	   IoCompleteRequest( pTraceIrp, IO_NO_INCREMENT );

        pTraceIrp = NULL;

        // There's not enough room in this IRP.  Get the next one.

        if (!IsListEmpty(&pPortExt->Trace.TraceQueue))
        {
           ListEntry = RemoveHeadList(&pPortExt->Trace.TraceQueue);
           pTraceIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
           pIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
           pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer;
           IrpSpace = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
        }
		else
			return;
   }

   if ( !pTraceIrp )
	   return;
   
        
 
    pHdr = (TRACE_HEADER*)pIrpBuffer;
    pIrpBuffer += sizeof(TRACE_HEADER);
    IrpSpace -= sizeof(TRACE_HEADER);
    pRead = (READ_TRACE*)pIrpBuffer;
    pIrpBuffer += sizeof(READ_TRACE);
    IrpSpace -= sizeof(READ_TRACE);

    pHdr->FunctionType = TRACETYPE_READ;
    KeQuerySystemTime( &pHdr->Time);

    pHdr->ModemStatus = 0;
    if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
		pHdr->ModemStatus |= 0x02;
    if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
        pHdr->ModemStatus |= 0x10;
    if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
        pHdr->ModemStatus |= 0x01;
    if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
        pHdr->ModemStatus |= 0x20;
    if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
        pHdr->ModemStatus |= 0x80;
    if (pPortExt->SdxbValues.Mstat & SDXB_RI)
        pHdr->ModemStatus |= 0x40;

    pHdr->Length = pIrp->IoStatus.Information;
    pRead->Irp = (ULONG_PTR)pIrp;
    pRead->Status = pIrp->IoStatus.Status;

    BytesToCopy = MIN(pIrp->IoStatus.Information, IrpSpace);
    RtlCopyMemory( pIrpBuffer, pIrp->AssociatedIrp.SystemBuffer, BytesToCopy);

    pTraceIrp->IoStatus.Information = pTraceIrp->IoStatus.Information + 
                                      sizeof(TRACE_HEADER) +
                                          sizeof(READ_TRACE) +
                                          BytesToCopy;

    InsertHeadList( &pPortExt->Trace.TraceQueue, &pTraceIrp->Tail.Overlay.ListEntry);
   
}
/*****************************************************************************************
**
**	void AsynTraceIoctlIrp( PASYN_PORT_EXT pPortExt, PIRP pIrp )
**
**	Purpose: Is to trace the Ioctl, if the irp is out of space then i will send it
**		on its merry way.
**
******************************************************************************************/
void AsynTraceIoctlIrp( PASYN_PORT_EXT   pPortExt, PIRP pIrp, BOOLEAN In, BOOLEAN Out)
{
    PUCHAR					pIrpBuffer;
    PIRP					pTraceIrp=NULL;
    PLIST_ENTRY				ListEntry;
    ULONG_PTR				IrpSpace, SpaceNeeded;
    PIO_STACK_LOCATION		pIrpSp, pTraceIrpSp;

    ASSERT(!(In && Out));


	if ( IsListEmpty(&pPortExt->Trace.TraceQueue) )
		return;

	pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
   
	// Previous versions of dgamode only trace IOCTL_SERIAL_WAIT_ON_MASK - make ours compatible for now...   
	if ( pIrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_WAIT_ON_MASK)
		Out = TRUE;   // TODO: currently both IN = FALSE and OUT = FALSE on call from AsynIoCompleteIrp
	else
   	return;      
   
	ListEntry = RemoveHeadList(&pPortExt->Trace.TraceQueue);
	pTraceIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
	pTraceIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
	pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer +  pTraceIrp->IoStatus.Information;
	IrpSpace = pTraceIrpSp->Parameters.DeviceIoControl.OutputBufferLength - pTraceIrp->IoStatus.Information;

	SpaceNeeded = sizeof(IOCTL_TRACE)+sizeof(TRACE_HEADER);

	if ( IrpSpace < SpaceNeeded )
	{ 
		pTraceIrp->IoStatus.Status = STATUS_SUCCESS;

		IoCompleteRequest( pTraceIrp, IO_NO_INCREMENT );

		if (!IsListEmpty( &pPortExt->Trace.TraceQueue))
		{
			ListEntry = RemoveHeadList( &pPortExt->Trace.TraceQueue);
			pTraceIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry);
			pTraceIrpSp = IoGetCurrentIrpStackLocation( pTraceIrp );
			pIrpBuffer = (PUCHAR)pTraceIrp->AssociatedIrp.SystemBuffer;
			IrpSpace = pTraceIrpSp->Parameters.DeviceIoControl.OutputBufferLength; 
		}
		else
			return;
	}

	if (pTraceIrp)
	{
        IOCTL_TRACE    *pIoctl;
        TRACE_HEADER   *pHdr;
        ULONG_PTR      BytesToCopy = 0;

        pHdr = (TRACE_HEADER*)pIrpBuffer;
        pIrpBuffer += sizeof(TRACE_HEADER);
        IrpSpace -= sizeof(TRACE_HEADER);
        pIoctl = (IOCTL_TRACE*)pIrpBuffer;
        pIrpBuffer += sizeof(IOCTL_TRACE);
        IrpSpace -= sizeof(IOCTL_TRACE);

        pHdr->FunctionType = TRACETYPE_IOCTL;
        KeQuerySystemTime( &pHdr->Time);

        pHdr->ModemStatus = 0;
        if (pPortExt->SdxbValues.Mstat & SDXB_RTS)
			pHdr->ModemStatus |= 0x02;
        if (pPortExt->SdxbValues.Mstat & SDXB_CTS)
            pHdr->ModemStatus |= 0x10;
        if (pPortExt->SdxbValues.Mstat & SDXB_DTR)
            pHdr->ModemStatus |= 0x01;
        if (pPortExt->SdxbValues.Mstat & SDXB_DSR)
            pHdr->ModemStatus |= 0x20;
        if (pPortExt->SdxbValues.Mstat & SDXB_DCD)
            pHdr->ModemStatus |= 0x80;
        if (pPortExt->SdxbValues.Mstat & SDXB_RI)
            pHdr->ModemStatus |= 0x40;

        if (In)
        {
            pHdr->Length = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
        }
        else if (Out)
        {
            pHdr->Length = pIrp->IoStatus.Information;
        }
        else
			pHdr->Length = 0;
   
        pIoctl->Irp = (ULONG_PTR)pIrp;
        pIoctl->Status = pIrp->IoStatus.Status;
        pIoctl->IoctlType = pIrpSp->Parameters.DeviceIoControl.IoControlCode;

		if ( pIrp->AssociatedIrp.SystemBuffer ) // Not all Ioctls have system buffers associated with them, this will avoid a page fault.
		{
			BytesToCopy = MIN(pHdr->Length, IrpSpace);
			RtlCopyMemory( pIrpBuffer, pIrp->AssociatedIrp.SystemBuffer, BytesToCopy);
		}

        pTraceIrp->IoStatus.Information = pTraceIrp->IoStatus.Information + 
                                             sizeof(TRACE_HEADER) +
                                             sizeof(IOCTL_TRACE) +
                                             BytesToCopy;

        InsertHeadList( &pPortExt->Trace.TraceQueue, &pTraceIrp->Tail.Overlay.ListEntry);
	 }
}

/******************************************************************************
**
** Function:   AsynTraceTimeout 
**
** Purpose:
**    We use this function to complete a pending TRACE ioctl every 500ms, if 
**    one is pending.
**
** Method:
**    Complete any Irps on the trace queue and schedule this DPC if we
**    are still tracing.
**
** Returns:
**    VOID
**
*******************************************************************************/
VOID AsynTraceTimeout(  IN PKDPC       Dpc,
						IN PVOID       DeferredContext,
						IN PVOID       SystemContext1,
						IN PVOID       SystemContext2  )
{
   PASYN_PORT_EXT   pPortExt = (PASYN_PORT_EXT)DeferredContext;
   PIRP				pIrp = NULL;

   DigiAcquireSpinLock( &pPortExt->Trace.TraceLock );
   KeCancelTimer( &pPortExt->Trace.TraceTimer );

   if (!IsListEmpty( &pPortExt->Trace.TraceQueue))
   {
      PLIST_ENTRY ListEntry = RemoveHeadList( &pPortExt->Trace.TraceQueue);
      pIrp = CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
   }
   if (!IsListEmpty( &pPortExt->Trace.TraceQueue))
   {
      KeSetTimer( &pPortExt->Trace.TraceTimer,
                  pPortExt->Trace.Timeout,
                  &pPortExt->Trace.TraceDpc );
   }

   DigiReleaseSpinLock( &pPortExt->Trace.TraceLock );

   if (pIrp)
   {
      pIrp->IoStatus.Status = STATUS_SUCCESS;
      IoCompleteRequest( pIrp, IO_NO_INCREMENT );
   }
}
/**********************************************************************
**
** Function:  AsynGetDeviceStatus
**
** Purpose:
**    Get a snapshot of the Port device status.
**
** Given: The port extension for the correct port, and the port status 
**		to fill in and pass back to the calling application.
**
**************************************************************************/
VOID AsynGetDeviceStatus( PASYN_PORT_EXT pPortExt, PPORT_STATUS pPortStatus )
{
   PASYN_SDXB	pSdxbExt = &pPortExt->SdxbValues;

   RtlFillMemory(pPortStatus, sizeof(PORT_STATUS), 0);

   DigiAcquireSpinLock( &pPortExt->Trace.TraceLock );

   pPortStatus->State =  pPortExt->State;

   pPortStatus->Mstat = pPortExt->SdxbValues.Mstat;
   pPortStatus->Mint = pPortExt->SdxbValues.Mint;
   pPortStatus->Ttime = (USHORT)pPortExt->SdxbValues.Ttime;
   pPortStatus->Tmax = (USHORT)pPortExt->SdxbValues.Tmax;

   pPortStatus->Estat = pPortExt->SdxbValues.Estat;
   pPortStatus->Mstat = pPortExt->SdxbValues.Mstat;
   pPortStatus->Mint = pPortExt->SdxbValues.Mint;


   pPortStatus->Iflag = pSdxbExt->PortParams.iflags;
   pPortStatus->Oflag = pPortExt->SdxbValues.PortParams.oflags;
   pPortStatus->Cflag = pPortExt->SdxbValues.PortParams.cflags;
   pPortStatus->Xflag = pPortExt->SdxbValues.PortParams.xflags;

   pPortStatus->Xon = pPortExt->SdxbValues.PortParams.xon;
   pPortStatus->Xoff = pPortExt->SdxbValues.PortParams.xoff;
   pPortStatus->Mflow = pPortExt->SdxbValues.PortParams.mflow;

   pPortStatus->Speed = pPortExt->SdxbValues.PortParams.speed;


   pPortStatus->Ltime = pPortExt->SdxbValues.Ltime;
   pPortStatus->Norun = (USHORT)pPortExt->SdxbValues.Norun;
   pPortStatus->Noflow = (USHORT)pPortExt->SdxbValues.Noflow;
   pPortStatus->Nframe = (USHORT)pPortExt->SdxbValues.Nframe;
   pPortStatus->Nparity =(USHORT) pPortExt->SdxbValues.Nparity;
   pPortStatus->Nbreak = (USHORT)pPortExt->SdxbValues.Nbreak;


   pPortStatus->Tout = (USHORT)pPortExt->Write.Sequence.CurrentCountOut;
   pPortStatus->Tin = (USHORT)pPortExt->Write.Sequence.CurrentCountIn;
   pPortStatus->Tmax = (USHORT)pPortExt->Write.cbMaxPutMsg;

   pPortStatus->Rout = (USHORT)pPortExt->Read.cbTotalRead;
   pPortStatus->Rmax = (USHORT)pPortExt->Read.cbMaxRxQ;
   pPortStatus->Rin = (USHORT)pPortExt->Read.cbRxQ;


   pPortStatus->Read.Timeout.Interval = pPortExt->Read.Timeout.Interval;
   pPortStatus->Read.Timeout.Total = pPortExt->Read.Timeout.Constant;
   pPortStatus->Read.Timeout.Multiplier = pPortExt->Read.Timeout.Multiplier;
   pPortStatus->Read.Timeout.Irp = pPortExt->Read.Timeout.pIrp;
   pPortStatus->Read.Timeout.Time = pPortExt->Read.Timeout.IntervalDuration;

   

   pPortStatus->Write.Timeout.Constant = pPortExt->Write.WriteTimer.TimeoutConstant;
   pPortStatus->Write.Timeout.Multiplier = pPortExt->Write.WriteTimer.TimeoutMultiplier;
   pPortStatus->Write.Timeout.Irp = NULL;
   pPortStatus->Write.Timeout.Time = pPortExt->Write.WriteTimer.TimerLength;


   pPortStatus->Wait.History = pPortExt->Wait.History;
   pPortStatus->Wait.Mask = pPortExt->Wait.Mask;
   pPortStatus->Wait.Irp = pPortExt->Wait.pPendingIrp;


   DigiReleaseSpinLock( &pPortExt->Trace.TraceLock );
}

