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.
 
 
 
 
 
 

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;
}