📄 read.c
字号:
/*++
Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
Module Name:
read.c
Abstract:
This module contains the code that is very specific to read
operations in the serial driver
Author:
Anthony V. Ercolano 26-Sep-1991
Environment:
Kernel mode
--*/
#include "precomp.h"
VOID
SerialCancelCurrentRead(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
);
BOOLEAN
SerialGrabReadFromIsr(
IN PVOID Context
);
BOOLEAN
SerialUpdateReadByIsr(
IN PVOID Context
);
ULONG
SerialGetCharsFromIntBuffer(
PSERIAL_DEVICE_EXTENSION Extension
);
BOOLEAN
SerialUpdateInterruptBuffer(
IN PVOID Context
);
BOOLEAN
SerialUpdateAndSwitchToUser(
IN PVOID Context
);
NTSTATUS
SerialResizeBuffer(
IN PSERIAL_DEVICE_EXTENSION Extension
);
ULONG
SerialMoveToNewIntBuffer(
PSERIAL_DEVICE_EXTENSION Extension,
PUCHAR NewBuffer
);
BOOLEAN
SerialUpdateAndSwitchToNew(
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGESER,SerialRead)
#pragma alloc_text(PAGESER,SerialStartRead)
#pragma alloc_text(PAGESER,SerialCancelCurrentRead)
#pragma alloc_text(PAGESER,SerialGrabReadFromIsr)
#pragma alloc_text(PAGESER,SerialUpdateReadByIsr)
#pragma alloc_text(PAGESER,SerialGetCharsFromIntBuffer)
#pragma alloc_text(PAGESER,SerialUpdateInterruptBuffer)
#pragma alloc_text(PAGESER,SerialUpdateAndSwitchToUser)
#pragma alloc_text(PAGESER,SerialResizeBuffer)
#pragma alloc_text(PAGESER,SerialMoveToNewIntBuffer)
#pragma alloc_text(PAGESER,SerialUpdateAndSwitchToNew)
#endif
NTSTATUS
SerialRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP 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:
DeviceObject - Pointer to the device object for this device
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.
--*/
{
PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
BOOLEAN acceptingIRPs;
NTSTATUS status;
SERIAL_LOCKED_PAGED_CODE();
SerialDbgPrintEx(DPFLTR_TRACE_LEVEL, ">SerialRead(%X, %X)\n", DeviceObject,
Irp);
if ((status = SerialIRPPrologue(Irp, extension)) != STATUS_SUCCESS) {
if(status != STATUS_PENDING) {
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
}
SerialDbgPrintEx(DPFLTR_TRACE_LEVEL, "<SerialRead (1) %X\n", status);
return status;
}
SerialDbgPrintEx(SERIRPPATH, "Dispatch entry for: %x\n", Irp);
if (SerialCompleteIfError(DeviceObject, Irp) != STATUS_SUCCESS) {
SerialDbgPrintEx(DPFLTR_TRACE_LEVEL, "<SerialRead (2) %X\n",
STATUS_CANCELLED);
return STATUS_CANCELLED;
}
Irp->IoStatus.Information = 0L;
//
// Quick check for a zero length read. If it is zero length
// then we are already done!
//
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length) {
//
// 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.
//
status = SerialStartOrQueue(extension, Irp, &extension->ReadQueue,
&extension->CurrentReadIrp, SerialStartRead);
SerialDbgPrintEx(DPFLTR_TRACE_LEVEL, "<SerialRead (3) %X\n", status);
return status;
} else {
Irp->IoStatus.Status = STATUS_SUCCESS;
SerialCompleteRequest(extension, Irp, 0);
SerialDbgPrintEx(DPFLTR_TRACE_LEVEL, "<SerialRead (4) %X\n",
STATUS_SUCCESS);
return STATUS_SUCCESS;
}
}
NTSTATUS
SerialStartRead(
IN PSERIAL_DEVICE_EXTENSION Extension
)
/*++
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.
Arguments:
Extension - Simply a pointer to the serial device extension.
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;
PIRP newIrp;
KIRQL oldIrql;
KIRQL controlIrql;
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;
SERIAL_LOCKED_PAGED_CODE();
SerialDbgPrintEx(DPFLTR_TRACE_LEVEL, ">SerialStartRead(%X)\n", Extension);
updateChar.Extension = Extension;
do {
//
// Check to see if this is a resize request. If it is
// then go to a routine that specializes in that.
//
if (IoGetCurrentIrpStackLocation(Extension->CurrentReadIrp)
->MajorFunction != IRP_MJ_READ) {
NTSTATUS localStatus = SerialResizeBuffer(Extension);
if (!setFirstStatus) {
firstStatus = localStatus;
setFirstStatus = TRUE;
}
} else {
Extension->NumberNeededForRead =
IoGetCurrentIrpStackLocation(Extension->CurrentReadIrp)
->Parameters.Read.Length;
//
// 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;
//
//
// CIMEXCIMEX -- this is a lie
//
// Always initialize the timer objects so that the
// completion code can tell when it attempts to
// cancel the timers whether the timers had ever
// been Set.
//
// CIMEXCIMEX -- this is the truth
//
// What we want to do is just make sure the timers are
// cancelled to the best of our ability and move on with
// life.
//
SerialCancelTimer(&Extension->ReadRequestTotalTimer, Extension);
SerialCancelTimer(&Extension->ReadRequestIntervalTimer, Extension);
// KeInitializeTimer(&Extension->ReadRequestTotalTimer);
// KeInitializeTimer(&Extension->ReadRequestIntervalTimer);
//
// We get the *current* timeout values to use for timing
// this read.
//
KeAcquireSpinLock(
&Extension->ControlLock,
&controlIrql
);
timeoutsForIrp = Extension->Timeouts;
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
//
// Calculate the interval timeout for the read.
//
if (timeoutsForIrp.ReadIntervalTimeout &&
(timeoutsForIrp.ReadIntervalTimeout !=
MAXULONG)) {
useIntervalTimer = TRUE;
Extension->IntervalTime.QuadPart =
UInt32x32To64(
timeoutsForIrp.ReadIntervalTimeout,
10000
);
if (Extension->IntervalTime.QuadPart >=
Extension->CutOverAmount.QuadPart) {
Extension->IntervalTimeToUse =
&Extension->LongIntervalAmount;
} else {
Extension->IntervalTimeToUse =
&Extension->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(
Extension->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.
//
KeAcquireSpinLock(
&Extension->ControlLock,
&controlIrql
);
updateChar.CharsCopied = SerialGetCharsFromIntBuffer(Extension);
//
// See if we have any cause to return immediately.
//
if (returnWithWhatsPresent || (!Extension->NumberNeededForRead) ||
(os2ssreturn &&
Extension->CurrentReadIrp->IoStatus.Information)) {
//
// We got all we needed for this read.
// Update the number of characters in the
// interrupt read buffer.
//
KeSynchronizeExecution(
Extension->Interrupt,
SerialUpdateInterruptBuffer,
&updateChar
);
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
Extension->CurrentReadIrp->IoStatus.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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -