mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
837 lines
25 KiB
837 lines
25 KiB
/***************************************************************************
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
READ.C
|
|
|
|
Abstract:
|
|
|
|
Routines that perform read functionality
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
PURPOSE.
|
|
|
|
Copyright (c) 1998 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
9/25/98 : created
|
|
|
|
Authors:
|
|
|
|
Louis J. Giliberto, Jr.
|
|
|
|
|
|
****************************************************************************/
|
|
|
|
#include <wdm.h>
|
|
#include <ntddser.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <usb.h>
|
|
#include <usbdrivr.h>
|
|
#include <usbdlib.h>
|
|
#include <usbcomm.h>
|
|
|
|
#ifdef WMI_SUPPORT
|
|
#include <wmilib.h>
|
|
#include <wmidata.h>
|
|
#include <wmistr.h>
|
|
#endif
|
|
|
|
#include "usbser.h"
|
|
#include "utils.h"
|
|
#include "debugwdm.h"
|
|
|
|
//
|
|
// PAGEUSBS is keyed off of UsbSer_Read, so UsbSer_Read must
|
|
// remain in PAGEUSBS for things to work properly
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGEUSBS, UsbSerCancelCurrentRead)
|
|
#pragma alloc_text(PAGEUSBS, UsbSer_Read)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerStartRead)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerGrabReadFromRx)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerReadTimeout)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerIntervalReadTimeout)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
/************************************************************************/
|
|
/* UsbSer_Read */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Process the IRPs sent to this device for Read calls */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Irp - pointer to an I/O Request Packet */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
UsbSer_Read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
USBSER_LOCKED_PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter UsbSer_Read");
|
|
UsbSerSerialDump(USBSERTRACERD, (">UsbSer_Read(%08X)\n", Irp));
|
|
|
|
// set return values to something known
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
DEBUG_TRACE2(("Read (%08X)\n", IrpStack->Parameters.Read.Length));
|
|
|
|
UsbSerSerialDump(USBSERTRACE, ("UsbSer_Read Irp: %08X (%08X)\n", Irp,
|
|
IrpStack->Parameters.Read.Length));
|
|
|
|
// make entry in IRP history table
|
|
DEBUG_LOG_IRP_HIST(DeviceObject, Irp, IrpStack->MajorFunction,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
IrpStack->Parameters.Read.Length);
|
|
|
|
if (IrpStack->Parameters.Read.Length != 0) {
|
|
NtStatus = UsbSerStartOrQueue(DeviceExtension, Irp,
|
|
&DeviceExtension->ReadQueue,
|
|
&DeviceExtension->CurrentReadIrp,
|
|
UsbSerStartRead);
|
|
} else {
|
|
Irp->IoStatus.Status = NtStatus = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
|
|
CompleteIO(DeviceObject, Irp, IrpStack->MajorFunction,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
Irp->IoStatus.Information);
|
|
}
|
|
|
|
// log an error if we got one
|
|
DEBUG_LOG_ERROR(NtStatus);
|
|
DEBUG_LOG_PATH("exit UsbSer_Read");
|
|
DEBUG_TRACE3(("status (%08X)\n", NtStatus));
|
|
UsbSerSerialDump(USBSERTRACERD, ("<UsbSer_Read %08X\n", NtStatus));
|
|
|
|
return NtStatus;
|
|
} // UsbSer_Read
|
|
|
|
|
|
|
|
NTSTATUS
|
|
UsbSerStartRead(IN PDEVICE_EXTENSION PDevExt)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the active read request by initializing any timers,
|
|
doing the initial submission to the read state machine, etc.
|
|
|
|
Arguments:
|
|
|
|
PDevExt - Pointer to the device extension for the device to start a read on
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS firstStatus = STATUS_SUCCESS;
|
|
BOOLEAN setFirstStatus = FALSE;
|
|
ULONG charsRead;
|
|
KIRQL oldIrql;
|
|
KIRQL controlIrql;
|
|
PIRP newIrp;
|
|
PIRP pReadIrp;
|
|
ULONG readLen;
|
|
ULONG multiplierVal;
|
|
ULONG constantVal;
|
|
BOOLEAN useTotalTimer;
|
|
BOOLEAN returnWithWhatsPresent;
|
|
BOOLEAN os2ssreturn;
|
|
BOOLEAN crunchDownToOne;
|
|
BOOLEAN useIntervalTimer;
|
|
SERIAL_TIMEOUTS timeoutsForIrp;
|
|
LARGE_INTEGER totalTime;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
DEBUG_LOG_PATH("Enter UsbSerStartRead");
|
|
UsbSerSerialDump(USBSERTRACERD, (">UsbSerStartRead\n"));
|
|
|
|
|
|
do {
|
|
pReadIrp = PDevExt->CurrentReadIrp;
|
|
readLen = IoGetCurrentIrpStackLocation(pReadIrp)->Parameters.Read.Length;
|
|
|
|
|
|
PDevExt->NumberNeededForRead = readLen;
|
|
|
|
DEBUG_TRACE3(("Start Reading %08X\n", PDevExt->NumberNeededForRead));
|
|
|
|
useTotalTimer = FALSE;
|
|
returnWithWhatsPresent = FALSE;
|
|
os2ssreturn = FALSE;
|
|
crunchDownToOne = FALSE;
|
|
useIntervalTimer = FALSE;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
ACQUIRE_SPINLOCK(PDevExt, &PDevExt->ControlLock, &controlIrql);
|
|
timeoutsForIrp = PDevExt->Timeouts;
|
|
PDevExt->CountOnLastRead = 0;
|
|
RELEASE_SPINLOCK(PDevExt, &PDevExt->ControlLock, controlIrql);
|
|
|
|
//
|
|
// Calculate the interval timeout for the read
|
|
//
|
|
|
|
if (timeoutsForIrp.ReadIntervalTimeout
|
|
&& (timeoutsForIrp.ReadIntervalTimeout != MAXULONG)) {
|
|
useIntervalTimer = TRUE;
|
|
|
|
PDevExt->IntervalTime.QuadPart
|
|
= UInt32x32To64(timeoutsForIrp.ReadIntervalTimeout, 10000);
|
|
|
|
if (PDevExt->IntervalTime.QuadPart
|
|
>= PDevExt->CutOverAmount.QuadPart) {
|
|
PDevExt->IntervalTimeToUse = &PDevExt->LongIntervalAmount;
|
|
} else {
|
|
PDevExt->IntervalTimeToUse = &PDevExt->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(PDevExt->NumberNeededForRead,
|
|
multiplierVal) + constantVal)) * -10000;
|
|
}
|
|
|
|
|
|
if (PDevExt->CharsInReadBuff) {
|
|
charsRead
|
|
= GetData(PDevExt, ((PUCHAR)(pReadIrp->AssociatedIrp.SystemBuffer))
|
|
+ readLen - PDevExt->NumberNeededForRead,
|
|
PDevExt->NumberNeededForRead,
|
|
&pReadIrp->IoStatus.Information);
|
|
} else {
|
|
charsRead = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// See if this read is complete
|
|
//
|
|
|
|
if (returnWithWhatsPresent || (PDevExt->NumberNeededForRead == 0)
|
|
|| (os2ssreturn && pReadIrp->IoStatus.Information)) {
|
|
|
|
#if DBG
|
|
if (UsbSerSerialDebugLevel & USBSERDUMPRD) {
|
|
ULONG i;
|
|
ULONG count;
|
|
|
|
if (PDevExt->CurrentReadIrp->IoStatus.Status == STATUS_SUCCESS) {
|
|
count = (ULONG)PDevExt->CurrentReadIrp->IoStatus.Information;
|
|
} else {
|
|
count = 0;
|
|
|
|
}
|
|
DbgPrint("RD3: A(%08X) G(%08X) I(%08X)\n",
|
|
IoGetCurrentIrpStackLocation(PDevExt->CurrentReadIrp)
|
|
->Parameters.Read.Length, count, PDevExt->CurrentReadIrp);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
DbgPrint("%02x ", *(((PUCHAR)PDevExt->CurrentReadIrp
|
|
->AssociatedIrp.SystemBuffer) + i) & 0xFF);
|
|
}
|
|
|
|
if (i == 0) {
|
|
DbgPrint("NULL (%08X)\n", PDevExt->CurrentReadIrp
|
|
->IoStatus.Status);
|
|
}
|
|
|
|
DbgPrint("\n\n");
|
|
}
|
|
#endif
|
|
//
|
|
// Update the amount of chars left in the ring buffer
|
|
//
|
|
|
|
pReadIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
if (!setFirstStatus) {
|
|
firstStatus = STATUS_SUCCESS;
|
|
setFirstStatus = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// The irp may be given to the buffering routine
|
|
//
|
|
|
|
USBSER_INIT_REFERENCE(pReadIrp);
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(PDevExt, &oldIrql);
|
|
|
|
//
|
|
// Check to see if it needs to be cancelled
|
|
//
|
|
|
|
if (pReadIrp->Cancel) {
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
|
|
pReadIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pReadIrp->IoStatus.Information = 0;
|
|
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = STATUS_CANCELLED;
|
|
}
|
|
|
|
UsbSerGetNextIrp(&PDevExt->CurrentReadIrp, &PDevExt->ReadQueue,
|
|
&newIrp, TRUE, PDevExt);
|
|
continue;
|
|
|
|
} 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) {
|
|
PDevExt->NumberNeededForRead = 1;
|
|
IoGetCurrentIrpStackLocation(pReadIrp)->Parameters.Read.Length
|
|
= 1;
|
|
}
|
|
|
|
USBSER_SET_REFERENCE(pReadIrp, USBSER_REF_RXBUFFER);
|
|
USBSER_SET_REFERENCE(pReadIrp, USBSER_REF_CANCEL);
|
|
|
|
if (useTotalTimer) {
|
|
USBSER_SET_REFERENCE(pReadIrp, USBSER_REF_TOTAL_TIMER);
|
|
KeSetTimer(&PDevExt->ReadRequestTotalTimer, totalTime,
|
|
&PDevExt->TotalReadTimeoutDpc);
|
|
}
|
|
|
|
if (useIntervalTimer) {
|
|
USBSER_SET_REFERENCE(pReadIrp, USBSER_REF_INT_TIMER);
|
|
KeQuerySystemTime(&PDevExt->LastReadTime);
|
|
|
|
KeSetTimer(&PDevExt->ReadRequestIntervalTimer,
|
|
*PDevExt->IntervalTimeToUse,
|
|
&PDevExt->IntervalReadTimeoutDpc);
|
|
}
|
|
|
|
//
|
|
// Mark IRP as cancellable
|
|
//
|
|
|
|
IoSetCancelRoutine(pReadIrp, UsbSerCancelCurrentRead);
|
|
|
|
IoMarkIrpPending(pReadIrp);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
|
|
if (!setFirstStatus) {
|
|
firstStatus = STATUS_PENDING;
|
|
}
|
|
}
|
|
DEBUG_LOG_PATH("Exit UsbSerStartRead (1)\n");
|
|
UsbSerSerialDump(USBSERTRACERD, ("<UsbSerStartRead (1) %08X\n",
|
|
firstStatus));
|
|
return firstStatus;
|
|
}
|
|
|
|
UsbSerGetNextIrp(&PDevExt->CurrentReadIrp, &PDevExt->ReadQueue,
|
|
&newIrp, TRUE, PDevExt);
|
|
} while (newIrp != NULL);
|
|
|
|
|
|
DEBUG_LOG_PATH("Exit UsbSerStartRead (2)\n");
|
|
UsbSerSerialDump(USBSERTRACERD, ("<UsbSerStartRead (2) %08X\n",
|
|
firstStatus));
|
|
return firstStatus;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
UsbSerGrabReadFromRx(IN PVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to grab (if possible) the irp from the
|
|
read callback mechanism. If it finds that the rx still owns the irp it
|
|
grabs the irp away and also decrements the reference count on the irp since
|
|
it no longer belongs to the rx routine.
|
|
|
|
NOTE: This routine assumes that it is called with the cancel spin
|
|
lock and/or control lock held.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
Always false.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)Context;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, ("Enter UsbSerGrabReadFromRx\n"));
|
|
|
|
|
|
USBSER_CLEAR_REFERENCE(pDevExt->CurrentReadIrp, USBSER_REF_RXBUFFER);
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, ("Exit UsbSerGrabReadFromRx\n"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerCancelCurrentRead(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to cancel the current read.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - Pointer to the device object for this device
|
|
|
|
PIrp - Pointer to the IRP to be canceled.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerCancelCurrentRead(%08X)\n",
|
|
PIrp));
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
pDevExt->CountOnLastRead = SERIAL_COMPLETE_READ_CANCEL;
|
|
|
|
|
|
//
|
|
// HACKHACK
|
|
//
|
|
|
|
UsbSerGrabReadFromRx(pDevExt);
|
|
|
|
UsbSerTryToCompleteCurrent(pDevExt, PIrp->CancelIrql, STATUS_CANCELLED,
|
|
&pDevExt->CurrentReadIrp, &pDevExt->ReadQueue,
|
|
&pDevExt->ReadRequestIntervalTimer,
|
|
&pDevExt->ReadRequestTotalTimer,
|
|
UsbSerStartRead, UsbSerGetNextIrp,
|
|
USBSER_REF_CANCEL, TRUE);
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerCancelCurrentRead\n"));
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerReadTimeout(IN PKDPC PDpc, IN PVOID DeferredContext,
|
|
IN PVOID SystemContext1, IN PVOID SystemContext2)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to complete a read because its total
|
|
timer has expired.
|
|
|
|
Arguments:
|
|
|
|
PDpc - Not Used.
|
|
|
|
DeferredContext - Really points to the device extension.
|
|
|
|
SystemContext1 - Not Used.
|
|
|
|
SystemContext2 - Not Used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION pDevExt = DeferredContext;
|
|
KIRQL oldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(SystemContext1);
|
|
UNREFERENCED_PARAMETER(SystemContext2);
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACETM, (">UsbSerReadTimeout\n"));
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(pDevExt, &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.
|
|
//
|
|
|
|
pDevExt->CountOnLastRead = SERIAL_COMPLETE_READ_TOTAL;
|
|
|
|
//
|
|
// HACKHACK
|
|
//
|
|
|
|
UsbSerGrabReadFromRx(pDevExt);
|
|
|
|
UsbSerTryToCompleteCurrent(pDevExt, oldIrql, STATUS_TIMEOUT,
|
|
&pDevExt->CurrentReadIrp, &pDevExt->ReadQueue,
|
|
&pDevExt->ReadRequestIntervalTimer,
|
|
&pDevExt->ReadRequestTotalTimer, UsbSerStartRead,
|
|
UsbSerGetNextIrp, USBSER_REF_TOTAL_TIMER, TRUE);
|
|
|
|
UsbSerSerialDump(USBSERTRACETM, ("<UsbSerReadTimeout\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerIntervalReadTimeout(IN PKDPC PDpc, IN PVOID DeferredContext,
|
|
IN PVOID SystemContext1, IN 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 extension 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:
|
|
|
|
PDpc - Not Used.
|
|
|
|
DeferredContext - Really points to the device extension.
|
|
|
|
SystemContext1 - Not Used.
|
|
|
|
SystemContext2 - Not Used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION pDevExt = DeferredContext;
|
|
KIRQL oldIrql;
|
|
KIRQL oldControlIrql;
|
|
|
|
UNREFERENCED_PARAMETER(SystemContext1);
|
|
UNREFERENCED_PARAMETER(SystemContext2);
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACETM, (">UsbSerIntervalReadTimeout "));
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(pDevExt, &oldIrql);
|
|
|
|
|
|
if (pDevExt->CountOnLastRead == SERIAL_COMPLETE_READ_TOTAL) {
|
|
UsbSerSerialDump(USBSERTRACETM, ("SERIAL_COMPLETE_READ_TOTAL\n"));
|
|
|
|
//
|
|
// This value is only set by the total
|
|
// timer to indicate that it has fired.
|
|
// If so, then we should simply try to complete.
|
|
//
|
|
|
|
//
|
|
// HACKHACK
|
|
//
|
|
ACQUIRE_SPINLOCK(pDevExt, &pDevExt->ControlLock, &oldControlIrql);
|
|
UsbSerGrabReadFromRx(pDevExt);
|
|
pDevExt->CountOnLastRead = 0;
|
|
RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldControlIrql);
|
|
|
|
UsbSerTryToCompleteCurrent(pDevExt, oldIrql, STATUS_TIMEOUT,
|
|
&pDevExt->CurrentReadIrp, &pDevExt->ReadQueue,
|
|
&pDevExt->ReadRequestIntervalTimer,
|
|
&pDevExt->ReadRequestTotalTimer,
|
|
UsbSerStartRead, UsbSerGetNextIrp,
|
|
USBSER_REF_INT_TIMER, TRUE);
|
|
|
|
} else if (pDevExt->CountOnLastRead == SERIAL_COMPLETE_READ_COMPLETE) {
|
|
UsbSerSerialDump(USBSERTRACETM, ("SERIAL_COMPLETE_READ_COMPLETE\n"));
|
|
|
|
//
|
|
// This value is only set by the regular
|
|
// completion routine.
|
|
//
|
|
// If so, then we should simply try to complete.
|
|
//
|
|
|
|
//
|
|
// HACKHACK
|
|
//
|
|
|
|
|
|
ACQUIRE_SPINLOCK(pDevExt, &pDevExt->ControlLock, &oldControlIrql);
|
|
UsbSerGrabReadFromRx(pDevExt);
|
|
pDevExt->CountOnLastRead = 0;
|
|
RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldControlIrql);
|
|
|
|
UsbSerTryToCompleteCurrent(pDevExt, oldIrql, STATUS_SUCCESS,
|
|
&pDevExt->CurrentReadIrp, &pDevExt->ReadQueue,
|
|
&pDevExt->ReadRequestIntervalTimer,
|
|
&pDevExt->ReadRequestTotalTimer,
|
|
UsbSerStartRead, UsbSerGetNextIrp,
|
|
USBSER_REF_INT_TIMER, TRUE);
|
|
|
|
} else if (pDevExt->CountOnLastRead == SERIAL_COMPLETE_READ_CANCEL) {
|
|
UsbSerSerialDump(USBSERTRACETM, ("SERIAL_COMPLETE_READ_CANCEL\n"));
|
|
|
|
//
|
|
// This value is only set by the cancel
|
|
// read routine.
|
|
//
|
|
// If so, then we should simply try to complete.
|
|
//
|
|
|
|
|
|
//
|
|
// HACKHACK
|
|
//
|
|
|
|
ACQUIRE_SPINLOCK(pDevExt, &pDevExt->ControlLock, &oldControlIrql);
|
|
UsbSerGrabReadFromRx(pDevExt);
|
|
pDevExt->CountOnLastRead = 0;
|
|
RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldControlIrql);
|
|
|
|
UsbSerTryToCompleteCurrent(pDevExt, oldIrql, STATUS_CANCELLED,
|
|
&pDevExt->CurrentReadIrp, &pDevExt->ReadQueue,
|
|
&pDevExt->ReadRequestIntervalTimer,
|
|
&pDevExt->ReadRequestTotalTimer,
|
|
UsbSerStartRead, UsbSerGetNextIrp,
|
|
USBSER_REF_INT_TIMER, TRUE);
|
|
|
|
} else if (pDevExt->CountOnLastRead || pDevExt->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 (pDevExt->ReadByIsr) {
|
|
UsbSerSerialDump(USBSERTRACETM, ("ReadByIsr\n"));
|
|
|
|
pDevExt->CountOnLastRead = pDevExt->ReadByIsr;
|
|
pDevExt->ReadByIsr = 0;
|
|
|
|
//
|
|
// 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(&pDevExt->LastReadTime);
|
|
|
|
KeSetTimer(&pDevExt->ReadRequestIntervalTimer,
|
|
*pDevExt->IntervalTimeToUse,
|
|
&pDevExt->IntervalReadTimeoutDpc);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, 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;
|
|
|
|
UsbSerSerialDump(USBSERTRACETM, ("TIMEOUT\n"));
|
|
|
|
KeQuerySystemTime(¤tTime);
|
|
|
|
if ((currentTime.QuadPart - pDevExt->LastReadTime.QuadPart) >=
|
|
pDevExt->IntervalTime.QuadPart) {
|
|
|
|
pDevExt->CountOnLastRead = pDevExt->ReadByIsr = 0;
|
|
|
|
//
|
|
// HACKHACK
|
|
//
|
|
|
|
ACQUIRE_SPINLOCK(pDevExt, &pDevExt->ControlLock, &oldControlIrql);
|
|
UsbSerGrabReadFromRx(pDevExt);
|
|
RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldControlIrql);
|
|
|
|
UsbSerTryToCompleteCurrent(pDevExt, oldIrql, STATUS_TIMEOUT,
|
|
&pDevExt->CurrentReadIrp,
|
|
&pDevExt->ReadQueue,
|
|
&pDevExt->ReadRequestIntervalTimer,
|
|
&pDevExt->ReadRequestTotalTimer,
|
|
UsbSerStartRead, UsbSerGetNextIrp,
|
|
USBSER_REF_INT_TIMER, TRUE);
|
|
|
|
} else {
|
|
KeSetTimer(&pDevExt->ReadRequestIntervalTimer,
|
|
*pDevExt->IntervalTimeToUse,
|
|
&pDevExt->IntervalReadTimeoutDpc);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, oldIrql);
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Timer doesn't really start until the first character.
|
|
// So we should simply resubmit ourselves.
|
|
//
|
|
|
|
UsbSerSerialDump(USBSERTRACETM, ("-\n"));
|
|
|
|
KeSetTimer(&pDevExt->ReadRequestIntervalTimer,
|
|
*pDevExt->IntervalTimeToUse, &pDevExt->IntervalReadTimeoutDpc);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, oldIrql);
|
|
}
|
|
|
|
UsbSerSerialDump(USBSERTRACETM, ("<UsbSerIntervalReadTimeout\n"));
|
|
}
|
|
|