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.
739 lines
25 KiB
739 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
polled.c
|
|
|
|
Abstract
|
|
|
|
Read handling routines
|
|
|
|
Author:
|
|
|
|
Ervin P.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* CompleteQueuedIrpsForPolled
|
|
********************************************************************************
|
|
*
|
|
* Complete all waiting client reads with the given report value.
|
|
*
|
|
* Note: report is a 'cooked' report (i.e. it already has the report id added).
|
|
*
|
|
*/
|
|
VOID CompleteQueuedIrpsForPolled( FDO_EXTENSION *fdoExt,
|
|
ULONG collectionNum,
|
|
PUCHAR report,
|
|
ULONG reportLen,
|
|
NTSTATUS status)
|
|
{
|
|
PHIDCLASS_COLLECTION hidCollection;
|
|
|
|
hidCollection = GetHidclassCollection(fdoExt, collectionNum);
|
|
|
|
if (hidCollection){
|
|
PLIST_ENTRY listEntry;
|
|
LIST_ENTRY irpsToComplete;
|
|
PIRP irp;
|
|
ULONG actualLen;
|
|
|
|
/*
|
|
* Note: In order to avoid an infinite loop with a client that
|
|
* resubmits the read each in his completion routine,
|
|
* we must build a separate list of IRPs to be completed
|
|
* while holding the spinlock continuously.
|
|
*/
|
|
InitializeListHead(&irpsToComplete);
|
|
|
|
if (hidCollection->secureReadMode) {
|
|
while (irp = DequeuePolledReadSystemIrp(hidCollection)){
|
|
InsertTailList(&irpsToComplete, &irp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
} else {
|
|
|
|
while (irp = DequeuePolledReadIrp(hidCollection)){
|
|
InsertTailList(&irpsToComplete, &irp->Tail.Overlay.ListEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!IsListEmpty(&irpsToComplete)){
|
|
PIO_STACK_LOCATION stackPtr;
|
|
PHIDCLASS_FILE_EXTENSION fileExtension;
|
|
|
|
listEntry = RemoveHeadList(&irpsToComplete);
|
|
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
stackPtr = IoGetCurrentIrpStackLocation(irp);
|
|
ASSERT(stackPtr);
|
|
fileExtension = (PHIDCLASS_FILE_EXTENSION)stackPtr->FileObject->FsContext;
|
|
ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG);
|
|
|
|
actualLen = 0;
|
|
if (NT_SUCCESS(status)){
|
|
PUCHAR callerBuf;
|
|
|
|
callerBuf = HidpGetSystemAddressForMdlSafe(irp->MdlAddress);
|
|
|
|
if (callerBuf && (stackPtr->Parameters.Read.Length >= reportLen)){
|
|
RtlCopyMemory(callerBuf, report, reportLen);
|
|
irp->IoStatus.Information = actualLen = reportLen;
|
|
} else {
|
|
status = STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
}
|
|
|
|
DBG_RECORD_READ(irp, actualLen, (ULONG)report[0], TRUE)
|
|
irp->IoStatus.Status = status;
|
|
IoCompleteRequest(irp, IO_KEYBOARD_INCREMENT);
|
|
|
|
}
|
|
}
|
|
else {
|
|
TRAP;
|
|
}
|
|
}
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HidpPolledReadComplete
|
|
********************************************************************************
|
|
*
|
|
* Note: the context passed to this callback is the PDO extension for
|
|
* the collection which initiated this read; however, the returned
|
|
* report may be for another collection.
|
|
*/
|
|
NTSTATUS HidpPolledReadComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
|
|
{
|
|
PDO_EXTENSION *pdoExt = (PDO_EXTENSION *)Context;
|
|
FDO_EXTENSION *fdoExt = &pdoExt->deviceFdoExt->fdoExt;
|
|
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
|
|
ULONG reportId;
|
|
PHIDP_REPORT_IDS reportIdent;
|
|
|
|
DBG_COMMON_ENTRY()
|
|
|
|
ASSERT(pdoExt->deviceFdoExt->Signature == HID_DEVICE_EXTENSION_SIG);
|
|
ASSERT(ISPTR(Irp->UserBuffer));
|
|
|
|
InterlockedIncrement(&fdoExt->outstandingRequests);
|
|
|
|
if (fdoExt->deviceDesc.ReportIDs[0].ReportID == 0) {
|
|
/*
|
|
* We previously incremented the UserBuffer to knock off the report id,
|
|
* so restore it now and set the default report id.
|
|
*/
|
|
*(PUCHAR)(--(PUCHAR)Irp->UserBuffer) = (UCHAR)0;
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)){
|
|
Irp->IoStatus.Information++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* WHETHER OR NOT THE CALL SUCCEEDED,
|
|
* we'll complete the waiting client read IRPs with
|
|
* the result of this read.
|
|
*/
|
|
reportId = (ULONG)(*(PUCHAR)Irp->UserBuffer);
|
|
reportIdent = GetReportIdentifier(fdoExt, reportId);
|
|
if (reportIdent){
|
|
ULONG collectionNum = reportIdent->CollectionNumber;
|
|
PHIDCLASS_COLLECTION hidpCollection = GetHidclassCollection(fdoExt, collectionNum);
|
|
PHIDP_COLLECTION_DESC hidCollectionDesc = GetCollectionDesc(fdoExt, collectionNum);
|
|
|
|
if (hidpCollection && hidCollectionDesc){
|
|
ULONG reportLen = (ULONG)Irp->IoStatus.Information;
|
|
|
|
ASSERT((reportLen == hidCollectionDesc->InputLength) || !NT_SUCCESS(Irp->IoStatus.Status));
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)){
|
|
KIRQL oldIrql;
|
|
|
|
/*
|
|
* If this report contains a power-button event, alert the system.
|
|
*/
|
|
CheckReportPowerEvent( fdoExt,
|
|
hidpCollection,
|
|
Irp->UserBuffer,
|
|
reportLen);
|
|
|
|
/*
|
|
* Save this report for "opportunistic" polled device
|
|
* readers who want a result right away.
|
|
* Use the polledDeviceReadQueueSpinLock to protect
|
|
* the savedPolledReportBuf.
|
|
*/
|
|
|
|
if (hidpCollection->secureReadMode) {
|
|
|
|
hidpCollection->polledDataIsStale = TRUE;
|
|
|
|
} else {
|
|
|
|
KeAcquireSpinLock(&hidpCollection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
ASSERT(reportLen <= fdoExt->maxReportSize+1);
|
|
RtlCopyMemory(hidpCollection->savedPolledReportBuf, Irp->UserBuffer, reportLen);
|
|
hidpCollection->savedPolledReportLen = reportLen;
|
|
hidpCollection->polledDataIsStale = FALSE;
|
|
KeReleaseSpinLock(&hidpCollection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy this report for all queued read IRPs on this polled device.
|
|
* Do this AFTER updating the savedPolledReport information
|
|
* because many clients will issue a read again immediately
|
|
* from the completion routine.
|
|
*/
|
|
CompleteQueuedIrpsForPolled( fdoExt,
|
|
collectionNum,
|
|
Irp->UserBuffer,
|
|
reportLen,
|
|
Irp->IoStatus.Status);
|
|
|
|
}
|
|
else {
|
|
TRAP;
|
|
}
|
|
}
|
|
else {
|
|
TRAP;
|
|
}
|
|
|
|
|
|
/*
|
|
* This is an IRP we created to poll the device.
|
|
* Free the buffer we allocated for the read.
|
|
*/
|
|
ExFreePool(Irp->UserBuffer);
|
|
IoFreeIrp(Irp);
|
|
|
|
/*
|
|
* MUST return STATUS_MORE_PROCESSING_REQUIRED here or
|
|
* NTKERN will touch the IRP.
|
|
*/
|
|
DBG_COMMON_EXIT()
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HidpPolledReadComplete_TimerDriven
|
|
********************************************************************************
|
|
*
|
|
*/
|
|
NTSTATUS HidpPolledReadComplete_TimerDriven(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
|
|
{
|
|
PDO_EXTENSION *pdoExt = (PDO_EXTENSION *)Context;
|
|
FDO_EXTENSION *fdoExt = &pdoExt->deviceFdoExt->fdoExt;
|
|
NTSTATUS status;
|
|
|
|
/*
|
|
* Call the actual completion routine.
|
|
*/
|
|
status = HidpPolledReadComplete(DeviceObject, Irp, Context);
|
|
|
|
/*
|
|
* Reset the timer of the collection which initiated this read,
|
|
* (which may be different than the collection that returned the report).
|
|
*/
|
|
if (pdoExt->state == COLLECTION_STATE_RUNNING){
|
|
PHIDCLASS_COLLECTION originatorCollection =
|
|
GetHidclassCollection(fdoExt, pdoExt->collectionNum);
|
|
|
|
if (originatorCollection){
|
|
LARGE_INTEGER timeout;
|
|
timeout.HighPart = -1;
|
|
timeout.LowPart = -(LONG)(originatorCollection->PollInterval_msec*10000);
|
|
KeSetTimer( &originatorCollection->polledDeviceTimer,
|
|
timeout,
|
|
&originatorCollection->polledDeviceTimerDPC);
|
|
}
|
|
else {
|
|
TRAP;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* ReadPolledDevice
|
|
*
|
|
* Issue a read to the polled device on behalf of the
|
|
* top-level collection indicated by pdoExt.
|
|
* (Note that because we keep separate polling loops for
|
|
* each collection, we do reads on behalf of specific collections).
|
|
*
|
|
*/
|
|
BOOLEAN ReadPolledDevice(PDO_EXTENSION *pdoExt, BOOLEAN isTimerDrivenRead)
|
|
{
|
|
BOOLEAN didPollDevice = FALSE;
|
|
FDO_EXTENSION *fdoExt;
|
|
PHIDP_COLLECTION_DESC hidCollectionDesc;
|
|
|
|
fdoExt = &pdoExt->deviceFdoExt->fdoExt;
|
|
|
|
hidCollectionDesc = GetCollectionDesc(fdoExt, pdoExt->collectionNum);
|
|
if (hidCollectionDesc){
|
|
|
|
PIRP irp = IoAllocateIrp(fdoExt->fdo->StackSize, FALSE);
|
|
if (irp){
|
|
/*
|
|
* We cannot issue a read on a specific collection.
|
|
* But we'll allocate a buffer just large enough for a report
|
|
* on the collection we want.
|
|
* Note that hidCollectionDesc->InputLength includes
|
|
* the report id byte, which we may have to prepend ourselves.
|
|
*/
|
|
ULONG reportLen = hidCollectionDesc->InputLength;
|
|
|
|
irp->UserBuffer = ALLOCATEPOOL(NonPagedPool, reportLen);
|
|
if (irp->UserBuffer){
|
|
PIO_COMPLETION_ROUTINE completionRoutine;
|
|
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(irp);
|
|
ASSERT(nextStack);
|
|
|
|
if (fdoExt->deviceDesc.ReportIDs[0].ReportID == 0) {
|
|
/*
|
|
* This device has only one report type,
|
|
* so the minidriver will not include the 1-byte report id
|
|
* (which is implicitly zero).
|
|
* However, we still need to return a 'cooked' report,
|
|
* with the report id, to the user; so bump the buffer
|
|
* we pass down to make room for the report id.
|
|
*/
|
|
*(((PUCHAR)irp->UserBuffer)++) = (UCHAR)0;
|
|
reportLen--;
|
|
}
|
|
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_READ_REPORT;
|
|
nextStack->Parameters.DeviceIoControl.OutputBufferLength = reportLen;
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
completionRoutine = (isTimerDrivenRead) ?
|
|
HidpPolledReadComplete_TimerDriven :
|
|
HidpPolledReadComplete;
|
|
|
|
IoSetCompletionRoutine( irp,
|
|
completionRoutine,
|
|
(PVOID)pdoExt, // context
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
InterlockedDecrement(&fdoExt->outstandingRequests);
|
|
HidpCallDriver(fdoExt->fdo, irp);
|
|
didPollDevice = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(hidCollectionDesc);
|
|
}
|
|
|
|
return didPollDevice;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HidpPolledTimerDpc
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
VOID HidpPolledTimerDpc( IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
{
|
|
PDO_EXTENSION *pdoExt = (PDO_EXTENSION *)DeferredContext;
|
|
FDO_EXTENSION *fdoExt = &pdoExt->deviceFdoExt->fdoExt;
|
|
|
|
ASSERT(pdoExt->deviceFdoExt->Signature == HID_DEVICE_EXTENSION_SIG);
|
|
|
|
if (pdoExt->state == COLLECTION_STATE_RUNNING){
|
|
PHIDCLASS_COLLECTION hidCollection;
|
|
|
|
hidCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum);
|
|
|
|
if (hidCollection){
|
|
KIRQL oldIrql;
|
|
BOOLEAN haveReadIrpsQueued;
|
|
BOOLEAN didPollDevice = FALSE;
|
|
|
|
/*
|
|
* If there are reads pending on this collection,
|
|
* issue a read to the device.
|
|
*
|
|
* Note: we have no control over which collection we are reading.
|
|
* This read may end up returning a report for a different
|
|
* collection! That's ok, since a report for this collection
|
|
* will eventually be returned.
|
|
*/
|
|
KeAcquireSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
haveReadIrpsQueued = !IsListEmpty(&hidCollection->polledDeviceReadQueue);
|
|
KeReleaseSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
if (haveReadIrpsQueued){
|
|
didPollDevice = ReadPolledDevice(pdoExt, TRUE);
|
|
}
|
|
else {
|
|
/*
|
|
* The timer period has expired, so any saved reports
|
|
* are now stale.
|
|
*/
|
|
hidCollection->polledDataIsStale = TRUE;
|
|
}
|
|
|
|
/*
|
|
* If we actually polled the device, we'll reset the timer in the
|
|
* completion routine; otherwise, we do it here.
|
|
*/
|
|
if (!didPollDevice){
|
|
LARGE_INTEGER timeout;
|
|
timeout.HighPart = -1;
|
|
timeout.LowPart = -(LONG)(hidCollection->PollInterval_msec*10000);
|
|
KeSetTimer( &hidCollection->polledDeviceTimer,
|
|
timeout,
|
|
&hidCollection->polledDeviceTimerDPC);
|
|
}
|
|
}
|
|
else {
|
|
TRAP;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* StartPollingLoop
|
|
********************************************************************************
|
|
*
|
|
* Start a polling loop for a particular collection.
|
|
*
|
|
*/
|
|
BOOLEAN StartPollingLoop( FDO_EXTENSION *fdoExt,
|
|
PHIDCLASS_COLLECTION hidCollection,
|
|
BOOLEAN freshQueue)
|
|
{
|
|
ULONG ctnIndex = hidCollection->CollectionIndex;
|
|
LARGE_INTEGER timeout;
|
|
KIRQL oldIrql;
|
|
|
|
if (freshQueue){
|
|
InitializeListHead(&hidCollection->polledDeviceReadQueue);
|
|
KeInitializeSpinLock(&hidCollection->polledDeviceReadQueueSpinLock);
|
|
KeInitializeTimer(&hidCollection->polledDeviceTimer);
|
|
}
|
|
|
|
/*
|
|
* Use polledDeviceReadQueueSpinLock to protect the timer structures as well
|
|
* as the queue.
|
|
*/
|
|
KeAcquireSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
|
|
KeInitializeDpc( &hidCollection->polledDeviceTimerDPC,
|
|
HidpPolledTimerDpc,
|
|
&fdoExt->collectionPdoExtensions[ctnIndex]->pdoExt);
|
|
|
|
KeReleaseSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
timeout.HighPart = -1;
|
|
timeout.LowPart = -(LONG)(hidCollection->PollInterval_msec*10000);
|
|
KeSetTimer( &hidCollection->polledDeviceTimer,
|
|
timeout,
|
|
&hidCollection->polledDeviceTimerDPC);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* StopPollingLoop
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
VOID StopPollingLoop(PHIDCLASS_COLLECTION hidCollection, BOOLEAN flushQueue)
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
/*
|
|
* Use polledDeviceReadQueueSpinLock to protect the timer structures as well
|
|
* as the queue.
|
|
*/
|
|
KeAcquireSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
|
|
KeCancelTimer(&hidCollection->polledDeviceTimer);
|
|
KeInitializeTimer(&hidCollection->polledDeviceTimer);
|
|
|
|
KeReleaseSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
/*
|
|
* Fail all the queued IRPs.
|
|
*/
|
|
if (flushQueue){
|
|
PIRP irp;
|
|
LIST_ENTRY irpsToComplete;
|
|
|
|
/*
|
|
* Move the IRPs to a temporary queue first so they don't get requeued
|
|
* on the completion thread and cause us to loop forever.
|
|
*/
|
|
InitializeListHead(&irpsToComplete);
|
|
while (irp = DequeuePolledReadIrp(hidCollection)){
|
|
InsertTailList(&irpsToComplete, &irp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
while (!IsListEmpty(&irpsToComplete)){
|
|
PLIST_ENTRY listEntry = RemoveHeadList(&irpsToComplete);
|
|
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
|
|
DBG_RECORD_READ(irp, 0, 0, TRUE)
|
|
irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
|
|
irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* PolledReadCancelRoutine
|
|
********************************************************************************
|
|
*
|
|
* We need to set an IRP's cancel routine to non-NULL before
|
|
* we queue it; so just use a pointer this NULL function.
|
|
*
|
|
*/
|
|
VOID PolledReadCancelRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
FDO_EXTENSION *fdoExt;
|
|
PHIDCLASS_COLLECTION hidCollection;
|
|
ULONG collectionIndex;
|
|
KIRQL oldIrql;
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
|
|
ASSERT(hidDeviceExtension->isClientPdo);
|
|
fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
|
|
|
|
collectionIndex = hidDeviceExtension->pdoExt.collectionIndex;
|
|
hidCollection = &fdoExt->classCollectionArray[collectionIndex];
|
|
|
|
KeAcquireSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
|
|
ASSERT(hidCollection->numPendingReads > 0);
|
|
hidCollection->numPendingReads--;
|
|
|
|
KeReleaseSpinLock(&hidCollection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
DBG_RECORD_READ(Irp, 0, 0, TRUE)
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
|
|
NTSTATUS EnqueuePolledReadIrp(PHIDCLASS_COLLECTION collection, PIRP Irp)
|
|
{
|
|
NTSTATUS status;
|
|
KIRQL oldIrql;
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
|
|
KeAcquireSpinLock(&collection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
|
|
/*
|
|
* Must set a cancel routine before
|
|
* checking the Cancel flag.
|
|
*/
|
|
oldCancelRoutine = IoSetCancelRoutine(Irp, PolledReadCancelRoutine);
|
|
ASSERT(!oldCancelRoutine);
|
|
|
|
/*
|
|
* Make sure this Irp wasn't just cancelled.
|
|
* Note that there is NO RACE CONDITION here
|
|
* because we are holding the fileExtension lock.
|
|
*/
|
|
if (Irp->Cancel){
|
|
/*
|
|
* This IRP was cancelled.
|
|
*/
|
|
oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
|
|
if (oldCancelRoutine){
|
|
/*
|
|
* The cancel routine was NOT called.
|
|
* Return error so that caller completes the IRP.
|
|
*/
|
|
DBG_RECORD_READ(Irp, IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length, 0, TRUE)
|
|
ASSERT(oldCancelRoutine == PolledReadCancelRoutine);
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else {
|
|
/*
|
|
* The cancel routine was called.
|
|
* As soon as we drop the spinlock it will dequeue
|
|
* and complete the IRP.
|
|
* Initialize the IRP's listEntry so that the dequeue
|
|
* doesn't cause corruption.
|
|
* Then don't touch the irp.
|
|
*/
|
|
InitializeListHead(&Irp->Tail.Overlay.ListEntry);
|
|
collection->numPendingReads++; // because cancel routine will decrement
|
|
|
|
IoMarkIrpPending(Irp);
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
else {
|
|
DBG_RECORD_READ(Irp, IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length, 0, FALSE)
|
|
|
|
/*
|
|
* There are no reports waiting.
|
|
* Queue this irp onto the file extension's list of pending irps.
|
|
*/
|
|
InsertTailList(&collection->polledDeviceReadQueue, &Irp->Tail.Overlay.ListEntry);
|
|
collection->numPendingReads++;
|
|
|
|
IoMarkIrpPending(Irp);
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
KeReleaseSpinLock(&collection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
DBGSUCCESS(status, TRUE)
|
|
return status;
|
|
}
|
|
|
|
PIRP DequeuePolledReadSystemIrp(PHIDCLASS_COLLECTION collection)
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP irp = NULL;
|
|
PLIST_ENTRY listEntry;
|
|
PHIDCLASS_FILE_EXTENSION fileExtension;
|
|
PFILE_OBJECT fileObject;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
|
|
|
|
|
|
KeAcquireSpinLock(&collection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
|
|
listEntry = &collection->polledDeviceReadQueue;
|
|
|
|
while (!irp && ((listEntry = listEntry->Flink) != &collection->polledDeviceReadQueue)) {
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
|
|
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
fileObject = irpSp->FileObject;
|
|
fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext;
|
|
|
|
if (!fileExtension->isSecureOpen) {
|
|
irp = NULL;
|
|
continue;
|
|
}
|
|
|
|
|
|
RemoveEntryList(listEntry);
|
|
oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
|
|
|
|
if (oldCancelRoutine){
|
|
ASSERT(oldCancelRoutine == PolledReadCancelRoutine);
|
|
ASSERT(collection->numPendingReads > 0);
|
|
collection->numPendingReads--;
|
|
}
|
|
else {
|
|
/*
|
|
* IRP was cancelled and cancel routine was called.
|
|
* As soon as we drop the spinlock,
|
|
* the cancel routine will dequeue and complete this IRP.
|
|
* Initialize the IRP's listEntry so that the dequeue doesn't cause corruption.
|
|
* Then, don't touch the IRP.
|
|
*/
|
|
ASSERT(irp->Cancel);
|
|
InitializeListHead(&irp->Tail.Overlay.ListEntry);
|
|
irp = NULL;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&collection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
return irp;
|
|
}
|
|
|
|
PIRP DequeuePolledReadIrp(PHIDCLASS_COLLECTION collection)
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP irp = NULL;
|
|
|
|
KeAcquireSpinLock(&collection->polledDeviceReadQueueSpinLock, &oldIrql);
|
|
|
|
while (!irp && !IsListEmpty(&collection->polledDeviceReadQueue)){
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
PLIST_ENTRY listEntry = RemoveHeadList(&collection->polledDeviceReadQueue);
|
|
|
|
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
|
|
|
|
if (oldCancelRoutine){
|
|
ASSERT(oldCancelRoutine == PolledReadCancelRoutine);
|
|
ASSERT(collection->numPendingReads > 0);
|
|
collection->numPendingReads--;
|
|
}
|
|
else {
|
|
/*
|
|
* IRP was cancelled and cancel routine was called.
|
|
* As soon as we drop the spinlock,
|
|
* the cancel routine will dequeue and complete this IRP.
|
|
* Initialize the IRP's listEntry so that the dequeue doesn't cause corruption.
|
|
* Then, don't touch the IRP.
|
|
*/
|
|
ASSERT(irp->Cancel);
|
|
InitializeListHead(&irp->Tail.Overlay.ListEntry);
|
|
irp = NULL;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&collection->polledDeviceReadQueueSpinLock, oldIrql);
|
|
|
|
return irp;
|
|
}
|