📄 read.cpp
字号:
VOID KdSerialDevice::CancelCurrentRead(KdIrp Irp)
/*++
Routine Description:
This routine is used to cancel the current read.
Arguments:
Irp - Pointer to the IRP to be canceled.
--*/
{
// We set this to indicate to the interval timer
// that the read has encountered a cancel.
//
// Recall that the interval timer dpc can be lurking in some
// DPC queue.
m_CountOnLastRead = SERIAL_COMPLETE_READ_CANCEL;
TryToCompleteCurrent(
(KDIRQ_SYNC_CALLBACK) GrabReadFromIsr,
Irp->CancelIrql,
STATUS_CANCELLED,
&m_CurrentReadIrp,
&m_ReadQueue,
&m_KdTimer_ReadIntervalTimerDpc,
&m_KdTimer_ReadRequestTotalTimerDpc,
StartRead,
GetNextIrp,
SERIAL_REF_CANCEL
);
}
BOOLEAN KdSerialDevice::GrabReadFromIsr()
/*++
Routine Description:
This routine is used to grab (if possible) the irp from the
isr. If it finds that the isr still owns the irp it grabs
the ipr away (updating the number of characters copied into the
users buffer). If it grabs it away it also decrements the
reference count on the irp since it no longer belongs to the
isr (and the dpc that would complete it).
NOTE: This routine assumes that if the current buffer that the
ISR is copying characters into is the interrupt buffer then
the dpc has already been queued.
NOTE: This routine is being called from KeSynchronizeExecution.
NOTE: This routine assumes that it is called with the cancel spin
lock held.
Return Value:
Always false.
--*/
{
if (m_ReadBufferBase != m_InterruptReadBuffer)
{
// We need to set the information to the number of characters
// that the read wanted minus the number of characters that
// didn't get read into the interrupt buffer.
m_CurrentReadIrp.Information() = m_CurrentReadIrp.ReadLength() -
((m_LastCharSlot -m_CurrentCharSlot) + 1);
// Switch back to the interrupt buffer.
m_ReadBufferBase = m_InterruptReadBuffer;
m_CurrentCharSlot = m_InterruptReadBuffer;
m_FirstReadableChar = m_InterruptReadBuffer;
m_LastCharSlot = m_InterruptReadBuffer +
(m_BufferSize - 1);
m_CharsInInterruptBuffer = 0;
SERIAL_CLEAR_REFERENCE(
m_CurrentReadIrp,
SERIAL_REF_ISR
);
}
return FALSE;
}
VOID KdSerialDevice::ReadTimeout(PKdDpc Dpc,
PVOID SystemContext1,
PVOID SystemContext2)
/*++
Routine Description:
This routine is used to complete a read because its total
timer has expired.
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 due to total timeout.
//
// Recall that the interval timer dpc can be lurking in some
// DPC queue.
m_CountOnLastRead = SERIAL_COMPLETE_READ_TOTAL;
TryToCompleteCurrent(
(KDIRQ_SYNC_CALLBACK) GrabReadFromIsr,
oldIrql,
STATUS_TIMEOUT,
&m_CurrentReadIrp,
&m_ReadQueue,
&m_KdTimer_ReadIntervalTimerDpc,
&m_KdTimer_ReadRequestTotalTimerDpc,
StartRead,
GetNextIrp,
SERIAL_REF_TOTAL_TIMER
);
}
BOOLEAN KdSerialDevice::UpdateReadByIsr()
/*++
Routine Description:
This routine is used to update the count of characters read
by the isr since the last interval timer expiration.
NOTE: This routine is being called from KeSynchronizeExecution.
NOTE: This routine assumes that it is called with the cancel spin
lock held.
Return Value:
Always false.
--*/
{
m_CountOnLastRead = m_ReadByIsr;
m_ReadByIsr = 0;
return FALSE;
}
VOID KdSerialDevice::IntervalReadTimeout(PKdDpc Dpc,
PVOID SystemContext1,
PVOID SystemContext2)
/*++
Routine Description:
This routine is used timeout the request if the time between
characters exceed the interval time. A global is kept in
the device that records the count of characters read
the last the last time this routine was invoked (This dpc
will resubmit the timer if the count has changed). If the
count has not changed then this routine will attempt to complete
the irp. Note the special case of the last count being zero.
The timer isn't really in effect until the first character is
read.
Arguments:
Dpc - Not Used.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
--*/
{
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
if (m_CountOnLastRead == SERIAL_COMPLETE_READ_TOTAL)
{
// This value is only set by the total
// timer to indicate that it has fired.
// If so, then we should simply try to complete.
TryToCompleteCurrent(
(KDIRQ_SYNC_CALLBACK) GrabReadFromIsr,
oldIrql,
STATUS_TIMEOUT,
&m_CurrentReadIrp,
&m_ReadQueue,
&m_KdTimer_ReadIntervalTimerDpc,
&m_KdTimer_ReadRequestTotalTimerDpc,
StartRead,
GetNextIrp,
SERIAL_REF_INT_TIMER
);
}
else if (m_CountOnLastRead == SERIAL_COMPLETE_READ_COMPLETE)
{
// This value is only set by the regular
// completion routine.
//
// If so, then we should simply try to complete.
TryToCompleteCurrent(
(KDIRQ_SYNC_CALLBACK) GrabReadFromIsr,
oldIrql,
STATUS_SUCCESS,
&m_CurrentReadIrp,
&m_ReadQueue,
&m_KdTimer_ReadIntervalTimerDpc,
&m_KdTimer_ReadRequestTotalTimerDpc,
StartRead,
GetNextIrp,
SERIAL_REF_INT_TIMER
);
}
else if (m_CountOnLastRead == SERIAL_COMPLETE_READ_CANCEL)
{
// This value is only set by the cancel
// read routine.
//
// If so, then we should simply try to complete.
TryToCompleteCurrent(
(KDIRQ_SYNC_CALLBACK) GrabReadFromIsr,
oldIrql,
STATUS_CANCELLED,
&m_CurrentReadIrp,
&m_ReadQueue,
&m_KdTimer_ReadIntervalTimerDpc,
&m_KdTimer_ReadRequestTotalTimerDpc,
StartRead,
GetNextIrp,
SERIAL_REF_INT_TIMER
);
}
else if (m_CountOnLastRead || m_ReadByIsr)
{
// Something has happened since we last came here. We
// check to see if the ISR has read in any more characters.
// If it did then we should update the isr's read count
// and resubmit the timer.
if (m_ReadByIsr)
{
m_KdInterrupt.SynchronizeExecution((KDIRQ_SYNC_CALLBACK) UpdateReadByIsr);
// Save off the "last" time something was read.
// As we come back to this routine we will compare
// the current time to the "last" time. If the
// difference is ever larger then the interval
// requested by the user, then time out the request.
KeQuerySystemTime(
&m_LastReadTime
);
m_KdTimer_ReadIntervalTimerDpc.Set(*m_IntervalTimeToUse);
IoReleaseCancelSpinLock(oldIrql);
}
else
{
// Take the difference between the current time
// and the last time we had characters and
// see if it is greater then the interval time.
// if it is, then time out the request. Otherwise
// go away again for a while.
// No characters read in the interval time. Kill
// this read.
LARGE_INTEGER currentTime;
KeQuerySystemTime(
¤tTime
);
if ((currentTime.QuadPart - m_LastReadTime.QuadPart) >=
m_IntervalTime.QuadPart) {
TryToCompleteCurrent(
(KDIRQ_SYNC_CALLBACK) GrabReadFromIsr,
oldIrql,
STATUS_TIMEOUT,
&m_CurrentReadIrp,
&m_ReadQueue,
&m_KdTimer_ReadIntervalTimerDpc,
&m_KdTimer_ReadRequestTotalTimerDpc,
StartRead,
GetNextIrp,
SERIAL_REF_INT_TIMER
);
}
else
{
m_KdTimer_ReadIntervalTimerDpc.Set(*m_IntervalTimeToUse);
IoReleaseCancelSpinLock(oldIrql);
}
}
}
else
{
// Timer doesn't really start until the first character.
// So we should simply resubmit ourselves.
m_KdTimer_ReadIntervalTimerDpc.Set(*m_IntervalTimeToUse);
IoReleaseCancelSpinLock(oldIrql);
}
}
ULONG KdSerialDevice::GetCharsFromIntBuffer()
/*++
Routine Description:
This routine is used to copy any characters out of the interrupt
buffer into the users buffer. It will be reading values that
are updated with the ISR but this is safe since this value is
only decremented by synchronization routines. This routine will
return the number of characters copied so some other routine
can call a synchronization routine to update what is seen at
interrupt level.
Return Value:
The number of characters that were copied into the user
buffer.
--*/
{
// This value will be the number of characters that this
// routine returns. It will be the minimum of the number
// of characters currently in the buffer or the number of
// characters required for the read.
ULONG numberOfCharsToGet;
// This holds the number of characters between the first
// readable character and - the last character we will read or
// the real physical end of the buffer (not the last readable
// character).
ULONG firstTryNumberToGet;
// The minimum of the number of characters we need and
// the number of characters available
numberOfCharsToGet = m_CharsInInterruptBuffer;
if (numberOfCharsToGet > m_NumberNeededForRead)
numberOfCharsToGet = m_NumberNeededForRead;
if (numberOfCharsToGet)
{
// This will hold the number of characters between the
// first available character and the end of the buffer.
// Note that the buffer could wrap around but for the
// purposes of the first copy we don't care about that.
firstTryNumberToGet = (m_LastCharSlot - m_FirstReadableChar) + 1;
if (firstTryNumberToGet > numberOfCharsToGet)
{
// The characters don't wrap. Actually they may wrap but
// we don't care for the purposes of this read since the
// characters we need are available before the wrap.
RtlMoveMemory(
((PUCHAR)(m_CurrentReadIrp.SystemBuffer()))
+ (m_CurrentReadIrp.ReadLength()
- m_NumberNeededForRead
),
m_FirstReadableChar,
numberOfCharsToGet
);
m_NumberNeededForRead -= numberOfCharsToGet;
// We now will move the pointer to the first character after
// what we just copied into the users buffer.
//
// We need to check if the stream of readable characters
// is wrapping around to the beginning of the buffer.
//
// Note that we may have just taken the last characters
// at the end of the buffer.
if ((m_FirstReadableChar + (numberOfCharsToGet - 1)) == m_LastCharSlot)
m_FirstReadableChar = m_InterruptReadBuffer;
else
m_FirstReadableChar += numberOfCharsToGet;
}
else
{
// The characters do wrap. Get up until the end of the buffer.
RtlMoveMemory(
((PUCHAR)(m_CurrentReadIrp.SystemBuffer()))
+ (m_CurrentReadIrp.ReadLength()
- m_NumberNeededForRead
),
m_FirstReadableChar,
firstTryNumberToGet
);
m_NumberNeededForRead -= firstTryNumberToGet;
// Now get the rest of the characters from the beginning of the
// buffer.
RtlMoveMemory(
((PUCHAR)(m_CurrentReadIrp.SystemBuffer()))
+ (m_CurrentReadIrp.ReadLength()
- m_NumberNeededForRead
),
m_InterruptReadBuffer,
numberOfCharsToGet - firstTryNumberToGet
);
m_FirstReadableChar = m_InterruptReadBuffer +
(numberOfCharsToGet -
firstTryNumberToGet);
m_NumberNeededForRead -= (numberOfCharsToGet -
firstTryNumberToGet);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -