/*++
*******************************************************************************
* Copyright (c) 1990-99 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:

   dxbstream.c

Abstract:

        A stream object represents a single dxb channel, or stream.  All activity on a
        channel or stream is managed by the stream object: state information, tx and rx 
        data queues, and handlers for all DStreams API requests (Open, Close, PutMsg, 
        Ioctl, etc.).

        This module contains all stream object methods.

Author:

   Michael Zarns

--*/
#include "digidxb.h"


#define DXB_CHANNEL_NONE                (DXB_MAX_CHANNELS)

STATIC INLINE PVOID GetIrpBuffer( PIRP pIrp ) { return pIrp->AssociatedIrp.SystemBuffer; }

STATIC INLINE VOID SetRequestInProgress( PDXB_IO_REQUEST pRequest )
{
    ASSERT( !pRequest->InProgress );
    pRequest->InProgress = TRUE;
}

STATIC INLINE VOID ClearRequestInProgress( PDXB_IO_REQUEST pRequest )
{
    pRequest->InProgress = FALSE;
}

STATIC INLINE BOOLEAN IsRequestInProgress( PDXB_IO_REQUEST pRequest )
{
    return pRequest->InProgress;
}

STATIC VOID
dxbCancelHandler( 
        IN PDEVICE_OBJECT pDevice, 
        IN PIRP           pIrp );

STATIC INLINE VOID PushRequestIrp( PDXB_IO_REQUEST pRequest, PIRP pIrp )
{
    ASSERT( pRequest->pIrp == NULL );
    ASSERT( !IsRequestInProgress( pRequest ) );
    ClearRequestInProgress( pRequest );
    (void)IoSetCancelRoutine( pIrp, dxbCancelHandler );
    pRequest->pIrp = pIrp;
}

STATIC INLINE PIRP PopRequestIrp( PDXB_IO_REQUEST pRequest )
{
    PIRP pIrp = pRequest->pIrp;
    if ( pIrp != NULL )
    {
        (void)IoSetCancelRoutine( pIrp, NULL );
        pRequest->pIrp = NULL;
        ClearRequestInProgress( pRequest );
    }
    else
    {
        SRAPRINT2(DIGIWARN, SRA_MED_LEVEL, 
                  ( "DXB!PopRequestIrp: found NULL Irp on pRequest 0x%x.  Cancelled?\n", pRequest ) );
    }
    return pIrp;
}

STATIC INLINE PIRP PeekRequestIrp( PDXB_IO_REQUEST pRequest )
{
    return pRequest->pIrp;
}

//
// The Request field in the following #defines is the name of the request: Open, 
// Close, Ioctl, or Link.
//

#define dxbStreamPushIrp( pStream, Request, pIrp )          \
        PushRequestIrp( &(pStream)->Control.##Request, pIrp )

#define dxbStreamPopIrp( pStream, Request )                 \
        PopRequestIrp( &(pStream)->Control.##Request )

#define dxbStreamPeekIrp( pStream, Request )                \
        PeekRequestIrp( &(pStream)->Control.##Request )

#define dxbStreamPeekRequest( pStream, Request )            \
        GetIrpBuffer( dxbStreamPeekIrp( pStream, Request ) )

#define dxbStreamSetInProgress( pStream, Request )          \
        SetRequestInProgress( &(pStream)->Control.##Request )

#define dxbStreamClearInProgress( pStream, Request )        \
        ClearRequestInProgress( &(pStream)->Control.##Request )

#define dxbStreamIsInProgress( pStream, Request )           \
        IsRequestInProgress( &(pStream)->Control.##Request )


STATIC INLINE BOOLEAN
dxbStreamAssignChannel( 
        IN PDXB_DEVICE_EXTENSION  pExt, 
        IN OUT PDXB_STREAM_OBJECT pStream )
{
    ULONG i;
    BOOLEAN Found;
    for ( Found = FALSE, i = 1; i < DXB_MAX_CHANNELS && !Found; i++ )
    {
        DigiAcquireSpinLock( &pExt->Channels[i].DxbLock );
        if ( pExt->Channels[i].State == STREAM_STATE_INACTIVE )
        {
            pExt->Channels[i].pStream = pStream;
            pExt->Channels[i].State = STREAM_STATE_OPENING;
            pStream->Channel = i;
            Found = TRUE;
        }
        DigiReleaseSpinLock( &pExt->Channels[i].DxbLock );
    }
    return Found;
}


STATIC INLINE DXB_STREAM_STATE
dxbStreamGetState( IN PDXB_STREAM_OBJECT pStream )
{
    return pStream->pExt->Channels[pStream->Channel].State;
}


STATIC INLINE VOID 
dxbStreamSetState( IN PDXB_STREAM_OBJECT pStream, IN DXB_STREAM_STATE State )
{
    pStream->pExt->Channels[pStream->Channel].State = State;
}

STATIC INLINE VOID
dxbStreamLock( 
        IN PDXB_STREAM_OBJECT pStream )
{
    ASSERT( MmIsAddressValid( pStream ) );
    DigiAcquireSpinLock( &pStream->pExt->Channels[pStream->Channel].DxbLock );
    SRAPRINT2(DIGISTREAMLOCK, SRA_MED_LEVEL, 
              ( "DXB:Stream Locked, channel %d\n", pStream->Channel) );
}

STATIC INLINE PDXB_STREAM_OBJECT
dxbStreamLockByChannelEx( 
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN ULONG                 Channel,
        IN BOOLEAN               IsNullStreamError )
{
    PDXB_STREAM_OBJECT pStream;
    ASSERT( Channel <= DXB_MAX_CHANNELS );

    DigiAcquireSpinLock( &pExt->Channels[Channel].DxbLock );
    SRAPRINT2(DIGISTREAMLOCK, SRA_MED_LEVEL, 
              ( "DXB!Stream Locked, channel %d\n", Channel) );
        
    pStream = pExt->Channels[Channel].pStream;
    if ( pStream == NULL )
    {
        if ( IsNullStreamError )
        {
            SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                      ( "DXB!StreamLockByChannel: Invalid operation on NULL stream, channel 0x%x!\n", Channel ) );
        }
        DigiReleaseSpinLock( &pExt->Channels[Channel].DxbLock );
    }

    return pStream;
}

#define dxbStreamLockByChannel( pExt, Channel )         dxbStreamLockByChannelEx( (pExt), (Channel), TRUE )


STATIC INLINE VOID
dxbStreamUnlock( 
        IN PDXB_STREAM_OBJECT pStream )
{
    ASSERT( pStream != NULL );
    SRAPRINT2(DIGISTREAMLOCK, SRA_MED_LEVEL, 
              ( "DXB!Stream Unlocked, channel %d\n", pStream->Channel) );
    DigiReleaseSpinLock( &pStream->pExt->Channels[pStream->Channel].DxbLock );
}

STATIC INLINE VOID
dxbStreamInitialize( 
        PDXB_STREAM_OBJECT    pStream,
        PDXB_DEVICE_EXTENSION pExt )
{
    RtlZeroMemory( pStream, sizeof *pStream );
    pStream->Type = DXB_TYPE_STREAM_OBJECT;
    pStream->pExt = pExt;
    // Set Invalid Channel number
    pStream->Control.LinkedUnder = pStream->Control.LinkedAbove = DXB_CHANNEL_NONE;

    // Init Data queues
    {
        ULONG i;
        for ( i = DS_PRIORITY_MIN; i < DS_PRIORITY_LIM; i++ )
        {
            InitializeListHead( &pStream->Data.PutQ[i] );
            InitializeListHead( &pStream->Data.GetQ[i] );
            InitializeListHead( &pStream->Data.IncomingQ[i] );
        }
    }

    pStream->Write = pExt->CreditDefault;
    pStream->Read  = pExt->CreditDefault;

    // Init list entries
    InitializeListHead(&pStream->Entries.StreamList);
    InitializeListHead(&pStream->Entries.Command.Entry);
    InitializeListHead(&pStream->Entries.Credit.Entry);
}


STATIC INLINE VOID 
dxbStreamFree( 
   PDXB_STREAM_OBJECT pStream )
{
    DigiFreeMem( pStream );
}


STATIC INLINE VOID
dxbStreamDelete( 
   PDXB_STREAM_OBJECT pStream )
/*++

Comments
        Requires stream lock to be held while executing.

--*/
{
    ULONG Chan = pStream->Channel;
    PDXB_DEVICE_EXTENSION pExt = pStream->pExt;

    ASSERT( pExt->Channels[Chan].State != STREAM_STATE_INACTIVE );
    ASSERT( pExt->Channels[Chan].pStream == pStream );
    pExt->Channels[Chan].pStream = NULL;
    pExt->Channels[Chan].State = STREAM_STATE_INACTIVE;

    // Assert if there is any outstanding IO...
    {
        ULONG Priority;
        ASSERT(dxbStreamPeekIrp( pStream, Open ) == NULL);
        ASSERT(dxbStreamPeekIrp( pStream, Ioctl ) == NULL);
        ASSERT(dxbStreamPeekIrp( pStream, Link ) == NULL);
        ASSERT(dxbStreamPeekIrp( pStream, Close ) == NULL);
        for (Priority = DS_PRIORITY_MIN; Priority < DS_PRIORITY_COUNT; Priority++ )
        {
            ASSERT(IsListEmpty(&pStream->Data.PutQ[Priority]));
            ASSERT(IsListEmpty(&pStream->Data.GetQ[Priority]));
        }
    }

    // Make sure stream is not on any workq...
    ASSERT(!pStream->Entries.Command.fInList && 
           IsListEmpty(&pStream->Entries.Command.Entry));
    ASSERT(!pStream->Entries.Credit.fInList && 
           IsListEmpty(&pStream->Entries.Credit.Entry));

    dxbStreamUnlock( pStream );

    DigiAcquireSpinLock( &pExt->DxbLock );
    RemoveEntryList( &pStream->Entries.StreamList );
    InitializeListHead(&pStream->Entries.StreamList);
    DigiReleaseSpinLock( &pExt->DxbLock );

    dxbStreamFree( pStream );
}


STATIC INLINE PDXB_STREAM_OBJECT
dxbStreamNew( 
        PDXB_DEVICE_EXTENSION pExt,
        HANDLE                OriginalFileObject )
{
    PDXB_STREAM_OBJECT pStream = DigiAllocMem( NonPagedPool, sizeof *pStream );
    if ( pStream == NULL )
        goto Exit;

    dxbStreamInitialize( pStream, pExt );

    if ( !dxbStreamAssignChannel( pExt, pStream ) )
    {
        dxbStreamFree( pStream );
        pStream = NULL;
        goto Exit;
    }

    pStream->OriginalFileObject = OriginalFileObject;

    DigiAcquireSpinLock( &pExt->DxbLock );
    InsertTailList( &pExt->StreamList, &pStream->Entries.StreamList );
    DigiReleaseSpinLock( &pExt->DxbLock );

 Exit:
    return pStream;
}


STATIC INLINE NTSTATUS
dxbStreamMarkLinked( 
        PDXB_DEVICE_EXTENSION pExt, 
        ULONG                 ChanUpper, 
        ULONG                 ChanLower )
{
    NTSTATUS Status;

    DigiAcquireSpinLock( &pExt->DxbLock );

    {
        PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, ChanUpper );
        if ( pStream != NULL )
        {
            if ( dxbStreamGetState( pStream ) == STREAM_STATE_READY )
            {
                pStream->Control.LinkedAbove = ChanLower;
                Status = STATUS_SUCCESS;
            }
            else
                Status = STATUS_UNSUCCESSFUL;
            dxbStreamUnlock( pStream );
        }
        else
            Status = STATUS_UNSUCCESSFUL;
    }

    if ( NT_SUCCESS( Status ) )
    {
        PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, ChanLower );
        if ( pStream != NULL )
        {
            if ( dxbStreamGetState( pStream ) == STREAM_STATE_READY ) 
            {
                pStream->Control.LinkedUnder = ChanUpper;
                dxbStreamUnlock( pStream );
                Status = STATUS_SUCCESS;
            }
            else
                Status = STATUS_UNSUCCESSFUL;
        }
        else
            Status = STATUS_UNSUCCESSFUL;
    }

    DigiReleaseSpinLock( &pExt->DxbLock );
    return Status;
}

STATIC INLINE VOID
dxbStreamMarkUnlinked( PDXB_DEVICE_EXTENSION pExt, ULONG Channel )
{
    ULONG ChanLower;
    DigiAcquireSpinLock( &pExt->DxbLock );
    {
        // Mark upper stream as not linked
        PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, Channel );
        ASSERT( pStream != NULL );
        ChanLower = pStream->Control.LinkedAbove;
        pStream->Control.LinkedAbove = DXB_CHANNEL_NONE; // Invalid channel number
        dxbStreamUnlock( pStream );
    }

    {
        // Mark lower stream as not linked
        PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, ChanLower );
        ASSERT( pStream != NULL );
        pStream->Control.LinkedUnder = DXB_CHANNEL_NONE; // Invalid channel number
        dxbStreamUnlock( pStream );
    }
    DigiReleaseSpinLock( &pExt->DxbLock );
}

STATIC INLINE BOOLEAN
dxbStreamLinkedUnder( PDXB_STREAM_OBJECT pStream )
{
    return pStream->Control.LinkedUnder != DXB_CHANNEL_NONE;
}

STATIC INLINE BOOLEAN
dxbStreamLinkedAbove( PDXB_STREAM_OBJECT pStream )
{
    return pStream->Control.LinkedAbove != DXB_CHANNEL_NONE;
}


STATIC BOOLEAN
dxbStreamCancelQueue(
        IN PLIST_ENTRY  pQueue,
        IN BOOLEAN      fCancelAll,
        OUT PLIST_ENTRY pCancelledQ )
/*++

Description:

        Cancel all Irps on a given stream queue array: PutQ, GetQ.
        
        Requires the stream lock be held when this routine is called and released upon 
        return.

Parameters:

        pQueue
                The queue to cancel

        fCancelAll
                Determines whether *all* Irps are cancelled, or only those with the cancel 
                flag set.

        pCancelledQ
                Initialized List of Irps which the caller must complete upon return and after 
                releasing the stream lock.

Return Value:

        TRUE if any Irps were cancelled.

--*/
{
    BOOLEAN fCancelled = FALSE;
    ULONG Priority;
    for ( Priority = DS_PRIORITY_MIN; Priority < DS_PRIORITY_COUNT; Priority++ )
    {
        PLIST_ENTRY pListHead = &pQueue[Priority];
        PLIST_ENTRY pEntry = pListHead->Flink;
        while ( pEntry != pListHead )
        {
            PIRP pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
            ASSERT( pIrp->Type == IO_TYPE_IRP );
            pEntry = pEntry->Flink;

            if ( fCancelAll || pIrp->Cancel )
            {
                RemoveEntryList( &pIrp->Tail.Overlay.ListEntry );
                (void)IoSetCancelRoutine( pIrp, NULL );
                InsertTailList( pCancelledQ, &pIrp->Tail.Overlay.ListEntry );
                fCancelled = TRUE;
            }
        }
    }

    return fCancelled;
}


STATIC VOID
dxbCancelHandler( 
        IN PDEVICE_OBJECT pDevice, 
        IN PIRP           pOrigIrp )
/*++

Description

    Cancel routine for all pended Irps.  Walk list of stream objects
    for the specified device/adapter completing all Irps held by the
    stream having the cancel flag set.

--*/
{
    PDXB_DEVICE_EXTENSION pExt=pDevice->DeviceExtension;

    SRAENTER(DBGTYPE_ALL,SRA_MED_LEVEL, 
             ( "dxbCancelHandler: Device %p, OrigIrp=%p\n",
               pDevice,pOrigIrp));

    ASSERT( MmIsAddressValid( pOrigIrp ) );
    IoReleaseCancelSpinLock( pOrigIrp->CancelIrql );

    DxbCancelAdapterIo(pExt,STATUS_CANCELLED,FALSE/* only cancelled IO */);

    SRAEXIT(DBGTYPE_ALL,SRA_MED_LEVEL, ("\n"));
}


STATIC INLINE BOOLEAN
dxbIsValidSz( 
        PSZ   pBuf,
        ULONG cbMax )
{
    ULONG i;
    for ( i = 0; i < cbMax; i++ )
    {
        if ( pBuf[i] == 0 )
            break;
    }
    return i != cbMax;
}


STATIC INLINE NTSTATUS
dxbStreamDispatchOpen( 
        IN PDXB_STREAM_OBJECT pStream,
        IN PDS_OPEN           pOpen )
{
    NTSTATUS Status = DxbCmdOpen( pStream->pExt, pStream->Channel, pOpen );
    if ( NT_SUCCESS( Status ) )
        dxbStreamSetInProgress( pStream, Open );
    return Status;
}


NTSTATUS
dxbStreamOpen( 
        IN PDXB_DEVICE_EXTENSION pExt,
        IN PIRP                  pIrp,
        IN PIO_STACK_LOCATION    pIos )
{
    NTSTATUS Status;
    PDXB_STREAM_OBJECT pStream;
    PDS_OPEN pOpen;

    ASSERT( pExt );
    ASSERT( pIrp );
    ASSERT( pIos );

    if ( pIos->Parameters.DeviceIoControl.InputBufferLength  != sizeof *pOpen ||
         pIos->Parameters.DeviceIoControl.OutputBufferLength != sizeof *pOpen )
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    pOpen = GetIrpBuffer( pIrp );
    if ( !dxbIsValidSz( pOpen->DeviceName, sizeof pOpen->DeviceName ) ||
         strlen( pOpen->DeviceName ) == 0 )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( pOpen->FixedCost > MAXUCHAR )
    {
        Status = STATUS_INVALID_PARAMETER_2;
        goto Exit;
    }

    if ( ((pOpen->pUserData == NULL) ^ (pOpen->cbUserData == 0)) || pOpen->cbUserData > MAXUSHORT )
    {
        Status = STATUS_INVALID_PARAMETER_4;
        goto Exit;
    }

    if ( pOpen->pUserData )
    {
        if ( NT_ERROR( DigiLockBuffer( pIrp, pOpen->pUserData, pOpen->cbUserData, &pOpen->pProviderData ) ) )
        {
            Status = STATUS_INVALID_PARAMETER_3;
            goto Exit;
        }
    }
    else
        pOpen->pProviderData = pOpen->pUserData;


    SRAPRINT2(DIGIDSOPEN, SRA_MED_LEVEL, 
              ( "DXB!StreamOpen: pExt 0x%08x, DeviceName: %s\n", pExt, pOpen->DeviceName ) );

    pStream = dxbStreamNew( pExt, pIrp->Tail.Overlay.OriginalFileObject );
    if ( pStream == NULL )
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    IoMarkIrpPending( pIrp );
    dxbStreamPushIrp( pStream, Open, pIrp );

    Status = dxbStreamDispatchOpen( pStream, pOpen );
    if ( NT_ERROR( Status ) )
        dxbWorkCmdInsertTail( pStream );

    // Override status.  It will complete sometime later.
    Status = STATUS_PENDING;

 Exit:
    if ( NT_ERROR( Status ) )
        dxbIoCompleteRequest( pIrp, Status, 0 );

    return Status;
}


VOID
dxbStreamOnOpenComplete( 
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN ULONG                 Chan, 
        IN LONG                  ReturnValue,
        IN UCHAR                 FixedCost )
{
    PIRP pIrp;
    PDXB_STREAM_OBJECT pStream;

    ASSERT( Chan < DXB_MAX_CHANNELS );

    pStream = dxbStreamLockByChannel( pExt, Chan );
    ASSERT( pStream != NULL );
    ASSERT( pStream->Type == DXB_TYPE_STREAM_OBJECT );
    ASSERT( dxbStreamGetState( pStream ) == STREAM_STATE_OPENING );

    pIrp = dxbStreamPopIrp( pStream, Open );
    if ( pIrp )
    {
        PDS_OPEN pDsOpen;
        pDsOpen = GetIrpBuffer( pIrp );
        pDsOpen->ErrorCode = ReturnValue;
        if ( ReturnValue != 0 )
        {
            dxbStreamDelete( pStream );
            pDsOpen->hStream = NULL;
            SRAPRINT2(DIGIDSOPEN, SRA_MED_LEVEL, 
                      ( "DXB!OpenComplete: Open rejected, value 0x%x\n", ReturnValue ) );
        }
        else
        {
            // Open succeeded!
            dxbStreamSetState( pStream, STREAM_STATE_READY );
            pStream->Write.FCost = FixedCost;
            dxbWorkCreditInsertTail( pStream );
            dxbStreamUnlock( pStream );
            pDsOpen->hStream = (HANDLE)Chan;
            SRAPRINT2(DIGIDSOPEN, SRA_MED_LEVEL, 
                      ( "DXB:OpenComplete: Open succeeded, hStream 0x%x\n", pDsOpen->hStream ) );
        }

        dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, sizeof *pDsOpen );
    }
    else
    {
        // TODO: Cleanup here, close the stream
        SRAPRINT2(DIGIWARN, SRA_MED_LEVEL, 
                  ( "DXB!OpenComplete: Irp already cancelled?\n" ) );
        dxbStreamDelete( pStream );
    } 
    return;
}


STATIC BOOLEAN
dxbStreamFreeIncomingQ(
        IN PDXB_STREAM_OBJECT pStream )
/*++

Description:

        Free all incoming data packets on the Incoming queue array.
        
        Requires the stream lock be held when this routine is called and released upon 
        return.

Parameters:

        pStream
                The stream whose incoming messages should be freed.

Return Value:

        TRUE if any messages were freed.

--*/
{
    int i;
    BOOLEAN fFreed = FALSE;

    for ( i = 0; i < DS_PRIORITY_LIM; i++ )
    {
        PLIST_ENTRY ListHead = &pStream->Data.IncomingQ[i];
        while ( !IsListEmpty( ListHead ) )
        {
            PLIST_ENTRY pEntry = RemoveHeadList( ListHead );
            DigiFreeMem( CONTAINING_RECORD( pEntry, DXB_MSG, Entry ) );
            fFreed = TRUE;
        }
    }

    return fFreed;
}


STATIC INLINE NTSTATUS
dxbStreamDispatchClose( 
        IN PDXB_STREAM_OBJECT pStream,
        IN PDS_CLOSE          pClose )
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;

    ASSERT( !dxbStreamIsInProgress( pStream, Close ) );
    ASSERT( !dxbStreamLinkedUnder(pStream) );

    if ( !dxbStreamLinkedUnder( pStream ) )
    {
        Status = DxbCmdClose( pStream->pExt, pStream->Channel, pClose );
        if ( NT_SUCCESS( Status ) )
            dxbStreamSetInProgress( pStream, Close );
    }
    return Status;
}


NTSTATUS
dxbStreamClose( 
        PDXB_DEVICE_EXTENSION pExt,
        PIRP                  pIrp,
        PIO_STACK_LOCATION    pIos )
{
    PDXB_STREAM_OBJECT pStream;
    PDS_CLOSE pClose;
    ULONG Channel;
    NTSTATUS Status;
    LIST_ENTRY CompletedQ;

    ASSERT( pExt );
    ASSERT( pIrp );
    ASSERT( pIos );

    pClose = GetIrpBuffer( pIrp );

    if ( pIos->Parameters.DeviceIoControl.InputBufferLength  != sizeof *pClose )
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    if ( (ULONG)pClose->hStream == 0 || (ULONG)pClose->hStream >= DXB_MAX_CHANNELS )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    Channel = (ULONG)pClose->hStream;
    pStream = dxbStreamLockByChannel( pExt, Channel );
    SRAPRINT2(DIGIDSCLOSE, SRA_MED_LEVEL, 
              ( "DXB!StreamClose: pExt 0x%08x, Chan %d, pStream 0x%08x\n",
                pExt, pClose->hStream, pStream ) );

    if ( pStream == NULL )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( dxbStreamGetState( pStream ) != STREAM_STATE_READY )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        dxbStreamUnlock( pStream );
        goto Exit;
    }

    ASSERT( dxbStreamPeekIrp( pStream, Close ) == NULL );

    // The original request is ALWAYS completed asynchronously
    Status = STATUS_PENDING;
    IoMarkIrpPending( pIrp );

    // Be sure to remove this stream from the credit dpc queue
    dxbWorkCreditRemoveEntry( pStream );

    dxbStreamUnlock( pStream );

    // Cancel outstanding IO
    DxbCancelStreamIo(pExt, Channel, STATUS_CANCELLED, TRUE/* all */);

    // Get locked access to stream object again
    pStream = dxbStreamLockByChannel( pExt, Channel );

    if (pStream) {
        // Save pending close request and initiate processing of it
        dxbStreamSetState( pStream, STREAM_STATE_CLOSING );
        dxbStreamPushIrp( pStream, Close, pIrp );
        dxbStreamUnlock( pStream );
        dxbWorkCmdInsertTail( pStream );
    }
    else {
        // Must have gone away in the meantime - just complete the Irp
        // NOTE: Still return STATUS_PENDING from here
        dxbIoCompleteRequest(pIrp, STATUS_SUCCESS, 0);
    }

 Exit:
    if ( NT_ERROR( Status ) )
        dxbIoCompleteRequest( pIrp, Status, 0 );

    return Status;
}


VOID 
dxbStreamOnCloseComplete( 
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN ULONG                 Channel )
{
    PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, Channel );
    PIRP pIrp;

    ASSERT( pStream );
    ASSERT( dxbStreamGetState( pStream ) == STREAM_STATE_CLOSING );

    SRAPRINT2(DIGIDSCLOSE, SRA_MED_LEVEL, 
              ( "DXB!CloseComplete: pExt 0x%08x, Chan %d, pStream 0x%08x\n",
                pExt, Channel, pStream ) );

    // Make sure stream is NOT on any work queues; this can happen if this
    // routine is called from cancel/hw error processing. Caller is
    // responsible for synchronizing with DPC processing if necessary.
    // NOTE: these routines are safe to call if stream is NOT on queue.
    dxbWorkCmdRemoveEntry( pStream );
    dxbWorkCreditRemoveEntry( pStream );

    pIrp = dxbStreamPopIrp( pStream, Close );

    dxbStreamDelete( pStream );
    if ( pIrp )
        dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, 0 );
    else
    {
        SRAPRINT2(DIGIWARN, SRA_MED_LEVEL, 
                  ( "ISDN!StreamOnCloseComplete: Irp already cancelled?\n" ) );
    }
    return;
}


STATIC BOOLEAN
IsQEmptyUp( 
        IN PLIST_ENTRY pQueueHead,
        IN DS_PRIORITY Priority )
/*++

Description:

        Looks at all queues at priority level "Priority" and higher.  Returns TRUE if
        no Msgs of equal or higher priority are pending.

Parameters:

        pStream
                Stream on which to check

        Priority
                DS_PRIORITY_* level.  Checks queues for this and higher priority levels.

Return Value:

        TRUE if no equal/higher priority requests are pending.

--*/
{
    BOOLEAN Empty;
    ULONG i;

    for ( i = Priority, Empty = TRUE; i < DS_PRIORITY_LIM; i++ )
    {
        if ( !IsListEmpty( &pQueueHead[i] ) )
        {
            Empty = FALSE;
            break;
        }
    }
    return Empty;
}


STATIC BOOLEAN
IsQEmptyDown( 
        IN PLIST_ENTRY pQueueHead,
        IN DS_PRIORITY Priority )
/*++

Description:

        Looks at all queues at priority level Priority and *LOWER*.  Returns TRUE if
        no Msgs of equal or lower priority are pending.  Notice, this is different than
        IsQEmptyUp which looks for Priority and *Higher*.

Parameters:

        pStream
                Stream on which to check

        Priority
                DS_PRIORITY_* level.  Checks queues for this and lower priority levels.

Return Value:

        TRUE if no equal/higher priority requests are pending.

--*/
{
    BOOLEAN Empty;
    ULONG i;

    for ( i = Priority, Empty = TRUE; i > DS_PRIORITY_MIN; i-- )
    {
        if ( !IsListEmpty( &pQueueHead[i] ) )
        {
            Empty = FALSE;
            break;
        }
    }
    return Empty;
}


STATIC BOOLEAN 
IsPutQEmpty( 
        IN PDXB_STREAM_OBJECT pStream,
        IN DS_PRIORITY        Priority )
{
    return IsQEmptyUp( pStream->Data.PutQ, Priority );
}


STATIC BOOLEAN 
IsGetQEmpty( 
        IN PDXB_STREAM_OBJECT pStream,
        IN DS_PRIORITY        Priority )
{
    return IsQEmptyDown( pStream->Data.GetQ, Priority );
}


STATIC BOOLEAN 
IsIncomingQEmpty( 
        IN PDXB_STREAM_OBJECT pStream,
        IN DS_PRIORITY        Priority )
{
    return IsQEmptyUp( pStream->Data.IncomingQ, Priority );
}


STATIC INLINE NTSTATUS
dxbStreamDispatchPutMsg( 
        IN PDXB_STREAM_OBJECT pStream,
        IN PDS_PUTMSG         pPutMsg )
{
    NTSTATUS Status;

    if ( pPutMsg->Priority > DS_PRIORITY_LOW )
    {
        // Hi priority messages are not subject to flow control (credit charging)
        Status = DxbCmdPutMsg( pStream->pExt, pStream->Channel, pPutMsg );
        if ( NT_SUCCESS( Status ) )
            pStream->Stats.nPuts[DS_PRIORITY_HIGH]++;
        goto Exit;
    }

    // Otherwise, it's low priority and subject to flow control
    if ( dxbCreditApproved( &pStream->Write, dxbCreditPutMsgCost( &pStream->Write, pPutMsg ) ) )
    {
        Status = DxbCmdPutMsg( pStream->pExt, pStream->Channel, pPutMsg );
        if ( Status == STATUS_SUCCESS )
        {
            pStream->Stats.nPuts[DS_PRIORITY_LOW]++;
            dxbCreditCharge( &pStream->Write, dxbCreditPutMsgCost( &pStream->Write, pPutMsg ) );
            SRAPRINT2(DIGITXCREDIT, SRA_MED_LEVEL, 
                      ( "DXB:Credit Write: PutMsg sent:  cb 0x%08x, available 0x%08x\n",
                        dxbCreditPutMsgCost( &pStream->Write, pPutMsg ), dxbCreditAvailable( &pStream->Write ) ) );
        }
    }
    else
    {
        SRAPRINT2(DIGITXCREDIT, SRA_MED_LEVEL,    
                  ( "DXB:Credit Write: PutMsg flowcontrolled:         available 0x%08x\n",
                    dxbCreditAvailable( &pStream->Write ) ) );
        Status = STATUS_DXB_CREDIT_EXCEEDED;
    }
        
 Exit:
    ASSERT( Status == STATUS_SUCCESS || 
            Status == STATUS_DXB_CREDIT_EXCEEDED || 
            Status == STATUS_DXB_CMD_QUEUE_FULL );
    return Status;
}


NTSTATUS
dxbStreamDelayedPutMsgs( 
        PDXB_STREAM_OBJECT pStream,
        PLIST_ENTRY        pCompletedList )
{
    NTSTATUS Status = STATUS_SUCCESS;
    int i;

    for ( i = DS_PRIORITY_MAX; i >= 0; i-- )
    {
        while ( !IsListEmpty( &pStream->Data.PutQ[i] ) )
        {
            PIRP pIrp;
            PDS_PUTMSG pPutMsg;
            PLIST_ENTRY pEntry = RemoveHeadList( &pStream->Data.PutQ[i] );
            pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
            ASSERT( pIrp->Type == IO_TYPE_IRP );
            pPutMsg = GetIrpBuffer( pIrp );

            Status = dxbStreamDispatchPutMsg( pStream, pPutMsg );
            if ( NT_ERROR( Status ) )
            {
                // Replace Irp at head of list
                InsertHeadList( &pStream->Data.PutQ[i], &pIrp->Tail.Overlay.ListEntry );
                goto Exit;
            }
            (void)IoSetCancelRoutine( pIrp, NULL );
            InsertTailList( pCompletedList, &pIrp->Tail.Overlay.ListEntry );
        }
    }

 Exit:
    return Status;
}


NTSTATUS
dxbStreamPutMsg( 
   PDXB_DEVICE_EXTENSION pExt,
   PIRP                  pIrp,
   PIO_STACK_LOCATION    pIos )
{
    PDXB_STREAM_OBJECT pStream;
    NTSTATUS Status;
    PDS_PUTMSG pPutMsg;

    if ( pIos->Parameters.DeviceIoControl.InputBufferLength  != sizeof *pPutMsg )
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    pPutMsg = GetIrpBuffer( pIrp );

    if ( (ULONG)pPutMsg->hStream == 0 || (ULONG)pPutMsg->hStream >= DXB_MAX_CHANNELS )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( (pPutMsg->pControl == NULL) ^ (pPutMsg->cbControl == 0) )
    {
        Status = STATUS_INVALID_PARAMETER_2;
        goto Exit;
    }

    if ( (pPutMsg->pData == NULL) ^ (pPutMsg->cbData == 0) )
    {
        Status = STATUS_INVALID_PARAMETER_3;
        goto Exit;
    }

    if ( pPutMsg->pControl == NULL && pPutMsg->pData == NULL )
    {
        Status = STATUS_INVALID_PARAMETER_4;
        goto Exit;
    }

    if ( pPutMsg->Priority > DS_PRIORITY_MAX )
    {
        Status = STATUS_INVALID_PARAMETER_4;
        goto Exit;
    }

    if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pPutMsg->pControl, 
                                            pPutMsg->cbControl, &pPutMsg->pProviderControl ) ) )
        goto Exit;

    if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pPutMsg->pData, 
                                            pPutMsg->cbData, &pPutMsg->pProviderData ) ) )
        goto Exit;

    pStream = dxbStreamLockByChannel( pExt, (ULONG)pPutMsg->hStream );
    if ( pStream == NULL )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( dxbStreamGetState( pStream ) != STREAM_STATE_READY )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Unlock;
    }

    pStream->Stats.nPutRequests++;

    if ( IsPutQEmpty( pStream, pPutMsg->Priority ) )
    {
        Status = dxbStreamDispatchPutMsg( pStream, pPutMsg );

        if ( Status == STATUS_SUCCESS )
            goto Unlock;
                
        if ( Status == STATUS_DXB_CMD_QUEUE_FULL )
            dxbWorkCmdInsertTail( pStream );
    }

    Status = STATUS_PENDING;
    IoMarkIrpPending( pIrp );
    (void)IoSetCancelRoutine( pIrp, dxbCancelHandler );
    InsertTailList( &pStream->Data.PutQ[pPutMsg->Priority], &pIrp->Tail.Overlay.ListEntry );

 Unlock:
    dxbStreamUnlock( pStream );

 Exit:

    if ( Status != STATUS_PENDING )
        dxbIoCompleteRequest( pIrp, Status, 0 );
    return Status;
}


