windows-nt/Source/XPSP1/NT/net/irda/comm/ircomm/read.c
2020-09-26 16:20:57 +08:00

957 lines
23 KiB
C

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