/*++ 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(PAGEMX0, MoxaGotoPowerState) //#pragma alloc_text(PAGEMX0, MoxaPowerDispatch) //#pragma alloc_text(PAGEMX0, MoxaSetPowerD0) //#pragma alloc_text(PAGEMX0, MoxaSetPowerD3) //#pragma alloc_text(PAGEMX0, MoxaSaveDeviceState) //#pragma alloc_text(PAGEMX0, MoxaRestoreDeviceState) //#pragma alloc_text(PAGEMX0, MoxaSendWaitWake) #endif // ALLOC_PRAGMA NTSTATUS MoxaSystemPowerCompletion(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 MoxaSaveDeviceState(IN PMOXA_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 --*/ { PMOXA_DEVICE_STATE pDevState = &PDevExt->DeviceState; KIRQL oldIrql; // PAGED_CODE(); MoxaKdPrint (MX_DBG_TRACE, ("Entering MoxaSaveDeviceState\n")); MoxaKillAllReadsOrWrites( PDevExt->DeviceObject, &PDevExt->WriteQueue, &PDevExt->CurrentWriteIrp ); MoxaKillAllReadsOrWrites( PDevExt->DeviceObject, &PDevExt->ReadQueue, &PDevExt->CurrentReadIrp ); // // Clean out the Tx/Rx queue // KeAcquireSpinLock( &PDevExt->ControlLock, &oldIrql ); PDevExt->TotalCharsQueued = 0; MoxaFunc( // flush input/output queue PDevExt->PortOfs, FC_FlushQueue, 2 ); KeReleaseSpinLock( &PDevExt->ControlLock, oldIrql ); // // Read necessary registers direct // pDevState->HostState = *(PUSHORT)(PDevExt->PortOfs + HostStat); MoxaKdPrint (MX_DBG_TRACE, ("Leaving MoxaSaveDeviceState\n")); } VOID MoxaRestoreDeviceState(IN PMOXA_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 --*/ { PMOXA_DEVICE_STATE pDevState = &PDevExt->DeviceState; SHORT divisor; USHORT max; // PAGED_CODE(); MoxaKdPrint (MX_DBG_TRACE, ("Enter MoxaRestoreDeviceState\n")); MoxaKdPrint (MX_DBG_TRACE, ("------ PDevExt: %x\n", PDevExt)); if (PDevExt->DeviceState.Reopen == TRUE) { USHORT arg,i; //MoxaFunc1(PDevExt->PortOfs, FC_ChannelReset, Magic_code); // // Restore Host Stat // *(PUSHORT)(PDevExt->PortOfs + HostStat) = pDevState->HostState; MoxaFunc1(PDevExt->PortOfs, FC_SetDataMode, PDevExt->DataMode); MoxaGetDivisorFromBaud( PDevExt->ClockType, PDevExt->CurrentBaud, &divisor ); MoxaFunc1(PDevExt->PortOfs, FC_SetBaud, divisor); *(PUSHORT)(PDevExt->PortOfs+ Tx_trigger) = (USHORT)MoxaTxLowWater; MoxaFunc1(PDevExt->PortOfs, FC_SetTxFIFOCnt, PDevExt->TxFifoAmount); MoxaFunc1(PDevExt->PortOfs, FC_SetRxFIFOTrig,PDevExt->RxFifoTrigger); MoxaFunc1(PDevExt->PortOfs, FC_SetLineIrq,Magic_code); MoxaFunc1(PDevExt->PortOfs, FC_SetXoffLimit, (USHORT)PDevExt->HandFlow.XoffLimit); MoxaFunc1(PDevExt->PortOfs, FC_SetFlowRepl, (USHORT)PDevExt->HandFlow.FlowReplace); arg = (MoxaFlagBit[PDevExt->PortNo] & 3); MoxaFunc1( PDevExt->PortOfs, FC_LineControl, arg ); for (i=0; iPortOfs + FuncArg)[i] = ((PUCHAR)&PDevExt->SpecialChars)[i]; *(PDevExt->PortOfs + FuncCode) = FC_SetChars; MoxaWaitFinish1(PDevExt->PortOfs); PDevExt->ModemStatus = *(PUSHORT)(PDevExt->PortOfs + FlagStat) >> 4; if (PDevExt->HandFlow.ControlHandShake & SERIAL_CTS_HANDSHAKE) arg = CTS_FlowCtl; if (PDevExt->HandFlow.FlowReplace & SERIAL_RTS_HANDSHAKE) arg |= RTS_FlowCtl; if (PDevExt->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) arg |= Tx_FlowCtl; if (PDevExt->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) arg |= Rx_FlowCtl; MoxaFunc1(PDevExt->PortOfs,FC_SetFlowCtl, arg); if (MoxaFlagBit[PDevExt->PortNo] & 4) MoxaFunc1(PDevExt->PortOfs,FC_SetXoffState,Magic_code); MoxaFunc1(PDevExt->PortOfs, FC_EnableCH, Magic_code); if (PDevExt->NumberNeededForRead) MoxaKdPrint (MX_DBG_TRACE, ("NumberNeededForRead=%d\n",PDevExt->NumberNeededForRead)); PDevExt->DeviceState.Reopen = FALSE; } MoxaKdPrint (MX_DBG_TRACE, ("Exit restore\n")); } NTSTATUS MoxaPowerDispatch(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 --*/ { PMOXA_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 (pDevExt->ControlDevice) { // Control Device PoStartNextPowerIrp(PIrp); status = STATUS_CANCELLED; PIrp->IoStatus.Information = 0L; PIrp->IoStatus.Status = status; IoCompleteRequest( PIrp, 0 ); return status; } if ((status = MoxaIRPPrologue(PIrp, pDevExt)) != STATUS_SUCCESS) { PoStartNextPowerIrp(PIrp); MoxaCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT); return status; } switch (pIrpStack->MinorFunction) { case IRP_MN_WAIT_WAKE: MoxaKdPrint (MX_DBG_TRACE, ("Got IRP_MN_WAIT_WAKE Irp\n")); break; case IRP_MN_POWER_SEQUENCE: MoxaKdPrint (MX_DBG_TRACE, ("Got IRP_MN_POWER_SEQUENCE Irp\n")); break; case IRP_MN_SET_POWER: MoxaKdPrint (MX_DBG_TRACE,("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 // MoxaKdPrint (MX_DBG_TRACE, ("------: 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 // PoRequestPowerIrp(pPdo, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL); goto PowerExit; } case DevicePowerState: MoxaKdPrint (MX_DBG_TRACE, ("------: DevicePowerState\n")); break; default: MoxaKdPrint (MX_DBG_TRACE, ("------: UNKNOWN PowerState\n")); goto PowerExit; } // // If we are already in the requested state, just pass the IRP down // if (pDevExt->PowerState == pIrpStack->Parameters.Power.State.DeviceState) { MoxaKdPrint (MX_DBG_TRACE, ("Already in requested power state\n") ); break; } switch (pIrpStack->Parameters.Power.State.DeviceState) { case PowerDeviceD0: MoxaKdPrint (MX_DBG_TRACE,("Going to power state D0\n")); return MoxaSetPowerD0(PDevObj, PIrp); case PowerDeviceD1: case PowerDeviceD2: case PowerDeviceD3: MoxaKdPrint (MX_DBG_TRACE,("Going to power state D3\n")); return MoxaSetPowerD3(PDevObj, PIrp); default: break; } break; case IRP_MN_QUERY_POWER: MoxaKdPrint (MX_DBG_TRACE,("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) { if (pIrpStack->Parameters.Power.Type == SystemPowerState && pIrpStack->Parameters.Power.State.SystemState > pDevExt->SystemWake) { status = PIrp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; PoStartNextPowerIrp(PIrp); MoxaCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT); return status; } } // // If no wait-wake, always successful // PIrp->IoStatus.Status = STATUS_SUCCESS; PoStartNextPowerIrp(PIrp); IoSkipCurrentIrpStackLocation(PIrp); return MoxaPoCallDriver(pDevExt, pLowerDevObj, PIrp); } // switch (pIrpStack->MinorFunction) PowerExit:; PoStartNextPowerIrp(PIrp); // // Pass to the lower driver // IoSkipCurrentIrpStackLocation(PIrp); status = MoxaPoCallDriver(pDevExt, pLowerDevObj, PIrp); return status; } NTSTATUS MoxaSetPowerD0(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. --*/ { PMOXA_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp); NTSTATUS status; ULONG boardReady = 0; KEVENT event; IO_STATUS_BLOCK IoStatusBlock; // PAGED_CODE(); MoxaKdPrint (MX_DBG_TRACE, ("In MoxaSetPowerD0\n")); MoxaKdPrint (MX_DBG_TRACE, ("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, MoxaSyncCompletion, &pDevExt->PowerD0Event, TRUE, TRUE, TRUE); MoxaKdPrint (MX_DBG_TRACE, ("Calling next driver\n")); status = PoCallDriver(pDevExt->LowerDeviceObject, PIrp); if (status == STATUS_PENDING) { MoxaKdPrint (MX_DBG_TRACE, ("Waiting for next driver\n")); KeWaitForSingleObject (&pDevExt->PowerD0Event, Executive, KernelMode, FALSE, NULL); } else { if (!NT_SUCCESS(status)) { PIrp->IoStatus.Status = status; PoStartNextPowerIrp(PIrp); MoxaCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT); return status; } } if (!NT_SUCCESS(PIrp->IoStatus.Status)) { PoStartNextPowerIrp(PIrp); MoxaCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT); return PIrp->IoStatus.Status; } KeInitializeEvent(&event, NotificationEvent, FALSE); MoxaKdPrint(MX_DBG_TRACE,("Get board ready ...\n")); status = MoxaIoSyncIoctlEx(IOCTL_MOXA_INTERNAL_BOARD_READY, TRUE, pDevExt->LowerDeviceObject, &event, &IoStatusBlock, NULL, 0, &boardReady, sizeof(boardReady)); MoxaKdPrint(MX_DBG_TRACE,("status=%x,boardReady=%x\n",status,boardReady)); if (NT_SUCCESS(status) && boardReady) { // // Restore the device // // // Theoretically we could change states in the middle of processing // the restore which would result in a bad PKINTERRUPT being used // in MoxaRestoreDeviceState(). // if (pDevExt->PNPState == SERIAL_PNP_STARTED) { MoxaRestoreDeviceState(pDevExt); } pDevExt->PowerState = PowerDeviceD0; MoxaGlobalData->BoardReady[pDevExt->BoardNo] = TRUE; } else MoxaGlobalData->BoardReady[pDevExt->BoardNo] = FALSE; // // Now that we are powered up, call PoSetPowerState // PoSetPowerState(PDevObj, pIrpStack->Parameters.Power.Type, pIrpStack->Parameters.Power.State); PoStartNextPowerIrp(PIrp); MoxaCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT); MoxaKdPrint (MX_DBG_TRACE,("Leaving MoxaSetPowerD0\n")); return STATUS_SUCCESS; } NTSTATUS MoxaGotoPowerState(IN PDEVICE_OBJECT PDevObj, IN PMOXA_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(); MoxaKdPrint (MX_DBG_TRACE,("In MoxaGotoPowerState\n")); powerState.DeviceState = DevPowerState; KeInitializeEvent(&gotoPowEvent, SynchronizationEvent, FALSE); status = PoRequestPowerIrp(PDevObj, IRP_MN_SET_POWER, powerState, MoxaSystemPowerCompletion, &gotoPowEvent, NULL); if (status == STATUS_PENDING) { KeWaitForSingleObject(&gotoPowEvent, Executive, KernelMode, FALSE, NULL); status = STATUS_SUCCESS; } #if DBG if (!NT_SUCCESS(status)) { MoxaKdPrint (MX_DBG_TRACE,("MoxaGotoPowerState FAILED\n")); } #endif MoxaKdPrint (MX_DBG_TRACE,("Leaving MoxaGotoPowerState\n")); return status; } NTSTATUS MoxaSetPowerD3(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; PMOXA_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp); // PAGED_CODE(); MoxaKdPrint (MX_DBG_TRACE,("In MoxaSetPowerD3\n")); // // Before we power down, call PoSetPowerState // PoSetPowerState(PDevObj, pIrpStack->Parameters.Power.Type, pIrpStack->Parameters.Power.State); pDevExt->PowerState = PowerDeviceD3; // // If the device is not closed, disable interrupts and allow the fifo's // to flush. // if (pDevExt->DeviceIsOpened == TRUE) { LARGE_INTEGER charTime; pDevExt->DeviceState.Reopen = TRUE; // // Save the device state // MoxaSaveDeviceState(pDevExt); MoxaFunc1(pDevExt->PortOfs, FC_DisableCH, Magic_code); MoxaKdPrint (MX_DBG_TRACE,("Port Disabled\n")); } // // 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; // // 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 MoxaPoCallDriver(pDevExt, pDevExt->LowerDeviceObject, PIrp); } NTSTATUS MoxaSendWaitWake(PMOXA_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; } // // 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, MoxaWakeCompletion, PDevExt, &pIrp); if (status == STATUS_PENDING) { status = STATUS_SUCCESS; PDevExt->PendingWakeIrp = pIrp; } else if (!NT_SUCCESS(status)) { MoxaIRPEpilogue(PDevExt); } return status; } NTSTATUS MoxaWakeCompletion(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; PMOXA_DEVICE_EXTENSION pDevExt = (PMOXA_DEVICE_EXTENSION)Context; POWER_STATE powerState; status = IoStatus->Status; if (NT_SUCCESS(status)) { NTSTATUS tmpStatus; PIRP pIrp; PKEVENT pEvent; // // A wakeup has occurred -- powerup our stack // powerState.DeviceState = PowerDeviceD0; pEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT)); if (pEvent == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExitWakeCompletion; } KeInitializeEvent(pEvent, SynchronizationEvent, FALSE); tmpStatus = PoRequestPowerIrp(pDevExt->Pdo, IRP_MN_SET_POWER, powerState, MoxaSystemPowerCompletion, pEvent, NULL); if (tmpStatus == STATUS_PENDING) { KeWaitForSingleObject(pEvent, Executive, KernelMode, FALSE, NULL); tmpStatus = STATUS_SUCCESS; } ExFreePool(pEvent); if (!NT_SUCCESS(tmpStatus)) { status = tmpStatus; goto ErrorExitWakeCompletion; } // // Send another WaitWake Irp // powerState.SystemState = pDevExt->SystemWake; tmpStatus = PoRequestPowerIrp(pDevExt->Pdo, IRP_MN_WAIT_WAKE, powerState, MoxaWakeCompletion, pDevExt, &pIrp); if (tmpStatus == STATUS_PENDING) { pDevExt->PendingWakeIrp = pIrp; goto ExitWakeCompletion; } status = tmpStatus; } ErrorExitWakeCompletion:; pDevExt->PendingWakeIrp = NULL; MoxaIRPEpilogue(pDevExt); ExitWakeCompletion:; return status; }