/*++
 ****************************************************************************
 * 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( &pSdxbExt->PendingIrps );
        
        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;

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",pDeviceObject,pIrp,FALSE));

    // 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 );
            }            
            // has port failed?
            else if (pPortExt->DeviceStateFlags & PNP_DEVICE_FAILED)
            {
                Status = STATUS_IO_DEVICE_ERROR;
                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;
    }
    }        

    if (Status == STATUS_PENDING) {
        // Now Irp is queued - check if adapter has failed and if so
        // cancel all outstanding IO
        // NOTE: This is done AFTER queueing the Irp for a very good
        //       reason! To check before in a synchronized way, we'd
        //       have to acquire and hold the device extension spinlock
        //       across the queue call - I dont like holding multiple
        //       locks because it's an invitation to deadlock city.
        //       Instead, we take the optimistic approach and check in
        //       an unsynchronized manner before and again after the fact. 
        //       No need to acquire the spinlock to do the check because 
        //       the place that SETS the flag also cancels outstanding IO
        // NOTE ALSO: This check does NOT affect whether or not STATUS_PENDING
        //            is returned to the caller - we ALWAYS do this.
        AsynCheckForPortFailure(pPortExt);
    }

    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;

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",pDevice,pIrp,FALSE));

    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;
        }
        
        // has port failed?
        if (pPortExt->DeviceStateFlags & PNP_DEVICE_FAILED)
        {
            Status = STATUS_IO_DEVICE_ERROR;
            AsynIoCompleteRequest( pIrp, Status, pPortExt );
            DigiDump( DIGIREAD, ("In AsynDeviceInternalIoControl port has failed\n" ));
            goto Exit;
        }

        Status = ProcessInternalIoctl( pPortExt, pIrp );

        if (Status == STATUS_PENDING) {
            // Now Irp is queued - check if adapter has failed and if so
            // cancel all outstanding IO
            // NOTE: This is done AFTER queueing the Irp for a very good
            //       reason! To check before in a synchronized way, we'd
            //       have to acquire and hold the device extension spinlock
            //       across the queue call - I dont like holding multiple
            //       locks because it's an invitation to deadlock city.
            //       Instead, we take the optimistic approach and check in
            //       an unsynchronized manner before and again after the fact. 
            //       No need to acquire the spinlock to do the check because 
            //       the place that SETS the flag also cancels outstanding IO
            // NOTE ALSO: This check does NOT affect whether or not STATUS_PENDING
            //            is returned to the caller - we ALWAYS do this.
            AsynCheckForPortFailure(pPortExt);
        }
    }   

Exit:   

    return Status;
}
#endif
