/*++ Copyright (c) 1996 Microsoft Corporation Module Name: complete.c Abstract Completion routines for the major IRP functions. Author: Ervin P. Environment: Kernel mode only Revision History: --*/ #include "pch.h" /* ******************************************************************************** * HidpSetMaxReportSize ******************************************************************************** * * Set the maxReportSize field in the HID device extension * */ ULONG HidpSetMaxReportSize(IN FDO_EXTENSION *fdoExtension) { PHIDP_DEVICE_DESC deviceDesc = &fdoExtension->deviceDesc; ULONG i; /* * For all reports (of all collections) for this device, * find the length of the longest one. */ fdoExtension->maxReportSize = 0; for (i = 0; i < deviceDesc->ReportIDsLength; i++){ PHIDP_REPORT_IDS reportIdent = &deviceDesc->ReportIDs[i]; PHIDCLASS_COLLECTION collection = GetHidclassCollection(fdoExtension, reportIdent->CollectionNumber); if (collection){ if (reportIdent->InputLength > fdoExtension->maxReportSize){ fdoExtension->maxReportSize = reportIdent->InputLength; } } } DBGASSERT(fdoExtension->maxReportSize, ("Input length is zero for fdo %x.", fdoExtension->fdo), FALSE) return fdoExtension->maxReportSize; } /* ******************************************************************************** * CompleteAllPendingReadsForFileExtension ******************************************************************************** * * */ VOID CompleteAllPendingReadsForFileExtension( PHIDCLASS_COLLECTION Collection, PHIDCLASS_FILE_EXTENSION fileExtension) { LIST_ENTRY irpsToComplete; PIRP irp; KIRQL oldIrql; ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG); /* * Move the IRPs to a private queue before completing so they don't * get requeued on the completion thread, causing us to spin forever. */ InitializeListHead(&irpsToComplete); LockFileExtension(fileExtension, &oldIrql); while (irp = DequeueInterruptReadIrp(Collection, fileExtension)){ // // Irps are created from nonpaged pool, // so this is ok to call at Dispatch level. // InsertTailList(&irpsToComplete, &irp->Tail.Overlay.ListEntry); } UnlockFileExtension(fileExtension, oldIrql); /* * Complete all the dequeued read IRPs. */ while (!IsListEmpty(&irpsToComplete)){ PLIST_ENTRY listEntry = RemoveHeadList(&irpsToComplete); irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry); irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED; DBGVERBOSE(("Aborting pending read with status=%xh.", irp->IoStatus.Status)) DBG_RECORD_READ(irp, 0, 0, TRUE) IoCompleteRequest(irp, IO_NO_INCREMENT); } } /* ******************************************************************************** * CompleteAllPendingReadsForCollection ******************************************************************************** * * */ VOID CompleteAllPendingReadsForCollection(PHIDCLASS_COLLECTION Collection) { LIST_ENTRY tmpList; PLIST_ENTRY listEntry; KIRQL oldIrql; InitializeListHead(&tmpList); KeAcquireSpinLock(&Collection->FileExtensionListSpinLock, &oldIrql); /* * We want to process each fileExtension in the list once. * But we can't keep track of where to stop by just remembering * the first item because fileExtensions can get closed while * we're completing the reads. So copy all the file extensions * to a temporary list first. * * This can all probably get removed, since this only gets called * on a remove, when a create can not be received. In addition, * we would have received all close irps since remove only gets * sent when all closes have come through. * */ while (!IsListEmpty(&Collection->FileExtensionList)){ listEntry = RemoveHeadList(&Collection->FileExtensionList); InsertTailList(&tmpList, listEntry); } /* * Now put the fileExtensions back in the list * and cancel the reads on each file extension. */ while (!IsListEmpty(&tmpList)){ PHIDCLASS_FILE_EXTENSION fileExtension; listEntry = RemoveHeadList(&tmpList); /* * Put the fileExtension back in FileExtensionList first * so that it's there in case we get the close while * completing the pending irps. */ InsertTailList(&Collection->FileExtensionList, listEntry); fileExtension = CONTAINING_RECORD(listEntry, HIDCLASS_FILE_EXTENSION, FileList); /* * We will be completing IRPs for this fileExtension. * Always release all spinlocks before calling outside the driver. */ KeReleaseSpinLock(&Collection->FileExtensionListSpinLock, oldIrql); CompleteAllPendingReadsForFileExtension(Collection, fileExtension); KeAcquireSpinLock(&Collection->FileExtensionListSpinLock, &oldIrql); } KeReleaseSpinLock(&Collection->FileExtensionListSpinLock, oldIrql); } /* ******************************************************************************** * CompleteAllPendingReadsForDevice ******************************************************************************** * * */ VOID CompleteAllPendingReadsForDevice(FDO_EXTENSION *fdoExt) { PHIDP_DEVICE_DESC deviceDesc = &fdoExt->deviceDesc; ULONG i; for (i = 0; i < deviceDesc->CollectionDescLength; i++){ PHIDCLASS_COLLECTION collection = &fdoExt->classCollectionArray[i]; CompleteAllPendingReadsForCollection(collection); } } /* ******************************************************************************** * HidpFreePowerEvent ******************************************************************************** * * */ VOID HidpFreePowerEventIrp( PHIDCLASS_COLLECTION Collection ) { PIRP powerEventIrpToComplete = NULL; KIRQL oldIrql; /* * If a power event IRP is queued for this collection, * fail it now. */ KeAcquireSpinLock(&Collection->powerEventSpinLock, &oldIrql); if (ISPTR(Collection->powerEventIrp)){ PDRIVER_CANCEL oldCancelRoutine; powerEventIrpToComplete = Collection->powerEventIrp; oldCancelRoutine = IoSetCancelRoutine(powerEventIrpToComplete, NULL); if (oldCancelRoutine){ ASSERT(oldCancelRoutine == PowerEventCancelRoutine); } else { /* * The IRP was cancelled and the cancel routine WAS called. * The cancel routine will complete the IRP as soon as we drop the spinlock, * so don't touch the IRP. */ ASSERT(powerEventIrpToComplete->Cancel); powerEventIrpToComplete = NULL; } Collection->powerEventIrp = BAD_POINTER; } KeReleaseSpinLock(&Collection->powerEventSpinLock, oldIrql); if (powerEventIrpToComplete){ powerEventIrpToComplete->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED; *(PULONG)powerEventIrpToComplete->AssociatedIrp.SystemBuffer = 0; powerEventIrpToComplete->IoStatus.Information = 0; IoCompleteRequest(powerEventIrpToComplete, IO_NO_INCREMENT); } } /* ******************************************************************************** * HidpDestroyCollection ******************************************************************************** * * */ VOID HidpDestroyCollection(FDO_EXTENSION *fdoExt, PHIDCLASS_COLLECTION Collection) { #if DBG static int reentrancyCounter = 0; if (reentrancyCounter++ != 0) TRAP; ASSERT(Collection->Signature == HIDCLASS_COLLECTION_SIG); #endif CompleteAllPendingReadsForCollection(Collection); if (Collection->hidCollectionInfo.Polled){ StopPollingLoop(Collection, TRUE); } HidpFreePowerEventIrp(Collection); #if DBG Collection->Signature = ~HIDCLASS_COLLECTION_SIG; reentrancyCounter--; #endif } /* ******************************************************************************** * HidpQueryCapsCompletion ******************************************************************************** * * */ NTSTATUS HidpQueryCapsCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PKEVENT event = Context; DBG_COMMON_ENTRY() KeSetEvent(event, 1, FALSE); DBG_COMMON_EXIT() return STATUS_MORE_PROCESSING_REQUIRED; }