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.
1135 lines
31 KiB
1135 lines
31 KiB
/*++
|
|
|
|
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
|
|
//
|
|
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;
|
|
|
|
|
|
//
|
|
// R2 card states
|
|
//
|
|
|
|
case PPW_PowerUp:
|
|
status = PcmciaRequestSocketPower(pdoExtension, PcmciaPdoPowerWorker);
|
|
break;
|
|
|
|
|
|
case PPW_PowerDown:
|
|
if (0 == (pdoExtension->Flags & PCMCIA_DEVICE_WAKE_PENDING)) {
|
|
status = PcmciaReleaseSocketPower(pdoExtension, PcmciaPdoPowerWorker);
|
|
}
|
|
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;
|
|
|
|
|
|
//
|
|
// Cardbus card states
|
|
//
|
|
|
|
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 = PCMCIA_CB_MODEM_READY_DELAY;
|
|
}
|
|
}
|
|
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_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;
|
|
}
|