|
|
/*++
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_TRACE1(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_TRACE1(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;
}
|