837 lines
25 KiB
C
837 lines
25 KiB
C
/***************************************************************************
|
||
|
||
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"));
|
||
}
|
||
|