xref: /reactos/win32ss/gdi/eng/device.c (revision d6eebaa4)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS kernel
4  * PURPOSE:           GDI Driver Device Functions
5  * FILE:              win32ss/gdi/eng/device.c
6  * PROGRAMER:         Jason Filby
7  *                    Timo Kreuzer
8  */
9 
10 #include <win32k.h>
11 #include <ntddvdeo.h>
12 
13 DBG_DEFAULT_CHANNEL(EngDev);
14 
15 static PGRAPHICS_DEVICE gpPrimaryGraphicsDevice;
16 static PGRAPHICS_DEVICE gpVgaGraphicsDevice;
17 
18 static PGRAPHICS_DEVICE gpGraphicsDeviceFirst = NULL;
19 static PGRAPHICS_DEVICE gpGraphicsDeviceLast = NULL;
20 static HSEMAPHORE ghsemGraphicsDeviceList;
21 static ULONG giDevNum = 1;
22 
23 CODE_SEG("INIT")
24 NTSTATUS
25 NTAPI
26 InitDeviceImpl(VOID)
27 {
28     ghsemGraphicsDeviceList = EngCreateSemaphore();
29     if (!ghsemGraphicsDeviceList)
30         return STATUS_INSUFFICIENT_RESOURCES;
31 
32     return STATUS_SUCCESS;
33 }
34 
35 static
36 BOOLEAN
37 EngpHasVgaDriver(
38     _In_ PGRAPHICS_DEVICE pGraphicsDevice)
39 {
40     WCHAR awcDeviceKey[256], awcServiceName[100];
41     PWSTR lastBkSlash;
42     NTSTATUS Status;
43     ULONG cbValue;
44     HKEY hkey;
45 
46     /* Open the key for the adapters */
47     Status = RegOpenKey(L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO", &hkey);
48     if (!NT_SUCCESS(Status))
49     {
50         ERR("Could not open HARDWARE\\DEVICEMAP\\VIDEO registry key: 0x%08lx\n", Status);
51         return FALSE;
52     }
53 
54     /* Read the name of the device key */
55     cbValue = sizeof(awcDeviceKey);
56     Status = RegQueryValue(hkey, pGraphicsDevice->szNtDeviceName, REG_SZ, awcDeviceKey, &cbValue);
57     ZwClose(hkey);
58     if (!NT_SUCCESS(Status))
59     {
60         ERR("Could not read '%S' registry value: 0x%08lx\n", Status);
61         return FALSE;
62     }
63 
64     /* Replace 'DeviceN' by 'Video' */
65     lastBkSlash = wcsrchr(awcDeviceKey, L'\\');
66     if (!lastBkSlash)
67     {
68         ERR("Invalid registry key '%S'\n", lastBkSlash);
69         return FALSE;
70     }
71     if (!NT_SUCCESS(RtlStringCchCopyW(lastBkSlash + 1,
72                                       ARRAYSIZE(awcDeviceKey) - (lastBkSlash + 1 - awcDeviceKey),
73                                       L"Video")))
74     {
75         ERR("Failed to add 'Video' to registry key '%S'\n", awcDeviceKey);
76         return FALSE;
77     }
78 
79     /* Open device key */
80     Status = RegOpenKey(awcDeviceKey, &hkey);
81     if (!NT_SUCCESS(Status))
82     {
83         ERR("Could not open %S registry key: 0x%08lx\n", awcDeviceKey, Status);
84         return FALSE;
85     }
86 
87     /* Read service name */
88     cbValue = sizeof(awcServiceName);
89     Status = RegQueryValue(hkey, L"Service", REG_SZ, awcServiceName, &cbValue);
90     ZwClose(hkey);
91     if (!NT_SUCCESS(Status))
92     {
93         ERR("Could not read Service registry value in %S: 0x%08lx\n", awcDeviceKey, Status);
94         return FALSE;
95     }
96 
97     /* Device is using VGA driver if service name is 'VGASave' (case insensitive) */
98     return (_wcsicmp(awcServiceName, L"VGASave") == 0);
99 }
100 
101 /*
102  * Add a device to gpGraphicsDeviceFirst/gpGraphicsDeviceLast list (if not already present).
103  */
104 _Requires_lock_held_(ghsemGraphicsDeviceList)
105 static
106 VOID
107 EngpLinkGraphicsDevice(
108     _In_ PGRAPHICS_DEVICE pToAdd)
109 {
110     PGRAPHICS_DEVICE pGraphicsDevice;
111 
112     TRACE("EngLinkGraphicsDevice(%p)\n", pToAdd);
113 
114     /* Search if device is not already linked */
115     for (pGraphicsDevice = gpGraphicsDeviceFirst;
116          pGraphicsDevice;
117          pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
118     {
119         if (pGraphicsDevice == pToAdd)
120             return;
121     }
122 
123     pToAdd->pNextGraphicsDevice = NULL;
124     if (gpGraphicsDeviceLast)
125         gpGraphicsDeviceLast->pNextGraphicsDevice = pToAdd;
126     gpGraphicsDeviceLast = pToAdd;
127     if (!gpGraphicsDeviceFirst)
128         gpGraphicsDeviceFirst = pToAdd;
129 }
130 
131 /*
132  * Remove a device from gpGraphicsDeviceFirst/gpGraphicsDeviceLast list.
133  */
134 _Requires_lock_held_(ghsemGraphicsDeviceList)
135 static
136 VOID
137 EngpUnlinkGraphicsDevice(
138     _In_ PGRAPHICS_DEVICE pToDelete)
139 {
140     PGRAPHICS_DEVICE pPrevGraphicsDevice = NULL;
141     PGRAPHICS_DEVICE pGraphicsDevice = gpGraphicsDeviceFirst;
142 
143     TRACE("EngpUnlinkGraphicsDevice('%S')\n", pToDelete->szNtDeviceName);
144 
145     while (pGraphicsDevice)
146     {
147         if (pGraphicsDevice != pToDelete)
148         {
149             /* Keep current device */
150             pPrevGraphicsDevice = pGraphicsDevice;
151             pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
152         }
153         else
154         {
155             /* At first, link again associated VGA Device */
156             if (pGraphicsDevice->pVgaDevice)
157                 EngpLinkGraphicsDevice(pGraphicsDevice->pVgaDevice);
158 
159             /* We need to remove current device */
160             pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
161 
162             /* Unlink chain */
163             if (!pPrevGraphicsDevice)
164                 gpGraphicsDeviceFirst = pToDelete->pNextGraphicsDevice;
165             else
166                 pPrevGraphicsDevice->pNextGraphicsDevice = pToDelete->pNextGraphicsDevice;
167             if (gpGraphicsDeviceLast == pToDelete)
168                 gpGraphicsDeviceLast = pPrevGraphicsDevice;
169         }
170     }
171 }
172 
173 NTSTATUS
174 EngpUpdateGraphicsDeviceList(VOID)
175 {
176     ULONG iDevNum, iVGACompatible = -1, ulMaxObjectNumber = 0;
177     WCHAR awcDeviceName[20], awcWinDeviceName[20];
178     UNICODE_STRING ustrDeviceName;
179     WCHAR awcBuffer[256];
180     NTSTATUS Status;
181     PGRAPHICS_DEVICE pGraphicsDevice;
182     BOOLEAN bFoundNewDevice = FALSE;
183     ULONG cbValue;
184     HKEY hkey;
185 
186     /* Open the key for the adapters */
187     Status = RegOpenKey(L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO", &hkey);
188     if (!NT_SUCCESS(Status))
189     {
190         ERR("Could not open HARDWARE\\DEVICEMAP\\VIDEO registry key:0x%lx\n", Status);
191         return Status;
192     }
193 
194     /* Read the name of the VGA adapter */
195     cbValue = sizeof(awcDeviceName);
196     Status = RegQueryValue(hkey, L"VgaCompatible", REG_SZ, awcDeviceName, &cbValue);
197     if (NT_SUCCESS(Status))
198     {
199         iVGACompatible = _wtoi(&awcDeviceName[sizeof("\\Device\\Video")-1]);
200         ERR("VGA adapter = %lu\n", iVGACompatible);
201     }
202 
203     /* Get the maximum number of adapters */
204     if (!RegReadDWORD(hkey, L"MaxObjectNumber", &ulMaxObjectNumber))
205     {
206         ERR("Could not read MaxObjectNumber, defaulting to 0.\n");
207     }
208 
209     TRACE("Found %lu devices\n", ulMaxObjectNumber + 1);
210 
211     /* Loop through all adapters */
212     for (iDevNum = 0; iDevNum <= ulMaxObjectNumber; iDevNum++)
213     {
214         /* Create the adapter's key name */
215         swprintf(awcDeviceName, L"\\Device\\Video%lu", iDevNum);
216 
217         /* Create the display device name */
218         swprintf(awcWinDeviceName, L"\\\\.\\DISPLAY%lu", iDevNum + 1);
219         RtlInitUnicodeString(&ustrDeviceName, awcWinDeviceName);
220 
221         /* Check if the device exists already */
222         pGraphicsDevice = EngpFindGraphicsDevice(&ustrDeviceName, iDevNum);
223         if (pGraphicsDevice != NULL)
224         {
225             continue;
226         }
227 
228         /* Read the reg key name */
229         cbValue = sizeof(awcBuffer);
230         Status = RegQueryValue(hkey, awcDeviceName, REG_SZ, awcBuffer, &cbValue);
231         if (!NT_SUCCESS(Status))
232         {
233             ERR("failed to query the registry path:0x%lx\n", Status);
234             continue;
235         }
236 
237         /* Initialize the driver for this device */
238         pGraphicsDevice = InitDisplayDriver(awcDeviceName, awcBuffer);
239         if (!pGraphicsDevice) continue;
240 
241         /* Check if this is a VGA compatible adapter */
242         if (pGraphicsDevice->StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE)
243         {
244             /* Save this as the VGA adapter */
245             if (!gpVgaGraphicsDevice || !EngpHasVgaDriver(gpVgaGraphicsDevice))
246             {
247                 gpVgaGraphicsDevice = pGraphicsDevice;
248                 TRACE("gpVgaGraphicsDevice = %p\n", gpVgaGraphicsDevice);
249             }
250         }
251         bFoundNewDevice = TRUE;
252 
253         /* Set the first one as primary device */
254         if (!gpPrimaryGraphicsDevice || EngpHasVgaDriver(gpPrimaryGraphicsDevice))
255         {
256             gpPrimaryGraphicsDevice = pGraphicsDevice;
257             TRACE("gpPrimaryGraphicsDevice = %p\n", gpPrimaryGraphicsDevice);
258         }
259     }
260 
261     /* Close the device map registry key */
262     ZwClose(hkey);
263 
264     /* Can we link VGA device to primary device? */
265     if (gpPrimaryGraphicsDevice &&
266         gpVgaGraphicsDevice &&
267         gpPrimaryGraphicsDevice != gpVgaGraphicsDevice &&
268         !gpPrimaryGraphicsDevice->pVgaDevice)
269     {
270         /* Yes. Remove VGA device from global list, and attach it to primary device */
271         TRACE("Linking VGA device %S to primary device %S\n", gpVgaGraphicsDevice->szNtDeviceName, gpPrimaryGraphicsDevice->szNtDeviceName);
272         EngpUnlinkGraphicsDevice(gpVgaGraphicsDevice);
273         gpPrimaryGraphicsDevice->pVgaDevice = gpVgaGraphicsDevice;
274     }
275 
276     if (bFoundNewDevice && gbBaseVideo)
277     {
278         PGRAPHICS_DEVICE pToDelete;
279 
280         /* Lock list */
281         EngAcquireSemaphore(ghsemGraphicsDeviceList);
282 
283         /* Remove every device from linked list, except base-video one */
284         pGraphicsDevice = gpGraphicsDeviceFirst;
285         while (pGraphicsDevice)
286         {
287             if (!EngpHasVgaDriver(pGraphicsDevice))
288             {
289                 /* Not base-video device. Remove it */
290                 pToDelete = pGraphicsDevice;
291                 TRACE("Removing non-base-video device %S (%S)\n", pToDelete->szWinDeviceName, pToDelete->szNtDeviceName);
292 
293                 EngpUnlinkGraphicsDevice(pGraphicsDevice);
294                 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
295 
296                 /* Free memory */
297                 ExFreePoolWithTag(pToDelete->pDiplayDrivers, GDITAG_DRVSUP);
298                 ExFreePoolWithTag(pToDelete, GDITAG_GDEVICE);
299             }
300             else
301             {
302                 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
303             }
304         }
305 
306         /* Unlock list */
307         EngReleaseSemaphore(ghsemGraphicsDeviceList);
308     }
309 
310     return STATUS_SUCCESS;
311 }
312 
313 /* Open display settings registry key
314  * Returns NULL in case of error. */
315 static HKEY
316 EngpGetRegistryHandleFromDeviceMap(
317     _In_ PGRAPHICS_DEVICE pGraphicsDevice)
318 {
319     static const PWCHAR KEY_VIDEO = L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO";
320     HKEY hKey;
321     WCHAR szDeviceKey[256];
322     ULONG cbSize;
323     NTSTATUS Status;
324 
325     /* Open the device map registry key */
326     Status = RegOpenKey(KEY_VIDEO, &hKey);
327     if (!NT_SUCCESS(Status))
328     {
329         ERR("Could not open HARDWARE\\DEVICEMAP\\VIDEO registry key: status 0x%08x\n", Status);
330         return NULL;
331     }
332 
333     /* Query the registry path */
334     cbSize = sizeof(szDeviceKey);
335     RegQueryValue(hKey,
336                   pGraphicsDevice->szNtDeviceName,
337                   REG_SZ,
338                   szDeviceKey,
339                   &cbSize);
340     ZwClose(hKey);
341 
342     /* Open the registry key */
343     Status = RegOpenKey(szDeviceKey, &hKey);
344     if (!NT_SUCCESS(Status))
345     {
346         ERR("Could not open registry key '%S': status 0x%08x\n", szDeviceKey, Status);
347         return NULL;
348     }
349 
350     return hKey;
351 }
352 
353 NTSTATUS
354 EngpGetDisplayDriverParameters(
355     _In_ PGRAPHICS_DEVICE pGraphicsDevice,
356     _Out_ PDEVMODEW pdm)
357 {
358     HKEY hKey;
359     NTSTATUS Status;
360     RTL_QUERY_REGISTRY_TABLE DisplaySettingsTable[] =
361     {
362 #define READ(field, str) \
363         { \
364             NULL, \
365             RTL_QUERY_REGISTRY_DIRECT, \
366             L ##str, \
367             &pdm->field, \
368             REG_NONE, NULL, 0 \
369         },
370     READ(dmBitsPerPel, "DefaultSettings.BitsPerPel")
371     READ(dmPelsWidth, "DefaultSettings.XResolution")
372     READ(dmPelsHeight, "DefaultSettings.YResolution")
373     READ(dmDisplayFlags, "DefaultSettings.Flags")
374     READ(dmDisplayFrequency, "DefaultSettings.VRefresh")
375     READ(dmPanningWidth, "DefaultSettings.XPanning")
376     READ(dmPanningHeight, "DefaultSettings.YPanning")
377     READ(dmDisplayOrientation, "DefaultSettings.Orientation")
378     READ(dmDisplayFixedOutput, "DefaultSettings.FixedOutput")
379     READ(dmPosition.x, "Attach.RelativeX")
380     READ(dmPosition.y, "Attach.RelativeY")
381 #undef READ
382         {0}
383     };
384 
385     hKey = EngpGetRegistryHandleFromDeviceMap(pGraphicsDevice);
386     if (!hKey)
387         return STATUS_UNSUCCESSFUL;
388 
389     Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
390                                     (PWSTR)hKey,
391                                     DisplaySettingsTable,
392                                     NULL,
393                                     NULL);
394 
395     ZwClose(hKey);
396     return Status;
397 }
398 
399 DWORD
400 EngpGetDisplayDriverAccelerationLevel(
401     _In_ PGRAPHICS_DEVICE pGraphicsDevice)
402 {
403     HKEY hKey;
404     DWORD dwAccelerationLevel = 0;
405     RTL_QUERY_REGISTRY_TABLE DisplaySettingsTable[] =
406     {
407         {
408             NULL,
409             RTL_QUERY_REGISTRY_DIRECT,
410             L"Acceleration.Level",
411             &dwAccelerationLevel,
412             REG_NONE, NULL, 0
413         },
414         {0}
415     };
416 
417     hKey = EngpGetRegistryHandleFromDeviceMap(pGraphicsDevice);
418     if (!hKey)
419         return 0;
420 
421     RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
422                            (PWSTR)hKey,
423                            DisplaySettingsTable,
424                            NULL,
425                            NULL);
426     ZwClose(hKey);
427 
428     return dwAccelerationLevel;
429 }
430 
431 extern VOID
432 UserRefreshDisplay(IN PPDEVOBJ ppdev);
433 
434 // PVIDEO_WIN32K_CALLOUT
435 VOID
436 NTAPI
437 VideoPortCallout(
438     _In_ PVOID Params)
439 {
440 /*
441  * IMPORTANT NOTICE!! On Windows XP/2003 this function triggers the creation of
442  * a specific VideoPortCalloutThread() system thread using the same mechanism
443  * as the RIT/desktop/Ghost system threads.
444  */
445 
446     PVIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams = (PVIDEO_WIN32K_CALLBACKS_PARAMS)Params;
447 
448     TRACE("VideoPortCallout(0x%p, 0x%x)\n",
449           CallbackParams, CallbackParams ? CallbackParams->CalloutType : -1);
450 
451     if (!CallbackParams)
452         return;
453 
454     switch (CallbackParams->CalloutType)
455     {
456         case VideoFindAdapterCallout:
457         {
458             TRACE("VideoPortCallout: VideoFindAdapterCallout called - Param = %s\n",
459                   CallbackParams->Param ? "TRUE" : "FALSE");
460             if (CallbackParams->Param == TRUE)
461             {
462                 /* Re-enable the display */
463                 UserRefreshDisplay(gpmdev->ppdevGlobal);
464             }
465             else
466             {
467                 /* Disable the display */
468                 NOTHING; // Nothing to do for the moment...
469             }
470 
471             CallbackParams->Status = STATUS_SUCCESS;
472             break;
473         }
474 
475         case VideoPowerNotifyCallout:
476         case VideoDisplaySwitchCallout:
477         case VideoEnumChildPdoNotifyCallout:
478         case VideoWakeupCallout:
479         case VideoChangeDisplaySettingsCallout:
480         case VideoPnpNotifyCallout:
481         case VideoDxgkDisplaySwitchCallout:
482         case VideoDxgkMonitorEventCallout:
483         case VideoDxgkFindAdapterTdrCallout:
484             ERR("VideoPortCallout: CalloutType 0x%x is UNIMPLEMENTED!\n", CallbackParams->CalloutType);
485             CallbackParams->Status = STATUS_NOT_IMPLEMENTED;
486             break;
487 
488         default:
489             ERR("VideoPortCallout: Unknown CalloutType 0x%x\n", CallbackParams->CalloutType);
490             CallbackParams->Status = STATUS_UNSUCCESSFUL;
491             break;
492     }
493 }
494 
495 PGRAPHICS_DEVICE
496 NTAPI
497 EngpRegisterGraphicsDevice(
498     _In_ PUNICODE_STRING pustrDeviceName,
499     _In_ PUNICODE_STRING pustrDiplayDrivers,
500     _In_ PUNICODE_STRING pustrDescription)
501 {
502     PGRAPHICS_DEVICE pGraphicsDevice;
503     PDEVICE_OBJECT pDeviceObject;
504     PFILE_OBJECT pFileObject;
505     NTSTATUS Status;
506     VIDEO_WIN32K_CALLBACKS Win32kCallbacks;
507     ULONG ulReturn;
508     PWSTR pwsz;
509     ULONG cj;
510 
511     TRACE("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName);
512 
513     /* Allocate a GRAPHICS_DEVICE structure */
514     pGraphicsDevice = ExAllocatePoolZero(PagedPool,
515                                          sizeof(GRAPHICS_DEVICE),
516                                          GDITAG_GDEVICE);
517     if (!pGraphicsDevice)
518     {
519         ERR("ExAllocatePoolWithTag failed\n");
520         return NULL;
521     }
522 
523     /* Try to open and enable the device */
524     Status = IoGetDeviceObjectPointer(pustrDeviceName,
525                                       FILE_READ_DATA | FILE_WRITE_DATA,
526                                       &pFileObject,
527                                       &pDeviceObject);
528     if (!NT_SUCCESS(Status))
529     {
530         ERR("Could not open device %wZ, 0x%lx\n", pustrDeviceName, Status);
531         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
532         return NULL;
533     }
534 
535     /* Copy the device and file object pointers */
536     pGraphicsDevice->DeviceObject = pDeviceObject;
537     pGraphicsDevice->FileObject = pFileObject;
538 
539     /* Initialize and register the device with videoprt for Win32k callbacks */
540     Win32kCallbacks.PhysDisp = pGraphicsDevice;
541     Win32kCallbacks.Callout = VideoPortCallout;
542     // Reset the data being returned prior to the call.
543     Win32kCallbacks.bACPI = FALSE;
544     Win32kCallbacks.pPhysDeviceObject = NULL;
545     Win32kCallbacks.DualviewFlags = 0;
546     Status = (NTSTATUS)EngDeviceIoControl((HANDLE)pDeviceObject,
547                                           IOCTL_VIDEO_INIT_WIN32K_CALLBACKS,
548                                           &Win32kCallbacks,
549                                           sizeof(Win32kCallbacks),
550                                           &Win32kCallbacks,
551                                           sizeof(Win32kCallbacks),
552                                           &ulReturn);
553     if (Status != ERROR_SUCCESS)
554     {
555         ERR("EngDeviceIoControl(0x%p, IOCTL_VIDEO_INIT_WIN32K_CALLBACKS) failed, Status 0x%lx\n",
556             pDeviceObject, Status);
557     }
558     // TODO: Set flags according to the results.
559     // if (Win32kCallbacks.bACPI)
560     // if (Win32kCallbacks.DualviewFlags & ???)
561     pGraphicsDevice->PhysDeviceHandle = Win32kCallbacks.pPhysDeviceObject;
562 
563     /* FIXME: Enumerate children monitor devices for this video adapter
564      *
565      * - Force the adapter to re-enumerate its monitors:
566      *   IoSynchronousInvalidateDeviceRelations(pdo, BusRelations)
567      *
568      * - Retrieve all monitor PDOs from VideoPrt:
569      *   EngDeviceIoControl(0x%p, IOCTL_VIDEO_ENUM_MONITOR_PDO)
570      *
571      * - Initialize these fields and structures accordingly:
572      *   pGraphicsDevice->dwMonCnt
573      *   pGraphicsDevice->pvMonDev[0..dwMonCnt-1]
574      */
575 
576     /* Copy the device name */
577     RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName,
578                       sizeof(pGraphicsDevice->szNtDeviceName),
579                       pustrDeviceName->Buffer,
580                       pustrDeviceName->Length);
581 
582     /* Create a Win32 device name (FIXME: virtual devices!) */
583     RtlStringCbPrintfW(pGraphicsDevice->szWinDeviceName,
584                        sizeof(pGraphicsDevice->szWinDeviceName),
585                        L"\\\\.\\DISPLAY%d",
586                        (int)giDevNum);
587 
588     /* Allocate a buffer for the strings */
589     cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR);
590     pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP);
591     if (!pwsz)
592     {
593         ERR("Could not allocate string buffer\n");
594         ASSERT(FALSE); // FIXME
595         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
596         return NULL;
597     }
598 
599     /* Copy the display driver names */
600     pGraphicsDevice->pDiplayDrivers = pwsz;
601     RtlCopyMemory(pGraphicsDevice->pDiplayDrivers,
602                   pustrDiplayDrivers->Buffer,
603                   pustrDiplayDrivers->Length);
604 
605     /* Copy the description */
606     pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR);
607     RtlCopyMemory(pGraphicsDevice->pwszDescription,
608                   pustrDescription->Buffer,
609                   pustrDescription->Length);
610     pGraphicsDevice->pwszDescription[pustrDescription->Length/sizeof(WCHAR)] = 0;
611 
612     /* Lock loader */
613     EngAcquireSemaphore(ghsemGraphicsDeviceList);
614 
615     /* Insert the device into the global list */
616     EngpLinkGraphicsDevice(pGraphicsDevice);
617 
618     /* Increment the device number */
619     giDevNum++;
620 
621     /* Unlock loader */
622     EngReleaseSemaphore(ghsemGraphicsDeviceList);
623     TRACE("Prepared %lu modes for %ls\n", pGraphicsDevice->cDevModes, pGraphicsDevice->pwszDescription);
624 
625     /* HACK: already in graphic mode; display wallpaper on this new display */
626     if (ScreenDeviceContext)
627     {
628         UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
629         UNICODE_STRING DisplayName;
630         HDC hdc;
631         RtlInitUnicodeString(&DisplayName, pGraphicsDevice->szWinDeviceName);
632         hdc = IntGdiCreateDC(&DriverName, &DisplayName, NULL, NULL, FALSE);
633         IntPaintDesktop(hdc);
634     }
635 
636     return pGraphicsDevice;
637 }
638 
639 PGRAPHICS_DEVICE
640 NTAPI
641 EngpFindGraphicsDevice(
642     _In_opt_ PUNICODE_STRING pustrDevice,
643     _In_ ULONG iDevNum)
644 {
645     UNICODE_STRING ustrCurrent;
646     PGRAPHICS_DEVICE pGraphicsDevice;
647     ULONG i;
648     TRACE("EngpFindGraphicsDevice('%wZ', %lu)\n",
649            pustrDevice, iDevNum);
650 
651     /* Lock list */
652     EngAcquireSemaphore(ghsemGraphicsDeviceList);
653 
654     if (pustrDevice && pustrDevice->Buffer)
655     {
656         /* Find specified video adapter by name */
657         for (pGraphicsDevice = gpGraphicsDeviceFirst;
658              pGraphicsDevice;
659              pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
660         {
661             /* Compare the device name */
662             RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
663             if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
664             {
665                 break;
666             }
667         }
668 
669         if (pGraphicsDevice)
670         {
671             /* Validate selected monitor number */
672 #if 0
673             if (iDevNum >= pGraphicsDevice->dwMonCnt)
674                 pGraphicsDevice = NULL;
675 #else
676             /* FIXME: dwMonCnt not initialized, see EngpRegisterGraphicsDevice */
677 #endif
678         }
679     }
680     else
681     {
682         /* Select video adapter by device number */
683         for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
684              pGraphicsDevice && i < iDevNum;
685              pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
686     }
687 
688     /* Unlock list */
689     EngReleaseSemaphore(ghsemGraphicsDeviceList);
690 
691     return pGraphicsDevice;
692 }
693 
694 static
695 NTSTATUS
696 EngpFileIoRequest(
697     _In_ PFILE_OBJECT pFileObject,
698     _In_ ULONG ulMajorFunction,
699     _In_reads_(nBufferSize) PVOID lpBuffer,
700     _In_ SIZE_T nBufferSize,
701     _In_ ULONGLONG ullStartOffset,
702     _Out_ PULONG_PTR lpInformation)
703 {
704     PDEVICE_OBJECT pDeviceObject;
705     KEVENT Event;
706     PIRP pIrp;
707     IO_STATUS_BLOCK Iosb;
708     NTSTATUS Status;
709     LARGE_INTEGER liStartOffset;
710 
711     /* Get corresponding device object */
712     pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
713     if (!pDeviceObject)
714     {
715         return STATUS_INVALID_PARAMETER;
716     }
717 
718     /* Initialize an event */
719     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
720 
721     /* Build IRP */
722     liStartOffset.QuadPart = ullStartOffset;
723     pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
724                                         pDeviceObject,
725                                         lpBuffer,
726                                         (ULONG)nBufferSize,
727                                         &liStartOffset,
728                                         &Event,
729                                         &Iosb);
730     if (!pIrp)
731     {
732         return STATUS_INSUFFICIENT_RESOURCES;
733     }
734 
735     /* Call the driver */
736     Status = IoCallDriver(pDeviceObject, pIrp);
737 
738     /* Wait if neccessary */
739     if (STATUS_PENDING == Status)
740     {
741         KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
742         Status = Iosb.Status;
743     }
744 
745     /* Return information to the caller about the operation. */
746     *lpInformation = Iosb.Information;
747 
748     /* Return NTSTATUS */
749     return Status;
750 }
751 
752 VOID
753 APIENTRY
754 EngFileWrite(
755     _In_ PFILE_OBJECT pFileObject,
756     _In_reads_(nLength) PVOID lpBuffer,
757     _In_ SIZE_T nLength,
758     _Out_ PSIZE_T lpBytesWritten)
759 {
760     NTSTATUS status;
761 
762     status = EngpFileIoRequest(pFileObject,
763                                IRP_MJ_WRITE,
764                                lpBuffer,
765                                nLength,
766                                0,
767                                lpBytesWritten);
768     if (!NT_SUCCESS(status))
769     {
770         *lpBytesWritten = 0;
771     }
772 }
773 
774 _Success_(return>=0)
775 NTSTATUS
776 APIENTRY
777 EngFileIoControl(
778     _In_ PFILE_OBJECT pFileObject,
779     _In_ DWORD dwIoControlCode,
780     _In_reads_(nInBufferSize) PVOID lpInBuffer,
781     _In_ SIZE_T nInBufferSize,
782     _Out_writes_(nOutBufferSize) PVOID lpOutBuffer,
783     _In_ SIZE_T nOutBufferSize,
784     _Out_ PULONG_PTR lpInformation)
785 {
786     PDEVICE_OBJECT pDeviceObject;
787     KEVENT Event;
788     PIRP pIrp;
789     IO_STATUS_BLOCK Iosb;
790     NTSTATUS Status;
791 
792     /* Get corresponding device object */
793     pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
794     if (!pDeviceObject)
795     {
796         return STATUS_INVALID_PARAMETER;
797     }
798 
799     /* Initialize an event */
800     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
801 
802     /* Build IO control IRP */
803     pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
804                                          pDeviceObject,
805                                          lpInBuffer,
806                                          (ULONG)nInBufferSize,
807                                          lpOutBuffer,
808                                          (ULONG)nOutBufferSize,
809                                          FALSE,
810                                          &Event,
811                                          &Iosb);
812     if (!pIrp)
813     {
814         return STATUS_INSUFFICIENT_RESOURCES;
815     }
816 
817     /* Call the driver */
818     Status = IoCallDriver(pDeviceObject, pIrp);
819 
820     /* Wait if neccessary */
821     if (Status == STATUS_PENDING)
822     {
823         KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
824         Status = Iosb.Status;
825     }
826 
827     /* Return information to the caller about the operation. */
828     *lpInformation = Iosb.Information;
829 
830     /* This function returns NTSTATUS */
831     return Status;
832 }
833 
834 /*
835  * @implemented
836  */
837 _Success_(return==0)
838 DWORD
839 APIENTRY
840 EngDeviceIoControl(
841     _In_ HANDLE hDevice,
842     _In_ DWORD dwIoControlCode,
843     _In_reads_bytes_opt_(cjInBufferSize) LPVOID lpInBuffer,
844     _In_ DWORD cjInBufferSize,
845     _Out_writes_bytes_opt_(cjOutBufferSize) LPVOID lpOutBuffer,
846     _In_ DWORD cjOutBufferSize,
847     _Out_ LPDWORD lpBytesReturned)
848 {
849     PIRP Irp;
850     NTSTATUS Status;
851     KEVENT Event;
852     IO_STATUS_BLOCK Iosb;
853     PDEVICE_OBJECT DeviceObject;
854 
855     TRACE("EngDeviceIoControl() called\n");
856 
857     if (!hDevice)
858     {
859         return ERROR_INVALID_HANDLE;
860     }
861 
862     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
863 
864     DeviceObject = (PDEVICE_OBJECT) hDevice;
865 
866     Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
867                                         DeviceObject,
868                                         lpInBuffer,
869                                         cjInBufferSize,
870                                         lpOutBuffer,
871                                         cjOutBufferSize,
872                                         FALSE,
873                                         &Event,
874                                         &Iosb);
875     if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
876 
877     Status = IoCallDriver(DeviceObject, Irp);
878 
879     if (Status == STATUS_PENDING)
880     {
881         (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
882         Status = Iosb.Status;
883     }
884 
885     TRACE("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
886            Iosb.Information);
887 
888     /* Return information to the caller about the operation. */
889     *lpBytesReturned = (DWORD)Iosb.Information;
890 
891     /* Convert NT status values to win32 error codes. */
892     switch (Status)
893     {
894         case STATUS_INSUFFICIENT_RESOURCES:
895             return ERROR_NOT_ENOUGH_MEMORY;
896 
897         case STATUS_BUFFER_OVERFLOW:
898             return ERROR_MORE_DATA;
899 
900         case STATUS_NOT_IMPLEMENTED:
901             return ERROR_INVALID_FUNCTION;
902 
903         case STATUS_INVALID_PARAMETER:
904             return ERROR_INVALID_PARAMETER;
905 
906         case STATUS_BUFFER_TOO_SMALL:
907             return ERROR_INSUFFICIENT_BUFFER;
908 
909         case STATUS_DEVICE_DOES_NOT_EXIST:
910             return ERROR_DEV_NOT_EXIST;
911 
912         case STATUS_PENDING:
913             return ERROR_IO_PENDING;
914     }
915 
916     return Status;
917 }
918 
919 /* EOF */
920