|
|
/*++
Copyright (c) 2002 Microsoft Corporation
Module Name:
wake.c
Abstract:
This module contains code to handle IRP_MJ_POWER dispatches for SD controllers
Authors:
Neil Sandlin (neilsa) Jan 1, 2002
Environment:
Kernel mode only
Notes:
Revision History:
--*/
#include "pch.h"
//
// Internal References
//
NTSTATUS SdbusFdoWaitWakeIoCompletion( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN PVOID Context ); VOID SdbusPdoWaitWakeCancelRoutine( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp );
/**************************************************************************
FDO ROUTINES
**************************************************************************/
NTSTATUS SdbusFdoWaitWake( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ) /*++
Routine Description
Handles WAIT_WAKE for the given sd controller
Arguments
Pdo - Pointer to the functional device object for the sd 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((SDBUS_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, SdbusFdoWaitWakeIoCompletion, Fdo, TRUE, TRUE, TRUE); //
// now pass this down to the lower driver..
//
PoCallDriver(fdoExtension->LowerDevice, Irp); return STATUS_PENDING; }
NTSTATUS SdbusFdoWaitWakeIoCompletion( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN PVOID Context ) /*++
Routine Description:
Completion routine for the IRP_MN_WAIT_WAKE request for this sd controller. This is called when the WAIT_WAKE IRP is completed by the lower driver (PCI/ACPI) indicating either that 1. SD bus 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 sd 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((SDBUS_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 (IsDeviceFlagSet(fdoExtension, SDBUS_FDO_WAKE_BY_CD)) { POWER_STATE powerState; ResetDeviceFlag(fdoExtension, SDBUS_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((SDBUS_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
//
// SdbusPdoWaitWakeCompletion(pdo, finishedIrp, pdoExtension);
IoCompleteRequest(finishedIrp, IO_NO_INCREMENT); } } PoStartNextPowerIrp(Irp); } return Irp->IoStatus.Status; }
VOID SdbusFdoWaitWakePoCompletion( 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 SDBUS 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((SDBUS_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 SdbusFdoArmForWake( 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((SDBUS_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, SdbusFdoWaitWakePoCompletion, NULL, NULL); if (!NT_SUCCESS(status)) { FdoExtension->WaitWakeState = WAKESTATE_DISARMED; DebugPrint((SDBUS_DEBUG_POWER, "WaitWake to FDO, expecting STATUS_PENDING, got %08X\n", status)); } return status; }
NTSTATUS SdbusFdoDisarmWake( 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((SDBUS_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 SdbusFdoCheckForIdle( IN PFDO_EXTENSION FdoExtension ) { POWER_STATE powerState; NTSTATUS status; //
// Make sure all sockets are empty
//
#if 0
for (socket = FdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) { if (IsCardInSocket(socket)) { return STATUS_UNSUCCESSFUL; } } #endif
//
// Arm for wakeup
//
status = SdbusFdoArmForWake(FdoExtension); if (!NT_SUCCESS(status)) { return status; } SetDeviceFlag(FdoExtension, SDBUS_FDO_WAKE_BY_CD); powerState.DeviceState = PowerDeviceD3; PoRequestPowerIrp(FdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL); return STATUS_SUCCESS; } /**************************************************************************
PDO ROUTINES
**************************************************************************/
NTSTATUS SdbusPdoWaitWake( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, OUT BOOLEAN *CompleteIrp ) /*++
Routine Description
Handles WAIT_WAKE for the given pc-card.
Arguments
Pdo - Pointer to the device object for the pc-card Irp - The IRP_MN_WAIT_WAKE Irp CompleteIrp - This routine will set this to TRUE if the IRP should be completed after this is called and FALSE if it should not be touched
Return Value
STATUS_PENDING - Wait wake is pending STATUS_SUCCESS - Wake is already asserted, wait wake IRP is completed in this case Any other status - Error --*/ {
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PFDO_EXTENSION fdoExtension = pdoExtension->FdoExtension; NTSTATUS status; *CompleteIrp = FALSE;
if ((pdoExtension->DeviceCapabilities.DeviceWake == PowerDeviceUnspecified) || (pdoExtension->DeviceCapabilities.DeviceWake < pdoExtension->DevicePowerState)) { //
// Either we don't support wake at all OR the current device power state
// of the PC-Card doesn't support wake
//
return STATUS_INVALID_DEVICE_STATE; }
if (pdoExtension->Flags & SDBUS_DEVICE_WAKE_PENDING) { //
// A WAKE is already pending
//
return STATUS_DEVICE_BUSY; }
status = SdbusFdoArmForWake(fdoExtension); if (!NT_SUCCESS(status)) { return status; }
//for the time being, expect STATUS_PENDING from FdoArmForWake
ASSERT(status == STATUS_PENDING); //
// Parent has one (more) waiter..
//
InterlockedIncrement(&fdoExtension->ChildWaitWakeCount); //for testing, make sure there is only one waiter
ASSERT (fdoExtension->ChildWaitWakeCount == 1);
pdoExtension->WaitWakeIrp = Irp; pdoExtension->Flags |= SDBUS_DEVICE_WAKE_PENDING; //
// Set Ring enable/cstschg for the card here..
//
// (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, TRUE);
//
// PCI currently does not do anything with a WW irp for a cardbus PDO. So we hack around
// this here by not passing the irp down. Instead it is held pending here, so we can
// set a cancel routine just like the read PDO driver would. If PCI were to do something
// with the irp, we could code something like the following:
//
// if (IsCardBusCard(pdoExtension)) {
// IoSetCompletionRoutine(Irp, SdbusPdoWaitWakeCompletion, pdoExtension,TRUE,TRUE,TRUE);
// IoCopyCurrentIrpStackLocationToNext(Irp);
// status = IoCallDriver (pdoExtension->LowerDevice, Irp);
// ASSERT (status == STATUS_PENDING);
// return status;
// }
IoMarkIrpPending(Irp);
//
// Allow IRP to be cancelled..
//
IoSetCancelRoutine(Irp, SdbusPdoWaitWakeCancelRoutine);
IoSetCompletionRoutine(Irp, SdbusPdoWaitWakeCompletion, pdoExtension, TRUE, TRUE, TRUE);
return STATUS_PENDING; }
NTSTATUS SdbusPdoWaitWakeCompletion( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, IN PPDO_EXTENSION PdoExtension ) /*++
Routine Description
Completion routine called when a pending IRP_MN_WAIT_WAKE Irp completes
Arguments
Pdo - Pointer to the physical device object for the pc-card Irp - Pointer to the wait wake IRP PdoExtension - Pointer to the device extension for the Pdo
Return Value
Status from the IRP
--*/ { PFDO_EXTENSION fdoExtension = PdoExtension->FdoExtension; DebugPrint((SDBUS_DEBUG_POWER, "pdo %08x irp %08x --> WaitWakeCompletion\n", Pdo, Irp));
ASSERT (PdoExtension->Flags & SDBUS_DEVICE_WAKE_PENDING);
PdoExtension->Flags &= ~SDBUS_DEVICE_WAKE_PENDING; PdoExtension->WaitWakeIrp = NULL; //
// Reset ring enable/cstschg
//
// (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, PdoExtension, FALSE);
ASSERT (fdoExtension->ChildWaitWakeCount > 0); InterlockedDecrement(&fdoExtension->ChildWaitWakeCount); //
// Wake completed
//
InterlockedDecrement(&PdoExtension->DeletionLock); return Irp->IoStatus.Status; }
VOID SdbusPdoWaitWakeCancelRoutine( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ) /*++
Routine Description:
Cancel an outstanding (pending) WAIT_WAKE Irp. Note: The CancelSpinLock is held on entry
Arguments:
Pdo - Pointer to the physical device object for the pc-card on which the WAKE is pending Irp - Pointer to the WAIT_WAKE Irp to be cancelled
Return Value
None
--*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PFDO_EXTENSION fdoExtension = pdoExtension->FdoExtension;
DebugPrint((SDBUS_DEBUG_POWER, "pdo %08x irp %08x --> WaitWakeCancelRoutine\n", Pdo, Irp));
IoReleaseCancelSpinLock(Irp->CancelIrql);
if (pdoExtension->WaitWakeIrp == NULL) { //
// Wait wake already completed/cancelled
//
return; }
pdoExtension->Flags &= ~SDBUS_DEVICE_WAKE_PENDING; pdoExtension->WaitWakeIrp = NULL;
//
// Reset ring enable, disabling wake..
//
// (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, FALSE);
//
// Since this is cancelled, see if parent's wait wake
// needs to be cancelled too.
// First, decrement the number of child waiters..
//
ASSERT (fdoExtension->ChildWaitWakeCount > 0); if (InterlockedDecrement(&fdoExtension->ChildWaitWakeCount) == 0) { //
// No more waiters.. cancel the parent's wake IRP
//
ASSERT(fdoExtension->WaitWakeIrp); if (fdoExtension->WaitWakeIrp) { IoCancelIrp(fdoExtension->WaitWakeIrp); } }
InterlockedDecrement(&pdoExtension->DeletionLock); //
// Complete the IRP
//
Irp->IoStatus.Information = 0;
//
// Is this necessary?
//
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); }
|