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