📄 read.c
字号:
Context - Points to a structure that contains a pointer to the
device extension, a count of the number of characters
that we previously copied into the users buffer, and
a boolean that we will set that defines whether we
switched the ISR to copy into the users buffer.
Return Value:
Always FALSE.
--*/
{
PSERIAL_UPDATE_CHAR updateChar = Context;
PSERIAL_DEVICE_EXTENSION extension = updateChar->Extension;
SERIAL_LOCKED_PAGED_CODE();
SerialUpdateInterruptBuffer(Context);
//
// There are more characters to get to satisfy this read.
// Copy any characters that have arrived since we got
// the last batch.
//
updateChar->CharsCopied = SerialGetCharsFromIntBuffer(extension);
SerialUpdateInterruptBuffer(Context);
//
// No more new characters will be "received" until we exit
// this routine. We again check to make sure that we
// haven't satisfied this read, and if we haven't we set things
// up so that the ISR copies into the user buffer.
//
if (extension->NumberNeededForRead) {
//
// We shouldn't be switching unless there are no
// characters left.
//
ASSERT(!extension->CharsInInterruptBuffer);
//
// We use the following to values to do inteval timing.
//
// CountOnLastRead is mostly used to simply prevent
// the interval timer from timing out before any characters
// are read. (Interval timing should only be effective
// after the first character is read.)
//
// After the first time the interval timer fires and
// characters have be read we will simply update with
// the value of ReadByIsr and then set ReadByIsr to zero.
// (We do that in a synchronization routine.
//
// If the interval timer dpc routine ever encounters
// ReadByIsr == 0 when CountOnLastRead is non-zero it
// will timeout the read.
//
// (Note that we have a special case of CountOnLastRead
// < 0. This is done by the read completion routines other
// than the total timeout dpc to indicate that the total
// timeout has expired.)
//
extension->CountOnLastRead =
(LONG)extension->CurrentReadIrp->IoStatus.Information;
extension->ReadByIsr = 0;
//
// By compareing the read buffer base address to the
// the base address of the interrupt buffer the ISR
// can determine whether we are using the interrupt
// buffer or the user buffer.
//
extension->ReadBufferBase =
extension->CurrentReadIrp->AssociatedIrp.SystemBuffer;
//
// The current char slot is after the last copied in
// character. We know there is always room since we
// we wouldn't have gotten here if there wasn't.
//
extension->CurrentCharSlot = extension->ReadBufferBase +
extension->CurrentReadIrp->IoStatus.Information;
//
// The last position that a character can go is on the
// last byte of user buffer. While the actual allocated
// buffer space may be bigger, we know that there is at
// least as much as the read length.
//
extension->LastCharSlot = extension->ReadBufferBase +
(IoGetCurrentIrpStackLocation(
extension->CurrentReadIrp
)->Parameters.Read.Length
- 1);
//
// Mark the irp as being in a cancelable state.
//
IoSetCancelRoutine(
extension->CurrentReadIrp,
SerialCancelCurrentRead
);
//
// Increment the reference count twice.
//
// Once for the Isr owning the irp and once
// because the cancel routine has a reference
// to it.
//
SERIAL_SET_REFERENCE(
extension->CurrentReadIrp,
SERIAL_REF_ISR
);
SERIAL_SET_REFERENCE(
extension->CurrentReadIrp,
SERIAL_REF_CANCEL
);
updateChar->Completed = FALSE;
} else {
updateChar->Completed = TRUE;
}
return FALSE;
}
//
// We use this structure only to communicate to the synchronization
// routine when we are switching to the resized buffer.
//
typedef struct _SERIAL_RESIZE_PARAMS {
PSERIAL_DEVICE_EXTENSION Extension;
PUCHAR OldBuffer;
PUCHAR NewBuffer;
ULONG NewBufferSize;
ULONG NumberMoved;
} SERIAL_RESIZE_PARAMS,*PSERIAL_RESIZE_PARAMS;
NTSTATUS
SerialResizeBuffer(
IN PSERIAL_DEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine will process the resize buffer request.
If size requested for the RX buffer is smaller than
the current buffer then we will simply return
STATUS_SUCCESS. (We don't want to make buffers smaller.
If we did that then we all of a sudden have "overrun"
problems to deal with as well as flow control to deal
with - very painful.) We ignore the TX buffer size
request since we don't use a TX buffer.
Arguments:
Extension - Pointer to the device extension for the port.
Return Value:
STATUS_SUCCESS if everything worked out ok.
STATUS_INSUFFICIENT_RESOURCES if we couldn't allocate the
memory for the buffer.
--*/
{
PSERIAL_QUEUE_SIZE rs = Extension->CurrentReadIrp->AssociatedIrp
.SystemBuffer;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(
Extension->CurrentReadIrp
);
PVOID newBuffer = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
SERIAL_LOCKED_PAGED_CODE();
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
Extension->CurrentReadIrp->IoStatus.Information = 0L;
Extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
if (rs->InSize <= Extension->BufferSize) {
//
// Nothing to do. We don't make buffers smaller. Just
// agree with the user. We must deallocate the memory
// that was already allocated in the ioctl dispatch routine.
//
ExFreePool(newBuffer);
} else {
SERIAL_RESIZE_PARAMS rp;
KIRQL controlIrql;
//
// Hmmm, looks like we actually have to go
// through with this. We need to move all the
// data that is in the current buffer into this
// new buffer. We'll do this in two steps.
//
// First we go up to dispatch level and try to
// move as much as we can without stopping the
// ISR from running. We go up to dispatch level
// by acquiring the control lock. We do it at
// dispatch using the control lock so that:
//
// 1) We can't be context switched in the middle
// of the move. Our pointers into the buffer
// could be *VERY* stale by the time we got back.
//
// 2) We use the control lock since we don't want
// some pesky purge irp to come along while
// we are trying to move.
//
// After the move, but while we still hold the control
// lock, we synch with the ISR and get those last
// (hopefully) few characters that have come in since
// we started the copy. We switch all of our pointers,
// counters, and such to point to this new buffer. NOTE:
// we need to be careful. If the buffer we were using
// was not the default one created when we initialized
// the device (i.e. it was created via a previous IRP of
// this type), we should deallocate it.
//
rp.Extension = Extension;
rp.OldBuffer = Extension->InterruptReadBuffer;
rp.NewBuffer = newBuffer;
rp.NewBufferSize = rs->InSize;
KeAcquireSpinLock(
&Extension->ControlLock,
&controlIrql
);
rp.NumberMoved = SerialMoveToNewIntBuffer(
Extension,
newBuffer
);
KeSynchronizeExecution(
Extension->Interrupt,
SerialUpdateAndSwitchToNew,
&rp
);
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
//
// Free up the memory that the old buffer consumed.
//
ExFreePool(rp.OldBuffer);
}
return STATUS_SUCCESS;
}
ULONG
SerialMoveToNewIntBuffer(
PSERIAL_DEVICE_EXTENSION Extension,
PUCHAR NewBuffer
)
/*++
Routine Description:
This routine is used to copy any characters out of the interrupt
buffer into the "new" 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.
Arguments:
Extension - A pointer to the device extension.
NewBuffer - Where the characters are to be move to.
Return Value:
The number of characters that were copied into the user
buffer.
--*/
{
ULONG numberOfCharsMoved = Extension->CharsInInterruptBuffer;
SERIAL_LOCKED_PAGED_CODE();
if (numberOfCharsMoved) {
//
// 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 = (ULONG)(Extension->LastCharSlot -
Extension->FirstReadableChar) + 1;
if (firstTryNumberToGet >= numberOfCharsMoved) {
//
// The characters don't wrap.
//
RtlMoveMemory(
NewBuffer,
Extension->FirstReadableChar,
numberOfCharsMoved
);
if ((Extension->FirstReadableChar+(numberOfCharsMoved-1)) ==
Extension->LastCharSlot) {
Extension->FirstReadableChar = Extension->InterruptReadBuffer;
} else {
Extension->FirstReadableChar += numberOfCharsMoved;
}
} else {
//
// The characters do wrap. Get up until the end of the buffer.
//
RtlMoveMemory(
NewBuffer,
Extension->FirstReadableChar,
firstTryNumberToGet
);
//
// Now get the rest of the characters from the beginning of the
// buffer.
//
RtlMoveMemory(
NewBuffer+firstTryNumberToGet,
Extension->InterruptReadBuffer,
numberOfCharsMoved - firstTryNumberToGet
);
Extension->FirstReadableChar = Extension->InterruptReadBuffer +
numberOfCharsMoved - firstTryNumberToGet;
}
}
return numberOfCharsMoved;
}
BOOLEAN
SerialUpdateAndSwitchToNew(
IN PVOID Context
)
/*++
Routine Description:
This routine gets the (hopefully) few characters that
remain in the interrupt buffer after the first time we tried
to get them out.
NOTE: This is called by KeSynchronizeExecution.
Arguments:
Context - Points to a structure that contains a pointer to the
device extension, a pointer to the buffer we are moving
to, and a count of the number of characters
that we previously copied into the new buffer, and the
actual size of the new buffer.
Return Value:
Always FALSE.
--*/
{
PSERIAL_RESIZE_PARAMS params = Context;
PSERIAL_DEVICE_EXTENSION extension = params->Extension;
ULONG tempCharsInInterruptBuffer = extension->CharsInInterruptBuffer;
SERIAL_LOCKED_PAGED_CODE();
ASSERT(extension->CharsInInterruptBuffer >= params->NumberMoved);
//
// We temporarily reduce the chars in interrupt buffer to
// "fool" the move routine. We will restore it after the
// move.
//
extension->CharsInInterruptBuffer -= params->NumberMoved;
if (extension->CharsInInterruptBuffer) {
SerialMoveToNewIntBuffer(
extension,
params->NewBuffer + params->NumberMoved
);
}
extension->CharsInInterruptBuffer = tempCharsInInterruptBuffer;
extension->LastCharSlot = params->NewBuffer + (params->NewBufferSize - 1);
extension->FirstReadableChar = params->NewBuffer;
extension->ReadBufferBase = params->NewBuffer;
extension->InterruptReadBuffer = params->NewBuffer;
extension->BufferSize = params->NewBufferSize;
//
// We *KNOW* that the new interrupt buffer is larger than the
// old buffer. We don't need to worry about it being full.
//
extension->CurrentCharSlot = extension->InterruptReadBuffer +
extension->CharsInInterruptBuffer;
//
// We set up the default xon/xoff limits.
//
extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
extension->HandFlow.XonLimit = extension->BufferSize >> 1;
extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;
extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
(extension->BufferSize>>4));
//
// Since we (essentially) reduced the percentage of the interrupt
// buffer being full, we need to handle any flow control.
//
SerialHandleReducedIntBuffer(extension);
return FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -