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.
1262 lines
39 KiB
1262 lines
39 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: power.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains the code to implement power management.
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 JerrySh Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <ntcsrmsg.h>
|
|
#include "csrmsg.h"
|
|
#include "ntddvdeo.h"
|
|
|
|
#pragma alloc_text(INIT, InitializePowerRequestList)
|
|
|
|
BOOL IsSessionSwitchBlocked();
|
|
NTSTATUS UserSessionSwitchBlock_Start();
|
|
void UserSessionSwitchBlock_End();
|
|
|
|
|
|
extern BOOL gbUserInitialized;
|
|
|
|
#define SWITCHACTION_RESETMODE 0x1
|
|
#define SWITCHACTION_REENUMERATE 0x2
|
|
|
|
LIST_ENTRY gPowerRequestList;
|
|
PFAST_MUTEX gpPowerRequestMutex;
|
|
PKEVENT gpEventPowerRequest;
|
|
ULONG gulDelayedSwitchAction = 0;
|
|
|
|
typedef struct tagPOWERREQUEST {
|
|
LIST_ENTRY PowerRequestLink;
|
|
union {
|
|
KEVENT Event;
|
|
WIN32_POWEREVENT_PARAMETERS CapturedParms;
|
|
};
|
|
NTSTATUS Status;
|
|
PKWIN32_POWEREVENT_PARAMETERS Parms;
|
|
} POWERREQUEST, *PPOWERREQUEST;
|
|
|
|
PPOWERREQUEST gpPowerRequestCurrent;
|
|
|
|
__inline VOID EnterPowerCrit(
|
|
VOID)
|
|
{
|
|
KeEnterCriticalRegion();
|
|
ExAcquireFastMutexUnsafe(gpPowerRequestMutex);
|
|
}
|
|
|
|
__inline VOID LeavePowerCrit(
|
|
VOID)
|
|
{
|
|
ExReleaseFastMutexUnsafe(gpPowerRequestMutex);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CancelPowerRequest
|
|
*
|
|
* The power request can't be satisfied because the worker thread is gone.
|
|
*
|
|
* History:
|
|
* 20-Oct-1998 JerrySh Created.
|
|
\***************************************************************************/
|
|
VOID
|
|
CancelPowerRequest(
|
|
PPOWERREQUEST pPowerRequest)
|
|
{
|
|
UserAssert(pPowerRequest != gpPowerRequestCurrent);
|
|
pPowerRequest->Status = STATUS_UNSUCCESSFUL;
|
|
|
|
/*
|
|
* If it was a callout, tell the waiting thread to proceed.
|
|
* If it was an event, there is no waiting thread but we need to
|
|
* free the pool
|
|
*/
|
|
if (pPowerRequest->Parms) {
|
|
UserFreePool(pPowerRequest);
|
|
} else {
|
|
KeSetEvent(&pPowerRequest->Event, EVENT_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* QueuePowerRequest
|
|
*
|
|
* Insert a power request into the list and wakeup CSRSS to process it.
|
|
*
|
|
* History:
|
|
* 20-Oct-1998 JerrySh Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
QueuePowerRequest(
|
|
PKWIN32_POWEREVENT_PARAMETERS Parms)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PPOWERREQUEST pPowerRequest;
|
|
TL tlPool;
|
|
|
|
UserAssert(gpEventPowerRequest != NULL);
|
|
UserAssert(gpPowerRequestMutex != NULL);
|
|
|
|
/*
|
|
* Allocate and initialize the power request.
|
|
*/
|
|
pPowerRequest = UserAllocPoolNonPagedNS(sizeof(POWERREQUEST), TAG_POWER);
|
|
if (pPowerRequest == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/*
|
|
* If this is a callout, there are no paramaters. Initialize the event to wait on.
|
|
* If this is an event, capture the parameters to be freed after the event
|
|
* is dispatched.
|
|
*/
|
|
if (Parms) {
|
|
pPowerRequest->CapturedParms = *Parms;
|
|
pPowerRequest->Parms = &pPowerRequest->CapturedParms;
|
|
} else {
|
|
KeInitializeEvent(&pPowerRequest->Event, SynchronizationEvent, FALSE);
|
|
pPowerRequest->Parms = NULL;
|
|
}
|
|
|
|
/*
|
|
* Insert the power request into the list.
|
|
*/
|
|
EnterPowerCrit();
|
|
if (gbNoMorePowerCallouts) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
} else {
|
|
InsertHeadList(&gPowerRequestList, &pPowerRequest->PowerRequestLink);
|
|
}
|
|
LeavePowerCrit();
|
|
|
|
/*
|
|
* if this thread is gone through attach process, or
|
|
* If this is a system thread or a non-GUI thread, tell CSRSS to do the
|
|
* work and wait for it to finish. Otherwise, we'll do the work ourselves.
|
|
*/
|
|
if (NT_SUCCESS(Status)) {
|
|
if (PsIsSystemThread(PsGetCurrentThread()) ||
|
|
KeIsAttachedProcess() ||
|
|
W32GetCurrentThread() == NULL) {
|
|
KeSetEvent(gpEventPowerRequest, EVENT_INCREMENT, FALSE);
|
|
} else {
|
|
EnterCrit();
|
|
ThreadLockPool(PtiCurrent(), pPowerRequest, &tlPool);
|
|
xxxUserPowerCalloutWorker();
|
|
ThreadUnlockPool(PtiCurrent(), &tlPool);
|
|
LeaveCrit();
|
|
}
|
|
|
|
/*
|
|
* If this is a callout, wait for it and then free the request.
|
|
* Otherwise, it is an event, and we do not need to wait for it
|
|
* to complete. The request will be freed after it is dequeued.
|
|
*/
|
|
if (Parms) {
|
|
return(STATUS_SUCCESS);
|
|
} else {
|
|
Status = KeWaitForSingleObject(&pPowerRequest->Event,
|
|
WrUserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = pPowerRequest->Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free the power request.
|
|
*/
|
|
UserAssert(pPowerRequest != gpPowerRequestCurrent);
|
|
UserFreePool(pPowerRequest);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UnqueuePowerRequest
|
|
*
|
|
* Remove a power request from the list.
|
|
*
|
|
* History:
|
|
* 20-Oct-1998 JerrySh Created.
|
|
\***************************************************************************/
|
|
PPOWERREQUEST
|
|
UnqueuePowerRequest(
|
|
VOID)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PPOWERREQUEST pPowerRequest = NULL;
|
|
|
|
/*
|
|
* Remove a power request from the list.
|
|
*/
|
|
EnterPowerCrit();
|
|
if (!IsListEmpty(&gPowerRequestList)) {
|
|
pEntry = RemoveTailList(&gPowerRequestList);
|
|
pPowerRequest = CONTAINING_RECORD(pEntry, POWERREQUEST, PowerRequestLink);
|
|
}
|
|
LeavePowerCrit();
|
|
|
|
return pPowerRequest;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitializePowerRequestList
|
|
*
|
|
* Initialize global power request list state.
|
|
*
|
|
* History:
|
|
* 20-Oct-1998 JerrySh Created.
|
|
\***************************************************************************/
|
|
NTSTATUS
|
|
InitializePowerRequestList(
|
|
HANDLE hPowerRequestEvent)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
InitializeListHead(&gPowerRequestList);
|
|
|
|
Status = ObReferenceObjectByHandle(hPowerRequestEvent,
|
|
EVENT_ALL_ACCESS,
|
|
*ExEventObjectType,
|
|
KernelMode,
|
|
&gpEventPowerRequest,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
gpPowerRequestMutex = UserAllocPoolNonPagedNS(sizeof(FAST_MUTEX), TAG_POWER);
|
|
if (gpPowerRequestMutex == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
ExInitializeFastMutex(gpPowerRequestMutex);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CleanupPowerRequestList
|
|
*
|
|
* Cancel any pending power requests.
|
|
*
|
|
* History:
|
|
* 20-Oct-1998 JerrySh Created.
|
|
\***************************************************************************/
|
|
VOID
|
|
CleanupPowerRequestList(
|
|
VOID)
|
|
{
|
|
PPOWERREQUEST pPowerRequest;
|
|
|
|
/*
|
|
* Make sure no new power requests come in.
|
|
*/
|
|
gbNoMorePowerCallouts = TRUE;
|
|
|
|
/*
|
|
* If we never allocated anything, there's nothing to clean up.
|
|
*/
|
|
if (gpPowerRequestMutex == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Mark any pending power requests as cacelled.
|
|
*/
|
|
while ((pPowerRequest = UnqueuePowerRequest()) != NULL) {
|
|
CancelPowerRequest(pPowerRequest);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DeletePowerRequestList
|
|
*
|
|
* Clean up any global power request state.
|
|
*
|
|
* History:
|
|
* 20-Oct-1998 JerrySh Created.
|
|
\***************************************************************************/
|
|
VOID
|
|
DeletePowerRequestList(
|
|
VOID)
|
|
{
|
|
if (gpPowerRequestMutex) {
|
|
|
|
/*
|
|
* Make sure there are no pending power requests.
|
|
*/
|
|
UserAssert(IsListEmpty(&gPowerRequestList));
|
|
|
|
/*
|
|
* Free the power request structures.
|
|
*/
|
|
UserFreePool(gpPowerRequestMutex);
|
|
gpPowerRequestMutex = NULL;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserPowerEventCalloutWorker
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 JerrySh Created.
|
|
\***************************************************************************/
|
|
NTSTATUS xxxUserPowerEventCalloutWorker(
|
|
PKWIN32_POWEREVENT_PARAMETERS Parms)
|
|
{
|
|
BROADCASTSYSTEMMSGPARAMS bsmParams;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSPOWEREVENTTYPE EventNumber = Parms->EventNumber;
|
|
ULONG_PTR Code = Parms->Code;
|
|
BOOL bCurrentPowerOn;
|
|
ULONGLONG ullLastSleepTime;
|
|
BOOL bGotLastSleepTime;
|
|
|
|
|
|
/*
|
|
* Make sure CSRSS is still running.
|
|
*/
|
|
if (gbNoMorePowerCallouts) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
switch (EventNumber) {
|
|
case PsW32FullWake:
|
|
|
|
if (!gbRemoteSession) {
|
|
/*
|
|
* Let all the services know that they can resume operation.
|
|
* There is no corresponding POWER_ACTION for this, but since this
|
|
* is a non-query event, PowerActionNone is as good as any.
|
|
*/
|
|
LeaveCrit();
|
|
IoPnPDeliverServicePowerNotification(PowerActionNone,
|
|
PBT_APMRESUMESUSPEND,
|
|
0,
|
|
FALSE);
|
|
EnterCrit();
|
|
}
|
|
|
|
/*
|
|
* Let all the applications know that they can resume operation.
|
|
* We must not send this message to a session, if it was created after machine went into sleep
|
|
*/
|
|
|
|
/*
|
|
* One of the side effects of NtPowerInformation is that it will
|
|
* dispatch pending power events. So we cannot call it with the user
|
|
* critsec held.
|
|
*
|
|
* Note: Same thing is done for IoPnPDeliverServicePowerNotification.
|
|
*/
|
|
LeaveCrit();
|
|
bGotLastSleepTime = ZwPowerInformation(LastSleepTime, NULL, 0, &ullLastSleepTime, sizeof(ULONGLONG)) == STATUS_SUCCESS;
|
|
EnterCrit();
|
|
|
|
if (!bGotLastSleepTime || gSessionCreationTime < ullLastSleepTime) {
|
|
|
|
bsmParams.dwRecipients = BSM_ALLDESKTOPS;
|
|
bsmParams.dwFlags = BSF_QUEUENOTIFYMESSAGE;
|
|
xxxSendMessageBSM(NULL,
|
|
WM_POWERBROADCAST,
|
|
PBT_APMRESUMESUSPEND,
|
|
0,
|
|
&bsmParams);
|
|
|
|
}
|
|
break;
|
|
|
|
case PsW32EventCode:
|
|
/*
|
|
* Post a message to winlogon, and let them put up a message box
|
|
* or play a sound.
|
|
*/
|
|
|
|
if (gspwndLogonNotify) {
|
|
glinp.ptiLastWoken = GETPTI(gspwndLogonNotify);
|
|
_PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY, LOGON_POWEREVENT, (ULONG)Code);
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
break;
|
|
|
|
case PsW32PowerPolicyChanged:
|
|
/*
|
|
* Set video timeout value.
|
|
*/
|
|
xxxSystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, (ULONG)Code, 0, 0);
|
|
xxxSystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, (ULONG)Code, 0, 0);
|
|
break;
|
|
|
|
case PsW32SystemPowerState:
|
|
|
|
if (!gbRemoteSession) {
|
|
/*
|
|
* Let all the services know that the power status has changed.
|
|
* There is no corresponding POWER_ACTION for this, but since this
|
|
* is a non-query event, PowerActionNone is as good as any.
|
|
*/
|
|
LeaveCrit();
|
|
IoPnPDeliverServicePowerNotification(PowerActionNone,
|
|
PBT_APMPOWERSTATUSCHANGE,
|
|
0,
|
|
FALSE);
|
|
EnterCrit();
|
|
}
|
|
|
|
/*
|
|
* Let all the applications know that the power status has changed.
|
|
*/
|
|
bsmParams.dwRecipients = BSM_ALLDESKTOPS;
|
|
bsmParams.dwFlags = BSF_POSTMESSAGE;
|
|
xxxSendMessageBSM(NULL,
|
|
WM_POWERBROADCAST,
|
|
PBT_APMPOWERSTATUSCHANGE,
|
|
0,
|
|
&bsmParams);
|
|
break;
|
|
|
|
case PsW32SystemTime:
|
|
/*
|
|
* Let all the applications know that the system time has changed.
|
|
*/
|
|
bsmParams.dwRecipients = BSM_ALLDESKTOPS;
|
|
bsmParams.dwFlags = BSF_POSTMESSAGE;
|
|
xxxSendMessageBSM(NULL,
|
|
WM_TIMECHANGE,
|
|
0,
|
|
0,
|
|
&bsmParams);
|
|
break;
|
|
|
|
case PsW32DisplayState:
|
|
/*
|
|
* Set video timeout active status.
|
|
*/
|
|
xxxSystemParametersInfo(SPI_SETLOWPOWERACTIVE, !Code, 0, 0);
|
|
xxxSystemParametersInfo(SPI_SETPOWEROFFACTIVE, !Code, 0, 0);
|
|
break;
|
|
|
|
case PsW32GdiOff:
|
|
/*
|
|
* At this point we will disable the display device, if no protocol switch is in progress.
|
|
*/
|
|
if (!gfSwitchInProgress) {
|
|
DrvSetMonitorPowerState(gpDispInfo->pmdev, PowerDeviceD3);
|
|
|
|
bCurrentPowerOn = DrvQueryMDEVPowerState(gpDispInfo->pmdev);
|
|
if (bCurrentPowerOn) {
|
|
SafeDisableMDEV();
|
|
}
|
|
DrvSetMDEVPowerState(gpDispInfo->pmdev, FALSE);
|
|
} else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
break;
|
|
|
|
case PsW32GdiOn:
|
|
/*
|
|
* Call video driver to turn the display back on, if no protocol
|
|
* switch is in progress.
|
|
*/
|
|
|
|
if (!gfSwitchInProgress) {
|
|
bCurrentPowerOn = DrvQueryMDEVPowerState(gpDispInfo->pmdev);
|
|
if (!bCurrentPowerOn) {
|
|
SafeEnableMDEV();
|
|
}
|
|
DrvSetMDEVPowerState(gpDispInfo->pmdev, TRUE);
|
|
DrvSetMonitorPowerState(gpDispInfo->pmdev, PowerDeviceD0);
|
|
} else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Repaint the whole screen.
|
|
*/
|
|
xxxUserResetDisplayDevice();
|
|
|
|
if (gulDelayedSwitchAction) {
|
|
HANDLE pdo;
|
|
|
|
//
|
|
// The first ACPI device is the one respond to hotkey.
|
|
//
|
|
PVOID PhysDisp = DrvWakeupHandler(&pdo);
|
|
|
|
if (PhysDisp && (gulDelayedSwitchAction & SWITCHACTION_RESETMODE)) {
|
|
UNICODE_STRING strDeviceName;
|
|
DEVMODEW NewMode;
|
|
ULONG bPrune;
|
|
|
|
if (DrvDisplaySwitchHandler(PhysDisp, &strDeviceName, &NewMode, &bPrune)) {
|
|
/*
|
|
* CSRSS is not the only process to deliver power callouts.
|
|
*/
|
|
bPrune = (bPrune ? 0 : CDS_RAWMODE) | CDS_TRYCLOSEST | CDS_RESET;
|
|
if (!ISCSRSS()) {
|
|
xxxUserChangeDisplaySettings(NULL,
|
|
NULL,
|
|
grpdeskRitInput,
|
|
bPrune,
|
|
0,
|
|
KernelMode);
|
|
} else {
|
|
xxxUserChangeDisplaySettings(NULL,
|
|
NULL,
|
|
NULL,
|
|
bPrune,
|
|
0,
|
|
KernelMode);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is a requirement to reenumerate sub-devices.
|
|
//
|
|
if (pdo && (gulDelayedSwitchAction & SWITCHACTION_REENUMERATE)) {
|
|
IoInvalidateDeviceRelations((PDEVICE_OBJECT)pdo, BusRelations);
|
|
}
|
|
}
|
|
}
|
|
gulDelayedSwitchAction = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserPowerEventCallout
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 JerrySh Created.
|
|
\***************************************************************************/
|
|
NTSTATUS UserPowerEventCallout(
|
|
PKWIN32_POWEREVENT_PARAMETERS Parms)
|
|
{
|
|
/*
|
|
* Make sure CSRSS is running.
|
|
*/
|
|
if (!gbVideoInitialized || gbNoMorePowerCallouts) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
UserAssert(gpepCSRSS != NULL);
|
|
|
|
/*
|
|
* Process the power request.
|
|
*/
|
|
return QueuePowerRequest(Parms);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserPowerStateCalloutWorker
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 JerrySh Created.
|
|
\***************************************************************************/
|
|
NTSTATUS xxxUserPowerStateCalloutWorker(
|
|
VOID)
|
|
{
|
|
BOOL fContinue;
|
|
BROADCASTSYSTEMMSGPARAMS bsmParams;
|
|
POWER_ACTION powerOperation;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
TL tlpwnd;
|
|
POWERSTATETASK Task = gPowerState.PowerStateTask;
|
|
ULONGLONG ullLastSleepTime;
|
|
BOOL bGotLastSleepTime;
|
|
|
|
/*
|
|
* By now we must have alrady blocked session switch, it's blocked only
|
|
* for win32k belonging to active console session.
|
|
*/
|
|
UserAssert(SharedUserData->ActiveConsoleId != gSessionId || IsSessionSwitchBlocked());
|
|
|
|
/*
|
|
* Make sure CSRSS is still running.
|
|
*/
|
|
if (gbNoMorePowerCallouts) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
switch (Task) {
|
|
|
|
case PowerState_Init:
|
|
|
|
/*
|
|
* Store the event so this thread can be promoted later.
|
|
*/
|
|
|
|
EnterPowerCrit();
|
|
gPowerState.pEvent = PtiCurrent()->pEventQueueServer;
|
|
LeavePowerCrit();
|
|
|
|
break;
|
|
|
|
case PowerState_QueryApps:
|
|
|
|
if (!gPowerState.fCritical) {
|
|
/*
|
|
* Ask the applications if we can suspend operation.
|
|
*/
|
|
if (gPowerState.fQueryAllowed) {
|
|
|
|
gPowerState.bsmParams.dwRecipients = BSM_ALLDESKTOPS;
|
|
gPowerState.bsmParams.dwFlags = BSF_NOHANG | BSF_FORCEIFHUNG;
|
|
if (gPowerState.fUIAllowed) {
|
|
gPowerState.bsmParams.dwFlags |= BSF_ALLOWSFW;
|
|
}
|
|
|
|
if (gPowerState.fOverrideApps == FALSE) {
|
|
gPowerState.bsmParams.dwFlags |= (BSF_QUERY | BSF_NOTIMEOUTIFNOTHUNG);
|
|
}
|
|
|
|
fContinue = xxxSendMessageBSM(NULL,
|
|
WM_POWERBROADCAST,
|
|
PBT_APMQUERYSUSPEND,
|
|
gPowerState.fUIAllowed,
|
|
&gPowerState.bsmParams);
|
|
|
|
|
|
if (fContinue && !gbRemoteSession) {
|
|
/*
|
|
* Ask the services if we can suspend operation.
|
|
* Map the power action event as needed.
|
|
*/
|
|
if (gPowerState.psParams.MinSystemState == PowerSystemHibernate) {
|
|
powerOperation = PowerActionHibernate;
|
|
} else {
|
|
powerOperation = gPowerState.psParams.SystemAction;
|
|
}
|
|
|
|
LeaveCrit();
|
|
fContinue = IoPnPDeliverServicePowerNotification(
|
|
powerOperation,
|
|
PBT_APMQUERYSUSPEND,
|
|
gPowerState.fUIAllowed,
|
|
TRUE); // synchronous query
|
|
EnterCrit();
|
|
}
|
|
|
|
/*
|
|
* If an app or service says to abort and we're not in
|
|
* override apps or critical mode, return query failed.
|
|
*/
|
|
if (!(fContinue || gPowerState.fOverrideApps || gPowerState.fCritical)) {
|
|
Status = STATUS_CANCELLED;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PowerState_QueryFailed:
|
|
|
|
/*
|
|
* Only send a suspend failed message to the applications, since pnp
|
|
* will already have delivered the suspend failed message to services if
|
|
* one of those aborted the query.
|
|
*/
|
|
gPowerState.bsmParams.dwRecipients = BSM_ALLDESKTOPS;
|
|
gPowerState.bsmParams.dwFlags = BSF_QUEUENOTIFYMESSAGE;
|
|
xxxSendMessageBSM(NULL,
|
|
WM_POWERBROADCAST,
|
|
PBT_APMQUERYSUSPENDFAILED,
|
|
0,
|
|
&gPowerState.bsmParams);
|
|
EnterPowerCrit();
|
|
gPowerState.pEvent = NULL;
|
|
gPowerState.fInProgress = FALSE;
|
|
LeavePowerCrit();
|
|
|
|
|
|
break;
|
|
|
|
case PowerState_SuspendApps:
|
|
|
|
if (!gPowerState.fCritical) {
|
|
|
|
if (!gbRemoteSession) {
|
|
/*
|
|
* Map the power action event as needed.
|
|
*/
|
|
if (gPowerState.psParams.MinSystemState == PowerSystemHibernate) {
|
|
powerOperation = PowerActionHibernate;
|
|
} else {
|
|
powerOperation = gPowerState.psParams.SystemAction;
|
|
}
|
|
|
|
LeaveCrit();
|
|
IoPnPDeliverServicePowerNotification(powerOperation,
|
|
PBT_APMSUSPEND,
|
|
0,
|
|
FALSE);
|
|
EnterCrit();
|
|
}
|
|
|
|
gPowerState.bsmParams.dwRecipients = BSM_ALLDESKTOPS;
|
|
gPowerState.bsmParams.dwFlags = BSF_NOHANG | BSF_FORCEIFHUNG;
|
|
xxxSendMessageBSM(NULL,
|
|
WM_POWERBROADCAST,
|
|
PBT_APMSUSPEND,
|
|
0,
|
|
&gPowerState.bsmParams);
|
|
}
|
|
|
|
/*
|
|
* Clear the event so the thread won't wake up prematurely.
|
|
*/
|
|
EnterPowerCrit();
|
|
gPowerState.pEvent = NULL;
|
|
LeavePowerCrit();
|
|
|
|
break;
|
|
|
|
case PowerState_ShowUI:
|
|
|
|
/*
|
|
* if this is not session 0 show ui for sessions.
|
|
* we shall take this ui off when we resume apps
|
|
* For session 0 we call PowerState_NotifyWL which takes care of it.
|
|
*/
|
|
|
|
if ((gSessionId != 0 ) && (gspwndLogonNotify != NULL)) {
|
|
|
|
ThreadLockAlways(gspwndLogonNotify, &tlpwnd);
|
|
|
|
Status = (NTSTATUS)xxxSendMessage(gspwndLogonNotify,
|
|
WM_LOGONNOTIFY,
|
|
LOGON_SHOW_POWER_MESSAGE,
|
|
(LPARAM)&gPowerState.psParams);
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case PowerState_NotifyWL:
|
|
|
|
if (gspwndLogonNotify != NULL) {
|
|
PWND pwndActive;
|
|
|
|
if (gpqForeground && (pwndActive = gpqForeground->spwndActive) &&
|
|
(GetFullScreen(pwndActive) == FULLSCREEN ||
|
|
GetFullScreen(pwndActive) == FULLSCREENMIN)) {
|
|
gPowerState.psParams.FullScreenMode = TRUE;
|
|
} else {
|
|
gPowerState.psParams.FullScreenMode = FALSE;
|
|
}
|
|
ThreadLockAlways(gspwndLogonNotify, &tlpwnd);
|
|
Status = (NTSTATUS)xxxSendMessage(gspwndLogonNotify,
|
|
WM_LOGONNOTIFY,
|
|
LOGON_POWERSTATE,
|
|
(LPARAM)&gPowerState.psParams);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
/*
|
|
* If we failed to to this power operation, don't lock the
|
|
* console.
|
|
*/
|
|
gPowerState.psParams.Flags &= ~POWER_ACTION_LOCK_CONSOLE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PowerState_ResumeApps:
|
|
/*
|
|
* If this is active console we need to lock it.
|
|
*/
|
|
if ((gPowerState.psParams.Flags & POWER_ACTION_LOCK_CONSOLE) &&
|
|
(gSessionId == SharedUserData->ActiveConsoleId) &&
|
|
(gspwndLogonNotify != NULL)) {
|
|
|
|
ThreadLockAlways(gspwndLogonNotify, &tlpwnd);
|
|
|
|
_PostMessage(gspwndLogonNotify,
|
|
WM_LOGONNOTIFY,
|
|
LOGON_LOCKWORKSTATION,
|
|
LOCK_RESUMEHIBERNATE);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// We dont need to remove power message, if we did not post one.
|
|
//
|
|
|
|
/*
|
|
* One of the side effects of NtPowerInformation is that it will
|
|
* dispatch pending power events. So we can not call it with the
|
|
* user critsec held.
|
|
*
|
|
* Note: The same thing is done for IoPnPDeliverServicePowerNotification.
|
|
*/
|
|
|
|
LeaveCrit();
|
|
bGotLastSleepTime = ZwPowerInformation(LastSleepTime, NULL, 0, &ullLastSleepTime, sizeof(ULONGLONG)) == STATUS_SUCCESS;
|
|
EnterCrit();
|
|
|
|
if (!bGotLastSleepTime || gSessionCreationTime < ullLastSleepTime) {
|
|
if (gSessionId != 0 && gspwndLogonNotify != NULL) {
|
|
ThreadLockAlways(gspwndLogonNotify, &tlpwnd);
|
|
Status = (NTSTATUS)xxxSendMessage(gspwndLogonNotify,
|
|
WM_LOGONNOTIFY,
|
|
LOGON_REMOVE_POWER_MESSAGE,
|
|
(LPARAM)&gPowerState.psParams);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The power state broadcast is over.
|
|
*/
|
|
EnterPowerCrit();
|
|
gPowerState.fInProgress = FALSE;
|
|
LeavePowerCrit();
|
|
|
|
/*
|
|
* Tickle the input time so we don't fire up a screen saver right away
|
|
* and remember that the monitor is on.
|
|
*/
|
|
glinp.timeLastInputMessage = NtGetTickCount();
|
|
glinp.dwFlags &= ~LINP_POWEROFF;
|
|
|
|
if (!gbRemoteSession) {
|
|
/*
|
|
* Re-init the keyboard state.
|
|
*/
|
|
InitKeyboardState();
|
|
|
|
/*
|
|
* Let all the services know that we're waking up. There is no
|
|
* corresponding POWER_ACTION for this, but since this is a
|
|
* non-query event, PowerActionNone is as good as any.
|
|
*/
|
|
LeaveCrit();
|
|
IoPnPDeliverServicePowerNotification(PowerActionNone,
|
|
PBT_APMRESUMEAUTOMATIC,
|
|
0,
|
|
FALSE);
|
|
EnterCrit();
|
|
}
|
|
|
|
/*
|
|
* Let all the applications know that we're waking up.
|
|
*/
|
|
bsmParams.dwRecipients = BSM_ALLDESKTOPS;
|
|
bsmParams.dwFlags = BSF_QUEUENOTIFYMESSAGE;
|
|
xxxSendMessageBSM(NULL,
|
|
WM_POWERBROADCAST,
|
|
PBT_APMRESUMEAUTOMATIC,
|
|
0,
|
|
&bsmParams);
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
RIPMSG1(RIP_ERROR, "Unknown task 0x%x", Task);
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserPowerStateCallout
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 JerrySh Created.
|
|
\***************************************************************************/
|
|
NTSTATUS UserPowerStateCallout(
|
|
PKWIN32_POWERSTATE_PARAMETERS Parms)
|
|
{
|
|
POWERSTATETASK Task = Parms->PowerStateTask;
|
|
BOOLEAN Promotion = Parms->Promotion;
|
|
POWER_ACTION SystemAction = Parms->SystemAction;
|
|
SYSTEM_POWER_STATE MinSystemState = Parms->MinSystemState;
|
|
ULONG Flags = Parms->Flags;
|
|
NTSTATUS Status;
|
|
|
|
if (Task == PowerState_BlockSessionSwitch) {
|
|
/*
|
|
* Don't allow active console session switch while we are in power
|
|
* callouts. First try to block the session switch.
|
|
*/
|
|
return UserSessionSwitchBlock_Start();
|
|
}
|
|
|
|
if (Task == PowerState_UnBlockSessionSwitch) {
|
|
UserAssert(IsSessionSwitchBlocked());
|
|
UserSessionSwitchBlock_End();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Make sure CSRSS is running.
|
|
*/
|
|
if (!gbVideoInitialized || gbNoMorePowerCallouts || !gspwndLogonNotify) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
UserAssert(gpepCSRSS != NULL);
|
|
|
|
EnterPowerCrit();
|
|
if (Task == PowerState_Init) {
|
|
/*
|
|
* Make sure we're not trying to promote a non-existent request
|
|
* or start a new one when we're already doing it.
|
|
*/
|
|
if ((Promotion && !gPowerState.fInProgress) ||
|
|
(!Promotion && gPowerState.fInProgress)) {
|
|
LeavePowerCrit();
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
* Save our state.
|
|
*/
|
|
gPowerState.fInProgress = TRUE;
|
|
gPowerState.fOverrideApps = (Flags & POWER_ACTION_OVERRIDE_APPS) != 0;
|
|
gPowerState.fCritical = (Flags & POWER_ACTION_CRITICAL) != 0;
|
|
gPowerState.fQueryAllowed = (Flags & POWER_ACTION_QUERY_ALLOWED) != 0;
|
|
gPowerState.fUIAllowed = (Flags & POWER_ACTION_UI_ALLOWED) != 0;
|
|
gPowerState.psParams.SystemAction = SystemAction;
|
|
gPowerState.psParams.MinSystemState = MinSystemState;
|
|
gPowerState.psParams.Flags = Flags;
|
|
if (gPowerState.fOverrideApps) {
|
|
gPowerState.bsmParams.dwFlags = BSF_NOHANG | BSF_FORCEIFHUNG;
|
|
}
|
|
if (gPowerState.fCritical) {
|
|
gPowerState.bsmParams.dwFlags = BSF_NOHANG | BSF_QUERY;
|
|
}
|
|
if (gPowerState.pEvent) {
|
|
KeSetEvent(gPowerState.pEvent, EVENT_INCREMENT, FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
gPowerState.PowerStateTask = Task;
|
|
|
|
LeavePowerCrit();
|
|
|
|
/*
|
|
* If this is a promotion, we're done.
|
|
*/
|
|
if (Promotion) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Process the power request.
|
|
*/
|
|
Status = QueuePowerRequest(NULL);
|
|
if (Task == PowerState_QueryApps && !NT_SUCCESS(Status)) {
|
|
/*
|
|
* Query was refused.
|
|
*/
|
|
Parms->fQueryDenied = TRUE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserPowerCalloutWorker
|
|
*
|
|
* Pull any pending power requests off the list and call the appropriate
|
|
* power callout function.
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 JerrySh Created.
|
|
\***************************************************************************/
|
|
VOID
|
|
xxxUserPowerCalloutWorker(
|
|
VOID)
|
|
{
|
|
PPOWERREQUEST pPowerRequest;
|
|
TL tlPool;
|
|
|
|
while ((pPowerRequest = UnqueuePowerRequest()) != NULL) {
|
|
/*
|
|
* Make sure the event gets signalled even if the thread dies in a
|
|
* callback or the waiting thread might get stuck.
|
|
*/
|
|
ThreadLockPoolCleanup(PtiCurrent(), pPowerRequest, &tlPool, CancelPowerRequest);
|
|
|
|
/*
|
|
* Call the appropriate power worker function.
|
|
*/
|
|
gpPowerRequestCurrent = pPowerRequest;
|
|
if (pPowerRequest->Parms) {
|
|
pPowerRequest->Status = xxxUserPowerEventCalloutWorker(pPowerRequest->Parms);
|
|
} else {
|
|
pPowerRequest->Status = xxxUserPowerStateCalloutWorker();
|
|
}
|
|
gpPowerRequestCurrent = NULL;
|
|
|
|
/*
|
|
* If it was a callout, tell the waiting thread to proceed. If it
|
|
* was an event, there is no waiting thread but we need to free the
|
|
* pool.
|
|
*/
|
|
ThreadUnlockPoolCleanup(PtiCurrent(), &tlPool);
|
|
if (pPowerRequest->Parms) {
|
|
UserFreePool(pPowerRequest);
|
|
} else {
|
|
KeSetEvent(&pPowerRequest->Event, EVENT_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* VideoPortCalloutThread
|
|
*
|
|
* Call the appropriate power callout function and return.
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 JerrySh Created.
|
|
\***************************************************************************/
|
|
VOID
|
|
VideoPortCalloutThread(
|
|
PPOWER_INIT pInitData)
|
|
{
|
|
NTSTATUS Status;
|
|
PVIDEO_WIN32K_CALLBACKS_PARAMS Params = pInitData->Params;
|
|
|
|
Params->Status = InitSystemThread(NULL);
|
|
|
|
if (!NT_SUCCESS(Params->Status)) {
|
|
goto RetThreadCallOut;
|
|
}
|
|
|
|
while (1) {
|
|
EnterCrit();
|
|
if (!gfSwitchInProgress) {
|
|
break;
|
|
} else {
|
|
LeaveCrit();
|
|
Status = KeWaitForSingleObject(gpevtVideoportCallout, WrUserRequest, KernelMode, FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
if (IsRemoteConnection()) {
|
|
LeaveCrit();
|
|
Params->Status = STATUS_UNSUCCESSFUL;
|
|
goto RetThreadCallOut;
|
|
}
|
|
|
|
switch (Params->CalloutType) {
|
|
|
|
case VideoWakeupCallout:
|
|
gulDelayedSwitchAction |= SWITCHACTION_RESETMODE;
|
|
|
|
break;
|
|
|
|
case VideoDisplaySwitchCallout:
|
|
{
|
|
UNICODE_STRING strDeviceName;
|
|
DEVMODEW NewMode;
|
|
ULONG bPrune;
|
|
|
|
|
|
Params->Status = STATUS_SUCCESS;
|
|
|
|
if (!DrvQueryMDEVPowerState(gpDispInfo->pmdev)) {
|
|
gulDelayedSwitchAction |= ((Params->PhysDisp != NULL) ? SWITCHACTION_RESETMODE : 0) |
|
|
((Params->Param) ? SWITCHACTION_REENUMERATE : 0);
|
|
break;
|
|
}
|
|
|
|
gulDelayedSwitchAction = 0;
|
|
if (Params->PhysDisp != NULL) {
|
|
if (DrvDisplaySwitchHandler(Params->PhysDisp, &strDeviceName, &NewMode, &bPrune)) {
|
|
DESKRESTOREDATA drdRestore;
|
|
drdRestore.pdeskRestore = NULL;
|
|
|
|
/*
|
|
* CSRSS is not the only process to deliver power callouts.
|
|
*/
|
|
if (!ISCSRSS() ||
|
|
NT_SUCCESS(xxxSetCsrssThreadDesktop(grpdeskRitInput, &drdRestore))) {
|
|
xxxUserChangeDisplaySettings(NULL, NULL, grpdeskRitInput,
|
|
((bPrune) ? 0 : CDS_RAWMODE) | CDS_TRYCLOSEST | CDS_RESET, 0, KernelMode);
|
|
|
|
if (ISCSRSS()) {
|
|
xxxRestoreCsrssThreadDesktop(&drdRestore);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there is a requirement to reenumerate sub-devices.
|
|
*/
|
|
if (Params->Param) {
|
|
IoInvalidateDeviceRelations((PDEVICE_OBJECT)Params->Param, BusRelations);
|
|
}
|
|
|
|
break;
|
|
|
|
case VideoChangeDisplaySettingsCallout:
|
|
{
|
|
DEVMODEW Devmode;
|
|
DESKRESTOREDATA drdRestore;
|
|
|
|
memset(&Devmode, 0, sizeof(DEVMODEW));
|
|
Devmode.dmSize = sizeof(DEVMODEW);
|
|
Devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
Devmode.dmBitsPerPel = 4;
|
|
Devmode.dmPelsWidth = 640;
|
|
Devmode.dmPelsHeight = 480;
|
|
|
|
drdRestore.pdeskRestore = NULL;
|
|
|
|
/*
|
|
* CSRSS is not the only process to deliver power callouts.
|
|
*/
|
|
if (!ISCSRSS() ||
|
|
NT_SUCCESS(xxxSetCsrssThreadDesktop(grpdeskRitInput, &drdRestore))) {
|
|
xxxUserChangeDisplaySettings(NULL, &Devmode, grpdeskRitInput, CDS_RESET, NULL, KernelMode);
|
|
|
|
if (ISCSRSS()) {
|
|
xxxRestoreCsrssThreadDesktop(&drdRestore);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VideoFindAdapterCallout:
|
|
if (Params->Param) {
|
|
SafeEnableMDEV();
|
|
xxxUserResetDisplayDevice();
|
|
} else {
|
|
SafeDisableMDEV();
|
|
}
|
|
|
|
Params->Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
RIPMSG1(RIP_ERROR,
|
|
"Unknown Params->CalloutType 0x%x",
|
|
Params->CalloutType);
|
|
Params->Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
LeaveCrit();
|
|
|
|
RetThreadCallOut:
|
|
/*
|
|
* Signal that the Callout has been ended.
|
|
*/
|
|
KeSetEvent(pInitData->pPowerReadyEvent, EVENT_INCREMENT, FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* VideoPortCallout
|
|
*
|
|
* History:
|
|
* 26-Jul-1998 AndreVa Created.
|
|
\***************************************************************************/
|
|
VOID
|
|
VideoPortCallout(
|
|
IN PVOID Params)
|
|
{
|
|
/*
|
|
* To make sure this is a system thread, we create a new thread.
|
|
*/
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
BOOL fRet;
|
|
USER_API_MSG m;
|
|
POWER_INIT initData;
|
|
|
|
//
|
|
// Make sure video has been initialized.
|
|
//
|
|
if (!gbVideoInitialized) {
|
|
((PVIDEO_WIN32K_CALLBACKS_PARAMS)Params)->Status = STATUS_UNSUCCESSFUL;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure the CsrApiPort has been initialized
|
|
//
|
|
|
|
if (!CsrApiPort) {
|
|
((PVIDEO_WIN32K_CALLBACKS_PARAMS)(Params))->Status = STATUS_INVALID_HANDLE;
|
|
return;
|
|
}
|
|
|
|
initData.Params = Params;
|
|
initData.pPowerReadyEvent = CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
if (initData.pPowerReadyEvent == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto RetCallOut;
|
|
}
|
|
|
|
UserAssert(ISCSRSS());
|
|
|
|
EnterCrit();
|
|
fRet = InitCreateSystemThreadsMsg(&m, CST_POWER, &initData, 0, FALSE);
|
|
LeaveCrit();
|
|
|
|
if (fRet) {
|
|
Status = LpcRequestPort(CsrApiPort, (PPORT_MESSAGE)&m);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
KeWaitForSingleObject(initData.pPowerReadyEvent, WrUserRequest,
|
|
KernelMode, FALSE, NULL);
|
|
Status = ((PVIDEO_WIN32K_CALLBACKS_PARAMS)(Params))->Status;
|
|
}
|
|
}
|
|
|
|
RetCallOut:
|
|
|
|
if (initData.pPowerReadyEvent) {
|
|
FreeKernelEvent(&initData.pPowerReadyEvent);
|
|
}
|
|
|
|
((PVIDEO_WIN32K_CALLBACKS_PARAMS)(Params))->Status = Status;
|
|
}
|