NTSTATUS
dxbStreamGetMsg( 
        PDXB_DEVICE_EXTENSION pExt,
        PIRP                  pIrp,
        PIO_STACK_LOCATION    pIos )
{
    PDXB_STREAM_OBJECT pStream;
    NTSTATUS Status;
    PDS_GETMSG pGetMsg;

    if ( pIos->Parameters.DeviceIoControl.InputBufferLength  != sizeof *pGetMsg ||
         pIos->Parameters.DeviceIoControl.OutputBufferLength != sizeof *pGetMsg )
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    pGetMsg = GetIrpBuffer( pIrp );

    if ( (ULONG)pGetMsg->hStream == 0 || (ULONG)pGetMsg->hStream >= DXB_MAX_CHANNELS )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( (pGetMsg->pControl == NULL) ^ (pGetMsg->cbMaxControl == 0) )
    {
        Status = STATUS_INVALID_PARAMETER_2;
        goto Exit;
    }

    if ( (pGetMsg->pData == NULL) ^ (pGetMsg->cbMaxData == 0) )
    {
        Status = STATUS_INVALID_PARAMETER_3;
        goto Exit;
    }

    if ( pGetMsg->pControl == NULL && pGetMsg->pData == NULL )
    {
        Status = STATUS_INVALID_PARAMETER_4;
        goto Exit;
    }

    if ( pGetMsg->Priority > DS_PRIORITY_MAX )
    {
        Status = STATUS_INVALID_PARAMETER_4;
        goto Exit;
    }

    if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pGetMsg->pControl, 
                                            pGetMsg->cbMaxControl, &pGetMsg->pProviderControl ) ) )
        goto Exit;

    if ( NT_ERROR( Status = DigiLockBuffer( pIrp, pGetMsg->pData, 
                                            pGetMsg->cbMaxData, &pGetMsg->pProviderData ) ) )
        goto Exit;

    pStream = dxbStreamLockByChannel( pExt, (ULONG)pGetMsg->hStream );
    if ( pStream == NULL )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( dxbStreamGetState( pStream ) != STREAM_STATE_READY )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Unlock;
    }

    // Sanity check queues.  We should never have equal-or-higher priority incoming 
    // messages queued while a GetMsg request is queued.
    ASSERT( IsListEmpty( &pStream->Data.GetQ[DS_PRIORITY_LOW] ) || IsIncomingQEmpty( pStream, DS_PRIORITY_LOW ) );
    ASSERT( IsListEmpty( &pStream->Data.GetQ[DS_PRIORITY_HIGH] )|| IsIncomingQEmpty( pStream, DS_PRIORITY_HIGH ) );

    // Only try to complete this GetMsg request if no previous requests of equal or 
    // higher priority are pending.
    if ( IsGetQEmpty( pStream, pGetMsg->Priority ) )
    {
        // Find highest priority incoming queue with pending messages
        PLIST_ENTRY pList = NULL;
        {
            LONG i;
            for ( i = DS_PRIORITY_MAX; i >= pGetMsg->Priority; i-- )
            {
                if ( !IsListEmpty( &pStream->Data.IncomingQ[i] ) )
                {
                    pList = &pStream->Data.IncomingQ[i];
                    break;
                }
            }
        }

        if ( pList != NULL )
        {
            // List found!  Grab msg and complete the GetMsg request.
            PDXB_MSG pMsg;
            PLIST_ENTRY pEntry = NULL;

            pEntry = RemoveHeadList( pList );
            pMsg = CONTAINING_RECORD( pEntry, DXB_MSG, Entry );

            if ( pMsg->cbCtrl > pGetMsg->cbMaxControl || 
                 pMsg->cbData > pGetMsg->cbMaxData )
            {
                // GetMsg can't hold this msg.  Replace msg on the queue, fail the request.
                SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB:StreamGetMsg: failing insufficient GetMsg request!\n" 
                            "    cbMaxControl 0x%08x, needed 0x%08x\n"
                            "    cbMaxData 0x%08x,    needed 0x%08x\n"
                            "    GetMsg Priority %d,      Incoming Priority %d\n",
                            pGetMsg->cbMaxControl, pMsg->cbCtrl, 
                            pGetMsg->cbMaxData, pMsg->cbData, 
                            pGetMsg->Priority, pList == &pStream->Data.IncomingQ[1] ) );
                InsertHeadList( pList, &pMsg->Entry );
                Status = STATUS_BUFFER_TOO_SMALL;
                goto Unlock;
            }

            pGetMsg->cbControl = pMsg->cbCtrl;
            if ( pMsg->cbCtrl != 0 )
                RtlCopyMemory( pGetMsg->pProviderControl, pMsg->pCtrl, pMsg->cbCtrl );

            pGetMsg->cbData = pMsg->cbData;
            if ( pMsg->cbData != 0 )
                RtlCopyMemory( pGetMsg->pProviderData, pMsg->pData, pMsg->cbData );

            pStream->Stats.nGets[pGetMsg->Priority]++;
            dxbCreditMarkRefundable( &pStream->Read, dxbCreditEventCost( &pStream->Read, pMsg->cbCtrl, pMsg->cbData ) );
            dxbWorkCreditInsertTail( pStream );
            DigiFreeMem( pMsg );
            Status = STATUS_SUCCESS;
            goto Unlock;
        }       
    }

    IoMarkIrpPending( pIrp );
    (void)IoSetCancelRoutine( pIrp, dxbCancelHandler );
    InsertTailList( &pStream->Data.GetQ[pGetMsg->Priority], &pIrp->Tail.Overlay.ListEntry );

    Status = STATUS_PENDING;

 Unlock:
    dxbStreamUnlock( pStream );

 Exit:

    if ( Status != STATUS_PENDING )
    {           
        dxbIoCompleteRequest( pIrp, Status, NT_SUCCESS( Status ) ? sizeof *pGetMsg : 0 );
    }

    return Status;
}


STATIC INLINE NTSTATUS
dxbStreamDispatchIoctl( 
        IN PDXB_STREAM_OBJECT pStream,
        IN PDS_IOCTL          pIoctl )
{
    NTSTATUS Status = DxbCmdIoctl( pStream->pExt, pStream->Channel, pIoctl );
    if ( NT_SUCCESS( Status ) )
    {
        pStream->Stats.nIoctls++;
        dxbStreamSetInProgress( pStream, Ioctl );
    }
    return Status;
}


NTSTATUS
dxbStreamIoctl( 
        PDXB_DEVICE_EXTENSION pExt,
        PIRP                  pIrp,
        PIO_STACK_LOCATION    pIos )
{
    PDXB_STREAM_OBJECT pStream;
    NTSTATUS Status;
    PDS_IOCTL pIoctl;

    if ( pIos->Parameters.DeviceIoControl.InputBufferLength  != sizeof *pIoctl ||
         pIos->Parameters.DeviceIoControl.OutputBufferLength != sizeof *pIoctl )
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    pIoctl = GetIrpBuffer( pIrp );
    if ( (ULONG)pIoctl->hStream == 0 || (ULONG)pIoctl->hStream >= DXB_MAX_CHANNELS )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( ( pIoctl->cbDataIn == 0 ) ^ ( pIoctl->pDataIn == NULL ) )
    {
        Status = STATUS_INVALID_PARAMETER_5;
        goto Exit;
    }

    if ( ( pIoctl->cbMaxDataOut == 0 ) ^ ( pIoctl->pDataOut == NULL ) )
    {
        Status = STATUS_INVALID_PARAMETER_7;
        goto Exit;
    }

    Status = DigiLockBuffer( pIrp, pIoctl->pDataIn, pIoctl->cbDataIn, &pIoctl->pProviderDataIn );

    if ( NT_ERROR( Status ) )
        goto Exit;

    Status = DigiLockBuffer( pIrp, pIoctl->pDataOut, pIoctl->cbMaxDataOut, &pIoctl->pProviderDataOut );

    if ( NT_ERROR( Status ) )
        goto Exit;

    pStream = dxbStreamLockByChannel( pExt, (ULONG)pIoctl->hStream );
    if ( pStream == NULL )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( dxbStreamGetState( pStream ) != STREAM_STATE_READY )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Unlock;
    }

    if ( dxbStreamPeekIrp( pStream, Ioctl ) != NULL )
    {
        Status = STATUS_UNSUCCESSFUL;
        goto Unlock;
    }

    IoMarkIrpPending( pIrp );
    dxbStreamPushIrp( pStream, Ioctl, pIrp );

    Status = dxbStreamDispatchIoctl( pStream, pIoctl );
    if ( NT_ERROR( Status ) )
        dxbWorkCmdInsertTail( pStream );

    Status = STATUS_PENDING;

 Unlock:
    dxbStreamUnlock( pStream );

 Exit:
    if ( Status != STATUS_PENDING )
    {
        DigiDump( DIGIERROR, ("***dxbStreamIoctl failed (0x%x)\n",Status) );
        dxbIoCompleteRequest( pIrp, Status, 0 );
    }

    return Status;
}


STATIC INLINE NTSTATUS
dxbStreamDispatchLink( 
        IN PDXB_STREAM_OBJECT pStream,
        IN PDS_LINK           pLink )
{
    NTSTATUS Status = DxbCmdLink( pStream->pExt, pStream->Channel, pLink );

    if ( NT_SUCCESS( Status ) )
    {
        pStream->Stats.nLinks++;
        dxbStreamSetInProgress( pStream, Link );
    }

    return Status;
}


NTSTATUS
dxbStreamLink( 
        PDXB_DEVICE_EXTENSION pExt,
        PIRP                  pIrp,
        PIO_STACK_LOCATION    pIos )
{
    NTSTATUS Status;
    PDS_LINK pLink;

    if ( pIos->Parameters.DeviceIoControl.InputBufferLength  != sizeof *pLink ||
         pIos->Parameters.DeviceIoControl.OutputBufferLength != sizeof *pLink )
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    pLink = GetIrpBuffer( pIrp );
    if ( (ULONG)pLink->hStreamUpper == 0 || (ULONG)pLink->hStreamUpper >= DXB_MAX_CHANNELS )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }
    if ( (ULONG)pLink->hStreamLower == 0 || (ULONG)pLink->hStreamLower >= DXB_MAX_CHANNELS )
    {
        Status = STATUS_INVALID_PARAMETER_2;
        goto Exit;
    }

    Status = dxbStreamMarkLinked( pExt, (ULONG)pLink->hStreamUpper, 
                                  (ULONG)pLink->hStreamLower );
    if ( NT_ERROR(Status) )
        goto Exit;

    {
        PDXB_STREAM_OBJECT pStream;
        pStream = dxbStreamLockByChannel( pExt, (ULONG)pLink->hStreamUpper );
        ASSERT( dxbStreamGetState( pStream ) == STREAM_STATE_READY );

        if ( dxbStreamPeekIrp( pStream, Link ) != NULL )
        {
            Status = STATUS_UNSUCCESSFUL;
            dxbStreamUnlock( pStream );
            dxbStreamMarkUnlinked( pStream->pExt, pStream->Channel );
            goto Exit;
        }

        IoMarkIrpPending( pIrp );
        dxbStreamPushIrp( pStream, Link, pIrp );

        Status = dxbStreamDispatchLink( pStream, pLink );
        if ( NT_ERROR( Status ) )
            dxbWorkCmdInsertTail( pStream );

        dxbStreamUnlock( pStream );
    }

    Status = STATUS_PENDING;

 Exit:
    if ( Status != STATUS_PENDING )
    {
        DigiDump( DIGIERROR, ("***dxbStreamLink failed (0x%x)\n",Status) );
        dxbIoCompleteRequest( pIrp, Status, 0 );
    }

    return Status;
}


