1135 lines
29 KiB
C
1135 lines
29 KiB
C
/*++
|
||
|
||
Copyright (c) 1997-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
pdopower.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code to handle
|
||
IRP_MJ_POWER dispatches for PDOs
|
||
enumerated by the PCMCIA bus driver
|
||
|
||
|
||
Authors:
|
||
|
||
Ravisankar Pudipeddi (ravisp) May 30, 1997
|
||
Neil Sandlin (neilsa) June 1 1999
|
||
|
||
Environment:
|
||
|
||
Kernel mode only
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
Neil Sandlin (neilsa) 04-Mar-1999
|
||
Made device power a state machine
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
//
|
||
// Internal References
|
||
//
|
||
|
||
NTSTATUS
|
||
PcmciaPdoWaitWake(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN PIRP Irp,
|
||
OUT BOOLEAN *CompleteIrp
|
||
);
|
||
|
||
VOID
|
||
PcmciaPdoWaitWakeCancelRoutine(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN OUT PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaSetPdoPowerState(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN OUT PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaSetPdoSystemPowerState(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN OUT PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaPdoPowerWorker(
|
||
IN PVOID Context,
|
||
IN NTSTATUS DeferredStatus
|
||
);
|
||
|
||
VOID
|
||
MoveToNextPdoPowerWorkerState(
|
||
PPDO_EXTENSION pdoExtension
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaPdoPowerSentIrpComplete(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaPdoPowerCompletion(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaPdoCompletePowerIrp(
|
||
IN PPDO_EXTENSION pdoExtension,
|
||
IN PIRP Irp,
|
||
IN NTSTATUS status
|
||
);
|
||
|
||
//
|
||
//
|
||
//
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaPdoPowerDispatch(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles power requests
|
||
for the PDOs.
|
||
|
||
Arguments:
|
||
|
||
Pdo - pointer to the physical device object
|
||
Irp - pointer to the io request packet
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
|
||
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
||
|
||
if(IsDevicePhysicallyRemoved(pdoExtension) || IsDeviceDeleted(pdoExtension)) {
|
||
// couldn't aquire RemoveLock - we're in the process of being removed - abort
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
PoStartNextPowerIrp( Irp );
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
return status;
|
||
}
|
||
|
||
InterlockedIncrement(&pdoExtension->DeletionLock);
|
||
|
||
|
||
switch (irpStack->MinorFunction) {
|
||
|
||
case IRP_MN_SET_POWER: {
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> IRP_MN_SET_POWER\n", Pdo, Irp));
|
||
DebugPrint((PCMCIA_DEBUG_POWER, " (%s%x, context %x)\n",
|
||
(irpStack->Parameters.Power.Type == SystemPowerState) ?
|
||
"S":
|
||
((irpStack->Parameters.Power.Type == DevicePowerState) ?
|
||
"D" :
|
||
"Unknown"),
|
||
irpStack->Parameters.Power.State,
|
||
irpStack->Parameters.Power.SystemContext
|
||
));
|
||
|
||
status = PcmciaSetPdoPowerState(Pdo, Irp);
|
||
break;
|
||
}
|
||
case IRP_MN_QUERY_POWER: {
|
||
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> IRP_MN_QUERY_POWER\n", Pdo, Irp));
|
||
DebugPrint((PCMCIA_DEBUG_POWER, " (%s%x, context %x)\n",
|
||
(irpStack->Parameters.Power.Type == SystemPowerState) ?
|
||
"S":
|
||
((irpStack->Parameters.Power.Type == DevicePowerState) ?
|
||
"D" :
|
||
"Unknown"),
|
||
irpStack->Parameters.Power.State,
|
||
irpStack->Parameters.Power.SystemContext
|
||
));
|
||
|
||
status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, STATUS_SUCCESS);
|
||
break;
|
||
}
|
||
|
||
case IRP_MN_WAIT_WAKE: {
|
||
|
||
BOOLEAN completeIrp;
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> IRP_MN_WAIT_WAKE\n", Pdo, Irp));
|
||
//
|
||
// Should not have a wake pending already
|
||
//
|
||
ASSERT (!(((PPDO_EXTENSION)Pdo->DeviceExtension)->Flags & PCMCIA_DEVICE_WAKE_PENDING));
|
||
|
||
status = PcmciaPdoWaitWake(Pdo, Irp, &completeIrp);
|
||
|
||
if (completeIrp) {
|
||
InterlockedDecrement(&pdoExtension->DeletionLock);
|
||
PoStartNextPowerIrp(Irp);
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
}
|
||
break;
|
||
}
|
||
|
||
default: {
|
||
//
|
||
// Unhandled minor function
|
||
//
|
||
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
||
|
||
status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, Irp->IoStatus.Status);
|
||
}
|
||
}
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x <-- %08x\n", Pdo, Irp, status));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaPdoWaitWake(
|
||
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;
|
||
PSOCKET socket = pdoExtension->Socket;
|
||
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
||
NTSTATUS status;
|
||
|
||
*CompleteIrp = FALSE;
|
||
|
||
ASSERT (socket != NULL);
|
||
|
||
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 & PCMCIA_DEVICE_WAKE_PENDING) {
|
||
//
|
||
// A WAKE is already pending
|
||
//
|
||
return STATUS_DEVICE_BUSY;
|
||
}
|
||
|
||
status = PcmciaFdoArmForWake(socket->DeviceExtension);
|
||
|
||
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 |= PCMCIA_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, PcmciaPdoWaitWakeCompletion, 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, PcmciaPdoWaitWakeCancelRoutine);
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
PcmciaPdoWaitWakeCompletion,
|
||
pdoExtension,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaPdoWaitWakeCompletion(
|
||
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
|
||
|
||
--*/
|
||
{
|
||
PSOCKET socket = PdoExtension->Socket;
|
||
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> WaitWakeCompletion\n", Pdo, Irp));
|
||
|
||
ASSERT (PdoExtension->Flags & PCMCIA_DEVICE_WAKE_PENDING);
|
||
|
||
PdoExtension->Flags &= ~PCMCIA_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
|
||
PcmciaPdoWaitWakeCancelRoutine(
|
||
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;
|
||
PSOCKET socket = pdoExtension->Socket;
|
||
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
||
|
||
DebugPrint((PCMCIA_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 &= ~PCMCIA_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);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaSetPdoPowerState(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN OUT PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Dispatches the IRP based on whether a system power state
|
||
or device power state transition is requested
|
||
|
||
Arguments
|
||
|
||
Pdo - Pointer to the physical device object for the pc-card
|
||
Irp - Pointer to the Irp for the power dispatch
|
||
|
||
Return value
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
||
PSOCKET socket = pdoExtension->Socket;
|
||
PFDO_EXTENSION fdoExtension=socket->DeviceExtension;
|
||
NTSTATUS status;
|
||
|
||
PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension);
|
||
|
||
//
|
||
// Don't handle any power requests for dead pdos
|
||
//
|
||
if (IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE)) {
|
||
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
|
||
//
|
||
// Card probably removed..
|
||
//
|
||
|
||
InterlockedDecrement(&pdoExtension->DeletionLock);
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
Irp->IoStatus.Status = status;
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x comp %08x\n", Pdo, Irp, status));
|
||
PoStartNextPowerIrp(Irp);
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return status;
|
||
}
|
||
|
||
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
|
||
|
||
|
||
switch (irpStack->Parameters.Power.Type) {
|
||
|
||
case DevicePowerState:
|
||
|
||
PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension);
|
||
|
||
if (fdoExtension->DevicePowerState != PowerDeviceD0) {
|
||
|
||
IoMarkIrpPending(Irp);
|
||
status = STATUS_PENDING;
|
||
InsertTailList(&fdoExtension->PdoPowerRetryList,
|
||
(PLIST_ENTRY) Irp->Tail.Overlay.DriverContext);
|
||
|
||
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
|
||
} else {
|
||
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
|
||
|
||
status = PcmciaSetPdoDevicePowerState(Pdo, Irp);
|
||
}
|
||
break;
|
||
|
||
case SystemPowerState:
|
||
status = PcmciaSetPdoSystemPowerState(Pdo, Irp);
|
||
break;
|
||
|
||
default:
|
||
status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, Irp->IoStatus.Status);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaSetPdoDevicePowerState(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN OUT PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Handles the device power state transition for the given pc-card.
|
||
If the state corresponds to a power-up, the parent for this pc-card
|
||
is requested to be powered up first. Similarily if this is a power-down
|
||
the parent is notified so that it may power down if all the children
|
||
are powered down.
|
||
|
||
Arguments
|
||
|
||
Pdo - Pointer to the physical device object for the pc-card
|
||
Irp - Irp for the system state transition
|
||
|
||
Return value
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
||
PSOCKET socket = pdoExtension->Socket;
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
DEVICE_POWER_STATE newDevicePowerState;
|
||
NTSTATUS status;
|
||
BOOLEAN setPowerRequest;
|
||
|
||
newDevicePowerState = irpStack->Parameters.Power.State.DeviceState;
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x transitioning D state %d => %d\n",
|
||
Pdo, pdoExtension->DevicePowerState, newDevicePowerState));
|
||
|
||
setPowerRequest = FALSE;
|
||
|
||
if (newDevicePowerState == PowerDeviceD0 ||
|
||
newDevicePowerState == PowerDeviceD1 ||
|
||
newDevicePowerState == PowerDeviceD2) {
|
||
|
||
if (pdoExtension->DevicePowerState == PowerDeviceD3) {
|
||
// D3 --> D0, D1 or D2 .. Wake up
|
||
setPowerRequest = TRUE;
|
||
SetDeviceFlag(pdoExtension, PCMCIA_POWER_WORKER_POWERUP);
|
||
} else {
|
||
//
|
||
// Nothing to do here...
|
||
//
|
||
|
||
}
|
||
} else { /* newDevicePowerState == D3 */
|
||
if (pdoExtension->DevicePowerState != PowerDeviceD3) {
|
||
//
|
||
// We need to power down now.
|
||
//
|
||
setPowerRequest=TRUE;
|
||
ResetDeviceFlag(pdoExtension, PCMCIA_POWER_WORKER_POWERUP);
|
||
}
|
||
|
||
}
|
||
|
||
if (setPowerRequest) {
|
||
if (pdoExtension->DevicePowerState == PowerDeviceD0) {
|
||
//
|
||
// Getting out of D0 - Call PoSetPowerState first
|
||
//
|
||
POWER_STATE newPowerState;
|
||
|
||
newPowerState.DeviceState = newDevicePowerState;
|
||
|
||
PoSetPowerState(Pdo,
|
||
DevicePowerState,
|
||
newPowerState);
|
||
}
|
||
|
||
ASSERT(pdoExtension->PowerWorkerState == PPW_Stopped);
|
||
pdoExtension->PowerWorkerState = PPW_InitialState;
|
||
pdoExtension->PendingPowerIrp = Irp;
|
||
|
||
status = PcmciaPdoPowerWorker(pdoExtension, STATUS_SUCCESS);
|
||
|
||
} else {
|
||
status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, STATUS_SUCCESS);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaPdoPowerWorker(
|
||
IN PVOID Context,
|
||
IN NTSTATUS status
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
State machine for executing the requested DevicePowerState change.
|
||
|
||
Arguments
|
||
|
||
Context - pdoExtension for the device
|
||
DeferredStatus - status from last deferred operation
|
||
|
||
Return Value
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PPDO_EXTENSION pdoExtension = Context;
|
||
PSOCKET socket = pdoExtension->Socket;
|
||
PIRP Irp;
|
||
UCHAR CurrentState = pdoExtension->PowerWorkerState;
|
||
ULONG DelayTime = 0;
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x power worker - %s\n", pdoExtension->DeviceObject,
|
||
PDO_POWER_WORKER_STRING(CurrentState)));
|
||
|
||
MoveToNextPdoPowerWorkerState(pdoExtension);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
//
|
||
// An error occurred previously. Skip to the end of the sequence
|
||
//
|
||
while((CurrentState != PPW_Exit) && (CurrentState != PPW_Stopped)) {
|
||
CurrentState = pdoExtension->PowerWorkerState;
|
||
MoveToNextPdoPowerWorkerState(pdoExtension);
|
||
}
|
||
}
|
||
|
||
switch(CurrentState) {
|
||
|
||
|
||
case PPW_InitialState:
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
|
||
case PPW_PowerUp:
|
||
status = PcmciaRequestSocketPower(pdoExtension, PcmciaPdoPowerWorker);
|
||
break;
|
||
|
||
case PPW_PowerUpComplete:
|
||
if (!NT_SUCCESS(status)) {
|
||
PcmciaReleaseSocketPower(pdoExtension, NULL);
|
||
}
|
||
break;
|
||
|
||
|
||
case PPW_PowerDown:
|
||
status = PcmciaReleaseSocketPower(pdoExtension, PcmciaPdoPowerWorker);
|
||
break;
|
||
|
||
|
||
case PPW_CardBusRefresh:
|
||
//
|
||
// Make sure the cardbus card is really working
|
||
//
|
||
status = PcmciaConfigureCardBusCard(pdoExtension);
|
||
|
||
if (NT_SUCCESS(status) && pdoExtension->WaitWakeIrp) {
|
||
//
|
||
// Make sure stuff like PME_EN is on
|
||
//
|
||
(*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, TRUE);
|
||
}
|
||
break;
|
||
|
||
|
||
case PPW_SendIrpDown:
|
||
//
|
||
// We're going to send the IRP down. Set completion routine
|
||
// and copy the stack
|
||
//
|
||
if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) {
|
||
IoMarkIrpPending(Irp);
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
IoSetCompletionRoutine(Irp,
|
||
PcmciaPdoPowerSentIrpComplete,
|
||
pdoExtension,
|
||
TRUE, TRUE, TRUE);
|
||
|
||
status = PoCallDriver(pdoExtension->LowerDevice, Irp);
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x sent irp returns %08x\n", pdoExtension->DeviceObject, Irp, status));
|
||
ASSERT(NT_SUCCESS(status));
|
||
status = STATUS_PENDING;
|
||
}
|
||
break;
|
||
|
||
|
||
case PPW_CardBusDelay:
|
||
//
|
||
// Make sure the cardbus card is really working
|
||
//
|
||
{
|
||
UCHAR BaseClass;
|
||
GetPciConfigSpace(pdoExtension, CFGSPACE_CLASSCODE_BASECLASS, &BaseClass, 1)
|
||
if (BaseClass == PCI_CLASS_SIMPLE_COMMS_CTLR) {
|
||
//
|
||
// Wait for modem to warm up
|
||
//
|
||
DelayTime = CBModemReadyDelay;
|
||
}
|
||
}
|
||
break;
|
||
|
||
|
||
case PPW_16BitConfigure:
|
||
|
||
if (IsDeviceStarted(pdoExtension)) {
|
||
|
||
status = PcmciaConfigurePcCard(pdoExtension, PcmciaPdoPowerWorker);
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x 16bit configure returns %08x\n", pdoExtension->DeviceObject, status));
|
||
|
||
}
|
||
break;
|
||
|
||
|
||
case PPW_Exit:
|
||
if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) {
|
||
//
|
||
// This is the IRP (for the pdo) that originally caused us to power up the parent
|
||
// Complete it now
|
||
//
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
BOOLEAN callPoSetPowerState;
|
||
|
||
callPoSetPowerState = TRUE;
|
||
|
||
Irp->IoStatus.Information = irpStack->Parameters.Power.State.DeviceState;
|
||
|
||
if (irpStack->Parameters.Power.Type == DevicePowerState) {
|
||
|
||
if (pdoExtension->DevicePowerState == PowerDeviceD0) {
|
||
//
|
||
// PoSetPowerState is called before we power down
|
||
//
|
||
callPoSetPowerState = FALSE;
|
||
}
|
||
|
||
if (pdoExtension->DevicePowerState != irpStack->Parameters.Power.State.DeviceState) {
|
||
|
||
DebugPrint ((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x transition D state complete: %d => %d\n",
|
||
pdoExtension->DeviceObject, Irp, pdoExtension->DevicePowerState, irpStack->Parameters.Power.State.DeviceState));
|
||
|
||
pdoExtension->DevicePowerState = (SYSTEM_POWER_STATE)Irp->IoStatus.Information;
|
||
}
|
||
}
|
||
|
||
if (callPoSetPowerState) {
|
||
//
|
||
// we didn't get out of device D0 state. calling PoSetPowerState now
|
||
//
|
||
PoSetPowerState (
|
||
pdoExtension->DeviceObject,
|
||
irpStack->Parameters.Power.Type,
|
||
irpStack->Parameters.Power.State
|
||
);
|
||
}
|
||
|
||
} else {
|
||
|
||
DebugPrint ((PCMCIA_DEBUG_FAIL,"PDO Ext 0x%x failed power Irp 0x%x. status = 0x%x\n", pdoExtension, Irp, status));
|
||
|
||
if (status == STATUS_NO_SUCH_DEVICE) {
|
||
PFDO_EXTENSION fdoExtension=socket->DeviceExtension;
|
||
|
||
SetSocketFlag(socket, SOCKET_CARD_STATUS_CHANGE);
|
||
IoInvalidateDeviceRelations(fdoExtension->Pdo, BusRelations);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, complete the irp
|
||
//
|
||
|
||
pdoExtension->PendingPowerIrp = NULL;
|
||
|
||
Irp->IoStatus.Status = status;
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x comp %08x\n", pdoExtension->DeviceObject, Irp, Irp->IoStatus.Status));
|
||
InterlockedDecrement(&pdoExtension->DeletionLock);
|
||
PoStartNextPowerIrp (Irp);
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
}
|
||
break;
|
||
|
||
|
||
case PPW_Stopped:
|
||
return status;
|
||
|
||
default:
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
if (status == STATUS_PENDING) {
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x power worker exit %08x\n", pdoExtension->DeviceObject, status));
|
||
//
|
||
// Current action calls us back
|
||
//
|
||
if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
return status;
|
||
}
|
||
//
|
||
// Not done yet. Recurse or call timer
|
||
//
|
||
|
||
if (DelayTime) {
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x power worker delay type %s, %d usec\n", pdoExtension->DeviceObject,
|
||
(KeGetCurrentIrql() < DISPATCH_LEVEL) ? "Wait" : "Timer",
|
||
DelayTime));
|
||
|
||
if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
|
||
PcmciaWait(DelayTime);
|
||
} else {
|
||
LARGE_INTEGER dueTime;
|
||
//
|
||
// Running on a DPC, kick of a kernel timer
|
||
//
|
||
|
||
pdoExtension->PowerWorkerDpcStatus = status;
|
||
dueTime.QuadPart = -((LONG) DelayTime*10);
|
||
KeSetTimer(&pdoExtension->PowerWorkerTimer, dueTime, &pdoExtension->PowerWorkerDpc);
|
||
|
||
//
|
||
// We will reenter on timer dpc
|
||
//
|
||
if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
return STATUS_PENDING;
|
||
}
|
||
}
|
||
//
|
||
// recurse
|
||
//
|
||
return (PcmciaPdoPowerWorker(pdoExtension, status));
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaPdoPowerSentIrpComplete(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
This is the completion routine for the device power IRPs sent
|
||
by PCMCIA to the underlying PCI PDO for cardbus cards.
|
||
All this does currently is return STATUS_MORE_PROCESSING_REQUIRED
|
||
to indicate that we'll complete the Irp later.
|
||
|
||
Arguments
|
||
|
||
Pdo - Pointer to device object for the cardbus card
|
||
Irp - Pointer to the IRP
|
||
Context - Unreferenced
|
||
|
||
Return Value
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED
|
||
|
||
--*/
|
||
{
|
||
PPDO_EXTENSION pdoExtension = Context;
|
||
|
||
#if !(DBG)
|
||
UNREFERENCED_PARAMETER (Pdo);
|
||
#endif
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x sent irp complete %08x\n", Pdo, Irp, Irp->IoStatus.Status));
|
||
|
||
pdoExtension->PowerWorkerDpcStatus = Irp->IoStatus.Status;
|
||
|
||
KeInsertQueueDpc(&pdoExtension->PowerWorkerDpc, NULL, NULL);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
PcmciaPdoPowerWorkerDpc(
|
||
IN PKDPC Dpc,
|
||
IN PVOID Context,
|
||
IN PVOID SystemArgument1,
|
||
IN PVOID SystemArgument2
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
This is the completion routine for socket power requests coming
|
||
from the PdoPowerWorker.
|
||
|
||
Arguments
|
||
|
||
|
||
Return Value
|
||
|
||
|
||
--*/
|
||
{
|
||
PPDO_EXTENSION pdoExtension = Context;
|
||
NTSTATUS status;
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x pdo power worker dpc\n", pdoExtension->DeviceObject));
|
||
|
||
status = PcmciaPdoPowerWorker(pdoExtension, pdoExtension->PowerWorkerDpcStatus);
|
||
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x pdo power worker dpc exit %08x\n", pdoExtension->DeviceObject, status));
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
MoveToNextPdoPowerWorkerState(
|
||
PPDO_EXTENSION pdoExtension
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
State machine for executing the requested DevicePowerState change.
|
||
|
||
Arguments
|
||
|
||
Context - pdoExtension for the device
|
||
DeferredStatus - status from last deferred operation
|
||
|
||
Return Value
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
static UCHAR PowerCardBusUpSequence[] = {PPW_CardBusRefresh,
|
||
PPW_SendIrpDown,
|
||
PPW_CardBusDelay,
|
||
PPW_Exit,
|
||
PPW_Stopped};
|
||
|
||
static UCHAR PowerCardBusDownSequence[] = {PPW_SendIrpDown,
|
||
PPW_Exit,
|
||
PPW_Stopped};
|
||
|
||
static UCHAR Power16BitUpSequence[] = {PPW_PowerUp,
|
||
PPW_PowerUpComplete,
|
||
PPW_16BitConfigure,
|
||
PPW_Exit,
|
||
PPW_Stopped};
|
||
|
||
static UCHAR Power16BitDownSequence[] = {PPW_PowerDown,
|
||
PPW_Exit,
|
||
PPW_Stopped};
|
||
|
||
if (pdoExtension->PowerWorkerState == PPW_InitialState) {
|
||
//
|
||
// Initialize sequence and phase
|
||
//
|
||
pdoExtension->PowerWorkerPhase = 0;
|
||
|
||
pdoExtension->PowerWorkerSequence =
|
||
IsCardBusCard(pdoExtension) ?
|
||
(IsDeviceFlagSet(pdoExtension, PCMCIA_POWER_WORKER_POWERUP) ?
|
||
PowerCardBusUpSequence : PowerCardBusDownSequence)
|
||
:
|
||
(IsDeviceFlagSet(pdoExtension, PCMCIA_POWER_WORKER_POWERUP) ?
|
||
Power16BitUpSequence : Power16BitDownSequence);
|
||
}
|
||
|
||
//
|
||
// The next state is pointed to by the current phase
|
||
//
|
||
pdoExtension->PowerWorkerState =
|
||
pdoExtension->PowerWorkerSequence[ pdoExtension->PowerWorkerPhase ];
|
||
|
||
//
|
||
// Increment the phase, but not past the end of the sequence
|
||
//
|
||
if (pdoExtension->PowerWorkerState != PPW_Stopped) {
|
||
pdoExtension->PowerWorkerPhase++;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaSetPdoSystemPowerState(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN OUT PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Handles the system power state transition for the given pc-card.
|
||
|
||
Arguments
|
||
|
||
Pdo - Pointer to the physical device object for the pc-card
|
||
Irp - Irp for the system state transition
|
||
|
||
Return value
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
SYSTEM_POWER_STATE systemPowerState;
|
||
|
||
|
||
systemPowerState = irpStack->Parameters.Power.State.SystemState;
|
||
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x transitioning S state %d => %d\n",
|
||
Pdo, pdoExtension->SystemPowerState, systemPowerState));
|
||
|
||
pdoExtension->SystemPowerState = systemPowerState;
|
||
//
|
||
// We are done.
|
||
//
|
||
return PcmciaPdoCompletePowerIrp(pdoExtension, Irp, STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaPdoCompletePowerIrp(
|
||
IN PPDO_EXTENSION pdoExtension,
|
||
IN PIRP Irp,
|
||
IN NTSTATUS status
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Completion routine for the Power Irp directed to the PDO of the
|
||
pc-card.
|
||
|
||
|
||
Arguments
|
||
|
||
DeviceObject - Pointer to the PDO for the pc-card
|
||
Irp - Irp that needs to be completed
|
||
|
||
Return Value
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
if (IsCardBusCard(pdoExtension)) {
|
||
//
|
||
// Pass irp down the stack
|
||
//
|
||
InterlockedDecrement(&pdoExtension->DeletionLock);
|
||
PoStartNextPowerIrp(Irp);
|
||
IoSkipCurrentIrpStackLocation(Irp);
|
||
status = PoCallDriver(pdoExtension->LowerDevice, Irp);
|
||
} else {
|
||
//
|
||
// Complete the irp for R2 cards
|
||
//
|
||
InterlockedDecrement(&pdoExtension->DeletionLock);
|
||
Irp->IoStatus.Status = status;
|
||
PoStartNextPowerIrp(Irp);
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
}
|
||
return status;
|
||
}
|