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.
1078 lines
27 KiB
1078 lines
27 KiB
/*--------------------------------------------------------------------------
|
|
*
|
|
* Copyright (C) Cyclades Corporation, 1999-2001.
|
|
* All rights reserved.
|
|
*
|
|
* Cyclom-Y Port Driver
|
|
*
|
|
* This file: cyypower.c
|
|
*
|
|
* Description: This module contains the code that handles the power
|
|
* IRPs for the Cyclom-Y 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 implementation based on Microsoft sample code.
|
|
*
|
|
*--------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGESRP0, CyyGotoPowerState)
|
|
#pragma alloc_text(PAGESRP0, CyyPowerDispatch)
|
|
//#pragma alloc_text(PAGESRP0, CyySetPowerD0)
|
|
#pragma alloc_text(PAGESRP0, CyySetPowerD3)
|
|
//#pragma alloc_text(PAGESRP0, CyySaveDeviceState)
|
|
//#pragma alloc_text(PAGESRP0, CyyRestoreDeviceState)
|
|
#pragma alloc_text(PAGESRP0, CyySendWaitWake)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
typedef struct _POWER_COMPLETION_CONTEXT {
|
|
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIRP SIrp;
|
|
|
|
} POWER_COMPLETION_CONTEXT, *PPOWER_COMPLETION_CONTEXT;
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CyySetPowerEvent(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;
|
|
}
|
|
|
|
#if 0
|
|
BOOLEAN
|
|
CyyDisableInterruptInPLX(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine disables the PLX interrupts and puts the hw in a "safe" state when
|
|
not in use (like a close or powerdown).
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
{
|
|
|
|
PCYY_DEVICE_EXTENSION PDevExt = Context;
|
|
PUCHAR chip = PDevExt->Cd1400;
|
|
ULONG bus = PDevExt->IsPci;
|
|
|
|
if (PDevExt->IsPci){
|
|
|
|
ULONG i;
|
|
UCHAR plx_ver;
|
|
ULONG original;
|
|
PCYY_DISPATCH pDispatch;
|
|
|
|
pDispatch = (PCYY_DISPATCH)PDevExt->OurIsrContext;
|
|
pDispatch->Cd1400[PDevExt->PortIndex] = NULL;
|
|
|
|
for (i = 0; i < CYY_MAX_PORTS; i++) {
|
|
if (pDispatch->Cd1400[PDevExt->PortIndex] != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == CYY_MAX_PORTS) {
|
|
|
|
// This was the last port, disable Interrupts.
|
|
|
|
CYY_CLEAR_INTERRUPT(PDevExt->BoardMemory,PDevExt->IsPci);
|
|
|
|
plx_ver = CYY_READ_PCI_TYPE(PDevExt->BoardMemory);
|
|
plx_ver &= 0x0f;
|
|
|
|
switch(plx_ver) {
|
|
case CYY_PLX9050:
|
|
original = PLX9050_READ_INTERRUPT_CONTROL(PDevExt->Runtime);
|
|
PLX9050_WRITE_INTERRUPT_CONTROL(PDevExt->Runtime,
|
|
original&~PLX9050_INT_ENABLE);
|
|
break;
|
|
case CYY_PLX9060:
|
|
case CYY_PLX9080:
|
|
default:
|
|
original = PLX9060_READ_INTERRUPT_CONTROL(PDevExt->Runtime);
|
|
PLX9060_WRITE_INTERRUPT_CONTROL(PDevExt->Runtime,
|
|
original&~PLX9060_INT_ENABLE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Disable interrupt mask in the CD1400
|
|
CD1400_WRITE(chip,bus,CAR,PDevExt->CdChannel & 0x03);
|
|
CD1400_WRITE(chip,bus,SRER,0x00);
|
|
CyyCDCmd(PDevExt,CCR_RESET_CHANNEL); // Disables tx and rx, all FIFOs flushed.
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyyFlushCd1400(IN PVOID Context)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine flushes the Tx FIFO.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCYY_DEVICE_EXTENSION extension = Context;
|
|
PUCHAR chip = extension->Cd1400;
|
|
ULONG bus = extension->IsPci;
|
|
|
|
// Flush TX FIFO
|
|
CD1400_WRITE(chip,bus,CAR,extension->CdChannel & 0x03);
|
|
CyyCDCmd(extension,CCR_FLUSH_TXFIFO);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
#endif
|
|
|
|
BOOLEAN
|
|
CyySaveDeviceState(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
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
|
|
|
|
|
|
--*/
|
|
{
|
|
PCYY_DEVICE_EXTENSION PDevExt = Context;
|
|
PCYY_DEVICE_STATE pDevState = &PDevExt->DeviceState;
|
|
PUCHAR chip = PDevExt->Cd1400;
|
|
ULONG bus = PDevExt->IsPci;
|
|
|
|
CyyDbgPrintEx(CYYTRACECALLS, "Entering CyySaveDeviceState\n");
|
|
|
|
if (PDevExt->IsPci){
|
|
|
|
ULONG i;
|
|
UCHAR plx_ver;
|
|
ULONG original;
|
|
PCYY_DISPATCH pDispatch;
|
|
|
|
pDispatch = (PCYY_DISPATCH)PDevExt->OurIsrContext;
|
|
pDispatch->Cd1400[PDevExt->PortIndex] = NULL;
|
|
|
|
for (i = 0; i < CYY_MAX_PORTS; i++) {
|
|
if (pDispatch->Cd1400[PDevExt->PortIndex] != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == CYY_MAX_PORTS) {
|
|
|
|
// This was the last port, disable Interrupts.
|
|
|
|
CYY_CLEAR_INTERRUPT(PDevExt->BoardMemory,PDevExt->IsPci);
|
|
|
|
plx_ver = CYY_READ_PCI_TYPE(PDevExt->BoardMemory);
|
|
plx_ver &= 0x0f;
|
|
|
|
switch(plx_ver) {
|
|
case CYY_PLX9050:
|
|
original = PLX9050_READ_INTERRUPT_CONTROL(PDevExt->Runtime);
|
|
PLX9050_WRITE_INTERRUPT_CONTROL(PDevExt->Runtime,
|
|
original&~PLX9050_INT_ENABLE);
|
|
break;
|
|
case CYY_PLX9060:
|
|
case CYY_PLX9080:
|
|
default:
|
|
original = PLX9060_READ_INTERRUPT_CONTROL(PDevExt->Runtime);
|
|
PLX9060_WRITE_INTERRUPT_CONTROL(PDevExt->Runtime,
|
|
original&~PLX9060_INT_ENABLE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flush TX FIFO
|
|
CD1400_WRITE(chip,bus,CAR,PDevExt->CdChannel & 0x03);
|
|
CyyCDCmd(PDevExt,CCR_FLUSH_TXFIFO);
|
|
|
|
CD1400_WRITE(chip,bus,CAR,PDevExt->CdChannel & 0x03);
|
|
pDevState->Srer = CD1400_READ(chip,bus,SRER);
|
|
pDevState->Cor1 = CD1400_READ(chip,bus,COR1);
|
|
pDevState->Cor2 = CD1400_READ(chip,bus,COR2);
|
|
pDevState->Cor3 = CD1400_READ(chip,bus,COR3);
|
|
pDevState->Schr1 = CD1400_READ(chip,bus,SCHR1);
|
|
pDevState->Schr2 = CD1400_READ(chip,bus,SCHR2);
|
|
pDevState->Mcor1 = CD1400_READ(chip,bus,MCOR1);
|
|
pDevState->Mcor2 = CD1400_READ(chip,bus,MCOR2);
|
|
pDevState->Rtpr = CD1400_READ(chip,bus,RTPR);
|
|
pDevState->Msvr1 = CD1400_READ(chip,bus,MSVR1);
|
|
pDevState->Msvr2 = CD1400_READ(chip,bus,MSVR2);
|
|
pDevState->Rbpr = CD1400_READ(chip,bus,RBPR);
|
|
pDevState->Tbpr = CD1400_READ(chip,bus,TBPR);
|
|
pDevState->Rcor = CD1400_READ(chip,bus,RCOR);
|
|
pDevState->Tcor = CD1400_READ(chip,bus,TCOR);
|
|
|
|
// Disable interrupt mask in the CD1400
|
|
CD1400_WRITE(chip,bus,CAR,PDevExt->CdChannel & 0x03);
|
|
CD1400_WRITE(chip,bus,SRER,0x00);
|
|
CyyCDCmd(PDevExt,CCR_RESET_CHANNEL); // Disables tx and rx, all FIFOs flushed.
|
|
|
|
PDevExt->PowerState = PowerDeviceD3;
|
|
|
|
CyyDbgPrintEx(CYYTRACECALLS, "Leaving CyySaveDeviceState\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
CyyEnableInterruptInPLX(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enables the PLX interrupts and puts the hw in a "safe" state when
|
|
not in use (like a close or powerdown).
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
{
|
|
|
|
PCYY_DEVICE_EXTENSION PDevExt = Context;
|
|
|
|
if (PDevExt->IsPci){
|
|
|
|
UCHAR plx_ver;
|
|
ULONG original;
|
|
|
|
plx_ver = CYY_READ_PCI_TYPE(PDevExt->BoardMemory);
|
|
plx_ver &= 0x0f;
|
|
|
|
switch(plx_ver) {
|
|
case CYY_PLX9050:
|
|
original = PLX9050_READ_INTERRUPT_CONTROL(PDevExt->Runtime);
|
|
if ((original & PLX9050_INT_ENABLE) != PLX9050_INT_ENABLE) {
|
|
PLX9050_WRITE_INTERRUPT_CONTROL(PDevExt->Runtime,
|
|
original|PLX9050_INT_ENABLE);
|
|
}
|
|
break;
|
|
case CYY_PLX9060:
|
|
case CYY_PLX9080:
|
|
default:
|
|
original = PLX9060_READ_INTERRUPT_CONTROL(PDevExt->Runtime);
|
|
if ((original & PLX9060_INT_ENABLE) != PLX9060_INT_ENABLE) {
|
|
PLX9060_WRITE_INTERRUPT_CONTROL(PDevExt->Runtime,
|
|
original|PLX9060_INT_ENABLE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyyRestoreDeviceState(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
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
|
|
|
|
|
|
--*/
|
|
{
|
|
PCYY_DEVICE_EXTENSION PDevExt = Context;
|
|
PCYY_DEVICE_STATE pDevState = &PDevExt->DeviceState;
|
|
PUCHAR chip = PDevExt->Cd1400;
|
|
ULONG bus = PDevExt->IsPci;
|
|
PCYY_DISPATCH pDispatch = PDevExt->OurIsrContext;
|
|
|
|
CyyDbgPrintEx(CYYTRACECALLS, "Enter CyyRestoreDeviceState\n");
|
|
CyyDbgPrintEx(CYYTRACECALLS, "PDevExt: %x\n", PDevExt);
|
|
|
|
CD1400_WRITE(chip,bus,CAR,PDevExt->CdChannel & 0x03);
|
|
CD1400_WRITE(chip,bus,SRER,0x00);
|
|
CyyCDCmd(PDevExt,CCR_RESET_CHANNEL);
|
|
|
|
CYY_CLEAR_INTERRUPT(PDevExt->BoardMemory,bus);
|
|
|
|
PDevExt->HoldingEmpty = TRUE;
|
|
|
|
// Set Cd1400 address for the ISR
|
|
pDispatch->Cd1400[PDevExt->PortIndex] = chip;
|
|
|
|
CyyEnableInterruptInPLX(PDevExt);
|
|
|
|
if (PDevExt->DeviceState.Reopen == TRUE) {
|
|
CyyDbgPrintEx(CYYPNPPOWER, "Reopening device\n");
|
|
|
|
PDevExt->DeviceIsOpened = TRUE;
|
|
PDevExt->DeviceState.Reopen = FALSE;
|
|
|
|
CD1400_WRITE(chip,bus,CAR,PDevExt->CdChannel & 0x03);
|
|
CD1400_WRITE(chip,bus,COR1, pDevState->Cor1);
|
|
CD1400_WRITE(chip,bus,COR2, pDevState->Cor2);
|
|
CD1400_WRITE(chip,bus,COR3, pDevState->Cor3);
|
|
CD1400_WRITE(chip,bus,SCHR1,pDevState->Schr1);
|
|
CD1400_WRITE(chip,bus,SCHR2,pDevState->Schr2);
|
|
CD1400_WRITE(chip,bus,MCOR1,pDevState->Mcor1);
|
|
CD1400_WRITE(chip,bus,MCOR2,pDevState->Mcor2);
|
|
CD1400_WRITE(chip,bus,RTPR, pDevState->Rtpr);
|
|
CD1400_WRITE(chip,bus,MSVR1,pDevState->Msvr1);
|
|
CD1400_WRITE(chip,bus,MSVR2,pDevState->Msvr2);
|
|
CD1400_WRITE(chip,bus,RBPR, pDevState->Rbpr);
|
|
CD1400_WRITE(chip,bus,TBPR, pDevState->Tbpr);
|
|
CD1400_WRITE(chip,bus,RCOR, pDevState->Rcor);
|
|
CD1400_WRITE(chip,bus,TCOR, pDevState->Tcor);
|
|
CyyCDCmd(PDevExt,CCR_CORCHG_COR1_COR2_COR3);
|
|
CyyCDCmd(PDevExt,CCR_ENA_TX_RX);
|
|
|
|
//
|
|
// This enables interrupts on the device!
|
|
//
|
|
CD1400_WRITE(chip,bus,SRER, pDevState->Srer);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
CyyPowerRequestComplete(
|
|
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
|
|
|
|
--*/
|
|
{
|
|
PCYY_DEVICE_EXTENSION pDevExt = (PCYY_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);
|
|
CyyCompleteRequest(pDevExt,sIrp,IO_NO_INCREMENT);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CyySystemPowerComplete (
|
|
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;
|
|
PCYY_DEVICE_EXTENSION data;
|
|
NTSTATUS status = Irp->IoStatus.Status;
|
|
|
|
UNREFERENCED_PARAMETER (Context);
|
|
|
|
data = DeviceObject->DeviceExtension;
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
CyyIRPEpilogue(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, CyyPowerRequestComplete,
|
|
powerContext, NULL);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
if (powerContext) {
|
|
ExFreePool(powerContext);
|
|
}
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
Irp->IoStatus.Status = status;
|
|
CyyCompleteRequest(data,Irp,IO_NO_INCREMENT); // To be equal to toaster
|
|
//CyyIRPEpilogue(data);
|
|
//return status;
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
CyyDevicePowerComplete (
|
|
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;
|
|
PCYY_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 CyyRestoreDeviceState().
|
|
//
|
|
|
|
if (pDevExt->PNPState == CYY_PNP_STARTED) {
|
|
KeSynchronizeExecution(
|
|
pDevExt->Interrupt,
|
|
CyyRestoreDeviceState,
|
|
pDevExt
|
|
);
|
|
}
|
|
|
|
//
|
|
// Now that we are powered up, call PoSetPowerState
|
|
//
|
|
|
|
PoSetPowerState(DeviceObject, powerType, powerState);
|
|
PoStartNextPowerIrp(Irp);
|
|
CyyCompleteRequest(pDevExt, Irp, IO_NO_INCREMENT); // Code back
|
|
return STATUS_MORE_PROCESSING_REQUIRED; // Code back
|
|
|
|
//CyyIRPEpilogue(pDevExt); // Added and removed Fanny
|
|
//return STATUS_SUCCESS; // Added and removed Fanny
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CyyPowerDispatch(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
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PCYY_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 = CyyIRPPrologue(PIrp, pDevExt)) != STATUS_SUCCESS) {
|
|
if (status != STATUS_PENDING) {
|
|
PoStartNextPowerIrp(PIrp);
|
|
CyyCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
switch (pIrpStack->MinorFunction) {
|
|
|
|
case IRP_MN_WAIT_WAKE:
|
|
CyyDbgPrintEx(CYYPNPPOWER, "Got IRP_MN_WAIT_WAKE Irp\n");
|
|
break;
|
|
|
|
|
|
case IRP_MN_POWER_SEQUENCE:
|
|
CyyDbgPrintEx(CYYPNPPOWER, "Got IRP_MN_POWER_SEQUENCE Irp\n");
|
|
break;
|
|
|
|
case IRP_MN_SET_POWER:
|
|
CyyDbgPrintEx(CYYPNPPOWER, "Got IRP_MN_SET_POWER Irp\n");
|
|
|
|
switch (pIrpStack->Parameters.Power.Type) {
|
|
case SystemPowerState:
|
|
|
|
CyyDbgPrintEx(CYYPNPPOWER, "SystemPowerState\n");
|
|
|
|
IoMarkIrpPending(PIrp);
|
|
IoCopyCurrentIrpStackLocationToNext (PIrp);
|
|
IoSetCompletionRoutine (PIrp,
|
|
CyySystemPowerComplete,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
PoCallDriver(pDevExt->LowerDeviceObject, PIrp);
|
|
return STATUS_PENDING;
|
|
|
|
case DevicePowerState:
|
|
|
|
CyyDbgPrintEx(CYYPNPPOWER, "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
|
|
CyyDbgPrintEx(CYYPNPPOWER, "Already in requested power state\n");
|
|
break;
|
|
}
|
|
switch (pIrpStack->Parameters.Power.State.DeviceState) {
|
|
case PowerDeviceD0:
|
|
if (pDevExt->OpenCount) {
|
|
|
|
CyyDbgPrintEx(CYYPNPPOWER, "Going to power state D0\n");
|
|
|
|
IoMarkIrpPending(PIrp);
|
|
IoCopyCurrentIrpStackLocationToNext (PIrp);
|
|
IoSetCompletionRoutine (PIrp,
|
|
CyyDevicePowerComplete,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
PoCallDriver(pDevExt->LowerDeviceObject, PIrp);
|
|
return STATUS_PENDING;
|
|
}
|
|
//return CyySetPowerD0(PDevObj, PIrp);
|
|
break;
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
case PowerDeviceD3:
|
|
|
|
CyyDbgPrintEx(CYYPNPPOWER, "Going to power state D3\n");
|
|
|
|
return CyySetPowerD3(PDevObj, PIrp);
|
|
}
|
|
break;
|
|
default:
|
|
CyyDbgPrintEx(CYYPNPPOWER, "UNKNOWN PowerState\n");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_POWER:
|
|
|
|
CyyDbgPrintEx (CYYPNPPOWER, "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);
|
|
CyyCompleteRequest(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 CyyPoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
|
|
} // switch (pIrpStack->MinorFunction)
|
|
|
|
|
|
PoStartNextPowerIrp(PIrp);
|
|
//
|
|
// Pass to the lower driver
|
|
//
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
status = CyyPoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CyyGotoPowerState(IN PDEVICE_OBJECT PDevObj,
|
|
IN PCYY_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();
|
|
|
|
CyyDbgPrintEx(CYYTRACECALLS, "In CyyGotoPowerState\n");
|
|
|
|
powerState.DeviceState = DevPowerState;
|
|
|
|
KeInitializeEvent(&gotoPowEvent, SynchronizationEvent, FALSE);
|
|
|
|
status = PoRequestPowerIrp(PDevObj, IRP_MN_SET_POWER, powerState,
|
|
CyySetPowerEvent, &gotoPowEvent,
|
|
NULL);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&gotoPowEvent, Executive, KernelMode, FALSE, NULL);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
CyyDbgPrintEx(CYYPNPPOWER, "CyyGotoPowerState FAILED\n");
|
|
}
|
|
#endif
|
|
|
|
CyyDbgPrintEx(CYYTRACECALLS, "Leaving CyyGotoPowerState\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CyySetPowerD3(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;
|
|
PCYY_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
|
|
|
PAGED_CODE();
|
|
|
|
CyyDbgPrintEx(CYYDIAG3, "In CyySetPowerD3\n");
|
|
|
|
//
|
|
// Send the wait wake now, just in time
|
|
//
|
|
|
|
|
|
if (pDevExt->SendWaitWake) {
|
|
CyySendWaitWake(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;
|
|
|
|
//
|
|
// Save the device state
|
|
//
|
|
KeSynchronizeExecution(
|
|
pDevExt->Interrupt,
|
|
CyySaveDeviceState,
|
|
pDevExt
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// If the device is not open, we don't need to save the state;
|
|
// we can just reset the device on power-up
|
|
//
|
|
|
|
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 CyyPoCallDriver(pDevExt, pDevExt->LowerDeviceObject, PIrp);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CyySendWaitWake(PCYY_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, CyyWakeCompletion, PDevExt, &pIrp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
status = STATUS_SUCCESS;
|
|
PDevExt->PendingWakeIrp = pIrp;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
CyyIRPEpilogue(PDevExt);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CyyWakeCompletion(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;
|
|
PCYY_DEVICE_EXTENSION pDevExt = (PCYY_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;
|
|
CyyIRPEpilogue(pDevExt);
|
|
|
|
return status;
|
|
}
|
|
|
|
|