903 lines
21 KiB
C
903 lines
21 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
power.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code that handles the power IRPs for the serial
|
||
driver.
|
||
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History :
|
||
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGESRP0, SerialGotoPowerState)
|
||
#pragma alloc_text(PAGESRP0, SerialPowerDispatch)
|
||
#pragma alloc_text(PAGESRP0, SerialSetPowerD0)
|
||
#pragma alloc_text(PAGESRP0, SerialSetPowerD3)
|
||
#pragma alloc_text(PAGESRP0, SerialSaveDeviceState)
|
||
#pragma alloc_text(PAGESRP0, SerialRestoreDeviceState)
|
||
#pragma alloc_text(PAGESRP0, SerialSendWaitWake)
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
|
||
NTSTATUS
|
||
SerialSystemPowerCompletion(IN PDEVICE_OBJECT PDevObj, UCHAR MinorFunction,
|
||
IN POWER_STATE PowerState, IN PVOID Context,
|
||
PIO_STATUS_BLOCK IoStatus)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the completion routine for PoRequestPowerIrp calls
|
||
in this module.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the device object the irp is completing for
|
||
|
||
MinorFunction - IRP_MN_XXXX value requested
|
||
|
||
PowerState - Power state request was made of
|
||
|
||
Context - Event to set or NULL if no setting required
|
||
|
||
IoStatus - Status block from request
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
|
||
--*/
|
||
{
|
||
if (Context != NULL) {
|
||
KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, 0);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SerialSaveDeviceState(IN PSERIAL_DEVICE_EXTENSION PDevExt)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine saves the device state of the UART
|
||
|
||
Arguments:
|
||
|
||
PDevExt - Pointer to the device extension for the devobj to save the state
|
||
for.
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_STATE pDevState = &PDevExt->DeviceState;
|
||
#if defined(NEC_98)
|
||
//
|
||
// This argument use at MACRO only.
|
||
//
|
||
PSERIAL_DEVICE_EXTENSION Extension = PDevExt;
|
||
#else
|
||
#endif //defined(NEC_98)
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: Entering SerialSaveDeviceState\n"));
|
||
|
||
//
|
||
// Read necessary registers direct
|
||
//
|
||
|
||
#if defined(NEC_98)
|
||
pDevState->IER = READ_INTERRUPT_ENABLE(Extension->Controller);
|
||
#else
|
||
pDevState->IER = READ_INTERRUPT_ENABLE(PDevExt->Controller);
|
||
#endif //defined(NEC_98)
|
||
pDevState->MCR = READ_MODEM_CONTROL(PDevExt->Controller);
|
||
pDevState->LCR = READ_LINE_CONTROL(PDevExt->Controller);
|
||
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: Leaving SerialSaveDeviceState\n"));
|
||
}
|
||
|
||
|
||
VOID
|
||
SerialRestoreDeviceState(IN PSERIAL_DEVICE_EXTENSION PDevExt)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine restores the device state of the UART
|
||
|
||
Arguments:
|
||
|
||
PDevExt - Pointer to the device extension for the devobj to restore the
|
||
state for.
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_STATE pDevState = &PDevExt->DeviceState;
|
||
SHORT divisor;
|
||
SERIAL_IOCTL_SYNC S;
|
||
#if defined(NEC_98)
|
||
//
|
||
// This argument use at MACRO only.
|
||
//
|
||
PSERIAL_DEVICE_EXTENSION Extension = PDevExt;
|
||
#else
|
||
#endif //defined(NEC_98)
|
||
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: Enter SerialRestoreDeviceState\n"));
|
||
SerialDump(SERTRACECALLS, ("------ PDevExt: %x\n", PDevExt));
|
||
|
||
//
|
||
// Disable interrupts both via OUT2 and IER
|
||
//
|
||
|
||
WRITE_MODEM_CONTROL(PDevExt->Controller, 0);
|
||
DISABLE_ALL_INTERRUPTS(PDevExt->Controller);
|
||
|
||
//
|
||
// Set the baud rate
|
||
//
|
||
|
||
SerialGetDivisorFromBaud(PDevExt->ClockRate, PDevExt->CurrentBaud, &divisor);
|
||
S.Extension = PDevExt;
|
||
S.Data = (PVOID)divisor;
|
||
SerialSetBaud(&S);
|
||
|
||
//
|
||
// Reset / Re-enable the FIFO's
|
||
//
|
||
|
||
if (PDevExt->FifoPresent) {
|
||
WRITE_FIFO_CONTROL(PDevExt->Controller, (UCHAR)0);
|
||
READ_RECEIVE_BUFFER(PDevExt->Controller);
|
||
WRITE_FIFO_CONTROL(PDevExt->Controller,
|
||
(UCHAR)(SERIAL_FCR_ENABLE | PDevExt->RxFifoTrigger
|
||
| SERIAL_FCR_RCVR_RESET
|
||
| SERIAL_FCR_TXMT_RESET));
|
||
} else {
|
||
WRITE_FIFO_CONTROL(PDevExt->Controller, (UCHAR)0);
|
||
}
|
||
|
||
//
|
||
// In case we are dealing with a bitmasked multiportcard,
|
||
// that has the mask register enabled, enable the
|
||
// interrupts.
|
||
//
|
||
|
||
if (PDevExt->InterruptStatus) {
|
||
if (PDevExt->Indexed) {
|
||
WRITE_PORT_UCHAR(PDevExt->InterruptStatus, (UCHAR)0xFF);
|
||
} else {
|
||
//
|
||
// Either we are standalone or already mapped
|
||
//
|
||
|
||
if (PDevExt->OurIsrContext == PDevExt) {
|
||
//
|
||
// This is a standalone
|
||
//
|
||
|
||
WRITE_PORT_UCHAR(PDevExt->InterruptStatus,
|
||
(UCHAR)(1 << (PDevExt->PortIndex - 1)));
|
||
} else {
|
||
//
|
||
// One of many
|
||
//
|
||
|
||
WRITE_PORT_UCHAR(PDevExt->InterruptStatus,
|
||
(UCHAR)((PSERIAL_MULTIPORT_DISPATCH)PDevExt->
|
||
OurIsrContext)->UsablePortMask);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Restore a couple more registers
|
||
//
|
||
|
||
#if defined(NEC_98)
|
||
WRITE_INTERRUPT_ENABLE(Extension->Controller, pDevState->IER);
|
||
#else
|
||
WRITE_INTERRUPT_ENABLE(PDevExt->Controller, pDevState->IER);
|
||
#endif //defined(NEC_98)
|
||
WRITE_LINE_CONTROL(PDevExt->Controller, pDevState->LCR);
|
||
|
||
//
|
||
// Clear out any stale interrupts
|
||
//
|
||
|
||
READ_INTERRUPT_ID_REG(PDevExt->Controller);
|
||
READ_LINE_STATUS(PDevExt->Controller);
|
||
READ_MODEM_STATUS(PDevExt->Controller);
|
||
|
||
|
||
if (PDevExt->DeviceState.Reopen == TRUE) {
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Reopening device\n"));
|
||
|
||
PDevExt->DeviceIsOpened = TRUE;
|
||
PDevExt->DeviceState.Reopen = FALSE;
|
||
|
||
//
|
||
// This enables interrupts on the device!
|
||
//
|
||
|
||
WRITE_MODEM_CONTROL(PDevExt->Controller,
|
||
(UCHAR)(pDevState->MCR | SERIAL_MCR_OUT2));
|
||
|
||
//
|
||
// Refire the state machine
|
||
//
|
||
|
||
DISABLE_ALL_INTERRUPTS(PDevExt->Controller);
|
||
ENABLE_ALL_INTERRUPTS(PDevExt->Controller);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SerialPowerDispatch(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a dispatch routine for the IRPs that come to the driver with the
|
||
IRP_MJ_POWER major code (power IRPs).
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the device object for this device
|
||
|
||
PIrp - Pointer to the IRP for the current request
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the call
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
||
NTSTATUS status;
|
||
PDEVICE_OBJECT pLowerDevObj = pDevExt->LowerDeviceObject;
|
||
PDEVICE_OBJECT pPdo = pDevExt->Pdo;
|
||
BOOLEAN acceptingIRPs;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((status = SerialIRPPrologue(PIrp, pDevExt)) != STATUS_SUCCESS) {
|
||
PoStartNextPowerIrp(PIrp);
|
||
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
switch (pIrpStack->MinorFunction) {
|
||
|
||
case IRP_MN_WAIT_WAKE:
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_WAIT_WAKE Irp\n"));
|
||
break;
|
||
|
||
|
||
case IRP_MN_POWER_SEQUENCE:
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_POWER_SEQUENCE Irp\n"));
|
||
break;
|
||
|
||
|
||
case IRP_MN_SET_POWER:
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_SET_POWER Irp\n"));
|
||
|
||
//
|
||
// Perform different ops if it was system or device
|
||
//
|
||
|
||
switch (pIrpStack->Parameters.Power.Type) {
|
||
case SystemPowerState: {
|
||
POWER_STATE powerState;
|
||
|
||
//
|
||
// They asked for a system power state change
|
||
//
|
||
|
||
SerialDump(SERPNPPOWER, ("------: SystemPowerState\n"));
|
||
|
||
//
|
||
// We will only service this if we are policy owner -- we
|
||
// don't need to lock on this value since we only service
|
||
// one power request at a time.
|
||
//
|
||
|
||
if (pDevExt->OwnsPowerPolicy != TRUE) {
|
||
status = STATUS_SUCCESS;
|
||
goto PowerExit;
|
||
}
|
||
|
||
|
||
switch (pIrpStack->Parameters.Power.State.SystemState) {
|
||
case PowerSystemUnspecified:
|
||
powerState.DeviceState = PowerDeviceUnspecified;
|
||
break;
|
||
|
||
case PowerSystemWorking:
|
||
powerState.DeviceState = PowerDeviceD0;
|
||
break;
|
||
|
||
case PowerSystemSleeping1:
|
||
case PowerSystemSleeping2:
|
||
case PowerSystemSleeping3:
|
||
case PowerSystemHibernate:
|
||
case PowerSystemShutdown:
|
||
case PowerSystemMaximum:
|
||
powerState.DeviceState = PowerDeviceD3;
|
||
break;
|
||
|
||
default:
|
||
status = STATUS_SUCCESS;
|
||
goto PowerExit;
|
||
break;
|
||
}
|
||
|
||
|
||
PoSetPowerState(PDevObj, pIrpStack->Parameters.Power.Type,
|
||
pIrpStack->Parameters.Power.State);
|
||
|
||
//
|
||
// Send IRP to change device state if we should change
|
||
//
|
||
|
||
//
|
||
// We only power up the stack if the device is open. This is based
|
||
// on our policy of keeping the device powered down unless it is
|
||
// open.
|
||
//
|
||
|
||
if (((powerState.DeviceState < pDevExt->PowerState)
|
||
&& pDevExt->OpenCount)) {
|
||
PoRequestPowerIrp(pPdo, IRP_MN_SET_POWER, powerState, NULL, NULL,
|
||
NULL);
|
||
}else {
|
||
//
|
||
// If powering down, we can't go past wake state
|
||
// if wait-wake pending
|
||
//
|
||
|
||
if (powerState.DeviceState >= pDevExt->PowerState) {
|
||
|
||
//
|
||
// Power down -- ensure there is no wake-wait pending OR
|
||
// we can do down to that level and still wake the machine
|
||
//
|
||
|
||
if ((pDevExt->PendingWakeIrp == NULL && !pDevExt->SendWaitWake)
|
||
|| powerState.DeviceState <= pDevExt->DeviceWake) {
|
||
PoRequestPowerIrp(pPdo, IRP_MN_SET_POWER, powerState, NULL,
|
||
NULL, NULL);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
status = STATUS_SUCCESS;
|
||
goto PowerExit;
|
||
}
|
||
|
||
case DevicePowerState:
|
||
SerialDump(SERPNPPOWER, ("------: DevicePowerState\n"));
|
||
break;
|
||
|
||
default:
|
||
SerialDump(SERPNPPOWER, ("------: UNKNOWN PowerState\n"));
|
||
status = STATUS_SUCCESS;
|
||
goto PowerExit;
|
||
}
|
||
|
||
|
||
//
|
||
// If we are already in the requested state, just pass the IRP down
|
||
//
|
||
|
||
if (pDevExt->PowerState
|
||
== pIrpStack->Parameters.Power.State.DeviceState) {
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Already in requested power state\n")
|
||
);
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
}
|
||
|
||
|
||
switch (pIrpStack->Parameters.Power.State.DeviceState) {
|
||
|
||
case PowerDeviceD0:
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Going to power state D0\n"));
|
||
return SerialSetPowerD0(PDevObj, PIrp);
|
||
|
||
case PowerDeviceD1:
|
||
case PowerDeviceD2:
|
||
case PowerDeviceD3:
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Going to power state D3\n"));
|
||
return SerialSetPowerD3(PDevObj, PIrp);
|
||
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
|
||
|
||
|
||
case IRP_MN_QUERY_POWER:
|
||
|
||
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_QUERY_POWER Irp\n"));
|
||
|
||
//
|
||
// Check if we have a wait-wake pending and if so,
|
||
// ensure we don't power down too far.
|
||
//
|
||
|
||
if (pDevExt->PendingWakeIrp != NULL || pDevExt->SendWaitWake) {
|
||
if (pIrpStack->Parameters.Power.Type == DevicePowerState
|
||
&& pIrpStack->Parameters.Power.State.DeviceState
|
||
> pDevExt->DeviceWake) {
|
||
status = PIrp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE;
|
||
PoStartNextPowerIrp(PIrp);
|
||
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If no wait-wake, always successful
|
||
//
|
||
|
||
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
status = STATUS_SUCCESS;
|
||
PoStartNextPowerIrp(PIrp);
|
||
IoSkipCurrentIrpStackLocation(PIrp);
|
||
return SerialPoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
||
|
||
} // switch (pIrpStack->MinorFunction)
|
||
|
||
|
||
PowerExit:;
|
||
|
||
PoStartNextPowerIrp(PIrp);
|
||
|
||
|
||
//
|
||
// Pass to the lower driver
|
||
//
|
||
IoSkipCurrentIrpStackLocation(PIrp);
|
||
status = SerialPoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SerialSetPowerD0(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine Decides if we need to pass the power Irp down the stack
|
||
or not. It then either sets up a completion handler to finish the
|
||
initialization or calls the completion handler directly.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the devobj we are changing power state on
|
||
|
||
PIrp - Pointer to the IRP for the current request
|
||
|
||
Return Value:
|
||
|
||
Return status of either PoCallDriver of the call to the initialization
|
||
routine.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: In SerialSetPowerD0\n"));
|
||
SerialDump(SERPNPPOWER, ("SERIAL: SetPowerD0 has IRP %x\n", PIrp));
|
||
|
||
ASSERT(pDevExt->LowerDeviceObject);
|
||
|
||
//
|
||
// Set up completion to init device when it is on
|
||
//
|
||
|
||
KeClearEvent(&pDevExt->PowerD0Event);
|
||
|
||
|
||
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
||
IoSetCompletionRoutine(PIrp, SerialSyncCompletion, &pDevExt->PowerD0Event,
|
||
TRUE, TRUE, TRUE);
|
||
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Calling next driver\n"));
|
||
|
||
status = PoCallDriver(pDevExt->LowerDeviceObject, PIrp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
SerialDump(SERPNPPOWER, ("SERIAL: Waiting for next driver\n"));
|
||
KeWaitForSingleObject (&pDevExt->PowerD0Event, Executive, KernelMode,
|
||
FALSE, NULL);
|
||
} else {
|
||
if (!NT_SUCCESS(status)) {
|
||
PIrp->IoStatus.Status = status;
|
||
PoStartNextPowerIrp(PIrp);
|
||
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(PIrp->IoStatus.Status)) {
|
||
status = PIrp->IoStatus.Status;
|
||
PoStartNextPowerIrp(PIrp);
|
||
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Restore the device
|
||
//
|
||
|
||
pDevExt->PowerState = PowerDeviceD0;
|
||
|
||
|
||
//
|
||
// Theoretically we could change states in the middle of processing
|
||
// the restore which would result in a bad PKINTERRUPT being used
|
||
// in SerialRestoreDeviceState().
|
||
//
|
||
|
||
if (pDevExt->PNPState == SERIAL_PNP_STARTED) {
|
||
SerialRestoreDeviceState(pDevExt);
|
||
}
|
||
|
||
//
|
||
// Now that we are powered up, call PoSetPowerState
|
||
//
|
||
|
||
PoSetPowerState(PDevObj, pIrpStack->Parameters.Power.Type,
|
||
pIrpStack->Parameters.Power.State);
|
||
|
||
PoStartNextPowerIrp(PIrp);
|
||
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
||
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: Leaving SerialSetPowerD0\n"));
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SerialGotoPowerState(IN PDEVICE_OBJECT PDevObj,
|
||
IN PSERIAL_DEVICE_EXTENSION PDevExt,
|
||
IN DEVICE_POWER_STATE DevPowerState)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes the driver to request the stack go to a particular
|
||
power state.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the device object for this device
|
||
|
||
PDevExt - Pointer to the device extension we are working from
|
||
|
||
DevPowerState - the power state we wish to go to
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the call
|
||
|
||
|
||
--*/
|
||
{
|
||
KEVENT gotoPowEvent;
|
||
NTSTATUS status;
|
||
POWER_STATE powerState;
|
||
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: In SerialGotoPowerState\n"));
|
||
|
||
powerState.DeviceState = DevPowerState;
|
||
|
||
KeInitializeEvent(&gotoPowEvent, SynchronizationEvent, FALSE);
|
||
|
||
status = PoRequestPowerIrp(PDevObj, IRP_MN_SET_POWER, powerState,
|
||
SerialSystemPowerCompletion, &gotoPowEvent,
|
||
NULL);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&gotoPowEvent, Executive, KernelMode, FALSE, NULL);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
#if DBG
|
||
if (!NT_SUCCESS(status)) {
|
||
SerialDump(SERPNPPOWER, ("SERIAL: SerialGotoPowerState FAILED\n"));
|
||
}
|
||
#endif
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: Leaving SerialGotoPowerState\n"));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SerialSetPowerD3(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the SET_POWER minor function.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the device object for this device
|
||
|
||
PIrp - Pointer to the IRP for the current request
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the call
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
||
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERDIAG3, ("SERIAL: In SerialSetPowerD3\n"));
|
||
|
||
//
|
||
// Send the wait wake now, just in time
|
||
//
|
||
|
||
|
||
if (pDevExt->SendWaitWake) {
|
||
SerialSendWaitWake(pDevExt);
|
||
}
|
||
//
|
||
// Before we power down, call PoSetPowerState
|
||
//
|
||
|
||
PoSetPowerState(PDevObj, pIrpStack->Parameters.Power.Type,
|
||
pIrpStack->Parameters.Power.State);
|
||
|
||
//
|
||
// If the device is not closed, disable interrupts and allow the fifo's
|
||
// to flush.
|
||
//
|
||
|
||
if (pDevExt->DeviceIsOpened == TRUE) {
|
||
LARGE_INTEGER charTime;
|
||
|
||
pDevExt->DeviceIsOpened = FALSE;
|
||
pDevExt->DeviceState.Reopen = TRUE;
|
||
|
||
charTime.QuadPart = -SerialGetCharTime(pDevExt).QuadPart;
|
||
|
||
//
|
||
// Shut down the chip
|
||
//
|
||
|
||
SerialDisableUART(pDevExt);
|
||
|
||
//
|
||
// Drain the device
|
||
//
|
||
|
||
SerialDrainUART(pDevExt, &charTime);
|
||
|
||
//
|
||
// Save the device state
|
||
//
|
||
|
||
SerialSaveDeviceState(pDevExt);
|
||
}
|
||
|
||
//
|
||
// If the device is not open, we don't need to save the state;
|
||
// we can just reset the device on power-up
|
||
//
|
||
|
||
|
||
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
pDevExt->PowerState = PowerDeviceD3;
|
||
|
||
//
|
||
// For what we are doing, we don't need a completion routine
|
||
// since we don't race on the power requests.
|
||
//
|
||
|
||
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
PoStartNextPowerIrp(PIrp);
|
||
IoSkipCurrentIrpStackLocation(PIrp);
|
||
|
||
return SerialPoCallDriver(pDevExt, pDevExt->LowerDeviceObject, PIrp);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SerialSendWaitWake(PSERIAL_DEVICE_EXTENSION PDevExt)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes a waitwake IRP to be sent
|
||
|
||
Arguments:
|
||
|
||
PDevExt - Pointer to the device extension for this device
|
||
|
||
Return Value:
|
||
|
||
STATUS_INVALID_DEVICE_STATE if one is already pending, else result
|
||
of call to PoRequestPowerIrp.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIRP pIrp;
|
||
POWER_STATE powerState;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Make sure one isn't pending already -- serial will only handle one at
|
||
// a time.
|
||
//
|
||
|
||
if (PDevExt->PendingWakeIrp != NULL) {
|
||
return STATUS_INVALID_DEVICE_STATE;
|
||
}
|
||
|
||
//
|
||
// Make sure we are capable of waking the machine
|
||
//
|
||
|
||
if (PDevExt->SystemWake <= PowerSystemWorking) {
|
||
return STATUS_INVALID_DEVICE_STATE;
|
||
}
|
||
|
||
if (PDevExt->DeviceWake == PowerDeviceUnspecified) {
|
||
return STATUS_INVALID_DEVICE_STATE;
|
||
}
|
||
|
||
//
|
||
// Send IRP to request wait wake and add a pending irp flag
|
||
//
|
||
//
|
||
|
||
InterlockedIncrement(&PDevExt->PendingIRPCnt);
|
||
|
||
powerState.SystemState = PDevExt->SystemWake;
|
||
|
||
status = PoRequestPowerIrp(PDevExt->Pdo, IRP_MN_WAIT_WAKE,
|
||
powerState, SerialWakeCompletion, PDevExt, &pIrp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
status = STATUS_SUCCESS;
|
||
PDevExt->PendingWakeIrp = pIrp;
|
||
} else if (!NT_SUCCESS(status)) {
|
||
SerialIRPEpilogue(PDevExt);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SerialWakeCompletion(IN PDEVICE_OBJECT PDevObj, IN UCHAR MinorFunction,
|
||
IN POWER_STATE PowerState, IN PVOID Context,
|
||
IN PIO_STATUS_BLOCK IoStatus)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles completion of the waitwake IRP.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the device object for this device
|
||
|
||
MinorFunction - Minor function previously supplied to PoRequestPowerIrp
|
||
|
||
PowerState - PowerState previously supplied to PoRequestPowerIrp
|
||
|
||
Context - a pointer to the device extension
|
||
|
||
IoStatus - current/final status of the waitwake IRP
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of attempting to process the
|
||
waitwake.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = (PSERIAL_DEVICE_EXTENSION)Context;
|
||
POWER_STATE powerState;
|
||
|
||
status = IoStatus->Status;
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// A wakeup has occurred -- powerup our stack
|
||
//
|
||
|
||
powerState.DeviceState = PowerDeviceD0;
|
||
|
||
PoRequestPowerIrp(pDevExt->Pdo, IRP_MN_SET_POWER, powerState, NULL,
|
||
NULL, NULL);
|
||
|
||
}
|
||
|
||
pDevExt->PendingWakeIrp = NULL;
|
||
SerialIRPEpilogue(pDevExt);
|
||
|
||
return status;
|
||
}
|
||
|