You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2051 lines
62 KiB
2051 lines
62 KiB
/*++
|
|
|
|
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 = PCMCIA_CONTROLLER_POWERUP_DELAY;
|
|
} 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;
|
|
}
|