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