You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2140 lines
52 KiB
2140 lines
52 KiB
/*++
|
|
|
|
Copyright (c) 2000-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dispatch.c
|
|
|
|
Abstract:
|
|
|
|
ACPI BIOS Simulator / Generic 3rd Party Operation Region Provider
|
|
Pnp / Power handler module
|
|
|
|
Author(s):
|
|
|
|
Vincent Geglia
|
|
Michael T. Murphy
|
|
Chris Burgess
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
//
|
|
// General includes
|
|
//
|
|
|
|
#include "ntddk.h"
|
|
|
|
//
|
|
// Specific includes
|
|
//
|
|
|
|
#include "acpisim.h"
|
|
#include "dispatch.h"
|
|
#include "util.h"
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
|
|
NTSTATUS
|
|
AcpisimPnpStartDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpStopDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpQueryStopDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpCancelStopDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpRemoveDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpQueryRemoveDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpCancelRemoveDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpSurpriseRemoval
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPnpQueryCapabilities
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPowerQueryPower
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPowerSetPower
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimPowerSIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimQueryPowerDIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimSetPowerDIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimCompletionRoutine
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimForwardIrpAndWait
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimIssuePowerDIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimCompleteSIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpisimD0Completion
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AcpisimInitDevPowerStateTable
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
|
|
//
|
|
// Pnp minor dispatch table
|
|
//
|
|
|
|
IRP_DISPATCH_TABLE PnpDispatchTable[] = {
|
|
IRP_MN_START_DEVICE, "Pnp/START_DEVICE", AcpisimPnpStartDevice,
|
|
IRP_MN_STOP_DEVICE, "Pnp/STOP_DEVICE", AcpisimPnpStopDevice,
|
|
IRP_MN_QUERY_STOP_DEVICE, "Pnp/QUERY_STOP_DEVICE", AcpisimPnpQueryStopDevice,
|
|
IRP_MN_CANCEL_STOP_DEVICE, "Pnp/CANCEL_STOP_DEVICE", AcpisimPnpCancelStopDevice,
|
|
IRP_MN_REMOVE_DEVICE, "Pnp/REMOVE_DEVICE", AcpisimPnpRemoveDevice,
|
|
IRP_MN_QUERY_REMOVE_DEVICE, "Pnp/QUERY_REMOVE_DEVICE", AcpisimPnpQueryRemoveDevice,
|
|
IRP_MN_CANCEL_REMOVE_DEVICE,"Pnp/CANCEL_REMOVE_DEVICE", AcpisimPnpCancelRemoveDevice,
|
|
IRP_MN_SURPRISE_REMOVAL, "Pnp/SURPRISE_REMOVAL", AcpisimPnpSurpriseRemoval,
|
|
IRP_MN_QUERY_CAPABILITIES, "Pnp/QUERY_CAPABILITIIES", AcpisimPnpQueryCapabilities
|
|
};
|
|
|
|
//
|
|
// Power minor dispatch table
|
|
//
|
|
|
|
IRP_DISPATCH_TABLE PowerDispatchTable[] = {
|
|
IRP_MN_QUERY_POWER, "Power/QUERY_POWER", AcpisimPowerQueryPower,
|
|
IRP_MN_SET_POWER, "Power/SET_POWER", AcpisimPowerSetPower
|
|
};
|
|
|
|
NTSTATUS
|
|
AcpisimDispatchPnp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the pnp IRP handler. It checks the minor code,
|
|
and passes on to the appropriate minor handler.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IRP processing
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
ULONG count = 0;
|
|
|
|
DBG_PRINT (DBG_INFO, "Entering AcpisimDispatchPnp.\n");
|
|
|
|
while (count < sizeof (PnpDispatchTable) / sizeof (IRP_DISPATCH_TABLE)) {
|
|
|
|
if (irpsp->MinorFunction == PnpDispatchTable[count].IrpFunction) {
|
|
DBG_PRINT (DBG_INFO,
|
|
"Recognized PnP IRP 0x%x '%s'.\n",
|
|
irpsp->MinorFunction,
|
|
PnpDispatchTable[count].IrpName);
|
|
|
|
status = PnpDispatchTable[count].IrpHandler (DeviceObject, Irp);
|
|
|
|
goto EndAcpisimDispatchPnp;
|
|
}
|
|
|
|
count ++;
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO, "Unrecognized PnP IRP 0x%x, pass it on.\n", irpsp->MinorFunction);
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
EndAcpisimDispatchPnp:
|
|
|
|
DBG_PRINT (DBG_INFO, "Exiting AcpisimDispatchPnp.\n");
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimDispatchPower
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the power IRP handler. It checks the minor code,
|
|
and passes on to the appropriate minor handler.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IRP processing
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
ULONG count = 0;
|
|
|
|
DBG_PRINT (DBG_INFO, "Entering AcpisimDispatchPower.\n");
|
|
|
|
while (count < sizeof (PowerDispatchTable) / sizeof (IRP_DISPATCH_TABLE)) {
|
|
|
|
if (irpsp->MinorFunction == PowerDispatchTable[count].IrpFunction) {
|
|
DBG_PRINT (DBG_INFO,
|
|
"Recognized Power IRP 0x%x '%s'.\n",
|
|
irpsp->MinorFunction,
|
|
PowerDispatchTable[count].IrpName);
|
|
|
|
status = PowerDispatchTable[count].IrpHandler (DeviceObject, Irp);
|
|
|
|
goto EndAcpisimDispatchPower;
|
|
}
|
|
|
|
count ++;
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO, "Unrecognized Power IRP 0x%x, pass it on.\n", irpsp->MinorFunction);
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = PoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
EndAcpisimDispatchPower:
|
|
|
|
DBG_PRINT (DBG_INFO, "Exiting AcpisimDispatchPower.\n");
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpStartDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Start Device handler. It enables the device interface
|
|
and registers the operation region handler.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IRP_MN_START_DEVICE processing
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
KIRQL oldirql;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpStartDevice.\n");
|
|
|
|
//
|
|
// We handle this IRP on the way back up.
|
|
//
|
|
|
|
status = AcpisimForwardIrpAndWait (DeviceObject, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if ((status != STATUS_SUCCESS && status != STATUS_PENDING) || !NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Error processing, or lower driver failed start IRP. IoCallDriver = %lx, Irp->IoStatus.Status = %lx\n",
|
|
status,
|
|
Irp->IoStatus.Status);
|
|
|
|
goto EndAcpisimPnpStartDevice;
|
|
}
|
|
|
|
//
|
|
// Check to see if we are already started. If we are,
|
|
// just return success since we aren't using resources
|
|
// anyway.
|
|
//
|
|
|
|
if (deviceextension->PnpState == PNP_STATE_STARTED) {
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto EndAcpisimPnpStartDevice;
|
|
}
|
|
|
|
//
|
|
// Enable our device interface
|
|
//
|
|
|
|
status = AcpisimEnableDisableDeviceInterface (DeviceObject, TRUE);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Error enabling device interface. Fail the start. Status = %lx.\n",
|
|
status);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
goto EndAcpisimPnpStartDevice;
|
|
}
|
|
|
|
AcpisimSetDevExtFlags (DeviceObject, DE_FLAG_INTERFACE_ENABLED);
|
|
|
|
//
|
|
// Typically, we would check the state of our hardware, and
|
|
// set our internal power state to reflect the current state
|
|
// of the hardware. However, in this case we are a virtual
|
|
// device, and it is safe to assume we are in D0 when we
|
|
// receive IRP_MN_START_DEVICE.
|
|
//
|
|
|
|
AcpisimUpdatePowerState (DeviceObject, POWER_STATE_WORKING);
|
|
AcpisimUpdateDevicePowerState (DeviceObject, PowerDeviceD0);
|
|
|
|
//
|
|
// Finally, we can register our operation region handler.
|
|
//
|
|
|
|
status = AcpisimRegisterOpRegionHandler (DeviceObject);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Couldn't register op region handler (%lx). Fail start IRP.\n",
|
|
status);
|
|
|
|
goto EndAcpisimPnpStartDevice;
|
|
}
|
|
|
|
AcpisimSetDevExtFlags (DeviceObject, DE_FLAG_OPREGION_REGISTERED);
|
|
|
|
|
|
EndAcpisimPnpStartDevice:
|
|
|
|
//
|
|
// If we completed the start successfully, change our pnp state
|
|
// to PNP_STARTED
|
|
//
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_STARTED);
|
|
|
|
} else {
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_STOPPED);
|
|
}
|
|
|
|
//
|
|
// Because we are handling this IRP "on the way up", we need
|
|
// to complete it when we are done working with it.
|
|
//
|
|
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpStartDevice.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpStopDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Stop Device handler. It checks to see if
|
|
there are any outstanding requests, and fails the stop IRP
|
|
if there are.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IRP_MN_STOP_DEVICE processing
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpStopDevice.\n");
|
|
|
|
//
|
|
// BUGBUG - We currently don't handle the case where
|
|
// there is still an outstanding request at stop
|
|
// time. If we were to do things correctly, we'd
|
|
// complete any outstanding requests in the driver
|
|
// with an appropriate error code. In this
|
|
// particular case, if a request happened to squeak
|
|
// by our check at QUERY STOP time, it is likely
|
|
// the request would not be completed at all.
|
|
//
|
|
|
|
//
|
|
// Oh, and we had better not have LESS then 2 count
|
|
// or we've got a bug somewhere.
|
|
//
|
|
|
|
if (deviceextension->OutstandingIrpCount < 2) {
|
|
DBG_PRINT (DBG_WARN,
|
|
"Possible internal consistency error - OutstandingIrpCount too low.\n");
|
|
}
|
|
|
|
ASSERT (deviceextension->OutstandingIrpCount == 2);
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"IRP_MN_STOP forwarding failed (%lx).\n",
|
|
status);
|
|
|
|
goto EndAcpisimPnpStopDevice;
|
|
}
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_STOPPED);
|
|
|
|
EndAcpisimPnpStopDevice:
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpStopDevice.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpQueryStopDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Query Stop Device handler. If there are any
|
|
outstanding requests, it vetos the IRP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IRP_MN_QUERY_STOP_DEVICE processing
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpQueryStopDevice.\n");
|
|
|
|
//
|
|
// Let existing IRPs in the driver complete before we say OK.
|
|
// But to do this, we need to get the OutstandingIrpsCount
|
|
// right. Subtract 2 since we are biased to 1, and we have an
|
|
// additional 1 for the QUERY_STOP IRP.
|
|
//
|
|
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
|
|
status = KeWaitForSingleObject (&deviceextension->IrpsCompleted,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
0);
|
|
|
|
InterlockedIncrement (&deviceextension->OutstandingIrpCount);
|
|
InterlockedIncrement (&deviceextension->OutstandingIrpCount);
|
|
KeResetEvent (&deviceextension->IrpsCompleted);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"KeWaitForSingleObject failed (%lx). IRP_MN_QUERY_STOP failed.\n",
|
|
status);
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
goto EndPnpQueryStopDevice;
|
|
}
|
|
|
|
//
|
|
// We can stop - change our state to stopping, and pass it on.
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_STOP_PENDING);
|
|
|
|
EndPnpQueryStopDevice:
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpQueryStopDevice.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpCancelStopDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Cancel Stop Device handler. It does nothing
|
|
more then returns the pnp state to started. This is a virtual
|
|
device so there is no work to do.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpCancelStopDevice.\n");
|
|
|
|
status = AcpisimForwardIrpAndWait (DeviceObject, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"IRP_MN_CANCEL_STOP forwarding failed (%lx).\n",
|
|
status);
|
|
|
|
goto EndPnpCancelStopDevice;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, 0);
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_STARTED);
|
|
|
|
EndPnpCancelStopDevice:
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpCancelStopDevice.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpRemoveDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Remove Device handler. It de-registers the
|
|
operation region handler, detaches the device object, and
|
|
deletes it if all goes well.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
status of removal operation
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
PDEVICE_OBJECT nextdevice = deviceextension->NextDevice;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpRemoveDevice.\n");
|
|
|
|
//
|
|
// BUGBUG - We currently don't handle the case where
|
|
// there is still an outstanding request at remove
|
|
// time. If we were to do things correctly, we'd
|
|
// complete any outstanding requests in the driver
|
|
// with an appropriate error code. In this
|
|
// particular case, if a request happened to squeak
|
|
// by our check at QUERY REMOVE time, it is likely
|
|
// the request would not be completed at all.
|
|
//
|
|
|
|
//
|
|
// Our OutstandingIrpCount logic is biased to 1. So
|
|
// if we are processing a remove IRP, and there are
|
|
// no other requests in the driver, OustandingIrpCount
|
|
// had better be 2.
|
|
|
|
if (deviceextension->OutstandingIrpCount < 2) {
|
|
DBG_PRINT (DBG_WARN,
|
|
"Possible internal consistency error - OutstandingIrpCount too low.\n");
|
|
}
|
|
|
|
ASSERT (deviceextension->OutstandingIrpCount == 2);
|
|
|
|
//
|
|
// Ok, we are ready to remove the device. Shut down the
|
|
// interface, deregister the opregion handler, and
|
|
// delete the device object.
|
|
//
|
|
|
|
status = AcpisimEnableDisableDeviceInterface (DeviceObject, FALSE);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
|
|
AcpisimClearDevExtFlags (DeviceObject, DE_FLAG_INTERFACE_ENABLED);
|
|
}
|
|
|
|
status = AcpisimUnRegisterOpRegionHandler (DeviceObject);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
|
|
AcpisimClearDevExtFlags (DeviceObject, DE_FLAG_OPREGION_REGISTERED);
|
|
}
|
|
|
|
RtlFreeUnicodeString (&deviceextension->InterfaceString);
|
|
|
|
IoDetachDevice (deviceextension->NextDevice);
|
|
IoDeleteDevice (DeviceObject);
|
|
|
|
//
|
|
// Now, pass it on...
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
status = IoCallDriver (nextdevice, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Passing remove IRP onto next driver failed for some reason (%lx).\n",
|
|
status);
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpRemoveDevice.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpQueryRemoveDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Query Remove Device handler. It waits for
|
|
existing requests in the driver to finish, and then completes
|
|
the IRP successfully.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
Status of query remove device operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpQueryRemoveDevice.\n");
|
|
|
|
//
|
|
// Make sure our state is correct
|
|
//
|
|
|
|
ASSERT (deviceextension->OutstandingIrpCount >= 2);
|
|
|
|
//
|
|
// Let existing IRPs in the driver complete before we say OK.
|
|
// But to do this, we need to get the OutstandingIrpsCount
|
|
// right. Subtract 2 since we are biased to 1, and we have an
|
|
// additional 1 for the QUERY_STOP IRP.
|
|
//
|
|
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
|
|
status = KeWaitForSingleObject (&deviceextension->IrpsCompleted,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
0);
|
|
|
|
InterlockedIncrement (&deviceextension->OutstandingIrpCount);
|
|
InterlockedIncrement (&deviceextension->OutstandingIrpCount);
|
|
KeResetEvent (&deviceextension->IrpsCompleted);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"KeWaitForSingleObject failed (%lx). IRP_MN_QUERY_REMOVE failed.\n",
|
|
status);
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
goto EndPnpQueryRemoveDevice;
|
|
}
|
|
|
|
//
|
|
// We can remove - change our state to remove pending, and pass it on.
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_REMOVE_PENDING);
|
|
|
|
EndPnpQueryRemoveDevice:
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpQueryRemoveDevice.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpCancelRemoveDevice
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Cancel Remove Device handler. It does nothing
|
|
more then returns the pnp state to started. This is a virtual
|
|
device so there is no work to do.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpCancelRemoveDevice.\n");
|
|
|
|
status = AcpisimForwardIrpAndWait (DeviceObject, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"IRP_MN_CANCEL_REMOVE forwarding failed (%lx).\n",
|
|
status);
|
|
|
|
goto EndPnpCancelRemoveDevice;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, 0);
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_STARTED);
|
|
|
|
EndPnpCancelRemoveDevice:
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpCancelRemoveDevice.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpSurpriseRemoval
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Pnp Surprise Remove handler. It basically updates
|
|
the state, and passes the IRP on.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpSurpriseRemoval.\n");
|
|
|
|
//
|
|
// Again, because we are a virtual device, handling
|
|
// surprise remove is really a no-op. Just update
|
|
// our state, and succeed the IRP.
|
|
//
|
|
|
|
AcpisimUpdatePnpState (DeviceObject, PNP_STATE_SURPRISE_REMOVAL);
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpSurpriseRemoval.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPnpQueryCapabilities
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles IRP_MN_QUERY_CAPABILITIES. We need this
|
|
information to build our power state table correctly. All
|
|
we do here is set a completion routine, as we need to gather this
|
|
data after the PDO has filled out DeviceState.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
Status of operation
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
PIO_STACK_LOCATION irpsp;
|
|
UCHAR count = 0;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPnpQueryCapabilities.\n");
|
|
|
|
//
|
|
// Fill out the power mapping table with a default
|
|
//
|
|
|
|
AcpisimInitDevPowerStateTable (DeviceObject);
|
|
|
|
//
|
|
// Handle this IRP after the PDO has filled out the structure
|
|
//
|
|
|
|
status = AcpisimForwardIrpAndWait (DeviceObject, Irp);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR, "Somebody failed the QUERY_CAPABILITIES IRP...\n");
|
|
goto EndAcpisimPnpQueryCapabilities;
|
|
}
|
|
|
|
irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
//
|
|
// Update our power mappings with what we found in the device
|
|
// capabilities structure. We only use valid mappings, e.g.
|
|
// PowerDeviceUnspecified is ignored.
|
|
//
|
|
|
|
DBG_PRINT (DBG_INFO, "Device mappings:\n");
|
|
|
|
for (count = 0; count < 6; count ++) {
|
|
|
|
if (irpsp->Parameters.DeviceCapabilities.Capabilities->DeviceState[count + 1] != PowerDeviceUnspecified) {
|
|
|
|
deviceextension->PowerMappings[count] = irpsp->Parameters.DeviceCapabilities.Capabilities->DeviceState [count + 1];
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO, "S%d --> D%d\n", count, deviceextension->PowerMappings[count] - 1);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = status;
|
|
|
|
EndAcpisimPnpQueryCapabilities:
|
|
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPnpQueryCapabilities.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPowerQueryPower
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the QUERY Power handler. It determines if the power
|
|
IRP is an S or D IRP, and passes it on to the proper handler.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
Status returned from power handler
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPowerQueryPower.\n");
|
|
|
|
switch (irpsp->Parameters.Power.Type) {
|
|
|
|
case SystemPowerState:
|
|
|
|
status = AcpisimPowerSIrp (DeviceObject, Irp);
|
|
break;
|
|
|
|
case DevicePowerState:
|
|
|
|
status = AcpisimQueryPowerDIrp (DeviceObject, Irp);
|
|
break;
|
|
|
|
default:
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Undefined QUERY Power IRP type. Ignoring.\n");
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPowerQueryPower.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPowerSetPower
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the SET Power handler. It determines if the power
|
|
IRP is an S or D IRP, and passes it on to the proper handler.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
Status returned from power handler
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPowerSetPower.\n");
|
|
|
|
switch (irpsp->Parameters.Power.Type) {
|
|
|
|
case SystemPowerState:
|
|
|
|
status = AcpisimPowerSIrp (DeviceObject, Irp);
|
|
break;
|
|
|
|
case DevicePowerState:
|
|
|
|
status = AcpisimSetPowerDIrp (DeviceObject, Irp);
|
|
break;
|
|
|
|
default:
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Undefined SET Power IRP type. Ignoring.\n");
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPowerSetPower.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimPowerSIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the power handler for S IRPs. It sets a
|
|
completion routine, which will queue a D IRP. We don't
|
|
do anything unless it is a D IRP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
Status returned from power handler
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimPowerSIrp.\n");
|
|
|
|
IoMarkIrpPending (Irp);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
IoSetCompletionRoutine (Irp,
|
|
AcpisimIssuePowerDIrp,
|
|
0,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
PoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimPowerSIrp.\n");
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimQueryPowerDIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the QUERY Power DIrp handler. Validate the state, and
|
|
say yes.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
Status returned from power handler
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimQueryPowerDIrp.\n");
|
|
|
|
//
|
|
// Here, we are supposed to figure out if we can go to the
|
|
// power state specified by the IRP. Since we are a virtual
|
|
// device, we don't have a good reason to not go to a different
|
|
// power state. Update our state, and wait to complete requests.
|
|
//
|
|
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
|
|
|
|
status = KeWaitForSingleObject (&deviceextension->IrpsCompleted,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
0);
|
|
|
|
InterlockedIncrement (&deviceextension->OutstandingIrpCount);
|
|
InterlockedIncrement (&deviceextension->OutstandingIrpCount);
|
|
KeResetEvent (&deviceextension->IrpsCompleted);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
//
|
|
// Validate the D IRP
|
|
//
|
|
|
|
switch (irpsp->Parameters.Power.State.DeviceState) {
|
|
case PowerDeviceD0:
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
case PowerDeviceD3:
|
|
|
|
AcpisimUpdatePowerState (DeviceObject, POWER_STATE_POWER_PENDING);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT (0);
|
|
DBG_PRINT (DBG_ERROR,
|
|
"AcpisimQueryPowerDIrp: Illegal or unknown PowerDeviceState. Failing.\n");
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = status;
|
|
status = PoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Leaving AcpisimQueryPowerDIrp.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimSetPowerDIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
POWER_STATE powerstate;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimSetPowerDIrp.\n");
|
|
|
|
//
|
|
// Validate the D IRP
|
|
//
|
|
|
|
switch (irpsp->Parameters.Power.State.DeviceState) {
|
|
|
|
//
|
|
// For D0, if we are powered down, we need to pass the IRP down, and
|
|
// set a completion routine. We need the PDO to succeed the power
|
|
// up before we do.
|
|
//
|
|
|
|
case PowerDeviceD0:
|
|
|
|
if (deviceextension->PowerState != POWER_STATE_WORKING) {
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
|
|
IoSetCompletionRoutine (Irp,
|
|
AcpisimD0Completion,
|
|
0,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
IoMarkIrpPending (Irp);
|
|
PoCallDriver (deviceextension->NextDevice, Irp);
|
|
status = STATUS_PENDING;
|
|
goto EndAcpisimSetPowerDIrp;
|
|
}
|
|
|
|
break;
|
|
|
|
case PowerDeviceD1:
|
|
|
|
powerstate.DeviceState = PowerDeviceD1;
|
|
PoSetPowerState (DeviceObject, DevicePowerState, powerstate);
|
|
|
|
AcpisimUpdatePowerState (DeviceObject, POWER_STATE_POWERED_DOWN);
|
|
AcpisimUpdateDevicePowerState (DeviceObject, irpsp->Parameters.Power.State.DeviceState);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case PowerDeviceD2:
|
|
|
|
powerstate.DeviceState = PowerDeviceD2;
|
|
PoSetPowerState (DeviceObject, DevicePowerState, powerstate);
|
|
|
|
AcpisimUpdatePowerState (DeviceObject, POWER_STATE_POWERED_DOWN);
|
|
AcpisimUpdateDevicePowerState (DeviceObject, irpsp->Parameters.Power.State.DeviceState);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case PowerDeviceD3:
|
|
|
|
powerstate.DeviceState = PowerDeviceD3;
|
|
PoSetPowerState (DeviceObject, DevicePowerState, powerstate);
|
|
|
|
AcpisimUpdatePowerState (DeviceObject, POWER_STATE_POWERED_DOWN);
|
|
AcpisimUpdateDevicePowerState (DeviceObject, irpsp->Parameters.Power.State.DeviceState);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT (0);
|
|
DBG_PRINT (DBG_ERROR,
|
|
"AcpisimSetPowerDIrp: Illegal or unknown PowerDeviceState. Failing.\n");
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
Irp->IoStatus.Status = status;
|
|
status = PoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
EndAcpisimSetPowerDIrp:
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Leaving AcpisimSetPowerDIrp.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimCompletionRoutine
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the generic Irp completion routine for when we
|
|
want to wait for an IRP to be completed by the PDO and
|
|
do post-completion work.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Context - Context passed in by IoSetCompletionRoutine.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
|
|
{
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimCompletionRoutine.\n");
|
|
|
|
KeSetEvent (Context, 0, FALSE);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimCompletionRoutine.\n");
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimForwardIrpAndWait
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This forwards the IRP down the device stack, sets
|
|
a completion routine, and waits on the completion
|
|
event. Useful for doing IRP post-completion, based
|
|
on the result of the completion.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
The status set in the IRP when the IRP was completed.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT context;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimForwardIrpAndWait.\n");
|
|
|
|
KeInitializeEvent (&context, SynchronizationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
IoSetCompletionRoutine (Irp,
|
|
AcpisimCompletionRoutine,
|
|
&context,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
status = IoCallDriver (deviceextension->NextDevice, Irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject (&context,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimForwardIrpAndWait.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimIssuePowerDIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the S-IRP completion routine. It examines the completed
|
|
IRP, and if there are no problems, asks the power manager to
|
|
send us the appropriate D-IRP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Context - Context passed into IoSetCompletionRoutine
|
|
|
|
Return Value:
|
|
|
|
Status of requesting D-IRP operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
POWER_STATE powerstate;
|
|
PPOWER_CONTEXT context = NULL;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimIssuePowerDIrp.\n");
|
|
|
|
powerstate.DeviceState = PowerDeviceUnspecified;
|
|
|
|
//
|
|
// Make sure this IRP wasn't failed by the PDO or Lower FFDO
|
|
//
|
|
|
|
if (!NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"AcpisimIssuePowerDIrp: Lower FFDO, BFDO, or PDO failed this IRP (%lx).\n",
|
|
status);
|
|
|
|
status = Irp->IoStatus.Status;
|
|
|
|
goto EndAcpisimIssuePowerDIrp;
|
|
}
|
|
|
|
if (NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
|
|
//
|
|
// Ok, everybody is agreeing to this S state. Send ourselves
|
|
// the appropriate D IRP.
|
|
//
|
|
|
|
//
|
|
// Make sure this is an S Irp
|
|
//
|
|
|
|
ASSERT (irpsp->Parameters.Power.Type == SystemPowerState);
|
|
|
|
if (irpsp->Parameters.Power.Type != SystemPowerState) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Didn't recieve an S Irp when we expected to, or somebody messed up the IRP. Fail it.\n");
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
goto EndAcpisimIssuePowerDIrp;
|
|
}
|
|
|
|
ASSERT (irpsp->MinorFunction == IRP_MN_QUERY_POWER || irpsp->MinorFunction == IRP_MN_SET_POWER);
|
|
|
|
if (irpsp->MinorFunction != IRP_MN_QUERY_POWER && irpsp->MinorFunction != IRP_MN_SET_POWER) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Irp isn't SET or QUERY. Not sure why this wasn't caught earlier (somebody probably messed it up).\nWe don't support any other type. Fail it.\n");
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
goto EndAcpisimIssuePowerDIrp;
|
|
}
|
|
|
|
//
|
|
// Make sure the S IRP is valid
|
|
//
|
|
|
|
if (irpsp->Parameters.Power.State.SystemState >= PowerSystemMaximum) {
|
|
|
|
ASSERT (0);
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Received an undefined S IRP, or somebody messed up the IRP. Fail it.\n");
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto EndAcpisimIssuePowerDIrp;
|
|
}
|
|
|
|
//
|
|
// Use our power mapping table to convert S-->D state
|
|
//
|
|
|
|
powerstate.DeviceState = deviceextension->PowerMappings [irpsp->Parameters.Power.State.SystemState - 1];
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"S%d --> D%d\n", irpsp->Parameters.Power.State.SystemState - 1, powerstate.DeviceState - 1);
|
|
|
|
//
|
|
// We need a context to pass a pointer to the S IRP to the D IRP handler
|
|
// and a pointer to the device object.
|
|
//
|
|
|
|
context = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof (POWER_CONTEXT)+4,
|
|
POWER_CONTEXT_TAG);
|
|
|
|
if (!context) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"Unable to allocate memory for the context.\n");
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto EndAcpisimIssuePowerDIrp;
|
|
}
|
|
|
|
context->SIrp = Irp;
|
|
context->Context = DeviceObject;
|
|
|
|
//
|
|
// Send the D Irp
|
|
//
|
|
|
|
status = PoRequestPowerIrp (deviceextension->Pdo,
|
|
irpsp->MinorFunction,
|
|
powerstate,
|
|
AcpisimCompleteSIrp,
|
|
context,
|
|
NULL);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"AcpisimIssuePowerDIrp: PoRequestPowerIrp failed (%lx).\n");
|
|
|
|
goto EndAcpisimIssuePowerDIrp;
|
|
}
|
|
}
|
|
|
|
status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
EndAcpisimIssuePowerDIrp:
|
|
|
|
//
|
|
// We need to complete the request if something went wrong. Also note,
|
|
// it is not necessary to assume our state is S0/D0 again. The power
|
|
// manager will send us an S0 IRP.
|
|
//
|
|
|
|
if (!NT_SUCCESS (status) && status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"AcpisimIssuePowerDIrp: Something bad happened. Just complete the S Irp with an error.");
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
IoReleaseRemoveLock (&deviceextension->RemoveLock, Irp);
|
|
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
|
|
if (context) {
|
|
|
|
ExFreePool (context);
|
|
}
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimIssuePowerDIrp.\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimCompleteSIrp
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the S-Irp completion routine set by PoRequestPowerIrp.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the FDO
|
|
|
|
MinorFunction - type of request
|
|
|
|
PowerState - type of IRP
|
|
|
|
Context - Context passed into PoRequestPowerIrp
|
|
|
|
IoStatus - IoStatus block of completed D Irp
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PPOWER_CONTEXT context = (PPOWER_CONTEXT) Context;
|
|
PDEVICE_OBJECT deviceobject = context->Context;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (deviceobject);
|
|
PIRP sirp = context->SIrp;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimCompleteSIrp.\n");
|
|
|
|
//
|
|
// Propagate the device power IRP's status in the system power IRP
|
|
//
|
|
|
|
sirp->IoStatus.Status = IoStatus->Status;
|
|
|
|
//
|
|
// Tell the power manager we are done with this IRP
|
|
//
|
|
|
|
PoStartNextPowerIrp (sirp);
|
|
|
|
IoCompleteRequest (sirp, IO_NO_INCREMENT);
|
|
IoReleaseRemoveLock (&deviceextension->RemoveLock, sirp);
|
|
ExFreePool (Context);
|
|
|
|
//
|
|
// Normally our dispatch routine decrements IRP counts,
|
|
// but since it was returned STATUS_PENDING, it wasn't
|
|
// decremented earlier
|
|
//
|
|
|
|
AcpisimDecrementIrpCount (deviceobject);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimCompleteSIrp.\n");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimD0Completion
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the D0 Irp completion routine
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the FDO
|
|
|
|
MinorFunction - type of request
|
|
|
|
Context - Context passed into IoSetCompletionRoutine
|
|
|
|
|
|
Return Value:
|
|
|
|
Error status or STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
POWER_STATE powerstate;
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Entering AcpisimD0Completion.\n");
|
|
|
|
//
|
|
// Make sure this IRP wasn't failed by the PDO or Lower FFDO
|
|
//
|
|
|
|
if (!NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"AcpisimD0Completion: Lower FFDO, BFDO, or PDO failed this IRP (%lx).\n",
|
|
status);
|
|
|
|
status = Irp->IoStatus.Status;
|
|
|
|
goto EndAcpisimD0Completion;
|
|
}
|
|
|
|
//
|
|
// This is where we do actual D0 transition work. Since this
|
|
// is a virtual device, the only thing we do is change our
|
|
// internal state.
|
|
//
|
|
|
|
AcpisimUpdatePowerState (DeviceObject, POWER_STATE_WORKING);
|
|
AcpisimUpdateDevicePowerState (DeviceObject, irpsp->Parameters.Power.State.DeviceState);
|
|
|
|
powerstate.DeviceState = PowerDeviceD0;
|
|
PoSetPowerState (DeviceObject, DevicePowerState, powerstate);
|
|
|
|
status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
EndAcpisimD0Completion:
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
IoReleaseRemoveLock (&deviceextension->RemoveLock, Irp);
|
|
AcpisimDecrementIrpCount (DeviceObject);
|
|
|
|
DBG_PRINT (DBG_INFO,
|
|
"Exiting AcpisimD0Completion.\n");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
VOID
|
|
AcpisimInitDevPowerStateTable
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills out the power mapping structure with defaults.
|
|
We simply default to using D3 in any non-S0 state.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the FDO
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
UCHAR count;
|
|
|
|
deviceextension->PowerMappings[0] = PowerDeviceD0;
|
|
|
|
for (count = 1; count < 5; count ++) {
|
|
|
|
deviceextension->PowerMappings[count] = PowerDeviceD3;
|
|
}
|
|
}
|
|
|
|
NTSTATUS AcpisimDispatchIoctl
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the handler for IOCTL requests. We just call the supplied
|
|
function to handle the IOCTL, or pass it on if the handler doesn't
|
|
handle it.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IRP processing
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
ULONG count = 0;
|
|
|
|
DBG_PRINT (DBG_INFO, "Entering AcpisimDispatchIoctl\n");
|
|
|
|
status = AcpisimHandleIoctl (DeviceObject, Irp);
|
|
|
|
if (status == STATUS_NOT_SUPPORTED) {
|
|
|
|
//
|
|
// IOCTL wasn't handled, pass it on...
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (AcpisimLibGetNextDevice (DeviceObject), Irp);
|
|
|
|
} else {
|
|
|
|
//
|
|
// IOCTL was handled, complete it.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
DBG_PRINT (DBG_INFO, "Exiting AcpisimDispatchIoctl\n");
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpisimDispatchSystemControl
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the handler for System Control requests. Since we currently
|
|
don't support any System Control calls, we are just going to pass
|
|
them on to the next driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IoCallDriver
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
DBG_PRINT (DBG_INFO, "Entering AcpisimDispatchSystemControl\n");
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (AcpisimLibGetNextDevice (DeviceObject), Irp);
|
|
|
|
DBG_PRINT (DBG_INFO, "Exiting AcpisimDispatchSystemControl\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS AcpisimCreateClose
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the handler for CreateFile and CloseHandle requests.
|
|
We do nothing except update our internal extension to track
|
|
the number of outstanding handles.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object the IRP pertains to
|
|
|
|
Irp - pointer to the IRP
|
|
|
|
Return Value:
|
|
|
|
result of IRP processing
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (Irp);
|
|
PDEVICE_EXTENSION deviceextension = AcpisimGetDeviceExtension (DeviceObject);
|
|
|
|
ASSERT (irpsp->MajorFunction == IRP_MJ_CREATE || irpsp->MajorFunction == IRP_MJ_CLOSE);
|
|
|
|
switch (irpsp->MajorFunction) {
|
|
|
|
case IRP_MJ_CREATE:
|
|
InterlockedIncrement (&deviceextension->HandleCount);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MJ_CLOSE:
|
|
InterlockedDecrement (&deviceextension->HandleCount);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
|
|
DBG_PRINT (DBG_ERROR,
|
|
"AcpisimCreateClose - unexpected Irp type.\n");
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, 0);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|