NTSTATUS
dxbStreamPurge( 
        PDXB_DEVICE_EXTENSION pExt,
        PIRP                  pIrp,
        PIO_STACK_LOCATION    pIos )
{
    NTSTATUS Status;
    PDS_PURGE pPurge;

    if ( pIos->Parameters.DeviceIoControl.InputBufferLength != sizeof *pPurge )
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    pPurge = GetIrpBuffer( pIrp );
    if ( (ULONG)pPurge->hStream == 0 || (ULONG)pPurge->hStream >= DXB_MAX_CHANNELS )
    {
        Status = STATUS_INVALID_PARAMETER_1;
        goto Exit;
    }

    if ( pPurge->Flags & ~DS_PURGE_ALL )
    {
        Status = STATUS_INVALID_PARAMETER_2;
        goto Exit;
    }
        
    {
        PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, (ULONG)pPurge->hStream );
        LIST_ENTRY CompletedQ;

        if ( pStream == NULL )
        {
            Status = STATUS_INVALID_PARAMETER_1;
            goto Exit;
        }

        if ( dxbStreamGetState( pStream ) != STREAM_STATE_READY )
        {
            Status = STATUS_INVALID_PARAMETER_1;
            dxbStreamUnlock( pStream );
            goto Exit;
        }
        
        InitializeListHead( &CompletedQ );
        if ( pPurge->Flags & DS_PURGE_PUTMSG )
            (void)dxbStreamCancelQueue( &pStream->Data.PutQ[0], TRUE, &CompletedQ );
        
        if ( pPurge->Flags & DS_PURGE_GETMSG )
        {
            (void)dxbStreamCancelQueue( &pStream->Data.GetQ[0], TRUE, &CompletedQ );
            (void)dxbStreamFreeIncomingQ(pStream );
        }

        dxbStreamUnlock( pStream );

        while ( !IsListEmpty( &CompletedQ ) )
        {
            PIRP pCancelledIrp;
            PLIST_ENTRY pEntry = RemoveHeadList( &CompletedQ );
            pCancelledIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
            dxbIoCompleteRequest( pCancelledIrp, STATUS_CANCELLED, 0 );
        }
    }

    Status = STATUS_SUCCESS;

 Exit:
    dxbIoCompleteRequest( pIrp, Status, 0 );
    return Status;
}


NTSTATUS
dxbStreamDelayedDispatch( 
        IN PDXB_STREAM_OBJECT pStream )
/*++

Description:

        Dxb delayed command processing routine.  Called by DxbCmdDpc to process DStreams
        API requests that were not completed at request time.  Typically this happens
        when the stream was flow-controlled or the command queue was full.

Parameters:

        pStream
                Stream object which has work to do.

Return Value:

        STATUS_SUCCESS if successful, 
        STATUS_CMD_QUEUE_FULL if full, 
        or STATUS_DXB_CREDIT_EXCEEDED if stream is flow-controlled.

--*/
{
    BOOLEAN IsDxbCmdQFull = FALSE;
    NTSTATUS Status = STATUS_SUCCESS;

    dxbStreamLock( pStream );
    switch ( dxbStreamGetState( pStream ) )
    {
    case STREAM_STATE_READY:
    {
        // Look for an Ioctl first
        if ( dxbStreamPeekIrp( pStream, Ioctl ) && 
             !dxbStreamIsInProgress( pStream, Ioctl ) )
        {
            Status = dxbStreamDispatchIoctl( pStream, dxbStreamPeekRequest( pStream, Ioctl ) );
            if ( NT_ERROR( Status ) )
            {
                dxbWorkCmdInsertTail( pStream );
                dxbStreamUnlock( pStream );
                IsDxbCmdQFull = TRUE;
                break;
            }
        }

        // Then look for a Link
        if ( dxbStreamPeekIrp( pStream, Link ) && 
             !dxbStreamIsInProgress( pStream, Link ) )
        {
            Status = dxbStreamDispatchLink( pStream, dxbStreamPeekRequest( pStream, Link ) );
            if ( NT_ERROR( Status ) )
            {
                dxbStreamUnlock( pStream );
                IsDxbCmdQFull = TRUE;
                break;
            }
        }

        // Then handle PutMsg requests
        {
            LIST_ENTRY CompletedList;
            InitializeListHead( &CompletedList );

            Status = dxbStreamDelayedPutMsgs( pStream, &CompletedList );
            if ( Status == STATUS_DXB_CMD_QUEUE_FULL )
                IsDxbCmdQFull = TRUE;

            dxbStreamUnlock( pStream );
                                
            // Now complete the PutMsg IRPs that were sent down to the adapter
            while ( !IsListEmpty( &CompletedList ) )
            {
                PIRP pIrp;
                PLIST_ENTRY pEntry = RemoveHeadList(&CompletedList);
                                        
                pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
                ASSERT( pIrp->Type == IO_TYPE_IRP );
                dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, 0 );
            }
        }
        break;
    }

    case STREAM_STATE_OPENING:
    {
        Status = dxbStreamDispatchOpen( pStream, dxbStreamPeekRequest( pStream, Open ) );
        if ( NT_ERROR( Status ) )
            IsDxbCmdQFull = TRUE;

        dxbStreamUnlock( pStream );
        break;
    }

    case STREAM_STATE_CLOSING:
    {
        ASSERT( !dxbStreamIsInProgress( pStream, Close ) );
        if ( !dxbStreamLinkedUnder( pStream ) )
        {
            Status = dxbStreamDispatchClose( pStream, dxbStreamPeekRequest( pStream, Close ) );
            if ( NT_ERROR( Status ) )
                IsDxbCmdQFull = TRUE;
            dxbStreamUnlock( pStream );
        }
        else
        {
            PDXB_DEVICE_EXTENSION pExt = pStream->pExt;
            ULONG Channel = pStream->Channel;
            dxbStreamUnlock( pStream );
            dxbStreamOnCloseComplete( pExt, Channel );
            Status = STATUS_SUCCESS;
        }

        break;
    }

    case STREAM_STATE_CLOSED:
    case STREAM_STATE_INACTIVE:
    default:
        SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                  ( "DxbCmdThread: Found stream 0x%08x, state %s in WorkQ!\n", pStream,
                    dxbStreamGetState( pStream ) == STREAM_STATE_CLOSED ? "CLOSED" :
                    dxbStreamGetState( pStream ) == STREAM_STATE_INACTIVE ? "INACTIVE" : "UNKNOWN") );

        dxbStreamUnlock( pStream );
        break;
    }

    if ( IsDxbCmdQFull )
    {
        dxbWorkCmdInsertHead( pStream );
        Status = STATUS_DXB_CMD_QUEUE_FULL;
    }
    else
        Status = STATUS_SUCCESS;

    return Status;
}


