6d20e30de5509f548334b2cd8a8e64ee7cac0b22
[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 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 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 */