[INTRIN]
[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: subsys/win32k/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()
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 PUNICODE_STRING pustrDeviceName,
38 PUNICODE_STRING pustrDiplayDrivers,
39 PUNICODE_STRING pustrDescription,
40 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 PUNICODE_STRING pustrDevice,
263 ULONG iDevNum,
264 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 PFILE_OBJECT pFileObject,
309 ULONG ulMajorFunction,
310 LPVOID lpBuffer,
311 SIZE_T nBufferSize,
312 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 PVOID lpBuffer,
368 IN SIZE_T nLength,
369 IN PSIZE_T lpBytesWritten)
370 {
371 EngpFileIoRequest(pFileObject,
372 IRP_MJ_WRITE,
373 lpBuffer,
374 nLength,
375 0,
376 lpBytesWritten);
377 }
378
379 NTSTATUS
380 APIENTRY
381 EngFileIoControl(
382 IN PFILE_OBJECT pFileObject,
383 IN DWORD dwIoControlCode,
384 IN PVOID lpInBuffer,
385 IN SIZE_T nInBufferSize,
386 OUT PVOID lpOutBuffer,
387 IN SIZE_T nOutBufferSize,
388 OUT PULONG_PTR lpInformation)
389 {
390 PDEVICE_OBJECT pDeviceObject;
391 KEVENT Event;
392 PIRP pIrp;
393 IO_STATUS_BLOCK Iosb;
394 NTSTATUS Status;
395
396 /* Get corresponding device object */
397 pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
398 if (!pDeviceObject)
399 {
400 return STATUS_INVALID_PARAMETER;
401 }
402
403 /* Initialize an event */
404 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
405
406 /* Build IO control IRP */
407 pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
408 pDeviceObject,
409 lpInBuffer,
410 (ULONG)nInBufferSize,
411 lpOutBuffer,
412 (ULONG)nOutBufferSize,
413 FALSE,
414 &Event,
415 &Iosb);
416 if (!pIrp)
417 {
418 return STATUS_INSUFFICIENT_RESOURCES;
419 }
420
421 /* Call the driver */
422 Status = IoCallDriver(pDeviceObject, pIrp);
423
424 /* Wait if neccessary */
425 if (Status == STATUS_PENDING)
426 {
427 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
428 Status = Iosb.Status;
429 }
430
431 /* Return information to the caller about the operation. */
432 *lpInformation = Iosb.Information;
433
434 /* This function returns NTSTATUS */
435 return Status;
436 }
437
438 /*
439 * @implemented
440 */
441 DWORD APIENTRY
442 EngDeviceIoControl(
443 _In_ HANDLE hDevice,
444 _In_ DWORD dwIoControlCode,
445 _In_opt_bytecount_(cjInBufferSize) LPVOID lpInBuffer,
446 _In_ DWORD cjInBufferSize,
447 _Out_opt_bytecap_(cjOutBufferSize) LPVOID lpOutBuffer,
448 _In_ DWORD cjOutBufferSize,
449 _Out_ LPDWORD lpBytesReturned)
450 {
451 PIRP Irp;
452 NTSTATUS Status;
453 KEVENT Event;
454 IO_STATUS_BLOCK Iosb;
455 PDEVICE_OBJECT DeviceObject;
456
457 TRACE("EngDeviceIoControl() called\n");
458
459 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
460
461 DeviceObject = (PDEVICE_OBJECT) hDevice;
462
463 Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
464 DeviceObject,
465 lpInBuffer,
466 cjInBufferSize,
467 lpOutBuffer,
468 cjOutBufferSize,
469 FALSE,
470 &Event,
471 &Iosb);
472 if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
473
474 Status = IoCallDriver(DeviceObject, Irp);
475
476 if (Status == STATUS_PENDING)
477 {
478 (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
479 Status = Iosb.Status;
480 }
481
482 TRACE("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
483 Iosb.Information);
484
485 /* Return information to the caller about the operation. */
486 *lpBytesReturned = (DWORD)Iosb.Information;
487
488 /* Convert NT status values to win32 error codes. */
489 switch (Status)
490 {
491 case STATUS_INSUFFICIENT_RESOURCES:
492 return ERROR_NOT_ENOUGH_MEMORY;
493
494 case STATUS_BUFFER_OVERFLOW:
495 return ERROR_MORE_DATA;
496
497 case STATUS_NOT_IMPLEMENTED:
498 return ERROR_INVALID_FUNCTION;
499
500 case STATUS_INVALID_PARAMETER:
501 return ERROR_INVALID_PARAMETER;
502
503 case STATUS_BUFFER_TOO_SMALL:
504 return ERROR_INSUFFICIENT_BUFFER;
505
506 case STATUS_DEVICE_DOES_NOT_EXIST:
507 return ERROR_DEV_NOT_EXIST;
508
509 case STATUS_PENDING:
510 return ERROR_IO_PENDING;
511 }
512
513 return Status;
514 }
515
516 /* EOF */