xref: /reactos/win32ss/gdi/eng/device.c (revision 2b933529)
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 BOOLEAN
36 EngpPopulateDeviceModeList(
37     _Inout_ PGRAPHICS_DEVICE pGraphicsDevice,
38     _In_ PDEVMODEW pdmDefault)
39 {
40     PWSTR pwsz;
41     PLDEVOBJ pldev;
42     PDEVMODEINFO pdminfo;
43     PDEVMODEW pdm, pdmEnd;
44     ULONG i, cModes = 0;
45     BOOLEAN bModeMatch = FALSE;
46 
47     ASSERT(pGraphicsDevice->pdevmodeInfo == NULL);
48     ASSERT(pGraphicsDevice->pDevModeList == NULL);
49 
50     pwsz = pGraphicsDevice->pDiplayDrivers;
51 
52     /* Loop through the driver names
53      * This is a REG_MULTI_SZ string */
54     for (; *pwsz; pwsz += wcslen(pwsz) + 1)
55     {
56         /* Try to load the display driver */
57         TRACE("Trying driver: %ls\n", pwsz);
58         pldev = EngLoadImageEx(pwsz, LDEV_DEVICE_DISPLAY);
59         if (!pldev)
60         {
61             ERR("Could not load driver: '%ls'\n", pwsz);
62             continue;
63         }
64 
65         /* Get the mode list from the driver */
66         pdminfo = LDEVOBJ_pdmiGetModes(pldev, pGraphicsDevice->DeviceObject);
67         if (!pdminfo)
68         {
69             ERR("Could not get mode list for '%ls'\n", pwsz);
70             continue;
71         }
72 
73         /* Attach the mode info to the device */
74         pdminfo->pdmiNext = pGraphicsDevice->pdevmodeInfo;
75         pGraphicsDevice->pdevmodeInfo = pdminfo;
76 
77         /* Loop all DEVMODEs */
78         pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
79         for (pdm = pdminfo->adevmode;
80              (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
81              pdm = (DEVMODEW*)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
82         {
83             /* Count this DEVMODE */
84             cModes++;
85 
86             /* Some drivers like the VBox driver don't fill the dmDeviceName
87                with the name of the display driver. So fix that here. */
88             RtlStringCbCopyW(pdm->dmDeviceName, sizeof(pdm->dmDeviceName), pwsz);
89         }
90 
91         // FIXME: release the driver again until it's used?
92     }
93 
94     if (!pGraphicsDevice->pdevmodeInfo || cModes == 0)
95     {
96         ERR("No devmodes\n");
97         return FALSE;
98     }
99 
100     /* Allocate an index buffer */
101     pGraphicsDevice->cDevModes = cModes;
102     pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool,
103                                                           cModes * sizeof(DEVMODEENTRY),
104                                                           GDITAG_GDEVICE);
105     if (!pGraphicsDevice->pDevModeList)
106     {
107         ERR("No devmode list\n");
108         return FALSE;
109     }
110 
111     TRACE("Looking for mode %lux%lux%lu(%lu Hz)\n",
112         pdmDefault->dmPelsWidth,
113         pdmDefault->dmPelsHeight,
114         pdmDefault->dmBitsPerPel,
115         pdmDefault->dmDisplayFrequency);
116 
117     /* Loop through all DEVMODEINFOs */
118     for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0;
119          pdminfo;
120          pdminfo = pdminfo->pdmiNext)
121     {
122         /* Calculate End of the DEVMODEs */
123         pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
124 
125         /* Loop through the DEVMODEs */
126         for (pdm = pdminfo->adevmode;
127              (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
128              pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
129         {
130             TRACE("    %S has mode %lux%lux%lu(%lu Hz)\n",
131                   pdm->dmDeviceName,
132                   pdm->dmPelsWidth,
133                   pdm->dmPelsHeight,
134                   pdm->dmBitsPerPel,
135                   pdm->dmDisplayFrequency);
136             /* Compare with the default entry */
137             if (!bModeMatch &&
138                 pdm->dmBitsPerPel == pdmDefault->dmBitsPerPel &&
139                 pdm->dmPelsWidth == pdmDefault->dmPelsWidth &&
140                 pdm->dmPelsHeight == pdmDefault->dmPelsHeight)
141             {
142                 pGraphicsDevice->iDefaultMode = i;
143                 pGraphicsDevice->iCurrentMode = i;
144                 TRACE("Found default entry: %lu '%ls'\n", i, pdm->dmDeviceName);
145                 if (pdm->dmDisplayFrequency == pdmDefault->dmDisplayFrequency)
146                 {
147                     /* Uh oh, even the display frequency matches. */
148                     bModeMatch = TRUE;
149                 }
150             }
151 
152             /* Initialize the entry */
153             pGraphicsDevice->pDevModeList[i].dwFlags = 0;
154             pGraphicsDevice->pDevModeList[i].pdm = pdm;
155             i++;
156         }
157     }
158     return TRUE;
159 }
160 
161 extern VOID
162 UserRefreshDisplay(IN PPDEVOBJ ppdev);
163 
164 // PVIDEO_WIN32K_CALLOUT
165 VOID
166 NTAPI
167 VideoPortCallout(
168     _In_ PVOID Params)
169 {
170 /*
171  * IMPORTANT NOTICE!! On Windows XP/2003 this function triggers the creation of
172  * a specific VideoPortCalloutThread() system thread using the same mechanism
173  * as the RIT/desktop/Ghost system threads.
174  */
175 
176     PVIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams = (PVIDEO_WIN32K_CALLBACKS_PARAMS)Params;
177 
178     TRACE("VideoPortCallout(0x%p, 0x%x)\n",
179           CallbackParams, CallbackParams ? CallbackParams->CalloutType : -1);
180 
181     if (!CallbackParams)
182         return;
183 
184     switch (CallbackParams->CalloutType)
185     {
186         case VideoFindAdapterCallout:
187         {
188             TRACE("VideoPortCallout: VideoFindAdapterCallout called - Param = %s\n",
189                   CallbackParams->Param ? "TRUE" : "FALSE");
190             if (CallbackParams->Param == TRUE)
191             {
192                 /* Re-enable the display */
193                 UserRefreshDisplay(gppdevPrimary);
194             }
195             else
196             {
197                 /* Disable the display */
198                 NOTHING; // Nothing to do for the moment...
199             }
200 
201             CallbackParams->Status = STATUS_SUCCESS;
202             break;
203         }
204 
205         case VideoPowerNotifyCallout:
206         case VideoDisplaySwitchCallout:
207         case VideoEnumChildPdoNotifyCallout:
208         case VideoWakeupCallout:
209         case VideoChangeDisplaySettingsCallout:
210         case VideoPnpNotifyCallout:
211         case VideoDxgkDisplaySwitchCallout:
212         case VideoDxgkMonitorEventCallout:
213         case VideoDxgkFindAdapterTdrCallout:
214             ERR("VideoPortCallout: CalloutType 0x%x is UNIMPLEMENTED!\n", CallbackParams->CalloutType);
215             CallbackParams->Status = STATUS_NOT_IMPLEMENTED;
216             break;
217 
218         default:
219             ERR("VideoPortCallout: Unknown CalloutType 0x%x\n", CallbackParams->CalloutType);
220             CallbackParams->Status = STATUS_UNSUCCESSFUL;
221             break;
222     }
223 }
224 
225 PGRAPHICS_DEVICE
226 NTAPI
227 EngpRegisterGraphicsDevice(
228     _In_ PUNICODE_STRING pustrDeviceName,
229     _In_ PUNICODE_STRING pustrDiplayDrivers,
230     _In_ PUNICODE_STRING pustrDescription,
231     _In_ PDEVMODEW pdmDefault)
232 {
233     PGRAPHICS_DEVICE pGraphicsDevice;
234     PDEVICE_OBJECT pDeviceObject;
235     PFILE_OBJECT pFileObject;
236     NTSTATUS Status;
237     VIDEO_WIN32K_CALLBACKS Win32kCallbacks;
238     ULONG ulReturn;
239     PWSTR pwsz;
240     ULONG cj;
241 
242     TRACE("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName);
243 
244     /* Allocate a GRAPHICS_DEVICE structure */
245     pGraphicsDevice = ExAllocatePoolWithTag(PagedPool,
246                                             sizeof(GRAPHICS_DEVICE),
247                                             GDITAG_GDEVICE);
248     if (!pGraphicsDevice)
249     {
250         ERR("ExAllocatePoolWithTag failed\n");
251         return NULL;
252     }
253 
254     /* Try to open and enable the device */
255     Status = IoGetDeviceObjectPointer(pustrDeviceName,
256                                       FILE_READ_DATA | FILE_WRITE_DATA,
257                                       &pFileObject,
258                                       &pDeviceObject);
259     if (!NT_SUCCESS(Status))
260     {
261         ERR("Could not open device %wZ, 0x%lx\n", pustrDeviceName, Status);
262         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
263         return NULL;
264     }
265 
266     /* Copy the device and file object pointers */
267     pGraphicsDevice->DeviceObject = pDeviceObject;
268     pGraphicsDevice->FileObject = pFileObject;
269 
270     /* Initialize and register the device with videoprt for Win32k callbacks */
271     Win32kCallbacks.PhysDisp = pGraphicsDevice;
272     Win32kCallbacks.Callout = VideoPortCallout;
273     // Reset the data being returned prior to the call.
274     Win32kCallbacks.bACPI = FALSE;
275     Win32kCallbacks.pPhysDeviceObject = NULL;
276     Win32kCallbacks.DualviewFlags = 0;
277     Status = (NTSTATUS)EngDeviceIoControl((HANDLE)pDeviceObject,
278                                           IOCTL_VIDEO_INIT_WIN32K_CALLBACKS,
279                                           &Win32kCallbacks,
280                                           sizeof(Win32kCallbacks),
281                                           &Win32kCallbacks,
282                                           sizeof(Win32kCallbacks),
283                                           &ulReturn);
284     if (Status != ERROR_SUCCESS)
285     {
286         ERR("EngDeviceIoControl(0x%p, IOCTL_VIDEO_INIT_WIN32K_CALLBACKS) failed, Status 0x%lx\n",
287             pDeviceObject, Status);
288     }
289     // TODO: Set flags according to the results.
290     // if (Win32kCallbacks.bACPI)
291     // if (Win32kCallbacks.DualviewFlags & ???)
292     // Win32kCallbacks.pPhysDeviceObject;
293 
294     /* Copy the device name */
295     RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName,
296                       sizeof(pGraphicsDevice->szNtDeviceName),
297                       pustrDeviceName->Buffer,
298                       pustrDeviceName->Length);
299 
300     /* Create a Win32 device name (FIXME: virtual devices!) */
301     RtlStringCbPrintfW(pGraphicsDevice->szWinDeviceName,
302                        sizeof(pGraphicsDevice->szWinDeviceName),
303                        L"\\\\.\\DISPLAY%d",
304                        (int)giDevNum);
305 
306     /* Allocate a buffer for the strings */
307     cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR);
308     pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP);
309     if (!pwsz)
310     {
311         ERR("Could not allocate string buffer\n");
312         ASSERT(FALSE); // FIXME
313         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
314         return NULL;
315     }
316 
317     /* Copy the display driver names */
318     pGraphicsDevice->pDiplayDrivers = pwsz;
319     RtlCopyMemory(pGraphicsDevice->pDiplayDrivers,
320                   pustrDiplayDrivers->Buffer,
321                   pustrDiplayDrivers->Length);
322 
323     /* Copy the description */
324     pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR);
325     RtlCopyMemory(pGraphicsDevice->pwszDescription,
326                   pustrDescription->Buffer,
327                   pustrDescription->Length);
328     pGraphicsDevice->pwszDescription[pustrDescription->Length/sizeof(WCHAR)] = 0;
329 
330     /* Initialize the pdevmodeInfo list and default index  */
331     pGraphicsDevice->pdevmodeInfo = NULL;
332     pGraphicsDevice->iDefaultMode = 0;
333     pGraphicsDevice->iCurrentMode = 0;
334 
335     // FIXME: initialize state flags
336     pGraphicsDevice->StateFlags = 0;
337 
338     /* Create the mode list */
339     pGraphicsDevice->pDevModeList = NULL;
340     if (!EngpPopulateDeviceModeList(pGraphicsDevice, pdmDefault))
341     {
342         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
343         return NULL;
344     }
345 
346     /* Lock loader */
347     EngAcquireSemaphore(ghsemGraphicsDeviceList);
348 
349     /* Insert the device into the global list */
350     pGraphicsDevice->pNextGraphicsDevice = NULL;
351     if (gpGraphicsDeviceLast)
352         gpGraphicsDeviceLast->pNextGraphicsDevice = pGraphicsDevice;
353     gpGraphicsDeviceLast = pGraphicsDevice;
354     if (!gpGraphicsDeviceFirst)
355         gpGraphicsDeviceFirst = pGraphicsDevice;
356 
357     /* Increment the device number */
358     giDevNum++;
359 
360     /* Unlock loader */
361     EngReleaseSemaphore(ghsemGraphicsDeviceList);
362     TRACE("Prepared %lu modes for %ls\n", pGraphicsDevice->cDevModes, pGraphicsDevice->pwszDescription);
363 
364     return pGraphicsDevice;
365 }
366 
367 PGRAPHICS_DEVICE
368 NTAPI
369 EngpFindGraphicsDevice(
370     _In_opt_ PUNICODE_STRING pustrDevice,
371     _In_ ULONG iDevNum,
372     _In_ DWORD dwFlags)
373 {
374     UNICODE_STRING ustrCurrent;
375     PGRAPHICS_DEVICE pGraphicsDevice;
376     ULONG i;
377     TRACE("EngpFindGraphicsDevice('%wZ', %lu, 0x%lx)\n",
378            pustrDevice, iDevNum, dwFlags);
379 
380     /* Lock list */
381     EngAcquireSemaphore(ghsemGraphicsDeviceList);
382 
383     if (pustrDevice && pustrDevice->Buffer)
384     {
385         /* Loop through the list of devices */
386         for (pGraphicsDevice = gpGraphicsDeviceFirst;
387              pGraphicsDevice;
388              pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
389         {
390             /* Compare the device name */
391             RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
392             if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
393             {
394                 break;
395             }
396         }
397     }
398     else
399     {
400         /* Loop through the list of devices */
401         for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
402              pGraphicsDevice && i < iDevNum;
403              pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
404     }
405 
406     /* Unlock list */
407     EngReleaseSemaphore(ghsemGraphicsDeviceList);
408 
409     return pGraphicsDevice;
410 }
411 
412 static
413 NTSTATUS
414 EngpFileIoRequest(
415     _In_ PFILE_OBJECT pFileObject,
416     _In_ ULONG ulMajorFunction,
417     _In_reads_(nBufferSize) PVOID lpBuffer,
418     _In_ SIZE_T nBufferSize,
419     _In_ ULONGLONG ullStartOffset,
420     _Out_ PULONG_PTR lpInformation)
421 {
422     PDEVICE_OBJECT pDeviceObject;
423     KEVENT Event;
424     PIRP pIrp;
425     IO_STATUS_BLOCK Iosb;
426     NTSTATUS Status;
427     LARGE_INTEGER liStartOffset;
428 
429     /* Get corresponding device object */
430     pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
431     if (!pDeviceObject)
432     {
433         return STATUS_INVALID_PARAMETER;
434     }
435 
436     /* Initialize an event */
437     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
438 
439     /* Build IRP */
440     liStartOffset.QuadPart = ullStartOffset;
441     pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
442                                         pDeviceObject,
443                                         lpBuffer,
444                                         (ULONG)nBufferSize,
445                                         &liStartOffset,
446                                         &Event,
447                                         &Iosb);
448     if (!pIrp)
449     {
450         return STATUS_INSUFFICIENT_RESOURCES;
451     }
452 
453     /* Call the driver */
454     Status = IoCallDriver(pDeviceObject, pIrp);
455 
456     /* Wait if neccessary */
457     if (STATUS_PENDING == Status)
458     {
459         KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
460         Status = Iosb.Status;
461     }
462 
463     /* Return information to the caller about the operation. */
464     *lpInformation = Iosb.Information;
465 
466     /* Return NTSTATUS */
467     return Status;
468 }
469 
470 VOID
471 APIENTRY
472 EngFileWrite(
473     _In_ PFILE_OBJECT pFileObject,
474     _In_reads_(nLength) PVOID lpBuffer,
475     _In_ SIZE_T nLength,
476     _Out_ PSIZE_T lpBytesWritten)
477 {
478     NTSTATUS status;
479 
480     status = EngpFileIoRequest(pFileObject,
481                                IRP_MJ_WRITE,
482                                lpBuffer,
483                                nLength,
484                                0,
485                                lpBytesWritten);
486     if (!NT_SUCCESS(status))
487     {
488         *lpBytesWritten = 0;
489     }
490 }
491 
492 _Success_(return>=0)
493 NTSTATUS
494 APIENTRY
495 EngFileIoControl(
496     _In_ PFILE_OBJECT pFileObject,
497     _In_ DWORD dwIoControlCode,
498     _In_reads_(nInBufferSize) PVOID lpInBuffer,
499     _In_ SIZE_T nInBufferSize,
500     _Out_writes_(nOutBufferSize) PVOID lpOutBuffer,
501     _In_ SIZE_T nOutBufferSize,
502     _Out_ PULONG_PTR lpInformation)
503 {
504     PDEVICE_OBJECT pDeviceObject;
505     KEVENT Event;
506     PIRP pIrp;
507     IO_STATUS_BLOCK Iosb;
508     NTSTATUS Status;
509 
510     /* Get corresponding device object */
511     pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
512     if (!pDeviceObject)
513     {
514         return STATUS_INVALID_PARAMETER;
515     }
516 
517     /* Initialize an event */
518     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
519 
520     /* Build IO control IRP */
521     pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
522                                          pDeviceObject,
523                                          lpInBuffer,
524                                          (ULONG)nInBufferSize,
525                                          lpOutBuffer,
526                                          (ULONG)nOutBufferSize,
527                                          FALSE,
528                                          &Event,
529                                          &Iosb);
530     if (!pIrp)
531     {
532         return STATUS_INSUFFICIENT_RESOURCES;
533     }
534 
535     /* Call the driver */
536     Status = IoCallDriver(pDeviceObject, pIrp);
537 
538     /* Wait if neccessary */
539     if (Status == STATUS_PENDING)
540     {
541         KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
542         Status = Iosb.Status;
543     }
544 
545     /* Return information to the caller about the operation. */
546     *lpInformation = Iosb.Information;
547 
548     /* This function returns NTSTATUS */
549     return Status;
550 }
551 
552 /*
553  * @implemented
554  */
555 _Success_(return==0)
556 DWORD
557 APIENTRY
558 EngDeviceIoControl(
559     _In_ HANDLE hDevice,
560     _In_ DWORD dwIoControlCode,
561     _In_reads_bytes_opt_(cjInBufferSize) LPVOID lpInBuffer,
562     _In_ DWORD cjInBufferSize,
563     _Out_writes_bytes_opt_(cjOutBufferSize) LPVOID lpOutBuffer,
564     _In_ DWORD cjOutBufferSize,
565     _Out_ LPDWORD lpBytesReturned)
566 {
567     PIRP Irp;
568     NTSTATUS Status;
569     KEVENT Event;
570     IO_STATUS_BLOCK Iosb;
571     PDEVICE_OBJECT DeviceObject;
572 
573     TRACE("EngDeviceIoControl() called\n");
574 
575     if (!hDevice)
576     {
577         return ERROR_INVALID_HANDLE;
578     }
579 
580     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
581 
582     DeviceObject = (PDEVICE_OBJECT) hDevice;
583 
584     Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
585                                         DeviceObject,
586                                         lpInBuffer,
587                                         cjInBufferSize,
588                                         lpOutBuffer,
589                                         cjOutBufferSize,
590                                         FALSE,
591                                         &Event,
592                                         &Iosb);
593     if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
594 
595     Status = IoCallDriver(DeviceObject, Irp);
596 
597     if (Status == STATUS_PENDING)
598     {
599         (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
600         Status = Iosb.Status;
601     }
602 
603     TRACE("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
604            Iosb.Information);
605 
606     /* Return information to the caller about the operation. */
607     *lpBytesReturned = (DWORD)Iosb.Information;
608 
609     /* Convert NT status values to win32 error codes. */
610     switch (Status)
611     {
612         case STATUS_INSUFFICIENT_RESOURCES:
613             return ERROR_NOT_ENOUGH_MEMORY;
614 
615         case STATUS_BUFFER_OVERFLOW:
616             return ERROR_MORE_DATA;
617 
618         case STATUS_NOT_IMPLEMENTED:
619             return ERROR_INVALID_FUNCTION;
620 
621         case STATUS_INVALID_PARAMETER:
622             return ERROR_INVALID_PARAMETER;
623 
624         case STATUS_BUFFER_TOO_SMALL:
625             return ERROR_INSUFFICIENT_BUFFER;
626 
627         case STATUS_DEVICE_DOES_NOT_EXIST:
628             return ERROR_DEV_NOT_EXIST;
629 
630         case STATUS_PENDING:
631             return ERROR_IO_PENDING;
632     }
633 
634     return Status;
635 }
636 
637 /* EOF */
638