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