NTSTATUS
dxbStreamDispatchCreditAwardMessage( 
        PDXB_STREAM_OBJECT pStream )
{
    NTSTATUS Status;
    ULONG nCredits;
    dxbStreamLock( pStream );
    if ( dxbStreamGetState( pStream ) != STREAM_STATE_READY )
    {
        DigiDump( DIGIERROR, ("***dxbStreamDispatchCreditAwardMessage: stream not ready\n") );
        Status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    nCredits = dxbCreditRefundable( &pStream->Read );
    ASSERT( nCredits <= dxbCreditCap( &pStream->Read ) );

    if ( nCredits > MAXUSHORT )
        nCredits = MAXUSHORT;

    Status = DxbCmdCreditAward( pStream->pExt, pStream->Channel, (USHORT)nCredits );
    if ( NT_SUCCESS( Status ) )
    {
        dxbCreditRefund( &pStream->Read, nCredits );
        SRAPRINT2(DIGIRXCREDIT, SRA_MED_LEVEL, 
                  ( "DXB:Credit Read: credit Award : cb 0x%08x, available 0x%08x\n",
                    nCredits, dxbCreditAvailable( &pStream->Read ) ) );
    }
    else
    {
        Status = STATUS_DXB_CMD_QUEUE_FULL;
        dxbWorkCreditInsertHead( pStream );
    }

 Exit:
    dxbStreamUnlock( pStream );
    return Status;
}


ULONG 
dxbStreamOnIoctlComplete( 
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN ULONG                 Channel,
        IN ULONG                 QOut,
        IN LONG                  ReturnValue,
        IN LONG                  ErrorCode,
        IN ULONG                 cbData )
{
    PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, Channel );
    PIRP pIrp = NULL;
    PDS_IOCTL pIoctl;
    PVOID pData;
    ULONG NewQOut;
    NTSTATUS Status;

    ASSERT( pStream != NULL );
    if ( pStream )
    {
        pIrp = dxbStreamPopIrp( pStream, Ioctl );
        if ( pIrp )
        {
            pIoctl = GetIrpBuffer( pIrp );
            dxbStreamUnlock( pStream );
        }
        else
        {
            SRAPRINT2(DIGIWARN, SRA_MED_LEVEL, 
                      ( "ISDN!StreamOnIoctlComplete: Irp already cancelled?\n" ) );
            pData = NULL;
            dxbStreamUnlock( pStream );
            goto Exit;
        }

        if ( cbData > pIoctl->cbMaxDataOut )
        {
            // Output buffer too small, abort!
            pData = NULL;
            Status = STATUS_BUFFER_OVERFLOW;
            goto Exit;
        }

        if ( ReturnValue == 0 && ErrorCode == 0 )
        {
            pData = pIoctl->pProviderDataOut;
            pIoctl->cbDataOut = cbData;
        }
        else
        {
            pData = NULL;
            pIoctl->cbDataOut = 0;
        }

        pIoctl->ReturnValue = ReturnValue;
        pIoctl->ErrorCode   = ErrorCode;
        Status = STATUS_SUCCESS;
    }

 Exit:

    NewQOut = DxbReadCData( pExt, QOut, NULL, 0, pData, cbData );
    if ( pIrp )
        dxbIoCompleteRequest( pIrp, Status, sizeof *pIoctl );
    return NewQOut;
}


VOID
dxbStreamOnLinkComplete( 
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN ULONG                 Channel,
        IN LONG                  ReturnValue,
        IN LONG                  ErrorCode )
{
    PDXB_STREAM_OBJECT pStream; 
    PIRP pIrp = NULL;
    PDS_LINK pLink;

    pStream = dxbStreamLockByChannel( pExt, Channel );
    ASSERT( pStream != NULL );
    if ( pStream )
    {
        pIrp = dxbStreamPopIrp( pStream, Link );
        if ( pIrp )
        {
            pLink = GetIrpBuffer( pIrp );
            ASSERT( dxbStreamGetState( pStream ) == STREAM_STATE_READY );
            dxbStreamUnlock( pStream );
        }
        else
        {
            SRAPRINT2(DIGIWARN, SRA_MED_LEVEL, 
                      ( "ISDN!StreamOnIoctlComplete: Irp already cancelled?\n" ) );
            dxbStreamUnlock( pStream );
            goto Exit;
        }
                
#if DBG
        {
            // Verify lower stream state
            PDXB_STREAM_OBJECT pLower;
            pLower = dxbStreamLockByChannel( pExt, (ULONG)pLink->hStreamLower );
            ASSERT( pLower != NULL );
            ASSERT( dxbStreamGetState( pLower ) == STREAM_STATE_READY );
            dxbStreamUnlock( pLower );
        }       
#endif

        pLink->ErrorCode = ErrorCode;
        if ( ErrorCode != 0 )
        {
            // Error
            dxbStreamMarkUnlinked( pExt, Channel );
            pLink->MuxId = -1;
        }
        else
            pLink->MuxId = ReturnValue;

        dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, sizeof *pLink );
    }

 Exit:
    return;
}


VOID
dxbStreamOnCreditEvent( 
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN ULONG                 Channel,
        IN ULONG                 nCredits )
{
    PDXB_STREAM_OBJECT pStream = dxbStreamLockByChannel( pExt, Channel );

    ASSERT( pStream );
    if ( pStream )
    {
        dxbCreditDeposit( &pStream->Write, nCredits );
        SRAPRINT2(DIGITXCREDIT, SRA_MED_LEVEL, 
                  ( "DXB:Credit Write: Credit Event: cb 0x%08x, available 0x%08x\n",
                    nCredits, dxbCreditAvailable( &pStream->Write ) ) );

        if ( !IsPutQEmpty( pStream, DS_PRIORITY_LOW ) )
            dxbWorkCmdInsertTail( pStream );
        dxbStreamUnlock( pStream );
    }
    return;
}


STATIC INLINE ULONG
dxbStreamQueueIncomingMessage( 
        PDXB_STREAM_OBJECT pStream,
        ULONG              QOut,
        ULONG              cbCtrl,
        ULONG              cbData,
        DS_PRIORITY        Priority )
{
    ULONG NewQOut;
    PDXB_MSG pMsg;
    ULONG cbAlloc = sizeof *pMsg + dxbAlign32( cbCtrl ) + cbData;

    // This must never happen!  We're queuing, so a request of the same priority cannot
    // be outstanding.
    ASSERT( IsListEmpty( &pStream->Data.GetQ[Priority] ) );

    pMsg = DigiAllocMem( NonPagedPool, cbAlloc );
    if ( pMsg == NULL )
    {
        NewQOut = DxbReadCData( pStream->pExt, QOut, NULL, cbCtrl, NULL, cbData );
        dxbCreditMarkRefundable( &pStream->Read, dxbCreditEventCost( &pStream->Read, cbCtrl, cbData ) );
        DigiDump( DIGIRXCREDIT, ( "DXB:Credit Read: Drop data    : cb 0x%08x, available 0x%08x\n",
                                  dxbCreditEventCost( &pStream->Read, cbCtrl, cbData ), dxbCreditAvailable( &pStream->Read ) ) );
    }
    else
    {
        pMsg->pCtrl = (PUCHAR)pMsg + sizeof *pMsg;
        pMsg->pData = (PUCHAR)pMsg->pCtrl + dxbAlign32( cbCtrl );
        pMsg->Type = DXB_TYPE_DXB_MSG;
        pMsg->cbCtrl = cbCtrl;
        pMsg->cbData = cbData;
        NewQOut = DxbReadCData( pStream->pExt, QOut, 
                                pMsg->pCtrl, pMsg->cbCtrl, 
                                pMsg->pData, pMsg->cbData );
        InsertTailList( &pStream->Data.IncomingQ[Priority], &pMsg->Entry );
    }
    return NewQOut;
}


