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