//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: power.c // //-------------------------------------------------------------------------- #include "pciidex.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PciIdeIssueSetPowerState) #pragma alloc_text(NONPAGE, PciIdePowerCompletionRoutine) #pragma alloc_text(NONPAGE, PciIdeSetPdoPowerState) #pragma alloc_text(NONPAGE, PciIdeSetFdoPowerState) #pragma alloc_text(NONPAGE, FdoContingentPowerCompletionRoutine) #pragma alloc_text(NONPAGE, FdoPowerCompletionRoutine) #pragma alloc_text(NONPAGE, FdoChildReportPowerDown) #pragma alloc_text(NONPAGE, FdoChildRequestPowerUp) #pragma alloc_text(NONPAGE, FdoChildRequestPowerUpCompletionRoutine) #endif // ALLOC_PRAGMA NTSTATUS PciIdeIssueSetPowerState ( IN PCTRLFDO_EXTENSION FdoExtension, IN POWER_STATE_TYPE Type, IN POWER_STATE State, IN BOOLEAN Sync ) { PIRP irp = NULL; PIO_STACK_LOCATION irpStack; SET_POWER_STATE_CONTEXT context; NTSTATUS status; CCHAR stackSize; PAGED_CODE(); if (Sync) { KeInitializeEvent( &context.Event, NotificationEvent, FALSE ); } stackSize = (CCHAR) (FdoExtension->DeviceObject->StackSize + 1); irp = IoAllocateIrp( stackSize, FALSE ); if (irp == NULL) { status = STATUS_NO_MEMORY; goto GetOut; } irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irpStack = IoGetNextIrpStackLocation(irp); irpStack->MajorFunction = IRP_MJ_POWER; irpStack->MinorFunction = IRP_MN_SET_POWER; irpStack->Parameters.Power.SystemContext = 0; irpStack->Parameters.Power.Type = Type; irpStack->Parameters.Power.State = State; IoSetCompletionRoutine(irp, PciIdePowerCompletionRoutine, Sync ? &context : NULL, TRUE, TRUE, TRUE); status = PoCallDriver(FdoExtension->DeviceObject, irp); // //Wait for the completion routine. It will be called anyway. // // if ((status == STATUS_PENDING) && (Sync)) { if (Sync) { KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL); status = context.Status; } GetOut: return status; } // PciIdeIssueSetPowerState NTSTATUS PciIdePowerCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PSET_POWER_STATE_CONTEXT context = Context; if (context) { context->Status = Irp->IoStatus.Status; KeSetEvent( &context->Event, EVENT_INCREMENT, FALSE ); } IoFreeIrp (Irp); return STATUS_MORE_PROCESSING_REQUIRED; } // PciIdePowerCompletionRoutine NTSTATUS PciIdeXQueryPowerState ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) { PDEVICE_EXTENSION_HEADER deviceExtensionHeader; NTSTATUS status; PIO_STACK_LOCATION irpStack; deviceExtensionHeader = DeviceObject->DeviceExtension; #if defined (DONT_POWER_DOWN_PAGING_DEVICE) irpStack = IoGetCurrentIrpStackLocation (Irp); if (!deviceExtensionHeader->CrashDumpPathCount || ((irpStack->Parameters.Power.Type == SystemPowerState) && (irpStack->Parameters.Power.State.SystemState == PowerSystemWorking)) || ((irpStack->Parameters.Power.Type == DevicePowerState) && (irpStack->Parameters.Power.State.SystemState == PowerDeviceD0))) { status = STATUS_SUCCESS; } else { status = STATUS_DEVICE_POWER_FAILURE; } #else status = STATUS_SUCCESS; #endif // DONT_POWER_DOWN_PAGING_DEVICE Irp->IoStatus.Status = status; PoStartNextPowerIrp (Irp); if (deviceExtensionHeader->AttacheeDeviceObject != NULL) { IoCopyCurrentIrpStackLocationToNext (Irp); status = PoCallDriver (deviceExtensionHeader->AttacheeDeviceObject, Irp); } else { IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } NTSTATUS PciIdeSetPdoPowerState ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) { PIO_STACK_LOCATION irpStack; PCHANPDO_EXTENSION pdoExtension; NTSTATUS status; pdoExtension = ChannelGetPdoExtension(DeviceObject); if (pdoExtension == NULL) { status = STATUS_NO_SUCH_DEVICE; } else { irpStack = IoGetCurrentIrpStackLocation (Irp); status = STATUS_SUCCESS; if (irpStack->Parameters.Power.Type == SystemPowerState) { if (pdoExtension->SystemPowerState != irpStack->Parameters.Power.State.SystemState) { POWER_STATE powerState; pdoExtension->SystemPowerState = irpStack->Parameters.Power.State.SystemState; DebugPrint ((1, "PciIdeX: New Pdo 0x%x system power state 0x%x\n", pdoExtension->ChannelNumber, irpStack->Parameters.Power.State.SystemState)); } } else if (irpStack->Parameters.Power.Type == DevicePowerState) { if (pdoExtension->DevicePowerState != irpStack->Parameters.Power.State.DeviceState) { // // checking old device state // if (pdoExtension->DevicePowerState == PowerDeviceD3) { // // waking up // IoMarkIrpPending(Irp); Irp->IoStatus.Information = irpStack->Parameters.Power.State.DeviceState; status = FdoChildRequestPowerUp ( pdoExtension->ParentDeviceExtension, pdoExtension, Irp ); ASSERT (NT_SUCCESS(status)); status = STATUS_PENDING; } else { if (pdoExtension->DevicePowerState == PowerDeviceD0) { // // getting out of D0 state, better call PoSetPowerState now // PoSetPowerState ( DeviceObject, DevicePowerState, irpStack->Parameters.Power.State ); } DebugPrint ((1, "PciIdeX: New Pdo 0x%x device power state 0x%x\n", pdoExtension->ChannelNumber, irpStack->Parameters.Power.State.DeviceState)); pdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState; if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) { // // tell parent that we just fell to sleep // FdoChildReportPowerDown ( pdoExtension->ParentDeviceExtension, pdoExtension ); } } } } else { ASSERT (FALSE); status = STATUS_NOT_IMPLEMENTED; } } if (status != STATUS_PENDING) { if (NT_SUCCESS(status)) { Irp->IoStatus.Information = irpStack->Parameters.Power.State.SystemState; } Irp->IoStatus.Status = status; PoStartNextPowerIrp (Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } // PciIdeSetPdoPowerState NTSTATUS PciIdeSetFdoPowerState ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) { NTSTATUS status = STATUS_SUCCESS; PCTRLFDO_EXTENSION fdoExtension; PIO_STACK_LOCATION irpStack; PFDO_POWER_CONTEXT context; BOOLEAN noCompletionRoutine; BOOLEAN systemPowerContext = FALSE; BOOLEAN devicePowerContext = FALSE; fdoExtension = DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation (Irp); //context = ExAllocatePool (NonPagedPool, sizeof(FDO_POWER_CONTEXT)); // // We need two pre-alloced context structures. This is because a system power irp // would result in a device power irp to be issued before the former is completed. // if (irpStack->Parameters.Power.Type == SystemPowerState) { ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 1, 0) == 0); context = &(fdoExtension->FdoPowerContext[0]); systemPowerContext = TRUE; } else { ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 1, 0) == 0); context = &(fdoExtension->FdoPowerContext[1]); devicePowerContext = TRUE; } if (context == NULL) { ASSERT(context); status = STATUS_NO_MEMORY; } else { RtlZeroMemory (context, sizeof(FDO_POWER_CONTEXT)); //irpStack = IoGetCurrentIrpStackLocation (Irp); context->OriginalPowerIrp = Irp; context->newPowerType = irpStack->Parameters.Power.Type; context->newPowerState = irpStack->Parameters.Power.State; noCompletionRoutine = FALSE; if (irpStack->Parameters.Power.Type == SystemPowerState) { if (fdoExtension->SystemPowerState != irpStack->Parameters.Power.State.SystemState) { POWER_STATE powerState; BOOLEAN requestPowerState = FALSE; if ((irpStack->Parameters.Power.State.SystemState == PowerSystemShutdown) && (irpStack->Parameters.Power.ShutdownType == PowerActionShutdownReset)) { // // spin up for BIOS POST // requestPowerState = TRUE; powerState.DeviceState = PowerDeviceD0; } else if (fdoExtension->SystemPowerState == PowerSystemWorking) { // // we are getting out of working state...power down // requestPowerState = TRUE; powerState.DeviceState = PowerDeviceD3; } if (requestPowerState) { IoMarkIrpPending(Irp); PoRequestPowerIrp ( fdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, FdoContingentPowerCompletionRoutine, context, NULL ); return STATUS_PENDING; } } else { // // We are already in the given state // noCompletionRoutine = TRUE; } } else if (irpStack->Parameters.Power.Type == DevicePowerState) { if (fdoExtension->DevicePowerState != irpStack->Parameters.Power.State.DeviceState) { if (fdoExtension->DevicePowerState == PowerDeviceD0) { // // getting out of D0 state, better call PoSetPowerState now // PoSetPowerState ( DeviceObject, DevicePowerState, irpStack->Parameters.Power.State ); } } else { // // We are already in the given state // noCompletionRoutine = TRUE; } } else { ASSERT (FALSE); status = STATUS_NOT_IMPLEMENTED; } } if (NT_SUCCESS(status)) { IoMarkIrpPending (Irp); IoCopyCurrentIrpStackLocationToNext (Irp); if (!noCompletionRoutine) { IoSetCompletionRoutine(Irp, FdoPowerCompletionRoutine, context, TRUE, TRUE, TRUE); } else { if (context) { //ExFreePool (context); if (systemPowerContext) { ASSERT(devicePowerContext == FALSE); ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1); } if (devicePowerContext) { ASSERT(systemPowerContext == FALSE); ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 0, 1) == 1); } } PoStartNextPowerIrp (Irp); } PoCallDriver (fdoExtension->AttacheeDeviceObject, Irp); return STATUS_PENDING; } else { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; if (context) { //ExFreePool (context); if (systemPowerContext) { ASSERT(devicePowerContext == FALSE); ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1); } if (devicePowerContext) { ASSERT(systemPowerContext == FALSE); ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 0, 1) == 1); } } PoStartNextPowerIrp (Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } } // PciIdeSetFdoPowerState NTSTATUS FdoContingentPowerCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { PFDO_POWER_CONTEXT context = Context; PIRP irp; PCTRLFDO_EXTENSION fdoExtension; fdoExtension = DeviceObject->DeviceExtension; irp = context->OriginalPowerIrp; if (NT_SUCCESS(IoStatus->Status)) { IoCopyCurrentIrpStackLocationToNext (irp); IoSetCompletionRoutine(irp, FdoPowerCompletionRoutine, context, TRUE, TRUE, TRUE); PoCallDriver (fdoExtension->AttacheeDeviceObject, irp); } else { irp->IoStatus.Information = 0; irp->IoStatus.Status = IoStatus->Status; //ExFreePool (context); ASSERT(context->newPowerType == SystemPowerState); ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1); PoStartNextPowerIrp (irp); IoCompleteRequest(irp, IO_NO_INCREMENT); } return IoStatus->Status; } // FdoContingentPowerCompletionRoutine NTSTATUS FdoPowerCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PFDO_POWER_CONTEXT context = Context; BOOLEAN callPoSetPowerState = FALSE; PCTRLFDO_EXTENSION fdoExtension; POWER_STATE newPowerState; POWER_STATE_TYPE newPowerType; BOOLEAN unlocked = FALSE; BOOLEAN moreProcessingRequired = FALSE; NTSTATUS status; fdoExtension = DeviceObject->DeviceExtension; newPowerType = context->newPowerType; newPowerState = context->newPowerState; if (NT_SUCCESS(Irp->IoStatus.Status)) { callPoSetPowerState = TRUE; if (context->newPowerType == SystemPowerState) { fdoExtension->SystemPowerState = context->newPowerState.SystemState; if (fdoExtension->SystemPowerState == PowerSystemWorking) { POWER_STATE powerState; ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1); unlocked = TRUE; moreProcessingRequired = TRUE; // // initiate a D0 here to cause a re-enumuration // powerState.DeviceState = PowerDeviceD0; status = PoRequestPowerIrp ( fdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, FdoSystemPowerUpCompletionRoutine, Irp, NULL ); ASSERT(status == STATUS_PENDING); } DebugPrint ((1, "PciIdeX: New Fdo system power state 0x%x\n", fdoExtension->SystemPowerState)); } else if (context->newPowerType == DevicePowerState) { if (fdoExtension->DevicePowerState == PowerDeviceD0) { // // PoSetPowerState is called before we get out of D0 // callPoSetPowerState = FALSE; } fdoExtension->DevicePowerState = context->newPowerState.DeviceState; if (fdoExtension->DevicePowerState == PowerDeviceD0) { // // Re-enumerate the devices on the channel // EnablePCIBusMastering (fdoExtension); IoInvalidateDeviceRelations ( fdoExtension->AttacheePdo, BusRelations ); } DebugPrint ((1, "PciIdeX: New Fdo device power state 0x%x\n", fdoExtension->DevicePowerState)); } if (callPoSetPowerState) { PoSetPowerState ( DeviceObject, newPowerType, newPowerState ); } } //ExFreePool (Context); if (!unlocked) { if (context->newPowerType == SystemPowerState) { ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1); } else { ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 0, 1) == 1); } } // // wait for the device irp to complete // if (moreProcessingRequired) { return STATUS_MORE_PROCESSING_REQUIRED; } // // If pending has be returned for this irp then mark the current stack as // pending. // // if (Irp->PendingReturned) { // IoMarkIrpPending(Irp); //} PoStartNextPowerIrp (Irp); return Irp->IoStatus.Status; } // FdoPowerCompletionRoutine VOID FdoChildReportPowerDown ( IN PCTRLFDO_EXTENSION FdoExtension, IN PCHANPDO_EXTENSION PdoExtension ) { POWER_STATE powerState; ULONG numChildrenPowerUp; ASSERT(FdoExtension->NumberOfChildrenPowerUp > 0); numChildrenPowerUp = InterlockedDecrement(&FdoExtension->NumberOfChildrenPowerUp); if (numChildrenPowerUp == 0) { DebugPrint ((1, "PciIdeX FdoChildReportPowerDown: sleep fdo 0x%x\n", FdoExtension)); // // All the children are powered down, we can now power down // the parent (the controller) // powerState.DeviceState = PowerDeviceD3; PoRequestPowerIrp ( FdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL ); } // else if (numChildrenPowerUp < 0) { // // should never happen. If it did, pretend it didn't // // ASSERT (FALSE); // InterlockedExchange(&FdoExtension->NumberOfChildrenPowerUp, 0); //} return; } // FdoChildReportPowerDown NTSTATUS FdoChildRequestPowerUp ( IN PCTRLFDO_EXTENSION FdoExtension, IN PCHANPDO_EXTENSION PdoExtension, IN PIRP ChildPowerIrp ) { NTSTATUS status; POWER_STATE powerState; ULONG numberOfChildrenPowerUp; IO_STATUS_BLOCK IoStatus; status = STATUS_SUCCESS; numberOfChildrenPowerUp = InterlockedExchange ( &FdoExtension->NumberOfChildrenPowerUp, FdoExtension->NumberOfChildrenPowerUp ); if (numberOfChildrenPowerUp == 0) { DebugPrint ((1, "PciIdeX FdoChildRequestPowerUp: wake up fdo 0x%x\n", FdoExtension)); // // One of the children is coming out of sleep, // we need to power up the parent (the controller) // powerState.DeviceState = PowerDeviceD0; status = PoRequestPowerIrp ( FdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, FdoChildRequestPowerUpCompletionRoutine, ChildPowerIrp, NULL ); ASSERT (NT_SUCCESS(status)); status = STATUS_PENDING; } else { powerState.DeviceState = PowerDeviceD0; IoStatus.Information = PowerDeviceD0; IoStatus.Status = STATUS_SUCCESS; FdoChildRequestPowerUpCompletionRoutine ( FdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, ChildPowerIrp, &IoStatus ); status = STATUS_PENDING; } return status; } // FdoChildRequestPowerUp NTSTATUS FdoChildRequestPowerUpCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { PIRP childPowerIrp = Context; PCTRLFDO_EXTENSION fdoExtension; PCHANPDO_EXTENSION pdoExtension; PIO_STACK_LOCATION irpStack; fdoExtension = DeviceObject->DeviceExtension; if (NT_SUCCESS(IoStatus->Status)) { ULONG numberOfChildrenPowerUp; numberOfChildrenPowerUp = InterlockedIncrement (&fdoExtension->NumberOfChildrenPowerUp); irpStack = IoGetCurrentIrpStackLocation (childPowerIrp); pdoExtension = irpStack->DeviceObject->DeviceExtension; pdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState; if (numberOfChildrenPowerUp > fdoExtension->NumberOfChildren) { // // should never happen. If it did, pretend it didn't // ASSERT (FALSE); fdoExtension->NumberOfChildrenPowerUp = fdoExtension->NumberOfChildren; } } childPowerIrp->IoStatus.Status = IoStatus->Status; PoStartNextPowerIrp (childPowerIrp); IoCompleteRequest(childPowerIrp, IO_NO_INCREMENT); return IoStatus->Status; } // FdoChildRequestPowerUpCompletionRoutine NTSTATUS FdoSystemPowerUpCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { PIRP irp = Context; // // start the next system power irp // PoStartNextPowerIrp (irp); if (!NT_SUCCESS(IoStatus->Status)) { irp->IoStatus.Status = IoStatus->Status; } IoCompleteRequest(irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; }