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