7691aad92f5dd04782f2af73ef8edd0e0cdbf95a
[reactos.git] / win32ss / gdi / eng / device.c
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 /* Try to load the display driver */
55 TRACE("Trying driver: %ls\n", pwsz);
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 RtlStringCbCopyW(pdm->dmDeviceName, sizeof(pdm->dmDeviceName), pwsz);
87 }
88
89 // FIXME: release the driver again until it's used?
90 }
91
92 if (!pGraphicsDevice->pdevmodeInfo || cModes == 0)
93 {
94 ERR("No devmodes\n");
95 return FALSE;
96 }
97
98 /* Allocate an index buffer */
99 pGraphicsDevice->cDevModes = cModes;
100 pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool,
101 cModes * sizeof(DEVMODEENTRY),
102 GDITAG_GDEVICE);
103 if (!pGraphicsDevice->pDevModeList)
104 {
105 ERR("No devmode list\n");
106 return FALSE;
107 }
108
109 TRACE("Looking for mode %lux%lux%lu(%lu Hz)\n",
110 pdmDefault->dmPelsWidth,
111 pdmDefault->dmPelsHeight,
112 pdmDefault->dmBitsPerPel,
113 pdmDefault->dmDisplayFrequency);
114
115 /* Loop through all DEVMODEINFOs */
116 for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0;
117 pdminfo;
118 pdminfo = pdminfo->pdmiNext)
119 {
120 /* Calculate End of the DEVMODEs */
121 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
122
123 /* Loop through the DEVMODEs */
124 for (pdm = pdminfo->adevmode;
125 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
126 pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
127 {
128 TRACE(" %S has mode %lux%lux%lu(%lu Hz)\n",
129 pdm->dmDeviceName,
130 pdm->dmPelsWidth,
131 pdm->dmPelsHeight,
132 pdm->dmBitsPerPel,
133 pdm->dmDisplayFrequency);
134 /* Compare with the default entry */
135 if (!bModeMatch &&
136 pdm->dmBitsPerPel == pdmDefault->dmBitsPerPel &&
137 pdm->dmPelsWidth == pdmDefault->dmPelsWidth &&
138 pdm->dmPelsHeight == pdmDefault->dmPelsHeight)
139 {
140 pGraphicsDevice->iDefaultMode = i;
141 pGraphicsDevice->iCurrentMode = i;
142 TRACE("Found default entry: %lu '%ls'\n", i, pdm->dmDeviceName);
143 if (pdm->dmDisplayFrequency == pdmDefault->dmDisplayFrequency)
144 {
145 /* Uh oh, even the display frequency matches. */
146 bModeMatch = TRUE;
147 }
148 }
149
150 /* Initialize the entry */
151 pGraphicsDevice->pDevModeList[i].dwFlags = 0;
152 pGraphicsDevice->pDevModeList[i].pdm = pdm;
153 i++;
154 }
155 }
156 return TRUE;
157 }
158
159 PGRAPHICS_DEVICE
160 NTAPI
161 EngpRegisterGraphicsDevice(
162 _In_ PUNICODE_STRING pustrDeviceName,
163 _In_ PUNICODE_STRING pustrDiplayDrivers,
164 _In_ PUNICODE_STRING pustrDescription,
165 _In_ PDEVMODEW pdmDefault)
166 {
167 PGRAPHICS_DEVICE pGraphicsDevice;
168 PDEVICE_OBJECT pDeviceObject;
169 PFILE_OBJECT pFileObject;
170 NTSTATUS Status;
171 PWSTR pwsz;
172 ULONG cj;
173 SIZE_T cjWritten;
174 BOOL bEnable = TRUE;
175
176 TRACE("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName);
177
178 /* Allocate a GRAPHICS_DEVICE structure */
179 pGraphicsDevice = ExAllocatePoolWithTag(PagedPool,
180 sizeof(GRAPHICS_DEVICE),
181 GDITAG_GDEVICE);
182 if (!pGraphicsDevice)
183 {
184 ERR("ExAllocatePoolWithTag failed\n");
185 return NULL;
186 }
187
188 /* Try to open and enable the device */
189 Status = IoGetDeviceObjectPointer(pustrDeviceName,
190 FILE_READ_DATA | FILE_WRITE_DATA,
191 &pFileObject,
192 &pDeviceObject);
193 if (!NT_SUCCESS(Status))
194 {
195 ERR("Could not open device %wZ, 0x%lx\n", pustrDeviceName, Status);
196 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
197 return NULL;
198 }
199
200 /* Enable the device */
201 EngFileWrite(pFileObject, &bEnable, sizeof(BOOL), &cjWritten);
202
203 /* Copy the device and file object pointers */
204 pGraphicsDevice->DeviceObject = pDeviceObject;
205 pGraphicsDevice->FileObject = pFileObject;
206
207 /* Copy the device name */
208 RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName,
209 sizeof(pGraphicsDevice->szNtDeviceName),
210 pustrDeviceName->Buffer,
211 pustrDeviceName->Length);
212
213 /* Create a Win32 device name (FIXME: virtual devices!) */
214 RtlStringCbPrintfW(pGraphicsDevice->szWinDeviceName,
215 sizeof(pGraphicsDevice->szWinDeviceName),
216 L"\\\\.\\DISPLAY%d",
217 (int)giDevNum);
218
219 /* Allocate a buffer for the strings */
220 cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR);
221 pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP);
222 if (!pwsz)
223 {
224 ERR("Could not allocate string buffer\n");
225 ASSERT(FALSE); // FIXME
226 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
227 return NULL;
228 }
229
230 /* Copy the display driver names */
231 pGraphicsDevice->pDiplayDrivers = pwsz;
232 RtlCopyMemory(pGraphicsDevice->pDiplayDrivers,
233 pustrDiplayDrivers->Buffer,
234 pustrDiplayDrivers->Length);
235
236 /* Copy the description */
237 pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR);
238 RtlCopyMemory(pGraphicsDevice->pwszDescription,
239 pustrDescription->Buffer,
240 pustrDescription->Length);
241 pGraphicsDevice->pwszDescription[pustrDescription->Length/sizeof(WCHAR)] = 0;
242
243 /* Initialize the pdevmodeInfo list and default index */
244 pGraphicsDevice->pdevmodeInfo = NULL;
245 pGraphicsDevice->iDefaultMode = 0;
246 pGraphicsDevice->iCurrentMode = 0;
247
248 // FIXME: initialize state flags
249 pGraphicsDevice->StateFlags = 0;
250
251 /* Create the mode list */
252 pGraphicsDevice->pDevModeList = NULL;
253 if (!EngpPopulateDeviceModeList(pGraphicsDevice, pdmDefault))
254 {
255 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
256 return NULL;
257 }
258
259 /* Lock loader */
260 EngAcquireSemaphore(ghsemGraphicsDeviceList);
261
262 /* Insert the device into the global list */
263 pGraphicsDevice->pNextGraphicsDevice = NULL;
264 if (gpGraphicsDeviceLast)
265 gpGraphicsDeviceLast->pNextGraphicsDevice = pGraphicsDevice;
266 gpGraphicsDeviceLast = pGraphicsDevice;
267 if (!gpGraphicsDeviceFirst)
268 gpGraphicsDeviceFirst = pGraphicsDevice;
269
270 /* Increment the device number */
271 giDevNum++;
272
273 /* Unlock loader */
274 EngReleaseSemaphore(ghsemGraphicsDeviceList);
275 TRACE("Prepared %lu modes for %ls\n", pGraphicsDevice->cDevModes, pGraphicsDevice->pwszDescription);
276
277 return pGraphicsDevice;
278 }
279
280 PGRAPHICS_DEVICE
281 NTAPI
282 EngpFindGraphicsDevice(
283 _In_opt_ PUNICODE_STRING pustrDevice,
284 _In_ ULONG iDevNum,
285 _In_ DWORD dwFlags)
286 {
287 UNICODE_STRING ustrCurrent;
288 PGRAPHICS_DEVICE pGraphicsDevice;
289 ULONG i;
290 TRACE("EngpFindGraphicsDevice('%wZ', %lu, 0x%lx)\n",
291 pustrDevice, iDevNum, dwFlags);
292
293 /* Lock list */
294 EngAcquireSemaphore(ghsemGraphicsDeviceList);
295
296 if (pustrDevice && pustrDevice->Buffer)
297 {
298 /* Loop through the list of devices */
299 for (pGraphicsDevice = gpGraphicsDeviceFirst;
300 pGraphicsDevice;
301 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
302 {
303 /* Compare the device name */
304 RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
305 if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
306 {
307 break;
308 }
309 }
310 }
311 else
312 {
313 /* Loop through the list of devices */
314 for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
315 pGraphicsDevice && i < iDevNum;
316 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
317 }
318
319 /* Unlock list */
320 EngReleaseSemaphore(ghsemGraphicsDeviceList);
321
322 return pGraphicsDevice;
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 if (!hDevice)
489 {
490 return ERROR_INVALID_HANDLE;
491 }
492
493 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
494
495 DeviceObject = (PDEVICE_OBJECT) hDevice;
496
497 Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
498 DeviceObject,
499 lpInBuffer,
500 cjInBufferSize,
501 lpOutBuffer,
502 cjOutBufferSize,
503 FALSE,
504 &Event,
505 &Iosb);
506 if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
507
508 Status = IoCallDriver(DeviceObject, Irp);
509
510 if (Status == STATUS_PENDING)
511 {
512 (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
513 Status = Iosb.Status;
514 }
515
516 TRACE("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
517 Iosb.Information);
518
519 /* Return information to the caller about the operation. */
520 *lpBytesReturned = (DWORD)Iosb.Information;
521
522 /* Convert NT status values to win32 error codes. */
523 switch (Status)
524 {
525 case STATUS_INSUFFICIENT_RESOURCES:
526 return ERROR_NOT_ENOUGH_MEMORY;
527
528 case STATUS_BUFFER_OVERFLOW:
529 return ERROR_MORE_DATA;
530
531 case STATUS_NOT_IMPLEMENTED:
532 return ERROR_INVALID_FUNCTION;
533
534 case STATUS_INVALID_PARAMETER:
535 return ERROR_INVALID_PARAMETER;
536
537 case STATUS_BUFFER_TOO_SMALL:
538 return ERROR_INSUFFICIENT_BUFFER;
539
540 case STATUS_DEVICE_DOES_NOT_EXIST:
541 return ERROR_DEV_NOT_EXIST;
542
543 case STATUS_PENDING:
544 return ERROR_IO_PENDING;
545 }
546
547 return Status;
548 }
549
550 /* EOF */