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.

1429 lines
44 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. enum.c
  5. Abstract:
  6. This is the NT Video port driver PnP enumeration support routines.
  7. Author:
  8. Bruce McQuistan (brucemc) Feb. 1997
  9. Environment:
  10. kernel mode only
  11. Notes:
  12. Revision History:
  13. --*/
  14. #include "videoprt.h"
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text(PAGE,pVideoPnPCapabilities)
  17. #pragma alloc_text(PAGE,pVideoPnPResourceRequirements)
  18. #pragma alloc_text(PAGE,pVideoPnPQueryId)
  19. #pragma alloc_text(PAGE,VpAddPdo)
  20. #pragma alloc_text(PAGE,pVideoPortEnumerateChildren)
  21. #pragma alloc_text(PAGE,pVideoPortCleanUpChildList)
  22. #endif
  23. NTSTATUS
  24. pVideoPnPCapabilities(
  25. IN PCHILD_PDO_EXTENSION PdoExtension,
  26. IN PDEVICE_CAPABILITIES Capabilities
  27. )
  28. /*+
  29. * Function: pVideoPnPCapabilities
  30. * Context: Called in the context of an IRP_MN_QUERY_CAPABILITIES minor function
  31. * and IRP_MJ_PNP major function.
  32. *
  33. * Arguments: PDEVICE_EXTENSION DeviceExtension - a pointer to a
  34. * CHILD_DEVICE_EXTENSION.
  35. *
  36. * PDEVICE_CAPABILITIES Capabilities - a pointer to the
  37. * Parameters.DeviceCapabilities.Capabilities of the IrpStack.
  38. *
  39. *
  40. * Comments: This routine fills in some capabilities data needed by the
  41. * PnP device manager.
  42. *
  43. -*/
  44. {
  45. BOOLEAN success ;
  46. DEVICE_POWER_STATE unused ;
  47. UCHAR count ;
  48. //
  49. // Make sure we're dealing with PDOs here.
  50. //
  51. ASSERT(IS_PDO(PdoExtension));
  52. if (!pVideoPortMapStoD(PdoExtension,
  53. PowerSystemSleeping1,
  54. &unused)) {
  55. return STATUS_UNSUCCESSFUL ;
  56. }
  57. for (count = PowerSystemUnspecified; count < PowerSystemMaximum; count++) {
  58. Capabilities->DeviceState[count] = PdoExtension->DeviceMapping[count] ;
  59. }
  60. //
  61. // Check to make sure that the monitor will actually get turned off
  62. // in sleep states.
  63. //
  64. if (Capabilities->DeviceState[PowerSystemSleeping1] == PowerDeviceD0) {
  65. Capabilities->DeviceState[PowerSystemSleeping1] = PowerDeviceD1 ;
  66. PdoExtension->DeviceMapping[PowerSystemSleeping1] =
  67. PowerDeviceD1 ;
  68. pVideoDebugPrint((0, "VideoPrt: QC - Override D0 for sleep on monitor.\n")) ;
  69. }
  70. PdoExtension->IsMappingReady = TRUE ;
  71. //
  72. // Begin with basic capabilities for the PDO
  73. //
  74. Capabilities->LockSupported = FALSE;
  75. Capabilities->EjectSupported = FALSE;
  76. Capabilities->Removable = FALSE;
  77. Capabilities->DockDevice = FALSE;
  78. //
  79. // Set the Raw bit to TRUE only for monitor like objects, since we
  80. // act as drivers for them.
  81. //
  82. Capabilities->RawDeviceOK = FALSE;
  83. if (PdoExtension->VideoChildDescriptor->Type == Monitor) {
  84. Capabilities->RawDeviceOK = TRUE;
  85. Capabilities->EjectSupported = TRUE;
  86. Capabilities->Removable = TRUE;
  87. Capabilities->SurpriseRemovalOK = TRUE;
  88. Capabilities->SilentInstall = TRUE;
  89. }
  90. //
  91. // Out address field contains the ID returned during enumeration.
  92. // This is key for ACPI devices in order for ACPI to install the
  93. // filter properly.
  94. //
  95. Capabilities->Address = PdoExtension->VideoChildDescriptor->UId;
  96. //
  97. // We do not generate unique IDs for our devices because we maight have
  98. // two video cards with monitors attached, for which the driver would
  99. // end up returning the same ID.
  100. //
  101. Capabilities->UniqueID = FALSE;
  102. //
  103. // The following are totally BOGUS.
  104. //
  105. Capabilities->SystemWake = PowerSystemUnspecified;
  106. Capabilities->DeviceWake = PowerDeviceUnspecified;
  107. Capabilities->D1Latency = 10;
  108. Capabilities->D2Latency = 10;
  109. Capabilities->D3Latency = 10;
  110. return STATUS_SUCCESS;
  111. }
  112. NTSTATUS
  113. pVideoPnPResourceRequirements(
  114. IN PCHILD_PDO_EXTENSION PdoExtension,
  115. OUT PCM_RESOURCE_LIST * ResourceList
  116. )
  117. /*+
  118. * Function: pVideoPnPResourceRequirements
  119. * Context: Called in the context of an IRP_MN_QUERY_RESOURCE_REQUIREMENTS
  120. * minor function and IRP_MJ_PNP major function.
  121. * Arguments: PDEVICE_EXTENSION PdoExtension - a pointer to a CHILD_DEVICE_EXTENSION.
  122. * PCM_RESOURCE_LIST * ResourceList - a pointer to the IRPs
  123. * IoStatus.Information
  124. *
  125. * Comments: This routine tells the PnP device manager that the child
  126. * devices (monitors) don't need system resources. This may not
  127. * be the case for all child devices.
  128. *
  129. -*/
  130. {
  131. PVIDEO_CHILD_DESCRIPTOR pChildDescriptor;
  132. //
  133. // Make sure we're dealing with PDOs here.
  134. //
  135. ASSERT(IS_PDO(PdoExtension));
  136. //
  137. // Get the child descriptor allocated during Enumerate phase.
  138. //
  139. pChildDescriptor = PdoExtension->VideoChildDescriptor;
  140. //
  141. // If the descriptor is null, then there are serious problems.
  142. //
  143. ASSERT(pChildDescriptor);
  144. switch (pChildDescriptor->Type) {
  145. default:
  146. //
  147. // Monitors don't need pci resources.
  148. //
  149. case Monitor:
  150. *ResourceList = NULL;
  151. break;
  152. }
  153. return STATUS_SUCCESS;
  154. }
  155. BOOLEAN pGetACPIEdid(PDEVICE_OBJECT DeviceObject, PVOID pEdid)
  156. /*+
  157. * Function: pGetACPIEdid
  158. * Return Value:
  159. * TRUE: Success
  160. * FALSE: Failure
  161. -*/
  162. {
  163. UCHAR EDIDBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + EDID_BUFFER_SIZE];
  164. ULONG EdidVersion = 2;
  165. BOOLEAN bReturn = FALSE;
  166. RtlZeroMemory(EDIDBuffer, sizeof(EDIDBuffer));
  167. if (NT_SUCCESS (pVideoPortACPIIoctl(IoGetAttachedDevice(DeviceObject),
  168. (ULONG) ('CDD_'),
  169. &EdidVersion,
  170. NULL,
  171. sizeof(EDIDBuffer),
  172. (PACPI_EVAL_OUTPUT_BUFFER) EDIDBuffer) )
  173. )
  174. {
  175. ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)EDIDBuffer)->Argument[0].Type == ACPI_METHOD_ARGUMENT_BUFFER);
  176. ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)EDIDBuffer)->Argument[0].DataLength <= EDID_BUFFER_SIZE);
  177. bReturn = TRUE;
  178. }
  179. RtlCopyMemory(pEdid,
  180. ((PACPI_EVAL_OUTPUT_BUFFER)EDIDBuffer)->Argument[0].Data,
  181. EDID_BUFFER_SIZE);
  182. return bReturn;
  183. }
  184. #define TOTAL_NAMES_SIZE 512
  185. NTSTATUS
  186. pVideoPnPQueryId(
  187. IN PDEVICE_OBJECT DeviceObject,
  188. IN BUS_QUERY_ID_TYPE BusQueryIdType,
  189. IN OUT PWSTR * BusQueryId
  190. )
  191. /*+
  192. * Function: pVideoPnPQueryId
  193. * Context: Called in the context of an IRP_MN_QUERY_ID minor function
  194. * and IRP_MJ_PNP major function.
  195. * Arguments: DeviceObject - a PDEVICE_OBJECT created when we enumerated
  196. * the child device.
  197. * BusQueryIdType - a BUS_QUERY_ID_TYPE passed in by the PnP
  198. * device Manager.
  199. * BusQueryId - a PWSTR * written to in some cases by this
  200. * routine.
  201. *
  202. * Comments:
  203. *
  204. -*/
  205. {
  206. PUSHORT nameBuffer;
  207. LPWSTR deviceName;
  208. WCHAR buffer[64];
  209. PCHILD_PDO_EXTENSION pDeviceExtension;
  210. PVIDEO_CHILD_DESCRIPTOR pChildDescriptor;
  211. PVOID pEdid;
  212. NTSTATUS ntStatus = STATUS_SUCCESS;
  213. //
  214. // Allocate enought to hold a MULTI_SZ. This will be passed to the Io
  215. // subsystem (via BusQueryId) who has the responsibility of freeing it.
  216. //
  217. nameBuffer = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  218. TOTAL_NAMES_SIZE,
  219. VP_TAG);
  220. if (!nameBuffer)
  221. {
  222. pVideoDebugPrint((0, "\t Can't allocate nameBuffer\n"));
  223. return STATUS_INSUFFICIENT_RESOURCES;
  224. }
  225. RtlZeroMemory(nameBuffer, TOTAL_NAMES_SIZE);
  226. //
  227. // Get the child descriptor allocated during Enumerate phase.
  228. //
  229. pDeviceExtension = (PCHILD_PDO_EXTENSION) DeviceObject->DeviceExtension;
  230. pChildDescriptor = pDeviceExtension->VideoChildDescriptor;
  231. //
  232. // If the descriptor is null, then there are serious problems.
  233. //
  234. ASSERT(pChildDescriptor);
  235. //
  236. // Setup pEdid.
  237. //
  238. pEdid = &(pChildDescriptor->Buffer);
  239. //
  240. // Switch on the type to set up the strings appropriately. This switch
  241. // generates the UNICODE_STRING deviceName, used by HardwareID and
  242. // DeviceID bus queries.
  243. //
  244. switch(pChildDescriptor->Type) {
  245. case Monitor:
  246. /////////////////////////////////////////////////////////
  247. // Get the EDID if this is an ACPI device.
  248. /////////////////////////////////////////////////////////
  249. pChildDescriptor->ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
  250. if (pChildDescriptor->bACPIDevice == TRUE)
  251. {
  252. if (pChildDescriptor->ACPIDDCFlag & ACPIDDC_TESTED)
  253. {
  254. if (pChildDescriptor->ValidEDID != GOOD_EDID &&
  255. (pChildDescriptor->ACPIDDCFlag & ACPIDDC_EXIST) )
  256. {
  257. pGetACPIEdid(DeviceObject, pEdid);
  258. pChildDescriptor->ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
  259. }
  260. }
  261. else
  262. {
  263. //
  264. // If we found Miniport gets a right EDID, it's equivalent to that _DDC method doesn't exist
  265. //
  266. pChildDescriptor->ACPIDDCFlag = ACPIDDC_TESTED;
  267. if (pChildDescriptor->ValidEDID != GOOD_EDID &&
  268. pGetACPIEdid(DeviceObject, pEdid))
  269. {
  270. pChildDescriptor->ACPIDDCFlag |= ACPIDDC_EXIST;
  271. pChildDescriptor->ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
  272. }
  273. }
  274. }
  275. //
  276. // If there's an EDID, decode it's OEM id. Otherwise, use
  277. // default.
  278. //
  279. if (pChildDescriptor->ValidEDID == GOOD_EDID) {
  280. pVideoDebugPrint((1, "\tNot a bogus edid\n"));
  281. pVideoPortGetEDIDId(pEdid, buffer);
  282. deviceName = buffer;
  283. } else {
  284. //
  285. // Use the passed in default name.
  286. //
  287. deviceName = L"Default_Monitor";
  288. }
  289. break;
  290. case Other:
  291. deviceName = (LPWSTR) pEdid;
  292. break;
  293. default:
  294. pVideoDebugPrint((0, "\t Unsupported Type: %x\n", pChildDescriptor->Type));
  295. ASSERT(FALSE);
  296. deviceName = L"Unknown_Video_Device";
  297. break;
  298. }
  299. pVideoDebugPrint((2, "\t The basic deviceName is %ws\n", deviceName));
  300. //
  301. // Craft a name dependent on what was passed in.
  302. //
  303. switch (BusQueryIdType) {
  304. case BusQueryCompatibleIDs:
  305. //
  306. // Compatible ID used for INF matching.
  307. //
  308. pVideoDebugPrint((2, "\t BusQueryCompatibleIDs\n"));
  309. if (pChildDescriptor->Type != Monitor) {
  310. swprintf(nameBuffer, L"DISPLAY\\%ws", deviceName);
  311. pVideoDebugPrint((2, "\t BusQueryCompatibleIDs = %ws", nameBuffer));
  312. } else {
  313. //
  314. // Put the default PNP id for monitors.
  315. //
  316. swprintf(nameBuffer, L"*PNP09FF");
  317. pVideoDebugPrint((2, "\t BusQueryCompatibleIDs = %ws", nameBuffer));
  318. }
  319. break;
  320. case BusQueryHardwareIDs:
  321. pVideoDebugPrint((2, "\t BusQueryHardwareIDs\n"));
  322. //
  323. // By this time, the keys should have been created, so write
  324. // the data to the registry. In this case the data is a string
  325. // that looks like '\Monitor\<string>' where string is either
  326. // 'Default_Monitor' or a name extracted from the edid.
  327. //
  328. if (pChildDescriptor->Type == Monitor) {
  329. //
  330. // Write the DDC information in the DEVICE part of the
  331. // registry (the part under ENUM\DISPLAY\*)
  332. //
  333. HANDLE hDeviceKey;
  334. NTSTATUS Status;
  335. Status = IoOpenDeviceRegistryKey(DeviceObject,
  336. PLUGPLAY_REGKEY_DEVICE,
  337. MAXIMUM_ALLOWED,
  338. &hDeviceKey);
  339. if (NT_SUCCESS(Status)) {
  340. RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
  341. hDeviceKey,
  342. (pChildDescriptor->ValidEDID == GOOD_EDID) ?
  343. L"EDID" : L"BAD_EDID",
  344. REG_BINARY,
  345. pEdid,
  346. EDID_BUFFER_SIZE);
  347. ZwClose(hDeviceKey);
  348. }
  349. swprintf(nameBuffer, L"Monitor\\%ws", deviceName);
  350. } else {
  351. swprintf(nameBuffer, L"DISPLAY\\%ws", deviceName);
  352. }
  353. pVideoDebugPrint((2, "\t BusQueryHardwareIDs = %ws\n", nameBuffer));
  354. break;
  355. case BusQueryDeviceID:
  356. //
  357. // Device ID (top part of the ID)
  358. //
  359. pVideoDebugPrint((2, "\t BusQueryDeviceID\n"));
  360. swprintf(nameBuffer, L"DISPLAY\\%ws", deviceName);
  361. pVideoDebugPrint((2, "\t BusQueryDeviceID = %ws", nameBuffer));
  362. break;
  363. case BusQueryInstanceID:
  364. //
  365. // Instance ID (low part of the ID)
  366. //
  367. pVideoDebugPrint((2, "\t BusQueryInstanceID\n"));
  368. swprintf(nameBuffer, L"%08x&%02x&%02x", pChildDescriptor->UId,
  369. pDeviceExtension->pFdoExtension->SystemIoBusNumber,
  370. pDeviceExtension->pFdoExtension->SlotNumber);
  371. pVideoDebugPrint((2, "\t BusQueryInstanceID = %ws", nameBuffer));
  372. break;
  373. default:
  374. pVideoDebugPrint((0, "\t Bad QueryIdType:%x\n", BusQueryIdType));
  375. return STATUS_NOT_SUPPORTED;
  376. break;
  377. }
  378. pVideoDebugPrint((2, "\t returning %ws\n", nameBuffer));
  379. *BusQueryId = nameBuffer;
  380. return ntStatus;
  381. }
  382. NTSTATUS
  383. VpAddPdo(
  384. PDEVICE_OBJECT DeviceObject,
  385. PVIDEO_CHILD_DESCRIPTOR VideoChildDescriptor
  386. )
  387. /*+
  388. * Function: VpAddPdo
  389. * Context: Called after enumerating a device identified by the miniports
  390. * HwGetVideoChildDescriptor.
  391. *
  392. * Arguments: DeviceObject - a PDEVICE_OBJECT created when we
  393. * enumerated the device.
  394. * VideoChildDescriptor - a PVIDEO_CHILD_DESCRIPTOR allocated
  395. * when we enumerated the device.
  396. * Comments: This routine actually makes the call that creates the child
  397. * device object during enumeration.
  398. *
  399. *
  400. -*/
  401. {
  402. PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  403. PCHILD_PDO_EXTENSION pChildDeviceExtension = fdoExtension->ChildPdoList;
  404. PDEVICE_OBJECT pChildPdo;
  405. USHORT nameBuffer[STRING_LENGTH];
  406. NTSTATUS ntStatus;
  407. UNICODE_STRING deviceName;
  408. POWER_STATE state;
  409. PVOID pEdid = VideoChildDescriptor->Buffer;
  410. //
  411. // Scan the old list to see if this is a duplicate. If a duplicate, mark it as
  412. // VIDEO_ENUMERATED and return STATUS_SUCCESS, because we will want to count it.
  413. // If no duplicates, pChildDeviceExtension will be NULL after exiting this loop
  414. // and a DEVICE_OBJECT will be created for this device instance. Mark the new
  415. // associated CHILD_DEVICE_EXTENSION as VIDEO_ENUMERATED.
  416. //
  417. while (pChildDeviceExtension) {
  418. PVIDEO_CHILD_DESCRIPTOR ChildDescriptor;
  419. BOOLEAN bEqualEDID = FALSE;
  420. ChildDescriptor = pChildDeviceExtension->VideoChildDescriptor;
  421. if (ChildDescriptor->UId == VideoChildDescriptor->UId)
  422. {
  423. if (ChildDescriptor->bACPIDevice == TRUE)
  424. {
  425. VideoChildDescriptor->ACPIDDCFlag = ChildDescriptor->ACPIDDCFlag;
  426. //
  427. // If it's non-monitor device, just ignore
  428. //
  429. if (VideoChildDescriptor->Type != Monitor)
  430. {
  431. bEqualEDID = TRUE;
  432. }
  433. //
  434. // Check the device is active, since inactive CRT may return false EDID
  435. //
  436. else if (pCheckActiveMonitor(pChildDeviceExtension) == FALSE)
  437. {
  438. bEqualEDID = TRUE;
  439. }
  440. else
  441. {
  442. VideoChildDescriptor->ValidEDID =
  443. pVideoPortIsValidEDID(VideoChildDescriptor->Buffer) ? GOOD_EDID : BAD_EDID;
  444. //
  445. // For ACPI system, try to retrieve EDID again.
  446. // At this moment, the handle of DeviceObject is still valid
  447. //
  448. if (VideoChildDescriptor->ValidEDID != GOOD_EDID &&
  449. (ChildDescriptor->ACPIDDCFlag & ACPIDDC_EXIST))
  450. {
  451. if (!pGetACPIEdid(pChildDeviceExtension->ChildDeviceObject,
  452. VideoChildDescriptor->Buffer))
  453. {
  454. bEqualEDID = TRUE;
  455. }
  456. }
  457. if (!bEqualEDID)
  458. {
  459. VideoChildDescriptor->ValidEDID =
  460. pVideoPortIsValidEDID(VideoChildDescriptor->Buffer) ? GOOD_EDID : BAD_EDID;
  461. if (VideoChildDescriptor->ValidEDID == ChildDescriptor->ValidEDID)
  462. {
  463. if (VideoChildDescriptor->ValidEDID != GOOD_EDID ||
  464. memcmp(ChildDescriptor->Buffer,
  465. VideoChildDescriptor->Buffer,
  466. EDID_BUFFER_SIZE) == 0)
  467. {
  468. bEqualEDID = TRUE;
  469. }
  470. }
  471. }
  472. }
  473. }
  474. //
  475. // For non-ACPI system, EDID has already contained VideoChildDescriptor
  476. //
  477. else
  478. {
  479. if (VideoChildDescriptor->Type != Monitor ||
  480. ChildDescriptor->ValidEDID != GOOD_EDID ||
  481. memcmp(ChildDescriptor->Buffer,
  482. VideoChildDescriptor->Buffer,
  483. EDID_BUFFER_SIZE) == 0)
  484. {
  485. bEqualEDID = TRUE;
  486. }
  487. }
  488. }
  489. if (bEqualEDID)
  490. {
  491. pChildDeviceExtension->bIsEnumerated = TRUE;
  492. pVideoDebugPrint((1,
  493. "VpAddPdo: duplicate device:%x\n",
  494. VideoChildDescriptor->UId));
  495. //
  496. // Replace the old child descriptor with the new one. This will
  497. // allow us to detect when and EDID changes, etc.
  498. //
  499. if (pChildDeviceExtension->VideoChildDescriptor->ValidEDID != NO_EDID)
  500. {
  501. RtlCopyMemory(VideoChildDescriptor,
  502. pChildDeviceExtension->VideoChildDescriptor,
  503. sizeof(VIDEO_CHILD_DESCRIPTOR) );
  504. }
  505. ExFreePool(pChildDeviceExtension->VideoChildDescriptor);
  506. pChildDeviceExtension->VideoChildDescriptor = VideoChildDescriptor;
  507. //
  508. // Return STATUS_SUCCESS, because we want to count this as a member of the
  509. // list (it's valid and in there already).
  510. //
  511. return STATUS_SUCCESS;
  512. }
  513. pChildDeviceExtension = pChildDeviceExtension->NextChild;
  514. }
  515. ntStatus = pVideoPortCreateDeviceName(L"\\Device\\VideoPdo",
  516. VideoChildDevices++,
  517. &deviceName,
  518. nameBuffer);
  519. if (NT_SUCCESS(ntStatus)) {
  520. //
  521. // Create the PDO for the child device.
  522. // Notice that we allocate the device extension as the size of
  523. // the FDO extension + the size of the miniports driver extension
  524. // for this device
  525. //
  526. ntStatus = IoCreateDevice(DeviceObject->DriverObject,
  527. sizeof(CHILD_PDO_EXTENSION),
  528. &deviceName,
  529. FILE_DEVICE_UNKNOWN,
  530. 0,
  531. FALSE, //TRUE,
  532. &pChildPdo);
  533. //
  534. // If the DeviceObject wasn't created, we won't get called to process
  535. // the QueryId IRP where VideoChildDescriptor gets freed, so do it
  536. // here.
  537. //
  538. if (!NT_SUCCESS(ntStatus)) {
  539. pVideoDebugPrint((0, "\t IoCreateDevice() failed with status %x\n", ntStatus));
  540. pVideoDebugPrint((0, "\t IoCreateDevice() doesn't like path %ws\n", deviceName.Buffer));
  541. ASSERT(0);
  542. return ntStatus;
  543. }
  544. //
  545. // Mark this object as supporting buffered I/O so that the I/O system
  546. // will only supply simple buffers in IRPs.
  547. // Set and clear the two power fields to ensure we only get called
  548. // as passive level to do power management operations.
  549. //
  550. pChildPdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
  551. pChildPdo->Flags &= ~(DO_DEVICE_INITIALIZING | DO_POWER_INRUSH);
  552. pChildPdo->DeviceType = FILE_DEVICE_SCREEN;
  553. //
  554. // Initialize fields in the ChildDeviceExtension.
  555. //
  556. pChildDeviceExtension = pChildPdo->DeviceExtension;
  557. pChildDeviceExtension->VideoChildDescriptor = VideoChildDescriptor;
  558. pChildDeviceExtension->ChildDeviceObject = pChildPdo;
  559. pChildDeviceExtension->pFdoExtension = fdoExtension;
  560. pChildDeviceExtension->Signature = VP_TAG;
  561. pChildDeviceExtension->ExtensionType = TypePdoExtension;
  562. pChildDeviceExtension->ChildUId = VideoChildDescriptor->UId;
  563. pChildDeviceExtension->bIsEnumerated = TRUE;
  564. pChildDeviceExtension->HwDeviceExtension = fdoExtension->HwDeviceExtension;
  565. pChildDeviceExtension->PowerOverride = FALSE;
  566. KeInitializeMutex(&pChildDeviceExtension->SyncMutex, 0);
  567. //
  568. // Initialize the remove lock.
  569. //
  570. IoInitializeRemoveLock(&pChildDeviceExtension->RemoveLock, VP_TAG, 0, 256);
  571. //
  572. // Initialize Power stuff.
  573. // Set the devices current power state.
  574. // NOTE - we assume the device is on at this point in time ...
  575. //
  576. pChildDeviceExtension->DevicePowerState = PowerDeviceD0;
  577. state.DeviceState = pChildDeviceExtension->DevicePowerState;
  578. state = PoSetPowerState(pChildPdo,
  579. DevicePowerState,
  580. state);
  581. //
  582. // Insert into list
  583. //
  584. pChildDeviceExtension->NextChild = fdoExtension->ChildPdoList;
  585. fdoExtension->ChildPdoList = pChildDeviceExtension;
  586. }
  587. return ntStatus;
  588. }
  589. NTSTATUS
  590. pVideoPortEnumerateChildren(
  591. PDEVICE_OBJECT DeviceObject,
  592. PIRP Irp
  593. )
  594. /*+
  595. * Function: pVideoPortEnumerateChildren
  596. * Context: Called in the context of an IRP_MN_QUERY_DEVICE_RELATIONS
  597. * minor function and IRP_MJ_PNP major function.
  598. * Arguments:
  599. * PDEVICE_OBJECT deviceObject - Passed in by caller of VideoPortDispatch().
  600. * PIRP pIrp - Passed in by caller of VideoPortDispatch().
  601. *
  602. * Comments: This routine enumerates devices attached to the video card. If
  603. * it's called before the driver is initialized, it returns
  604. * STATUS_INSUFFICIENT_RESOURCES. Otherwise, it attempts to read
  605. * the edid from the device and refer to that via the
  606. * DEVICE_EXTENSION and create a DEVICE_OBJECT (PDO) for each
  607. * detected device. This sets up the IO subsystem for issuing
  608. * further PnP IRPs such as IRP_MN_QUERY_DEVICE_ID
  609. *
  610. *
  611. -*/
  612. {
  613. UCHAR outputBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 128];
  614. PCHILD_PDO_EXTENSION pChildDeviceExtension;
  615. PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  616. ULONG moreChild;
  617. ULONG moreDevices = 1;
  618. ULONG Unused = 0;
  619. ULONG count = 0;
  620. PACPI_METHOD_ARGUMENT pAcpiArguments = NULL;
  621. VIDEO_CHILD_ENUM_INFO childEnumInfo;
  622. ULONG relationsSize;
  623. PDEVICE_RELATIONS deviceRelations = NULL;
  624. ULONG ulChildCount = 0;
  625. ULONG debugCount = 0;
  626. PDEVICE_OBJECT *pdo;
  627. NTSTATUS ntStatus;
  628. //
  629. // Make sure we are called with an FDO
  630. //
  631. ASSERT(IS_FDO(fdoExtension));
  632. if ((fdoExtension->AllowEarlyEnumeration == FALSE) &&
  633. (fdoExtension->HwInitStatus != HwInitSucceeded))
  634. {
  635. return STATUS_INSUFFICIENT_RESOURCES;
  636. }
  637. //
  638. // Mark all of the child devices as not being enumerated
  639. //
  640. for (pChildDeviceExtension = fdoExtension->ChildPdoList;
  641. pChildDeviceExtension != NULL;
  642. pChildDeviceExtension = pChildDeviceExtension->NextChild)
  643. {
  644. pChildDeviceExtension->bIsEnumerated = FALSE;
  645. }
  646. //
  647. // Let's call ACPI to determine if we have the IDs of the devices that
  648. // need to be enumerated.
  649. //
  650. ntStatus = pVideoPortACPIIoctl(fdoExtension->AttachedDeviceObject,
  651. (ULONG) ('DOD_'),
  652. NULL,
  653. NULL,
  654. sizeof(outputBuffer),
  655. (PACPI_EVAL_OUTPUT_BUFFER) outputBuffer);
  656. if (NT_SUCCESS(ntStatus))
  657. {
  658. count = ((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Count;
  659. pAcpiArguments = &(((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0]);
  660. }
  661. childEnumInfo.Size = sizeof(VIDEO_CHILD_ENUM_INFO);
  662. childEnumInfo.ChildDescriptorSize = EDID_BUFFER_SIZE;
  663. childEnumInfo.ChildIndex = 0;
  664. childEnumInfo.ACPIHwId = 0;
  665. childEnumInfo.ChildHwDeviceExtension = NULL;
  666. //
  667. // Call the miniport to enumerate the children
  668. // Keep calling for each ACPI device, and then call the driver if it
  669. // has any more devices.
  670. //
  671. while (moreDevices)
  672. {
  673. PVIDEO_CHILD_DESCRIPTOR pVideoChildDescriptor;
  674. //
  675. // Allocate Space for the Child Descriptor
  676. //
  677. pVideoChildDescriptor = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  678. sizeof(VIDEO_CHILD_DESCRIPTOR),
  679. VP_TAG);
  680. if (!pVideoChildDescriptor)
  681. {
  682. break;
  683. }
  684. RtlZeroMemory(pVideoChildDescriptor, sizeof(VIDEO_CHILD_DESCRIPTOR));
  685. //
  686. // On ACPI machine, the HwId contains the ID returned by ACPI
  687. // Otherwise, the value is initialized to NULL and the miniport driver
  688. // must fill it out
  689. //
  690. if (count)
  691. {
  692. ASSERT(pAcpiArguments->Type == 0);
  693. ASSERT(pAcpiArguments->DataLength == 4);
  694. // The lower 16bit are HWID
  695. childEnumInfo.ACPIHwId = pAcpiArguments->Argument & 0x0000FFFF;
  696. pVideoChildDescriptor->bACPIDevice = TRUE;
  697. pAcpiArguments++;
  698. count--;
  699. }
  700. else
  701. {
  702. //
  703. // Increment the child index for non-ACPI devices
  704. //
  705. childEnumInfo.ChildIndex++;
  706. childEnumInfo.ACPIHwId = 0;
  707. }
  708. //
  709. // For ACPI CRTs, Miniport should return EDID directly.
  710. // So for CRT, the buffer is garanteed to be overwriten.
  711. // We use this attibute to distinguish the CRT from LCD and TV.
  712. //
  713. if (pVideoChildDescriptor->bACPIDevice)
  714. {
  715. *((PULONG)pVideoChildDescriptor->Buffer) = NONEDID_SIGNATURE;
  716. }
  717. moreChild = fdoExtension->HwGetVideoChildDescriptor(
  718. fdoExtension->HwDeviceExtension,
  719. &childEnumInfo,
  720. &(pVideoChildDescriptor->Type),
  721. (PUCHAR)(pVideoChildDescriptor->Buffer),
  722. &(pVideoChildDescriptor->UId),
  723. &Unused);
  724. if (moreChild == ERROR_MORE_DATA || moreChild == VIDEO_ENUM_MORE_DEVICES)
  725. {
  726. //
  727. // Perform the required functions on the returned type.
  728. //
  729. ntStatus = VpAddPdo(DeviceObject,
  730. pVideoChildDescriptor);
  731. if (NT_SUCCESS(ntStatus))
  732. {
  733. ++ulChildCount;
  734. }
  735. else
  736. {
  737. moreChild = VIDEO_ENUM_INVALID_DEVICE;
  738. }
  739. }
  740. //
  741. // Stop enumerating the driver returns an error
  742. // For ACPI devices, if miniports returns ERROR_MORE_DATA, stop enumeration.
  743. // If it returns VIDEO_ENUM_MORE_DEVICE, continue on to Non-ACPI device.
  744. // It is the responsibility of Miniport not to enumerate duplicated ACPI and Non-ACPI devices .
  745. //
  746. if (moreChild == ERROR_MORE_DATA &&
  747. (pVideoChildDescriptor->bACPIDevice == TRUE) && (count == 0))
  748. {
  749. moreDevices = 0;
  750. }
  751. if ((moreChild != ERROR_MORE_DATA) &&
  752. (moreChild != VIDEO_ENUM_MORE_DEVICES) &&
  753. (moreChild != VIDEO_ENUM_INVALID_DEVICE)
  754. )
  755. {
  756. moreDevices = 0;
  757. }
  758. //
  759. // Free the memory in case of error.
  760. //
  761. if ((moreChild != ERROR_MORE_DATA) && (moreChild != VIDEO_ENUM_MORE_DEVICES))
  762. {
  763. ExFreePool(pVideoChildDescriptor);
  764. }
  765. }
  766. //
  767. // Now that we know how many devices we have, allocate the blob to be returned and
  768. // fill it.
  769. //
  770. relationsSize = sizeof(DEVICE_RELATIONS) +
  771. (ulChildCount * sizeof(PDEVICE_OBJECT));
  772. deviceRelations = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  773. relationsSize,
  774. VP_TAG);
  775. if (deviceRelations == NULL) {
  776. return STATUS_INSUFFICIENT_RESOURCES;
  777. }
  778. RtlZeroMemory(deviceRelations, relationsSize);
  779. //
  780. // Walk our chain of children, and store them in the relations array.
  781. //
  782. pChildDeviceExtension = fdoExtension->ChildPdoList;
  783. pdo = &(deviceRelations->Objects[0]);
  784. while (pChildDeviceExtension) {
  785. if (pChildDeviceExtension->bIsEnumerated) {
  786. //
  787. // Refcount the ChildDeviceObject.
  788. //
  789. ObReferenceObject(pChildDeviceExtension->ChildDeviceObject);
  790. *pdo++ = pChildDeviceExtension->ChildDeviceObject;
  791. ++debugCount;
  792. }
  793. pChildDeviceExtension = pChildDeviceExtension->NextChild;
  794. }
  795. if (debugCount != ulChildCount) {
  796. pVideoDebugPrint((0, "List management ERROR line %d\n", __LINE__));
  797. ASSERT(FALSE);
  798. }
  799. fdoExtension->ChildPdoNumber = ulChildCount;
  800. deviceRelations->Count = ulChildCount;
  801. //
  802. // Stuff that pDeviceRelations into the IRP and return SUCCESS.
  803. //
  804. Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
  805. return STATUS_SUCCESS;
  806. }
  807. NTSTATUS
  808. pVideoPortCleanUpChildList(
  809. PFDO_EXTENSION FdoExtension,
  810. PDEVICE_OBJECT DeviceObject
  811. )
  812. /*+
  813. * Function: pVideoPortCleanUpChildList
  814. * Context: Called in the context of an IRP_MN_REMOVE_DEVICE
  815. * minor function and IRP_MJ_PNP major function.
  816. * Arguments:
  817. * PFDO_EXTENSION FdoExtension - Device extension of the parent
  818. * PDEVICE_OBJECT deviceObject - Device object to be deleted
  819. *
  820. * Comments: This routine deletes a monitor device object when it is no
  821. * longer needed
  822. * It actually determines if the device is still present by
  823. * checking the enumerate flag in the device extension
  824. * We only do lazy deletion, that is delete the device objects
  825. * after reenumeration has shown the device not to be there
  826. *
  827. -*/
  828. {
  829. PCHILD_PDO_EXTENSION PrevChild = NULL;
  830. PCHILD_PDO_EXTENSION pChildDeviceExtension = FdoExtension->ChildPdoList;
  831. ASSERT(pChildDeviceExtension != NULL);
  832. //
  833. // Search the ChildPdoList for the device we are
  834. // removing.
  835. //
  836. while (pChildDeviceExtension)
  837. {
  838. if (pChildDeviceExtension->ChildDeviceObject == DeviceObject) {
  839. break;
  840. }
  841. PrevChild = pChildDeviceExtension;
  842. pChildDeviceExtension = pChildDeviceExtension->NextChild;
  843. }
  844. if (pChildDeviceExtension) {
  845. //
  846. // Remove the device from the list.
  847. //
  848. if (PrevChild == NULL) {
  849. FdoExtension->ChildPdoList = pChildDeviceExtension->NextChild;
  850. } else {
  851. PrevChild->NextChild = pChildDeviceExtension->NextChild;
  852. }
  853. //
  854. // Free the memory associated with this child device and then delete it.
  855. //
  856. ExFreePool(pChildDeviceExtension->VideoChildDescriptor);
  857. IoDeleteDevice(DeviceObject);
  858. } else {
  859. //
  860. // Why couldn't we find the device in the list?
  861. //
  862. ASSERT(FALSE);
  863. }
  864. return STATUS_SUCCESS;
  865. }
  866. /*+
  867. * Function: pVideoPortConvertAsciiToWchar
  868. * convert that Ascii into a LPWSTR which
  869. * is then placed in Buffer.
  870. *
  871. *
  872. * Arguments: UCHAR Ascii - Pointer to an ascii string.
  873. *
  874. * WCHAR Buffer[64] - Buffer used to convert from ascii to
  875. * WCHAR.
  876. *
  877. * Comments: If DeviceName is returned to some caller outside the videoprt,
  878. * then Buffer had better have the right lifetime.
  879. *
  880. -*/
  881. VOID
  882. pVideoPortConvertAsciiToWChar(
  883. IN PUCHAR Ascii,
  884. OUT WCHAR Buffer[64]
  885. )
  886. {
  887. ANSI_STRING ansiString;
  888. UNICODE_STRING us;
  889. //
  890. // Create a unicode string holding the ascii Name.
  891. //
  892. RtlInitAnsiString(&ansiString, Ascii);
  893. //
  894. // Attach a buffer to the UNICODE_STRING
  895. //
  896. us.Buffer = Buffer;
  897. us.Length = 0;
  898. us.MaximumLength = 64;
  899. RtlZeroMemory(Buffer, sizeof(Buffer));
  900. RtlAnsiStringToUnicodeString(&us,
  901. &ansiString,
  902. FALSE);
  903. }
  904. NTSTATUS
  905. pVideoPortQueryDeviceText(
  906. IN PDEVICE_OBJECT ChildDeviceObject,
  907. IN DEVICE_TEXT_TYPE TextType,
  908. OUT PWSTR * ReturnValue
  909. )
  910. /*+
  911. * Function:
  912. * Context: Called in the context of an IRP_MN_QUERY_DEVICE_TEXT
  913. * minor function and IRP_MJ_PNP major function.
  914. * Arguments:
  915. * PDEVICE_OBJECT ChildDeviceObject - Passed in by caller
  916. * of pVideoPortPnpDispatch().
  917. *
  918. * DEVICE_TEXT_TYPE TextType - Passed in by caller
  919. * of pVideoPortPnpDispatch().
  920. *
  921. * PWSTR * ReturnValue - Created by caller of
  922. * this routine.
  923. -*/
  924. {
  925. PCHILD_PDO_EXTENSION pdoExtension;
  926. PVIDEO_CHILD_DESCRIPTOR pChildDescriptor;
  927. PAGED_CODE();
  928. //
  929. // Get the child descriptor allocated during Enumerate phase.
  930. //
  931. pdoExtension = (PCHILD_PDO_EXTENSION) ChildDeviceObject->DeviceExtension;
  932. ASSERT(IS_PDO(pdoExtension));
  933. pChildDescriptor = pdoExtension->VideoChildDescriptor;
  934. *ReturnValue = NULL;
  935. switch (TextType) {
  936. case DeviceTextDescription:
  937. if (pChildDescriptor->Type == Monitor)
  938. {
  939. ULONG asciiStringLength = 0;
  940. UCHAR pTmp[64];
  941. PWSTR tmpBuffer = (PWSTR)ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  942. 128,
  943. VP_TAG);
  944. if (!tmpBuffer) {
  945. return STATUS_INSUFFICIENT_RESOURCES;
  946. }
  947. memset(pTmp, '0', 64);
  948. if (pChildDescriptor->ValidEDID == GOOD_EDID) {
  949. asciiStringLength = pVideoPortGetEdidOemID(&(pChildDescriptor->Buffer), pTmp);
  950. ASSERT(asciiStringLength <= 64);
  951. pVideoPortConvertAsciiToWChar(pTmp, tmpBuffer);
  952. if (asciiStringLength) {
  953. pVideoDebugPrint((2, "Ascii name:%s\n", pTmp));
  954. pVideoDebugPrint((2, "WChar name:%ws\n", tmpBuffer));
  955. }
  956. *ReturnValue = tmpBuffer;
  957. } else {
  958. wcscpy(tmpBuffer, L"Monitor");
  959. *ReturnValue = tmpBuffer;
  960. }
  961. return STATUS_SUCCESS;
  962. }
  963. return STATUS_NOT_SUPPORTED;
  964. default:
  965. return STATUS_NOT_SUPPORTED;
  966. }
  967. }
  968. BOOLEAN pCheckDeviceRelations(PFDO_EXTENSION FdoExtension, BOOLEAN bNewMonitor)
  969. /*+
  970. * Function: pCheckDeviceRelations
  971. * Arguments:
  972. * bNewMonitor New monitor has been plugged in
  973. * Return Value:
  974. * TRUE: Monitors had been changed, need to reenumarate
  975. * FALSE: No child device change
  976. -*/
  977. {
  978. BOOLEAN bInvalidateRelation = FALSE;
  979. PCHILD_PDO_EXTENSION pChildDeviceExtension;
  980. UCHAR pEdid[EDID_BUFFER_SIZE + sizeof(ACPI_EVAL_OUTPUT_BUFFER)];
  981. for (pChildDeviceExtension = FdoExtension->ChildPdoList;
  982. pChildDeviceExtension != NULL;
  983. pChildDeviceExtension = pChildDeviceExtension->NextChild
  984. )
  985. {
  986. PVIDEO_CHILD_DESCRIPTOR VideoChildDescriptor = pChildDeviceExtension->VideoChildDescriptor;
  987. BOOLEAN ValidEDID, bEqualEDID = TRUE;
  988. if (VideoChildDescriptor->bACPIDevice == TRUE)
  989. {
  990. //
  991. // If it's non-monitor device, just ignore
  992. //
  993. if (VideoChildDescriptor->Type != Monitor)
  994. {
  995. continue;
  996. }
  997. //
  998. // For each output device, we are going to retrieve EDID when it's active.
  999. //
  1000. if (bNewMonitor)
  1001. {
  1002. VideoChildDescriptor->bInvalidate = TRUE;
  1003. }
  1004. else if (VideoChildDescriptor->bInvalidate == FALSE)
  1005. {
  1006. continue;
  1007. }
  1008. //
  1009. // Check the device is active, since inactive CRT may return false EDID
  1010. // If inactive, delay the EDID retieving until next hotkey switching
  1011. //
  1012. if (pCheckActiveMonitor(pChildDeviceExtension) == FALSE)
  1013. {
  1014. continue;
  1015. }
  1016. VideoChildDescriptor->bInvalidate = FALSE;
  1017. //
  1018. // Get DDC from Miniport first
  1019. //
  1020. {
  1021. VIDEO_CHILD_ENUM_INFO childEnumInfo;
  1022. VIDEO_CHILD_TYPE childType;
  1023. ULONG UId, Unused, moreChild;
  1024. childEnumInfo.Size = sizeof(VIDEO_CHILD_ENUM_INFO);
  1025. childEnumInfo.ChildDescriptorSize = EDID_BUFFER_SIZE;
  1026. childEnumInfo.ChildIndex = 0;
  1027. childEnumInfo.ACPIHwId = VideoChildDescriptor->UId;
  1028. childEnumInfo.ChildHwDeviceExtension = NULL;
  1029. moreChild = FdoExtension->HwGetVideoChildDescriptor(
  1030. FdoExtension->HwDeviceExtension,
  1031. &childEnumInfo,
  1032. &childType,
  1033. (PUCHAR)pEdid,
  1034. &UId,
  1035. &Unused);
  1036. ASSERT (moreChild == ERROR_MORE_DATA || moreChild == VIDEO_ENUM_MORE_DEVICES);
  1037. ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
  1038. }
  1039. //
  1040. // For ACPI system, retrieve EDID again.
  1041. // At this moment, the handle of DeviceObject is still valid
  1042. //
  1043. if (ValidEDID != GOOD_EDID &&
  1044. VideoChildDescriptor->ACPIDDCFlag & ACPIDDC_EXIST)
  1045. {
  1046. if (!pGetACPIEdid(pChildDeviceExtension->ChildDeviceObject, pEdid))
  1047. {
  1048. continue;
  1049. }
  1050. ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
  1051. }
  1052. if (VideoChildDescriptor->ValidEDID != ValidEDID)
  1053. {
  1054. bEqualEDID = FALSE;
  1055. }
  1056. else if (ValidEDID == GOOD_EDID)
  1057. {
  1058. if (memcmp(VideoChildDescriptor->Buffer, pEdid, EDID_BUFFER_SIZE) != 0)
  1059. {
  1060. bEqualEDID = FALSE;
  1061. }
  1062. }
  1063. if (!bEqualEDID)
  1064. {
  1065. bInvalidateRelation = TRUE;
  1066. //
  1067. // Forcing UId to become a bad value will invalidate the device
  1068. //
  1069. VideoChildDescriptor->UId = 0xFFFF8086;
  1070. }
  1071. }
  1072. }
  1073. return bInvalidateRelation;
  1074. }
  1075. BOOLEAN pCheckActiveMonitor(PCHILD_PDO_EXTENSION pChildDeviceExtension)
  1076. {
  1077. ULONG UId, flag;
  1078. UId = pChildDeviceExtension->ChildUId;
  1079. if (NT_SUCCESS
  1080. (pVideoMiniDeviceIoControl(pChildDeviceExtension->ChildDeviceObject,
  1081. IOCTL_VIDEO_GET_CHILD_STATE,
  1082. &UId,
  1083. sizeof(ULONG),
  1084. &flag,
  1085. sizeof(ULONG) ) )
  1086. )
  1087. {
  1088. return ((flag & VIDEO_CHILD_ACTIVE) ?
  1089. TRUE :
  1090. FALSE);
  1091. }
  1092. if (pChildDeviceExtension->VideoChildDescriptor->bACPIDevice == TRUE)
  1093. {
  1094. UCHAR outputBuffer[0x10 + sizeof(ACPI_EVAL_OUTPUT_BUFFER)];
  1095. if (NT_SUCCESS
  1096. (pVideoPortACPIIoctl(IoGetAttachedDevice(pChildDeviceExtension->ChildDeviceObject),
  1097. (ULONG) ('SCD_'),
  1098. NULL,
  1099. NULL,
  1100. sizeof(outputBuffer),
  1101. (PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)
  1102. )
  1103. )
  1104. {
  1105. if ( ((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].Argument & 0x02)
  1106. {
  1107. return TRUE;
  1108. }
  1109. else
  1110. {
  1111. return FALSE;
  1112. }
  1113. }
  1114. else
  1115. {
  1116. return TRUE;
  1117. }
  1118. }
  1119. else
  1120. {
  1121. //
  1122. // For Non-ACPI machines, if miniport doesn't handle IOCTL_VIDEO_GET_CHILD_STATE, we just assume all Monitors are active
  1123. //
  1124. return TRUE;
  1125. }
  1126. }