/*++ Copyright (c) Microsoft Corporation Module Name: FxPkgPdoKM.cpp Abstract: This module implements the Pnp package for Pdo devices. Author: Environment: Kernel mode only Revision History: --*/ #include "../pnppriv.hpp" #include // Tracing support #if defined(EVENT_TRACING) extern "C" { #include "FxPkgPdoKM.tmh" } #endif _Must_inspect_result_ NTSTATUS FxPkgPdo::_PnpQueryResources( __inout FxPkgPnp* This, __inout FxIrp *Irp ) { return ((FxPkgPdo*) This)->PnpQueryResources(Irp); } _Must_inspect_result_ NTSTATUS FxPkgPdo::PnpQueryResources( __inout FxIrp *Irp ) /*++ Routine Description: This method is invoked in response to a Pnp QueryResources IRP. We return the resources that the device is currently consuming. Arguments: Irp - a pointer to the FxIrp Returns: NTSTATUS --*/ { FxCmResList *pResList = NULL; PCM_RESOURCE_LIST pWdmResourceList; WDFCMRESLIST list; NTSTATUS status; // // It is only necessary to create a collection if the caller is interested // in this callback. // if (m_DeviceResourcesQuery.m_Method == NULL) { return CompletePnpRequest(Irp, Irp->GetStatus()); } pWdmResourceList = NULL; status = FxCmResList::_CreateAndInit(&pResList, GetDriverGlobals(), m_Device, WDF_NO_OBJECT_ATTRIBUTES, FxResourceAllAccessAllowed); if (!NT_SUCCESS(status)) { goto exit; } status = pResList->Commit(NULL, (PWDFOBJECT) &list); if (NT_SUCCESS(status)) { status = m_DeviceResourcesQuery.Invoke( m_Device->GetHandle(), list); if (NT_SUCCESS(status)) { // // Walk the resource collection and create the appropriate // CM_RESOURCE_LIST. // if (pResList->Count()) { pWdmResourceList = pResList->CreateWdmList(); } else { // // The driver didn't add any resources, so we'll just // ignore this Irp. // // Return that status that was passed in. // status = Irp->GetStatus(); pWdmResourceList = (PCM_RESOURCE_LIST) Irp->GetInformation(); } } } pResList->DeleteObject(); exit: Irp->SetInformation((ULONG_PTR) pWdmResourceList); return CompletePnpRequest(Irp, status); } _Must_inspect_result_ NTSTATUS FxPkgPdo::_PnpQueryResourceRequirements( __inout FxPkgPnp* This, __inout FxIrp *Irp ) { return ((FxPkgPdo*) This)->PnpQueryResourceRequirements(Irp); } _Must_inspect_result_ NTSTATUS FxPkgPdo::PnpQueryResourceRequirements( __inout FxIrp *Irp ) /*++ Routine Description: This method is invoked in response to a Pnp QueryResourceRequirements IRP. We return the set (of sets) of possible resources that we could accept which would allow our device to work. Arguments: Irp - a pointer to the FxIrp Returns: NTSTATUS --*/ { PIO_RESOURCE_REQUIREMENTS_LIST pWdmRequirementsList; FxIoResReqList *pIoResReqList = NULL; PSINGLE_LIST_ENTRY ple; NTSTATUS status; status = STATUS_SUCCESS; m_DeviceInterfaceLock.AcquireLock(GetDriverGlobals()); // We know for sure that the PDO is known to pnp now because it has received // this query resource requirements request. m_Device->m_PdoKnown = TRUE; // // Now that it is a known PDO, we can register interfaces on it whose // registration was delayed because the PDO was unknown at the time of the // call to WdfDeviceCreateDeviceInterface. If it is not the first time // then we re-register (see comments below for reason). // for (ple = m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) { FxDeviceInterface *pDeviceInterface; pDeviceInterface = FxDeviceInterface::_FromEntry(ple); // // At this time the interface may be in registered or unregistered state. // The reason being that pnp may unregister the interface from underneath // during the life of PDO (note that drivers never unregister explicitly // as there is no unregistration API, and the scenarios in which it can // happen are given below). Therefore WDF needs to re-register the interface, // otherwise driver may end up with an unregistered interface and fail to // enable interface. // // Pnp can unregister the interface in following cases: // 1. The driver is uninstalled, and re-installed // In this case, the stack is torn down but the PDO is not // deleted. Pnp deletes the interface registration, however WDF // doesn't delete the interface structure because it is deleted as // part of PDO deletion in destructor. When the driver is re-installed // Pnp sends another query resource requirements irp and WDF needs to // re-register at this time. // // 2. Pnp couldn't find a driver and loaded a NULL driver while // waiting for reinstall to happen from WU. This is similar to case above // except that the pnp activities are transparent to user. // In this case, PDO was never started. However it did get // the query resource requirements irp and therefore its interface // was registered by WDF. Pnp deleted the interface registration // when installing NULL driver. When pnp finally finds a driver from // WU, it sends another query resource requirement irp. At this time, // WDF needs to re-register. // // In both the above cases, WDF has to re-register (and free previous // sym link) when query resource requirements irp arrives again. Note // that Pnp doesn't delete the interface registration during disable, // s/w surprise-removal, or resource rebalance. In case of resource // rebalance, query resource requirement irp is sent after stop, and // WDF will end up registering again even though pnp did not unregister // the interface. This is fine because kernel API for registration // allows multiple calls to register, and in case registration already // exists it returns informational status (not error status) // STATUS_OBJECT_NAME_EXISTS and also returns the same symbolic link. // // // Free the symbolic link if already present // if (pDeviceInterface->m_SymbolicLinkName.Buffer != NULL) { RtlFreeUnicodeString(&pDeviceInterface->m_SymbolicLinkName); RtlZeroMemory(&pDeviceInterface->m_SymbolicLinkName, sizeof(pDeviceInterface->m_SymbolicLinkName)); } // // Register. Note that if the interface was already registered, the // call to IoRegisterDeviceInterface will return informational status // (not error status) STATUS_OBJECT_NAME_EXISTS and also return the // symbolic link. // status = pDeviceInterface->Register( m_Device->GetPhysicalDevice() ); if (!NT_SUCCESS(status)) { DoTraceLevelMessage( GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, "could not register device interface on PDO WDFDEVICE %p, " "!devobj %p, failing IRP_MN_QUERY_RESOURCE_REQUIREMENTS %!STATUS!", m_Device->GetHandle(), m_Device->GetDeviceObject(), status); break; } } m_DeviceInterfaceLock.ReleaseLock(GetDriverGlobals()); if (!NT_SUCCESS(status)) { return CompletePnpRequest(Irp, status); } // // Driver writer is not interested in this callback, forgoe the allocation // of the FxCollection and complete the request here. // if (m_DeviceResourceRequirementsQuery.m_Method == NULL) { return CompletePnpRequest(Irp, status); } pWdmRequirementsList = NULL; // // Create a collection which will be populated by the // bus driver. // status = FxIoResReqList::_CreateAndInit(&pIoResReqList, GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES, FxResourceAllAccessAllowed); if (!NT_SUCCESS(status)) { goto exit; } WDFIORESREQLIST reqlist; // // Get a handle to the collection that can be passed to the driver. // status = pIoResReqList->Commit(NULL, (PWDFOBJECT) &reqlist); // // We control the object's state, this should never fail // ASSERT(NT_SUCCESS(status)); UNREFERENCED_PARAMETER(status); // // Call the driver. The driver will populate the resource collection // with a set of child collections which will contain each of the // possible resource assignments. // status = m_DeviceResourceRequirementsQuery.Invoke( m_Device->GetHandle(), reqlist); if (NT_SUCCESS(status)) { if (pIoResReqList->Count()) { // // Create a IO_RESOURCE_REQUIREMENTS_LIST based on the // contents of our collection. // pWdmRequirementsList = pIoResReqList->CreateWdmList(); if (pWdmRequirementsList != NULL) { Irp->SetInformation((ULONG_PTR) pWdmRequirementsList); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { // // The driver didn't add any resources, so we'll just // ignore this request. // // Return the status that was passed in. // status = Irp->GetStatus(); } } pIoResReqList->DeleteObject(); exit: return CompletePnpRequest(Irp, status); } _Must_inspect_result_ NTSTATUS FxPkgPdo::_PnpFilterResourceRequirements( __inout FxPkgPnp* This, __inout FxIrp *Irp ) /*++ Routine Description: Filter resource requirements for the PDO. A chance to further muck with the resources assigned to the device. Arguments: This - the package Irp - the request Return Value: NTSTATUS --*/ { NTSTATUS status; // // Give the Framework objects a pass at the list. // status = ((FxPkgPdo*) This)->FilterResourceRequirements( (PIO_RESOURCE_REQUIREMENTS_LIST*)(&Irp->GetIrp()->IoStatus.Information) ); if (NT_SUCCESS(status)) { // // Upon successful internal filtering, return the embedded status. // status = Irp->GetStatus(); } else { // // Only on failure do we change the status of the irp // Irp->SetStatus(status); } return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, status); }