|
|
/*++
Copyright (c) 1990-2000 Microsoft Corporation
Module Name:
pnp.c
Abstract:
This is the pnp portion of the video port driver.
Environment:
kernel mode only
Revision History:
--*/
#include "videoprt.h"
#pragma alloc_text(PAGE,pVideoPortQueryACPIInterface)
#pragma alloc_text(PAGE,pVideoPortACPIEventHandler)
#pragma alloc_text(PAGE,pVideoPortACPIIoctl)
#pragma alloc_text(PAGE,VpRegisterLCDCallbacks)
#pragma alloc_text(PAGE,VpUnregisterLCDCallbacks)
#pragma alloc_text(PAGE,VpRegisterPowerStateCallback)
#pragma alloc_text(PAGE,VpDelayedPowerStateCallback)
#pragma alloc_text(PAGE,VpSetLCDPowerUsage)
BOOLEAN pCheckDeviceRelations( PFDO_EXTENSION FdoExtension, BOOLEAN bNewMonitor );
NTSTATUS pVideoPortQueryACPIInterface( PDEVICE_SPECIFIC_EXTENSION DoSpecificExtension )
/*++
Routine Description:
Send a QueryInterface Irp to our parent (the PCI bus driver) to retrieve the AGP_BUS_INTERFACE.
Returns:
NT_STATUS code
--*/
{ KEVENT Event; PIRP QueryIrp = NULL; IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION NextStack; NTSTATUS Status; ACPI_INTERFACE_STANDARD AcpiInterface; PFDO_EXTENSION FdoExtension = DoSpecificExtension->pFdoExtension;
//
// For those special cases, don't use ACPI HotKey switching
//
if (VpSetupTypeAtBoot != SETUPTYPE_NONE) { return STATUS_INVALID_PARAMETER; }
if (FdoExtension->Flags & LEGACY_DETECT) { return STATUS_INVALID_PARAMETER; } if ((FdoExtension->Flags & FINDADAPTER_SUCCEEDED) == 0) { return STATUS_INVALID_PARAMETER; }
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
QueryIrp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, FdoExtension->AttachedDeviceObject, NULL, 0, NULL, &Event, &IoStatusBlock);
if (QueryIrp == NULL) {
return STATUS_INVALID_PARAMETER; }
//
// Set the default error code.
//
QueryIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
//
// Set up for a QueryInterface Irp.
//
NextStack = IoGetNextIrpStackLocation(QueryIrp);
NextStack->MajorFunction = IRP_MJ_PNP; NextStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
NextStack->Parameters.QueryInterface.InterfaceType = &GUID_ACPI_INTERFACE_STANDARD; NextStack->Parameters.QueryInterface.Size = sizeof(ACPI_INTERFACE_STANDARD); NextStack->Parameters.QueryInterface.Version = 1; NextStack->Parameters.QueryInterface.Interface = (PINTERFACE) &AcpiInterface; NextStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
AcpiInterface.Size = sizeof(ACPI_INTERFACE_STANDARD); AcpiInterface.Version = 1;
//
// Call the filter driver.
//
Status = IoCallDriver(FdoExtension->AttachedDeviceObject, QueryIrp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (NT_SUCCESS(Status)) { pVideoDebugPrint((0, "VideoPort: This is an ACPI Machine !\n"));
//
// Let's register for this event and provide our default handler.
//
AcpiInterface.RegisterForDeviceNotifications(AcpiInterface.Context, //FdoExtension->AttachedDeviceObject,
pVideoPortACPIEventCallback, DoSpecificExtension);
//
// Register for LCD notifications
//
VpRegisterLCDCallbacks(); }
//
// Turn on HotKey switching notify mode
//
if (NT_SUCCESS(Status)) { ULONG active = 0; UCHAR outputBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 10]; Status = pVideoPortACPIIoctl(FdoExtension->AttachedDeviceObject, (ULONG) ('SOD_'), &active, NULL, 0, (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer); }
//
// Register Dock/Undock notification
//
if (NT_SUCCESS(Status)) { Status = IoRegisterPlugPlayNotification(EventCategoryHardwareProfileChange, 0, NULL, FdoExtension->FunctionalDeviceObject->DriverObject, pVideoPortDockEventCallback, DoSpecificExtension, &DockCallbackHandle); }
return Status; }
NTSTATUS pVideoPortDockEventCallback ( PVOID NotificationStructure, PDEVICE_SPECIFIC_EXTENSION DoSpecificExtension ) { UNREFERENCED_PARAMETER(NotificationStructure);
pVideoPortACPIEventCallback(DoSpecificExtension, 0x77);
return STATUS_SUCCESS; }
VOID pVideoPortACPIEventCallback( PDEVICE_SPECIFIC_EXTENSION DoSpecificExtension, ULONG eventID ) /*++
Routine Description:
Event notification callback for panel switching
NOTE This routine is not pageable as it is called from DPC level by the ACPI BIOS.
--*/ { PVIDEO_ACPI_EVENT_CONTEXT pContext;
//
// There are some cases the BIOS send the notofication even before the device is opened
//
if (!DoSpecificExtension->DeviceOpened) return;
if (InterlockedIncrement(&(DoSpecificExtension->AcpiVideoEventsOutstanding)) < 2) {
// Queue work item
pContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(VIDEO_ACPI_EVENT_CONTEXT), VP_TAG);
if (pContext && (eventID == 0x80 || eventID == 0x81 || eventID == 0x90 || eventID == 0x77)) { pContext->DoSpecificExtension = DoSpecificExtension; pContext->EventID = eventID;
ExInitializeWorkItem(&(pContext->workItem), pVideoPortACPIEventHandler, pContext);
ExQueueWorkItem(&(pContext->workItem), DelayedWorkQueue); } } else { // We're getting a Notify storm, and we already have a work item on the job.
InterlockedDecrement(&(DoSpecificExtension->AcpiVideoEventsOutstanding)); }
return; }
VOID pVideoPortACPIEventHandler( PVIDEO_ACPI_EVENT_CONTEXT EventContext ) /*++
Routine Description:
Event handler for panel switching
--*/ { UCHAR outputBuffer[0x200 + sizeof(ACPI_EVAL_OUTPUT_BUFFER)]; PCHILD_PDO_EXTENSION pChildDeviceExtension; PDEVICE_OBJECT AttachedDeviceObject; PDEVICE_OBJECT pChildPdos[10]; ULONG active, szChildIDs, i, AllowSwitch = 0, Switched = 0; PVIDEO_CHILD_STATE_CONFIGURATION pChildIDs; NTSTATUS Status; BOOLEAN bNewMonitor; VIDEO_WIN32K_CALLBACKS_PARAMS calloutParams; PFDO_EXTENSION FdoExtension;
FdoExtension = EventContext->DoSpecificExtension->pFdoExtension;
ASSERT (FdoExtension != NULL); pVideoDebugPrint((1, "pVideoPortACPIEventHandler: Event %08lx trigerred!\n", EventContext->EventID));
AttachedDeviceObject = FdoExtension->AttachedDeviceObject;
if (FdoExtension->DevicePowerState != PowerDeviceD0) { EventContext->DoSpecificExtension->CachedEventID = EventContext->EventID; goto ExitACPIEventHandler; } else { EventContext->DoSpecificExtension->CachedEventID = 0; }
//
// Dock/Undock event handling
//
if (EventContext->EventID == 0x77) { calloutParams.CalloutType = VideoDisplaySwitchCallout; calloutParams.PhysDisp = EventContext->DoSpecificExtension->PhysDisp; calloutParams.Param = (ULONG_PTR)NULL; VpWin32kCallout(&calloutParams);
goto ExitACPIEventHandler; }
//
// Disable BIOS notification
//
active = 2; pVideoPortACPIIoctl(AttachedDeviceObject, (ULONG) ('SOD_'), &active, NULL, 0, (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
if (EventContext->EventID == 0x90) { calloutParams.CalloutType = VideoWakeupCallout;
VpWin32kCallout(&calloutParams); } else { szChildIDs = sizeof(VIDEO_CHILD_STATE_CONFIGURATION) + FdoExtension->ChildPdoNumber*sizeof(VIDEO_CHILD_STATE); pChildIDs = (PVIDEO_CHILD_STATE_CONFIGURATION)ExAllocatePoolWithTag(PagedPool, szChildIDs, VP_TAG); if (pChildIDs != NULL) { //
// During switching, no PnP action is allowed
//
ACQUIRE_DEVICE_LOCK (FdoExtension);
pChildIDs->Count = 0;
for (pChildDeviceExtension = FdoExtension->ChildPdoList; pChildDeviceExtension != NULL; pChildDeviceExtension = pChildDeviceExtension->NextChild ) { if ((!pChildDeviceExtension->bIsEnumerated) || pChildDeviceExtension->VideoChildDescriptor->Type != Monitor) { continue; }
pChildIDs->ChildStateArray[pChildIDs->Count].Id = pChildDeviceExtension->VideoChildDescriptor->UId; pChildIDs->ChildStateArray[pChildIDs->Count].State = 0; pChildPdos[pChildIDs->Count] = pChildDeviceExtension->ChildDeviceObject;
Status = pVideoPortACPIIoctl( IoGetAttachedDevice(pChildDeviceExtension->ChildDeviceObject), (ULONG) ('SGD_'), NULL, NULL, sizeof(ACPI_EVAL_OUTPUT_BUFFER)+0x10, (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
if (NT_SUCCESS(Status)) { ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].Type == ACPI_METHOD_ARGUMENT_INTEGER); ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].DataLength == sizeof(ULONG));
if (((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].Argument) { pChildIDs->ChildStateArray[pChildIDs->Count].State = 1; } }
pChildIDs->Count++; }
szChildIDs = sizeof(VIDEO_CHILD_STATE_CONFIGURATION) + pChildIDs->Count*sizeof(VIDEO_CHILD_STATE);
//
// Notify Miniport that display switching is about to happen.
// Treat the switch is allowed by default.
//
AllowSwitch = 1;
pVideoMiniDeviceIoControl(FdoExtension->FunctionalDeviceObject, IOCTL_VIDEO_VALIDATE_CHILD_STATE_CONFIGURATION, (PVOID)pChildIDs, szChildIDs, &AllowSwitch, sizeof(ULONG));
//
// If Miniport says it's OK to proceed
//
if (AllowSwitch != 0) { //
// Check the Miniport do the switching for us
//
Status = pVideoMiniDeviceIoControl(FdoExtension->FunctionalDeviceObject, IOCTL_VIDEO_SET_CHILD_STATE_CONFIGURATION, (PVOID)pChildIDs, szChildIDs, NULL, 0); if (NT_SUCCESS(Status)) { pVideoDebugPrint((1, "VideoPort: Moniport does the switch!\n")); Switched = 1; } }
//
// The last _DSS needs to commit the switching
//
if (pChildIDs->Count > 0) { pChildIDs->ChildStateArray[pChildIDs->Count-1].State |= 0x80000000; } for (i = 0; i < pChildIDs->Count; i++) { //
// If Miniport doesn't like to proceed or it does the switching already, just notify BIOS to go to next _DGS state
//
// Found some bad BIOS(Toshiba). They do switch anyway regardless of 0x40000000 bit. This has extremely bad effect
// on DualView
//
if (!AllowSwitch) continue; if (Switched) { pChildIDs->ChildStateArray[i].State |= 0x40000000; } pVideoPortACPIIoctl(IoGetAttachedDevice(pChildPdos[i]), (ULONG) ('SSD_'), &pChildIDs->ChildStateArray[i].State, NULL, 0, (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer); }
RELEASE_DEVICE_LOCK (FdoExtension);
ExFreePool(pChildIDs); }
//
// On switching displays, call GDI / USER to tell the device to rebuild mode list
// and change current mode if neccesary
//
pVideoDebugPrint((0, "VideoPrt.sys: Display switching occured - calling GDI to rebuild mode table.\n"));
calloutParams.CalloutType = VideoDisplaySwitchCallout; calloutParams.PhysDisp = (AllowSwitch) ? EventContext->DoSpecificExtension->PhysDisp : NULL;
//
// On Monitor changing, we receive Notify(81)
// On waking up from hibernation, we receive Notify(90)
// We also make IoInvalidateDeviceRelation happen inside Callout routine
//
bNewMonitor = (EventContext->EventID == 0x81);
ACQUIRE_DEVICE_LOCK (FdoExtension);
if (pCheckDeviceRelations(FdoExtension, bNewMonitor) ) { calloutParams.Param = (ULONG_PTR)FdoExtension->PhysicalDeviceObject; } else { calloutParams.Param = (ULONG_PTR)NULL; }
RELEASE_DEVICE_LOCK (FdoExtension);
VpWin32kCallout(&calloutParams); }
ExitACPIEventHandler:
//
// Reenable BIOS notification
//
active = 0; pVideoPortACPIIoctl(AttachedDeviceObject, (ULONG) ('SOD_'), &active, NULL, 0, (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
InterlockedDecrement(&(EventContext->DoSpecificExtension->AcpiVideoEventsOutstanding));
//
// This also ends up freeing the work item as it's embedded in the context.
//
ExFreePool(EventContext);
return; }
NTSTATUS pVideoPortACPIIoctl( IN PDEVICE_OBJECT DeviceObject, IN ULONG MethodName, IN PULONG InputParam1, IN PULONG InputParam2, IN ULONG OutputBufferSize, IN PACPI_EVAL_OUTPUT_BUFFER pOutputBuffer ) /*++
Routine Description:
Called to send a request to the DeviceObject
Arguments:
DeviceObject - The request is sent to this device object MethodName - Name of the method to be run in ACPI space pArgumets - Pointer that will receive the address of the ACPI data
Return Value:
NT Status of the operation
--*/ { UCHAR buffer[sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) + sizeof(ACPI_METHOD_ARGUMENT)]; PACPI_EVAL_INPUT_BUFFER_COMPLEX pInputBuffer; ULONG size; IO_STATUS_BLOCK ioBlock; KEVENT event; NTSTATUS status; PIRP irp;
pVideoDebugPrint((2, "Call ACPI method %c%c%c%c!\n", *((PUCHAR)&MethodName), *((PUCHAR)&MethodName+1), *((PUCHAR)&MethodName+2), *((PUCHAR)&MethodName+3) ));
pInputBuffer = (PACPI_EVAL_INPUT_BUFFER_COMPLEX) buffer;
pInputBuffer->MethodNameAsUlong = MethodName;
if (InputParam1 == NULL) { size = sizeof(ACPI_EVAL_INPUT_BUFFER);
pInputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; } else { size = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX);
pInputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; pInputBuffer->Size = sizeof(ACPI_METHOD_ARGUMENT); pInputBuffer->ArgumentCount = 1;
pInputBuffer->Argument[0].Type = ACPI_METHOD_ARGUMENT_INTEGER; pInputBuffer->Argument[0].DataLength = sizeof(ULONG); pInputBuffer->Argument[0].Argument = *InputParam1; }
if (InputParam2) { size = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) + sizeof(ACPI_METHOD_ARGUMENT);
pInputBuffer->Size = 2 * sizeof(ACPI_METHOD_ARGUMENT); pInputBuffer->ArgumentCount = 2;
pInputBuffer->Argument[1].Type = ACPI_METHOD_ARGUMENT_INTEGER; pInputBuffer->Argument[1].DataLength = sizeof(ULONG); pInputBuffer->Argument[1].Argument = *InputParam2; }
//
// Initialize an event to wait on
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
//
// Build the request
//
irp = IoBuildDeviceIoControlRequest(IOCTL_ACPI_EVAL_METHOD, DeviceObject, pInputBuffer, size, pOutputBuffer, OutputBufferSize, FALSE, &event, &ioBlock);
if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; }
//
// Pass request to DeviceObject, always wait for completion routine
//
status = IoCallDriver(DeviceObject, irp);
if (status == STATUS_PENDING) { //
// Wait for the irp to be completed, then grab the real status code
//
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioBlock.Status; }
//
// Sanity check the data
//
if (NT_SUCCESS(status) && OutputBufferSize != 0) { if (((pOutputBuffer)->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) || ((pOutputBuffer)->Count == 0)) { status = STATUS_ACPI_INVALID_DATA; } }
return status; }
NTSTATUS pVideoMiniDeviceIoControl( IN PDEVICE_OBJECT DeviceObject, IN ULONG dwIoControlCode, IN PVOID lpInBuffer, IN ULONG nInBufferSize, OUT PVOID lpOutBuffer, IN ULONG nOutBufferSize ) { PFDO_EXTENSION combinedExtension; PFDO_EXTENSION fdoExtension;
VIDEO_REQUEST_PACKET vrp; STATUS_BLOCK statusBlock;
combinedExtension = DeviceObject->DeviceExtension; fdoExtension = combinedExtension->pFdoExtension;
statusBlock.Status = ERROR_INVALID_FUNCTION;
vrp.IoControlCode = dwIoControlCode; vrp.StatusBlock = &statusBlock; vrp.InputBuffer = lpInBuffer; vrp.InputBufferLength = nInBufferSize; vrp.OutputBuffer = lpOutBuffer; vrp.OutputBufferLength = nOutBufferSize;
//
// Send the request to the miniport directly.
//
fdoExtension->HwStartIO(combinedExtension->HwDeviceExtension, &vrp);
pVideoPortMapToNtStatus(&statusBlock);
return (statusBlock.Status); }
NTSTATUS VpSetLCDPowerUsage( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN FullPower )
/*++
Routine Description:
It changes the brightness level, based on the value of FullPower. Arguments:
DeviceObject: the ACPI device object attached to our LCD device. FullPower: if TRUE, it sets the brightness level to FullPower level if FALSE, it sets the brightness level to Battery level Returns:
NO_ERROR if it succeeds Various error codes if it fails --*/
{ PACPI_EVAL_OUTPUT_BUFFER Buffer = NULL; PACPI_METHOD_ARGUMENT Argument = NULL; ULONG Granularity = 80; ULONG BufferMaxSize = 4096; NTSTATUS Status = STATUS_UNSUCCESSFUL; ULONG Level = 100;
PAGED_CODE(); ASSERT (DeviceObject != NULL);
//
// Get the list of brightness levels supported
//
do {
Buffer = (PACPI_EVAL_OUTPUT_BUFFER)ExAllocatePoolWithTag( PagedPool, Granularity, VP_TAG); if (Buffer == NULL) { pVideoDebugPrint((Warn, "VIDEOPRT: VpSetLCDPowerUsage: Memory allocation failed.")); Status = STATUS_INSUFFICIENT_RESOURCES; break; }
RtlZeroMemory(Buffer, Granularity);
Status = pVideoPortACPIIoctl( DeviceObject, (ULONG) ('LCB_'), NULL, NULL, Granularity, Buffer); if (Status == STATUS_BUFFER_OVERFLOW) {
ExFreePool(Buffer); Buffer = NULL; Granularity <<= 1; if (Granularity > BufferMaxSize) {
pVideoDebugPrint((Warn, "VIDEOPRT: _BCL failed. Expected buffer is too big.")); Status = STATUS_ACPI_INVALID_DATA; break; } } else if (!NT_SUCCESS(Status)) { pVideoDebugPrint((Warn, "VIDEOPRT: _BCL failed. Status = 0x%x\n", Status)); } else {
pVideoDebugPrint((Trace, "VIDEOPRT: _BCL succeeded.\n")); } } while (Status == STATUS_BUFFER_OVERFLOW);
if ((Buffer == NULL) || (!NT_SUCCESS(Status))) goto Fallout;
//
// Now try to set the state.
//
if (Buffer->Count < 2) { pVideoDebugPrint((Warn, "VIDEOPRT: _BCL returned an invalid number of arguments.")); Status = STATUS_ACPI_INVALID_DATA; goto Fallout; } Argument = Buffer->Argument; if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) { pVideoDebugPrint((Warn, "VIDEOPRT: _BCL returned an invalid argument.")); Status = STATUS_ACPI_INVALID_DATA; goto Fallout; }
Level = Argument->Argument;
if (!FullPower) { Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument); if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) { pVideoDebugPrint((Warn, "VIDEOPRT: _BCL returned an invalid argument.")); Status = STATUS_ACPI_INVALID_DATA; goto Fallout; }
//
// Full power level should be greater than the battery level
//
ASSERT (Level >= Argument->Argument); Level = Argument->Argument; }
Status = pVideoPortACPIIoctl( DeviceObject, (ULONG) ('MCB_'), &Level, NULL, 0, (PACPI_EVAL_OUTPUT_BUFFER)NULL);
if (!NT_SUCCESS(Status)) {
pVideoDebugPrint((Warn, "VIDEOPRT: _BCM failed. Status = 0x%x\n", Status)); } else {
pVideoDebugPrint((Trace, "VIDEOPRT: _BCM succeeded.\n")); } Fallout:
if (Buffer != NULL) { ExFreePool(Buffer); }
return Status; }
VOID VpPowerStateCallback( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 ) /*++
Routine Description:
This is the callback routine that is called when the power state changes. Arguments:
CallbackContext: NULL Argument1: event code Argument2: if Argument1 is PO_CB_AC_STATUS, Argument2 contains TRUE if the current power source is AC and FALSE otherwise.
Note:
This function can be called at DISPATCH_LEVEL --*/
{ PPOWER_STATE_WORK_ITEM PowerStateWorkItem = NULL;
ASSERT (CallbackContext == NULL);
switch ((ULONG_PTR)Argument1) {
case PO_CB_LID_SWITCH_STATE: case PO_CB_AC_STATUS:
PowerStateWorkItem = (PPOWER_STATE_WORK_ITEM)ExAllocatePoolWithTag( NonPagedPool, sizeof(POWER_STATE_WORK_ITEM), VP_TAG);
if (PowerStateWorkItem != NULL) {
PowerStateWorkItem->Argument1 = Argument1; PowerStateWorkItem->Argument2 = Argument2;
ExInitializeWorkItem( &PowerStateWorkItem->WorkItem, VpDelayedPowerStateCallback, PowerStateWorkItem);
ExQueueWorkItem( &PowerStateWorkItem->WorkItem, DelayedWorkQueue);
} else {
pVideoDebugPrint((Warn, "VIDEOPRT: VpPowerStateCallback: Memory allocation failed.")); }
break;
default: //
// Ignore all other cases
//
break; } }
VOID VpDelayedPowerStateCallback( IN PVOID Context )
/*++
Routine Description:
VpPowerStateCallback queues this work item in order to handle PowerState changes at PASSIVE_LEVEL. Arguments:
Context: pointer to POWER_STATE_WORK_ITEM
--*/
{ PPOWER_STATE_WORK_ITEM PowerStateWorkItem = (PPOWER_STATE_WORK_ITEM)Context; PDEVICE_OBJECT AttachedDevice = NULL; NTSTATUS status; POWER_STATE powerState; PCHILD_PDO_EXTENSION pdoExtension; BOOLEAN PowerOverride;
PAGED_CODE(); ASSERT (PowerStateWorkItem != NULL);
KeWaitForSingleObject(&LCDPanelMutex, Executive, KernelMode, FALSE, (PTIME)NULL);
if (LCDPanelDevice == NULL) { goto Fallout; }
switch ((ULONG_PTR)PowerStateWorkItem->Argument1) {
case PO_CB_AC_STATUS:
AttachedDevice = IoGetAttachedDeviceReference(LCDPanelDevice); VpSetLCDPowerUsage( AttachedDevice, (PowerStateWorkItem->Argument2 != 0)); ObDereferenceObject(AttachedDevice); break;
case PO_CB_LID_SWITCH_STATE:
pdoExtension = LCDPanelDevice->DeviceExtension;
if ((ULONG_PTR)PowerStateWorkItem->Argument2 == 0) { //
// The lid is closed. Put the LCD Panel into D3 and override
// any future power requests to the panel.
//
PowerOverride = TRUE; powerState.DeviceState = PowerDeviceD3; pVideoDebugPrint((Trace, "VIDEOPRT: LCD Panel Closed.\n"));
} else if ((ULONG_PTR)PowerStateWorkItem->Argument2 == 1) {
pdoExtension->PowerOverride = PowerOverride = FALSE; powerState.DeviceState = PowerDeviceD0; pVideoDebugPrint((Trace, "VIDEOPRT: LCD Panel Open.\n"));
} else {
pVideoDebugPrint((Error, "VIDEOPRT: Unknown LCD lid close event recieved.\n")); ASSERT(FALSE); goto Fallout; }
if (pdoExtension->pFdoExtension->DevicePowerState == PowerDeviceD0) { status = PoRequestPowerIrp (LCDPanelDevice, IRP_MN_SET_POWER, powerState, pVideoPortPowerIrpComplete, (PVOID)NULL, NULL);
if (status != STATUS_PENDING) { pVideoDebugPrint((Error, "VIDEOPRT: Could not send power IRP to toggle panel\n")); } }
pdoExtension->PowerOverride = PowerOverride;
break;
default: pVideoDebugPrint((Warn, "VIDEOPRT: Unexpected PowerState event recieved.\n")); ASSERT(FALSE); break; }
Fallout:
KeReleaseMutex(&LCDPanelMutex, FALSE);
ExFreePool(Context); }
VOID VpRegisterPowerStateCallback( VOID )
/*++
Routine Description:
This routine registers the PowerState callback routine. --*/
{ OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING CallbackName; PCALLBACK_OBJECT CallbackObject; NTSTATUS Status;
PAGED_CODE();
RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
InitializeObjectAttributes( &ObjectAttributes, &CallbackName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL );
Status = ExCreateCallback( &CallbackObject, &ObjectAttributes, FALSE, TRUE );
if (NT_SUCCESS(Status)) { PowerStateCallbackHandle = ExRegisterCallback( CallbackObject, VpPowerStateCallback, NULL );
if (PowerStateCallbackHandle == NULL) {
pVideoDebugPrint((Warn, "VIDEOPRT: Could not register VpPowerStateCallback.\n")); } else {
pVideoDebugPrint((Trace, "VIDEOPRT: VpPowerStateCallback registered. \n")); } } else {
pVideoDebugPrint((Warn, "VIDEOPRT: Could not get the PowerState callback object.\n")); } }
VOID VpRegisterLCDCallbacks( VOID )
/*++
Routine Description:
This routine registers a callback with the system so that we can be notified of power state changes. --*/
{ PAGED_CODE();
//
// Register power state callback. This works for the lid as well.
//
if (PowerStateCallbackHandle == NULL) { VpRegisterPowerStateCallback(); } }
VOID VpUnregisterLCDCallbacks( VOID )
/*++
Routine Description:
This routine unregisters the callbacks previously registered by VpRegisterLCDCallbacks
Arguments:
None. Note: The global PowerStateCallbackHandle acts as an implicit parameter.
--*/
{ PAGED_CODE();
//
// Unregister power state callback
//
if (PowerStateCallbackHandle != NULL) { ExUnregisterCallback(PowerStateCallbackHandle); PowerStateCallbackHandle = NULL; } }
|