/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    read.c

Abstract:

    This module contains the code that is very specific to initialization
    and unload operations in the irenum driver

Author:

    Brian Lieuallen, 7-13-2000

Environment:

    Kernel mode

Revision History :

--*/

#include "internal.h"

VOID
ReadCancelRoutine(
    PDEVICE_OBJECT    DeviceObject,
    PIRP              Irp
    );


VOID
MoveDataFromBufferToIrp(
    PFDO_DEVICE_EXTENSION DeviceExtension
    );

VOID
SeeIfIrpShouldBeCompleted(
    PFDO_DEVICE_EXTENSION DeviceExtension
    );


NTSTATUS
IrCommRead(
    PDEVICE_OBJECT    DeviceObject,
    PIRP              Irp
    )

{
    PFDO_DEVICE_EXTENSION    DeviceExtension=(PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
    NTSTATUS                 Status=STATUS_SUCCESS;

    D_TRACE(DbgPrint("IRCOMM: IrCommRead\n");)

    if (DeviceExtension->Removing) {
        //
        //  the device has been removed, no more irps
        //
        Irp->IoStatus.Status=STATUS_DEVICE_REMOVED;
        IoCompleteRequest(Irp,IO_NO_INCREMENT);
        return STATUS_DEVICE_REMOVED;
    }


#if DBG
    {
        PIO_STACK_LOCATION       IrpSp = IoGetCurrentIrpStackLocation(Irp);

        RtlFillMemory(
            Irp->AssociatedIrp.SystemBuffer,
            IrpSp->Parameters.Read.Length,
            0xf1
            );


    }
#endif

    IoMarkIrpPending(Irp);

    Irp->IoStatus.Information=0;

    QueuePacket(&DeviceExtension->Read.Queue,Irp,FALSE);

    return STATUS_PENDING;

}

VOID
ReadStartRoutine(
    PVOID    Context,
    PIRP     Irp
    )

{

    PFDO_DEVICE_EXTENSION    DeviceExtension=(PFDO_DEVICE_EXTENSION)Context;
    PIO_STACK_LOCATION       IrpSp = IoGetCurrentIrpStackLocation(Irp);

    KIRQL                    OldIrql;
    KIRQL                    CancelIrql;

    Irp->IoStatus.Information=0;
    Irp->IoStatus.Status=STATUS_TIMEOUT;

    KeAcquireSpinLock(
        &DeviceExtension->Read.ReadLock,
        &OldIrql
        );

    ASSERT(!DeviceExtension->Read.TotalTimerSet);
    ASSERT(!DeviceExtension->Read.IntervalTimerSet);

    //
    //  add one refcount for this routine.
    //
    DeviceExtension->Read.IrpRefCount=1;
    DeviceExtension->Read.CurrentIrp=Irp;
    DeviceExtension->Read.IrpShouldBeCompleted=FALSE;
    DeviceExtension->Read.IrpShouldBeCompletedWithAnyData=FALSE;

    IoAcquireCancelSpinLock(&CancelIrql);

    if (Irp->Cancel) {
        //
        //  it has already been canceled, just mark it to complete
        //
        DeviceExtension->Read.IrpShouldBeCompleted=FALSE;
    }

    DeviceExtension->Read.IrpRefCount++;

    IoSetCancelRoutine(Irp,ReadCancelRoutine);

    IoReleaseCancelSpinLock(CancelIrql);

    if ((DeviceExtension->TimeOuts.ReadIntervalTimeout == MAXULONG)
        &&
        (DeviceExtension->TimeOuts.ReadTotalTimeoutMultiplier == 0)
        &&
        (DeviceExtension->TimeOuts.ReadTotalTimeoutConstant == 0)) {
         //
         //  The set of timeouts means that the request should simply return with
         //  whatever data is availible
         //
         DeviceExtension->Read.IrpShouldBeCompleted=TRUE;
         Irp->IoStatus.Status=STATUS_SUCCESS;

    }

    if ((DeviceExtension->TimeOuts.ReadTotalTimeoutMultiplier != 0) || (DeviceExtension->TimeOuts.ReadTotalTimeoutConstant != 0)) {
        //
        //  need a total timeout
        //
        LARGE_INTEGER    DueTime;
        ULONG            TimeoutMultiplier=DeviceExtension->TimeOuts.ReadTotalTimeoutMultiplier;

        if ((TimeoutMultiplier == MAXULONG) && (DeviceExtension->TimeOuts.ReadIntervalTimeout == MAXULONG)) {
            //
            //  this means that the read should complete as soon as any data is read, or the constant timeout
            //  expires.
            //
            DeviceExtension->Read.IrpShouldBeCompletedWithAnyData=TRUE;

            TimeoutMultiplier=0;
        }


        DueTime.QuadPart= ((LONGLONG)(DeviceExtension->TimeOuts.ReadTotalTimeoutConstant +
                           (TimeoutMultiplier * IrpSp->Parameters.Read.Length)))
                           * -10000;

        KeSetTimer(
            &DeviceExtension->Read.TotalTimer,
            DueTime,
            &DeviceExtension->Read.TotalTimerDpc
            );

        DeviceExtension->Read.TotalTimerSet=TRUE;
        DeviceExtension->Read.IrpRefCount++;
    }

    DeviceExtension->Read.IntervalTimeOut=0;

    if ((DeviceExtension->TimeOuts.ReadIntervalTimeout != 0) && (DeviceExtension->TimeOuts.ReadIntervalTimeout != MAXULONG)) {
        //
        //  capture the interval timer we will use for this irp
        //
        DeviceExtension->Read.IntervalTimeOut=DeviceExtension->TimeOuts.ReadIntervalTimeout;
    }

    KeReleaseSpinLock(
        &DeviceExtension->Read.ReadLock,
        OldIrql
        );



    MoveDataFromBufferToIrp(
        DeviceExtension
        );


    SeeIfIrpShouldBeCompleted(
        DeviceExtension
        );


    return;
}

BOOLEAN
CopyMemoryAndCheckForChar(
    PUCHAR    Destination,
    PUCHAR    Source,
    ULONG     Length,
    UCHAR     CharacterToCheck
    )

{
    PUCHAR    EndPoint=Destination+Length;
    BOOLEAN   ReturnValue=FALSE;

    while (Destination < EndPoint) {

        *Destination = *Source;

        if (*Destination == CharacterToCheck) {

//            DbgPrint("Got event char\n");
            ReturnValue=TRUE;
        }

        Destination++;
        Source++;
    }

    return ReturnValue;
}


NTSTATUS
DataAvailibleHandler(
    PVOID    Context,
    PUCHAR   Buffer,
    ULONG    BytesAvailible,
    PULONG   BytesUsed
    )

{

    PFDO_DEVICE_EXTENSION    DeviceExtension=(PFDO_DEVICE_EXTENSION)Context;
    ULONG                    BytesToCopy;
    ULONG                    BytesToCopyInFirstPass;
    BOOLEAN                  FoundEventCharacter;
    BOOLEAN                  FoundEventCharacter2=FALSE;
    BOOLEAN                  EightyPercentFull=FALSE;


    KIRQL     OldIrql;

    *BytesUsed = 0;

    ASSERT(BytesAvailible <= INPUT_BUFFER_SIZE);

    KeAcquireSpinLock(
        &DeviceExtension->Read.ReadLock,
        &OldIrql
        );

    //
    //  find out how many bytes can be copied
    //
    BytesToCopy = min(BytesAvailible , INPUT_BUFFER_SIZE - DeviceExtension->Read.BytesInBuffer);

    if (BytesToCopy < BytesAvailible) {

        if (DeviceExtension->Read.DtrState) {
            //
            //  only take the whole packet, so we don't have to worry about how to figure out if
            //  the ircomm control info in on the front of the buffer
            //
            DeviceExtension->Read.RefusedDataIndication=TRUE;

            D_TRACE1(DbgPrint("IRCOMM: data refused\n");)

            KeReleaseSpinLock(
                &DeviceExtension->Read.ReadLock,
                OldIrql
                );

            *BytesUsed=0;
            return STATUS_DATA_NOT_ACCEPTED;

        } else {
            //
            //  dtr is low, just throw the data away as we are probably trying to hangup
            //
            D_TRACE1(DbgPrint("IRCOMM: overflow data thrown away because dtr low - %d\n",BytesAvailible);)

            KeReleaseSpinLock(
                &DeviceExtension->Read.ReadLock,
                OldIrql
                );

            *BytesUsed=BytesAvailible;
            return STATUS_SUCCESS;


        }
    }

    //
    //  see how much more is left before we wrap the buffer
    //
    BytesToCopyInFirstPass= (ULONG)(&DeviceExtension->Read.InputBuffer[INPUT_BUFFER_SIZE] - DeviceExtension->Read.NextEmptyByte);

    //
    //  only can copy as many as are actually availible
    //
    BytesToCopyInFirstPass= min( BytesToCopy , BytesToCopyInFirstPass );

    FoundEventCharacter=CopyMemoryAndCheckForChar(
        DeviceExtension->Read.NextEmptyByte,
        Buffer,
        BytesToCopyInFirstPass,
        DeviceExtension->SerialChars.EventChar
        );

    DeviceExtension->Read.NextEmptyByte += BytesToCopyInFirstPass;
    *BytesUsed += BytesToCopyInFirstPass;
    DeviceExtension->Read.BytesInBuffer += BytesToCopyInFirstPass;

    if (BytesToCopyInFirstPass < BytesToCopy) {
        //
        //  must have wrapped, copy the rest
        //
        ULONG   BytesToCopyInSecondPass=BytesToCopy-BytesToCopyInFirstPass;

        ASSERT(DeviceExtension->Read.NextEmptyByte == &DeviceExtension->Read.InputBuffer[INPUT_BUFFER_SIZE]);

        //
        //  back to the beggining
        //
        DeviceExtension->Read.NextEmptyByte=&DeviceExtension->Read.InputBuffer[0];

        FoundEventCharacter2 =CopyMemoryAndCheckForChar(
            DeviceExtension->Read.NextEmptyByte,
            Buffer+BytesToCopyInFirstPass,
            BytesToCopyInSecondPass,
            DeviceExtension->SerialChars.EventChar
            );

        DeviceExtension->Read.NextEmptyByte += BytesToCopyInSecondPass;
        *BytesUsed += BytesToCopyInSecondPass;
        DeviceExtension->Read.BytesInBuffer += BytesToCopyInSecondPass;
    }

    if (DeviceExtension->Read.CurrentIrp != NULL) {
        //
        //  there is currently a read irp, Check to see if we should set an interval timeout
        //
        if (DeviceExtension->Read.IntervalTimerSet) {
            //
            //  the time is already set, cancel it first
            //
            BOOLEAN Canceled;

            Canceled=KeCancelTimer(
                &DeviceExtension->Read.IntervalTimer
                );

            if (Canceled) {
                //
                //  the timer had not fired yet, reset these since they will be changed below
                //
                DeviceExtension->Read.IntervalTimerSet=FALSE;
                DeviceExtension->Read.IrpRefCount--;

            } else {
                //
                //  the time has already expired. it will complete the current irp
                //
            }
        }

        //
        //  either this is the first time we are setting the timer, or we tried to
        //  cancel a previous version of it. If we did cancel it this is the same as
        //  it not being set. If it was set before and we did not cancel it, then we
        //  won't set a new one since the timer DPC is queued to run and will complete
        //  the irp
        //
        if ((DeviceExtension->Read.IntervalTimeOut != 0) && !DeviceExtension->Read.IntervalTimerSet) {
            //
            //  we need an interval timer
            //
            LARGE_INTEGER    DueTime;

            DueTime.QuadPart= (LONGLONG)DeviceExtension->Read.IntervalTimeOut * -10000;

            KeSetTimer(
                &DeviceExtension->Read.IntervalTimer,
                DueTime,
                &DeviceExtension->Read.IntervalTimerDpc
                );

            DeviceExtension->Read.IntervalTimerSet=TRUE;
            DeviceExtension->Read.IrpRefCount++;

        }
    }

    EightyPercentFull= DeviceExtension->Read.BytesInBuffer > (INPUT_BUFFER_SIZE * 8)/10;

    KeReleaseSpinLock(
        &DeviceExtension->Read.ReadLock,
        OldIrql
        );

    //
    //  try to move the buffered data to a read irp
    //
    MoveDataFromBufferToIrp(
        DeviceExtension
        );

    SeeIfIrpShouldBeCompleted(
        DeviceExtension
        );

    EventNotification(
        DeviceExtension,
        SERIAL_EV_RXCHAR |
        ((FoundEventCharacter || FoundEventCharacter) ? SERIAL_EV_RXFLAG : 0) |
        ((EightyPercentFull) ? SERIAL_EV_RX80FULL : 0)
        );

    ASSERT(*BytesUsed == BytesAvailible);


    return STATUS_SUCCESS;

}
#if 0
VOID
DebugCopyMemory(
    PUCHAR    Destination,
    PUCHAR    Source,
    ULONG     Length
    )

{
    PUCHAR    EndPoint=Destination+Length;

    while (Destination < EndPoint) {

        *Destination = *Source;

        if ((*Source == 0xe1) || (*Source == 0xe2)) {

            DbgPrint("IRCOMM: bad data at %p\n",Source);
            DbgBreakPoint();
        }

        Destination++;
        Source++;
    }

    return;
}
#endif


VOID
MoveDataFromBufferToIrp(
    PFDO_DEVICE_EXTENSION DeviceExtension
    )

{

    KIRQL     OldIrql;
    BOOLEAN   RequestDataIndications=FALSE;

    KeAcquireSpinLock(
        &DeviceExtension->Read.ReadLock,
        &OldIrql
        );

    if (DeviceExtension->Read.CurrentIrp != NULL) {

        PIRP                     Irp   = DeviceExtension->Read.CurrentIrp;
        PIO_STACK_LOCATION       IrpSp = IoGetCurrentIrpStackLocation(Irp);

        ULONG                    TotalBytesToCopy;
        ULONG                    BytesToCopyInFirstPass;
        ULONG                    BytesToCopyInSecondPass;
        ULONG                    BytesToEndOfBuffer;

        //
        //  find the max number of bytes that can be copied
        //
        TotalBytesToCopy = min(DeviceExtension->Read.BytesInBuffer , IrpSp->Parameters.Read.Length - (ULONG)Irp->IoStatus.Information );

        //
        //  Find out how many bytes are between the first filled byte and the end of the buffer
        //
        BytesToEndOfBuffer= (ULONG)(&DeviceExtension->Read.InputBuffer[INPUT_BUFFER_SIZE] - DeviceExtension->Read.NextFilledByte);

        //
        //  If the buffer wraps, the bytes to the end will be the limiting factor, otherwise
        //  it does not wrap and in that case the total bytes will be the limiting factor
        //
        BytesToCopyInFirstPass= min(TotalBytesToCopy , BytesToEndOfBuffer);


        RtlCopyMemory(
            (PUCHAR)Irp->AssociatedIrp.SystemBuffer + Irp->IoStatus.Information,
            DeviceExtension->Read.NextFilledByte,
            BytesToCopyInFirstPass
            );
#if DBG
        RtlFillMemory(
            DeviceExtension->Read.NextFilledByte,
            BytesToCopyInFirstPass,
            0xe1
            );
#endif

        DeviceExtension->Read.NextFilledByte += BytesToCopyInFirstPass;
        DeviceExtension->Read.BytesInBuffer -= BytesToCopyInFirstPass;
        Irp->IoStatus.Information+= BytesToCopyInFirstPass;

        BytesToCopyInSecondPass= TotalBytesToCopy - BytesToCopyInFirstPass;

        if (BytesToCopyInSecondPass > 0) {

            //
            //  back to the begining of the buffer
            //
            ASSERT( DeviceExtension->Read.NextFilledByte == &DeviceExtension->Read.InputBuffer[INPUT_BUFFER_SIZE]);

            DeviceExtension->Read.NextFilledByte=&DeviceExtension->Read.InputBuffer[0];

            RtlCopyMemory(
                (PUCHAR)Irp->AssociatedIrp.SystemBuffer + Irp->IoStatus.Information,
                DeviceExtension->Read.NextFilledByte,
                BytesToCopyInSecondPass
                );
#if DBG
            RtlFillMemory(
                DeviceExtension->Read.NextFilledByte,
                BytesToCopyInSecondPass,
                0xe2
                );
#endif

            DeviceExtension->Read.NextFilledByte += BytesToCopyInSecondPass;
            DeviceExtension->Read.BytesInBuffer -= BytesToCopyInSecondPass;
            Irp->IoStatus.Information+= BytesToCopyInSecondPass;

        }

        if (Irp->IoStatus.Information == IrpSp->Parameters.Read.Length) {
            //
            //  the irp is full, set status to success
            //
            Irp->IoStatus.Status=STATUS_SUCCESS;

            //
            //  since it is now full, it can complete now
            //
            DeviceExtension->Read.IrpShouldBeCompleted=TRUE;
        }

        if (DeviceExtension->Read.IrpShouldBeCompletedWithAnyData && (Irp->IoStatus.Information > 0)) {
            //
            //  the client wants the irp to complete when any data is present
            //
            Irp->IoStatus.Status=STATUS_SUCCESS;

            //
            //  make complete
            //
            DeviceExtension->Read.IrpShouldBeCompleted=TRUE;
        }

    }

    if ((DeviceExtension->Read.BytesInBuffer == 0) && DeviceExtension->Read.RefusedDataIndication) {
        //
        //  the buffer is empty now and we previous refused some indicated data
        //
        DbgPrint("IRCOMM: requesting data\n");

        DeviceExtension->Read.RefusedDataIndication=FALSE;
        RequestDataIndications=TRUE;
    }

    KeReleaseSpinLock(
        &DeviceExtension->Read.ReadLock,
        OldIrql
        );


    if (RequestDataIndications) {

        IndicateReceiveBufferSpaceAvailible(
            DeviceExtension->ConnectionHandle
            );
    }

    return;
}

VOID
ReadCancelRoutine(
    PDEVICE_OBJECT    DeviceObject,
    PIRP              Irp
    )

{

    PFDO_DEVICE_EXTENSION DeviceExtension=DeviceObject->DeviceExtension;
    KIRQL                 OldIrql;

    IoReleaseCancelSpinLock(Irp->CancelIrql);

    KeAcquireSpinLock(
        &DeviceExtension->Read.ReadLock,
        &OldIrql
        );

    DeviceExtension->Read.IrpRefCount--;
    DeviceExtension->Read.IrpShouldBeCompleted=TRUE;


    KeReleaseSpinLock(
        &DeviceExtension->Read.ReadLock,
        OldIrql
        );


    SeeIfIrpShouldBeCompleted(
        DeviceExtension
        );


    return;

}


VOID
IntervalTimeProc(
    PKDPC    Dpc,
    PVOID    Context,
    PVOID    SystemParam1,
    PVOID    SystemParam2
    )

{

    PFDO_DEVICE_EXTENSION DeviceExtension=Context;
    KIRQL                 OldIrql;
    PIRP                  Irp=NULL;

    D_ERROR(DbgPrint("IRCOMM: Interval timeout expired\n");)

    MoveDataFromBufferToIrp(
        DeviceExtension
        );


    KeAcquireSpinLock(
        &DeviceExtension->Read.ReadLock,
        &OldIrql
        );

    ASSERT(DeviceExtension->Read.IntervalTimerSet);

    //
    //  this timer is not set anymore
    //
    DeviceExtension->Read.IntervalTimerSet=FALSE;
    DeviceExtension->Read.IrpRefCount--;
    DeviceExtension->Read.IrpShouldBeCompleted=TRUE;


    KeReleaseSpinLock(
        &DeviceExtension->Read.ReadLock,
        OldIrql
        );


    SeeIfIrpShouldBeCompleted(
        DeviceExtension
        );


    return;

}


VOID
TotalTimerProc(
    PKDPC    Dpc,
    PVOID    Context,
    PVOID    SystemParam1,
    PVOID    SystemParam2
    )

{

    PFDO_DEVICE_EXTENSION DeviceExtension=Context;
    KIRQL     OldIrql;

    D_TRACE(DbgPrint("IRCOMM: Total timeout expired\n");)

    MoveDataFromBufferToIrp(
        DeviceExtension
        );


    KeAcquireSpinLock(
        &DeviceExtension->Read.ReadLock,
        &OldIrql
        );

    ASSERT(DeviceExtension->Read.TotalTimerSet);

    //
    //  this timer is not set anymore
    //
    DeviceExtension->Read.TotalTimerSet=FALSE;
    DeviceExtension->Read.IrpRefCount--;
    DeviceExtension->Read.IrpShouldBeCompleted=TRUE;

    KeReleaseSpinLock(
        &DeviceExtension->Read.ReadLock,
        OldIrql
        );

    SeeIfIrpShouldBeCompleted(
        DeviceExtension
        );

    return;

}

VOID
SeeIfIrpShouldBeCompleted(
    PFDO_DEVICE_EXTENSION DeviceExtension
    )

{
    KIRQL                 OldIrql;
    PIRP                  Irp=NULL;

    KeAcquireSpinLock(
        &DeviceExtension->Read.ReadLock,
        &OldIrql
        );

    if (DeviceExtension->Read.CurrentIrp != NULL) {
        //
        //  There is an irp present
        //
        if (DeviceExtension->Read.IrpShouldBeCompleted) {
            //
            //  either the irp is full, or a timer has expired. We are done with this irp in anycase.
            //
            PVOID     OldCancelRoutine;

            //
            //  try to cancel the timers, since we want to complete the irp now.
            //
            if (DeviceExtension->Read.IntervalTimerSet) {

                BOOLEAN    Canceled;

                Canceled=KeCancelTimer(
                    &DeviceExtension->Read.IntervalTimer
                    );

                if (Canceled) {
                    //
                    //  We ended up canceling the timer
                    //
                    DeviceExtension->Read.IrpRefCount--;
                    DeviceExtension->Read.IntervalTimerSet=FALSE;

                } else {
                    //
                    //  The timer is already running, we will just let it complete
                    //  and do the clean up.
                    //

                }
            }

            if (DeviceExtension->Read.TotalTimerSet) {

                BOOLEAN    Canceled;

                Canceled=KeCancelTimer(
                    &DeviceExtension->Read.TotalTimer
                    );

                if (Canceled) {
                    //
                    //  We ended up canceling the timer
                    //
                    DeviceExtension->Read.IrpRefCount--;
                    DeviceExtension->Read.TotalTimerSet=FALSE;

                } else {
                    //
                    //  The timer is already running, we will just let it complete
                    //  and do the clean up.
                    //

                }
            }

            OldCancelRoutine=IoSetCancelRoutine(DeviceExtension->Read.CurrentIrp,NULL);

            if (OldCancelRoutine != NULL) {
                //
                //  the irp has not been canceled yet, and will not be now
                //
                DeviceExtension->Read.IrpRefCount--;

            } else {
                //
                //  the cancel routine has run and decremented the ref count for us
                //

            }


            ASSERT(DeviceExtension->Read.IrpRefCount > 0);

            if (DeviceExtension->Read.IrpRefCount == 1) {
                //
                //  We can complete the irp now
                //
                ASSERT(!DeviceExtension->Read.TotalTimerSet);
                ASSERT(!DeviceExtension->Read.IntervalTimerSet);
#if DBG
                DeviceExtension->Read.IrpRefCount=0;
#endif
                Irp=DeviceExtension->Read.CurrentIrp;
                DeviceExtension->Read.CurrentIrp=NULL;

                InterlockedExchangeAdd(&DeviceExtension->Read.BytesRead,(LONG)Irp->IoStatus.Information);
            }

        }

    }

    KeReleaseSpinLock(
        &DeviceExtension->Read.ReadLock,
        OldIrql
        );


    if (Irp != NULL) {
        //
        //  we should complete this irp now
        //
        IoCompleteRequest(Irp,IO_NO_INCREMENT);
        StartNextPacket(&DeviceExtension->Read.Queue);
    }

    return;
}



VOID
ReadPurge(
    PFDO_DEVICE_EXTENSION DeviceExtension,
    ULONG                 Flags
    )

{

    KIRQL                 OldIrql;
    BOOLEAN               RequestDataIndications=FALSE;

    if (Flags == READ_PURGE_CLEAR_BUFFER) {
        //
        //  the caller wants the buffer cleared
        //
        KeAcquireSpinLock(
            &DeviceExtension->Read.ReadLock,
            &OldIrql
            );

        DeviceExtension->Read.BytesInBuffer=0;
        DeviceExtension->Read.NextFilledByte=&DeviceExtension->Read.InputBuffer[0];
        DeviceExtension->Read.NextEmptyByte=&DeviceExtension->Read.InputBuffer[0];

#if DBG
        RtlFillMemory(
            &DeviceExtension->Read.InputBuffer[0],
            sizeof(DeviceExtension->Read.InputBuffer),
            0xf7
            );
#endif

        if (DeviceExtension->Read.RefusedDataIndication) {
            //
            //  the buffer is empty now and we previous refused some indicated data
            //
            DbgPrint("IRCOMM: requesting data from purge\n");

            DeviceExtension->Read.RefusedDataIndication=FALSE;
            RequestDataIndications=TRUE;
        }

        KeReleaseSpinLock(
            &DeviceExtension->Read.ReadLock,
            OldIrql
            );

    }

    if (Flags == READ_PURGE_ABORT_IRP) {
        //
        //  the caller wants the current irp to complete
        //
        DeviceExtension->Read.IrpShouldBeCompleted=TRUE;

        SeeIfIrpShouldBeCompleted(
            DeviceExtension
            );
    }

    if (RequestDataIndications) {

        IndicateReceiveBufferSpaceAvailible(
            DeviceExtension->ConnectionHandle
            );
    }

    return;

}