Source code of Windows XP (NT5)
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.

1403 lines
36 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. if (pCheckDeviceRelations(FdoExtension, bNewMonitor) )
  365. {
  366. calloutParams.Param = (ULONG_PTR)FdoExtension->PhysicalDeviceObject;
  367. }
  368. else
  369. {
  370. calloutParams.Param = (ULONG_PTR)NULL;
  371. }
  372. VpWin32kCallout(&calloutParams);
  373. }
  374. ExitACPIEventHandler:
  375. //
  376. // Reenable BIOS notification
  377. //
  378. active = 0;
  379. pVideoPortACPIIoctl(AttachedDeviceObject,
  380. (ULONG) ('SOD_'),
  381. &active,
  382. NULL,
  383. 0,
  384. (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer);
  385. InterlockedDecrement(&(EventContext->DoSpecificExtension->AcpiVideoEventsOutstanding));
  386. //
  387. // This also ends up freeing the work item as it's embedded in the context.
  388. //
  389. ExFreePool(EventContext);
  390. return;
  391. }
  392. NTSTATUS
  393. pVideoPortACPIIoctl(
  394. IN PDEVICE_OBJECT DeviceObject,
  395. IN ULONG MethodName,
  396. IN PULONG InputParam1,
  397. IN PULONG InputParam2,
  398. IN ULONG OutputBufferSize,
  399. IN PACPI_EVAL_OUTPUT_BUFFER pOutputBuffer
  400. )
  401. /*++
  402. Routine Description:
  403. Called to send a request to the DeviceObject
  404. Arguments:
  405. DeviceObject - The request is sent to this device object
  406. MethodName - Name of the method to be run in ACPI space
  407. pArgumets - Pointer that will receive the address of the ACPI data
  408. Return Value:
  409. NT Status of the operation
  410. --*/
  411. {
  412. UCHAR buffer[sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) +
  413. sizeof(ACPI_METHOD_ARGUMENT)];
  414. PACPI_EVAL_INPUT_BUFFER_COMPLEX pInputBuffer;
  415. ULONG size;
  416. IO_STATUS_BLOCK ioBlock;
  417. KEVENT event;
  418. NTSTATUS status;
  419. PIRP irp;
  420. pVideoDebugPrint((2, "Call ACPI method %c%c%c%c!\n",
  421. *((PUCHAR)&MethodName), *((PUCHAR)&MethodName+1),
  422. *((PUCHAR)&MethodName+2), *((PUCHAR)&MethodName+3) ));
  423. pInputBuffer = (PACPI_EVAL_INPUT_BUFFER_COMPLEX) buffer;
  424. pInputBuffer->MethodNameAsUlong = MethodName;
  425. if (InputParam1 == NULL)
  426. {
  427. size = sizeof(ACPI_EVAL_INPUT_BUFFER);
  428. pInputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
  429. }
  430. else
  431. {
  432. size = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX);
  433. pInputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
  434. pInputBuffer->Size = sizeof(ACPI_METHOD_ARGUMENT);
  435. pInputBuffer->ArgumentCount = 1;
  436. pInputBuffer->Argument[0].Type = ACPI_METHOD_ARGUMENT_INTEGER;
  437. pInputBuffer->Argument[0].DataLength = sizeof(ULONG);
  438. pInputBuffer->Argument[0].Argument = *InputParam1;
  439. }
  440. if (InputParam2)
  441. {
  442. size = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) +
  443. sizeof(ACPI_METHOD_ARGUMENT);
  444. pInputBuffer->Size = 2 * sizeof(ACPI_METHOD_ARGUMENT);
  445. pInputBuffer->ArgumentCount = 2;
  446. pInputBuffer->Argument[1].Type = ACPI_METHOD_ARGUMENT_INTEGER;
  447. pInputBuffer->Argument[1].DataLength = sizeof(ULONG);
  448. pInputBuffer->Argument[1].Argument = *InputParam2;
  449. }
  450. //
  451. // Initialize an event to wait on
  452. //
  453. KeInitializeEvent(&event, SynchronizationEvent, FALSE);
  454. //
  455. // Build the request
  456. //
  457. irp = IoBuildDeviceIoControlRequest(IOCTL_ACPI_EVAL_METHOD,
  458. DeviceObject,
  459. pInputBuffer,
  460. size,
  461. pOutputBuffer,
  462. OutputBufferSize,
  463. FALSE,
  464. &event,
  465. &ioBlock);
  466. if (!irp)
  467. {
  468. return STATUS_INSUFFICIENT_RESOURCES;
  469. }
  470. //
  471. // Pass request to DeviceObject, always wait for completion routine
  472. //
  473. status = IoCallDriver(DeviceObject, irp);
  474. if (status == STATUS_PENDING)
  475. {
  476. //
  477. // Wait for the irp to be completed, then grab the real status code
  478. //
  479. KeWaitForSingleObject(&event,
  480. Executive,
  481. KernelMode,
  482. FALSE,
  483. NULL);
  484. status = ioBlock.Status;
  485. }
  486. //
  487. // Sanity check the data
  488. //
  489. if (NT_SUCCESS(status) && OutputBufferSize != 0)
  490. {
  491. if (((pOutputBuffer)->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) ||
  492. ((pOutputBuffer)->Count == 0))
  493. {
  494. status = STATUS_ACPI_INVALID_DATA;
  495. }
  496. }
  497. return status;
  498. }
  499. NTSTATUS
  500. pVideoMiniDeviceIoControl(
  501. IN PDEVICE_OBJECT DeviceObject,
  502. IN ULONG dwIoControlCode,
  503. IN PVOID lpInBuffer,
  504. IN ULONG nInBufferSize,
  505. OUT PVOID lpOutBuffer,
  506. IN ULONG nOutBufferSize
  507. )
  508. {
  509. PFDO_EXTENSION combinedExtension;
  510. PFDO_EXTENSION fdoExtension;
  511. VIDEO_REQUEST_PACKET vrp;
  512. STATUS_BLOCK statusBlock;
  513. combinedExtension = DeviceObject->DeviceExtension;
  514. fdoExtension = combinedExtension->pFdoExtension;
  515. statusBlock.Status = ERROR_INVALID_FUNCTION;
  516. vrp.IoControlCode = dwIoControlCode;
  517. vrp.StatusBlock = &statusBlock;
  518. vrp.InputBuffer = lpInBuffer;
  519. vrp.InputBufferLength = nInBufferSize;
  520. vrp.OutputBuffer = lpOutBuffer;
  521. vrp.OutputBufferLength = nOutBufferSize;
  522. //
  523. // Send the request to the miniport directly.
  524. //
  525. fdoExtension->HwStartIO(combinedExtension->HwDeviceExtension, &vrp);
  526. pVideoPortMapToNtStatus(&statusBlock);
  527. return (statusBlock.Status);
  528. }
  529. NTSTATUS
  530. VpQueryBacklightLevels(
  531. IN PDEVICE_OBJECT DeviceObject,
  532. OUT PUCHAR ucBacklightLevels,
  533. OUT PULONG pulNumberOfLevelsSupported
  534. )
  535. /*++
  536. Routine Description:
  537. This function will query the list of levels supported by _BCL.
  538. Arguments:
  539. DeviceObject: The ACPI device object attached to our LCD device.
  540. ucBacklightLevels: The list of backlight levels supported by the ACPI BIOS.
  541. pulNumberOfLevelsSupported: This is the number of actual levels the ACPI BIOS supports,
  542. Returns:
  543. NO_ERROR if it succeeds
  544. Various error codes if it fails
  545. --*/
  546. {
  547. PACPI_EVAL_OUTPUT_BUFFER Buffer = NULL;
  548. PACPI_METHOD_ARGUMENT Argument = NULL;
  549. ULONG Granularity = 80;
  550. ULONG BufferMaxSize = 4096;
  551. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  552. ULONG Level = 100;
  553. ULONG count = 0;
  554. PBACKLIGHT_STATUS pVpBacklightStatus = &VpBacklightStatus;
  555. PUCHAR ucLevels = ucBacklightLevels;
  556. PAGED_CODE();
  557. ASSERT (DeviceObject != NULL);
  558. //
  559. // Get the list of brightness levels supported
  560. //
  561. do {
  562. Buffer = (PACPI_EVAL_OUTPUT_BUFFER)ExAllocatePoolWithTag(
  563. PagedPool,
  564. Granularity,
  565. VP_TAG);
  566. if (Buffer == NULL) {
  567. pVideoDebugPrint((Warn,
  568. "VIDEOPRT: VpQueryBacklightLevels: Memory allocation failed."));
  569. Status = STATUS_INSUFFICIENT_RESOURCES;
  570. break;
  571. }
  572. RtlZeroMemory(Buffer, Granularity);
  573. Status = pVideoPortACPIIoctl(
  574. DeviceObject,
  575. (ULONG) ('LCB_'),
  576. NULL,
  577. NULL,
  578. Granularity,
  579. Buffer);
  580. if (Status == STATUS_BUFFER_OVERFLOW) {
  581. ExFreePool(Buffer);
  582. Buffer = NULL;
  583. Granularity <<= 1;
  584. if (Granularity > BufferMaxSize) {
  585. pVideoDebugPrint((Warn,
  586. "VIDEOPRT: _BCL failed. Expected buffer is too big."));
  587. Status = STATUS_ACPI_INVALID_DATA;
  588. break;
  589. }
  590. } else if (!NT_SUCCESS(Status)) {
  591. pVideoDebugPrint((Warn,
  592. "VIDEOPRT: _BCL failed. Status = 0x%x\n", Status));
  593. } else {
  594. pVideoDebugPrint((Trace, "VIDEOPRT: _BCL succeeded.\n"));
  595. }
  596. } while (Status == STATUS_BUFFER_OVERFLOW);
  597. if ((Buffer == NULL) || (!NT_SUCCESS(Status)))
  598. goto Fallout;
  599. //
  600. // We should have 2+ levels. If we have only have 2 levels, the
  601. // BIOS only reports the recommended AC/DC values. This function
  602. // is therefore only useful if we have 3+ levels reported by the
  603. // ACPI BIOS.
  604. //
  605. if (Buffer->Count < 3) {
  606. pVideoDebugPrint((Warn,
  607. "VIDEOPRT: _BCL returned an fewer than three arguments."));
  608. Status = STATUS_ACPI_INVALID_DATA;
  609. goto Fallout;
  610. }
  611. //
  612. // Save off BIOS "default" AC value for initial settings.
  613. //
  614. Argument = Buffer->Argument;
  615. if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
  616. pVideoDebugPrint((Warn,
  617. "VIDEOPRT: _BCL returned an invalid argument."));
  618. Status = STATUS_ACPI_INVALID_DATA;
  619. goto Fallout;
  620. }
  621. Level = Argument->Argument;
  622. pVpBacklightStatus->bBIOSDefaultACKnown = TRUE;
  623. pVpBacklightStatus->ucBIOSDefaultAC = (unsigned char) Level;
  624. //
  625. // Save off BIOS "default" DC value for initial settings.
  626. //
  627. Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
  628. if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
  629. pVideoDebugPrint((Warn,
  630. "VIDEOPRT: _BCL returned an invalid argument."));
  631. Status = STATUS_ACPI_INVALID_DATA;
  632. goto Fallout;
  633. }
  634. //
  635. // Full power level should be greater than the battery level
  636. //
  637. ASSERT (Level >= Argument->Argument);
  638. Level = Argument->Argument;
  639. pVpBacklightStatus->bBIOSDefaultDCKnown = TRUE;
  640. pVpBacklightStatus->ucBIOSDefaultDC = (unsigned char) Level;
  641. //
  642. // Run through the list of supported modes that follow the AC/DC
  643. // values and return them to the caller.
  644. //
  645. *pulNumberOfLevelsSupported = 0;
  646. for (count = 0; count < (Buffer->Count - 2); count++) {
  647. //
  648. // Proceed to the next argument
  649. //
  650. Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
  651. if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
  652. pVideoDebugPrint((Warn,
  653. "VIDEOPRT: _BCL returned an invalid argument."));
  654. Status = STATUS_ACPI_INVALID_DATA;
  655. goto Fallout;
  656. }
  657. //
  658. // Save off the argument in our array of levels.
  659. //
  660. Level = Argument->Argument;
  661. *ucLevels++ = (unsigned char) Level;
  662. *pulNumberOfLevelsSupported += 1;
  663. }
  664. Status = NO_ERROR;
  665. Fallout:
  666. if (Buffer != NULL) {
  667. ExFreePool(Buffer);
  668. }
  669. return Status;
  670. }
  671. NTSTATUS
  672. VpSetLCDPowerUsage(
  673. IN PDEVICE_OBJECT DeviceObject,
  674. IN BOOLEAN FullPower
  675. )
  676. /*++
  677. Routine Description:
  678. It changes the brightness level, based on the value of FullPower.
  679. Arguments:
  680. DeviceObject: the ACPI device object attached to our LCD device.
  681. FullPower: if TRUE, it sets the brightness level to FullPower level
  682. if FALSE, it sets the brightness level to Battery level
  683. Returns:
  684. NO_ERROR if it succeeds
  685. Various error codes if it fails
  686. --*/
  687. {
  688. PACPI_EVAL_OUTPUT_BUFFER Buffer = NULL;
  689. PACPI_METHOD_ARGUMENT Argument = NULL;
  690. ULONG Granularity = 80;
  691. ULONG BufferMaxSize = 4096;
  692. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  693. ULONG Level = 100;
  694. PBACKLIGHT_STATUS pVpBacklightStatus = &VpBacklightStatus;
  695. PAGED_CODE();
  696. ASSERT (DeviceObject != NULL);
  697. //
  698. // Track whether we are running on AC or DC
  699. //
  700. VpRunningOnAC = FullPower;
  701. //
  702. // If we are using the new API for backlight control, we don't need to query the
  703. // BIOS with _BCL. We will set the AC or DC level as appropriate with _BCM.
  704. //
  705. if (pVpBacklightStatus->bNewAPISupported) {
  706. if (VpRunningOnAC) {
  707. Level = (ULONG) pVpBacklightStatus->ucACBrightness;
  708. }
  709. else {
  710. Level = (ULONG) pVpBacklightStatus->ucDCBrightness;
  711. }
  712. Status = pVideoPortACPIIoctl(
  713. DeviceObject,
  714. (ULONG) ('MCB_'),
  715. &Level,
  716. NULL,
  717. 0,
  718. (PACPI_EVAL_OUTPUT_BUFFER)NULL);
  719. return Status;
  720. }
  721. //
  722. // Get the list of brightness levels supported
  723. //
  724. do {
  725. Buffer = (PACPI_EVAL_OUTPUT_BUFFER)ExAllocatePoolWithTag(
  726. PagedPool,
  727. Granularity,
  728. VP_TAG);
  729. if (Buffer == NULL) {
  730. pVideoDebugPrint((Warn,
  731. "VIDEOPRT: VpSetLCDPowerUsage: Memory allocation failed."));
  732. Status = STATUS_INSUFFICIENT_RESOURCES;
  733. break;
  734. }
  735. RtlZeroMemory(Buffer, Granularity);
  736. Status = pVideoPortACPIIoctl(
  737. DeviceObject,
  738. (ULONG) ('LCB_'),
  739. NULL,
  740. NULL,
  741. Granularity,
  742. Buffer);
  743. if (Status == STATUS_BUFFER_OVERFLOW) {
  744. ExFreePool(Buffer);
  745. Buffer = NULL;
  746. Granularity <<= 1;
  747. if (Granularity > BufferMaxSize) {
  748. pVideoDebugPrint((Warn,
  749. "VIDEOPRT: _BCL failed. Expected buffer is too big."));
  750. Status = STATUS_ACPI_INVALID_DATA;
  751. break;
  752. }
  753. } else if (!NT_SUCCESS(Status)) {
  754. pVideoDebugPrint((Warn,
  755. "VIDEOPRT: _BCL failed. Status = 0x%x\n", Status));
  756. } else {
  757. pVideoDebugPrint((Trace, "VIDEOPRT: _BCL succeeded.\n"));
  758. }
  759. } while (Status == STATUS_BUFFER_OVERFLOW);
  760. if ((Buffer == NULL) || (!NT_SUCCESS(Status)))
  761. goto Fallout;
  762. //
  763. // Now try to set the state.
  764. //
  765. if (Buffer->Count < 2) {
  766. pVideoDebugPrint((Warn,
  767. "VIDEOPRT: _BCL returned an invalid number of arguments."));
  768. Status = STATUS_ACPI_INVALID_DATA;
  769. goto Fallout;
  770. }
  771. Argument = Buffer->Argument;
  772. if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
  773. pVideoDebugPrint((Warn,
  774. "VIDEOPRT: _BCL returned an invalid argument."));
  775. Status = STATUS_ACPI_INVALID_DATA;
  776. goto Fallout;
  777. }
  778. Level = Argument->Argument;
  779. if (!FullPower) {
  780. Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
  781. if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
  782. pVideoDebugPrint((Warn,
  783. "VIDEOPRT: _BCL returned an invalid argument."));
  784. Status = STATUS_ACPI_INVALID_DATA;
  785. goto Fallout;
  786. }
  787. //
  788. // Full power level should be greater than the battery level
  789. //
  790. ASSERT (Level >= Argument->Argument);
  791. Level = Argument->Argument;
  792. }
  793. Status = pVideoPortACPIIoctl(
  794. DeviceObject,
  795. (ULONG) ('MCB_'),
  796. &Level,
  797. NULL,
  798. 0,
  799. (PACPI_EVAL_OUTPUT_BUFFER)NULL);
  800. if (!NT_SUCCESS(Status)) {
  801. pVideoDebugPrint((Warn,
  802. "VIDEOPRT: _BCM failed. Status = 0x%x\n", Status));
  803. } else {
  804. pVideoDebugPrint((Trace, "VIDEOPRT: _BCM succeeded.\n"));
  805. }
  806. Fallout:
  807. if (Buffer != NULL) {
  808. ExFreePool(Buffer);
  809. }
  810. return Status;
  811. }
  812. VOID
  813. VpPowerStateCallback(
  814. IN PVOID CallbackContext,
  815. IN PVOID Argument1,
  816. IN PVOID Argument2
  817. )
  818. /*++
  819. Routine Description:
  820. This is the callback routine that is called when the power state changes.
  821. Arguments:
  822. CallbackContext: NULL
  823. Argument1: event code
  824. Argument2: if Argument1 is PO_CB_AC_STATUS, Argument2 contains TRUE if
  825. the current power source is AC and FALSE otherwise.
  826. Note:
  827. This function can be called at DISPATCH_LEVEL
  828. --*/
  829. {
  830. PPOWER_STATE_WORK_ITEM PowerStateWorkItem = NULL;
  831. ASSERT (CallbackContext == NULL);
  832. switch ((ULONG_PTR)Argument1) {
  833. case PO_CB_LID_SWITCH_STATE:
  834. case PO_CB_AC_STATUS:
  835. PowerStateWorkItem =
  836. (PPOWER_STATE_WORK_ITEM)ExAllocatePoolWithTag(
  837. NonPagedPool,
  838. sizeof(POWER_STATE_WORK_ITEM),
  839. VP_TAG);
  840. if (PowerStateWorkItem != NULL) {
  841. PowerStateWorkItem->Argument1 = Argument1;
  842. PowerStateWorkItem->Argument2 = Argument2;
  843. ExInitializeWorkItem(
  844. &PowerStateWorkItem->WorkItem,
  845. VpDelayedPowerStateCallback,
  846. PowerStateWorkItem);
  847. ExQueueWorkItem(
  848. &PowerStateWorkItem->WorkItem,
  849. DelayedWorkQueue);
  850. } else {
  851. pVideoDebugPrint((Warn,
  852. "VIDEOPRT: VpPowerStateCallback: Memory allocation failed."));
  853. }
  854. break;
  855. default:
  856. //
  857. // Ignore all other cases
  858. //
  859. break;
  860. }
  861. }
  862. VOID
  863. VpDelayedPowerStateCallback(
  864. IN PVOID Context
  865. )
  866. /*++
  867. Routine Description:
  868. VpPowerStateCallback queues this work item in order to handle
  869. PowerState changes at PASSIVE_LEVEL.
  870. Arguments:
  871. Context: pointer to POWER_STATE_WORK_ITEM
  872. --*/
  873. {
  874. PPOWER_STATE_WORK_ITEM PowerStateWorkItem =
  875. (PPOWER_STATE_WORK_ITEM)Context;
  876. PDEVICE_OBJECT AttachedDevice = NULL;
  877. NTSTATUS status;
  878. POWER_STATE powerState;
  879. PCHILD_PDO_EXTENSION pdoExtension;
  880. PAGED_CODE();
  881. ASSERT (PowerStateWorkItem != NULL);
  882. KeWaitForSingleObject(&LCDPanelMutex,
  883. Executive,
  884. KernelMode,
  885. FALSE,
  886. (PTIME)NULL);
  887. if (LCDPanelDevice == NULL) {
  888. goto Fallout;
  889. }
  890. switch ((ULONG_PTR)PowerStateWorkItem->Argument1) {
  891. case PO_CB_AC_STATUS:
  892. AttachedDevice = IoGetAttachedDeviceReference(LCDPanelDevice);
  893. VpSetLCDPowerUsage(
  894. AttachedDevice,
  895. (PowerStateWorkItem->Argument2 != 0));
  896. ObDereferenceObject(AttachedDevice);
  897. break;
  898. case PO_CB_LID_SWITCH_STATE:
  899. pdoExtension = LCDPanelDevice->DeviceExtension;
  900. if ((ULONG_PTR)PowerStateWorkItem->Argument2 == 0) {
  901. //
  902. // The lid is closed. Put the LCD Panel into D3 and override
  903. // any future power requests to the panel.
  904. //
  905. pdoExtension->PowerOverride = TRUE;
  906. powerState.DeviceState = PowerDeviceD3;
  907. pVideoDebugPrint((Trace, "VIDEOPRT: LCD Panel Closed.\n"));
  908. } else if ((ULONG_PTR)PowerStateWorkItem->Argument2 == 1) {
  909. pdoExtension->PowerOverride = FALSE;
  910. powerState.DeviceState = PowerDeviceD0;
  911. pVideoDebugPrint((Trace, "VIDEOPRT: LCD Panel Open.\n"));
  912. } else {
  913. pVideoDebugPrint((Error, "VIDEOPRT: Unknown LCD lid close event recieved.\n"));
  914. ASSERT(FALSE);
  915. goto Fallout;
  916. }
  917. if (pdoExtension->pFdoExtension->DevicePowerState == PowerDeviceD0)
  918. {
  919. if (VpLidCloseSetPower == TRUE)
  920. {
  921. status = PoRequestPowerIrp (LCDPanelDevice,
  922. IRP_MN_SET_POWER,
  923. powerState,
  924. pVideoPortPowerIrpComplete,
  925. (PVOID)NULL,
  926. NULL);
  927. if (status != STATUS_PENDING) {
  928. pVideoDebugPrint((Error, "VIDEOPRT: Could not send power IRP to toggle panel\n"));
  929. }
  930. }
  931. }
  932. break;
  933. default:
  934. pVideoDebugPrint((Warn,
  935. "VIDEOPRT: Unexpected PowerState event recieved.\n"));
  936. ASSERT(FALSE);
  937. break;
  938. }
  939. Fallout:
  940. KeReleaseMutex(&LCDPanelMutex, FALSE);
  941. ExFreePool(Context);
  942. }
  943. VOID
  944. VpRegisterPowerStateCallback(
  945. VOID
  946. )
  947. /*++
  948. Routine Description:
  949. This routine registers the PowerState callback routine.
  950. --*/
  951. {
  952. OBJECT_ATTRIBUTES ObjectAttributes;
  953. UNICODE_STRING CallbackName;
  954. PCALLBACK_OBJECT CallbackObject;
  955. NTSTATUS Status;
  956. PAGED_CODE();
  957. RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
  958. InitializeObjectAttributes(
  959. &ObjectAttributes,
  960. &CallbackName,
  961. OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
  962. NULL,
  963. NULL
  964. );
  965. Status = ExCreateCallback(
  966. &CallbackObject,
  967. &ObjectAttributes,
  968. FALSE,
  969. TRUE
  970. );
  971. if (NT_SUCCESS(Status)) {
  972. PowerStateCallbackHandle = ExRegisterCallback(
  973. CallbackObject,
  974. VpPowerStateCallback,
  975. NULL
  976. );
  977. if (PowerStateCallbackHandle == NULL) {
  978. pVideoDebugPrint((Warn,
  979. "VIDEOPRT: Could not register VpPowerStateCallback.\n"));
  980. } else {
  981. pVideoDebugPrint((Trace,
  982. "VIDEOPRT: VpPowerStateCallback registered. \n"));
  983. }
  984. } else {
  985. pVideoDebugPrint((Warn,
  986. "VIDEOPRT: Could not get the PowerState callback object.\n"));
  987. }
  988. }
  989. VOID
  990. VpRegisterLCDCallbacks(
  991. VOID
  992. )
  993. /*++
  994. Routine Description:
  995. This routine registers a callback with the system so that we can
  996. be notified of power state changes.
  997. --*/
  998. {
  999. PAGED_CODE();
  1000. //
  1001. // Register power state callback. This works for the lid as well.
  1002. //
  1003. if (PowerStateCallbackHandle == NULL) {
  1004. VpRegisterPowerStateCallback();
  1005. }
  1006. }
  1007. VOID
  1008. VpUnregisterLCDCallbacks(
  1009. VOID
  1010. )
  1011. /*++
  1012. Routine Description:
  1013. This routine unregisters the callbacks previously registered by
  1014. VpRegisterLCDCallbacks
  1015. Arguments:
  1016. None.
  1017. Note:
  1018. The global PowerStateCallbackHandle acts as an implicit parameter.
  1019. --*/
  1020. {
  1021. PAGED_CODE();
  1022. //
  1023. // Unregister power state callback
  1024. //
  1025. if (PowerStateCallbackHandle != NULL) {
  1026. ExUnregisterCallback(PowerStateCallbackHandle);
  1027. PowerStateCallbackHandle = NULL;
  1028. }
  1029. }