xref: /reactos/win32ss/gdi/eng/device.c (revision c2c66aff)
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 DBG_DEFAULT_CHANNEL(EngDev)
12 
13 PGRAPHICS_DEVICE gpPrimaryGraphicsDevice;
14 PGRAPHICS_DEVICE gpVgaGraphicsDevice;
15 
16 static PGRAPHICS_DEVICE gpGraphicsDeviceFirst = NULL;
17 static PGRAPHICS_DEVICE gpGraphicsDeviceLast = NULL;
18 static HSEMAPHORE ghsemGraphicsDeviceList;
19 static ULONG giDevNum = 1;
20 
21 INIT_FUNCTION
22 NTSTATUS
23 NTAPI
24 InitDeviceImpl(VOID)
25 {
26     ghsemGraphicsDeviceList = EngCreateSemaphore();
27     if (!ghsemGraphicsDeviceList)
28         return STATUS_INSUFFICIENT_RESOURCES;
29 
30     return STATUS_SUCCESS;
31 }
32 
33 BOOLEAN
34 EngpPopulateDeviceModeList(
35     _Inout_ PGRAPHICS_DEVICE pGraphicsDevice,
36     _In_ PDEVMODEW pdmDefault)
37 {
38     PWSTR pwsz;
39     PLDEVOBJ pldev;
40     PDEVMODEINFO pdminfo;
41     PDEVMODEW pdm, pdmEnd;
42     ULONG i, cModes = 0;
43     BOOLEAN bModeMatch = FALSE;
44 
45     ASSERT(pGraphicsDevice->pdevmodeInfo == NULL);
46     ASSERT(pGraphicsDevice->pDevModeList == NULL);
47 
48     pwsz = pGraphicsDevice->pDiplayDrivers;
49 
50     /* Loop through the driver names
51      * This is a REG_MULTI_SZ string */
52     for (; *pwsz; pwsz += wcslen(pwsz) + 1)
53     {
54         TRACE("trying driver: %ls\n", pwsz);
55         /* Try to load the display driver */
56         pldev = EngLoadImageEx(pwsz, LDEV_DEVICE_DISPLAY);
57         if (!pldev)
58         {
59             ERR("Could not load driver: '%ls'\n", pwsz);
60             continue;
61         }
62 
63         /* Get the mode list from the driver */
64         pdminfo = LDEVOBJ_pdmiGetModes(pldev, pGraphicsDevice->DeviceObject);
65         if (!pdminfo)
66         {
67             ERR("Could not get mode list for '%ls'\n", pwsz);
68             continue;
69         }
70 
71         /* Attach the mode info to the device */
72         pdminfo->pdmiNext = pGraphicsDevice->pdevmodeInfo;
73         pGraphicsDevice->pdevmodeInfo = pdminfo;
74 
75         /* Loop all DEVMODEs */
76         pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
77         for (pdm = pdminfo->adevmode;
78              (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
79              pdm = (DEVMODEW*)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
80         {
81             /* Count this DEVMODE */
82             cModes++;
83 
84             /* Some drivers like the VBox driver don't fill the dmDeviceName
85                with the name of the display driver. So fix that here. */
86             wcsncpy(pdm->dmDeviceName, pwsz, CCHDEVICENAME);
87             pdm->dmDeviceName[CCHDEVICENAME - 1] = 0;
88         }
89 
90         // FIXME: release the driver again until it's used?
91     }
92 
93     if (!pGraphicsDevice->pdevmodeInfo || cModes == 0)
94     {
95         ERR("No devmodes\n");
96         return FALSE;
97     }
98 
99     /* Allocate an index buffer */
100     pGraphicsDevice->cDevModes = cModes;
101     pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool,
102                                                           cModes * sizeof(DEVMODEENTRY),
103                                                           GDITAG_GDEVICE);
104     if (!pGraphicsDevice->pDevModeList)
105     {
106         ERR("No devmode list\n");
107         return FALSE;
108     }
109 
110     TRACE("Looking for mode %lux%lux%lu(%lu Hz)\n",
111         pdmDefault->dmPelsWidth,
112         pdmDefault->dmPelsHeight,
113         pdmDefault->dmBitsPerPel,
114         pdmDefault->dmDisplayFrequency);
115 
116     /* Loop through all DEVMODEINFOs */
117     for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0;
118          pdminfo;
119          pdminfo = pdminfo->pdmiNext)
120     {
121         /* Calculate End of the DEVMODEs */
122         pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
123 
124         /* Loop through the DEVMODEs */
125         for (pdm = pdminfo->adevmode;
126              (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
127              pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
128         {
129             TRACE("    %S has mode %lux%lux%lu(%lu Hz)\n",
130                   pdm->dmDeviceName,
131                   pdm->dmPelsWidth,
132                   pdm->dmPelsHeight,
133                   pdm->dmBitsPerPel,
134                   pdm->dmDisplayFrequency);
135             /* Compare with the default entry */
136             if (!bModeMatch &&
137                 pdm->dmBitsPerPel == pdmDefault->dmBitsPerPel &&
138                 pdm->dmPelsWidth == pdmDefault->dmPelsWidth &&
139                 pdm->dmPelsHeight == pdmDefault->dmPelsHeight)
140             {
141                 pGraphicsDevice->iDefaultMode = i;
142                 pGraphicsDevice->iCurrentMode = i;
143                 TRACE("Found default entry: %lu '%ls'\n", i, pdm->dmDeviceName);
144                 if (pdm->dmDisplayFrequency == pdmDefault->dmDisplayFrequency)
145                 {
146                     /* Uh oh, even the display frequency matches. */
147                     bModeMatch = TRUE;
148                 }
149             }
150 
151             /* Initialize the entry */
152             pGraphicsDevice->pDevModeList[i].dwFlags = 0;
153             pGraphicsDevice->pDevModeList[i].pdm = pdm;
154             i++;
155         }
156     }
157     return TRUE;
158 }
159 
160 PGRAPHICS_DEVICE
161 NTAPI
162 EngpRegisterGraphicsDevice(
163     _In_ PUNICODE_STRING pustrDeviceName,
164     _In_ PUNICODE_STRING pustrDiplayDrivers,
165     _In_ PUNICODE_STRING pustrDescription,
166     _In_ PDEVMODEW pdmDefault)
167 {
168     PGRAPHICS_DEVICE pGraphicsDevice;
169     PDEVICE_OBJECT pDeviceObject;
170     PFILE_OBJECT pFileObject;
171     NTSTATUS Status;
172     PWSTR pwsz;
173     ULONG cj;
174     SIZE_T cjWritten;
175     BOOL bEnable = TRUE;
176 
177     TRACE("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName);
178 
179     /* Allocate a GRAPHICS_DEVICE structure */
180     pGraphicsDevice = ExAllocatePoolWithTag(PagedPool,
181                                             sizeof(GRAPHICS_DEVICE),
182                                             GDITAG_GDEVICE);
183     if (!pGraphicsDevice)
184     {
185         ERR("ExAllocatePoolWithTag failed\n");
186         return NULL;
187     }
188 
189     /* Try to open the driver */
190     Status = IoGetDeviceObjectPointer(pustrDeviceName,
191                                       FILE_READ_DATA | FILE_WRITE_DATA,
192                                       &pFileObject,
193                                       &pDeviceObject);
194     if (!NT_SUCCESS(Status))
195     {
196         ERR("Could not open driver %wZ, 0x%lx\n", pustrDeviceName, Status);
197         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
198         return NULL;
199     }
200 
201     /* Enable the device */
202     EngFileWrite(pFileObject, &bEnable, sizeof(BOOL), &cjWritten);
203 
204     /* Copy the device and file object pointers */
205     pGraphicsDevice->DeviceObject = pDeviceObject;
206     pGraphicsDevice->FileObject = pFileObject;
207 
208     /* Copy device name */
209     RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName,
210                      sizeof(pGraphicsDevice->szNtDeviceName),
211                      pustrDeviceName->Buffer,
212                      pustrDeviceName->Length);
213 
214     /* Create a win device name (FIXME: virtual devices!) */
215     swprintf(pGraphicsDevice->szWinDeviceName, L"\\\\.\\DISPLAY%d", (int)giDevNum);
216 
217     /* Allocate a buffer for the strings */
218     cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR);
219     pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP);
220     if (!pwsz)
221     {
222         ERR("Could not allocate string buffer\n");
223         ASSERT(FALSE); // FIXME
224         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
225         return NULL;
226     }
227 
228     /* Copy display driver names */
229     pGraphicsDevice->pDiplayDrivers = pwsz;
230     RtlCopyMemory(pGraphicsDevice->pDiplayDrivers,
231                   pustrDiplayDrivers->Buffer,
232                   pustrDiplayDrivers->Length);
233 
234     /* Copy description */
235     pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR);
236     RtlCopyMemory(pGraphicsDevice->pwszDescription,
237                   pustrDescription->Buffer,
238                   pustrDescription->Length);
239     pGraphicsDevice->pwszDescription[pustrDescription->Length/sizeof(WCHAR)] = 0;
240 
241     /* Initialize the pdevmodeInfo list and default index  */
242     pGraphicsDevice->pdevmodeInfo = NULL;
243     pGraphicsDevice->iDefaultMode = 0;
244     pGraphicsDevice->iCurrentMode = 0;
245 
246     // FIXME: initialize state flags
247     pGraphicsDevice->StateFlags = 0;
248 
249     /* Create the mode list */
250     pGraphicsDevice->pDevModeList = NULL;
251     if (!EngpPopulateDeviceModeList(pGraphicsDevice, pdmDefault))
252     {
253         ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
254         return NULL;
255     }
256 
257     /* Lock loader */
258     EngAcquireSemaphore(ghsemGraphicsDeviceList);
259 
260     /* Insert the device into the global list */
261     pGraphicsDevice->pNextGraphicsDevice = NULL;
262     if (gpGraphicsDeviceLast)
263         gpGraphicsDeviceLast->pNextGraphicsDevice = pGraphicsDevice;
264     gpGraphicsDeviceLast = pGraphicsDevice;
265     if (!gpGraphicsDeviceFirst)
266         gpGraphicsDeviceFirst = pGraphicsDevice;
267 
268     /* Increment device number */
269     giDevNum++;
270 
271     /* Unlock loader */
272     EngReleaseSemaphore(ghsemGraphicsDeviceList);
273     TRACE("Prepared %lu modes for %ls\n", pGraphicsDevice->cDevModes, pGraphicsDevice->pwszDescription);
274 
275     return pGraphicsDevice;
276 }
277 
278 
279 PGRAPHICS_DEVICE
280 NTAPI
281 EngpFindGraphicsDevice(
282     _In_opt_ PUNICODE_STRING pustrDevice,
283     _In_ ULONG iDevNum,
284     _In_ DWORD dwFlags)
285 {
286     UNICODE_STRING ustrCurrent;
287     PGRAPHICS_DEVICE pGraphicsDevice;
288     ULONG i;
289     TRACE("EngpFindGraphicsDevice('%wZ', %lu, 0x%lx)\n",
290            pustrDevice, iDevNum, dwFlags);
291 
292     /* Lock list */
293     EngAcquireSemaphore(ghsemGraphicsDeviceList);
294 
295     if (pustrDevice && pustrDevice->Buffer)
296     {
297         /* Loop through the list of devices */
298         for (pGraphicsDevice = gpGraphicsDeviceFirst;
299              pGraphicsDevice;
300              pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
301         {
302             /* Compare the device name */
303             RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
304             if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
305             {
306                 break;
307             }
308         }
309     }
310     else
311     {
312         /* Loop through the list of devices */
313         for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
314              pGraphicsDevice && i < iDevNum;
315              pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
316     }
317 
318     /* Unlock list */
319     EngReleaseSemaphore(ghsemGraphicsDeviceList);
320 
321     return pGraphicsDevice;
322 }
323 
324 
325 static
326 NTSTATUS
327 EngpFileIoRequest(
328     _In_ PFILE_OBJECT pFileObject,
329     _In_ ULONG ulMajorFunction,
330     _In_reads_(nBufferSize) PVOID lpBuffer,
331     _In_ SIZE_T nBufferSize,
332     _In_ ULONGLONG ullStartOffset,
333     _Out_ PULONG_PTR lpInformation)
334 {
335     PDEVICE_OBJECT pDeviceObject;
336     KEVENT Event;
337     PIRP pIrp;
338     IO_STATUS_BLOCK Iosb;
339     NTSTATUS Status;
340     LARGE_INTEGER liStartOffset;
341 
342     /* Get corresponding device object */
343     pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
344     if (!pDeviceObject)
345     {
346         return STATUS_INVALID_PARAMETER;
347     }
348 
349     /* Initialize an event */
350     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
351 
352     /* Build IRP */
353     liStartOffset.QuadPart = ullStartOffset;
354     pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
355                                         pDeviceObject,
356                                         lpBuffer,
357                                         (ULONG)nBufferSize,
358                                         &liStartOffset,
359                                         &Event,
360                                         &Iosb);
361     if (!pIrp)
362     {
363         return STATUS_INSUFFICIENT_RESOURCES;
364     }
365 
366     /* Call the driver */
367     Status = IoCallDriver(pDeviceObject, pIrp);
368 
369     /* Wait if neccessary */
370     if (STATUS_PENDING == Status)
371     {
372         KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
373         Status = Iosb.Status;
374     }
375 
376     /* Return information to the caller about the operation. */
377     *lpInformation = Iosb.Information;
378 
379     /* Return NTSTATUS */
380     return Status;
381 }
382 
383 VOID
384 APIENTRY
385 EngFileWrite(
386     _In_ PFILE_OBJECT pFileObject,
387     _In_reads_(nLength) PVOID lpBuffer,
388     _In_ SIZE_T nLength,
389     _Out_ PSIZE_T lpBytesWritten)
390 {
391     NTSTATUS status;
392 
393     status = EngpFileIoRequest(pFileObject,
394                                IRP_MJ_WRITE,
395                                lpBuffer,
396                                nLength,
397                                0,
398                                lpBytesWritten);
399     if (!NT_SUCCESS(status))
400     {
401         *lpBytesWritten = 0;
402     }
403 }
404 
405 _Success_(return>=0)
406 NTSTATUS
407 APIENTRY
408 EngFileIoControl(
409     _In_ PFILE_OBJECT pFileObject,
410     _In_ DWORD dwIoControlCode,
411     _In_reads_(nInBufferSize) PVOID lpInBuffer,
412     _In_ SIZE_T nInBufferSize,
413     _Out_writes_(nOutBufferSize) PVOID lpOutBuffer,
414     _In_ SIZE_T nOutBufferSize,
415     _Out_ PULONG_PTR lpInformation)
416 {
417     PDEVICE_OBJECT pDeviceObject;
418     KEVENT Event;
419     PIRP pIrp;
420     IO_STATUS_BLOCK Iosb;
421     NTSTATUS Status;
422 
423     /* Get corresponding device object */
424     pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
425     if (!pDeviceObject)
426     {
427         return STATUS_INVALID_PARAMETER;
428     }
429 
430     /* Initialize an event */
431     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
432 
433     /* Build IO control IRP */
434     pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
435                                          pDeviceObject,
436                                          lpInBuffer,
437                                          (ULONG)nInBufferSize,
438                                          lpOutBuffer,
439                                          (ULONG)nOutBufferSize,
440                                          FALSE,
441                                          &Event,
442                                          &Iosb);
443     if (!pIrp)
444     {
445         return STATUS_INSUFFICIENT_RESOURCES;
446     }
447 
448     /* Call the driver */
449     Status = IoCallDriver(pDeviceObject, pIrp);
450 
451     /* Wait if neccessary */
452     if (Status == STATUS_PENDING)
453     {
454         KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
455         Status = Iosb.Status;
456     }
457 
458     /* Return information to the caller about the operation. */
459     *lpInformation = Iosb.Information;
460 
461     /* This function returns NTSTATUS */
462     return Status;
463 }
464 
465 /*
466  * @implemented
467  */
468 _Success_(return==0)
469 DWORD
470 APIENTRY
471 EngDeviceIoControl(
472     _In_ HANDLE hDevice,
473     _In_ DWORD dwIoControlCode,
474     _In_reads_bytes_opt_(cjInBufferSize) LPVOID lpInBuffer,
475     _In_ DWORD cjInBufferSize,
476     _Out_writes_bytes_opt_(cjOutBufferSize) LPVOID lpOutBuffer,
477     _In_ DWORD cjOutBufferSize,
478     _Out_ LPDWORD lpBytesReturned)
479 {
480     PIRP Irp;
481     NTSTATUS Status;
482     KEVENT Event;
483     IO_STATUS_BLOCK Iosb;
484     PDEVICE_OBJECT DeviceObject;
485 
486     TRACE("EngDeviceIoControl() called\n");
487 
488     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
489 
490     DeviceObject = (PDEVICE_OBJECT) hDevice;
491 
492     Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
493                                         DeviceObject,
494                                         lpInBuffer,
495                                         cjInBufferSize,
496                                         lpOutBuffer,
497                                         cjOutBufferSize,
498                                         FALSE,
499                                         &Event,
500                                         &Iosb);
501     if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
502 
503     Status = IoCallDriver(DeviceObject, Irp);
504 
505     if (Status == STATUS_PENDING)
506     {
507         (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
508         Status = Iosb.Status;
509     }
510 
511     TRACE("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
512            Iosb.Information);
513 
514     /* Return information to the caller about the operation. */
515     *lpBytesReturned = (DWORD)Iosb.Information;
516 
517     /* Convert NT status values to win32 error codes. */
518     switch (Status)
519     {
520         case STATUS_INSUFFICIENT_RESOURCES:
521             return ERROR_NOT_ENOUGH_MEMORY;
522 
523         case STATUS_BUFFER_OVERFLOW:
524             return ERROR_MORE_DATA;
525 
526         case STATUS_NOT_IMPLEMENTED:
527             return ERROR_INVALID_FUNCTION;
528 
529         case STATUS_INVALID_PARAMETER:
530             return ERROR_INVALID_PARAMETER;
531 
532         case STATUS_BUFFER_TOO_SMALL:
533             return ERROR_INSUFFICIENT_BUFFER;
534 
535         case STATUS_DEVICE_DOES_NOT_EXIST:
536             return ERROR_DEV_NOT_EXIST;
537 
538         case STATUS_PENDING:
539             return ERROR_IO_PENDING;
540     }
541 
542     return Status;
543 }
544 
545 /* EOF */
546