📄 read.cpp
字号:
/*++
Abstract:
This module contains the code that is very specific to read
operations in the serial driver
--*/
#include "precomp.h"
NTSTATUS
KdSerialDevice::DispatchRead(IN KdIrp &Irp)
/*++
Routine Description:
This is the dispatch routine for reading. It validates the parameters
for the read request and if all is ok then it places the request
on the work queue.
Arguments:
Irp - Pointer to the IRP for the current request
Return Value:
If the io is zero length then it will return STATUS_SUCCESS,
otherwise this routine will return the status returned by
the actual start read routine.
--*/
{
DebugDump(DBG_DIAG6, ("Dispatch entry for: %x\n",Irp) );
if (CompleteIfError(Irp) != STATUS_SUCCESS)
return STATUS_CANCELLED;
Irp.Information() = 0L;
// Quick check for a zero length read. If it is zero length
// then we are already done!
if (Irp.ReadLength())
{
// Well it looks like we actually have to do some
// work. Put the read on the queue so that we can
// process it when our previous reads are done.
return StartOrQueue(
Irp,
&m_ReadQueue,
&m_CurrentReadIrp,
StartRead
);
}
else
{
Irp.Status() = STATUS_SUCCESS;
DebugDump(DBG_DIAG6, ("Complete Irp: %x\n",Irp) );
Irp.Complete();
return STATUS_SUCCESS;
}
}
NTSTATUS KdSerialDevice::StartRead()
/*++
Routine Description:
This routine is used to start off any read. It initializes
the Iostatus fields of the irp. It will set up any timers
that are used to control the read. It will attempt to complete
the read from data already in the interrupt buffer. If the
read can be completed quickly it will start off another if
necessary.
Return Value:
This routine will return the status of the first read
irp. This is useful in that if we have a read that can
complete right away (AND there had been nothing in the
queue before it) the read could return SUCCESS and the
application won't have to do a wait.
--*/
{
SERIAL_UPDATE_CHAR updateChar;
KdIrp newIrp;
KIRQL oldIrql;
BOOLEAN returnWithWhatsPresent;
BOOLEAN os2ssreturn;
BOOLEAN crunchDownToOne;
BOOLEAN useTotalTimer;
BOOLEAN useIntervalTimer;
ULONG multiplierVal;
ULONG constantVal;
LARGE_INTEGER totalTime;
SERIAL_TIMEOUTS timeoutsForIrp;
BOOLEAN setFirstStatus = FALSE;
NTSTATUS firstStatus;
do {
// Check to see if this is a resize request. If it is
// then go to a routine that specializes in that.
if (m_CurrentReadIrp.MajorFunction() != IRP_MJ_READ)
{
NTSTATUS localStatus = ResizeBuffer();
if (!setFirstStatus)
{
firstStatus = localStatus;
setFirstStatus = TRUE;
}
}
else
{
m_NumberNeededForRead = m_CurrentReadIrp.ReadLength();
// Calculate the timeout value needed for the
// request. Note that the values stored in the
// timeout record are in milliseconds.
useTotalTimer = FALSE;
returnWithWhatsPresent = FALSE;
os2ssreturn = FALSE;
crunchDownToOne = FALSE;
useIntervalTimer = FALSE;
// We get the *current* timeout values to use for timing
// this read.
m_ControlLock.Lock();
timeoutsForIrp = m_Timeouts;
m_ControlLock.Release();
// Calculate the interval timeout for the read.
if (timeoutsForIrp.ReadIntervalTimeout &&
(timeoutsForIrp.ReadIntervalTimeout != MAXULONG))
{
useIntervalTimer = TRUE;
m_IntervalTime.QuadPart =
UInt32x32To64(
timeoutsForIrp.ReadIntervalTimeout,
10000
);
if (m_IntervalTime.QuadPart >= m_CutOverAmount.QuadPart)
m_IntervalTimeToUse = &m_LongIntervalAmount;
else
m_IntervalTimeToUse = &m_ShortIntervalAmount;
}
if (timeoutsForIrp.ReadIntervalTimeout == MAXULONG)
{
// We need to do special return quickly stuff here.
//
// 1) If both constant and multiplier are
// 0 then we return immediately with whatever
// we've got, even if it was zero.
//
// 2) If constant and multiplier are not MAXULONG
// then return immediately if any characters
// are present, but if nothing is there, then
// use the timeouts as specified.
//
// 3) If multiplier is MAXULONG then do as in
// "2" but return when the first character
// arrives.
if (!timeoutsForIrp.ReadTotalTimeoutConstant &&
!timeoutsForIrp.ReadTotalTimeoutMultiplier)
{
returnWithWhatsPresent = TRUE;
}
else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
&&
(timeoutsForIrp.ReadTotalTimeoutMultiplier
!= MAXULONG))
{
useTotalTimer = TRUE;
os2ssreturn = TRUE;
multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
}
else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
&&
(timeoutsForIrp.ReadTotalTimeoutMultiplier
== MAXULONG))
{
useTotalTimer = TRUE;
os2ssreturn = TRUE;
crunchDownToOne = TRUE;
multiplierVal = 0;
constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
}
}
else
{
// If both the multiplier and the constant are
// zero then don't do any total timeout processing.
if (timeoutsForIrp.ReadTotalTimeoutMultiplier ||
timeoutsForIrp.ReadTotalTimeoutConstant)
{
// We have some timer values to calculate.
useTotalTimer = TRUE;
multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
}
}
if (useTotalTimer)
{
totalTime.QuadPart = ((LONGLONG)(UInt32x32To64(
m_NumberNeededForRead,
multiplierVal
)
+ constantVal))
* -10000;
}
// We do this copy in the hope of getting most (if not
// all) of the characters out of the interrupt buffer.
//
// Note that we need to protect this operation with a
// spinlock since we don't want a purge to hose us.
m_ControlLock.Lock();
updateChar.CharsCopied = GetCharsFromIntBuffer();
// See if we have any cause to return immediately.
if (returnWithWhatsPresent || (!m_NumberNeededForRead) ||
(os2ssreturn && m_CurrentReadIrp.Information()))
{
// We got all we needed for this read.
// Update the number of characters in the
// interrupt read buffer.
m_KdInterrupt.SynchronizeExecution((KDIRQ_SYNC_CALLBACK_PARM) UpdateInterruptBuffer, &updateChar);
m_ControlLock.Release();
m_CurrentReadIrp.Status() = STATUS_SUCCESS;
if (!setFirstStatus)
{
firstStatus = STATUS_SUCCESS;
setFirstStatus = TRUE;
}
}
else
{
// The irp might go under control of the isr. It
// won't hurt to initialize the reference count
// right now.
SERIAL_INIT_REFERENCE(m_CurrentReadIrp);
IoAcquireCancelSpinLock(&oldIrql);
// We need to see if this irp should be canceled.
if (m_CurrentReadIrp->Cancel) {
IoReleaseCancelSpinLock(oldIrql);
m_ControlLock.Release();
m_CurrentReadIrp.Status() =
STATUS_CANCELLED;
m_CurrentReadIrp.Information() = 0;
if (!setFirstStatus)
{
firstStatus = STATUS_CANCELLED;
setFirstStatus = TRUE;
}
}
else
{
// If we are supposed to crunch the read down to
// one character, then update the read length
// in the irp and truncate the number needed for
// read down to one. Note that if we are doing
// this crunching, then the information must be
// zero (or we would have completed above) and
// the number needed for the read must still be
// equal to the read length.
if (crunchDownToOne)
{
ASSERT((!m_CurrentReadIrp.Information()) &&
(m_NumberNeededForRead==m_CurrentReadIrp.ReadLength()));
m_NumberNeededForRead = 1;
m_CurrentReadIrp.ReadLength() = 1;
}
// We still need to get more characters for this read.
// synchronize with the isr so that we can update the
// number of characters and if necessary it will have the
// isr switch to copying into the users buffer.
m_KdInterrupt.SynchronizeExecution((KDIRQ_SYNC_CALLBACK_PARM) UpdateAndSwitchToUser, &updateChar);
if (!updateChar.Completed)
{
// The irp still isn't complete. The
// completion routines will end up reinvoking
// this routine. So we simply leave.
//
// First thought we should start off the total
// timer for the read and increment the reference
// count that the total timer has on the current
// irp. Note that this is safe, because even if
// the io has been satisfied by the isr it can't
// complete yet because we still own the cancel
// spinlock.
if (useTotalTimer)
{
SERIAL_SET_REFERENCE(
m_CurrentReadIrp,
SERIAL_REF_TOTAL_TIMER
);
m_KdTimer_TotalReadTimeoutDpc.Set(totalTime);
}
if (useIntervalTimer)
{
SERIAL_SET_REFERENCE(
m_CurrentReadIrp,
SERIAL_REF_INT_TIMER
);
KeQuerySystemTime(&m_LastReadTime);
m_KdTimer_ReadIntervalTimerDpc.Set(*m_IntervalTimeToUse);
}
m_CurrentReadIrp.MarkPending();
IoReleaseCancelSpinLock(oldIrql);
m_ControlLock.Release();
if (!setFirstStatus)
firstStatus = STATUS_PENDING;
return firstStatus;
}
else
{
IoReleaseCancelSpinLock(oldIrql);
m_ControlLock.Release();
m_CurrentReadIrp.Status() = STATUS_SUCCESS;
if (!setFirstStatus)
{
firstStatus = STATUS_SUCCESS;
setFirstStatus = TRUE;
}
}
}
}
}
// Well the operation is complete.
GetNextIrp(
&m_CurrentReadIrp,
&m_ReadQueue,
&newIrp,
TRUE
);
} while (newIrp);
return firstStatus;
}
VOID KdSerialDevice::CompleteRead(PKdDpc Dpc,
PVOID SystemContext1,
PVOID SystemContext2)
/*++
Routine Description:
This routine is merely used to complete any read that
ended up being used by the Isr. It assumes that the
status and the information fields of the irp are already
correctly filled in.
Arguments:
Dpc - Not Used.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
--*/
{
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
// We set this to indicate to the interval timer
// that the read has completed.
//
// Recall that the interval timer dpc can be lurking in some
// DPC queue.
m_CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE;
TryToCompleteCurrent(
NULL,
oldIrql,
STATUS_SUCCESS,
&m_CurrentReadIrp,
&m_ReadQueue,
&m_KdTimer_ReadIntervalTimerDpc,
&m_KdTimer_ReadRequestTotalTimerDpc,
StartRead,
GetNextIrp,
SERIAL_REF_ISR
);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -