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.

1172 lines
32 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. pnp.c
  5. Abstract:
  6. This is the pnp portion of the video port driver.
  7. Environment:
  8. kernel mode only
  9. Revision History:
  10. --*/
  11. #include "videoprt.h"
  12. #pragma alloc_text(PAGE,pVideoPortQueryACPIInterface)
  13. #pragma alloc_text(PAGE,pVideoPortACPIEventHandler)
  14. #pragma alloc_text(PAGE,pVideoPortACPIIoctl)
  15. #pragma alloc_text(PAGE,VpRegisterLCDCallbacks)
  16. #pragma alloc_text(PAGE,VpUnregisterLCDCallbacks)
  17. #pragma alloc_text(PAGE,VpRegisterPowerStateCallback)
  18. #pragma alloc_text(PAGE,VpDelayedPowerStateCallback)
  19. #pragma alloc_text(PAGE,VpSetLCDPowerUsage)
  20. BOOLEAN
  21. pCheckDeviceRelations(
  22. PFDO_EXTENSION FdoExtension,
  23. BOOLEAN bNewMonitor
  24. );
  25. NTSTATUS
  26. pVideoPortQueryACPIInterface(
  27. PDEVICE_SPECIFIC_EXTENSION DoSpecificExtension
  28. )
  29. /*++
  30. Routine Description:
  31. Send a QueryInterface Irp to our parent (the PCI bus driver) to
  32. retrieve the AGP_BUS_INTERFACE.
  33. Returns:
  34. NT_STATUS code
  35. --*/
  36. {
  37. KEVENT Event;
  38. PIRP QueryIrp = NULL;
  39. IO_STATUS_BLOCK IoStatusBlock;
  40. PIO_STACK_LOCATION NextStack;
  41. NTSTATUS Status;
  42. ACPI_INTERFACE_STANDARD AcpiInterface;
  43. PFDO_EXTENSION FdoExtension = DoSpecificExtension->pFdoExtension;
  44. //
  45. // For those special cases, don't use ACPI HotKey switching
  46. //
  47. if (VpSetupTypeAtBoot != SETUPTYPE_NONE) {
  48. return STATUS_INVALID_PARAMETER;
  49. }
  50. if (FdoExtension->Flags & LEGACY_DETECT) {
  51. return STATUS_INVALID_PARAMETER;
  52. }
  53. if ((FdoExtension->Flags & FINDADAPTER_SUCCEEDED) == 0) {
  54. return STATUS_INVALID_PARAMETER;
  55. }
  56. KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
  57. QueryIrp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
  58. FdoExtension->AttachedDeviceObject,
  59. NULL,
  60. 0,
  61. NULL,
  62. &Event,
  63. &IoStatusBlock);
  64. if (QueryIrp == NULL) {
  65. return STATUS_INVALID_PARAMETER;
  66. }
  67. //
  68. // Set the default error code.
  69. //
  70. QueryIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  71. //
  72. // Set up for a QueryInterface Irp.
  73. //
  74. NextStack = IoGetNextIrpStackLocation(QueryIrp);
  75. NextStack->MajorFunction = IRP_MJ_PNP;
  76. NextStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
  77. NextStack->Parameters.QueryInterface.InterfaceType = &GUID_ACPI_INTERFACE_STANDARD;
  78. NextStack->Parameters.QueryInterface.Size = sizeof(ACPI_INTERFACE_STANDARD);
  79. NextStack->Parameters.QueryInterface.Version = 1;
  80. NextStack->Parameters.QueryInterface.Interface = (PINTERFACE) &AcpiInterface;
  81. NextStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
  82. AcpiInterface.Size = sizeof(ACPI_INTERFACE_STANDARD);
  83. AcpiInterface.Version = 1;
  84. //
  85. // Call the filter driver.
  86. //
  87. Status = IoCallDriver(FdoExtension->AttachedDeviceObject, QueryIrp);
  88. if (Status == STATUS_PENDING) {
  89. KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
  90. Status = IoStatusBlock.Status;
  91. }
  92. if (NT_SUCCESS(Status))
  93. {
  94. pVideoDebugPrint((0, "VideoPort: This is an ACPI Machine !\n"));
  95. //
  96. // Let's register for this event and provide our default handler.
  97. //
  98. AcpiInterface.RegisterForDeviceNotifications(AcpiInterface.Context, //FdoExtension->AttachedDeviceObject,
  99. pVideoPortACPIEventCallback,
  100. DoSpecificExtension);
  101. //
  102. // Register for LCD notifications
  103. //
  104. VpRegisterLCDCallbacks();
  105. }
  106. //
  107. // Turn on HotKey switching notify mode
  108. //
  109. if (NT_SUCCESS(Status))
  110. {
  111. ULONG active = 0;
  112. UCHAR outputBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 10];
  113. Status = pVideoPortACPIIoctl(FdoExtension->AttachedDeviceObject,
  114. (ULONG) ('SOD_'),
  115. &active,
  116. NULL,
  117. 0,
  118. (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
  119. }
  120. //
  121. // Register Dock/Undock notification
  122. //
  123. if (NT_SUCCESS(Status))
  124. {
  125. Status = IoRegisterPlugPlayNotification(EventCategoryHardwareProfileChange,
  126. 0,
  127. NULL,
  128. FdoExtension->FunctionalDeviceObject->DriverObject,
  129. pVideoPortDockEventCallback,
  130. DoSpecificExtension,
  131. &DockCallbackHandle);
  132. }
  133. return Status;
  134. }
  135. NTSTATUS
  136. pVideoPortDockEventCallback (
  137. PVOID NotificationStructure,
  138. PDEVICE_SPECIFIC_EXTENSION DoSpecificExtension
  139. )
  140. {
  141. UNREFERENCED_PARAMETER(NotificationStructure);
  142. pVideoPortACPIEventCallback(DoSpecificExtension, 0x77);
  143. return STATUS_SUCCESS;
  144. }
  145. VOID
  146. pVideoPortACPIEventCallback(
  147. PDEVICE_SPECIFIC_EXTENSION DoSpecificExtension,
  148. ULONG eventID
  149. )
  150. /*++
  151. Routine Description:
  152. Event notification callback for panel switching
  153. NOTE This routine is not pageable as it is called from DPC level by
  154. the ACPI BIOS.
  155. --*/
  156. {
  157. PVIDEO_ACPI_EVENT_CONTEXT pContext;
  158. //
  159. // There are some cases the BIOS send the notofication even before the device is opened
  160. //
  161. if (!DoSpecificExtension->DeviceOpened)
  162. return;
  163. if (InterlockedIncrement(&(DoSpecificExtension->AcpiVideoEventsOutstanding)) < 2) {
  164. // Queue work item
  165. pContext = ExAllocatePoolWithTag(NonPagedPool,
  166. sizeof(VIDEO_ACPI_EVENT_CONTEXT),
  167. VP_TAG);
  168. if (pContext && (eventID == 0x80 || eventID == 0x81 || eventID == 0x90 || eventID == 0x77))
  169. {
  170. pContext->DoSpecificExtension = DoSpecificExtension;
  171. pContext->EventID = eventID;
  172. ExInitializeWorkItem(&(pContext->workItem),
  173. pVideoPortACPIEventHandler,
  174. pContext);
  175. ExQueueWorkItem(&(pContext->workItem), DelayedWorkQueue);
  176. }
  177. }
  178. else
  179. {
  180. // We're getting a Notify storm, and we already have a work item on the job.
  181. InterlockedDecrement(&(DoSpecificExtension->AcpiVideoEventsOutstanding));
  182. }
  183. return;
  184. }
  185. VOID
  186. pVideoPortACPIEventHandler(
  187. PVIDEO_ACPI_EVENT_CONTEXT EventContext
  188. )
  189. /*++
  190. Routine Description:
  191. Event handler for panel switching
  192. --*/
  193. {
  194. UCHAR outputBuffer[0x200 + sizeof(ACPI_EVAL_OUTPUT_BUFFER)];
  195. PCHILD_PDO_EXTENSION pChildDeviceExtension;
  196. PDEVICE_OBJECT AttachedDeviceObject;
  197. PDEVICE_OBJECT pChildPdos[10];
  198. ULONG active, szChildIDs, i, AllowSwitch = 0, Switched = 0;
  199. PVIDEO_CHILD_STATE_CONFIGURATION pChildIDs;
  200. NTSTATUS Status;
  201. BOOLEAN bNewMonitor;
  202. VIDEO_WIN32K_CALLBACKS_PARAMS calloutParams;
  203. PFDO_EXTENSION FdoExtension;
  204. FdoExtension = EventContext->DoSpecificExtension->pFdoExtension;
  205. ASSERT (FdoExtension != NULL);
  206. pVideoDebugPrint((1, "pVideoPortACPIEventHandler: Event %08lx trigerred!\n",
  207. EventContext->EventID));
  208. AttachedDeviceObject = FdoExtension->AttachedDeviceObject;
  209. if (FdoExtension->DevicePowerState != PowerDeviceD0)
  210. {
  211. EventContext->DoSpecificExtension->CachedEventID = EventContext->EventID;
  212. goto ExitACPIEventHandler;
  213. }
  214. else
  215. {
  216. EventContext->DoSpecificExtension->CachedEventID = 0;
  217. }
  218. //
  219. // Dock/Undock event handling
  220. //
  221. if (EventContext->EventID == 0x77)
  222. {
  223. calloutParams.CalloutType = VideoDisplaySwitchCallout;
  224. calloutParams.PhysDisp = EventContext->DoSpecificExtension->PhysDisp;
  225. calloutParams.Param = (ULONG_PTR)NULL;
  226. VpWin32kCallout(&calloutParams);
  227. goto ExitACPIEventHandler;
  228. }
  229. //
  230. // Disable BIOS notification
  231. //
  232. active = 2;
  233. pVideoPortACPIIoctl(AttachedDeviceObject,
  234. (ULONG) ('SOD_'),
  235. &active,
  236. NULL,
  237. 0,
  238. (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
  239. if (EventContext->EventID == 0x90)
  240. {
  241. calloutParams.CalloutType = VideoWakeupCallout;
  242. VpWin32kCallout(&calloutParams);
  243. }
  244. else
  245. {
  246. szChildIDs = sizeof(VIDEO_CHILD_STATE_CONFIGURATION) + FdoExtension->ChildPdoNumber*sizeof(VIDEO_CHILD_STATE);
  247. pChildIDs = (PVIDEO_CHILD_STATE_CONFIGURATION)ExAllocatePoolWithTag(PagedPool,
  248. szChildIDs,
  249. VP_TAG);
  250. if (pChildIDs != NULL)
  251. {
  252. //
  253. // During switching, no PnP action is allowed
  254. //
  255. ACQUIRE_DEVICE_LOCK (FdoExtension);
  256. pChildIDs->Count = 0;
  257. for (pChildDeviceExtension = FdoExtension->ChildPdoList;
  258. pChildDeviceExtension != NULL;
  259. pChildDeviceExtension = pChildDeviceExtension->NextChild
  260. )
  261. {
  262. if ((!pChildDeviceExtension->bIsEnumerated) ||
  263. pChildDeviceExtension->VideoChildDescriptor->Type != Monitor)
  264. {
  265. continue;
  266. }
  267. pChildIDs->ChildStateArray[pChildIDs->Count].Id = pChildDeviceExtension->VideoChildDescriptor->UId;
  268. pChildIDs->ChildStateArray[pChildIDs->Count].State = 0;
  269. pChildPdos[pChildIDs->Count] = pChildDeviceExtension->ChildDeviceObject;
  270. Status = pVideoPortACPIIoctl(
  271. IoGetAttachedDevice(pChildDeviceExtension->ChildDeviceObject),
  272. (ULONG) ('SGD_'),
  273. NULL,
  274. NULL,
  275. sizeof(ACPI_EVAL_OUTPUT_BUFFER)+0x10,
  276. (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
  277. if (NT_SUCCESS(Status))
  278. {
  279. ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].Type == ACPI_METHOD_ARGUMENT_INTEGER);
  280. ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].DataLength == sizeof(ULONG));
  281. if (((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].Argument)
  282. {
  283. pChildIDs->ChildStateArray[pChildIDs->Count].State = 1;
  284. }
  285. }
  286. pChildIDs->Count++;
  287. }
  288. szChildIDs = sizeof(VIDEO_CHILD_STATE_CONFIGURATION) + pChildIDs->Count*sizeof(VIDEO_CHILD_STATE);
  289. //
  290. // Notify Miniport that display switching is about to happen.
  291. // Treat the switch is allowed by default.
  292. //
  293. AllowSwitch = 1;
  294. pVideoMiniDeviceIoControl(FdoExtension->FunctionalDeviceObject,
  295. IOCTL_VIDEO_VALIDATE_CHILD_STATE_CONFIGURATION,
  296. (PVOID)pChildIDs,
  297. szChildIDs,
  298. &AllowSwitch,
  299. sizeof(ULONG));
  300. //
  301. // If Miniport says it's OK to proceed
  302. //
  303. if (AllowSwitch != 0)
  304. {
  305. //
  306. // Check the Miniport do the switching for us
  307. //
  308. Status = pVideoMiniDeviceIoControl(FdoExtension->FunctionalDeviceObject,
  309. IOCTL_VIDEO_SET_CHILD_STATE_CONFIGURATION,
  310. (PVOID)pChildIDs,
  311. szChildIDs,
  312. NULL,
  313. 0);
  314. if (NT_SUCCESS(Status))
  315. {
  316. pVideoDebugPrint((1, "VideoPort: Moniport does the switch!\n"));
  317. Switched = 1;
  318. }
  319. }
  320. //
  321. // The last _DSS needs to commit the switching
  322. //
  323. if (pChildIDs->Count > 0)
  324. {
  325. pChildIDs->ChildStateArray[pChildIDs->Count-1].State |= 0x80000000;
  326. }
  327. for (i = 0; i < pChildIDs->Count; i++)
  328. {
  329. //
  330. // If Miniport doesn't like to proceed or it does the switching already, just notify BIOS to go to next _DGS state
  331. //
  332. // Found some bad BIOS(Toshiba). They do switch anyway regardless of 0x40000000 bit. This has extremely bad effect
  333. // on DualView
  334. //
  335. if (!AllowSwitch)
  336. continue;
  337. if (Switched)
  338. {
  339. pChildIDs->ChildStateArray[i].State |= 0x40000000;
  340. }
  341. pVideoPortACPIIoctl(IoGetAttachedDevice(pChildPdos[i]),
  342. (ULONG) ('SSD_'),
  343. &pChildIDs->ChildStateArray[i].State,
  344. NULL,
  345. 0,
  346. (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
  347. }
  348. RELEASE_DEVICE_LOCK (FdoExtension);
  349. ExFreePool(pChildIDs);
  350. }
  351. //
  352. // On switching displays, call GDI / USER to tell the device to rebuild mode list
  353. // and change current mode if neccesary
  354. //
  355. pVideoDebugPrint((0, "VideoPrt.sys: Display switching occured - calling GDI to rebuild mode table.\n"));
  356. calloutParams.CalloutType = VideoDisplaySwitchCallout;
  357. calloutParams.PhysDisp = (AllowSwitch) ? EventContext->DoSpecificExtension->PhysDisp : NULL;
  358. //
  359. // On Monitor changing, we receive Notify(81)
  360. // On waking up from hibernation, we receive Notify(90)
  361. // We also make IoInvalidateDeviceRelation happen inside Callout routine
  362. //
  363. bNewMonitor = (EventContext->EventID == 0x81);
  364. ACQUIRE_DEVICE_LOCK (FdoExtension);
  365. if (pCheckDeviceRelations(FdoExtension, bNewMonitor) )
  366. {
  367. calloutParams.Param = (ULONG_PTR)FdoExtension->PhysicalDeviceObject;
  368. }
  369. else
  370. {
  371. calloutParams.Param = (ULONG_PTR)NULL;
  372. }
  373. RELEASE_DEVICE_LOCK (FdoExtension);
  374. VpWin32kCallout(&calloutParams);
  375. }
  376. ExitACPIEventHandler:
  377. //
  378. // Reenable BIOS notification
  379. //
  380. active = 0;
  381. pVideoPortACPIIoctl(AttachedDeviceObject,
  382. (ULONG) ('SOD_'),
  383. &active,
  384. NULL,
  385. 0,
  386. (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
  387. InterlockedDecrement(&(EventContext->DoSpecificExtension->AcpiVideoEventsOutstanding));
  388. //
  389. // This also ends up freeing the work item as it's embedded in the context.
  390. //
  391. ExFreePool(EventContext);
  392. return;
  393. }
  394. NTSTATUS
  395. pVideoPortACPIIoctl(
  396. IN PDEVICE_OBJECT DeviceObject,
  397. IN ULONG MethodName,
  398. IN PULONG InputParam1,
  399. IN PULONG InputParam2,
  400. IN ULONG OutputBufferSize,
  401. IN PACPI_EVAL_OUTPUT_BUFFER pOutputBuffer
  402. )
  403. /*++
  404. Routine Description:
  405. Called to send a request to the DeviceObject
  406. Arguments:
  407. DeviceObject - The request is sent to this device object
  408. MethodName - Name of the method to be run in ACPI space
  409. pArgumets - Pointer that will receive the address of the ACPI data
  410. Return Value:
  411. NT Status of the operation
  412. --*/
  413. {
  414. UCHAR buffer[sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) +
  415. sizeof(ACPI_METHOD_ARGUMENT)];
  416. PACPI_EVAL_INPUT_BUFFER_COMPLEX pInputBuffer;
  417. ULONG size;
  418. IO_STATUS_BLOCK ioBlock;
  419. KEVENT event;
  420. NTSTATUS status;
  421. PIRP irp;
  422. pVideoDebugPrint((2, "Call ACPI method %c%c%c%c!\n",
  423. *((PUCHAR)&MethodName), *((PUCHAR)&MethodName+1),
  424. *((PUCHAR)&MethodName+2), *((PUCHAR)&MethodName+3) ));
  425. pInputBuffer = (PACPI_EVAL_INPUT_BUFFER_COMPLEX) buffer;
  426. pInputBuffer->MethodNameAsUlong = MethodName;
  427. if (InputParam1 == NULL)
  428. {
  429. size = sizeof(ACPI_EVAL_INPUT_BUFFER);
  430. pInputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
  431. }
  432. else
  433. {
  434. size = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX);
  435. pInputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
  436. pInputBuffer->Size = sizeof(ACPI_METHOD_ARGUMENT);
  437. pInputBuffer->ArgumentCount = 1;
  438. pInputBuffer->Argument[0].Type = ACPI_METHOD_ARGUMENT_INTEGER;
  439. pInputBuffer->Argument[0].DataLength = sizeof(ULONG);
  440. pInputBuffer->Argument[0].Argument = *InputParam1;
  441. }
  442. if (InputParam2)
  443. {
  444. size = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) +
  445. sizeof(ACPI_METHOD_ARGUMENT);
  446. pInputBuffer->Size = 2 * sizeof(ACPI_METHOD_ARGUMENT);
  447. pInputBuffer->ArgumentCount = 2;
  448. pInputBuffer->Argument[1].Type = ACPI_METHOD_ARGUMENT_INTEGER;
  449. pInputBuffer->Argument[1].DataLength = sizeof(ULONG);
  450. pInputBuffer->Argument[1].Argument = *InputParam2;
  451. }
  452. //
  453. // Initialize an event to wait on
  454. //
  455. KeInitializeEvent(&event, SynchronizationEvent, FALSE);
  456. //
  457. // Build the request
  458. //
  459. irp = IoBuildDeviceIoControlRequest(IOCTL_ACPI_EVAL_METHOD,
  460. DeviceObject,
  461. pInputBuffer,
  462. size,
  463. pOutputBuffer,
  464. OutputBufferSize,
  465. FALSE,
  466. &event,
  467. &ioBlock);
  468. if (!irp)
  469. {
  470. return STATUS_INSUFFICIENT_RESOURCES;
  471. }
  472. //
  473. // Pass request to DeviceObject, always wait for completion routine
  474. //
  475. status = IoCallDriver(DeviceObject, irp);
  476. if (status == STATUS_PENDING)
  477. {
  478. //
  479. // Wait for the irp to be completed, then grab the real status code
  480. //
  481. KeWaitForSingleObject(&event,
  482. Executive,
  483. KernelMode,
  484. FALSE,
  485. NULL);
  486. status = ioBlock.Status;
  487. }
  488. //
  489. // Sanity check the data
  490. //
  491. if (NT_SUCCESS(status) && OutputBufferSize != 0)
  492. {
  493. if (((pOutputBuffer)->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) ||
  494. ((pOutputBuffer)->Count == 0))
  495. {
  496. status = STATUS_ACPI_INVALID_DATA;
  497. }
  498. }
  499. return status;
  500. }
  501. NTSTATUS
  502. pVideoMiniDeviceIoControl(
  503. IN PDEVICE_OBJECT DeviceObject,
  504. IN ULONG dwIoControlCode,
  505. IN PVOID lpInBuffer,
  506. IN ULONG nInBufferSize,
  507. OUT PVOID lpOutBuffer,
  508. IN ULONG nOutBufferSize
  509. )
  510. {
  511. PFDO_EXTENSION combinedExtension;
  512. PFDO_EXTENSION fdoExtension;
  513. VIDEO_REQUEST_PACKET vrp;
  514. STATUS_BLOCK statusBlock;
  515. combinedExtension = DeviceObject->DeviceExtension;
  516. fdoExtension = combinedExtension->pFdoExtension;
  517. statusBlock.Status = ERROR_INVALID_FUNCTION;
  518. vrp.IoControlCode = dwIoControlCode;
  519. vrp.StatusBlock = &statusBlock;
  520. vrp.InputBuffer = lpInBuffer;
  521. vrp.InputBufferLength = nInBufferSize;
  522. vrp.OutputBuffer = lpOutBuffer;
  523. vrp.OutputBufferLength = nOutBufferSize;
  524. //
  525. // Send the request to the miniport directly.
  526. //
  527. fdoExtension->HwStartIO(combinedExtension->HwDeviceExtension, &vrp);
  528. pVideoPortMapToNtStatus(&statusBlock);
  529. return (statusBlock.Status);
  530. }
  531. NTSTATUS
  532. VpSetLCDPowerUsage(
  533. IN PDEVICE_OBJECT DeviceObject,
  534. IN BOOLEAN FullPower
  535. )
  536. /*++
  537. Routine Description:
  538. It changes the brightness level, based on the value of FullPower.
  539. Arguments:
  540. DeviceObject: the ACPI device object attached to our LCD device.
  541. FullPower: if TRUE, it sets the brightness level to FullPower level
  542. if FALSE, it sets the brightness level to Battery level
  543. Returns:
  544. NO_ERROR if it succeeds
  545. Various error codes if it fails
  546. --*/
  547. {
  548. PACPI_EVAL_OUTPUT_BUFFER Buffer = NULL;
  549. PACPI_METHOD_ARGUMENT Argument = NULL;
  550. ULONG Granularity = 80;
  551. ULONG BufferMaxSize = 4096;
  552. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  553. ULONG Level = 100;
  554. PAGED_CODE();
  555. ASSERT (DeviceObject != NULL);
  556. //
  557. // Get the list of brightness levels supported
  558. //
  559. do {
  560. Buffer = (PACPI_EVAL_OUTPUT_BUFFER)ExAllocatePoolWithTag(
  561. PagedPool,
  562. Granularity,
  563. VP_TAG);
  564. if (Buffer == NULL) {
  565. pVideoDebugPrint((Warn,
  566. "VIDEOPRT: VpSetLCDPowerUsage: Memory allocation failed."));
  567. Status = STATUS_INSUFFICIENT_RESOURCES;
  568. break;
  569. }
  570. RtlZeroMemory(Buffer, Granularity);
  571. Status = pVideoPortACPIIoctl(
  572. DeviceObject,
  573. (ULONG) ('LCB_'),
  574. NULL,
  575. NULL,
  576. Granularity,
  577. Buffer);
  578. if (Status == STATUS_BUFFER_OVERFLOW) {
  579. ExFreePool(Buffer);
  580. Buffer = NULL;
  581. Granularity <<= 1;
  582. if (Granularity > BufferMaxSize) {
  583. pVideoDebugPrint((Warn,
  584. "VIDEOPRT: _BCL failed. Expected buffer is too big."));
  585. Status = STATUS_ACPI_INVALID_DATA;
  586. break;
  587. }
  588. } else if (!NT_SUCCESS(Status)) {
  589. pVideoDebugPrint((Warn,
  590. "VIDEOPRT: _BCL failed. Status = 0x%x\n", Status));
  591. } else {
  592. pVideoDebugPrint((Trace, "VIDEOPRT: _BCL succeeded.\n"));
  593. }
  594. } while (Status == STATUS_BUFFER_OVERFLOW);
  595. if ((Buffer == NULL) || (!NT_SUCCESS(Status)))
  596. goto Fallout;
  597. //
  598. // Now try to set the state.
  599. //
  600. if (Buffer->Count < 2) {
  601. pVideoDebugPrint((Warn,
  602. "VIDEOPRT: _BCL returned an invalid number of arguments."));
  603. Status = STATUS_ACPI_INVALID_DATA;
  604. goto Fallout;
  605. }
  606. Argument = Buffer->Argument;
  607. if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
  608. pVideoDebugPrint((Warn,
  609. "VIDEOPRT: _BCL returned an invalid argument."));
  610. Status = STATUS_ACPI_INVALID_DATA;
  611. goto Fallout;
  612. }
  613. Level = Argument->Argument;
  614. if (!FullPower) {
  615. Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
  616. if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
  617. pVideoDebugPrint((Warn,
  618. "VIDEOPRT: _BCL returned an invalid argument."));
  619. Status = STATUS_ACPI_INVALID_DATA;
  620. goto Fallout;
  621. }
  622. //
  623. // Full power level should be greater than the battery level
  624. //
  625. ASSERT (Level >= Argument->Argument);
  626. Level = Argument->Argument;
  627. }
  628. Status = pVideoPortACPIIoctl(
  629. DeviceObject,
  630. (ULONG) ('MCB_'),
  631. &Level,
  632. NULL,
  633. 0,
  634. (PACPI_EVAL_OUTPUT_BUFFER)NULL);
  635. if (!NT_SUCCESS(Status)) {
  636. pVideoDebugPrint((Warn,
  637. "VIDEOPRT: _BCM failed. Status = 0x%x\n", Status));
  638. } else {
  639. pVideoDebugPrint((Trace, "VIDEOPRT: _BCM succeeded.\n"));
  640. }
  641. Fallout:
  642. if (Buffer != NULL) {
  643. ExFreePool(Buffer);
  644. }
  645. return Status;
  646. }
  647. VOID
  648. VpPowerStateCallback(
  649. IN PVOID CallbackContext,
  650. IN PVOID Argument1,
  651. IN PVOID Argument2
  652. )
  653. /*++
  654. Routine Description:
  655. This is the callback routine that is called when the power state changes.
  656. Arguments:
  657. CallbackContext: NULL
  658. Argument1: event code
  659. Argument2: if Argument1 is PO_CB_AC_STATUS, Argument2 contains TRUE if
  660. the current power source is AC and FALSE otherwise.
  661. Note:
  662. This function can be called at DISPATCH_LEVEL
  663. --*/
  664. {
  665. PPOWER_STATE_WORK_ITEM PowerStateWorkItem = NULL;
  666. ASSERT (CallbackContext == NULL);
  667. switch ((ULONG_PTR)Argument1) {
  668. case PO_CB_LID_SWITCH_STATE:
  669. case PO_CB_AC_STATUS:
  670. PowerStateWorkItem =
  671. (PPOWER_STATE_WORK_ITEM)ExAllocatePoolWithTag(
  672. NonPagedPool,
  673. sizeof(POWER_STATE_WORK_ITEM),
  674. VP_TAG);
  675. if (PowerStateWorkItem != NULL) {
  676. PowerStateWorkItem->Argument1 = Argument1;
  677. PowerStateWorkItem->Argument2 = Argument2;
  678. ExInitializeWorkItem(
  679. &PowerStateWorkItem->WorkItem,
  680. VpDelayedPowerStateCallback,
  681. PowerStateWorkItem);
  682. ExQueueWorkItem(
  683. &PowerStateWorkItem->WorkItem,
  684. DelayedWorkQueue);
  685. } else {
  686. pVideoDebugPrint((Warn,
  687. "VIDEOPRT: VpPowerStateCallback: Memory allocation failed."));
  688. }
  689. break;
  690. default:
  691. //
  692. // Ignore all other cases
  693. //
  694. break;
  695. }
  696. }
  697. VOID
  698. VpDelayedPowerStateCallback(
  699. IN PVOID Context
  700. )
  701. /*++
  702. Routine Description:
  703. VpPowerStateCallback queues this work item in order to handle
  704. PowerState changes at PASSIVE_LEVEL.
  705. Arguments:
  706. Context: pointer to POWER_STATE_WORK_ITEM
  707. --*/
  708. {
  709. PPOWER_STATE_WORK_ITEM PowerStateWorkItem =
  710. (PPOWER_STATE_WORK_ITEM)Context;
  711. PDEVICE_OBJECT AttachedDevice = NULL;
  712. NTSTATUS status;
  713. POWER_STATE powerState;
  714. PCHILD_PDO_EXTENSION pdoExtension;
  715. BOOLEAN PowerOverride;
  716. PAGED_CODE();
  717. ASSERT (PowerStateWorkItem != NULL);
  718. KeWaitForSingleObject(&LCDPanelMutex,
  719. Executive,
  720. KernelMode,
  721. FALSE,
  722. (PTIME)NULL);
  723. if (LCDPanelDevice == NULL) {
  724. goto Fallout;
  725. }
  726. switch ((ULONG_PTR)PowerStateWorkItem->Argument1) {
  727. case PO_CB_AC_STATUS:
  728. AttachedDevice = IoGetAttachedDeviceReference(LCDPanelDevice);
  729. VpSetLCDPowerUsage(
  730. AttachedDevice,
  731. (PowerStateWorkItem->Argument2 != 0));
  732. ObDereferenceObject(AttachedDevice);
  733. break;
  734. case PO_CB_LID_SWITCH_STATE:
  735. pdoExtension = LCDPanelDevice->DeviceExtension;
  736. if ((ULONG_PTR)PowerStateWorkItem->Argument2 == 0) {
  737. //
  738. // The lid is closed. Put the LCD Panel into D3 and override
  739. // any future power requests to the panel.
  740. //
  741. PowerOverride = TRUE;
  742. powerState.DeviceState = PowerDeviceD3;
  743. pVideoDebugPrint((Trace, "VIDEOPRT: LCD Panel Closed.\n"));
  744. } else if ((ULONG_PTR)PowerStateWorkItem->Argument2 == 1) {
  745. pdoExtension->PowerOverride = PowerOverride = FALSE;
  746. powerState.DeviceState = PowerDeviceD0;
  747. pVideoDebugPrint((Trace, "VIDEOPRT: LCD Panel Open.\n"));
  748. } else {
  749. pVideoDebugPrint((Error, "VIDEOPRT: Unknown LCD lid close event recieved.\n"));
  750. ASSERT(FALSE);
  751. goto Fallout;
  752. }
  753. if (pdoExtension->pFdoExtension->DevicePowerState == PowerDeviceD0)
  754. {
  755. status = PoRequestPowerIrp (LCDPanelDevice,
  756. IRP_MN_SET_POWER,
  757. powerState,
  758. pVideoPortPowerIrpComplete,
  759. (PVOID)NULL,
  760. NULL);
  761. if (status != STATUS_PENDING) {
  762. pVideoDebugPrint((Error, "VIDEOPRT: Could not send power IRP to toggle panel\n"));
  763. }
  764. }
  765. pdoExtension->PowerOverride = PowerOverride;
  766. break;
  767. default:
  768. pVideoDebugPrint((Warn,
  769. "VIDEOPRT: Unexpected PowerState event recieved.\n"));
  770. ASSERT(FALSE);
  771. break;
  772. }
  773. Fallout:
  774. KeReleaseMutex(&LCDPanelMutex, FALSE);
  775. ExFreePool(Context);
  776. }
  777. VOID
  778. VpRegisterPowerStateCallback(
  779. VOID
  780. )
  781. /*++
  782. Routine Description:
  783. This routine registers the PowerState callback routine.
  784. --*/
  785. {
  786. OBJECT_ATTRIBUTES ObjectAttributes;
  787. UNICODE_STRING CallbackName;
  788. PCALLBACK_OBJECT CallbackObject;
  789. NTSTATUS Status;
  790. PAGED_CODE();
  791. RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
  792. InitializeObjectAttributes(
  793. &ObjectAttributes,
  794. &CallbackName,
  795. OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
  796. NULL,
  797. NULL
  798. );
  799. Status = ExCreateCallback(
  800. &CallbackObject,
  801. &ObjectAttributes,
  802. FALSE,
  803. TRUE
  804. );
  805. if (NT_SUCCESS(Status)) {
  806. PowerStateCallbackHandle = ExRegisterCallback(
  807. CallbackObject,
  808. VpPowerStateCallback,
  809. NULL
  810. );
  811. if (PowerStateCallbackHandle == NULL) {
  812. pVideoDebugPrint((Warn,
  813. "VIDEOPRT: Could not register VpPowerStateCallback.\n"));
  814. } else {
  815. pVideoDebugPrint((Trace,
  816. "VIDEOPRT: VpPowerStateCallback registered. \n"));
  817. }
  818. } else {
  819. pVideoDebugPrint((Warn,
  820. "VIDEOPRT: Could not get the PowerState callback object.\n"));
  821. }
  822. }
  823. VOID
  824. VpRegisterLCDCallbacks(
  825. VOID
  826. )
  827. /*++
  828. Routine Description:
  829. This routine registers a callback with the system so that we can
  830. be notified of power state changes.
  831. --*/
  832. {
  833. PAGED_CODE();
  834. //
  835. // Register power state callback. This works for the lid as well.
  836. //
  837. if (PowerStateCallbackHandle == NULL) {
  838. VpRegisterPowerStateCallback();
  839. }
  840. }
  841. VOID
  842. VpUnregisterLCDCallbacks(
  843. VOID
  844. )
  845. /*++
  846. Routine Description:
  847. This routine unregisters the callbacks previously registered by
  848. VpRegisterLCDCallbacks
  849. Arguments:
  850. None.
  851. Note:
  852. The global PowerStateCallbackHandle acts as an implicit parameter.
  853. --*/
  854. {
  855. PAGED_CODE();
  856. //
  857. // Unregister power state callback
  858. //
  859. if (PowerStateCallbackHandle != NULL) {
  860. ExUnregisterCallback(PowerStateCallbackHandle);
  861. PowerStateCallbackHandle = NULL;
  862. }
  863. }