NTSTATUS Bus_PDO_PnP ( __in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, __in PIO_STACK_LOCATION IrpStack, __in PPDO_DEVICE_DATA DeviceData ) /*++ Routine Description: Handle requests from the Plug & Play system for the devices on the BUS --*/ { NTSTATUS status; PAGED_CODE (); // // NB: Because we are a bus enumerator, we have no one to whom we could // defer these irps. Therefore we do not pass them down but merely // return them. // switch (IrpStack->MinorFunction) { case IRP_MN_START_DEVICE: // // Here we do what ever initialization and ``turning on'' that is // required to allow others to access this device. // Power up the device. // DeviceData->DevicePowerState = PowerDeviceD0; SET_NEW_PNP_STATE(DeviceData, Started); status = STATUS_SUCCESS; break; case IRP_MN_STOP_DEVICE: // // Here we shut down the device and give up and unmap any resources // we acquired for the device. // SET_NEW_PNP_STATE(DeviceData, Stopped); status = STATUS_SUCCESS; break; case IRP_MN_QUERY_STOP_DEVICE: // // No reason here why we can't stop the device. // If there were a reason we should speak now, because answering success // here may result in a stop device irp. // SET_NEW_PNP_STATE(DeviceData, StopPending); status = STATUS_SUCCESS; break; case IRP_MN_CANCEL_STOP_DEVICE: // // The stop was canceled. Whatever state we set, or resources we put // on hold in anticipation of the forthcoming STOP device IRP should be // put back to normal. Someone, in the long list of concerned parties, // has failed the stop device query. // // // First check to see whether you have received cancel-stop // without first receiving a query-stop. This could happen if someone // above us fails a query-stop and passes down the subsequent // cancel-stop. // if (StopPending == DeviceData->DevicePnPState) { // // We did receive a query-stop, so restore. // RESTORE_PREVIOUS_PNP_STATE(DeviceData); } status = STATUS_SUCCESS;// We must not fail this IRP. break; case IRP_MN_QUERY_REMOVE_DEVICE: // // Check to see whether the device can be removed safely. // If not fail this request. This is the last opportunity // to do so. // if (DeviceData->ToasterInterfaceRefCount){ // // Somebody is still using our interface. // We must fail remove. // status = STATUS_UNSUCCESSFUL; break; } SET_NEW_PNP_STATE(DeviceData, RemovePending); status = STATUS_SUCCESS; break; case IRP_MN_CANCEL_REMOVE_DEVICE: // // Clean up a remove that did not go through. // // // First check to see whether you have received cancel-remove // without first receiving a query-remove. This could happen if // someone above us fails a query-remove and passes down the // subsequent cancel-remove. // if (RemovePending == DeviceData->DevicePnPState) { // // We did receive a query-remove, so restore. // RESTORE_PREVIOUS_PNP_STATE(DeviceData); } status = STATUS_SUCCESS; // We must not fail this IRP. break; case IRP_MN_SURPRISE_REMOVAL: // // We should stop all access to the device and relinquish all the // resources. Let's just mark that it happened and we will do // the cleanup later in IRP_MN_REMOVE_DEVICE. // SET_NEW_PNP_STATE(DeviceData, SurpriseRemovePending); status = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: // // Present is set to true when the pdo is exposed via PlugIn IOCTL. // It is set to FALSE when a UnPlug IOCTL is received. // We will delete the PDO only after we have reported to the // Plug and Play manager that it's missing. // if (DeviceData->ReportedMissing) { PFDO_DEVICE_DATA fdoData; SET_NEW_PNP_STATE(DeviceData, Deleted); // // Remove the PDO from the list and decrement the count of PDO. // Don't forget to synchronize access to the FDO data. // If the parent FDO is deleted before child PDOs, the ParentFdo // pointer will be NULL. This could happen if the child PDO // is in a SurpriseRemovePending state when the parent FDO // is removed. // if (DeviceData->ParentFdo) { fdoData = FDO_FROM_PDO(DeviceData); ExAcquireFastMutex (&fdoData->Mutex); RemoveEntryList (&DeviceData->Link); fdoData->NumPDOs--; ExReleaseFastMutex (&fdoData->Mutex); } // // Free up resources associated with PDO and delete it. // status = Bus_DestroyPdo(DeviceObject, DeviceData); break; } if (DeviceData->Present) { // // When the device is disabled, the PDO transitions from // RemovePending to NotStarted. We shouldn't delete // the PDO because a) the device is still present on the bus, // b) we haven't reported missing to the PnP manager. // SET_NEW_PNP_STATE(DeviceData, NotStarted); status = STATUS_SUCCESS; } else { ASSERT(DeviceData->Present); status = STATUS_SUCCESS; } break; case IRP_MN_QUERY_CAPABILITIES: // // Return the capabilities of a device, such as whether the device // can be locked or ejected..etc // status = Bus_PDO_QueryDeviceCaps(DeviceData, Irp); break; case IRP_MN_QUERY_ID: // Query the IDs of the device Bus_KdPrint_Cont (DeviceData, BUS_DBG_PNP_TRACE, ("\tQueryId Type: %s\n", DbgDeviceIDString(IrpStack->Parameters.QueryId.IdType))); status = Bus_PDO_QueryDeviceId(DeviceData, Irp); break; case IRP_MN_QUERY_DEVICE_RELATIONS: Bus_KdPrint_Cont (DeviceData, BUS_DBG_PNP_TRACE, ("\tQueryDeviceRelation Type: %s\n",DbgDeviceRelationString(\ IrpStack->Parameters.QueryDeviceRelations.Type))); status = Bus_PDO_QueryDeviceRelations(DeviceData, Irp); break; case IRP_MN_QUERY_DEVICE_TEXT: status = Bus_PDO_QueryDeviceText(DeviceData, Irp); break; case IRP_MN_QUERY_RESOURCES: status = Bus_PDO_QueryResources(DeviceData, Irp); break; case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: status = Bus_PDO_QueryResourceRequirements(DeviceData, Irp); break; case IRP_MN_QUERY_BUS_INFORMATION: status = Bus_PDO_QueryBusInformation(DeviceData, Irp); break; case IRP_MN_DEVICE_USAGE_NOTIFICATION: // // OPTIONAL for bus drivers. // This bus drivers any of the bus's descendants // (child device, child of a child device, etc.) do not // contain a memory file namely paging file, dump file, // or hibernation file. So we fail this Irp. // status = STATUS_UNSUCCESSFUL; break; case IRP_MN_EJECT: // // For the device to be ejected, the device must be in the D3 // device power state (off) and must be unlocked // (if the device supports locking). Any driver that returns success // for this IRP must wait until the device has been ejected before // completing the IRP. // DeviceData->Present = FALSE; status = STATUS_SUCCESS; break; case IRP_MN_QUERY_INTERFACE: // // This request enables a driver to export a direct-call // interface to other drivers. A bus driver that exports // an interface must handle this request for its child // devices (child PDOs). // status = Bus_PDO_QueryInterface(DeviceData, Irp); break; case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: // // OPTIONAL for bus drivers. // The PnP Manager sends this IRP to a device // stack so filter and function drivers can adjust the // resources required by the device, if appropriate. // //break; //case IRP_MN_QUERY_PNP_DEVICE_STATE: // // OPTIONAL for bus drivers. // The PnP Manager sends this IRP after the drivers for // a device return success from the IRP_MN_START_DEVICE // request. The PnP Manager also sends this IRP when a // driver for the device calls IoInvalidateDeviceState. // // break; //case IRP_MN_READ_CONFIG: //case IRP_MN_WRITE_CONFIG: // // Bus drivers for buses with configuration space must handle // this request for their child devices. Our devices don't // have a config space. // // break; //case IRP_MN_SET_LOCK: // // Our device is not a lockable device // so we don't support this Irp. // // break; default: // //Bus_KdPrint_Cont (DeviceData, BUS_DBG_PNP_TRACE,("\t Not handled\n")); // For PnP requests to the PDO that we do not understand we should // return the IRP WITHOUT setting the status or information fields. // These fields may have already been set by a filter (eg acpi). status = Irp->IoStatus.Status; break; } Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; }
NTSTATUS Bus_FDO_PnP ( __in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, __in PIO_STACK_LOCATION IrpStack, __in PFDO_DEVICE_DATA DeviceData ) /*++ Routine Description: Handle requests from the Plug & Play system for the BUS itself --*/ { NTSTATUS status; ULONG length, prevcount, numPdosPresent; PLIST_ENTRY entry, listHead, nextEntry; PPDO_DEVICE_DATA pdoData; PDEVICE_RELATIONS relations, oldRelations; PAGED_CODE (); Bus_IncIoCount (DeviceData); switch (IrpStack->MinorFunction) { case IRP_MN_START_DEVICE: // // Send the Irp down and wait for it to come back. // Do not touch the hardware until then. // status = Bus_SendIrpSynchronously (DeviceData->NextLowerDriver, Irp); if (NT_SUCCESS(status)) { // // Initialize your device with the resources provided // by the PnP manager to your device. // status = Bus_StartFdo (DeviceData, Irp); } // // We must now complete the IRP, since we stopped it in the // completion routine with MORE_PROCESSING_REQUIRED. // Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); Bus_DecIoCount (DeviceData); return status; case IRP_MN_QUERY_STOP_DEVICE: // // The PnP manager is trying to stop the device // for resource rebalancing. Fail this now if you // cannot stop the device in response to STOP_DEVICE. // SET_NEW_PNP_STATE(DeviceData, StopPending); Irp->IoStatus.Status = STATUS_SUCCESS; // You must not fail the IRP. break; case IRP_MN_CANCEL_STOP_DEVICE: // // The PnP Manager sends this IRP, at some point after an // IRP_MN_QUERY_STOP_DEVICE, to inform the drivers for a // device that the device will not be stopped for // resource reconfiguration. // // // First check to see whether you have received cancel-stop // without first receiving a query-stop. This could happen if // someone above us fails a query-stop and passes down the subsequent // cancel-stop. // if (StopPending == DeviceData->DevicePnPState) { // // We did receive a query-stop, so restore. // RESTORE_PREVIOUS_PNP_STATE(DeviceData); ASSERT(DeviceData->DevicePnPState == Started); } Irp->IoStatus.Status = STATUS_SUCCESS; // We must not fail the IRP. break; case IRP_MN_STOP_DEVICE: // // Stop device means that the resources given during Start device // are now revoked. Note: You must not fail this Irp. // But before you relieve resources make sure there are no I/O in // progress. Wait for the existing ones to be finished. // To do that, first we will decrement this very operation. // When the counter goes to 1, Stop event is set. // Bus_DecIoCount(DeviceData); KeWaitForSingleObject( &DeviceData->StopEvent, Executive, // Waiting reason of a driver KernelMode, // Waiting in kernel mode FALSE, // No allert NULL); // No timeout // // Increment the counter back because this IRP has to // be sent down to the lower stack. // Bus_IncIoCount (DeviceData); // // Free resources given by start device. // SET_NEW_PNP_STATE(DeviceData, Stopped); // // We don't need a completion routine so fire and forget. // // Set the current stack location to the next stack location and // call the next device object. // Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_QUERY_REMOVE_DEVICE: // // If we were to fail this call then we would need to complete the // IRP here. Since we are not, set the status to SUCCESS and // call the next driver. // SET_NEW_PNP_STATE(DeviceData, RemovePending); Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_CANCEL_REMOVE_DEVICE: // // If we were to fail this call then we would need to complete the // IRP here. Since we are not, set the status to SUCCESS and // call the next driver. // // // First check to see whether you have received cancel-remove // without first receiving a query-remove. This could happen if // someone above us fails a query-remove and passes down the // subsequent cancel-remove. // if (RemovePending == DeviceData->DevicePnPState) { // // We did receive a query-remove, so restore. // RESTORE_PREVIOUS_PNP_STATE(DeviceData); } Irp->IoStatus.Status = STATUS_SUCCESS;// You must not fail the IRP. break; case IRP_MN_SURPRISE_REMOVAL: // // The device has been unexpectedly removed from the machine // and is no longer available for I/O. Bus_RemoveFdo clears // all the resources, frees the interface and de-registers // with WMI, but it doesn't delete the FDO. That's done // later in Remove device query. // SET_NEW_PNP_STATE(DeviceData, SurpriseRemovePending); Bus_RemoveFdo(DeviceData); ExAcquireFastMutex (&DeviceData->Mutex); listHead = &DeviceData->ListOfPDOs; for(entry = listHead->Flink,nextEntry = entry->Flink; entry != listHead; entry = nextEntry,nextEntry = entry->Flink) { pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link); RemoveEntryList (&pdoData->Link); InitializeListHead (&pdoData->Link); pdoData->ParentFdo = NULL; pdoData->ReportedMissing = TRUE; } ExReleaseFastMutex (&DeviceData->Mutex); Irp->IoStatus.Status = STATUS_SUCCESS; // You must not fail the IRP. break; case IRP_MN_REMOVE_DEVICE: // // The Plug & Play system has dictated the removal of this device. // We have no choice but to detach and delete the device object. // // // Check the state flag to see whether you are surprise removed // if (DeviceData->DevicePnPState != SurpriseRemovePending) { Bus_RemoveFdo(DeviceData); } SET_NEW_PNP_STATE(DeviceData, Deleted); // // Wait for all outstanding requests to complete. // We need two decrements here, one for the increment in // the beginning of this function, the other for the 1-biased value of // OutstandingIO. // Bus_DecIoCount (DeviceData); // // The requestCount is at least one here (is 1-biased) // Bus_DecIoCount (DeviceData); KeWaitForSingleObject ( &DeviceData->RemoveEvent, Executive, KernelMode, FALSE, NULL); // // Typically the system removes all the children before // removing the parent FDO. If for any reason child Pdos are // still present we will destroy them explicitly, with one exception - // we will not delete the PDOs that are in SurpriseRemovePending state. // ExAcquireFastMutex (&DeviceData->Mutex); listHead = &DeviceData->ListOfPDOs; for(entry = listHead->Flink,nextEntry = entry->Flink; entry != listHead; entry = nextEntry,nextEntry = entry->Flink) { pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link); RemoveEntryList (&pdoData->Link); if (SurpriseRemovePending == pdoData->DevicePnPState) { // // We will reinitialize the list head so that we // wouldn't barf when we try to delink this PDO from // the parent's PDOs list, when the system finally // removes the PDO. Let's also not forget to set the // ReportedMissing flag to cause the deletion of the PDO. // Bus_KdPrint_Cont(DeviceData, BUS_DBG_PNP_INFO, ("\tFound a surprise removed device: 0x%p\n", pdoData->Self)); InitializeListHead (&pdoData->Link); pdoData->ParentFdo = NULL; pdoData->ReportedMissing = TRUE; continue; } DeviceData->NumPDOs--; Bus_DestroyPdo (pdoData->Self, pdoData); } ExReleaseFastMutex (&DeviceData->Mutex); // // We need to send the remove down the stack before we detach, // but we don't need to wait for the completion of this operation // (and to register a completion routine). // Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (DeviceData->NextLowerDriver, Irp); // // Detach from the underlying devices. // IoDetachDevice (DeviceData->NextLowerDriver); Bus_KdPrint_Cont(DeviceData, BUS_DBG_PNP_INFO, ("\tDeleting FDO: 0x%p\n", DeviceObject)); IoDeleteDevice (DeviceObject); return status; case IRP_MN_QUERY_DEVICE_RELATIONS: Bus_KdPrint_Cont (DeviceData, BUS_DBG_PNP_TRACE, ("\tQueryDeviceRelation Type: %s\n", DbgDeviceRelationString(\ IrpStack->Parameters.QueryDeviceRelations.Type))); if (BusRelations != IrpStack->Parameters.QueryDeviceRelations.Type) { // // We don't support any other Device Relations // break; } // // Tell the plug and play system about all the PDOs. // // There might also be device relations below and above this FDO, // so, be sure to propagate the relations from the upper drivers. // // No Completion routine is needed so long as the status is preset // to success. (PDOs complete plug and play irps with the current // IoStatus.Status and IoStatus.Information as the default.) // ExAcquireFastMutex (&DeviceData->Mutex); oldRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information; if (oldRelations) { prevcount = oldRelations->Count; if (!DeviceData->NumPDOs) { // // There is a device relations struct already present and we have // nothing to add to it, so just call IoSkip and IoCall // ExReleaseFastMutex (&DeviceData->Mutex); break; } } else { prevcount = 0; } // // Calculate the number of PDOs actually present on the bus // numPdosPresent = 0; for (entry = DeviceData->ListOfPDOs.Flink; entry != &DeviceData->ListOfPDOs; entry = entry->Flink) { pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link); if (pdoData->Present) numPdosPresent++; } // // Need to allocate a new relations structure and add our // PDOs to it. // length = sizeof(DEVICE_RELATIONS) + ((numPdosPresent + prevcount) * sizeof (PDEVICE_OBJECT)) -1; relations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag (PagedPool, length, BUSENUM_POOL_TAG); if (NULL == relations) { // // Fail the IRP // ExReleaseFastMutex (&DeviceData->Mutex); Irp->IoStatus.Status = status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest (Irp, IO_NO_INCREMENT); Bus_DecIoCount (DeviceData); return status; } // // Copy in the device objects so far // if (prevcount) { RtlCopyMemory (relations->Objects, oldRelations->Objects, prevcount * sizeof (PDEVICE_OBJECT)); } relations->Count = prevcount + numPdosPresent; // // For each PDO present on this bus add a pointer to the device relations // buffer, being sure to take out a reference to that object. // The Plug & Play system will dereference the object when it is done // with it and free the device relations buffer. // for (entry = DeviceData->ListOfPDOs.Flink; entry != &DeviceData->ListOfPDOs; entry = entry->Flink) { pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link); if (pdoData->Present) { relations->Objects[prevcount] = pdoData->Self; ObReferenceObject (pdoData->Self); prevcount++; } else { pdoData->ReportedMissing = TRUE; } } Bus_KdPrint_Cont (DeviceData, BUS_DBG_PNP_TRACE, ("\t#PDOS present = %d\n\t#PDOs reported = %d\n", DeviceData->NumPDOs, relations->Count)); // // Replace the relations structure in the IRP with the new // one. // if (oldRelations) { ExFreePool (oldRelations); } Irp->IoStatus.Information = (ULONG_PTR) relations; ExReleaseFastMutex (&DeviceData->Mutex); // // Set up and pass the IRP further down the stack // Irp->IoStatus.Status = STATUS_SUCCESS; break; default: // // In the default case we merely call the next driver. // We must not modify Irp->IoStatus.Status or complete the IRP. // break; } IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (DeviceData->NextLowerDriver, Irp); Bus_DecIoCount (DeviceData); return status; }
NTSTATUS Bus_FDO_PnP(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, __in PIO_STACK_LOCATION IrpStack, __in PFDO_DEVICE_DATA DeviceData) { NTSTATUS status; ULONG length, prevcount, numPdosPresent, numPdosMissing; PLIST_ENTRY entry, listHead, nextEntry; PPDO_DEVICE_DATA pdoData; PDEVICE_RELATIONS relations, oldRelations; PAGED_CODE(); Bus_IncIoCount(DeviceData); switch (IrpStack->MinorFunction) { case IRP_MN_START_DEVICE: status = Bus_SendIrpSynchronously(DeviceData->NextLowerDriver, Irp); if (NT_SUCCESS(status)) { status = Bus_StartFdo (DeviceData, Irp); } Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); Bus_DecIoCount(DeviceData); return status; case IRP_MN_QUERY_STOP_DEVICE: SET_NEW_PNP_STATE(DeviceData, StopPending); Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_CANCEL_STOP_DEVICE: if (StopPending == DeviceData->DevicePnPState) { RESTORE_PREVIOUS_PNP_STATE(DeviceData); ASSERT(DeviceData->DevicePnPState == Started); } Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_STOP_DEVICE: Bus_DecIoCount(DeviceData); KeWaitForSingleObject(&DeviceData->StopEvent, Executive, KernelMode, FALSE, NULL); Bus_IncIoCount(DeviceData); SET_NEW_PNP_STATE(DeviceData, Stopped); Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_QUERY_REMOVE_DEVICE: SET_NEW_PNP_STATE(DeviceData, RemovePending); Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_CANCEL_REMOVE_DEVICE: if (DeviceData->DevicePnPState == RemovePending) { RESTORE_PREVIOUS_PNP_STATE(DeviceData); } Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_SURPRISE_REMOVAL: SET_NEW_PNP_STATE(DeviceData, SurpriseRemovePending); Bus_RemoveFdo(DeviceData); ExAcquireFastMutex(&DeviceData->Mutex); { listHead = &DeviceData->ListOfPDOs; for(entry = listHead->Flink,nextEntry = entry->Flink; entry != listHead; entry = nextEntry, nextEntry = entry->Flink) { pdoData = CONTAINING_RECORD(entry, PDO_DEVICE_DATA, Link); RemoveEntryList(&pdoData->Link); InitializeListHead(&pdoData->Link); pdoData->ParentFdo = NULL; pdoData->ReportedMissing = TRUE; } } ExReleaseFastMutex(&DeviceData->Mutex); Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: if (DeviceData->DevicePnPState != SurpriseRemovePending) { Bus_RemoveFdo(DeviceData); } SET_NEW_PNP_STATE(DeviceData, Deleted); Bus_DecIoCount(DeviceData); Bus_DecIoCount(DeviceData); KeWaitForSingleObject(&DeviceData->RemoveEvent, Executive, KernelMode, FALSE, NULL); ExAcquireFastMutex(&DeviceData->Mutex); { listHead = &DeviceData->ListOfPDOs; for(entry = listHead->Flink,nextEntry = entry->Flink; entry != listHead; entry = nextEntry, nextEntry = entry->Flink) { pdoData = CONTAINING_RECORD(entry, PDO_DEVICE_DATA, Link); RemoveEntryList(&pdoData->Link); if (pdoData->DevicePnPState == SurpriseRemovePending) { Bus_KdPrint(("\tFound a surprise removed device: 0x%p\n", pdoData->Self)); InitializeListHead(&pdoData->Link); pdoData->ParentFdo = NULL; pdoData->ReportedMissing = TRUE; continue; } Bus_DestroyPdo(pdoData->Self, pdoData); } } ExReleaseFastMutex(&DeviceData->Mutex); Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(DeviceData->NextLowerDriver, Irp); IoDetachDevice(DeviceData->NextLowerDriver); Bus_KdPrint(("\tDeleting FDO: 0x%p\n", DeviceObject)); IoDeleteDevice(DeviceObject); return status; case IRP_MN_QUERY_DEVICE_RELATIONS: Bus_KdPrint(("\tQueryDeviceRelation Type: %s\n", DbgDeviceRelationString(IrpStack->Parameters.QueryDeviceRelations.Type))); if (IrpStack->Parameters.QueryDeviceRelations.Type != BusRelations) { break; } ExAcquireFastMutex(&DeviceData->Mutex); oldRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information; if (oldRelations) { prevcount = oldRelations->Count; if (!DeviceData->NumPDOs) { ExReleaseFastMutex(&DeviceData->Mutex); break; } } else { prevcount = 0; } numPdosPresent = 0; numPdosMissing = 0; for (entry = DeviceData->ListOfPDOs.Flink; entry != &DeviceData->ListOfPDOs; entry = entry->Flink) { pdoData = CONTAINING_RECORD(entry, PDO_DEVICE_DATA, Link); if (pdoData->Present) numPdosPresent++; } length = sizeof(DEVICE_RELATIONS) + ((numPdosPresent + prevcount) * sizeof (PDEVICE_OBJECT)) - 1; relations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag(PagedPool, length, BUSENUM_POOL_TAG); if (relations == NULL) { ExReleaseFastMutex(&DeviceData->Mutex); Irp->IoStatus.Status = status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest(Irp, IO_NO_INCREMENT); Bus_DecIoCount(DeviceData); return status; } if (prevcount) { RtlCopyMemory(relations->Objects, oldRelations->Objects, prevcount * sizeof (PDEVICE_OBJECT)); } relations->Count = prevcount + numPdosPresent; for (entry = DeviceData->ListOfPDOs.Flink; entry != &DeviceData->ListOfPDOs; entry = entry->Flink) { pdoData = CONTAINING_RECORD(entry, PDO_DEVICE_DATA, Link); if (pdoData->Present) { relations->Objects[prevcount] = pdoData->Self; ObReferenceObject(pdoData->Self); prevcount++; } else { pdoData->ReportedMissing = TRUE; numPdosMissing++; } } Bus_KdPrint(("#PDOS Present = %d, Reported = %d, Missing = %d, Listed = %d", numPdosPresent, relations->Count, numPdosMissing, DeviceData->NumPDOs)); if (oldRelations) { ExFreePool(oldRelations); } Irp->IoStatus.Information = (ULONG_PTR) relations; ExReleaseFastMutex(&DeviceData->Mutex); Irp->IoStatus.Status = STATUS_SUCCESS; break; default: break; } IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(DeviceData->NextLowerDriver, Irp); Bus_DecIoCount(DeviceData); return status; }