1318 lines
32 KiB
C
1318 lines
32 KiB
C
/*++
|
|
|
|
Copyright (c) 1997-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fdo.c
|
|
|
|
Abstract:
|
|
|
|
This module provides the functions which answer IRPs to functional devices.
|
|
|
|
Author:
|
|
|
|
Andy Thornton (andrewth) 20-Oct-97
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "mfp.h"
|
|
|
|
/*++
|
|
|
|
The majority of functions in this file are called based on their presence
|
|
in Pnp and Po dispatch tables. In the interests of brevity the arguments
|
|
to all those functions will be described below:
|
|
|
|
NTSTATUS
|
|
MfXxxFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
|
|
Routine Description:
|
|
|
|
This function handles the Xxx requests for multifunction FDO's
|
|
|
|
Arguments:
|
|
|
|
Irp - Points to the IRP associated with this request.
|
|
|
|
Parent - Points to the parent FDO's device extension.
|
|
|
|
IrpStack - Points to the current stack location for this request.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
STATUS_NOT_SUPPORTED indicates that the IRP should be passed down without
|
|
changing the Irp->IoStatus.Status field otherwise it is updated with this
|
|
status.
|
|
|
|
--*/
|
|
|
|
|
|
NTSTATUS
|
|
MfDeferProcessingFdo(
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN OUT PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
MfStartFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfStartFdoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
MfStartFdoInitializeArbiters(
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PCM_RESOURCE_LIST ResList,
|
|
IN PCM_RESOURCE_LIST TranslatedResList
|
|
);
|
|
|
|
NTSTATUS
|
|
MfQueryStopFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfCancelStopFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfQueryRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfSurpriseRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfCancelRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfQueryDeviceRelationsFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfQueryInterfaceFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfQueryCapabilitiesFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfQueryPowerFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfSetPowerFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
NTSTATUS
|
|
MfPassIrp(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, MfCancelRemoveFdo)
|
|
#pragma alloc_text(PAGE, MfCancelStopFdo)
|
|
#pragma alloc_text(PAGE, MfCreateFdo)
|
|
#pragma alloc_text(PAGE, MfDeferProcessingFdo)
|
|
#pragma alloc_text(PAGE, MfDispatchPnpFdo)
|
|
#pragma alloc_text(PAGE, MfPassIrp)
|
|
#pragma alloc_text(PAGE, MfQueryCapabilitiesFdo)
|
|
#pragma alloc_text(PAGE, MfQueryDeviceRelationsFdo)
|
|
#pragma alloc_text(PAGE, MfQueryInterfaceFdo)
|
|
#pragma alloc_text(PAGE, MfQueryRemoveFdo)
|
|
#pragma alloc_text(PAGE, MfQueryStopFdo)
|
|
#pragma alloc_text(PAGE, MfRemoveFdo)
|
|
#pragma alloc_text(PAGE, MfStartFdo)
|
|
#pragma alloc_text(PAGE, MfStartFdoInitializeArbiters)
|
|
#pragma alloc_text(PAGE, MfSurpriseRemoveFdo)
|
|
#endif
|
|
|
|
|
|
PMF_DISPATCH MfPnpDispatchTableFdo[] = {
|
|
|
|
MfStartFdo, // IRP_MN_START_DEVICE
|
|
MfQueryRemoveFdo, // IRP_MN_QUERY_REMOVE_DEVICE
|
|
MfRemoveFdo, // IRP_MN_REMOVE_DEVICE
|
|
MfCancelRemoveFdo, // IRP_MN_CANCEL_REMOVE_DEVICE
|
|
MfPassIrp, // IRP_MN_STOP_DEVICE
|
|
MfQueryStopFdo, // IRP_MN_QUERY_STOP_DEVICE
|
|
MfCancelStopFdo, // IRP_MN_CANCEL_STOP_DEVICE
|
|
MfQueryDeviceRelationsFdo, // IRP_MN_QUERY_DEVICE_RELATIONS
|
|
MfQueryInterfaceFdo, // IRP_MN_QUERY_INTERFACE
|
|
MfQueryCapabilitiesFdo, // IRP_MN_QUERY_CAPABILITIES
|
|
MfPassIrp, // IRP_MN_QUERY_RESOURCES
|
|
MfPassIrp, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS
|
|
MfPassIrp, // IRP_MN_QUERY_DEVICE_TEXT
|
|
MfPassIrp, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS
|
|
MfPassIrp, // Unused
|
|
MfPassIrp, // IRP_MN_READ_CONFIG
|
|
MfPassIrp, // IRP_MN_WRITE_CONFIG
|
|
MfPassIrp, // IRP_MN_EJECT
|
|
MfPassIrp, // IRP_MN_SET_LOCK
|
|
MfPassIrp, // IRP_MN_QUERY_ID
|
|
MfPassIrp, // IRP_MN_QUERY_PNP_DEVICE_STATE
|
|
MfPassIrp, // IRP_MN_QUERY_BUS_INFORMATION
|
|
MfDeviceUsageNotificationCommon,// IRP_MN_DEVICE_USAGE_NOTIFICATION
|
|
MfSurpriseRemoveFdo, // IRP_MN_SURPRISE_REMOVAL
|
|
MfPassIrp // IRP_MN_QUERY_LEGACY_BUS_INFORMATION
|
|
};
|
|
|
|
PMF_DISPATCH MfPoDispatchTableFdo[] = {
|
|
|
|
NULL, // IRP_MN_WAIT_WAKE
|
|
NULL, // IRP_MN_POWER_SEQUENCE
|
|
MfSetPowerFdo, // IRP_MN_SET_POWER
|
|
MfQueryPowerFdo // IRP_MN_QUERY_POWER
|
|
|
|
};
|
|
|
|
|
|
NTSTATUS
|
|
MfCreateFdo(
|
|
OUT PDEVICE_OBJECT *Fdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a new FDO and initializes it.
|
|
|
|
Arguments:
|
|
|
|
Fdo - Pointer to where the FDO should be returned
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PMF_PARENT_EXTENSION extension;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT((sizeof(MfPnpDispatchTableFdo) / sizeof(PMF_DISPATCH)) - 1
|
|
== IRP_MN_PNP_MAXIMUM_FUNCTION);
|
|
|
|
ASSERT((sizeof(MfPoDispatchTableFdo) / sizeof(PMF_DISPATCH)) -1
|
|
== IRP_MN_PO_MAXIMUM_FUNCTION);
|
|
|
|
*Fdo = NULL;
|
|
|
|
status = IoCreateDevice(MfDriverObject,
|
|
sizeof(MF_PARENT_EXTENSION),
|
|
NULL,
|
|
FILE_DEVICE_BUS_EXTENDER,
|
|
0,
|
|
FALSE,
|
|
Fdo
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize the extension
|
|
//
|
|
|
|
extension = (PMF_PARENT_EXTENSION) (*Fdo)->DeviceExtension;
|
|
|
|
MfInitCommonExtension(&extension->Common, MfFunctionalDeviceObject);
|
|
extension->Self = *Fdo;
|
|
|
|
InitializeListHead(&extension->Arbiters);
|
|
|
|
InitializeListHead(&extension->Children);
|
|
KeInitializeEvent(&extension->ChildrenLock, SynchronizationEvent, TRUE);
|
|
|
|
KeInitializeSpinLock(&extension->PowerLock);
|
|
|
|
IoInitializeRemoveLock(&extension->RemoveLock, MF_POOL_TAG, 1, 20);
|
|
|
|
extension->Common.PowerState = PowerDeviceD3;
|
|
|
|
DEBUG_MSG(1, ("Created FDO @ 0x%08x\n", *Fdo));
|
|
|
|
return status;
|
|
|
|
cleanup:
|
|
|
|
if (*Fdo) {
|
|
IoDeleteDevice(*Fdo);
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
VOID
|
|
MfAcquireChildrenLock(
|
|
IN PMF_PARENT_EXTENSION Parent
|
|
)
|
|
{
|
|
KeWaitForSingleObject(&Parent->ChildrenLock,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
VOID
|
|
MfReleaseChildrenLock(
|
|
IN PMF_PARENT_EXTENSION Parent
|
|
)
|
|
{
|
|
KeSetEvent(&Parent->ChildrenLock, 0, FALSE);
|
|
}
|
|
|
|
VOID
|
|
MfDeleteFdo(
|
|
IN PDEVICE_OBJECT Fdo
|
|
)
|
|
{
|
|
PMF_PARENT_EXTENSION parent = Fdo->DeviceExtension;
|
|
PMF_ARBITER current, next;
|
|
|
|
if (parent->Common.DeviceState & MF_DEVICE_DELETED) {
|
|
//
|
|
// Trying to delete twice
|
|
//
|
|
ASSERT(!(parent->Common.DeviceState & MF_DEVICE_DELETED));
|
|
return;
|
|
}
|
|
|
|
parent->Common.DeviceState = MF_DEVICE_DELETED;
|
|
|
|
//
|
|
// Free up any memory we have allocated
|
|
//
|
|
|
|
if (parent->ResourceList) {
|
|
ExFreePool(parent->ResourceList);
|
|
}
|
|
|
|
if (parent->TranslatedResourceList) {
|
|
ExFreePool(parent->TranslatedResourceList);
|
|
}
|
|
|
|
if (parent->DeviceID.Buffer) {
|
|
ExFreePool(parent->DeviceID.Buffer);
|
|
}
|
|
|
|
if (parent->InstanceID.Buffer) {
|
|
ExFreePool(parent->InstanceID.Buffer);
|
|
}
|
|
|
|
FOR_ALL_IN_LIST_SAFE(MF_ARBITER, &parent->Arbiters, current, next) {
|
|
ArbDeleteArbiterInstance(¤t->Instance);
|
|
ExFreePool(current);
|
|
}
|
|
|
|
ASSERT(IsListEmpty(&parent->Children));
|
|
|
|
IoDeleteDevice(Fdo);
|
|
|
|
DEBUG_MSG(1, ("Deleted FDO @ 0x%08x\n", Fdo));
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
MfPassIrp(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
return IoCallDriver(Parent->AttachedDevice, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
MfDispatchPnpFdo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles IRP_MJ_PNP IRPs for FDOs.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the FDO for which this IRP applies.
|
|
|
|
Parent - FDO extension
|
|
|
|
IrpStack - Current stack location
|
|
|
|
Irp - Pointer to the IRP_MJ_PNP IRP to dispatch.
|
|
|
|
Return Value:
|
|
|
|
NT status.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN isRemoveDevice;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a pointer to our stack location and take appropriate action based
|
|
// on the minor function.
|
|
//
|
|
|
|
IoAcquireRemoveLock(&Parent->RemoveLock, (PVOID) Irp);
|
|
|
|
isRemoveDevice = IrpStack->MinorFunction == IRP_MN_REMOVE_DEVICE;
|
|
|
|
if (IrpStack->MinorFunction > IRP_MN_PNP_MAXIMUM_FUNCTION) {
|
|
|
|
status = MfPassIrp(Irp, Parent, IrpStack);
|
|
|
|
} else {
|
|
|
|
status =
|
|
MfPnpDispatchTableFdo[IrpStack->MinorFunction](Irp,
|
|
Parent,
|
|
IrpStack
|
|
);
|
|
}
|
|
|
|
if (!isRemoveDevice) {
|
|
IoReleaseRemoveLock(&Parent->RemoveLock, (PVOID) Irp);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfPnPFdoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to defer processing of an IRP until drivers
|
|
lower in the stack including the bus driver have done their
|
|
processing.
|
|
|
|
This routine triggers the event to indicate that processing of the
|
|
irp can now continue.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the FDO for which this IRP applies.
|
|
|
|
Irp - Pointer to the IRP_MJ_PNP IRP to dispatch.
|
|
|
|
Return Value:
|
|
|
|
NT status.
|
|
|
|
--*/
|
|
|
|
{
|
|
KeSetEvent((PKEVENT) Context, EVENT_INCREMENT, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfDeferProcessingFdo(
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to defer processing of an IRP until drivers
|
|
lower in the stack including the bus driver have done their
|
|
processing.
|
|
|
|
This routine uses an IoCompletion routine along with an event to
|
|
wait until the lower level drivers have completed processing of
|
|
the irp.
|
|
|
|
Arguments:
|
|
|
|
Parent - FDO extension for the FDO devobj in question
|
|
|
|
Irp - Pointer to the IRP_MJ_PNP IRP to defer
|
|
|
|
Return Value:
|
|
|
|
NT status.
|
|
|
|
--*/
|
|
{
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Set our completion routine
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp,
|
|
MfPnPFdoCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
status = IoCallDriver(Parent->AttachedDevice, Irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfStartFdoInitializeArbiters(
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PCM_RESOURCE_LIST ResList,
|
|
IN PCM_RESOURCE_LIST TranslatedResList
|
|
)
|
|
{
|
|
|
|
NTSTATUS status;
|
|
ULONG size;
|
|
ULONG count;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_MSG(1, ("Start Fdo arbiters intiialization\n"));
|
|
|
|
//
|
|
// If we were started with any resources then remember them
|
|
//
|
|
|
|
if (ResList && TranslatedResList) {
|
|
|
|
#if DBG
|
|
MfDbgPrintCmResList(1, ResList);
|
|
#endif
|
|
|
|
//
|
|
// We only deal with resources on a single bus - which is all we
|
|
// should see in a start irp.
|
|
//
|
|
|
|
ASSERT(ResList->Count == 1);
|
|
ASSERT(TranslatedResList->Count == 1);
|
|
|
|
//
|
|
// Both lists should have the same number of descriptors
|
|
//
|
|
|
|
ASSERT(ResList->List[0].PartialResourceList.Count == TranslatedResList->List[0].PartialResourceList.Count);
|
|
|
|
//
|
|
// Calculate the size of the resouceList
|
|
//
|
|
|
|
size = sizeof(CM_RESOURCE_LIST) +
|
|
((ResList->List[0].PartialResourceList.Count - 1) *
|
|
sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
|
|
|
|
//
|
|
// Allocate a buffer and copy the data
|
|
//
|
|
|
|
Parent->ResourceList = ExAllocatePoolWithTag(NonPagedPool,
|
|
size,
|
|
MF_PARENTS_RESOURCE_TAG
|
|
);
|
|
|
|
if (!Parent->ResourceList) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(Parent->ResourceList, ResList, size);
|
|
|
|
//
|
|
// do the same for the TranslatedResList.
|
|
//
|
|
|
|
Parent->TranslatedResourceList = ExAllocatePoolWithTag(NonPagedPool,
|
|
size,
|
|
MF_PARENTS_RESOURCE_TAG
|
|
);
|
|
|
|
if (!Parent->TranslatedResourceList) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(Parent->TranslatedResourceList, TranslatedResList, size);
|
|
|
|
//
|
|
// As we have resources we are going to need some arbiters
|
|
//
|
|
|
|
status = MfInitializeArbiters(Parent);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
DEBUG_MSG(1, ("Parent started with no resources\n"));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (Parent->ResourceList) {
|
|
ExFreePool(Parent->ResourceList);
|
|
Parent->ResourceList = NULL;
|
|
}
|
|
|
|
if (Parent->TranslatedResourceList) {
|
|
ExFreePool(Parent->TranslatedResourceList);
|
|
Parent->TranslatedResourceList = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
// REBALANCE
|
|
//
|
|
// FUTURE DESIGN NOTE:
|
|
// If rebalance was actually supported by this component i.e arbiters
|
|
// become stoppable, then there are various issues raised by the start
|
|
// code. It performs a number of operations assuming that the device
|
|
// has never been started before including the query ids, resource
|
|
// list storage, etc. There are also issues in redistributing these
|
|
// new resources to the children. The current requirements given to
|
|
// the children are absolute. Relative requirements have some other
|
|
// issues.
|
|
//
|
|
|
|
NTSTATUS
|
|
MfStartFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
IO_STACK_LOCATION location;
|
|
PWSTR string;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = MfDeferProcessingFdo(Parent, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
Parent->Common.PowerState = PowerDeviceD0;
|
|
|
|
//
|
|
// We need to find out some information about our parent
|
|
//
|
|
|
|
Parent->DeviceID.Buffer = NULL;
|
|
Parent->InstanceID.Buffer = NULL;
|
|
|
|
RtlZeroMemory(&location, sizeof(IO_STACK_LOCATION));
|
|
location.MajorFunction = IRP_MJ_PNP;
|
|
location.MinorFunction = IRP_MN_QUERY_ID;
|
|
|
|
//
|
|
// ...DeviceID...
|
|
//
|
|
|
|
location.Parameters.QueryId.IdType = BusQueryDeviceID;
|
|
|
|
status = MfSendPnpIrp(Parent->PhysicalDeviceObject,
|
|
&location,
|
|
(PULONG_PTR)&string
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(&Parent->DeviceID, string);
|
|
|
|
DEBUG_MSG(1, ("Parent DeviceID: %wZ\n", &Parent->DeviceID));
|
|
|
|
//
|
|
// ...InstanceID
|
|
//
|
|
|
|
location.Parameters.QueryId.IdType = BusQueryInstanceID;
|
|
|
|
status = MfSendPnpIrp(Parent->PhysicalDeviceObject,
|
|
&location,
|
|
(PULONG_PTR)&string
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(&Parent->InstanceID, string);
|
|
|
|
DEBUG_MSG(1, ("Parent InstanceID: %wZ\n", &Parent->InstanceID));
|
|
|
|
status = MfStartFdoInitializeArbiters(
|
|
Parent,
|
|
IrpStack->Parameters.StartDevice.AllocatedResources,
|
|
IrpStack->Parameters.StartDevice.AllocatedResourcesTranslated
|
|
);
|
|
|
|
cleanup:
|
|
|
|
Irp->IoStatus.Status = status;
|
|
if (!NT_SUCCESS(status)) {
|
|
if (Parent->DeviceID.Buffer) {
|
|
ExFreePool(Parent->DeviceID.Buffer);
|
|
Parent->DeviceID.Buffer = NULL;
|
|
}
|
|
|
|
if (Parent->InstanceID.Buffer) {
|
|
ExFreePool(Parent->InstanceID.Buffer);
|
|
Parent->InstanceID.Buffer = NULL;
|
|
}
|
|
} else {
|
|
//
|
|
// We are now started!
|
|
//
|
|
}
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfQueryStopFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfCancelStopFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = MfDeferProcessingFdo(Parent, Irp);
|
|
// NTRAID#53498
|
|
// ASSERT(status == STATUS_SUCCESS);
|
|
// Uncomment after PCI state machine is fixed to not fail bogus stops
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfQueryRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
return MfPassIrp(Irp, Parent, IrpStack);
|
|
}
|
|
|
|
NTSTATUS
|
|
MfRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
PMF_CHILD_EXTENSION current;
|
|
PLIST_ENTRY currentEntry;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// If we have any children then make sure they are removed and
|
|
// delete them.
|
|
//
|
|
|
|
MfAcquireChildrenLock(Parent);
|
|
|
|
while (!IsListEmpty(&Parent->Children)) {
|
|
|
|
currentEntry = RemoveHeadList(&Parent->Children);
|
|
ASSERT(currentEntry);
|
|
|
|
current = CONTAINING_RECORD(currentEntry, MF_CHILD_EXTENSION,
|
|
ListEntry);
|
|
|
|
//
|
|
// * If this child has been surprise removed, and hasn't
|
|
// received the subsequent remove, then leave
|
|
// the PDO intact but mark it 'missing'.
|
|
//
|
|
// * If this child has handled a previous remove (this is
|
|
// fundamentally the case if we've gotten to the point of
|
|
// removing the parent) and hasn't subsequently received a
|
|
// surprise remove, then delete the pdo.
|
|
//
|
|
|
|
if (current->Common.DeviceState & MF_DEVICE_SURPRISE_REMOVED) {
|
|
//
|
|
// Mark as 'missing' and unlink dangerous reference to parent
|
|
//
|
|
|
|
current->Parent = NULL;
|
|
current->Common.DeviceState &= ~MF_DEVICE_ENUMERATED;
|
|
} else {
|
|
MfDeletePdo(current);
|
|
}
|
|
}
|
|
|
|
MfReleaseChildrenLock(Parent);
|
|
|
|
Parent->Common.PowerState = PowerDeviceD3;
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
status = MfPassIrp(Irp, Parent, IrpStack);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
IoReleaseRemoveLockAndWait(&Parent->RemoveLock, (PVOID) Irp);
|
|
|
|
//
|
|
// Detach and delete myself
|
|
//
|
|
|
|
IoDetachDevice(Parent->AttachedDevice);
|
|
Parent->AttachedDevice = NULL;
|
|
|
|
MfDeleteFdo(Parent->Self);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfSurpriseRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
PLIST_ENTRY currentEntry;
|
|
PMF_CHILD_EXTENSION current;
|
|
|
|
PAGED_CODE();
|
|
|
|
Parent->Common.DeviceState |= MF_DEVICE_SURPRISE_REMOVED;
|
|
|
|
MfAcquireChildrenLock(Parent);
|
|
|
|
for (currentEntry = Parent->Children.Flink;
|
|
currentEntry != &Parent->Children;
|
|
currentEntry = currentEntry->Flink) {
|
|
|
|
current = CONTAINING_RECORD(currentEntry,
|
|
MF_CHILD_EXTENSION,
|
|
ListEntry);
|
|
current->Common.DeviceState &= ~MF_DEVICE_ENUMERATED;
|
|
}
|
|
|
|
MfReleaseChildrenLock(Parent);
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
return MfPassIrp(Irp, Parent, IrpStack);
|
|
}
|
|
|
|
NTSTATUS
|
|
MfCancelRemoveFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = MfDeferProcessingFdo(Parent, Irp);
|
|
// NTRAID#53498
|
|
// ASSERT(status == STATUS_SUCCESS);
|
|
// Uncomment after PCI state machine is fixed to not fail bogus stops
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MfQueryDeviceRelationsFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PDEVICE_RELATIONS relations = NULL;
|
|
ULONG relationsSize, childrenCount, i;
|
|
PDEVICE_OBJECT *currentRelation;
|
|
PMF_CHILD_EXTENSION currentChild;
|
|
PLIST_ENTRY currentEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_MSG(1,
|
|
("%s\n",
|
|
RELATION_STRING(IrpStack->Parameters.QueryDeviceRelations.Type)
|
|
));
|
|
|
|
switch (IrpStack->Parameters.QueryDeviceRelations.Type) {
|
|
|
|
case BusRelations:
|
|
|
|
MfAcquireChildrenLock(Parent);
|
|
|
|
status = MfEnumerate(Parent);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
MfReleaseChildrenLock(Parent);
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
childrenCount = 0;
|
|
for (currentEntry = Parent->Children.Flink;
|
|
currentEntry != &Parent->Children;
|
|
currentEntry = currentEntry->Flink) {
|
|
|
|
childrenCount++;
|
|
}
|
|
|
|
if (childrenCount == 0) {
|
|
relationsSize = sizeof(DEVICE_RELATIONS);
|
|
} else {
|
|
relationsSize = sizeof(DEVICE_RELATIONS) +
|
|
(childrenCount-1) * sizeof(PDEVICE_OBJECT);
|
|
}
|
|
|
|
relations = ExAllocatePoolWithTag(PagedPool,
|
|
relationsSize,
|
|
MF_BUS_RELATIONS_TAG
|
|
);
|
|
|
|
if (!relations) {
|
|
MfReleaseChildrenLock(Parent);
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(relations, relationsSize);
|
|
|
|
//
|
|
// Iterate through the list of children in the parent and build the
|
|
// relations structure
|
|
//
|
|
|
|
currentRelation = relations->Objects;
|
|
relations->Count = childrenCount;
|
|
|
|
for (currentEntry = Parent->Children.Flink;
|
|
currentEntry != &Parent->Children;
|
|
currentEntry = currentEntry->Flink) {
|
|
|
|
currentChild = CONTAINING_RECORD(currentEntry,
|
|
MF_CHILD_EXTENSION,
|
|
ListEntry);
|
|
|
|
currentChild->Common.DeviceState |= MF_DEVICE_ENUMERATED;
|
|
ObReferenceObject(currentChild->Self);
|
|
*currentRelation = currentChild->Self;
|
|
#if DBG
|
|
DEBUG_MSG(1, ("\tPDO Enumerated @ 0x%08x\n", currentChild));
|
|
DEBUG_MSG(1, ("\tName: %wZ\n", ¤tChild->Info.Name));
|
|
DEBUG_MSG(1, ("\tHardwareID: "));
|
|
MfDbgPrintMultiSz(1, currentChild->Info.HardwareID.Buffer);
|
|
|
|
DEBUG_MSG(1, ("\tCompatibleID: "));
|
|
MfDbgPrintMultiSz(1, currentChild->Info.CompatibleID.Buffer);
|
|
|
|
DEBUG_MSG(1, ("\tResourceMap: "));
|
|
MfDbgPrintResourceMap(1, currentChild->Info.ResourceMap);
|
|
|
|
DEBUG_MSG(1, ("\tVaryingMap: "));
|
|
MfDbgPrintVaryingResourceMap(1, currentChild->Info.VaryingResourceMap);
|
|
DEBUG_MSG(1, ("\tFlags: 0x%08x\n", currentChild->Info.MfFlags));
|
|
|
|
#endif
|
|
currentRelation++;
|
|
}
|
|
|
|
MfReleaseChildrenLock(Parent);
|
|
|
|
//
|
|
// Hand back the relations
|
|
//
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR) relations;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
//
|
|
// For the rest of the relations just pass down the irp untouched.
|
|
//
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return MfPassIrp(Irp, Parent, IrpStack);
|
|
}
|
|
|
|
VOID
|
|
MfArbiterReference(
|
|
PVOID Context
|
|
)
|
|
{
|
|
}
|
|
|
|
VOID
|
|
MfArbiterDereference(
|
|
PVOID Context
|
|
)
|
|
{
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MfQueryInterfaceFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
PMF_ARBITER current;
|
|
PARBITER_INTERFACE interface = (PARBITER_INTERFACE) IrpStack->Parameters.QueryInterface.Interface;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// We only provide arbiters
|
|
//
|
|
|
|
if (MfCompareGuid(&GUID_ARBITER_INTERFACE_STANDARD,
|
|
IrpStack->Parameters.QueryInterface.InterfaceType)) {
|
|
|
|
//
|
|
// We only support version 1 of the ARBITER_INTERFACE so we
|
|
// don't need to bother checking version numbers, just that the
|
|
// return buffer is big enough
|
|
//
|
|
|
|
if (IrpStack->Parameters.QueryInterface.Size < sizeof(ARBITER_INTERFACE)) {
|
|
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
FOR_ALL_IN_LIST(MF_ARBITER, &Parent->Arbiters, current) {
|
|
|
|
if (current->Type == (CM_RESOURCE_TYPE)((ULONG_PTR)
|
|
IrpStack->Parameters.QueryInterface.InterfaceSpecificData)) {
|
|
|
|
DEBUG_MSG(1,(" Returning Arbiter interface\n"));
|
|
|
|
//
|
|
// Fill in the interface
|
|
//
|
|
|
|
interface->Size = sizeof(ARBITER_INTERFACE);
|
|
interface->Version = MF_ARBITER_INTERFACE_VERSION;
|
|
interface->Context = ¤t->Instance;
|
|
interface->InterfaceReference = MfArbiterReference;
|
|
interface->InterfaceDereference = MfArbiterDereference;
|
|
interface->ArbiterHandler = ArbArbiterHandler;
|
|
interface->Flags = 0;
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return MfPassIrp(Irp, Parent, IrpStack);
|
|
}
|
|
|
|
NTSTATUS
|
|
MfQueryCapabilitiesFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = MfDeferProcessingFdo(Parent, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
if (IrpStack->Parameters.DeviceCapabilities.Capabilities->Version != 1) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
IrpStack->Parameters.DeviceCapabilities.Capabilities->WakeFromD0 =
|
|
IrpStack->Parameters.DeviceCapabilities.Capabilities->WakeFromD1 =
|
|
IrpStack->Parameters.DeviceCapabilities.Capabilities->WakeFromD2 =
|
|
IrpStack->Parameters.DeviceCapabilities.Capabilities->WakeFromD3 = 0;
|
|
|
|
IrpStack->Parameters.DeviceCapabilities.Capabilities->DeviceWake =
|
|
PowerSystemUnspecified;
|
|
IrpStack->Parameters.DeviceCapabilities.Capabilities->SystemWake =
|
|
PowerSystemUnspecified;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfDispatchPowerFdo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
PMF_PARENT_EXTENSION Parent,
|
|
PIO_STACK_LOCATION IrpStack,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles all IRP_MJ_POWER IRPs for the FDO. It dispatches
|
|
to the routines described in the PoDispatchTable entry in the device object
|
|
extension.
|
|
|
|
This routine is NOT pageable as it can be called at DISPATCH_LEVEL
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for which this IRP applies.
|
|
|
|
Parent - FDO Extension
|
|
|
|
Irp - Pointer to the IRP_MJ_PNP IRP to dispatch.
|
|
|
|
Return Value:
|
|
|
|
NT status.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PMF_COMMON_EXTENSION common = (PMF_COMMON_EXTENSION) Parent;
|
|
|
|
IoAcquireRemoveLock(&Parent->RemoveLock, (PVOID) Irp);
|
|
|
|
//
|
|
// Call the appropriate function
|
|
//
|
|
|
|
if ((IrpStack->MinorFunction <= IRP_MN_PO_MAXIMUM_FUNCTION) &&
|
|
(MfPoDispatchTableFdo[IrpStack->MinorFunction])) {
|
|
|
|
status =
|
|
MfPoDispatchTableFdo[IrpStack->MinorFunction](Irp,
|
|
(PVOID) common,
|
|
IrpStack
|
|
);
|
|
|
|
} else {
|
|
//
|
|
// We don't know about this irp
|
|
//
|
|
|
|
DEBUG_MSG(0,
|
|
("Unknown POWER IRP 0x%x for FDO 0x%08x\n",
|
|
IrpStack->MinorFunction,
|
|
DeviceObject
|
|
));
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = PoCallDriver(Parent->AttachedDevice, Irp);
|
|
}
|
|
|
|
IoReleaseRemoveLock(&Parent->RemoveLock, (PVOID) Irp);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfQueryPowerFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
return PoCallDriver(Parent->AttachedDevice, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
MfSetPowerFdoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PMF_PARENT_EXTENSION parent = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Remember the parent's power state
|
|
//
|
|
|
|
if (irpStack->Parameters.Power.Type == DevicePowerState) {
|
|
parent->Common.PowerState =
|
|
irpStack->Parameters.Power.State.DeviceState;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MfSetPowerFdo(
|
|
IN PIRP Irp,
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
PoStartNextPowerIrp(Irp);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp,
|
|
MfSetPowerFdoCompletion,
|
|
NULL, //Context
|
|
TRUE, //InvokeOnSuccess
|
|
FALSE, //InvokeOnError
|
|
FALSE //InvokeOnCancel
|
|
);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
return PoCallDriver(Parent->AttachedDevice, Irp);
|
|
}
|
|
|
|
|