ULONG 
dxbStreamOnDataEvent( 
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN ULONG                 Channel,
        IN ULONG                 QOut,
        IN ULONG                 cbCtrl,
        IN ULONG                 cbData,
        IN DS_PRIORITY           Priority )
{
    PDXB_STREAM_OBJECT pStream; 
    LIST_ENTRY FailedQ;
    ULONG NewQOut;
    PIRP pIrp;
    PDS_GETMSG pGetMsg;
        

    pStream = dxbStreamLockByChannel( pExt, Channel );

    ASSERT( pStream );

    if ( !pStream )
    {
        // Received data on a stream we've already closed.  This should only happen if 
        // firmware has a bug that allows it to send up data after stream close.
        NewQOut = DxbReadCData( pExt, QOut, NULL, cbCtrl, NULL, cbData );

        DigiDump( DIGIERROR, ( "***DXB:StreamOnDataEvent: killed data from closed stream\n" ) );
        goto Exit;
    }

    InitializeListHead( &FailedQ );

    if ( dxbStreamGetState( pStream ) != STREAM_STATE_READY )
    {
        DigiDump( DIGIERROR, ( "***DXB:StreamOnDataEvent: kill data from unready stream\n" ) );

        // Must be closing.  Drop rx data, ignore credit accounting since we're closing.
        dxbStreamUnlock( pStream );
        NewQOut = DxbReadCData( pExt, QOut, NULL, cbCtrl, NULL, cbData );
        goto Exit;
    }

    pStream->Stats.nIncoming[Priority]++;       

    if ( Priority == DS_PRIORITY_LOW )
        dxbCreditCharge( &pStream->Read, dxbCreditEventCost( &pStream->Read, cbCtrl, cbData ) );

    SRAPRINT2(DIGIRXCREDIT, SRA_MED_LEVEL,    
              ( "DXB:Credit Read: incoming data: cb 0x%08x, available 0x%08x\n",
                dxbCreditEventCost( &pStream->Read, cbCtrl, cbData ), dxbCreditAvailable( &pStream->Read ) ) );

    // Sanity check queues.  We should never have equal-or-higher priority incoming 
    // messages queued while a GetMsg request is queued.
    ASSERT( IsListEmpty( &pStream->Data.GetQ[DS_PRIORITY_LOW] )  || IsIncomingQEmpty( pStream, DS_PRIORITY_LOW ) );
    ASSERT( IsListEmpty( &pStream->Data.GetQ[DS_PRIORITY_HIGH] ) || IsIncomingQEmpty( pStream, DS_PRIORITY_HIGH ) );

    // Only try to complete a GetMsg if no earlier Incoming messages of EQUAL OR HIGHER
    // priority are pending - we can never return incoming messages out of order!
    pGetMsg = NULL;
    if ( IsIncomingQEmpty( pStream, Priority ) )
    {
        // Find GetMsg queue of EQUAL OR LOWER priority containing pending requests
        // We can satisfy a low priority GetMsg with high priority data, but we can't
        // satisfy a high priority GetMsg with low priority data.
        int i;
        for ( i = Priority; i >= DS_PRIORITY_MIN; i-- )
        {
            // The pending GetMsg may not be large enough to hold this message.  
            // Repeatedly try to find one that can be completed successfully.
            while ( !IsListEmpty( &pStream->Data.GetQ[i] ) )
            {
                // Request available!  Grab it and check if it's large enough
                PLIST_ENTRY pList  = &pStream->Data.GetQ[i];
                PLIST_ENTRY pEntry = RemoveHeadList( pList );
                pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
                ASSERT( pIrp->Type == IO_TYPE_IRP );
                (void)IoSetCancelRoutine( pIrp, NULL );
                pGetMsg = GetIrpBuffer( pIrp );

                if ( cbCtrl <= pGetMsg->cbMaxControl && cbData <= pGetMsg->cbMaxData )
                {
                    // Found Getmsg to complete.  Stop looking and fill it in.
                    goto Continue;
                }

                // This GetMsg request was too small.  Fail it and look for another
                SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB:StreamOnDataEvent: failing insufficient GetMsg request!\n" 
                            "    cbMaxControl 0x%08x, needed 0x%08x\n"
                            "    cbMaxData 0x%08x,    needed 0x%08x\n"
                            "    GetMsg->Priority %d,     Incoming Priority %d\n",
                            pGetMsg->cbMaxControl, cbCtrl, pGetMsg->cbMaxData, cbData,
                            pGetMsg->Priority, Priority ) );
                InsertTailList( &FailedQ, &pIrp->Tail.Overlay.ListEntry );
                pGetMsg = NULL;
            }
        }
    }

 Continue:
    if ( pGetMsg == NULL )
    {
        // No satisfactory request found.  Queue the message            
        NewQOut = dxbStreamQueueIncomingMessage( pStream, QOut, cbCtrl, cbData, Priority );
        dxbStreamUnlock( pStream );
    }
    else
    {
        // Immediately fulfill the GetMsg request
        dxbCreditMarkRefundable( &pStream->Read, dxbCreditEventCost( &pStream->Read, cbCtrl, cbData ) );
        SRAPRINT2(DIGIRXCREDIT, SRA_MED_LEVEL, 
                  ( "DXB:Credit Read: found GetMsg : cb 0x%08x, available 0x%08x\n",
                    dxbCreditEventCost( &pStream->Read, cbCtrl, cbData ), dxbCreditAvailable( &pStream->Read ) ) );
        dxbWorkCreditInsertTail( pStream );
        dxbStreamUnlock( pStream );

        pGetMsg->cbControl = cbCtrl;
        pGetMsg->cbData = cbData;
        pGetMsg->Priority = Priority;
        NewQOut = DxbReadCData( pExt, QOut, 
                                pGetMsg->pProviderControl, cbCtrl,
                                pGetMsg->pProviderData, cbData );
        pStream->Stats.nGets[Priority]++;
    }

    while ( !IsListEmpty( &FailedQ ) )
    {
        PIRP pFailedIrp;
        PLIST_ENTRY pEntry = RemoveHeadList( &FailedQ );
        pFailedIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
        dxbIoCompleteRequest( pFailedIrp, STATUS_BUFFER_TOO_SMALL, 0 );
    }

    if ( pGetMsg != NULL )
        dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, sizeof *pGetMsg );

 Exit:
        
    return NewQOut;
}

VOID
DxbCancelStreamIo(
    IN PDXB_DEVICE_EXTENSION pExt,
    IN ULONG Channel,
    IN NTSTATUS CompletionStatus,
    BOOLEAN fCancelAll
    )
/*++

Description

    Cancel all IO on specifed stream. If fCancelAll flag is FALSE then
    only already cancelled IO is completed, otherwise ALL IO is completed.

--*/
{
    PDXB_STREAM_OBJECT pStream;
    LIST_ENTRY CancelList;
    BOOLEAN CompleteOpen  = FALSE;
    BOOLEAN CompleteClose = FALSE;

    SRAENTER(DBGTYPE_ALL,SRA_MED_LEVEL, 
             ("DxbCancelStreamIo: DevExt %p, Channel %d, Status %x, "
              "CancelAll %s\n",
              pExt,Channel,CompletionStatus,
              fCancelAll?"true":"false"));

    InitializeListHead( &CancelList );

    pStream = dxbStreamLockByChannelEx( pExt, Channel, FALSE );

    if ( pStream )
    {
        // Cancel all cancel flagged Irps on this stream

        // Open
        {
            PIRP pIrp = dxbStreamPeekIrp( pStream, Open );
            if ( pIrp && (fCancelAll || pIrp->Cancel ))
            {
                CompleteOpen = TRUE;
            }
        }

        // Ioctl
        {
            PIRP pIrp = dxbStreamPeekIrp( pStream, Ioctl );
            if ( pIrp && (fCancelAll || pIrp->Cancel ))
            {
                (void)dxbStreamPopIrp( pStream, Ioctl );
                InsertTailList( &CancelList, &pIrp->Tail.Overlay.ListEntry );
            }
        }

        // Link
        {
            PIRP pIrp = dxbStreamPeekIrp( pStream, Link );
            if ( pIrp && (fCancelAll || pIrp->Cancel ))
            {
                (void)dxbStreamPopIrp( pStream, Link );
                InsertTailList( &CancelList, &pIrp->Tail.Overlay.ListEntry );
            }
        }

        // PutMsg Queues
        (void)dxbStreamCancelQueue( &pStream->Data.PutQ[0], 
                                    fCancelAll, 
                                    &CancelList );

        // GetMsg Queues
        (void)dxbStreamCancelQueue( &pStream->Data.GetQ[0], 
                                    fCancelAll,
                                    &CancelList );


        // Close
        {
            PIRP pIrp = dxbStreamPeekIrp( pStream, Close );
            if ( pIrp && (fCancelAll || pIrp->Cancel ))
            {
                CompleteClose = TRUE;
            }
        }

        dxbStreamUnlock( pStream );

        if (CompleteOpen) {
            SRAPRINT(SRA_MED_LEVEL, 
                     ( "Completing open processing on stream with error\n"));

            dxbStreamOnOpenComplete(pExt,Channel,EIO,0);
        }
            
        while ( !IsListEmpty( &CancelList ) )
        {
            PIRP pIrp;
            PLIST_ENTRY pEntry = RemoveHeadList( &CancelList );
            pIrp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
            ASSERT( pIrp->Type == IO_TYPE_IRP );

            SRAPRINT(SRA_MED_LEVEL, 
                     ( "Completing Irp 0x%x with status %x\n", 
                       pIrp,CompletionStatus));

            dxbIoCompleteRequest( pIrp, CompletionStatus, 0 );
        }

        if (CompleteClose) {
            BOOLEAN GotCommandDpc=FALSE;
            BOOLEAN GotCreditDpc =FALSE;

            // Want to complete close processing - in order to do this,
            // we MUST synchronize with DPC processing of command and
            // credit queues since it might be simultaneously processing
            // the same stream... If we cant get both DPCs then just bail
            // since the HW check will call us back here is a short time...
            // I don't believe the other callers of this routine will ever
            // do so with a close pending so this should be safe - famous
            // last words...
            GotCommandDpc = dxbDpcSetInProgress( pExt, 
                                                 &pExt->Work.Command.DxbDpc );
            if (GotCommandDpc)
                GotCreditDpc = dxbDpcSetInProgress( pExt,
                                                    &pExt->Work.Credit.DxbDpc );

            if (GotCommandDpc && GotCreditDpc) {
                // OK - no DPC is running; complete close processing
                SRAPRINT(SRA_MED_LEVEL, 
                         ( "Completing close processing on stream\n"));

                dxbStreamOnCloseComplete(pExt,Channel);
            }

            // Now, release DPCs
            if (GotCreditDpc)
                dxbDpcSetNotInProgress( pExt,
                                        &pExt->Work.Credit.DxbDpc );
            if (GotCommandDpc)
                dxbDpcSetNotInProgress( pExt,
                                        &pExt->Work.Command.DxbDpc );
        }
    }

    SRAEXIT(DBGTYPE_ALL,SRA_MED_LEVEL, ("\n"));
}


VOID
DxbCancelAdapterIo( 
    IN PDXB_DEVICE_EXTENSION pExt,
    IN NTSTATUS CompletionStatus,
    BOOLEAN fCancelAll
    )
/*++

Description

    Cancel all IO related to a specific extension. If fCancelAll is FALSE,
    only Irps that have been marked for cancellation are completed, otherwise
    ALL Irps are cancelled

--*/
{
    ULONG Channel;

    SRAENTER(DBGTYPE_ALL,SRA_MED_LEVEL, 
             ( "DxbCancelAdapterI0: DevExt %p, Status %x, CancelAll %s\n",
               pExt,CompletionStatus,fCancelAll?"true":"false"));

    // Walk through all channels on this adapter looking for open streams
    for ( Channel = 1; Channel < DXB_MAX_CHANNELS; Channel++ )
    {
        DxbCancelStreamIo(pExt,Channel,CompletionStatus,fCancelAll);
    }

    SRAEXIT(DBGTYPE_ALL,SRA_MED_LEVEL, ("\n"));
}
