Leaked source code of windows server 2003
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.
 
 
 
 
 
 

904 lines
22 KiB

/*--------------------------------------------------------------------------
*
* Copyright (C) Cyclades Corporation, 2000-2001.
* All rights reserved.
*
* Cyclades-Z Port Driver
*
* This file: cyzpower.c
*
* Description: This module contains the code that handles the power
* IRPs for the Cyclades-Z Port driver.
*
* Notes: This code supports Windows 2000 and Windows XP,
* x86 and IA64 processors.
*
* Complies with Cyclades SW Coding Standard rev 1.3.
*
*--------------------------------------------------------------------------
*/
/*-------------------------------------------------------------------------
*
* Change History
*
*--------------------------------------------------------------------------
* Initial implementatin based on Microsoft sample code.
*
*--------------------------------------------------------------------------
*/
#include "precomp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGESRP0, CyzGotoPowerState)
#pragma alloc_text(PAGESRP0, CyzPowerDispatch)
#pragma alloc_text(PAGESRP0, CyzSetPowerD0)
//#pragma alloc_text(PAGESRP0, CyzSetPowerD3) Not pageable because it gets spin lock
#pragma alloc_text(PAGESRP0, CyzSaveDeviceState)
//#pragma alloc_text(PAGESRP0, CyzRestoreDeviceState) Not pageable because it gets spin lock.
#pragma alloc_text(PAGESRP0, CyzSendWaitWake)
#endif // ALLOC_PRAGMA
typedef struct _POWER_COMPLETION_CONTEXT {
PDEVICE_OBJECT DeviceObject;
PIRP SIrp;
} POWER_COMPLETION_CONTEXT, *PPOWER_COMPLETION_CONTEXT;
NTSTATUS
CyzSetPowerEvent(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
CyzSaveDeviceState(IN PCYZ_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
--*/
{
PCYZ_DEVICE_STATE pDevState = &PDevExt->DeviceState;
struct CH_CTRL *ch_ctrl;
PAGED_CODE();
CyzDbgPrintEx(CYZTRACECALLS, "Entering CyzSaveDeviceState\n");
#if 0
ch_ctrl = PDevExt->ChCtrl;
pDevState->op_mode = CYZ_READ_ULONG(&ch_ctrl->op_mode);
pDevState->intr_enable = CYZ_READ_ULONG(&ch_ctrl->intr_enable);
pDevState->sw_flow = CYZ_READ_ULONG(&ch_ctrl->sw_flow);
pDevState->comm_baud = CYZ_READ_ULONG(&ch_ctrl->comm_baud);
pDevState->comm_parity = CYZ_READ_ULONG(&ch_ctrl->comm_parity);
pDevState->comm_data_l = CYZ_READ_ULONG(&ch_ctrl->comm_data_l);
pDevState->hw_flow = CYZ_READ_ULONG(&ch_ctrl->hw_flow);
pDevState->rs_control = CYZ_READ_ULONG(&ch_ctrl->rs_control);
#endif
CyzDbgPrintEx(CYZTRACECALLS, "Leaving CyzSaveDeviceState\n");
}
VOID
CyzRestoreDeviceState(IN PCYZ_DEVICE_EXTENSION PDevExt)
/*++
Routine Description:
This routine restores the device state of the UART
Arguments:
PDevExt - Pointer to the device PDevExt for the devobj to restore the
state for.
Return Value:
VOID
--*/
{
PCYZ_DEVICE_STATE pDevState = &PDevExt->DeviceState;
struct CH_CTRL *ch_ctrl;
PCYZ_DISPATCH pDispatch;
KIRQL oldIrql;
#ifndef POLL
ULONG portindex;
#endif
PAGED_CODE();
CyzDbgPrintEx(CYZTRACECALLS, "Enter CyzRestoreDeviceState\n");
CyzDbgPrintEx(CYZTRACECALLS, "PDevExt: %x\n", PDevExt);
#ifndef POLL
//
// While the device isn't open, disable all interrupts.
//
CYZ_WRITE_ULONG(&(PDevExt->ChCtrl)->intr_enable,C_IN_DISABLE); //1.0.0.11
CyzIssueCmd(PDevExt,C_CM_IOCTL,0L,FALSE);
pDispatch = (PCYZ_DISPATCH)PDevExt->OurIsrContext;
for (portindex=0; portindex<pDispatch->NChannels; portindex++) {
if (pDispatch->PoweredOn[portindex]) {
break;
}
}
if (portindex == pDispatch->NChannels)
{
// No port was powered on, this is the first port. Enable PLX interrupts
ULONG intr_reg;
intr_reg = CYZ_READ_ULONG(&(PDevExt->Runtime)->intr_ctrl_stat);
intr_reg |= (0x00030B00UL);
CYZ_WRITE_ULONG(&(PDevExt->Runtime)->intr_ctrl_stat,intr_reg);
}
pDispatch->PoweredOn[PDevExt->PortIndex] = TRUE;
#endif
if (PDevExt->DeviceState.Reopen == TRUE) {
CyzDbgPrintEx(CYZPNPPOWER, "Reopening device\n");
CyzReset(PDevExt);
PDevExt->DeviceIsOpened = TRUE;
PDevExt->DeviceState.Reopen = FALSE;
#ifdef POLL
//
// This enables polling routine!
//
pDispatch = PDevExt->OurIsrContext;
KeAcquireSpinLock(&pDispatch->PollingLock,&oldIrql);
pDispatch->Extensions[PDevExt->PortIndex] = PDevExt;
if (!pDispatch->PollingStarted) {
// Start polling timer
KeSetTimerEx(
&pDispatch->PollingTimer,
pDispatch->PollingTime,
pDispatch->PollingPeriod,
&pDispatch->PollingDpc
);
pDispatch->PollingStarted = TRUE;
pDispatch->PollingDrained = FALSE;
}
KeReleaseSpinLock(&pDispatch->PollingLock,oldIrql);
#endif
//TODO: Should we re-start transmissions in interrupt mode?
}
}
VOID
CyzPowerRequestComplete(
PDEVICE_OBJECT DeviceObject,
UCHAR MinorFunction,
POWER_STATE state,
POWER_COMPLETION_CONTEXT* PowerContext,
PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Completion routine for D-IRP.
Arguments:
Return Value:
NT status code
--*/
{
PCYZ_DEVICE_EXTENSION pDevExt = (PCYZ_DEVICE_EXTENSION) PowerContext->DeviceObject->DeviceExtension;
PIRP sIrp = PowerContext->SIrp;
UNREFERENCED_PARAMETER (DeviceObject);
UNREFERENCED_PARAMETER (MinorFunction);
UNREFERENCED_PARAMETER (state);
//
// Cleanup
//
ExFreePool(PowerContext);
//
// Here we copy the D-IRP status into the S-IRP
//
sIrp->IoStatus.Status = IoStatus->Status;
//
// Release the IRP
//
PoStartNextPowerIrp(sIrp);
CyzCompleteRequest(pDevExt,sIrp,IO_NO_INCREMENT);
}
NTSTATUS
CyzSystemPowerComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
--*/
{
POWER_COMPLETION_CONTEXT* powerContext;
POWER_STATE powerState;
POWER_STATE_TYPE powerType;
PIO_STACK_LOCATION stack;
PCYZ_DEVICE_EXTENSION data;
NTSTATUS status = Irp->IoStatus.Status;
UNREFERENCED_PARAMETER (Context);
data = DeviceObject->DeviceExtension;
if (!NT_SUCCESS(status)) {
PoStartNextPowerIrp(Irp);
CyzIRPEpilogue(data);
return STATUS_SUCCESS;
}
stack = IoGetCurrentIrpStackLocation (Irp);
powerState = stack->Parameters.Power.State;
switch (stack->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 = data->DeviceStateMap[stack->Parameters.Power.State.SystemState];
break;
default:
powerState.DeviceState = PowerDeviceD3;
}
//
// Send IRP to change device state
//
powerContext = (POWER_COMPLETION_CONTEXT*)
ExAllocatePool(NonPagedPool, sizeof(POWER_COMPLETION_CONTEXT));
if (!powerContext) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
powerContext->DeviceObject = DeviceObject;
powerContext->SIrp = Irp;
status = PoRequestPowerIrp(DeviceObject, IRP_MN_SET_POWER, powerState, CyzPowerRequestComplete,
powerContext, NULL);
}
if (!NT_SUCCESS(status)) {
if (powerContext) {
ExFreePool(powerContext);
}
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = status;
CyzCompleteRequest(data,Irp,IO_NO_INCREMENT); // To be equal to toaster
//CyzIRPEpilogue(data);
//return status;
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
CyzDevicePowerComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
The completion routine for Power Up D-IRP.
Arguments:
DeviceObject - pointer to a device object.
Irp - pointer to an I/O Request Packet.
Context - context pointer
Return Value:
NT status code
--*/
{
POWER_STATE powerState;
POWER_STATE_TYPE powerType;
PIO_STACK_LOCATION stack;
PCYZ_DEVICE_EXTENSION pDevExt;
UNREFERENCED_PARAMETER (Context);
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
pDevExt = DeviceObject->DeviceExtension;
stack = IoGetCurrentIrpStackLocation (Irp);
powerType = stack->Parameters.Power.Type;
powerState = stack->Parameters.Power.State;
//
// 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 CyzRestoreDeviceState().
//
if (pDevExt->PNPState == CYZ_PNP_STARTED) {
CyzRestoreDeviceState(pDevExt);
}
//
// Now that we are powered up, call PoSetPowerState
//
PoSetPowerState(DeviceObject, powerType, powerState);
PoStartNextPowerIrp(Irp);
CyzCompleteRequest(pDevExt, Irp, IO_NO_INCREMENT); // Code back
return STATUS_MORE_PROCESSING_REQUIRED; // Code back
//CyzIRPEpilogue(pDevExt); // Added and removed Fanny
//return STATUS_SUCCESS; // Added and removed Fanny
}
NTSTATUS
CyzPowerDispatch(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
--*/
{
PCYZ_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 = CyzIRPPrologue(PIrp, pDevExt)) != STATUS_SUCCESS) {
if (status != STATUS_PENDING) {
PoStartNextPowerIrp(PIrp);
CyzCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
}
return status;
}
status = STATUS_SUCCESS;
switch (pIrpStack->MinorFunction) {
case IRP_MN_WAIT_WAKE:
CyzDbgPrintEx(CYZPNPPOWER, "Got IRP_MN_WAIT_WAKE Irp\n");
break;
case IRP_MN_POWER_SEQUENCE:
CyzDbgPrintEx(CYZPNPPOWER, "Got IRP_MN_POWER_SEQUENCE Irp\n");
break;
case IRP_MN_SET_POWER:
CyzDbgPrintEx(CYZPNPPOWER, "Got IRP_MN_SET_POWER Irp\n");
//
// Perform different ops if it was system or device
//
switch (pIrpStack->Parameters.Power.Type) {
case SystemPowerState:
CyzDbgPrintEx(CYZPNPPOWER, "SystemPowerState\n");
IoMarkIrpPending(PIrp);
IoCopyCurrentIrpStackLocationToNext (PIrp);
IoSetCompletionRoutine (PIrp,
CyzSystemPowerComplete,
NULL,
TRUE,
TRUE,
TRUE);
PoCallDriver(pDevExt->LowerDeviceObject, PIrp);
return STATUS_PENDING;
case DevicePowerState:
CyzDbgPrintEx(CYZPNPPOWER, "DevicePowerState\n");
status = PIrp->IoStatus.Status = STATUS_SUCCESS;
if (pDevExt->PowerState == pIrpStack->Parameters.Power.State.DeviceState) {
// If we are already in the requested state, just pass the IRP down
CyzDbgPrintEx(CYZPNPPOWER, "Already in requested power state\n");
break;
}
switch (pIrpStack->Parameters.Power.State.DeviceState) {
case PowerDeviceD0:
if (pDevExt->OpenCount) {
CyzDbgPrintEx(CYZPNPPOWER, "Going to power state D0\n");
IoMarkIrpPending(PIrp);
IoCopyCurrentIrpStackLocationToNext (PIrp);
IoSetCompletionRoutine (PIrp,
CyzDevicePowerComplete,
NULL,
TRUE,
TRUE,
TRUE);
PoCallDriver(pDevExt->LowerDeviceObject, PIrp);
return STATUS_PENDING;
}
//return CyzSetPowerD0(PDevObj, PIrp);
break;
case PowerDeviceD1:
case PowerDeviceD2:
case PowerDeviceD3:
CyzDbgPrintEx(CYZPNPPOWER, "Going to power state D3\n");
return CyzSetPowerD3(PDevObj, PIrp);
}
break;
default:
CyzDbgPrintEx(CYZPNPPOWER, "UNKNOWN PowerState\n");
break;
}
break;
case IRP_MN_QUERY_POWER:
CyzDbgPrintEx (CYZPNPPOWER, "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);
CyzCompleteRequest(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 CyzPoCallDriver(pDevExt, pLowerDevObj, PIrp);
} // switch (pIrpStack->MinorFunction)
PoStartNextPowerIrp(PIrp);
//
// Pass to the lower driver
//
IoSkipCurrentIrpStackLocation(PIrp);
status = CyzPoCallDriver(pDevExt, pLowerDevObj, PIrp);
return status;
}
NTSTATUS
CyzGotoPowerState(IN PDEVICE_OBJECT PDevObj,
IN PCYZ_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();
CyzDbgPrintEx(CYZTRACECALLS, "In CyzGotoPowerState\n");
powerState.DeviceState = DevPowerState;
KeInitializeEvent(&gotoPowEvent, SynchronizationEvent, FALSE);
status = PoRequestPowerIrp(PDevObj, IRP_MN_SET_POWER, powerState,
CyzSetPowerEvent, &gotoPowEvent,
NULL);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&gotoPowEvent, Executive, KernelMode, FALSE, NULL);
status = STATUS_SUCCESS;
}
#if DBG
if (!NT_SUCCESS(status)) {
CyzDbgPrintEx(CYZPNPPOWER, "CyzGotoPowerState FAILED\n");
}
#endif
CyzDbgPrintEx(CYZTRACECALLS, "Leaving CyzGotoPowerState\n");
return status;
}
NTSTATUS
CyzSetPowerD3(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;
PCYZ_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
PAGED_CODE();
CyzDbgPrintEx(CYZDIAG3, "In CyzSetPowerD3\n");
//
// Send the wait wake now, just in time
//
if (pDevExt->SendWaitWake) {
CyzSendWaitWake(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 = -CyzGetCharTime(pDevExt).QuadPart;
//
// Shut down the chip
//
#ifdef POLL
CyzTryToDisableTimer(pDevExt);
#endif
//TODO FANNY: SHOULD WE RESET THE CHANNEL HERE?
// //
// // Drain the device
// //
//
// CyzDrainUART(pDevExt, &charTime);
//
// Save the device state
//
CyzSaveDeviceState(pDevExt);
}
#ifndef POLL
{
PCYZ_DISPATCH pDispatch;
pDispatch = (PCYZ_DISPATCH)pDevExt->OurIsrContext;
pDispatch->PoweredOn[pDevExt->PortIndex] = FALSE;
}
#endif
//
// 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 CyzPoCallDriver(pDevExt, pDevExt->LowerDeviceObject, PIrp);
}
NTSTATUS
CyzSendWaitWake(PCYZ_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, CyzWakeCompletion, PDevExt, &pIrp);
if (status == STATUS_PENDING) {
status = STATUS_SUCCESS;
PDevExt->PendingWakeIrp = pIrp;
} else if (!NT_SUCCESS(status)) {
CyzIRPEpilogue(PDevExt);
}
return status;
}
NTSTATUS
CyzWakeCompletion(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;
PCYZ_DEVICE_EXTENSION pDevExt = (PCYZ_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;
CyzIRPEpilogue(pDevExt);
return status;
}