Leaked source code of windows server 2003
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.
 
 
 
 
 
 

694 lines
18 KiB

/*++
Copyright (c) 2002 Microsoft Corporation
Module Name:
wake.c
Abstract:
This module contains code to handle
IRP_MJ_POWER dispatches for SD controllers
Authors:
Neil Sandlin (neilsa) Jan 1, 2002
Environment:
Kernel mode only
Notes:
Revision History:
--*/
#include "pch.h"
//
// Internal References
//
NTSTATUS
SdbusFdoWaitWakeIoCompletion(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
IN PVOID Context
);
VOID
SdbusPdoWaitWakeCancelRoutine(
IN PDEVICE_OBJECT Pdo,
IN OUT PIRP Irp
);
/**************************************************************************
FDO ROUTINES
**************************************************************************/
NTSTATUS
SdbusFdoWaitWake(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp
)
/*++
Routine Description
Handles WAIT_WAKE for the given sd controller
Arguments
Pdo - Pointer to the functional device object for the sd controller
Irp - The IRP_MN_WAIT_WAKE Irp
Return Value
STATUS_PENDING - Wait wake is pending
STATUS_SUCCESS - Wake is already asserted, wait wake IRP is completed
in this case
Any other status - Error
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
WAKESTATE oldWakeState;
//
// Record the wait wake Irp..
//
fdoExtension->WaitWakeIrp = Irp;
oldWakeState = InterlockedCompareExchange(&fdoExtension->WaitWakeState,
WAKESTATE_ARMED, WAKESTATE_WAITING);
DebugPrint((SDBUS_DEBUG_POWER, "fdo %x irp %x WaitWake: prevState %s\n",
Fdo, Irp, WAKESTATE_STRING(oldWakeState)));
if (oldWakeState == WAKESTATE_WAITING_CANCELLED) {
fdoExtension->WaitWakeState = WAKESTATE_COMPLETING;
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_CANCELLED;
}
IoMarkIrpPending(Irp);
IoCopyCurrentIrpStackLocationToNext (Irp);
//
// Set our completion routine in the Irp..
//
IoSetCompletionRoutine(Irp,
SdbusFdoWaitWakeIoCompletion,
Fdo,
TRUE,
TRUE,
TRUE);
//
// now pass this down to the lower driver..
//
PoCallDriver(fdoExtension->LowerDevice, Irp);
return STATUS_PENDING;
}
NTSTATUS
SdbusFdoWaitWakeIoCompletion(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
Completion routine for the IRP_MN_WAIT_WAKE request for this
sd controller. This is called when the WAIT_WAKE IRP is
completed by the lower driver (PCI/ACPI) indicating either that
1. SD bus controller asserted wake
2. WAIT_WAKE was cancelled
3. Lower driver returned an error for some reason
Arguments:
Fdo - Pointer to Functional device object for the sd controller
Irp - Pointer to the IRP for the power request (IRP_MN_WAIT_WAKE)
Context - Not used
Return Value:
STATUS_SUCCESS - WAIT_WAKE was completed with success
Any other status - Wake could be not be accomplished.
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PPDO_EXTENSION pdoExtension;
PDEVICE_OBJECT pdo;
WAKESTATE oldWakeState;
UNREFERENCED_PARAMETER(Context);
oldWakeState = InterlockedExchange(&fdoExtension->WaitWakeState, WAKESTATE_COMPLETING);
DebugPrint((SDBUS_DEBUG_POWER, "fdo %x irp %x WW IoComp: prev=%s\n",
Fdo, Irp, WAKESTATE_STRING(oldWakeState)));
if (oldWakeState != WAKESTATE_ARMED) {
ASSERT(oldWakeState == WAKESTATE_ARMING_CANCELLED);
return STATUS_MORE_PROCESSING_REQUIRED;
}
if (IsDeviceFlagSet(fdoExtension, SDBUS_FDO_WAKE_BY_CD)) {
POWER_STATE powerState;
ResetDeviceFlag(fdoExtension, SDBUS_FDO_WAKE_BY_CD);
PoStartNextPowerIrp(Irp);
powerState.DeviceState = PowerDeviceD0;
PoRequestPowerIrp(fdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL);
} else {
// NOTE:
// At this point we do NOT know how to distinguish which function
// in a multifunction device has asserted wake.
// So we go through the entire list of PDOs hanging off this FDO
// and complete all the outstanding WAIT_WAKE Irps for every PDO that
// that's waiting. We leave it up to the FDO for the device to figure
// if it asserted wake
//
for (pdo = fdoExtension->PdoList; pdo != NULL ; pdo = pdoExtension->NextPdoInFdoChain) {
pdoExtension = pdo->DeviceExtension;
if (IsDeviceLogicallyRemoved(pdoExtension) ||
IsDevicePhysicallyRemoved(pdoExtension)) {
//
// This pdo is about to be removed ..
// skip it
//
continue;
}
if (pdoExtension->WaitWakeIrp != NULL) {
PIRP finishedIrp;
//
// Ah.. this is a possible candidate to have asserted the wake
//
//
// Make sure this IRP will not be completed again or cancelled
//
finishedIrp = pdoExtension->WaitWakeIrp;
DebugPrint((SDBUS_DEBUG_POWER, "fdo %x WW IoComp: irp %08x for pdo %08x\n",
Fdo, finishedIrp, pdo));
IoSetCancelRoutine(finishedIrp, NULL);
//
// Propagate parent's status to child
//
PoStartNextPowerIrp(finishedIrp);
finishedIrp->IoStatus.Status = Irp->IoStatus.Status;
//
// Since we didn't pass this IRP down, call our own completion routine
//
// SdbusPdoWaitWakeCompletion(pdo, finishedIrp, pdoExtension);
IoCompleteRequest(finishedIrp, IO_NO_INCREMENT);
}
}
PoStartNextPowerIrp(Irp);
}
return Irp->IoStatus.Status;
}
VOID
SdbusFdoWaitWakePoCompletion(
IN PDEVICE_OBJECT Fdo,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description
This routine is called on completion of a D irp generated by an S irp.
Parameters
DeviceObject - Pointer to the Fdo for the SDBUS controller
MinorFunction - Minor function of the IRP_MJ_POWER request
PowerState - Power state requested
Context - Context passed in to the completion routine
IoStatus - Pointer to the status block which will contain
the returned status
Return Value
Status
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
DebugPrint((SDBUS_DEBUG_POWER, "fdo %x irp %x WaitWakePoCompletion: prevState %s\n",
Fdo, fdoExtension->WaitWakeIrp,
WAKESTATE_STRING(fdoExtension->WaitWakeState)));
ASSERT (fdoExtension->WaitWakeIrp);
fdoExtension->WaitWakeIrp = NULL;
ASSERT (fdoExtension->WaitWakeState == WAKESTATE_COMPLETING);
fdoExtension->WaitWakeState = WAKESTATE_DISARMED;
}
NTSTATUS
SdbusFdoArmForWake(
PFDO_EXTENSION FdoExtension
)
/*++
Routine Description:
This routine is called to enable the controller for wake. It is called by the Pdo
wake routines when a wake-enabled controller gets a wait-wake irp, and also by
the idle routine to arm for wake from D3 by card insertion.
Arguments:
FdoExtension - device extension of the controller
Return Value:
status
--*/
{
NTSTATUS status = STATUS_PENDING;
PIO_STACK_LOCATION irpStack;
PIRP irp;
LONG oldWakeState;
POWER_STATE powerState;
oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
WAKESTATE_WAITING, WAKESTATE_DISARMED);
DebugPrint((SDBUS_DEBUG_POWER, "fdo %x ArmForWake: prevState %s\n",
FdoExtension->DeviceObject, WAKESTATE_STRING(oldWakeState)));
if ((oldWakeState == WAKESTATE_ARMED) || (oldWakeState == WAKESTATE_WAITING)) {
return STATUS_SUCCESS;
}
if (oldWakeState != WAKESTATE_DISARMED) {
return STATUS_UNSUCCESSFUL;
}
powerState.SystemState = FdoExtension->DeviceCapabilities.SystemWake;
status = PoRequestPowerIrp(FdoExtension->DeviceObject,
IRP_MN_WAIT_WAKE,
powerState,
SdbusFdoWaitWakePoCompletion,
NULL,
NULL);
if (!NT_SUCCESS(status)) {
FdoExtension->WaitWakeState = WAKESTATE_DISARMED;
DebugPrint((SDBUS_DEBUG_POWER, "WaitWake to FDO, expecting STATUS_PENDING, got %08X\n", status));
}
return status;
}
NTSTATUS
SdbusFdoDisarmWake(
PFDO_EXTENSION FdoExtension
)
/*++
Routine Description:
This routine is called to disable the controller for wake.
Arguments:
FdoExtension - device extension of the controller
Return Value:
status
--*/
{
WAKESTATE oldWakeState;
oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
WAKESTATE_WAITING_CANCELLED, WAKESTATE_WAITING);
DebugPrint((SDBUS_DEBUG_POWER, "fdo %x DisarmWake: prevState %s\n",
FdoExtension->DeviceObject, WAKESTATE_STRING(oldWakeState)));
if (oldWakeState != WAKESTATE_WAITING) {
oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
WAKESTATE_ARMING_CANCELLED, WAKESTATE_ARMED);
if (oldWakeState != WAKESTATE_ARMED) {
return STATUS_UNSUCCESSFUL;
}
}
if (oldWakeState == WAKESTATE_ARMED) {
IoCancelIrp(FdoExtension->WaitWakeIrp);
//
// Now that we've cancelled the IRP, try to give back ownership
// to the completion routine by restoring the WAKESTATE_ARMED state
//
oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
WAKESTATE_ARMED, WAKESTATE_ARMING_CANCELLED);
if (oldWakeState == WAKESTATE_COMPLETING) {
//
// We didn't give control back of the IRP in time, we we own it now
//
IoCompleteRequest(FdoExtension->WaitWakeIrp, IO_NO_INCREMENT);
}
}
return STATUS_SUCCESS;
}
NTSTATUS
SdbusFdoCheckForIdle(
IN PFDO_EXTENSION FdoExtension
)
{
POWER_STATE powerState;
NTSTATUS status;
//
// Make sure all sockets are empty
//
#if 0
for (socket = FdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
if (IsCardInSocket(socket)) {
return STATUS_UNSUCCESSFUL;
}
}
#endif
//
// Arm for wakeup
//
status = SdbusFdoArmForWake(FdoExtension);
if (!NT_SUCCESS(status)) {
return status;
}
SetDeviceFlag(FdoExtension, SDBUS_FDO_WAKE_BY_CD);
powerState.DeviceState = PowerDeviceD3;
PoRequestPowerIrp(FdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL);
return STATUS_SUCCESS;
}
/**************************************************************************
PDO ROUTINES
**************************************************************************/
NTSTATUS
SdbusPdoWaitWake(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp,
OUT BOOLEAN *CompleteIrp
)
/*++
Routine Description
Handles WAIT_WAKE for the given pc-card.
Arguments
Pdo - Pointer to the device object for the pc-card
Irp - The IRP_MN_WAIT_WAKE Irp
CompleteIrp - This routine will set this to TRUE if the IRP should be
completed after this is called and FALSE if it should not be
touched
Return Value
STATUS_PENDING - Wait wake is pending
STATUS_SUCCESS - Wake is already asserted, wait wake IRP is completed
in this case
Any other status - Error
--*/
{
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
PFDO_EXTENSION fdoExtension = pdoExtension->FdoExtension;
NTSTATUS status;
*CompleteIrp = FALSE;
if ((pdoExtension->DeviceCapabilities.DeviceWake == PowerDeviceUnspecified) ||
(pdoExtension->DeviceCapabilities.DeviceWake < pdoExtension->DevicePowerState)) {
//
// Either we don't support wake at all OR the current device power state
// of the PC-Card doesn't support wake
//
return STATUS_INVALID_DEVICE_STATE;
}
if (pdoExtension->Flags & SDBUS_DEVICE_WAKE_PENDING) {
//
// A WAKE is already pending
//
return STATUS_DEVICE_BUSY;
}
status = SdbusFdoArmForWake(fdoExtension);
if (!NT_SUCCESS(status)) {
return status;
}
//for the time being, expect STATUS_PENDING from FdoArmForWake
ASSERT(status == STATUS_PENDING);
//
// Parent has one (more) waiter..
//
InterlockedIncrement(&fdoExtension->ChildWaitWakeCount);
//for testing, make sure there is only one waiter
ASSERT (fdoExtension->ChildWaitWakeCount == 1);
pdoExtension->WaitWakeIrp = Irp;
pdoExtension->Flags |= SDBUS_DEVICE_WAKE_PENDING;
//
// Set Ring enable/cstschg for the card here..
//
// (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, TRUE);
//
// PCI currently does not do anything with a WW irp for a cardbus PDO. So we hack around
// this here by not passing the irp down. Instead it is held pending here, so we can
// set a cancel routine just like the read PDO driver would. If PCI were to do something
// with the irp, we could code something like the following:
//
// if (IsCardBusCard(pdoExtension)) {
// IoSetCompletionRoutine(Irp, SdbusPdoWaitWakeCompletion, pdoExtension,TRUE,TRUE,TRUE);
// IoCopyCurrentIrpStackLocationToNext(Irp);
// status = IoCallDriver (pdoExtension->LowerDevice, Irp);
// ASSERT (status == STATUS_PENDING);
// return status;
// }
IoMarkIrpPending(Irp);
//
// Allow IRP to be cancelled..
//
IoSetCancelRoutine(Irp, SdbusPdoWaitWakeCancelRoutine);
IoSetCompletionRoutine(Irp,
SdbusPdoWaitWakeCompletion,
pdoExtension,
TRUE,
TRUE,
TRUE);
return STATUS_PENDING;
}
NTSTATUS
SdbusPdoWaitWakeCompletion(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp,
IN PPDO_EXTENSION PdoExtension
)
/*++
Routine Description
Completion routine called when a pending IRP_MN_WAIT_WAKE Irp completes
Arguments
Pdo - Pointer to the physical device object for the pc-card
Irp - Pointer to the wait wake IRP
PdoExtension - Pointer to the device extension for the Pdo
Return Value
Status from the IRP
--*/
{
PFDO_EXTENSION fdoExtension = PdoExtension->FdoExtension;
DebugPrint((SDBUS_DEBUG_POWER, "pdo %08x irp %08x --> WaitWakeCompletion\n", Pdo, Irp));
ASSERT (PdoExtension->Flags & SDBUS_DEVICE_WAKE_PENDING);
PdoExtension->Flags &= ~SDBUS_DEVICE_WAKE_PENDING;
PdoExtension->WaitWakeIrp = NULL;
//
// Reset ring enable/cstschg
//
// (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, PdoExtension, FALSE);
ASSERT (fdoExtension->ChildWaitWakeCount > 0);
InterlockedDecrement(&fdoExtension->ChildWaitWakeCount);
//
// Wake completed
//
InterlockedDecrement(&PdoExtension->DeletionLock);
return Irp->IoStatus.Status;
}
VOID
SdbusPdoWaitWakeCancelRoutine(
IN PDEVICE_OBJECT Pdo,
IN OUT PIRP Irp
)
/*++
Routine Description:
Cancel an outstanding (pending) WAIT_WAKE Irp.
Note: The CancelSpinLock is held on entry
Arguments:
Pdo - Pointer to the physical device object for the pc-card
on which the WAKE is pending
Irp - Pointer to the WAIT_WAKE Irp to be cancelled
Return Value
None
--*/
{
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
PFDO_EXTENSION fdoExtension = pdoExtension->FdoExtension;
DebugPrint((SDBUS_DEBUG_POWER, "pdo %08x irp %08x --> WaitWakeCancelRoutine\n", Pdo, Irp));
IoReleaseCancelSpinLock(Irp->CancelIrql);
if (pdoExtension->WaitWakeIrp == NULL) {
//
// Wait wake already completed/cancelled
//
return;
}
pdoExtension->Flags &= ~SDBUS_DEVICE_WAKE_PENDING;
pdoExtension->WaitWakeIrp = NULL;
//
// Reset ring enable, disabling wake..
//
// (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, FALSE);
//
// Since this is cancelled, see if parent's wait wake
// needs to be cancelled too.
// First, decrement the number of child waiters..
//
ASSERT (fdoExtension->ChildWaitWakeCount > 0);
if (InterlockedDecrement(&fdoExtension->ChildWaitWakeCount) == 0) {
//
// No more waiters.. cancel the parent's wake IRP
//
ASSERT(fdoExtension->WaitWakeIrp);
if (fdoExtension->WaitWakeIrp) {
IoCancelIrp(fdoExtension->WaitWakeIrp);
}
}
InterlockedDecrement(&pdoExtension->DeletionLock);
//
// Complete the IRP
//
Irp->IoStatus.Information = 0;
//
// Is this necessary?
//
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}