windows-nt/Source/XPSP1/NT/base/busdrv/pccard/pcmcibus/fdopower.c
2020-09-26 16:20:57 +08:00

2052 lines
59 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
fdopower.c
Abstract:
This module contains code to handle
IRP_MJ_POWER dispatches for PCMCIA controllers
Contains support routines for pc-card power management
Authors:
Ravisankar Pudipeddi (ravisp) May 30, 1997
Neil Sandlin (neilsa) June 1, 1999
Environment:
Kernel mode only
Notes:
Revision History:
Neil Sandlin (neilsa) April 16, 1999
- split setpower into device and system, fixed synchronization
--*/
#include "pch.h"
//
// Internal References
//
NTSTATUS
PcmciaFdoWaitWake(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp
);
NTSTATUS
PcmciaFdoWaitWakeIoCompletion(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
PcmciaFdoSaveControllerContext(
IN PFDO_EXTENSION FdoExtension
);
NTSTATUS
PcmciaFdoRestoreControllerContext(
IN PFDO_EXTENSION FdoExtension
);
NTSTATUS
PcmciaFdoSaveSocketContext(
IN PSOCKET Socket
);
NTSTATUS
PcmciaFdoRestoreSocketContext(
IN PSOCKET Socket
);
NTSTATUS
PcmciaSetFdoPowerState(
IN PDEVICE_OBJECT Fdo,
IN OUT PIRP Irp
);
NTSTATUS
PcmciaSetFdoSystemPowerState(
IN PDEVICE_OBJECT Fdo,
IN OUT PIRP Irp
);
VOID
PcmciaFdoSystemPowerDeviceIrpComplete(
IN PDEVICE_OBJECT Fdo,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
);
NTSTATUS
PcmciaSetFdoDevicePowerState(
IN PDEVICE_OBJECT Fdo,
IN OUT PIRP Irp
);
NTSTATUS
PcmciaFdoPowerWorker (
IN PVOID Context,
IN NTSTATUS Status
);
NTSTATUS
PcmciaFdoDevicePowerCompletion(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
IN PVOID Context
);
//
//
//
NTSTATUS
PcmciaFdoPowerDispatch(
IN PDEVICE_OBJECT Fdo,
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
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
switch (irpStack->MinorFunction) {
case IRP_MN_SET_POWER: {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x --> IRP_MN_SET_POWER\n", Fdo, 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 = PcmciaSetFdoPowerState(Fdo, Irp);
break;
}
case IRP_MN_QUERY_POWER: {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x --> IRP_MN_QUERY_POWER\n", Fdo, 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
));
//
// Let the pdo handle it
//
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
status = PoCallDriver(fdoExtension->LowerDevice, Irp);
break;
}
case IRP_MN_WAIT_WAKE: {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x --> IRP_MN_WAIT_WAKE\n", Fdo, Irp));
status = PcmciaFdoWaitWake(Fdo, Irp);
break;
}
default: {
DebugPrint((PCMCIA_DEBUG_POWER, "FdoPowerDispatch: Unhandled Irp %x received for 0x%08x\n",
Irp,
Fdo));
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
status = PoCallDriver(fdoExtension->LowerDevice, Irp);
break;
}
}
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x <-- %08x\n", Fdo, Irp, status));
return status;
}
/**************************************************************************
WAKE ROUTINES
**************************************************************************/
NTSTATUS
PcmciaFdoWaitWake(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp
)
/*++
Routine Description
Handles WAIT_WAKE for the given pcmcia controller
Arguments
Pdo - Pointer to the functional device object for the pcmcia 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((PCMCIA_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,
PcmciaFdoWaitWakeIoCompletion,
Fdo,
TRUE,
TRUE,
TRUE);
//
// now pass this down to the lower driver..
//
PoCallDriver(fdoExtension->LowerDevice, Irp);
return STATUS_PENDING;
}
NTSTATUS
PcmciaFdoWaitWakeIoCompletion(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
Completion routine for the IRP_MN_WAIT_WAKE request for this
pcmcia controller. This is called when the WAIT_WAKE IRP is
completed by the lower driver (PCI/ACPI) indicating either that
1. PCMCIA 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 pcmcia 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((PCMCIA_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 (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_WAKE_BY_CD)) {
POWER_STATE powerState;
ResetFdoFlag(fdoExtension, PCMCIA_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((PCMCIA_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
//
PcmciaPdoWaitWakeCompletion(pdo, finishedIrp, pdoExtension);
IoCompleteRequest(finishedIrp, IO_NO_INCREMENT);
}
}
PoStartNextPowerIrp(Irp);
}
return Irp->IoStatus.Status;
}
VOID
PcmciaFdoWaitWakePoCompletion(
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 PCMCIA 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((PCMCIA_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
PcmciaFdoArmForWake(
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((PCMCIA_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,
PcmciaFdoWaitWakePoCompletion,
NULL,
NULL);
if (!NT_SUCCESS(status)) {
FdoExtension->WaitWakeState = WAKESTATE_DISARMED;
DebugPrint((PCMCIA_DEBUG_POWER, "WaitWake to FDO, expecting STATUS_PENDING, got %08X\n", status));
}
return status;
}
NTSTATUS
PcmciaFdoDisarmWake(
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((PCMCIA_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
PcmciaFdoCheckForIdle(
IN PFDO_EXTENSION FdoExtension
)
{
POWER_STATE powerState;
NTSTATUS status;
PSOCKET socket;
if (!(PcmciaPowerPolicy & PCMCIA_PP_D3_ON_IDLE)) {
return STATUS_SUCCESS;
}
//
// Make sure all sockets are empty
//
for (socket = FdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
if (IsCardInSocket(socket)) {
return STATUS_UNSUCCESSFUL;
}
}
//
// Arm for wakeup
//
status = PcmciaFdoArmForWake(FdoExtension);
if (!NT_SUCCESS(status)) {
return status;
}
SetFdoFlag(FdoExtension, PCMCIA_FDO_WAKE_BY_CD);
powerState.DeviceState = PowerDeviceD3;
PoRequestPowerIrp(FdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL);
return STATUS_SUCCESS;
}
/**************************************************************************
POWER ROUTINES
**************************************************************************/
NTSTATUS
PcmciaSetFdoPowerState(
IN PDEVICE_OBJECT Fdo,
IN OUT PIRP Irp
)
/*++
Routine Description
Dispatches the IRP based on whether a system power state
or device power state transition is requested
Arguments
DeviceObject - Pointer to the functional device object for the pcmcia controller
Irp - Pointer to the Irp for the power dispatch
Return value
status
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status;
if (irpStack->Parameters.Power.Type == DevicePowerState) {
status = PcmciaSetFdoDevicePowerState(Fdo, Irp);
} else if (irpStack->Parameters.Power.Type == SystemPowerState) {
status = PcmciaSetFdoSystemPowerState(Fdo, Irp);
} else {
status = Irp->IoStatus.Status;
PoStartNextPowerIrp (Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status;
}
NTSTATUS
PcmciaSetFdoSystemPowerState(
IN PDEVICE_OBJECT Fdo,
IN OUT PIRP Irp
)
/*++
Routine Description
Handles system power state IRPs for the pccard controller.
Arguments
DeviceObject - Pointer to the functional device object for the pcmcia controller
Irp - Pointer to the Irp for the power dispatch
Return value
status
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
SYSTEM_POWER_STATE newSystemState = irpStack->Parameters.Power.State.SystemState;
NTSTATUS status = STATUS_SUCCESS;
POWER_STATE powerState;
ASSERT(irpStack->Parameters.Power.Type == SystemPowerState);
//
// Find the device power state corresponding to this system state
//
if (newSystemState >= PowerSystemHibernate) {
//
// Turn device off beyond hibernate..
//
powerState.DeviceState = PowerDeviceD3;
} else {
//
// Switch to the appropriate device power state
//
powerState.DeviceState = fdoExtension->DeviceCapabilities.DeviceState[newSystemState];
if (powerState.DeviceState == PowerDeviceUnspecified) {
//
// Capabilities not obtained?
// do the best we can
//
// Working --> D0
// otherwise power it off
//
if (newSystemState == PowerSystemWorking) {
powerState.DeviceState = PowerDeviceD0;
} else {
powerState.DeviceState = PowerDeviceD3;
}
}
// NOTE: HACKHACK:
//
// This hack is available to work around a BIOS bug. The way that WOL is supposed to work
// is that, after the device causes the wake, then the BIOS should run a method which
// issues a "notify(,0x2)", and thus prompting ACPI to complete the wait-wake IRP. If the
// W/W IRP is completed, then this allows the device state to be cleared before repowering
// the device.
//
// If the device state is not cleared, then we get an interrupt storm. This happens because
// when PCI.SYS switches the device to D0, then the PME# which was asserted to wake the system
// is still firing, which becomes a cardbus STSCHG interrupt, which asserts the PCI IRQ. But
// the act of switching the device to D0 has cleared the socket register BAR, so now the ISR
// can't clear the interrupt.
//
// The risk of forcing the device to D0 while going to standby is that the machine may be
// designed such that cardbus bridge may not function. So this should only be applied when
// we know that it will work.
//
if ((PcmciaPowerPolicy & PCMCIA_PP_WAKE_FROM_D0) &&
(powerState.DeviceState != PowerDeviceD0) && (fdoExtension->WaitWakeState != WAKESTATE_DISARMED) &&
(newSystemState < PowerSystemHibernate)) {
powerState.DeviceState = PowerDeviceD0; // force D0
}
}
//
// Transitioned to system state
//
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x transition S state %d => %d, sending D%d\n",
Fdo, Irp, fdoExtension->SystemPowerState-1, newSystemState-1, powerState.DeviceState-1));
fdoExtension->SystemPowerState = newSystemState;
//
// Send a D IRP to the cardbus controller stack if necessary
//
if ((powerState.DeviceState > PowerDeviceUnspecified) &&
(powerState.DeviceState != fdoExtension->DevicePowerState)) {
if (powerState.DeviceState == PowerDeviceD0) {
//
// Powering up, optimize by letting the S irp complete immediately
//
PoRequestPowerIrp(fdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL);
PoSetPowerState (Fdo, SystemPowerState, irpStack->Parameters.Power.State);
//
// Send the S IRP to the pdo
//
PoStartNextPowerIrp (Irp);
IoSkipCurrentIrpStackLocation(Irp);
status = PoCallDriver(fdoExtension->LowerDevice, Irp);
} else {
IoMarkIrpPending(Irp);
status = PoRequestPowerIrp(fdoExtension->DeviceObject,
IRP_MN_SET_POWER,
powerState,
PcmciaFdoSystemPowerDeviceIrpComplete,
Irp,
NULL
);
if (status != STATUS_PENDING) {
//
// Probably low memory failure
//
ASSERT( !NT_SUCCESS(status) );
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
//
// We've already marked the IRP pending, so we must return STATUS_PENDING
// (ie fail it asynchronously)
//
status = STATUS_PENDING;
}
}
} else {
PoSetPowerState (Fdo, SystemPowerState, irpStack->Parameters.Power.State);
//
// Send the S IRP to the pdo
//
PoStartNextPowerIrp (Irp);
IoSkipCurrentIrpStackLocation(Irp);
status = PoCallDriver(fdoExtension->LowerDevice, Irp);
}
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x <-- %08x\n", Fdo, Irp, status));
return status;
}
VOID
PcmciaFdoSystemPowerDeviceIrpComplete(
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 PCMCIA 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;
PIRP Irp = Context;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(NT_SUCCESS(IoStatus->Status));
PoSetPowerState (Fdo, SystemPowerState, irpStack->Parameters.Power.State);
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x request for D%d complete, passing S irp down\n",
Fdo, Irp, PowerState.DeviceState-1));
//
// Send the S IRP to the pdo
//
PoStartNextPowerIrp (Irp);
IoSkipCurrentIrpStackLocation(Irp);
PoCallDriver(fdoExtension->LowerDevice, Irp);
}
NTSTATUS
PcmciaSetFdoDevicePowerState (
IN PDEVICE_OBJECT Fdo,
IN OUT PIRP Irp
)
/*++
Routine Description
Handles device power state IRPs for the pccard controller.
Arguments
DeviceObject - Pointer to the functional device object for the pcmcia controller
Irp - Pointer to the Irp for the power dispatch
Return value
status
--*/
{
NTSTATUS status;
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
if ((fdoExtension->PendingPowerIrp != NULL) || (fdoExtension->PowerWorkerState != FPW_Stopped)) {
//
// oops. We already have a pending irp.
//
ASSERT(fdoExtension->PendingPowerIrp == NULL);
status = STATUS_DEVICE_BUSY;
Irp->IoStatus.Status = status;
PoStartNextPowerIrp (Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
} else {
fdoExtension->PendingPowerIrp = Irp;
if (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) {
fdoExtension->PowerWorkerState = FPW_BeginPowerDown;
} else {
fdoExtension->PowerWorkerState = FPW_BeginPowerUp;
}
status = PcmciaFdoPowerWorker(Fdo, STATUS_SUCCESS);
}
return status;
}
VOID
MoveToNextFdoPowerWorkerState(
PFDO_EXTENSION fdoExtension,
LONG increment
)
/*++
Routine Description
This routine controls the sequencing of FDO power worker.
Initially, the state must be set to one of two states, namely BeginPowerDown
or BeginPowerUp. From there, this routine defines the list of states to follow.
The parameter "increment" is normally the value '1'. Other values are used
to modify the normal sequence. For example, '-1' backs the engine up 1 step.
Use FPW_END_SEQUENCE to skip to the end of the sequence.
Arguments
Return Value
status
--*/
{
//
// NOTE!: code in power worker dependent on the following state sequences
// in PowerUpSequence remaining adjacent:
//
// FPW_PowerUpSocket : FPW_PowerUpSocketVerify : FPW_PowerUpComplete
//
// FPW_IrpComplete : FPW_Stopped
//
static FDO_POWER_WORKER_STATE PowerUpSequence[] = {
FPW_SendIrpDown,
FPW_PowerUp,
FPW_PowerUpSocket,
FPW_PowerUpSocket2,
FPW_PowerUpSocketVerify,
FPW_PowerUpSocketComplete,
FPW_PowerUpComplete,
FPW_CompleteIrp,
FPW_Stopped
};
static FDO_POWER_WORKER_STATE PowerDownSequence[] = {
FPW_PowerDown,
FPW_PowerDownSocket,
FPW_PowerDownComplete,
FPW_SendIrpDown,
FPW_CompleteIrp,
FPW_Stopped
};
static FDO_POWER_WORKER_STATE NoOpSequence[] = {
FPW_SendIrpDown,
FPW_CompleteIrp,
FPW_Stopped
};
if (fdoExtension->PowerWorkerState == FPW_BeginPowerDown) {
//
// Initialize sequence and phase
//
fdoExtension->PowerWorkerPhase = (UCHAR) -1;
if (fdoExtension->DevicePowerState == PowerDeviceD0) {
fdoExtension->PowerWorkerSequence = PowerDownSequence;
fdoExtension->PowerWorkerMaxPhase = sizeof(PowerDownSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
} else {
fdoExtension->PowerWorkerSequence = NoOpSequence;
fdoExtension->PowerWorkerMaxPhase = sizeof(NoOpSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
}
} else if (fdoExtension->PowerWorkerState == FPW_BeginPowerUp) {
//
// Initialize sequence and phase
//
fdoExtension->PowerWorkerPhase = (UCHAR) -1;
if (fdoExtension->DevicePowerState > PowerDeviceD0) {
fdoExtension->PowerWorkerSequence = PowerUpSequence;
fdoExtension->PowerWorkerMaxPhase = sizeof(PowerUpSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
} else {
fdoExtension->PowerWorkerSequence = NoOpSequence;
fdoExtension->PowerWorkerMaxPhase = sizeof(NoOpSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
}
}
//
// Increment the phase, but not past the end of the sequence
//
if (fdoExtension->PowerWorkerState != FPW_Stopped) {
if (increment == FPW_END_SEQUENCE) {
fdoExtension->PowerWorkerPhase = fdoExtension->PowerWorkerMaxPhase;
} else {
fdoExtension->PowerWorkerPhase += (UCHAR)increment;
if (fdoExtension->PowerWorkerPhase > fdoExtension->PowerWorkerMaxPhase) {
fdoExtension->PowerWorkerPhase = fdoExtension->PowerWorkerMaxPhase;
}
}
//
// The next state is pointed to by the current phase
//
fdoExtension->PowerWorkerState =
fdoExtension->PowerWorkerSequence[ fdoExtension->PowerWorkerPhase ];
}
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker next state : %s\n", fdoExtension->DeviceObject,
FDO_POWER_WORKER_STRING(fdoExtension->PowerWorkerState)));
}
NTSTATUS
PcmciaFdoPowerWorker (
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description
This routine handles sequencing of the device power state change for the
ppcard controller.
Arguments
DeviceObject - Pointer to the functional device object for the pcmcia controller
Status - status from previous operation
Return value
status
--*/
{
PDEVICE_OBJECT Fdo = Context;
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIRP Irp = fdoExtension->PendingPowerIrp;
PIO_STACK_LOCATION irpStack;
NTSTATUS status = Status;
PDEVICE_OBJECT pdo;
PPDO_EXTENSION pdoExtension;
PSOCKET socket;
BOOLEAN cardInSocket;
BOOLEAN deviceChange;
ULONG DelayTime = 0;
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker - %s\n", Fdo,
FDO_POWER_WORKER_STRING(fdoExtension->PowerWorkerState)));
switch(fdoExtension->PowerWorkerState) {
//-------------------------------------------------------------------------
// POWER DOWN STATES
//-------------------------------------------------------------------------
case FPW_BeginPowerDown:
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
case FPW_PowerDown:
//
// Controller being powered down
//
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x preparing for powerdown\n", Fdo, Irp));
//
// Getting out of D0
//
if (fdoExtension->Flags & PCMCIA_USE_POLLED_CSC) {
//
// Cancel the poll timer
//
KeCancelTimer(&fdoExtension->PollTimer);
}
//
// Save necessary controller registers
//
PcmciaFdoSaveControllerContext(fdoExtension);
fdoExtension->PendingPowerSocket = fdoExtension->SocketList;
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
case FPW_PowerDownSocket:
if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
}
//
// Ready to turn off the socket
//
PcmciaFdoSaveSocketContext(socket);
//
// Clear card detect unless we intend to wake using it
//
if (IsSocketFlagSet(socket, SOCKET_ENABLED_FOR_CARD_DETECT) && !IsFdoFlagSet(fdoExtension, PCMCIA_FDO_WAKE_BY_CD)) {
(*(socket->SocketFnPtr->PCBEnableDisableCardDetectEvent))(socket, FALSE);
}
//
// Cardbus cards need socket power all the time to allow PCI.SYS to read config space, even
// if the device is logically removed. So instead of turning off socket power when the
// children go to D3, we turn it off here when the parent goes to D3.
// For R2 cards, the power may already be off, since the socket power does follow the
// children. So this step would typically be superfluous.
//
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x skt %08x power worker SystemState=S%d\n",
Fdo, socket, fdoExtension->SystemPowerState-1));
switch(fdoExtension->SystemPowerState) {
case PowerSystemWorking:
//
// The system is still running, we must be powering down because the socket is idle
//
ASSERT(!IsCardInSocket(socket));
status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
break;
case PowerSystemSleeping1:
case PowerSystemSleeping2:
case PowerSystemSleeping3:
//
// If the device is armed for wakeup, we need to leave socket power on
//
if (fdoExtension->WaitWakeState == WAKESTATE_DISARMED) {
status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
}
break;
case PowerSystemHibernate:
status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
break;
case PowerSystemShutdown:
//
// Doing a shutdown - check to see if we need to leave socket power on since NDIS
// and SCSIPORT leaves their devices in D0. This can be a problem since many machines
// will hang in the BIOS if devices are left powered up.
//
if (!IsDeviceFlagSet(fdoExtension, PCMCIA_FDO_DISABLE_AUTO_POWEROFF)) {
status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
}
break;
default:
ASSERT(FALSE);
}
//
// if success, then recurse below
// if pending, then release_socket_power will call us back
// Prepare to go to next socket
//
fdoExtension->PendingPowerSocket = fdoExtension->PendingPowerSocket->NextSocket;
if (fdoExtension->PendingPowerSocket == NULL) {
//
// Done, ready to move on
//
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
}
break;
case FPW_PowerDownComplete:
irpStack = IoGetCurrentIrpStackLocation(Irp);
fdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState;
PoSetPowerState (Fdo, DevicePowerState, irpStack->Parameters.Power.State);
fdoExtension->Flags |= PCMCIA_FDO_OFFLINE;
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
//-------------------------------------------------------------------------
// POWER UP STATES
//-------------------------------------------------------------------------
case FPW_BeginPowerUp:
//
// Back in D0. Restore the minimal context so we can access the registers
//
PcmciaFdoRestoreControllerContext(fdoExtension);
//
// Delay for a while after restoring PCI config space to allow the controller
// to settle. The Panasonic Toughbook with at TI-1251B seemed to need this delay
// before the cardbus state register showed the right value.
//
DelayTime = 8192;
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
case FPW_PowerUp:
//
// Should be ready to touch the registers again
//
fdoExtension->Flags &= ~PCMCIA_FDO_OFFLINE;
//
// Get registers to a known state
//
PcmciaInitializeController(Fdo);
if (!ValidateController(fdoExtension)) {
status = STATUS_DEVICE_NOT_READY;
//
// Fast forward the sequence skipping to complete the irp
//
MoveToNextFdoPowerWorkerState(fdoExtension, FPW_END_SEQUENCE); // moves to state: Stopped
MoveToNextFdoPowerWorkerState(fdoExtension, -1); // moves to state: CompleteIrp
break;
}
//
// We just transitioned into D0
// Set socket flags to current state
//
for (socket = fdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
if (fdoExtension->PcmciaInterruptObject) {
//
// this should clear any pending interrupts in the socket event register
//
((*(socket->SocketFnPtr->PCBDetectCardChanged))(socket));
}
//
// Some cardbus cards (NEC based 1394 cards) are not quiet when they power up,
// avoid an interrupt storm here by setting ISA irq routing
//
if (IsCardBusCardInSocket(socket)) {
USHORT word;
GetPciConfigSpace(fdoExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
word |= BCTRL_IRQROUTING_ENABLE;
SetPciConfigSpace(fdoExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
}
}
fdoExtension->PendingPowerSocket = fdoExtension->SocketList;
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
case FPW_PowerUpSocket:
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
break;
}
//
// Make sure socket is really off first before powering up
//
status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
break;
case FPW_PowerUpSocket2:
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
break;
}
//
// We now decide if the socket should be turned on. We really want to turn
// at this point to make sure the device hasn't been swapped while the
// controller was off. If status_change is already set on the socket flags,
// then we anyway will power it on during enumeration, so don't bother now.
//
if (!IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE) && IsCardInSocket(socket)) {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker - PowerON socket %08x\n", Fdo, socket));
status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWERON);
}
break;
case FPW_PowerUpSocketVerify:
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
break;
}
//
// verify the same card is still inserted.
//
if (!IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE) &&
IsCardInSocket(socket) &&
IsSocketFlagSet(socket, SOCKET_CARD_POWERED_UP)) {
PcmciaVerifyCardInSocket(socket);
}
//
// Now we decide whether or not to turn the power back off.
//
if (Is16BitCardInSocket(socket)) {
if (IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE) ||
(socket->PowerRequests == 0)) {
status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
}
}
break;
case FPW_PowerUpSocketComplete:
if (fdoExtension->PendingPowerSocket == NULL) {
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
}
//
// go to next socket, if any
//
fdoExtension->PendingPowerSocket = fdoExtension->PendingPowerSocket->NextSocket;
if (fdoExtension->PendingPowerSocket != NULL) {
//
// Back up sequence to FPW_PowerUpSocket
//
MoveToNextFdoPowerWorkerState(fdoExtension, -2);
break;
}
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
case FPW_PowerUpComplete:
irpStack = IoGetCurrentIrpStackLocation(Irp);
deviceChange = FALSE;
for (socket = fdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
if (IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE)) {
deviceChange = TRUE;
}
PcmciaFdoRestoreSocketContext(socket);
if (CardBus(socket)) {
CBEnableDeviceInterruptRouting(socket);
}
if (IsSocketFlagSet(socket, SOCKET_ENABLED_FOR_CARD_DETECT)) {
(*(socket->SocketFnPtr->PCBEnableDisableCardDetectEvent))(socket, TRUE);
}
}
fdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState;
PoSetPowerState (Fdo, DevicePowerState, irpStack->Parameters.Power.State);
if (deviceChange) {
//
// Make sure i/o arbiter is not hanging on the devnode
//
if (CardBusExtension(fdoExtension)) {
IoInvalidateDeviceState(fdoExtension->Pdo);
}
//
// Device state changed..
//
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker - Invalidating Device Relations!\n", Fdo));
IoInvalidateDeviceRelations(fdoExtension->Pdo, BusRelations);
}
//
// Getting back to D0, set the poll timer on
//
if (fdoExtension->Flags & PCMCIA_USE_POLLED_CSC) {
LARGE_INTEGER dueTime;
//
// Set first fire to twice the peroidic interval - just
//
dueTime.QuadPart = -PCMCIA_CSC_POLL_INTERVAL * 1000 * 10 * 2;
KeSetTimerEx(&(fdoExtension->PollTimer),
dueTime,
PCMCIA_CSC_POLL_INTERVAL,
&fdoExtension->TimerDpc
);
}
PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension);
if (!IsListEmpty(&fdoExtension->PdoPowerRetryList)) {
PLIST_ENTRY NextEntry;
PIRP pdoIrp;
NextEntry = RemoveHeadList(&fdoExtension->PdoPowerRetryList);
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
pdoIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.DriverContext[0]);
KeInsertQueueDpc(&fdoExtension->PdoPowerRetryDpc, pdoIrp, NULL);
} else {
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
}
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
break;
//-------------------------------------------------------------------------
// IRP HANDLING STATES
//-------------------------------------------------------------------------
case FPW_SendIrpDown:
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x sending irp down to PDO\n", Fdo, Irp));
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
//
// Send the IRP to the pdo
//
IoMarkIrpPending(Irp);
IoCopyCurrentIrpStackLocationToNext (Irp);
IoSetCompletionRoutine(Irp,
PcmciaFdoDevicePowerCompletion,
NULL,
TRUE,
TRUE,
TRUE);
status = PoCallDriver(fdoExtension->LowerDevice, Irp);
if (NT_SUCCESS(status)) {
status = STATUS_PENDING;
}
break;
case FPW_CompleteIrp:
MoveToNextFdoPowerWorkerState(fdoExtension, 1);
if (Irp) {
fdoExtension->PendingPowerIrp = NULL;
Irp->IoStatus.Status = status;
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x comp %08x\n", fdoExtension->DeviceObject, Irp, Irp->IoStatus.Status));
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
fdoExtension->PowerWorkerState = FPW_Stopped;
break;
case FPW_Stopped:
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker final exit %08x\n", Fdo, status));
if (!NT_SUCCESS(status)) {
for (socket = fdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
SetSocketFlag(socket, SOCKET_CARD_STATUS_CHANGE);
}
}
return status;
default:
ASSERT(FALSE);
}
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker status %08x\n", Fdo, status));
if (status == STATUS_PENDING) {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker exit (pending)\n", Fdo));
//
// Current action calls us back
//
if ((Irp=fdoExtension->PendingPowerIrp)!=NULL) {
IoMarkIrpPending(Irp);
}
return status;
}
//
// Not done yet. Recurse or call timer
//
if (DelayTime) {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker delay type %s, %d usec\n", Fdo,
(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
//
dueTime.QuadPart = -((LONG) DelayTime*10);
KeSetTimer(&fdoExtension->PowerTimer, dueTime, &fdoExtension->PowerDpc);
//
// We will reenter on timer dpc
//
if ((Irp=fdoExtension->PendingPowerIrp)!=NULL) {
IoMarkIrpPending(Irp);
}
return STATUS_PENDING;
}
}
//
// recurse
//
return (PcmciaFdoPowerWorker(Fdo, status));
}
NTSTATUS
PcmciaFdoDevicePowerCompletion(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description
Completion routine for the power IRP sent down to the PDO for the
pcmcia controller. If we are getting out of a working system state,
requests a power IRP to put the device in appropriate device power state.
Also makes sure that we stop poking device registers when the controller
is powered down and reenables that if necessary when it powers up
Parameters
DeviceObject - Pointer to FDO for the controller
Irp - Pointer to the IRP for the power request
Context - Pointer to the FDO_POWER_CONTEXT which is
filled in when the IRP is passed down
Return Value
Status
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG timerInterval;
NTSTATUS status;
LARGE_INTEGER dueTime;
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x DevicePowerCompletion PDO status %08x\n", Fdo, Irp, Irp->IoStatus.Status));
if ((NT_SUCCESS(Irp->IoStatus.Status))) {
if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
timerInterval = ControllerPowerUpDelay;
} else {
//
// powering down
// stall to avoid hardware problem on ThinkPads where power
// to the device is left on after the system in general powers off
//
timerInterval = 20000;
}
//
// Do the rest in our timer routine
//
dueTime.QuadPart = -((LONG) timerInterval*10);
KeSetTimer(&fdoExtension->PowerTimer, dueTime, &fdoExtension->PowerDpc);
status = STATUS_MORE_PROCESSING_REQUIRED;
} else {
DebugPrint ((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x power irp failed by pdo %08x\n", Fdo, Irp, fdoExtension->LowerDevice));
PoStartNextPowerIrp (Irp);
status = Irp->IoStatus.Status;
//
// This irp is now complete
//
fdoExtension->PendingPowerIrp = NULL;
MoveToNextFdoPowerWorkerState(fdoExtension, FPW_END_SEQUENCE); // moves to state: Stopped
PcmciaFdoPowerWorker(Fdo, status);
}
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x DevicePowerCompletion <-- %08x\n", Fdo, Irp, status));
return status;
}
VOID
PcmciaFdoPowerWorkerDpc(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description
This routine is called a short time after the controller power state
is changed in order to give the hardware a chance to stabilize. It
is called in the context of a device power request.
Parameters
same as KDPC (Context is fdoExtension)
Return Value
none
--*/
{
PFDO_EXTENSION fdoExtension = Context;
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x PowerWorkerDpc\n", fdoExtension->DeviceObject, fdoExtension->PendingPowerIrp));
//
// Fdo power worker will complete the irp
//
PcmciaFdoPowerWorker(fdoExtension->DeviceObject, STATUS_SUCCESS);
}
VOID
PcmciaFdoRetryPdoPowerRequest(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description
This routine is called to finish off any PDO power irps that may have
been queued.
Parameters
same as KDPC (Context is fdoExtension)
Return Value
none
--*/
{
PFDO_EXTENSION fdoExtension = Context;
PIRP Irp = SystemArgument1;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x FdoRetryPdoPowerRequest\n", fdoExtension->DeviceObject, Irp));
PcmciaSetPdoDevicePowerState(irpStack->DeviceObject, Irp);
while(TRUE) {
PLIST_ENTRY NextEntry;
PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension);
if (IsListEmpty(&fdoExtension->PdoPowerRetryList)) {
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
break;
}
NextEntry = RemoveHeadList(&fdoExtension->PdoPowerRetryList);
PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.DriverContext[0]);
irpStack = IoGetCurrentIrpStackLocation(Irp);
PcmciaSetPdoDevicePowerState(irpStack->DeviceObject, Irp);
}
}
NTSTATUS
PcmciaFdoSaveControllerContext(
IN PFDO_EXTENSION FdoExtension
)
/*++
Routine Description:
Saves the state of the necessary PCI config registers
in the device extension of the cardbus controller
Arguments:
FdoExtension - Pointer to device extension for the FDO of the
cardbus controller
Return Value:
Status
--*/
{
ULONG index, offset, count;
PULONG alignedBuffer;
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x save reg context\n", FdoExtension->DeviceObject));
if (!FdoExtension->PciContext.BufferLength) {
// nothing to save
return STATUS_SUCCESS;
}
if (!ValidateController(FdoExtension)) {
return STATUS_DEVICE_NOT_READY;
}
SetDeviceFlag(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED);
if (FdoExtension->PciContextBuffer == NULL) {
FdoExtension->PciContextBuffer = ExAllocatePool(NonPagedPool, FdoExtension->PciContext.BufferLength);
if (FdoExtension->PciContextBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
alignedBuffer = ExAllocatePool(NonPagedPool, FdoExtension->PciContext.MaxLen);
if (alignedBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
if (CardBusExtension(FdoExtension)) {
//
// Save PCI context
//
for (index = 0, offset = 0; index < FdoExtension->PciContext.RangeCount; index++) {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x saving PCI context, offset %x length %x\n",
FdoExtension->DeviceObject,
FdoExtension->PciContext.Range[index].wOffset,
FdoExtension->PciContext.Range[index].wLen));
ASSERT(FdoExtension->PciContext.Range[index].wLen <= FdoExtension->PciContext.MaxLen);
GetPciConfigSpace(FdoExtension,
(ULONG) FdoExtension->PciContext.Range[index].wOffset,
alignedBuffer,
FdoExtension->PciContext.Range[index].wLen);
RtlCopyMemory(&FdoExtension->PciContextBuffer[offset], alignedBuffer, FdoExtension->PciContext.Range[index].wLen);
offset += FdoExtension->PciContext.Range[index].wLen;
}
}
ExFreePool(alignedBuffer);
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaFdoSaveSocketContext(
IN PSOCKET Socket
)
/*++
Routine Description:
Saves the state of the necessary socket registers (CB, EXCA)
Arguments:
Socket - Pointer to socket data structure
Return Value:
Status
--*/
{
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
ULONG index, offset, count;
if (CardBusExtension(fdoExtension) && fdoExtension->CardbusContext.BufferLength) {
//
// Save Cardbus context
//
if (Socket->CardbusContextBuffer == NULL) {
Socket->CardbusContextBuffer = ExAllocatePool(NonPagedPool, fdoExtension->CardbusContext.BufferLength);
if (Socket->CardbusContextBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
for (index = 0, offset = 0; index < fdoExtension->CardbusContext.RangeCount; index++) {
PULONG pBuffer = (PULONG) &Socket->CardbusContextBuffer[offset];
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x saving Cardbus context, offset %x length %x\n",
fdoExtension->DeviceObject,
fdoExtension->CardbusContext.Range[index].wOffset,
fdoExtension->CardbusContext.Range[index].wLen));
for (count = 0; count < (fdoExtension->CardbusContext.Range[index].wLen/sizeof(ULONG)) ; count++) {
*pBuffer++ = CBReadSocketRegister(Socket,
(UCHAR) (fdoExtension->CardbusContext.Range[index].wOffset + count*sizeof(ULONG)));
}
offset += fdoExtension->CardbusContext.Range[index].wLen;
}
}
//
// Save Exca context
//
if (fdoExtension->ExcaContext.BufferLength) {
if (Socket->ExcaContextBuffer == NULL) {
Socket->ExcaContextBuffer = ExAllocatePool(NonPagedPool, fdoExtension->ExcaContext.BufferLength);
if (Socket->ExcaContextBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
for (index = 0, offset = 0; index < fdoExtension->ExcaContext.RangeCount; index++) {
PUCHAR pBuffer = &Socket->ExcaContextBuffer[offset];
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x saving Exca context, offset %x length %x\n",
fdoExtension->DeviceObject,
fdoExtension->ExcaContext.Range[index].wOffset,
fdoExtension->ExcaContext.Range[index].wLen));
for (count = 0; count < fdoExtension->ExcaContext.Range[index].wLen; count++) {
*pBuffer++ = PcicReadSocket(Socket,
fdoExtension->ExcaContext.Range[index].wOffset + count);
}
offset += fdoExtension->ExcaContext.Range[index].wLen;
}
}
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaFdoRestoreControllerContext(
IN PFDO_EXTENSION FdoExtension
)
/*++
Routine Description:
Restores the state of the necessary PCI config registers
from the device extension of the cardbus controller
Arguments:
FdoExtension - Pointer to device extension for the FDO of the
cardbus controller
Return Value:
Status
--*/
{
ULONG index, offset, count;
PULONG alignedBuffer;
if (!CardBusExtension(FdoExtension)) {
return STATUS_SUCCESS;
}
//
// Make sure we don't restore stale or uninitialized data
//
if (!IsDeviceFlagSet(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED)) {
ASSERT(IsDeviceFlagSet(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED));
return STATUS_UNSUCCESSFUL;
}
ResetDeviceFlag(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED);
if (FdoExtension->PciContextBuffer == NULL) {
// nothing to restore... strange since our flag was set
ASSERT(FALSE);
return STATUS_SUCCESS;
}
alignedBuffer = ExAllocatePool(NonPagedPool, FdoExtension->PciContext.MaxLen);
if (alignedBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
DebugPrint((PCMCIA_DEBUG_POWER,
"fdo %08x restore reg context\n", FdoExtension->DeviceObject));
//
// Restore PCI context
//
for (index = 0, offset = 0; index < FdoExtension->PciContext.RangeCount; index++) {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x restoring PCI context, offset %x length %x\n",
FdoExtension->DeviceObject,
FdoExtension->PciContext.Range[index].wOffset,
FdoExtension->PciContext.Range[index].wLen));
ASSERT(FdoExtension->PciContext.Range[index].wLen <= FdoExtension->PciContext.MaxLen);
RtlCopyMemory(alignedBuffer, &FdoExtension->PciContextBuffer[offset], FdoExtension->PciContext.Range[index].wLen);
SetPciConfigSpace(FdoExtension,
(ULONG) FdoExtension->PciContext.Range[index].wOffset,
alignedBuffer,
FdoExtension->PciContext.Range[index].wLen);
offset += FdoExtension->PciContext.Range[index].wLen;
//
// hang on resume on NEC NX laptop (w/ Ricoh devid 0x475) is avoided with a stall here.
// The hang occurs if CBRST is turned OFF. I'm unclear about the reason for it, this
// is just an empirically derived hack.
//
PcmciaWait(1);
}
ExFreePool(alignedBuffer);
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaFdoRestoreSocketContext(
IN PSOCKET Socket
)
/*++
Routine Description:
Restores the state of the necessary socket registers (CB, EXCA)
Arguments:
Socket - Pointer to socket data structure
Return Value:
Status
--*/
{
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
ULONG index, offset, count;
if (CardBusExtension(fdoExtension) && (Socket->CardbusContextBuffer != NULL)) {
//
// Restore Cardbus context
//
for (index = 0, offset = 0; index < fdoExtension->CardbusContext.RangeCount; index++) {
PULONG pBuffer = (PULONG) &Socket->CardbusContextBuffer[offset];
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x restoring Cardbus context offset %x length %x\n",
fdoExtension->DeviceObject,
fdoExtension->CardbusContext.Range[index].wOffset,
fdoExtension->CardbusContext.Range[index].wLen));
for (count = 0; count < (fdoExtension->CardbusContext.Range[index].wLen/sizeof(ULONG)) ; count++) {
CBWriteSocketRegister(Socket,
(UCHAR) (fdoExtension->CardbusContext.Range[index].wOffset + count*sizeof(ULONG)),
*pBuffer++);
}
offset += fdoExtension->CardbusContext.Range[index].wLen;
}
}
//
// Restore Exca context
//
if (Socket->ExcaContextBuffer != NULL) {
for (index = 0, offset = 0; index < fdoExtension->ExcaContext.RangeCount; index++) {
PUCHAR pBuffer = &Socket->ExcaContextBuffer[offset];
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x Restoring Exca context, offset %x length %x\n",
fdoExtension->DeviceObject,
fdoExtension->ExcaContext.Range[index].wOffset,
fdoExtension->ExcaContext.Range[index].wLen));
for (count = 0; count < fdoExtension->ExcaContext.Range[index].wLen; count++) {
PcicWriteSocket(Socket,
fdoExtension->ExcaContext.Range[index].wOffset + count,
*pBuffer++);
}
offset += fdoExtension->ExcaContext.Range[index].wLen;
}
}
return STATUS_SUCCESS;
}