2052 lines
59 KiB
C
2052 lines
59 KiB
C
/*++
|
||
|
||
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;
|
||
}
|