/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: pdopower.c Abstract: This module contains code to handle IRP_MJ_POWER dispatches for PDOs enumerated by the PCMCIA bus driver Authors: Ravisankar Pudipeddi (ravisp) May 30, 1997 Neil Sandlin (neilsa) June 1 1999 Environment: Kernel mode only Notes: Revision History: Neil Sandlin (neilsa) 04-Mar-1999 Made device power a state machine --*/ #include "pch.h" // // Internal References // NTSTATUS PcmciaPdoWaitWake( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, OUT BOOLEAN *CompleteIrp ); VOID PcmciaPdoWaitWakeCancelRoutine( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ); NTSTATUS PcmciaSetPdoPowerState( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ); NTSTATUS PcmciaSetPdoSystemPowerState( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ); NTSTATUS PcmciaPdoPowerWorker( IN PVOID Context, IN NTSTATUS DeferredStatus ); VOID MoveToNextPdoPowerWorkerState( PPDO_EXTENSION pdoExtension ); NTSTATUS PcmciaPdoPowerSentIrpComplete( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, IN PVOID Context ); NTSTATUS PcmciaPdoPowerCompletion( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, IN PVOID Context ); NTSTATUS PcmciaPdoCompletePowerIrp( IN PPDO_EXTENSION pdoExtension, IN PIRP Irp, IN NTSTATUS status ); // // // NTSTATUS PcmciaPdoPowerDispatch( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: This routine handles power requests for the PDOs. Arguments: Pdo - pointer to the physical device object Irp - pointer to the io request packet Return Value: status --*/ { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; if(IsDevicePhysicallyRemoved(pdoExtension) || IsDeviceDeleted(pdoExtension)) { // couldn't aquire RemoveLock - we're in the process of being removed - abort status = STATUS_NO_SUCH_DEVICE; PoStartNextPowerIrp( Irp ); Irp->IoStatus.Status = status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } InterlockedIncrement(&pdoExtension->DeletionLock); switch (irpStack->MinorFunction) { case IRP_MN_SET_POWER: { DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> IRP_MN_SET_POWER\n", Pdo, Irp)); DebugPrint((PCMCIA_DEBUG_POWER, " (%s%x, context %x)\n", (irpStack->Parameters.Power.Type == SystemPowerState) ? "S": ((irpStack->Parameters.Power.Type == DevicePowerState) ? "D" : "Unknown"), irpStack->Parameters.Power.State, irpStack->Parameters.Power.SystemContext )); status = PcmciaSetPdoPowerState(Pdo, Irp); break; } case IRP_MN_QUERY_POWER: { DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> IRP_MN_QUERY_POWER\n", Pdo, Irp)); DebugPrint((PCMCIA_DEBUG_POWER, " (%s%x, context %x)\n", (irpStack->Parameters.Power.Type == SystemPowerState) ? "S": ((irpStack->Parameters.Power.Type == DevicePowerState) ? "D" : "Unknown"), irpStack->Parameters.Power.State, irpStack->Parameters.Power.SystemContext )); status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, STATUS_SUCCESS); break; } case IRP_MN_WAIT_WAKE: { BOOLEAN completeIrp; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> IRP_MN_WAIT_WAKE\n", Pdo, Irp)); // // Should not have a wake pending already // ASSERT (!(((PPDO_EXTENSION)Pdo->DeviceExtension)->Flags & PCMCIA_DEVICE_WAKE_PENDING)); status = PcmciaPdoWaitWake(Pdo, Irp, &completeIrp); if (completeIrp) { InterlockedDecrement(&pdoExtension->DeletionLock); PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } break; } default: { // // Unhandled minor function // PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, Irp->IoStatus.Status); } } DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x <-- %08x\n", Pdo, Irp, status)); return status; } NTSTATUS PcmciaPdoWaitWake( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, OUT BOOLEAN *CompleteIrp ) /*++ Routine Description Handles WAIT_WAKE for the given pc-card. Arguments Pdo - Pointer to the device object for the pc-card Irp - The IRP_MN_WAIT_WAKE Irp CompleteIrp - This routine will set this to TRUE if the IRP should be completed after this is called and FALSE if it should not be touched Return Value STATUS_PENDING - Wait wake is pending STATUS_SUCCESS - Wake is already asserted, wait wake IRP is completed in this case Any other status - Error --*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PSOCKET socket = pdoExtension->Socket; PFDO_EXTENSION fdoExtension = socket->DeviceExtension; NTSTATUS status; *CompleteIrp = FALSE; ASSERT (socket != NULL); if ((pdoExtension->DeviceCapabilities.DeviceWake == PowerDeviceUnspecified) || (pdoExtension->DeviceCapabilities.DeviceWake < pdoExtension->DevicePowerState)) { // // Either we don't support wake at all OR the current device power state // of the PC-Card doesn't support wake // return STATUS_INVALID_DEVICE_STATE; } if (pdoExtension->Flags & PCMCIA_DEVICE_WAKE_PENDING) { // // A WAKE is already pending // return STATUS_DEVICE_BUSY; } status = PcmciaFdoArmForWake(socket->DeviceExtension); if (!NT_SUCCESS(status)) { return status; } //for the time being, expect STATUS_PENDING from FdoArmForWake ASSERT(status == STATUS_PENDING); // // Parent has one (more) waiter.. // InterlockedIncrement(&fdoExtension->ChildWaitWakeCount); //for testing, make sure there is only one waiter ASSERT (fdoExtension->ChildWaitWakeCount == 1); pdoExtension->WaitWakeIrp = Irp; pdoExtension->Flags |= PCMCIA_DEVICE_WAKE_PENDING; // // Set Ring enable/cstschg for the card here.. // (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, TRUE); // // PCI currently does not do anything with a WW irp for a cardbus PDO. So we hack around // this here by not passing the irp down. Instead it is held pending here, so we can // set a cancel routine just like the read PDO driver would. If PCI were to do something // with the irp, we could code something like the following: // // if (IsCardBusCard(pdoExtension)) { // IoSetCompletionRoutine(Irp, PcmciaPdoWaitWakeCompletion, pdoExtension,TRUE,TRUE,TRUE); // IoCopyCurrentIrpStackLocationToNext(Irp); // status = IoCallDriver (pdoExtension->LowerDevice, Irp); // ASSERT (status == STATUS_PENDING); // return status; // } IoMarkIrpPending(Irp); // // Allow IRP to be cancelled.. // IoSetCancelRoutine(Irp, PcmciaPdoWaitWakeCancelRoutine); IoSetCompletionRoutine(Irp, PcmciaPdoWaitWakeCompletion, pdoExtension, TRUE, TRUE, TRUE); return STATUS_PENDING; } NTSTATUS PcmciaPdoWaitWakeCompletion( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, IN PPDO_EXTENSION PdoExtension ) /*++ Routine Description Completion routine called when a pending IRP_MN_WAIT_WAKE Irp completes Arguments Pdo - Pointer to the physical device object for the pc-card Irp - Pointer to the wait wake IRP PdoExtension - Pointer to the device extension for the Pdo Return Value Status from the IRP --*/ { PSOCKET socket = PdoExtension->Socket; PFDO_EXTENSION fdoExtension = socket->DeviceExtension; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> WaitWakeCompletion\n", Pdo, Irp)); ASSERT (PdoExtension->Flags & PCMCIA_DEVICE_WAKE_PENDING); PdoExtension->Flags &= ~PCMCIA_DEVICE_WAKE_PENDING; PdoExtension->WaitWakeIrp = NULL; // // Reset ring enable/cstschg // (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, PdoExtension, FALSE); ASSERT (fdoExtension->ChildWaitWakeCount > 0); InterlockedDecrement(&fdoExtension->ChildWaitWakeCount); // // Wake completed // InterlockedDecrement(&PdoExtension->DeletionLock); return Irp->IoStatus.Status; } VOID PcmciaPdoWaitWakeCancelRoutine( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ) /*++ Routine Description: Cancel an outstanding (pending) WAIT_WAKE Irp. Note: The CancelSpinLock is held on entry Arguments: Pdo - Pointer to the physical device object for the pc-card on which the WAKE is pending Irp - Pointer to the WAIT_WAKE Irp to be cancelled Return Value None --*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PSOCKET socket = pdoExtension->Socket; PFDO_EXTENSION fdoExtension = socket->DeviceExtension; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x --> WaitWakeCancelRoutine\n", Pdo, Irp)); IoReleaseCancelSpinLock(Irp->CancelIrql); if (pdoExtension->WaitWakeIrp == NULL) { // // Wait wake already completed/cancelled // return; } pdoExtension->Flags &= ~PCMCIA_DEVICE_WAKE_PENDING; pdoExtension->WaitWakeIrp = NULL; // // Reset ring enable, disabling wake.. // (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, FALSE); // // Since this is cancelled, see if parent's wait wake // needs to be cancelled too. // First, decrement the number of child waiters.. // ASSERT (fdoExtension->ChildWaitWakeCount > 0); if (InterlockedDecrement(&fdoExtension->ChildWaitWakeCount) == 0) { // // No more waiters.. cancel the parent's wake IRP // ASSERT(fdoExtension->WaitWakeIrp); if (fdoExtension->WaitWakeIrp) { IoCancelIrp(fdoExtension->WaitWakeIrp); } } InterlockedDecrement(&pdoExtension->DeletionLock); // // Complete the IRP // Irp->IoStatus.Information = 0; // // Is this necessary? // PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); } NTSTATUS PcmciaSetPdoPowerState( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ) /*++ Routine Description Dispatches the IRP based on whether a system power state or device power state transition is requested Arguments Pdo - Pointer to the physical device object for the pc-card Irp - Pointer to the Irp for the power dispatch Return value status --*/ { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PSOCKET socket = pdoExtension->Socket; PFDO_EXTENSION fdoExtension=socket->DeviceExtension; NTSTATUS status; PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension); // // Don't handle any power requests for dead pdos // if (IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE)) { PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension); // // Card probably removed.. // InterlockedDecrement(&pdoExtension->DeletionLock); status = STATUS_NO_SUCH_DEVICE; Irp->IoStatus.Status = status; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x comp %08x\n", Pdo, Irp, status)); PoStartNextPowerIrp(Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension); switch (irpStack->Parameters.Power.Type) { case DevicePowerState: PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension); if (fdoExtension->DevicePowerState != PowerDeviceD0) { IoMarkIrpPending(Irp); status = STATUS_PENDING; InsertTailList(&fdoExtension->PdoPowerRetryList, (PLIST_ENTRY) Irp->Tail.Overlay.DriverContext); PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension); } else { PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension); status = PcmciaSetPdoDevicePowerState(Pdo, Irp); } break; case SystemPowerState: status = PcmciaSetPdoSystemPowerState(Pdo, Irp); break; default: status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, Irp->IoStatus.Status); } return status; } NTSTATUS PcmciaSetPdoDevicePowerState( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ) /*++ Routine Description Handles the device power state transition for the given pc-card. If the state corresponds to a power-up, the parent for this pc-card is requested to be powered up first. Similarily if this is a power-down the parent is notified so that it may power down if all the children are powered down. Arguments Pdo - Pointer to the physical device object for the pc-card Irp - Irp for the system state transition Return value status --*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PSOCKET socket = pdoExtension->Socket; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); DEVICE_POWER_STATE newDevicePowerState; NTSTATUS status; BOOLEAN setPowerRequest; newDevicePowerState = irpStack->Parameters.Power.State.DeviceState; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x transitioning D state %d => %d\n", Pdo, pdoExtension->DevicePowerState, newDevicePowerState)); setPowerRequest = FALSE; if (newDevicePowerState == PowerDeviceD0 || newDevicePowerState == PowerDeviceD1 || newDevicePowerState == PowerDeviceD2) { if (pdoExtension->DevicePowerState == PowerDeviceD3) { // D3 --> D0, D1 or D2 .. Wake up setPowerRequest = TRUE; SetDeviceFlag(pdoExtension, PCMCIA_POWER_WORKER_POWERUP); } else { // // Nothing to do here... // } } else { /* newDevicePowerState == D3 */ if (pdoExtension->DevicePowerState != PowerDeviceD3) { // // We need to power down now. // setPowerRequest=TRUE; ResetDeviceFlag(pdoExtension, PCMCIA_POWER_WORKER_POWERUP); } } if (setPowerRequest) { if (pdoExtension->DevicePowerState == PowerDeviceD0) { // // Getting out of D0 - Call PoSetPowerState first // POWER_STATE newPowerState; newPowerState.DeviceState = newDevicePowerState; PoSetPowerState(Pdo, DevicePowerState, newPowerState); } ASSERT(pdoExtension->PowerWorkerState == PPW_Stopped); pdoExtension->PowerWorkerState = PPW_InitialState; pdoExtension->PendingPowerIrp = Irp; status = PcmciaPdoPowerWorker(pdoExtension, STATUS_SUCCESS); } else { status = PcmciaPdoCompletePowerIrp(pdoExtension, Irp, STATUS_SUCCESS); } return status; } NTSTATUS PcmciaPdoPowerWorker( IN PVOID Context, IN NTSTATUS status ) /*++ Routine Description State machine for executing the requested DevicePowerState change. Arguments Context - pdoExtension for the device DeferredStatus - status from last deferred operation Return Value status --*/ { PPDO_EXTENSION pdoExtension = Context; PSOCKET socket = pdoExtension->Socket; PIRP Irp; UCHAR CurrentState = pdoExtension->PowerWorkerState; ULONG DelayTime = 0; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x power worker - %s\n", pdoExtension->DeviceObject, PDO_POWER_WORKER_STRING(CurrentState))); MoveToNextPdoPowerWorkerState(pdoExtension); if (!NT_SUCCESS(status)) { // // An error occurred previously. Skip to the end of the sequence // while((CurrentState != PPW_Exit) && (CurrentState != PPW_Stopped)) { CurrentState = pdoExtension->PowerWorkerState; MoveToNextPdoPowerWorkerState(pdoExtension); } } switch(CurrentState) { case PPW_InitialState: status = STATUS_SUCCESS; break; case PPW_PowerUp: status = PcmciaRequestSocketPower(pdoExtension, PcmciaPdoPowerWorker); break; case PPW_PowerUpComplete: if (!NT_SUCCESS(status)) { PcmciaReleaseSocketPower(pdoExtension, NULL); } break; case PPW_PowerDown: status = PcmciaReleaseSocketPower(pdoExtension, PcmciaPdoPowerWorker); break; case PPW_CardBusRefresh: // // Make sure the cardbus card is really working // status = PcmciaConfigureCardBusCard(pdoExtension); if (NT_SUCCESS(status) && pdoExtension->WaitWakeIrp) { // // Make sure stuff like PME_EN is on // (*socket->SocketFnPtr->PCBEnableDisableWakeupEvent)(socket, pdoExtension, TRUE); } break; case PPW_SendIrpDown: // // We're going to send the IRP down. Set completion routine // and copy the stack // if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) { IoMarkIrpPending(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, PcmciaPdoPowerSentIrpComplete, pdoExtension, TRUE, TRUE, TRUE); status = PoCallDriver(pdoExtension->LowerDevice, Irp); DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x sent irp returns %08x\n", pdoExtension->DeviceObject, Irp, status)); ASSERT(NT_SUCCESS(status)); status = STATUS_PENDING; } break; case PPW_CardBusDelay: // // Make sure the cardbus card is really working // { UCHAR BaseClass; GetPciConfigSpace(pdoExtension, CFGSPACE_CLASSCODE_BASECLASS, &BaseClass, 1) if (BaseClass == PCI_CLASS_SIMPLE_COMMS_CTLR) { // // Wait for modem to warm up // DelayTime = CBModemReadyDelay; } } break; case PPW_16BitConfigure: if (IsDeviceStarted(pdoExtension)) { status = PcmciaConfigurePcCard(pdoExtension, PcmciaPdoPowerWorker); DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x 16bit configure returns %08x\n", pdoExtension->DeviceObject, status)); } break; case PPW_Exit: if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) { // // This is the IRP (for the pdo) that originally caused us to power up the parent // Complete it now // if (NT_SUCCESS(status)) { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); BOOLEAN callPoSetPowerState; callPoSetPowerState = TRUE; Irp->IoStatus.Information = irpStack->Parameters.Power.State.DeviceState; if (irpStack->Parameters.Power.Type == DevicePowerState) { if (pdoExtension->DevicePowerState == PowerDeviceD0) { // // PoSetPowerState is called before we power down // callPoSetPowerState = FALSE; } if (pdoExtension->DevicePowerState != irpStack->Parameters.Power.State.DeviceState) { DebugPrint ((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x transition D state complete: %d => %d\n", pdoExtension->DeviceObject, Irp, pdoExtension->DevicePowerState, irpStack->Parameters.Power.State.DeviceState)); pdoExtension->DevicePowerState = (SYSTEM_POWER_STATE)Irp->IoStatus.Information; } } if (callPoSetPowerState) { // // we didn't get out of device D0 state. calling PoSetPowerState now // PoSetPowerState ( pdoExtension->DeviceObject, irpStack->Parameters.Power.Type, irpStack->Parameters.Power.State ); } } else { DebugPrint ((PCMCIA_DEBUG_FAIL,"PDO Ext 0x%x failed power Irp 0x%x. status = 0x%x\n", pdoExtension, Irp, status)); if (status == STATUS_NO_SUCH_DEVICE) { PFDO_EXTENSION fdoExtension=socket->DeviceExtension; SetSocketFlag(socket, SOCKET_CARD_STATUS_CHANGE); IoInvalidateDeviceRelations(fdoExtension->Pdo, BusRelations); } } // // Finally, complete the irp // pdoExtension->PendingPowerIrp = NULL; Irp->IoStatus.Status = status; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x comp %08x\n", pdoExtension->DeviceObject, Irp, Irp->IoStatus.Status)); InterlockedDecrement(&pdoExtension->DeletionLock); PoStartNextPowerIrp (Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); } break; case PPW_Stopped: return status; default: ASSERT(FALSE); } if (status == STATUS_PENDING) { DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x power worker exit %08x\n", pdoExtension->DeviceObject, status)); // // Current action calls us back // if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) { IoMarkIrpPending(Irp); } return status; } // // Not done yet. Recurse or call timer // if (DelayTime) { DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x power worker delay type %s, %d usec\n", pdoExtension->DeviceObject, (KeGetCurrentIrql() < DISPATCH_LEVEL) ? "Wait" : "Timer", DelayTime)); if (KeGetCurrentIrql() < DISPATCH_LEVEL) { PcmciaWait(DelayTime); } else { LARGE_INTEGER dueTime; // // Running on a DPC, kick of a kernel timer // pdoExtension->PowerWorkerDpcStatus = status; dueTime.QuadPart = -((LONG) DelayTime*10); KeSetTimer(&pdoExtension->PowerWorkerTimer, dueTime, &pdoExtension->PowerWorkerDpc); // // We will reenter on timer dpc // if ((Irp=pdoExtension->PendingPowerIrp)!=NULL) { IoMarkIrpPending(Irp); } return STATUS_PENDING; } } // // recurse // return (PcmciaPdoPowerWorker(pdoExtension, status)); } NTSTATUS PcmciaPdoPowerSentIrpComplete( IN PDEVICE_OBJECT Pdo, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description This is the completion routine for the device power IRPs sent by PCMCIA to the underlying PCI PDO for cardbus cards. All this does currently is return STATUS_MORE_PROCESSING_REQUIRED to indicate that we'll complete the Irp later. Arguments Pdo - Pointer to device object for the cardbus card Irp - Pointer to the IRP Context - Unreferenced Return Value STATUS_MORE_PROCESSING_REQUIRED --*/ { PPDO_EXTENSION pdoExtension = Context; #if !(DBG) UNREFERENCED_PARAMETER (Pdo); #endif DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x irp %08x sent irp complete %08x\n", Pdo, Irp, Irp->IoStatus.Status)); pdoExtension->PowerWorkerDpcStatus = Irp->IoStatus.Status; KeInsertQueueDpc(&pdoExtension->PowerWorkerDpc, NULL, NULL); return STATUS_MORE_PROCESSING_REQUIRED; } VOID PcmciaPdoPowerWorkerDpc( IN PKDPC Dpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description This is the completion routine for socket power requests coming from the PdoPowerWorker. Arguments Return Value --*/ { PPDO_EXTENSION pdoExtension = Context; NTSTATUS status; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x pdo power worker dpc\n", pdoExtension->DeviceObject)); status = PcmciaPdoPowerWorker(pdoExtension, pdoExtension->PowerWorkerDpcStatus); DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x pdo power worker dpc exit %08x\n", pdoExtension->DeviceObject, status)); } VOID MoveToNextPdoPowerWorkerState( PPDO_EXTENSION pdoExtension ) /*++ Routine Description State machine for executing the requested DevicePowerState change. Arguments Context - pdoExtension for the device DeferredStatus - status from last deferred operation Return Value status --*/ { static UCHAR PowerCardBusUpSequence[] = {PPW_CardBusRefresh, PPW_SendIrpDown, PPW_CardBusDelay, PPW_Exit, PPW_Stopped}; static UCHAR PowerCardBusDownSequence[] = {PPW_SendIrpDown, PPW_Exit, PPW_Stopped}; static UCHAR Power16BitUpSequence[] = {PPW_PowerUp, PPW_PowerUpComplete, PPW_16BitConfigure, PPW_Exit, PPW_Stopped}; static UCHAR Power16BitDownSequence[] = {PPW_PowerDown, PPW_Exit, PPW_Stopped}; if (pdoExtension->PowerWorkerState == PPW_InitialState) { // // Initialize sequence and phase // pdoExtension->PowerWorkerPhase = 0; pdoExtension->PowerWorkerSequence = IsCardBusCard(pdoExtension) ? (IsDeviceFlagSet(pdoExtension, PCMCIA_POWER_WORKER_POWERUP) ? PowerCardBusUpSequence : PowerCardBusDownSequence) : (IsDeviceFlagSet(pdoExtension, PCMCIA_POWER_WORKER_POWERUP) ? Power16BitUpSequence : Power16BitDownSequence); } // // The next state is pointed to by the current phase // pdoExtension->PowerWorkerState = pdoExtension->PowerWorkerSequence[ pdoExtension->PowerWorkerPhase ]; // // Increment the phase, but not past the end of the sequence // if (pdoExtension->PowerWorkerState != PPW_Stopped) { pdoExtension->PowerWorkerPhase++; } } NTSTATUS PcmciaSetPdoSystemPowerState( IN PDEVICE_OBJECT Pdo, IN OUT PIRP Irp ) /*++ Routine Description Handles the system power state transition for the given pc-card. Arguments Pdo - Pointer to the physical device object for the pc-card Irp - Irp for the system state transition Return value status --*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); SYSTEM_POWER_STATE systemPowerState; systemPowerState = irpStack->Parameters.Power.State.SystemState; DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x transitioning S state %d => %d\n", Pdo, pdoExtension->SystemPowerState, systemPowerState)); pdoExtension->SystemPowerState = systemPowerState; // // We are done. // return PcmciaPdoCompletePowerIrp(pdoExtension, Irp, STATUS_SUCCESS); } NTSTATUS PcmciaPdoCompletePowerIrp( IN PPDO_EXTENSION pdoExtension, IN PIRP Irp, IN NTSTATUS status ) /*++ Routine Description Completion routine for the Power Irp directed to the PDO of the pc-card. Arguments DeviceObject - Pointer to the PDO for the pc-card Irp - Irp that needs to be completed Return Value None --*/ { if (IsCardBusCard(pdoExtension)) { // // Pass irp down the stack // InterlockedDecrement(&pdoExtension->DeletionLock); PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); status = PoCallDriver(pdoExtension->LowerDevice, Irp); } else { // // Complete the irp for R2 cards // InterlockedDecrement(&pdoExtension->DeletionLock); Irp->IoStatus.Status = status; PoStartNextPowerIrp(Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; }