xref: /reactos/win32ss/drivers/videoprt/videoprt.c (revision 3c5a56ed)
1 /*
2  * VideoPort driver
3  *
4  * Copyright (C) 2002-2004, 2007 ReactOS Team
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21 
22 #include "videoprt.h"
23 
24 #include <stdio.h>
25 #include <ndk/exfuncs.h>
26 #include <ndk/obfuncs.h>
27 #include <ndk/rtlfuncs.h>
28 
29 #define NDEBUG
30 #include <debug.h>
31 
32 /* GLOBAL VARIABLES ***********************************************************/
33 
34 ULONG VideoDebugLevel = 0;
35 
36 BOOLEAN VpBaseVideo = FALSE;
37 BOOLEAN VpNoVesa = FALSE;
38 
39 PKPROCESS CsrProcess = NULL;
40 static ULONG VideoPortMaxObjectNumber = -1;
41 BOOLEAN VideoPortUseNewKey = FALSE;
42 KMUTEX VideoPortInt10Mutex;
43 KSPIN_LOCK HwResetAdaptersLock;
44 RTL_STATIC_LIST_HEAD(HwResetAdaptersList);
45 
46 /* PRIVATE FUNCTIONS **********************************************************/
47 
48 ULONG
49 NTAPI
50 DriverEntry(
51     IN PVOID Context1,
52     IN PVOID Context2)
53 {
54     return STATUS_SUCCESS;
55 }
56 
57 static
58 NTSTATUS
59 IntVideoPortAddDeviceMapLink(
60     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension)
61 {
62     PUNICODE_STRING RegistryPath;
63     WCHAR DeviceBuffer[20];
64     UNICODE_STRING DeviceName;
65     WCHAR SymlinkBuffer[20];
66     UNICODE_STRING SymlinkName;
67     ULONG DeviceNumber;
68     NTSTATUS Status;
69 
70     /* Create a unicode device name. */
71     DeviceNumber = DeviceExtension->DeviceNumber;
72     swprintf(DeviceBuffer, L"\\Device\\Video%lu", DeviceNumber);
73 
74     if (VideoPortUseNewKey)
75         RegistryPath = &DeviceExtension->NewRegistryPath;
76     else
77         RegistryPath = &DeviceExtension->RegistryPath;
78 
79     /* Add entry to DEVICEMAP\VIDEO key in registry. */
80     Status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
81                                    L"VIDEO",
82                                    DeviceBuffer,
83                                    REG_SZ,
84                                    RegistryPath->Buffer,
85                                    RegistryPath->Length + sizeof(UNICODE_NULL));
86     if (!NT_SUCCESS(Status))
87     {
88         ERR_(VIDEOPRT, "Failed to create DEVICEMAP registry entry: 0x%X\n", Status);
89         return Status;
90     }
91 
92     Status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
93                                    L"VIDEO",
94                                    L"MaxObjectNumber",
95                                    REG_DWORD,
96                                    &DeviceNumber,
97                                    sizeof(DeviceNumber));
98     if (!NT_SUCCESS(Status))
99     {
100         ERR_(VIDEOPRT, "Failed to write MaxObjectNumber: 0x%X\n", Status);
101         return Status;
102     }
103 
104     /* Create symbolic link "\??\DISPLAYx" */
105     swprintf(SymlinkBuffer, L"\\??\\DISPLAY%lu", DeviceNumber + 1);
106     RtlInitUnicodeString(&SymlinkName, SymlinkBuffer);
107     RtlInitUnicodeString(&DeviceName, DeviceBuffer);
108     Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
109     if (!NT_SUCCESS(Status))
110     {
111         ERR_(VIDEOPRT, "Failed to create symbolic link: 0x%X\n", Status);
112         return Status;
113     }
114 
115     /* Update MaxObjectNumber */
116     VideoPortMaxObjectNumber = DeviceNumber;
117 
118     return STATUS_SUCCESS;
119 }
120 
121 PVOID
122 NTAPI
123 IntVideoPortImageDirectoryEntryToData(
124     PVOID BaseAddress,
125     ULONG Directory)
126 {
127     PIMAGE_NT_HEADERS NtHeader;
128     ULONG Va;
129 
130     NtHeader = RtlImageNtHeader(BaseAddress);
131     if (NtHeader == NULL)
132         return NULL;
133 
134     if (Directory >= NtHeader->OptionalHeader.NumberOfRvaAndSizes)
135         return NULL;
136 
137     Va = NtHeader->OptionalHeader.DataDirectory[Directory].VirtualAddress;
138     if (Va == 0)
139         return NULL;
140 
141     return (PVOID)((ULONG_PTR)BaseAddress + Va);
142 }
143 
144 VOID
145 NTAPI
146 IntVideoPortDeferredRoutine(
147     IN PKDPC Dpc,
148     IN PVOID DeferredContext,
149     IN PVOID SystemArgument1,
150     IN PVOID SystemArgument2)
151 {
152     PVOID HwDeviceExtension =
153         &((PVIDEO_PORT_DEVICE_EXTENSION)DeferredContext)->MiniPortDeviceExtension;
154     ((PMINIPORT_DPC_ROUTINE)SystemArgument1)(HwDeviceExtension, SystemArgument2);
155 }
156 
157 NTSTATUS
158 NTAPI
159 IntVideoPortCreateAdapterDeviceObject(
160    _In_ PDRIVER_OBJECT DriverObject,
161    _In_ PVIDEO_PORT_DRIVER_EXTENSION DriverExtension,
162    _In_opt_ PDEVICE_OBJECT PhysicalDeviceObject,
163    _In_ USHORT AdapterNumber,
164    _In_ USHORT DisplayNumber,
165    _Out_opt_ PDEVICE_OBJECT *DeviceObject)
166 {
167     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
168     ULONG DeviceNumber;
169     ULONG PciSlotNumber;
170     PCI_SLOT_NUMBER SlotNumber;
171     ULONG Size;
172     NTSTATUS Status;
173     WCHAR DeviceBuffer[20];
174     UNICODE_STRING DeviceName;
175     PDEVICE_OBJECT DeviceObject_;
176 
177     if (DeviceObject == NULL)
178         DeviceObject = &DeviceObject_;
179 
180     /*
181      * Find the first free device number that can be used for video device
182      * object names and symlinks.
183      */
184     DeviceNumber = VideoPortMaxObjectNumber + 1;
185     if (DeviceNumber == (ULONG)-1)
186     {
187         WARN_(VIDEOPRT, "Can't find free device number\n");
188         return STATUS_UNSUCCESSFUL;
189     }
190 
191     /*
192      * Create the device object.
193      */
194 
195     /* Create a unicode device name. */
196     swprintf(DeviceBuffer, L"\\Device\\Video%lu", DeviceNumber);
197     RtlInitUnicodeString(&DeviceName, DeviceBuffer);
198 
199     INFO_(VIDEOPRT, "HwDeviceExtension size is: 0x%x\n",
200           DriverExtension->InitializationData.HwDeviceExtensionSize);
201 
202     /* Create the device object. */
203     Size = sizeof(VIDEO_PORT_DEVICE_EXTENSION) +
204         DriverExtension->InitializationData.HwDeviceExtensionSize;
205     Status = IoCreateDevice(DriverObject,
206                             Size,
207                             &DeviceName,
208                             FILE_DEVICE_VIDEO,
209                             0,
210                             TRUE,
211                             DeviceObject);
212 
213     if (!NT_SUCCESS(Status))
214     {
215         WARN_(VIDEOPRT, "IoCreateDevice call failed with status 0x%08x\n", Status);
216         return Status;
217     }
218 
219     /*
220      * Set the buffering strategy here. If you change this, remember
221      * to change VidDispatchDeviceControl too.
222      */
223 
224     (*DeviceObject)->Flags |= DO_BUFFERED_IO;
225 
226     /* Initialize device extension. */
227     DeviceExtension = (PVIDEO_PORT_DEVICE_EXTENSION)((*DeviceObject)->DeviceExtension);
228     DeviceExtension->Common.Fdo = TRUE;
229     DeviceExtension->DeviceNumber = DeviceNumber;
230     DeviceExtension->DriverObject = DriverObject;
231     DeviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
232     DeviceExtension->FunctionalDeviceObject = *DeviceObject;
233     DeviceExtension->DriverExtension = DriverExtension;
234     DeviceExtension->SessionId = -1;
235     DeviceExtension->AdapterNumber = AdapterNumber;
236     DeviceExtension->DisplayNumber = DisplayNumber;
237 
238     InitializeListHead(&DeviceExtension->ChildDeviceList);
239 
240     /* Get the registry path associated with this device. */
241     Status = IntCreateRegistryPath(&DriverExtension->RegistryPath,
242                                    DeviceExtension->AdapterNumber,
243                                    &DeviceExtension->RegistryPath);
244     if (!NT_SUCCESS(Status))
245     {
246         WARN_(VIDEOPRT, "IntCreateRegistryPath() call failed with status 0x%08x\n", Status);
247         IoDeleteDevice(*DeviceObject);
248         *DeviceObject = NULL;
249         return Status;
250     }
251 
252     if (PhysicalDeviceObject != NULL)
253     {
254         /* Get bus number from the upper level bus driver. */
255         Size = sizeof(ULONG);
256         Status = IoGetDeviceProperty(PhysicalDeviceObject,
257                                      DevicePropertyBusNumber,
258                                      Size,
259                                      &DeviceExtension->SystemIoBusNumber,
260                                      &Size);
261         if (!NT_SUCCESS(Status))
262         {
263             WARN_(VIDEOPRT, "Couldn't get an information from bus driver. We will try to\n"
264                   "use legacy detection method, but even that doesn't mean that\n"
265                   "it will work.\n");
266             DeviceExtension->PhysicalDeviceObject = NULL;
267         }
268     }
269 
270     DeviceExtension->AdapterInterfaceType =
271         DriverExtension->InitializationData.AdapterInterfaceType;
272 
273     if (PhysicalDeviceObject != NULL)
274     {
275         /* Get bus type from the upper level bus driver. */
276         Size = sizeof(ULONG);
277         IoGetDeviceProperty(PhysicalDeviceObject,
278                             DevicePropertyLegacyBusType,
279                             Size,
280                             &DeviceExtension->AdapterInterfaceType,
281                             &Size);
282 
283         /* Get bus device address from the upper level bus driver. */
284         Size = sizeof(ULONG);
285         IoGetDeviceProperty(PhysicalDeviceObject,
286                             DevicePropertyAddress,
287                             Size,
288                             &PciSlotNumber,
289                             &Size);
290 
291         /* Convert slotnumber to PCI_SLOT_NUMBER */
292         SlotNumber.u.AsULONG = 0;
293         SlotNumber.u.bits.DeviceNumber = (PciSlotNumber >> 16) & 0xFFFF;
294         SlotNumber.u.bits.FunctionNumber = PciSlotNumber & 0xFFFF;
295         DeviceExtension->SystemIoSlotNumber = SlotNumber.u.AsULONG;
296     }
297 
298     InitializeListHead(&DeviceExtension->AddressMappingListHead);
299     InitializeListHead(&DeviceExtension->DmaAdapterList);
300 
301     KeInitializeDpc(&DeviceExtension->DpcObject,
302                     IntVideoPortDeferredRoutine,
303                     DeviceExtension);
304 
305     KeInitializeMutex(&DeviceExtension->DeviceLock, 0);
306 
307     /* Attach the device. */
308     if ((PhysicalDeviceObject != NULL) && (DisplayNumber == 0))
309         DeviceExtension->NextDeviceObject = IoAttachDeviceToDeviceStack(
310                                                 *DeviceObject,
311                                                 PhysicalDeviceObject);
312 
313     Status = IntCreateNewRegistryPath(DeviceExtension);
314     if (!NT_SUCCESS(Status))
315     {
316         ERR_(VIDEOPRT, "IntCreateNewRegistryPath() failed with status 0x%08x\n", Status);
317         IoDeleteDevice(*DeviceObject);
318         *DeviceObject = NULL;
319         return Status;
320     }
321 
322     IntSetupDeviceSettingsKey(DeviceExtension);
323 
324     /* Remove the initailizing flag */
325     (*DeviceObject)->Flags &= ~DO_DEVICE_INITIALIZING;
326 
327     /* Set up the VIDEO/DEVICEMAP registry keys */
328     Status = IntVideoPortAddDeviceMapLink(DeviceExtension);
329     if (!NT_SUCCESS(Status))
330     {
331         ERR_(VIDEOPRT, "IntVideoPortAddDeviceMapLink() failed with status 0x%08x\n", Status);
332         IoDeleteDevice(*DeviceObject);
333         *DeviceObject = NULL;
334         return Status;
335     }
336 
337     if (DisplayNumber == 0)
338     {
339         DriverExtension->InitializationData.StartingDeviceNumber++;
340     }
341 
342     return STATUS_SUCCESS;
343 }
344 
345 
346 NTSTATUS
347 NTAPI
348 IntVideoPortFindAdapter(
349     IN PDRIVER_OBJECT DriverObject,
350     IN PVIDEO_PORT_DRIVER_EXTENSION DriverExtension,
351     IN PDEVICE_OBJECT DeviceObject)
352 {
353     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
354     NTSTATUS Status;
355     VP_STATUS vpStatus;
356     VIDEO_PORT_CONFIG_INFO ConfigInfo;
357     SYSTEM_BASIC_INFORMATION SystemBasicInfo;
358     UCHAR Again = FALSE;
359     BOOL LegacyDetection = FALSE;
360 
361     DeviceExtension = (PVIDEO_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
362 
363     /* Setup a ConfigInfo structure that we will pass to HwFindAdapter. */
364     RtlZeroMemory(&ConfigInfo, sizeof(VIDEO_PORT_CONFIG_INFO));
365     ConfigInfo.Length = sizeof(VIDEO_PORT_CONFIG_INFO);
366     ConfigInfo.AdapterInterfaceType = DeviceExtension->AdapterInterfaceType;
367     if (ConfigInfo.AdapterInterfaceType == PCIBus)
368         ConfigInfo.InterruptMode = LevelSensitive;
369     else
370         ConfigInfo.InterruptMode = Latched;
371     ConfigInfo.DriverRegistryPath = DriverExtension->RegistryPath.Buffer;
372     ConfigInfo.VideoPortGetProcAddress = IntVideoPortGetProcAddress;
373     ConfigInfo.SystemIoBusNumber = DeviceExtension->SystemIoBusNumber;
374     ConfigInfo.BusInterruptLevel = DeviceExtension->InterruptLevel;
375     ConfigInfo.BusInterruptVector = DeviceExtension->InterruptVector;
376 
377     Status = ZwQuerySystemInformation(SystemBasicInformation,
378                                       &SystemBasicInfo,
379                                       sizeof(SystemBasicInfo),
380                                       NULL);
381     if (NT_SUCCESS(Status))
382     {
383         ConfigInfo.SystemMemorySize = SystemBasicInfo.NumberOfPhysicalPages *
384                                       SystemBasicInfo.PageSize;
385     }
386 
387     // FIXME: Check the adapter key and update VideoDebugLevel variable.
388 
389     /*
390      * Call miniport HwVidFindAdapter entry point to detect if
391      * particular device is present. There are two possible code
392      * paths. The first one is for Legacy drivers (NT4) and cases
393      * when we don't have information about what bus we're on. The
394      * second case is the standard one for Plug & Play drivers.
395      */
396     if (DeviceExtension->PhysicalDeviceObject == NULL)
397     {
398         LegacyDetection = TRUE;
399     }
400 
401     if (LegacyDetection)
402     {
403         ULONG BusNumber, MaxBuses;
404 
405         MaxBuses = DeviceExtension->AdapterInterfaceType == PCIBus ? PCI_MAX_BRIDGE_NUMBER : 1;
406 
407         for (BusNumber = 0; BusNumber < MaxBuses; BusNumber++)
408         {
409             DeviceExtension->SystemIoBusNumber =
410                 ConfigInfo.SystemIoBusNumber = BusNumber;
411 
412             RtlZeroMemory(&DeviceExtension->MiniPortDeviceExtension,
413                           DriverExtension->InitializationData.HwDeviceExtensionSize);
414 
415             /* FIXME: Need to figure out what string to pass as param 3. */
416             vpStatus = DriverExtension->InitializationData.HwFindAdapter(
417                          &DeviceExtension->MiniPortDeviceExtension,
418                          DriverExtension->HwContext,
419                          NULL,
420                          &ConfigInfo,
421                          &Again);
422 
423             if (vpStatus == ERROR_DEV_NOT_EXIST)
424             {
425                 continue;
426             }
427             else
428             {
429                 break;
430             }
431         }
432     }
433     else
434     {
435         /* FIXME: Need to figure out what string to pass as param 3. */
436         vpStatus = DriverExtension->InitializationData.HwFindAdapter(
437                      &DeviceExtension->MiniPortDeviceExtension,
438                      DriverExtension->HwContext,
439                      NULL,
440                      &ConfigInfo,
441                      &Again);
442     }
443 
444     if (vpStatus != NO_ERROR)
445     {
446         ERR_(VIDEOPRT, "HwFindAdapter call failed with error 0x%X\n", vpStatus);
447         Status = STATUS_UNSUCCESSFUL;
448         goto Failure;
449     }
450 
451     /*
452      * Now we know the device is present, so let's do all additional tasks
453      * such as creating symlinks or setting up interrupts and timer.
454      */
455 
456     /* FIXME: Allocate hardware resources for device. */
457 
458     /* Allocate interrupt for device. */
459     if (!IntVideoPortSetupInterrupt(DeviceObject, DriverExtension, &ConfigInfo))
460     {
461         Status = STATUS_INSUFFICIENT_RESOURCES;
462         goto Failure;
463     }
464 
465     /* Allocate timer for device. */
466     if (!IntVideoPortSetupTimer(DeviceObject, DriverExtension))
467     {
468         if (DeviceExtension->InterruptObject != NULL)
469             IoDisconnectInterrupt(DeviceExtension->InterruptObject);
470         ERR_(VIDEOPRT, "IntVideoPortSetupTimer failed\n");
471         Status = STATUS_INSUFFICIENT_RESOURCES;
472         goto Failure;
473     }
474 
475     /* If the device can be reset, insert it in the list of resettable adapters */
476     InitializeListHead(&DeviceExtension->HwResetListEntry);
477     if (DriverExtension->InitializationData.HwResetHw != NULL)
478     {
479         ExInterlockedInsertTailList(&HwResetAdaptersList,
480                                     &DeviceExtension->HwResetListEntry,
481                                     &HwResetAdaptersLock);
482     }
483 
484     INFO_(VIDEOPRT, "STATUS_SUCCESS\n");
485     return STATUS_SUCCESS;
486 
487 Failure:
488     RtlFreeUnicodeString(&DeviceExtension->RegistryPath);
489     if (DeviceExtension->NextDeviceObject)
490         IoDetachDevice(DeviceExtension->NextDeviceObject);
491     IoDeleteDevice(DeviceObject);
492     return Status;
493 }
494 
495 VOID
496 FASTCALL
497 IntAttachToCSRSS(
498     PKPROCESS *CallingProcess,
499     PKAPC_STATE ApcState)
500 {
501     *CallingProcess = (PKPROCESS)PsGetCurrentProcess();
502     if (*CallingProcess != CsrProcess)
503     {
504         KeStackAttachProcess(CsrProcess, ApcState);
505     }
506 }
507 
508 VOID
509 FASTCALL
510 IntDetachFromCSRSS(
511     PKPROCESS *CallingProcess,
512     PKAPC_STATE ApcState)
513 {
514     if (*CallingProcess != CsrProcess)
515     {
516         KeUnstackDetachProcess(ApcState);
517     }
518 }
519 
520 VOID
521 FASTCALL
522 IntLoadRegistryParameters(VOID)
523 {
524     NTSTATUS Status;
525     HANDLE KeyHandle;
526     UNICODE_STRING UseNewKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\GraphicsDrivers\\UseNewKey");
527     UNICODE_STRING Path = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control");
528     UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"SystemStartOptions");
529     OBJECT_ATTRIBUTES ObjectAttributes;
530     PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
531     ULONG Length, NewLength;
532 
533     /* Check if we need to use new registry */
534     InitializeObjectAttributes(&ObjectAttributes,
535                                &UseNewKeyPath,
536                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
537                                NULL,
538                                NULL);
539     Status = ZwOpenKey(&KeyHandle,
540                        GENERIC_READ | GENERIC_WRITE,
541                        &ObjectAttributes);
542     if (NT_SUCCESS(Status))
543     {
544         VideoPortUseNewKey = TRUE;
545         ZwClose(KeyHandle);
546     }
547 
548     /* Initialize object attributes with the path we want */
549     InitializeObjectAttributes(&ObjectAttributes,
550                                &Path,
551                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
552                                NULL,
553                                NULL);
554 
555     /* Open the key */
556     Status = ZwOpenKey(&KeyHandle,
557                        KEY_QUERY_VALUE,
558                        &ObjectAttributes);
559     if (!NT_SUCCESS(Status))
560     {
561         VideoPortDebugPrint(Error, "ZwOpenKey failed (0x%x)\n", Status);
562         return;
563     }
564 
565     /* Find out how large our buffer should be */
566     Status = ZwQueryValueKey(KeyHandle,
567                              &ValueName,
568                              KeyValuePartialInformation,
569                              NULL,
570                              0,
571                              &Length);
572     if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL)
573     {
574         VideoPortDebugPrint(Error, "ZwQueryValueKey failed (0x%x)\n", Status);
575         ObCloseHandle(KeyHandle, KernelMode);
576         return;
577     }
578 
579     /* Allocate it */
580     KeyInfo = ExAllocatePoolWithTag(PagedPool, Length, TAG_VIDEO_PORT);
581     if (!KeyInfo)
582     {
583         VideoPortDebugPrint(Error, "Out of memory\n");
584         ObCloseHandle(KeyHandle, KernelMode);
585         return;
586     }
587 
588     /* Now for real this time */
589     Status = ZwQueryValueKey(KeyHandle,
590                              &ValueName,
591                              KeyValuePartialInformation,
592                              KeyInfo,
593                              Length,
594                              &NewLength);
595     ObCloseHandle(KeyHandle, KernelMode);
596 
597     if (!NT_SUCCESS(Status))
598     {
599         VideoPortDebugPrint(Error, "ZwQueryValueKey failed (0x%x)\n", Status);
600         ExFreePoolWithTag(KeyInfo, TAG_VIDEO_PORT);
601         return;
602     }
603 
604     /* Sanity check */
605     if (KeyInfo->Type != REG_SZ)
606     {
607         VideoPortDebugPrint(Error, "Invalid type for SystemStartOptions\n");
608         ExFreePoolWithTag(KeyInfo, TAG_VIDEO_PORT);
609         return;
610     }
611 
612     /* Check if BASEVIDEO or NOVESA is present in the start options */
613     if (wcsstr((PWCHAR)KeyInfo->Data, L"BASEVIDEO"))
614         VpBaseVideo = TRUE;
615     if (wcsstr((PWCHAR)KeyInfo->Data, L"NOVESA"))
616         VpNoVesa = TRUE;
617 
618     ExFreePoolWithTag(KeyInfo, TAG_VIDEO_PORT);
619 
620     /* FIXME: Old ReactOS-compatibility... */
621     if (VpBaseVideo) VpNoVesa = TRUE;
622 
623     if (VpNoVesa)
624         VideoPortDebugPrint(Info, "VESA mode disabled\n");
625     else
626         VideoPortDebugPrint(Info, "VESA mode enabled\n");
627 
628     /* If we are in BASEVIDEO, create the volatile registry key for Win32k */
629     if (VpBaseVideo)
630     {
631         RtlInitUnicodeString(&Path, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\GraphicsDrivers\\BaseVideo");
632 
633         InitializeObjectAttributes(&ObjectAttributes,
634                                    &Path,
635                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
636                                    NULL,
637                                    NULL);
638 
639         Status = ZwCreateKey(&KeyHandle,
640                              READ_CONTROL, // Non-0 placeholder: no use for this handle.
641                              &ObjectAttributes,
642                              0,
643                              NULL,
644                              REG_OPTION_VOLATILE,
645                              NULL);
646         if (NT_SUCCESS(Status))
647             ObCloseHandle(KeyHandle, KernelMode);
648         else
649             ERR_(VIDEOPRT, "Failed to create the BaseVideo key (0x%x)\n", Status);
650     }
651 
652     return;
653 }
654 
655 /* PUBLIC FUNCTIONS ***********************************************************/
656 
657 /*
658  * @implemented
659  */
660 ULONG
661 NTAPI
662 VideoPortInitialize(
663     IN PVOID Context1,
664     IN PVOID Context2,
665     IN PVIDEO_HW_INITIALIZATION_DATA HwInitializationData,
666     IN PVOID HwContext)
667 {
668     PDRIVER_OBJECT DriverObject = Context1;
669     PUNICODE_STRING RegistryPath = Context2;
670     NTSTATUS Status;
671     PVIDEO_PORT_DRIVER_EXTENSION DriverExtension;
672     BOOLEAN PnpDriver = FALSE, LegacyDetection = FALSE;
673     static BOOLEAN FirstInitialization;
674 
675     TRACE_(VIDEOPRT, "VideoPortInitialize\n");
676 
677     if (!FirstInitialization)
678     {
679         FirstInitialization = TRUE;
680         KeInitializeMutex(&VideoPortInt10Mutex, 0);
681         KeInitializeSpinLock(&HwResetAdaptersLock);
682         IntLoadRegistryParameters();
683     }
684 
685     /* As a first thing do parameter checks. */
686     if (HwInitializationData->HwInitDataSize > sizeof(VIDEO_HW_INITIALIZATION_DATA))
687     {
688         ERR_(VIDEOPRT, "Invalid HwInitializationData\n");
689         return STATUS_REVISION_MISMATCH;
690     }
691 
692     if ((HwInitializationData->HwFindAdapter == NULL) ||
693         (HwInitializationData->HwInitialize == NULL) ||
694         (HwInitializationData->HwStartIO == NULL))
695     {
696         ERR_(VIDEOPRT, "Invalid HwInitializationData\n");
697         return STATUS_INVALID_PARAMETER;
698     }
699 
700     switch (HwInitializationData->HwInitDataSize)
701     {
702             /*
703              * NT4 drivers are special case, because we must use legacy method
704              * of detection instead of the Plug & Play one.
705              */
706         case SIZE_OF_NT4_VIDEO_HW_INITIALIZATION_DATA:
707             INFO_(VIDEOPRT, "We were loaded by a Windows NT miniport driver.\n");
708             break;
709 
710         case SIZE_OF_W2K_VIDEO_HW_INITIALIZATION_DATA:
711             INFO_(VIDEOPRT, "We were loaded by a Windows 2000 miniport driver.\n");
712             break;
713 
714         case sizeof(VIDEO_HW_INITIALIZATION_DATA):
715             INFO_(VIDEOPRT, "We were loaded by a Windows XP or later miniport driver.\n");
716             break;
717 
718         default:
719             ERR_(VIDEOPRT, "Invalid HwInitializationData size.\n");
720             return STATUS_UNSUCCESSFUL;
721     }
722 
723     /* Set dispatching routines */
724     DriverObject->MajorFunction[IRP_MJ_CREATE] = IntVideoPortDispatchOpen;
725     DriverObject->MajorFunction[IRP_MJ_CLOSE] = IntVideoPortDispatchClose;
726     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
727         IntVideoPortDispatchDeviceControl;
728     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] =
729         IntVideoPortDispatchDeviceControl;
730     DriverObject->DriverUnload = IntVideoPortUnload;
731 
732     /* Determine type of the miniport driver */
733     if ((HwInitializationData->HwInitDataSize >=
734             FIELD_OFFSET(VIDEO_HW_INITIALIZATION_DATA, HwQueryInterface)) &&
735         (HwInitializationData->HwSetPowerState != NULL) &&
736         (HwInitializationData->HwGetPowerState != NULL) &&
737         (HwInitializationData->HwGetVideoChildDescriptor != NULL))
738     {
739         INFO_(VIDEOPRT, "The miniport is a PnP miniport driver\n");
740         PnpDriver = TRUE;
741     }
742 
743     /* Check if legacy detection should be applied */
744     if (!PnpDriver || HwContext)
745     {
746         INFO_(VIDEOPRT, "Legacy detection for adapter interface %d\n",
747               HwInitializationData->AdapterInterfaceType);
748 
749         /* FIXME: Move the code for legacy detection
750            to another function and call it here */
751         LegacyDetection = TRUE;
752     }
753 
754     /*
755      * NOTE:
756      * The driver extension can be already allocated in case that we were
757      * called by legacy driver and failed detecting device. Some miniport
758      * drivers in that case adjust parameters and call VideoPortInitialize
759      * again.
760      */
761     DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
762     if (DriverExtension == NULL)
763     {
764         Status = IoAllocateDriverObjectExtension(DriverObject,
765                                                  DriverObject,
766                                                  sizeof(VIDEO_PORT_DRIVER_EXTENSION),
767                                                  (PVOID *)&DriverExtension);
768         if (!NT_SUCCESS(Status))
769         {
770             ERR_(VIDEOPRT, "IoAllocateDriverObjectExtension failed 0x%x\n", Status);
771             return Status;
772         }
773 
774         /*
775          * Save the registry path. This should be done only once even if
776          * VideoPortInitialize is called multiple times.
777          */
778         if (RegistryPath->Length != 0)
779         {
780             DriverExtension->RegistryPath.Length = 0;
781             DriverExtension->RegistryPath.MaximumLength =
782                 RegistryPath->Length + sizeof(UNICODE_NULL);
783             DriverExtension->RegistryPath.Buffer =
784                 ExAllocatePoolWithTag(
785                     PagedPool,
786                     DriverExtension->RegistryPath.MaximumLength,
787                     'RTSU');
788             if (DriverExtension->RegistryPath.Buffer == NULL)
789             {
790                 RtlInitUnicodeString(&DriverExtension->RegistryPath, NULL);
791                 return STATUS_INSUFFICIENT_RESOURCES;
792             }
793 
794             RtlCopyUnicodeString(&DriverExtension->RegistryPath, RegistryPath);
795 
796             /* There is a bug in Spice guest agent, which searches 'System' case-sensitively.
797              * Replace 'SYSTEM' by 'System' to fix that.
798              * Probably for similar reason, Windows also replaces 'MACHINE' by 'Machine'.
799              */
800             wcsncpy(wcsstr(DriverExtension->RegistryPath.Buffer, L"\\SYSTEM\\"), L"\\System\\", ARRAYSIZE(L"\\SYSTEM\\") - 1);
801             wcsncpy(wcsstr(DriverExtension->RegistryPath.Buffer, L"\\MACHINE\\"), L"\\Machine\\", ARRAYSIZE(L"\\MACHINE\\") - 1);
802 
803             INFO_(VIDEOPRT, "RegistryPath: %wZ\n", &DriverExtension->RegistryPath);
804         }
805         else
806         {
807             RtlInitUnicodeString(&DriverExtension->RegistryPath, NULL);
808         }
809     }
810 
811     /* Copy the correct miniport initialization data to the device extension. */
812     RtlCopyMemory(&DriverExtension->InitializationData,
813                   HwInitializationData,
814                   HwInitializationData->HwInitDataSize);
815     if (HwInitializationData->HwInitDataSize <
816             sizeof(VIDEO_HW_INITIALIZATION_DATA))
817     {
818         RtlZeroMemory((PVOID)((ULONG_PTR)&DriverExtension->InitializationData +
819                               HwInitializationData->HwInitDataSize),
820                       sizeof(VIDEO_HW_INITIALIZATION_DATA) -
821                       HwInitializationData->HwInitDataSize);
822     }
823     DriverExtension->HwContext = HwContext;
824 
825     /*
826      * Plug & Play drivers registers the device in AddDevice routine.
827      * For legacy drivers we must do it now.
828      */
829     if (LegacyDetection)
830     {
831         PDEVICE_OBJECT DeviceObject;
832 
833         if (HwInitializationData->HwInitDataSize != SIZE_OF_NT4_VIDEO_HW_INITIALIZATION_DATA)
834         {
835             /* Power management */
836             DriverObject->MajorFunction[IRP_MJ_POWER] = IntVideoPortDispatchPower;
837         }
838 
839         Status = IntVideoPortCreateAdapterDeviceObject(DriverObject,
840                                                        DriverExtension,
841                                                        NULL,
842                                                        DriverExtension->InitializationData.StartingDeviceNumber,
843                                                        0,
844                                                        &DeviceObject);
845         if (!NT_SUCCESS(Status))
846         {
847             ERR_(VIDEOPRT, "IntVideoPortCreateAdapterDeviceObject returned 0x%x\n", Status);
848             return Status;
849         }
850 
851         Status = IntVideoPortFindAdapter(DriverObject, DriverExtension, DeviceObject);
852         if (!NT_SUCCESS(Status))
853             ERR_(VIDEOPRT, "IntVideoPortFindAdapter returned 0x%x\n", Status);
854 
855         return Status;
856     }
857     else
858     {
859         DriverObject->DriverExtension->AddDevice = IntVideoPortAddDevice;
860         DriverObject->MajorFunction[IRP_MJ_PNP] = IntVideoPortDispatchPnp;
861         DriverObject->MajorFunction[IRP_MJ_POWER] = IntVideoPortDispatchPower;
862         DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = IntVideoPortDispatchSystemControl;
863 
864         return STATUS_SUCCESS;
865     }
866 }
867 
868 /*
869  * @implemented
870  */
871 VOID
872 VideoPortDebugPrint(
873     IN VIDEO_DEBUG_LEVEL DebugPrintLevel,
874     IN PCHAR DebugMessage,
875     ...)
876 {
877     va_list ap;
878 
879     if (VideoDebugLevel >= DebugPrintLevel)
880         DebugPrintLevel = Error;
881 
882     va_start(ap, DebugMessage);
883     vDbgPrintEx(DPFLTR_IHVVIDEO_ID, DebugPrintLevel, DebugMessage, ap);
884     va_end(ap);
885 }
886 
887 /*
888  * @unimplemented
889  */
890 VOID
891 NTAPI
892 VideoPortLogError(
893     IN PVOID HwDeviceExtension,
894     IN PVIDEO_REQUEST_PACKET Vrp OPTIONAL,
895     IN VP_STATUS ErrorCode,
896     IN ULONG UniqueId)
897 {
898     UNIMPLEMENTED;
899 
900     INFO_(VIDEOPRT, "VideoPortLogError ErrorCode %d (0x%x) UniqueId %lu (0x%lx)\n",
901           ErrorCode, ErrorCode, UniqueId, UniqueId);
902     if (Vrp)
903         INFO_(VIDEOPRT, "Vrp->IoControlCode %lu (0x%lx)\n", Vrp->IoControlCode, Vrp->IoControlCode);
904 }
905 
906 /*
907  * @implemented
908  */
909 UCHAR
910 NTAPI
911 VideoPortGetCurrentIrql(VOID)
912 {
913     return KeGetCurrentIrql();
914 }
915 
916 typedef struct QueryRegistryCallbackContext
917 {
918     PVOID HwDeviceExtension;
919     PVOID HwContext;
920     PMINIPORT_GET_REGISTRY_ROUTINE HwGetRegistryRoutine;
921 } QUERY_REGISTRY_CALLBACK_CONTEXT, *PQUERY_REGISTRY_CALLBACK_CONTEXT;
922 
923 static
924 NTSTATUS
925 NTAPI
926 QueryRegistryCallback(
927     IN PWSTR ValueName,
928     IN ULONG ValueType,
929     IN PVOID ValueData,
930     IN ULONG ValueLength,
931     IN PVOID Context,
932     IN PVOID EntryContext)
933 {
934     PQUERY_REGISTRY_CALLBACK_CONTEXT CallbackContext = (PQUERY_REGISTRY_CALLBACK_CONTEXT) Context;
935 
936     INFO_(VIDEOPRT, "Found registry value for name %S: type %d, length %d\n",
937           ValueName, ValueType, ValueLength);
938     return (*(CallbackContext->HwGetRegistryRoutine))(
939                CallbackContext->HwDeviceExtension,
940                CallbackContext->HwContext,
941                ValueName,
942                ValueData,
943                ValueLength);
944 }
945 
946 /*
947  * @unimplemented
948  */
949 
950 VP_STATUS
951 NTAPI
952 VideoPortGetRegistryParameters(
953     IN PVOID HwDeviceExtension,
954     IN PWSTR ParameterName,
955     IN UCHAR IsParameterFileName,
956     IN PMINIPORT_GET_REGISTRY_ROUTINE GetRegistryRoutine,
957     IN PVOID HwContext)
958 {
959     RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}};
960     QUERY_REGISTRY_CALLBACK_CONTEXT Context;
961     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
962     NTSTATUS Status;
963 
964     DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
965 
966     TRACE_(VIDEOPRT, "VideoPortGetRegistryParameters ParameterName %S, RegPath: %wZ\n",
967            ParameterName, &DeviceExtension->RegistryPath);
968 
969     Context.HwDeviceExtension = HwDeviceExtension;
970     Context.HwContext = HwContext;
971     Context.HwGetRegistryRoutine = GetRegistryRoutine;
972 
973     QueryTable[0].QueryRoutine = QueryRegistryCallback;
974     QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
975     QueryTable[0].Name = ParameterName;
976 
977     Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
978                                     DeviceExtension->RegistryPath.Buffer,
979                                     QueryTable,
980                                     &Context,
981                                     NULL);
982     if (!NT_SUCCESS(Status))
983     {
984         WARN_(VIDEOPRT, "VideoPortGetRegistryParameters could not find the "
985               "requested parameter\n");
986         return ERROR_INVALID_PARAMETER;
987     }
988 
989     if (IsParameterFileName)
990     {
991         /* FIXME: need to read the contents of the file */
992         UNIMPLEMENTED;
993     }
994 
995     return NO_ERROR;
996 }
997 
998 /*
999  * @implemented
1000  */
1001 VP_STATUS
1002 NTAPI
1003 VideoPortSetRegistryParameters(
1004     IN PVOID HwDeviceExtension,
1005     IN PWSTR ValueName,
1006     IN PVOID ValueData,
1007     IN ULONG ValueLength)
1008 {
1009     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1010     VP_STATUS Status;
1011 
1012     DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1013     TRACE_(VIDEOPRT, "VideoPortSetRegistryParameters ParameterName %S, RegPath: %wZ\n",
1014            ValueName,
1015            &DeviceExtension->RegistryPath);
1016     ASSERT_IRQL_LESS_OR_EQUAL(PASSIVE_LEVEL);
1017     Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1018                                    DeviceExtension->RegistryPath.Buffer,
1019                                    ValueName,
1020                                    REG_BINARY,
1021                                    ValueData,
1022                                    ValueLength);
1023     if (Status != NO_ERROR)
1024         WARN_(VIDEOPRT, "VideoPortSetRegistryParameters error 0x%x\n", Status);
1025 
1026     return Status;
1027 }
1028 
1029 /*
1030  * @implemented
1031  */
1032 VP_STATUS
1033 NTAPI
1034 VideoPortGetVgaStatus(
1035     IN PVOID HwDeviceExtension,
1036     OUT PULONG VgaStatus)
1037 {
1038     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1039 
1040     TRACE_(VIDEOPRT, "VideoPortGetVgaStatus\n");
1041 
1042     DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1043     if (KeGetCurrentIrql() == PASSIVE_LEVEL)
1044     {
1045         if (DeviceExtension->AdapterInterfaceType == PCIBus)
1046         {
1047             /* VgaStatus: 0 == VGA not enabled, 1 == VGA enabled. */
1048             /* Assumed for now */
1049             *VgaStatus = 1;
1050             return NO_ERROR;
1051         }
1052     }
1053 
1054     return ERROR_INVALID_FUNCTION;
1055 }
1056 
1057 /*
1058  * @implemented
1059  */
1060 PVOID
1061 NTAPI
1062 VideoPortGetRomImage(
1063     IN PVOID HwDeviceExtension,
1064     IN PVOID Unused1,
1065     IN ULONG Unused2,
1066     IN ULONG Length)
1067 {
1068     static PVOID RomImageBuffer = NULL;
1069     PKPROCESS CallingProcess;
1070     KAPC_STATE ApcState;
1071 
1072     TRACE_(VIDEOPRT, "VideoPortGetRomImage(HwDeviceExtension 0x%X Length 0x%X)\n",
1073            HwDeviceExtension, Length);
1074 
1075     /* If the length is zero then free the existing buffer. */
1076     if (Length == 0)
1077     {
1078         if (RomImageBuffer != NULL)
1079         {
1080             ExFreePool(RomImageBuffer);
1081             RomImageBuffer = NULL;
1082         }
1083         return NULL;
1084     }
1085     else
1086     {
1087         /*
1088          * The DDK says we shouldn't use the legacy C0000 method but get the
1089          * rom base address from the corresponding pci or acpi register but
1090          * lets ignore that and use C0000 anyway. We have already mapped the
1091          * bios area into memory so we'll copy from there.
1092          */
1093 
1094         /* Copy the bios. */
1095         Length = min(Length, 0x10000);
1096         if (RomImageBuffer != NULL)
1097         {
1098             ExFreePool(RomImageBuffer);
1099         }
1100 
1101         RomImageBuffer = ExAllocatePool(PagedPool, Length);
1102         if (RomImageBuffer == NULL)
1103         {
1104             return NULL;
1105         }
1106 
1107         IntAttachToCSRSS(&CallingProcess, &ApcState);
1108         RtlCopyMemory(RomImageBuffer, (PUCHAR)0xC0000, Length);
1109         IntDetachFromCSRSS(&CallingProcess, &ApcState);
1110 
1111         return RomImageBuffer;
1112     }
1113 }
1114 
1115 /*
1116  * @implemented
1117  */
1118 BOOLEAN
1119 NTAPI
1120 VideoPortScanRom(
1121     IN PVOID HwDeviceExtension,
1122     IN PUCHAR RomBase,
1123     IN ULONG RomLength,
1124     IN PUCHAR String)
1125 {
1126     SIZE_T StringLength;
1127     BOOLEAN Found;
1128     PUCHAR SearchLocation;
1129 
1130     TRACE_(VIDEOPRT, "VideoPortScanRom RomBase %p RomLength 0x%x String %s\n", RomBase, RomLength, String);
1131 
1132     StringLength = strlen((PCHAR)String);
1133     Found = FALSE;
1134     for (SearchLocation = RomBase;
1135             !Found && SearchLocation < RomBase + RomLength - StringLength;
1136             SearchLocation++)
1137     {
1138         Found = (RtlCompareMemory(SearchLocation, String, StringLength) == StringLength);
1139         if (Found)
1140         {
1141             INFO_(VIDEOPRT, "Match found at %p\n", SearchLocation);
1142         }
1143     }
1144 
1145     return Found;
1146 }
1147 
1148 /*
1149  * @implemented
1150  */
1151 BOOLEAN
1152 NTAPI
1153 VideoPortSynchronizeExecution(
1154     IN PVOID HwDeviceExtension,
1155     IN VIDEO_SYNCHRONIZE_PRIORITY Priority,
1156     IN PMINIPORT_SYNCHRONIZE_ROUTINE SynchronizeRoutine,
1157     OUT PVOID Context)
1158 {
1159     BOOLEAN Ret;
1160     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1161     KIRQL OldIrql;
1162 
1163     switch (Priority)
1164     {
1165         case VpLowPriority:
1166             Ret = (*SynchronizeRoutine)(Context);
1167             break;
1168 
1169         case VpMediumPriority:
1170             DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1171             if (DeviceExtension->InterruptObject == NULL)
1172                 Ret = (*SynchronizeRoutine)(Context);
1173             else
1174                 Ret = KeSynchronizeExecution(
1175                           DeviceExtension->InterruptObject,
1176                           SynchronizeRoutine,
1177                           Context);
1178             break;
1179 
1180         case VpHighPriority:
1181             OldIrql = KeGetCurrentIrql();
1182             if (OldIrql < SYNCH_LEVEL)
1183                 KeRaiseIrql(SYNCH_LEVEL, &OldIrql);
1184 
1185             Ret = (*SynchronizeRoutine)(Context);
1186 
1187             if (OldIrql < SYNCH_LEVEL)
1188                 KeLowerIrql(OldIrql);
1189             break;
1190 
1191         default:
1192             Ret = FALSE;
1193     }
1194 
1195     return Ret;
1196 }
1197 
1198 /*
1199  * @implemented
1200  */
1201 NTSTATUS NTAPI
1202 IntVideoPortEnumerateChildren(
1203     IN PDEVICE_OBJECT DeviceObject,
1204     IN PIRP Irp)
1205 {
1206     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1207     ULONG Status;
1208     VIDEO_CHILD_ENUM_INFO ChildEnumInfo;
1209     BOOLEAN bHaveLastMonitorID = FALSE;
1210     UCHAR LastMonitorID[10];
1211     ULONG Unused;
1212     UINT i;
1213     PDEVICE_OBJECT ChildDeviceObject;
1214     PVIDEO_PORT_CHILD_EXTENSION ChildExtension;
1215 
1216     INFO_(VIDEOPRT, "Starting child device probe\n");
1217     DeviceExtension = DeviceObject->DeviceExtension;
1218     if (DeviceExtension->DriverExtension->InitializationData.HwGetVideoChildDescriptor == NULL)
1219     {
1220         WARN_(VIDEOPRT, "Miniport's HwGetVideoChildDescriptor is NULL!\n");
1221         return STATUS_SUCCESS;
1222     }
1223 
1224     if (!IsListEmpty(&DeviceExtension->ChildDeviceList))
1225     {
1226         ERR_(VIDEOPRT, "FIXME: Support calling VideoPortEnumerateChildren again!\n");
1227         return STATUS_SUCCESS;
1228     }
1229 
1230     /* Enumerate the children */
1231     for (i = 1; ; i++)
1232     {
1233         Status = IoCreateDevice(DeviceExtension->DriverObject,
1234                                 sizeof(VIDEO_PORT_CHILD_EXTENSION) +
1235                                 DeviceExtension->DriverExtension->InitializationData.HwChildDeviceExtensionSize,
1236                                 NULL,
1237                                 FILE_DEVICE_CONTROLLER,
1238                                 FILE_DEVICE_SECURE_OPEN,
1239                                 FALSE,
1240                                 &ChildDeviceObject);
1241         if (!NT_SUCCESS(Status))
1242             return Status;
1243 
1244         ChildExtension = ChildDeviceObject->DeviceExtension;
1245 
1246         RtlZeroMemory(ChildExtension,
1247                       sizeof(VIDEO_PORT_CHILD_EXTENSION) +
1248                         DeviceExtension->DriverExtension->InitializationData.HwChildDeviceExtensionSize);
1249 
1250         ChildExtension->Common.Fdo = FALSE;
1251         ChildExtension->ChildId = i;
1252         ChildExtension->PhysicalDeviceObject = ChildDeviceObject;
1253         ChildExtension->DriverObject = DeviceExtension->DriverObject;
1254 
1255         /* Setup the ChildEnumInfo */
1256         ChildEnumInfo.Size = sizeof(ChildEnumInfo);
1257         ChildEnumInfo.ChildDescriptorSize = sizeof(ChildExtension->ChildDescriptor);
1258         ChildEnumInfo.ACPIHwId = 0;
1259 
1260         if (DeviceExtension->DriverExtension->InitializationData.HwChildDeviceExtensionSize)
1261             ChildEnumInfo.ChildHwDeviceExtension = VIDEO_PORT_GET_CHILD_EXTENSION(ChildExtension);
1262         else
1263             ChildEnumInfo.ChildHwDeviceExtension = NULL;
1264 
1265         ChildEnumInfo.ChildIndex = ChildExtension->ChildId;
1266 
1267         INFO_(VIDEOPRT, "Probing child: %d\n", ChildEnumInfo.ChildIndex);
1268         Status = DeviceExtension->DriverExtension->InitializationData.HwGetVideoChildDescriptor(
1269                      DeviceExtension->MiniPortDeviceExtension,
1270                      &ChildEnumInfo,
1271                      &ChildExtension->ChildType,
1272                      ChildExtension->ChildDescriptor,
1273                      &ChildExtension->ChildId,
1274                      &Unused);
1275         if (Status == VIDEO_ENUM_MORE_DEVICES)
1276         {
1277             if (ChildExtension->ChildType == Monitor)
1278             {
1279                 // Check if the EDID is valid
1280                 if (ChildExtension->ChildDescriptor[0] == 0x00 &&
1281                         ChildExtension->ChildDescriptor[1] == 0xFF &&
1282                         ChildExtension->ChildDescriptor[2] == 0xFF &&
1283                         ChildExtension->ChildDescriptor[3] == 0xFF &&
1284                         ChildExtension->ChildDescriptor[4] == 0xFF &&
1285                         ChildExtension->ChildDescriptor[5] == 0xFF &&
1286                         ChildExtension->ChildDescriptor[6] == 0xFF &&
1287                         ChildExtension->ChildDescriptor[7] == 0x00)
1288                 {
1289                     if (bHaveLastMonitorID)
1290                     {
1291                         // Compare the previous monitor ID with the current one, break the loop if they are identical
1292                         if (RtlCompareMemory(LastMonitorID, &ChildExtension->ChildDescriptor[8], sizeof(LastMonitorID)) == sizeof(LastMonitorID))
1293                         {
1294                             INFO_(VIDEOPRT, "Found identical Monitor ID two times, stopping enumeration\n");
1295                             IoDeleteDevice(ChildDeviceObject);
1296                             break;
1297                         }
1298                     }
1299 
1300                     // Copy 10 bytes from the EDID, which can be used to uniquely identify the monitor
1301                     RtlCopyMemory(LastMonitorID, &ChildExtension->ChildDescriptor[8], sizeof(LastMonitorID));
1302                     bHaveLastMonitorID = TRUE;
1303 
1304                     /* Mark it valid */
1305                     ChildExtension->EdidValid = TRUE;
1306                 }
1307                 else
1308                 {
1309                     /* Mark it invalid */
1310                     ChildExtension->EdidValid = FALSE;
1311                 }
1312             }
1313         }
1314         else if (Status == VIDEO_ENUM_INVALID_DEVICE)
1315         {
1316             WARN_(VIDEOPRT, "Child device %d is invalid!\n", ChildEnumInfo.ChildIndex);
1317             IoDeleteDevice(ChildDeviceObject);
1318             continue;
1319         }
1320         else if (Status == VIDEO_ENUM_NO_MORE_DEVICES)
1321         {
1322             INFO_(VIDEOPRT, "End of child enumeration! (%d children enumerated)\n", i - 1);
1323             IoDeleteDevice(ChildDeviceObject);
1324             break;
1325         }
1326         else
1327         {
1328             WARN_(VIDEOPRT, "HwGetVideoChildDescriptor returned unknown status code 0x%x!\n", Status);
1329             IoDeleteDevice(ChildDeviceObject);
1330             break;
1331         }
1332 
1333         if (ChildExtension->ChildType == Monitor)
1334         {
1335             UINT j;
1336             PUCHAR p = ChildExtension->ChildDescriptor;
1337             INFO_(VIDEOPRT, "Monitor device enumerated! (ChildId = 0x%x)\n", ChildExtension->ChildId);
1338             for (j = 0; j < sizeof (ChildExtension->ChildDescriptor); j += 8)
1339             {
1340                 INFO_(VIDEOPRT, "%02x %02x %02x %02x %02x %02x %02x %02x\n",
1341                       p[j + 0], p[j + 1], p[j + 2], p[j + 3],
1342                       p[j + 4], p[j + 5], p[j + 6], p[j + 7]);
1343             }
1344         }
1345         else if (ChildExtension->ChildType == Other)
1346         {
1347             INFO_(VIDEOPRT, "\"Other\" device enumerated: DeviceId = %S\n", (PWSTR)ChildExtension->ChildDescriptor);
1348         }
1349         else
1350         {
1351             ERR_(VIDEOPRT, "HwGetVideoChildDescriptor returned unsupported type: %d\n", ChildExtension->ChildType);
1352         }
1353 
1354         /* Clear the init flag */
1355         ChildDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1356 
1357         InsertTailList(&DeviceExtension->ChildDeviceList,
1358                        &ChildExtension->ListEntry);
1359     }
1360 
1361     return STATUS_SUCCESS;
1362 }
1363 
1364 VP_STATUS
1365 NTAPI
1366 VideoPortEnumerateChildren(
1367     IN PVOID HwDeviceExtension,
1368     IN PVOID Reserved)
1369 {
1370     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1371 
1372     DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1373     ASSERT(DeviceExtension);
1374 
1375     if (DeviceExtension->PhysicalDeviceObject)
1376     {
1377         /* Trigger reenumeration by the PnP manager */
1378         IoInvalidateDeviceRelations(DeviceExtension->PhysicalDeviceObject, BusRelations);
1379     }
1380 
1381     return NO_ERROR;
1382 }
1383 
1384 /*
1385  * @unimplemented
1386  */
1387 VP_STATUS
1388 NTAPI
1389 VideoPortCreateSecondaryDisplay(
1390     IN PVOID HwDeviceExtension,
1391     IN OUT PVOID *SecondaryDeviceExtension,
1392     IN ULONG Flag)
1393 {
1394     PDEVICE_OBJECT DeviceObject;
1395     PVIDEO_PORT_DEVICE_EXTENSION FirstDeviceExtension, DeviceExtension;
1396     NTSTATUS Status;
1397 
1398     ASSERT(SecondaryDeviceExtension);
1399 
1400     if (Flag != 0)
1401     {
1402         UNIMPLEMENTED;
1403     }
1404 
1405     FirstDeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1406 
1407     if (FirstDeviceExtension->DisplayNumber != 0)
1408     {
1409         DPRINT1("Calling VideoPortCreateSecondaryDisplay for InstanceId %lu\n",
1410                 FirstDeviceExtension->DisplayNumber);
1411     }
1412 
1413     Status = IntVideoPortCreateAdapterDeviceObject(FirstDeviceExtension->DriverObject,
1414                                                    FirstDeviceExtension->DriverExtension,
1415                                                    FirstDeviceExtension->PhysicalDeviceObject,
1416                                                    FirstDeviceExtension->AdapterNumber,
1417                                                    FirstDeviceExtension->NumberOfSecondaryDisplays + 1,
1418                                                    &DeviceObject);
1419     if (!NT_SUCCESS(Status))
1420     {
1421         DPRINT1("IntVideoPortCreateAdapterDeviceObject() failed with status 0x%08x\n", Status);
1422         return ERROR_DEV_NOT_EXIST;
1423     }
1424 
1425     DeviceExtension = DeviceObject->DeviceExtension;
1426 
1427     /* Increment secondary display count */
1428     FirstDeviceExtension->NumberOfSecondaryDisplays++;
1429 
1430     *SecondaryDeviceExtension = DeviceExtension->MiniPortDeviceExtension;
1431     return NO_ERROR;
1432 }
1433 
1434 /*
1435  * @implemented
1436  */
1437 BOOLEAN
1438 NTAPI
1439 VideoPortQueueDpc(
1440     IN PVOID HwDeviceExtension,
1441     IN PMINIPORT_DPC_ROUTINE CallbackRoutine,
1442     IN PVOID Context)
1443 {
1444     return KeInsertQueueDpc(
1445                &VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension)->DpcObject,
1446                (PVOID)CallbackRoutine,
1447                (PVOID)Context);
1448 }
1449 
1450 /*
1451  * @implemented
1452  */
1453 PVOID
1454 NTAPI
1455 VideoPortGetAssociatedDeviceExtension(
1456     IN PVOID DeviceObject)
1457 {
1458     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1459 
1460     TRACE_(VIDEOPRT, "VideoPortGetAssociatedDeviceExtension\n");
1461     DeviceExtension = ((PDEVICE_OBJECT)DeviceObject)->DeviceExtension;
1462     if (!DeviceExtension)
1463         return NULL;
1464     return DeviceExtension->MiniPortDeviceExtension;
1465 }
1466 
1467 /*
1468  * @implemented
1469  */
1470 VP_STATUS
1471 NTAPI
1472 VideoPortGetVersion(
1473     IN PVOID HwDeviceExtension,
1474     IN OUT PVPOSVERSIONINFO VpOsVersionInfo)
1475 {
1476     RTL_OSVERSIONINFOEXW Version;
1477 
1478     Version.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
1479     if (VpOsVersionInfo->Size >= sizeof(VPOSVERSIONINFO))
1480     {
1481 #if 1
1482         if (NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&Version)))
1483         {
1484             VpOsVersionInfo->MajorVersion = Version.dwMajorVersion;
1485             VpOsVersionInfo->MinorVersion = Version.dwMinorVersion;
1486             VpOsVersionInfo->BuildNumber = Version.dwBuildNumber;
1487             VpOsVersionInfo->ServicePackMajor = Version.wServicePackMajor;
1488             VpOsVersionInfo->ServicePackMinor = Version.wServicePackMinor;
1489             return NO_ERROR;
1490         }
1491         return ERROR_INVALID_PARAMETER;
1492 #else
1493         VpOsVersionInfo->MajorVersion = 5;
1494         VpOsVersionInfo->MinorVersion = 0;
1495         VpOsVersionInfo->BuildNumber = 2195;
1496         VpOsVersionInfo->ServicePackMajor = 4;
1497         VpOsVersionInfo->ServicePackMinor = 0;
1498         return NO_ERROR;
1499 #endif
1500     }
1501 
1502     return ERROR_INVALID_PARAMETER;
1503 }
1504 
1505 /*
1506  * @implemented
1507  */
1508 BOOLEAN
1509 NTAPI
1510 VideoPortCheckForDeviceExistence(
1511     IN PVOID HwDeviceExtension,
1512     IN USHORT VendorId,
1513     IN USHORT DeviceId,
1514     IN UCHAR RevisionId,
1515     IN USHORT SubVendorId,
1516     IN USHORT SubSystemId,
1517     IN ULONG Flags)
1518 {
1519     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1520     PCI_DEVICE_PRESENT_INTERFACE PciDevicePresentInterface;
1521     IO_STATUS_BLOCK IoStatusBlock;
1522     IO_STACK_LOCATION IoStack;
1523     ULONG PciFlags = 0;
1524     NTSTATUS Status;
1525     BOOL DevicePresent;
1526 
1527     TRACE_(VIDEOPRT, "VideoPortCheckForDeviceExistence\n");
1528 
1529     if (Flags & ~(CDE_USE_REVISION | CDE_USE_SUBSYSTEM_IDS))
1530     {
1531         WARN_(VIDEOPRT, "VideoPortCheckForDeviceExistence: Unknown flags 0x%lx\n", Flags & ~(CDE_USE_REVISION | CDE_USE_SUBSYSTEM_IDS));
1532         return FALSE;
1533     }
1534 
1535     DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1536 
1537     PciDevicePresentInterface.Size = sizeof(PCI_DEVICE_PRESENT_INTERFACE);
1538     PciDevicePresentInterface.Version = 1;
1539     IoStack.Parameters.QueryInterface.Size = PciDevicePresentInterface.Size;
1540     IoStack.Parameters.QueryInterface.Version = PciDevicePresentInterface.Version;
1541     IoStack.Parameters.QueryInterface.Interface = (PINTERFACE)&PciDevicePresentInterface;
1542     IoStack.Parameters.QueryInterface.InterfaceType =
1543         &GUID_PCI_DEVICE_PRESENT_INTERFACE;
1544     Status = IopInitiatePnpIrp(DeviceExtension->NextDeviceObject,
1545                                &IoStatusBlock, IRP_MN_QUERY_INTERFACE, &IoStack);
1546     if (!NT_SUCCESS(Status))
1547     {
1548         WARN_(VIDEOPRT, "IopInitiatePnpIrp() failed! (Status 0x%lx)\n", Status);
1549         return FALSE;
1550     }
1551 
1552     if (Flags & CDE_USE_REVISION)
1553         PciFlags |= PCI_USE_REVISION;
1554     if (Flags & CDE_USE_SUBSYSTEM_IDS)
1555         PciFlags |= PCI_USE_SUBSYSTEM_IDS;
1556 
1557     DevicePresent = PciDevicePresentInterface.IsDevicePresent(
1558                         VendorId, DeviceId, RevisionId,
1559                         SubVendorId, SubSystemId, PciFlags);
1560 
1561     PciDevicePresentInterface.InterfaceDereference(PciDevicePresentInterface.Context);
1562 
1563     return DevicePresent;
1564 }
1565 
1566 /*
1567  * @unimplemented
1568  */
1569 VP_STATUS
1570 NTAPI
1571 VideoPortRegisterBugcheckCallback(
1572     IN PVOID HwDeviceExtension,
1573     IN ULONG BugcheckCode,
1574     IN PVIDEO_BUGCHECK_CALLBACK Callback,
1575     IN ULONG BugcheckDataSize)
1576 {
1577     UNIMPLEMENTED;
1578     return NO_ERROR;
1579 }
1580 
1581 /*
1582  * @implemented
1583  */
1584 LONGLONG
1585 NTAPI
1586 VideoPortQueryPerformanceCounter(
1587     IN PVOID HwDeviceExtension,
1588     OUT PLONGLONG PerformanceFrequency OPTIONAL)
1589 {
1590     LARGE_INTEGER Result;
1591 
1592     TRACE_(VIDEOPRT, "VideoPortQueryPerformanceCounter\n");
1593     Result = KeQueryPerformanceCounter((PLARGE_INTEGER)PerformanceFrequency);
1594     return Result.QuadPart;
1595 }
1596 
1597 /*
1598  * @implemented
1599  */
1600 VOID
1601 NTAPI
1602 VideoPortAcquireDeviceLock(
1603     IN PVOID  HwDeviceExtension)
1604 {
1605     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1606 
1607     TRACE_(VIDEOPRT, "VideoPortAcquireDeviceLock\n");
1608     DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1609     KeWaitForMutexObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1610     // ASSERT(Status == STATUS_SUCCESS);
1611 }
1612 
1613 /*
1614  * @implemented
1615  */
1616 VOID
1617 NTAPI
1618 VideoPortReleaseDeviceLock(
1619     IN PVOID HwDeviceExtension)
1620 {
1621     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
1622 
1623     TRACE_(VIDEOPRT, "VideoPortReleaseDeviceLock\n");
1624     DeviceExtension = VIDEO_PORT_GET_DEVICE_EXTENSION(HwDeviceExtension);
1625     KeReleaseMutex(&DeviceExtension->DeviceLock, FALSE);
1626     //ASSERT(Status == STATUS_SUCCESS);
1627 }
1628 
1629 /*
1630  * @unimplemented
1631  */
1632 VOID
1633 NTAPI
1634 VpNotifyEaData(
1635     IN PDEVICE_OBJECT DeviceObject,
1636     IN PVOID Data)
1637 {
1638     UNIMPLEMENTED;
1639 }
1640 
1641 /*
1642  * @implemented
1643  */
1644 PVOID
1645 NTAPI
1646 VideoPortAllocateContiguousMemory(
1647     IN PVOID HwDeviceExtension,
1648     IN ULONG NumberOfBytes,
1649     IN PHYSICAL_ADDRESS HighestAcceptableAddress
1650 )
1651 {
1652     return MmAllocateContiguousMemory(NumberOfBytes, HighestAcceptableAddress);
1653 }
1654 
1655 /*
1656  * @implemented
1657  */
1658 BOOLEAN
1659 NTAPI
1660 VideoPortIsNoVesa(VOID)
1661 {
1662     return VpNoVesa;
1663 }
1664