windows-nt/Source/XPSP1/NT/base/basedrv/pnpmem/pnp.c
2020-09-26 16:20:57 +08:00

721 lines
16 KiB
C

/*++
Copyright (c) 1999-2001 Microsoft Corporation
Module Name:
pnp.c
Abstract:
This module implements the IRP_MJ_PNP IRP processing routines for the
Plug and Play Memory driver. Dispatch routines are invoked through
tables located at the bottom of the module.
Author:
Dave Richards (daveri) 16-Aug-1999
Environment:
Kernel mode only.
Revision History:
--*/
#include "pnpmem.h"
NTSTATUS
PmPassIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmPnpDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmDeferProcessing(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
);
NTSTATUS
PmStartDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmQueryRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmCancelRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmQueryStopDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmCancelStopDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmQueryCapabilities(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
PmSurpriseRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PmPassIrp)
#pragma alloc_text(PAGE, PmPnpDispatch)
#pragma alloc_text(PAGE, PmDeferProcessing)
#pragma alloc_text(PAGE, PmStartDevice)
#pragma alloc_text(PAGE, PmQueryRemoveDevice)
#pragma alloc_text(PAGE, PmRemoveDevice)
#pragma alloc_text(PAGE, PmCancelRemoveDevice)
#pragma alloc_text(PAGE, PmQueryStopDevice)
#pragma alloc_text(PAGE, PmCancelStopDevice)
#pragma alloc_text(PAGE, PmSurpriseRemoveDevice)
#pragma alloc_text(PAGE, PmQueryCapabilities)
#endif
PDRIVER_DISPATCH PmPnpDispatchTable[];
extern ULONG PmPnpDispatchTableSize;
NTSTATUS
PmPassIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PPM_DEVICE_EXTENSION deviceExtension;
PAGED_CODE();
deviceExtension = DeviceObject->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(deviceExtension->AttachedDevice, Irp);
}
NTSTATUS
PmPnpDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles IRP_MJ_PNP IRPs for FDOs.
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.
--*/
{
NTSTATUS status;
BOOLEAN isRemoveDevice;
PIO_STACK_LOCATION irpSp;
PPM_DEVICE_EXTENSION deviceExtension;
PAGED_CODE();
//
// Get a pointer to our stack location and take appropriate action based
// on the minor function.
//
irpSp = IoGetCurrentIrpStackLocation(Irp);
deviceExtension = DeviceObject->DeviceExtension;
IoAcquireRemoveLock(&deviceExtension->RemoveLock, (PVOID) Irp);
isRemoveDevice = irpSp->MinorFunction == IRP_MN_REMOVE_DEVICE;
if ((irpSp->MinorFunction < PmPnpDispatchTableSize) &&
PmPnpDispatchTable[irpSp->MinorFunction]) {
status =
PmPnpDispatchTable[irpSp->MinorFunction](DeviceObject,
Irp
);
} else {
status = PmPassIrp(DeviceObject, Irp);
}
if (!isRemoveDevice) {
IoReleaseRemoveLock(&deviceExtension->RemoveLock, (PVOID) Irp);
}
return status;
}
NTSTATUS
PmPnpCompletion(
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
PmDeferProcessing(
IN PDEVICE_OBJECT DeviceObject,
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.
--*/
{
PPM_DEVICE_EXTENSION deviceExtension;
KEVENT event;
NTSTATUS status;
PAGED_CODE();
deviceExtension = DeviceObject->DeviceExtension;
KeInitializeEvent(&event, NotificationEvent, FALSE);
//
// Set our completion routine
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
PmPnpCompletion,
&event,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(deviceExtension->AttachedDevice, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
}
return status;
}
NTSTATUS
PmStartDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function handles IRP_MN_START_DEVICE IRPs.
Arguments:
DeviceObject - The functional device object.
Irp - The I/O request packet.
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION irpSp;
PPM_RANGE_LIST RangeList;
PPM_DEVICE_EXTENSION deviceExtension;
POWER_STATE power;
NTSTATUS status;
PAGED_CODE();
status = PmDeferProcessing(DeviceObject, Irp);
if (!NT_SUCCESS(status)) {
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
PmDumpOsMemoryRanges(L"Before Start");
irpSp = IoGetCurrentIrpStackLocation(Irp);
deviceExtension = DeviceObject->DeviceExtension;
ASSERT(deviceExtension->RangeList == NULL);
if (irpSp->Parameters.StartDevice.AllocatedResources) {
RangeList = PmCreateRangeListFromCmResourceList(
irpSp->Parameters.StartDevice.AllocatedResources
);
if (RangeList == NULL) {
//
// The memory allocation failure here is more serious than
// is intially obvious. If we fail this allocation, we're
// going to get removed from the stack before we find out
// if the OS knows about this memory. If the OS knows
// about this memory already, then ejecting the PDO would
// cause the memory underneath the OS to disappear.
// Better to be on the stack, but not have added any
// memory then to be off the stack and leave a dangerous
// situation.
//
// Only solution is to arbitrarily fail
// IRP_MN_QUERY_REMOVE.
//
deviceExtension->FailQueryRemoves = TRUE;
}
} else {
RangeList = NULL;
}
PmTrimReservedMemory(deviceExtension, &RangeList);
PmDebugDumpRangeList(PNPMEM_MEMORY, "Memory Ranges to be added:\n",
RangeList);
if (deviceExtension->FailQueryRemoves) {
PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY, "PNPMEM device can't be removed\n"));
}
if (RangeList != NULL) {
(VOID) PmAddPhysicalMemory(DeviceObject, RangeList);
deviceExtension->RangeList = RangeList;
}
power.DeviceState = PowerDeviceD0;
PoSetPowerState(DeviceObject, DevicePowerState, power);
deviceExtension->PowerState = PowerDeviceD0;
PmDumpOsMemoryRanges(L"After Start ");
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
PmQueryRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function handles IRP_MN_QUERY_REMOVE_DEVICE IRPs.
Arguments:
DeviceObject - The functional device object.
Irp - The I/O request packet.
Return Value:
NTSTATUS
--*/
{
PPM_DEVICE_EXTENSION deviceExtension;
NTSTATUS status;
PAGED_CODE();
deviceExtension = DeviceObject->DeviceExtension;
if (!MemoryRemovalSupported) {
PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY,
"QueryRemove vetoed because memory removal is not supported\n"));
status = STATUS_UNSUCCESSFUL;
} else if (deviceExtension->FailQueryRemoves) {
PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY,
"QueryRemove vetoed because removing this memory device may contain special memory\n"));
status = STATUS_UNSUCCESSFUL;
} else if (deviceExtension->RangeList != NULL) {
status = PmRemovePhysicalMemory(deviceExtension->RangeList);
if (!NT_SUCCESS(status)) {
//
// Some ranges may have been removed, before failure. Add
// them back. Should be low-cost due to optimizations in
// PmAddPhysicalMemory.
//
(VOID) PmAddPhysicalMemory(DeviceObject, deviceExtension->RangeList);
}
} else {
status = STATUS_SUCCESS;
}
Irp->IoStatus.Status = status;
if (NT_SUCCESS(status)) {
return PmPassIrp(DeviceObject, Irp);
} else {
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
}
NTSTATUS
PmRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function handles IRP_MN_REMOVE_DEVICE IRPs.
Arguments:
DeviceObject - The functional device object.
Irp - The I/O request packet.
Return Value:
NTSTATUS
--*/
{
PPM_DEVICE_EXTENSION deviceExtension;
PPM_RANGE_LIST RangeList;
POWER_STATE power;
NTSTATUS status;
PAGED_CODE();
deviceExtension = DeviceObject->DeviceExtension;
if ((deviceExtension->Flags & DF_SURPRISE_REMOVED) == 0) {
power.DeviceState = PowerDeviceD3;
PoSetPowerState(DeviceObject, DevicePowerState, power);
deviceExtension->PowerState = PowerDeviceD0;
if (deviceExtension->RangeList != NULL) {
status = PmRemovePhysicalMemory(deviceExtension->RangeList);
ASSERT(status == STATUS_SUCCESS);
DbgBreakPoint();
}
}
Irp->IoStatus.Status = STATUS_SUCCESS;
status = PmPassIrp(DeviceObject, Irp);
IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, (PVOID) Irp);
IoDetachDevice(deviceExtension->AttachedDevice);
deviceExtension->AttachedDevice = NULL;
if (deviceExtension->RangeList != NULL) {
PmFreeRangeList(deviceExtension->RangeList);
deviceExtension->RangeList = NULL;
}
IoDeleteDevice(DeviceObject);
return status;
}
NTSTATUS
PmCancelRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function handles IRP_MN_CANCEL_REMOVE_DEVICE IRPs.
Arguments:
DeviceObject - The functional device object.
Irp - The I/O request packet.
Return Value:
NTSTATUS
--*/
{
PPM_DEVICE_EXTENSION deviceExtension;
PAGED_CODE();
(VOID) PmDeferProcessing(DeviceObject, Irp);
deviceExtension = DeviceObject->DeviceExtension;
if (deviceExtension->RangeList != NULL) {
(VOID) PmAddPhysicalMemory(DeviceObject, deviceExtension->RangeList);
}
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
PmQueryStopDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function handles IRP_MN_QUERY_STOP_DEVICE IRPs.
Arguments:
DeviceObject - The functional device object.
Irp - The I/O request packet.
Return Value:
NTSTATUS
--*/
{
PAGED_CODE();
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_DEVICE_BUSY;
}
NTSTATUS
PmCancelStopDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function handles IRP_MN_CANCEL_STOP_DEVICE IRPs.
Arguments:
DeviceObject - The functional device object.
Irp - The I/O request packet.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PAGED_CODE();
(VOID) PmDeferProcessing(DeviceObject, Irp);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
PmQueryCapabilities(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PPM_DEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
ULONG i;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation(Irp);
deviceExtension = DeviceObject->DeviceExtension;
status = PmDeferProcessing(DeviceObject, Irp);
if (!NT_SUCCESS(status)) {
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
if (irpSp->Parameters.DeviceCapabilities.Capabilities->Version != 1) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
for (i = 0; i < PowerSystemMaximum; i++) {
deviceExtension->DeviceStateMapping[i] =
irpSp->Parameters.DeviceCapabilities.Capabilities->DeviceState[i];
}
//
// Would *LIKE* to smash the eject supported, and removable bits
// here but this isn't really supported. The hot plug applet pops
// up (because the device is marked removable or ejectable) and
// then goes away a few seconds later when the driver is installed
// (and the capabilities requeried).
//
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
PmSurpriseRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PPM_DEVICE_EXTENSION deviceExtension;
PAGED_CODE();
deviceExtension = DeviceObject->DeviceExtension;
if (deviceExtension->RangeList != NULL) {
// XXX
// KeBugCheckEx(MEMORY_FATAL_ERROR,
// ....
}
deviceExtension->Flags |= DF_SURPRISE_REMOVED;
if (deviceExtension->RangeList != NULL) {
PmFreeRangeList(deviceExtension->RangeList);
deviceExtension->RangeList = NULL;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
return PmPassIrp(DeviceObject, Irp);
}
PDRIVER_DISPATCH PmPnpDispatchTable[] = {
PmStartDevice, // IRP_MN_START_DEVICE
PmQueryRemoveDevice, // IRP_MN_QUERY_REMOVE_DEVICE
PmRemoveDevice, // IRP_MN_REMOVE_DEVICE
PmCancelRemoveDevice, // IRP_MN_CANCEL_REMOVE_DEVICE
NULL, // IRP_MN_STOP_DEVICE (never get, fails query-stop)
PmQueryStopDevice, // IRP_MN_QUERY_STOP_DEVICE
PmCancelStopDevice, // IRP_MN_CANCEL_STOP_DEVICE
NULL, // IRP_MN_QUERY_DEVICE_RELATIONS
NULL, // IRP_MN_QUERY_INTERFACE
PmQueryCapabilities, // IRP_MN_QUERY_CAPABILITIES
NULL, // IRP_MN_QUERY_RESOURCES
NULL, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS
NULL, // IRP_MN_QUERY_DEVICE_TEXT
NULL, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS
NULL, // unused
NULL, // IRP_MN_READ_CONFIG
NULL, // IRP_MN_WRITE_CONFIG
NULL, // IRP_MN_EJECT
NULL, // IRP_MN_SET_LOCK
NULL, // IRP_MN_QUERY_ID
NULL, // IRP_MN_QUERY_PNP_DEVICE_STATE
NULL, // IRP_MN_QUERY_BUS_INFORMATION
NULL, // IRP_MN_DEVICE_USAGE_NOTIFICATION
PmSurpriseRemoveDevice, // IRP_MN_SURPRISE_REMOVAL
};
ULONG PmPnpDispatchTableSize =
sizeof (PmPnpDispatchTable) / sizeof (PmPnpDispatchTable[0]);