[REACTOS]
[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
12 #define NDEBUG
13 #include <debug.h>
14
15 PGRAPHICS_DEVICE gpPrimaryGraphicsDevice;
16 PGRAPHICS_DEVICE gpVgaGraphicsDevice;
17
18 static PGRAPHICS_DEVICE gpGraphicsDeviceFirst = NULL;
19 static PGRAPHICS_DEVICE gpGraphicsDeviceLast = NULL;
20 static HSEMAPHORE ghsemGraphicsDeviceList;
21 static ULONG giDevNum = 1;
22
23 INIT_FUNCTION
24 NTSTATUS
25 NTAPI
26 InitDeviceImpl()
27 {
28 ghsemGraphicsDeviceList = EngCreateSemaphore();
29 if (!ghsemGraphicsDeviceList)
30 return STATUS_INSUFFICIENT_RESOURCES;
31
32 return STATUS_SUCCESS;
33 }
34
35
36 PGRAPHICS_DEVICE
37 NTAPI
38 EngpRegisterGraphicsDevice(
39 PUNICODE_STRING pustrDeviceName,
40 PUNICODE_STRING pustrDiplayDrivers,
41 PUNICODE_STRING pustrDescription,
42 PDEVMODEW pdmDefault)
43 {
44 PGRAPHICS_DEVICE pGraphicsDevice;
45 PDEVICE_OBJECT pDeviceObject;
46 PFILE_OBJECT pFileObject;
47 NTSTATUS Status;
48 PWSTR pwsz;
49 ULONG i, cj, cModes = 0;
50 SIZE_T cjWritten;
51 BOOL bEnable = TRUE;
52 PDEVMODEINFO pdminfo;
53 PDEVMODEW pdm, pdmEnd;
54 PLDEVOBJ pldev;
55
56 DPRINT("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName);
57
58 /* Allocate a GRAPHICS_DEVICE structure */
59 pGraphicsDevice = ExAllocatePoolWithTag(PagedPool,
60 sizeof(GRAPHICS_DEVICE),
61 GDITAG_GDEVICE);
62 if (!pGraphicsDevice)
63 {
64 DPRINT1("ExAllocatePoolWithTag failed\n");
65 return NULL;
66 }
67
68 /* Try to open the driver */
69 Status = IoGetDeviceObjectPointer(pustrDeviceName,
70 FILE_READ_DATA | FILE_WRITE_DATA,
71 &pFileObject,
72 &pDeviceObject);
73 if (!NT_SUCCESS(Status))
74 {
75 DPRINT1("Could not open driver %wZ, 0x%lx\n", pustrDeviceName, Status);
76 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
77 return NULL;
78 }
79
80 /* Enable the device */
81 EngFileWrite(pFileObject, &bEnable, sizeof(BOOL), &cjWritten);
82
83 /* Copy the device and file object pointers */
84 pGraphicsDevice->DeviceObject = pDeviceObject;
85 pGraphicsDevice->FileObject = pFileObject;
86
87 /* Copy device name */
88 RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName,
89 sizeof(pGraphicsDevice->szNtDeviceName),
90 pustrDeviceName->Buffer,
91 pustrDeviceName->Length);
92
93 /* Create a win device name (FIXME: virtual devices!) */
94 swprintf(pGraphicsDevice->szWinDeviceName, L"\\\\.\\DISPLAY%d", (int)giDevNum);
95
96 /* Allocate a buffer for the strings */
97 cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR);
98 pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP);
99 if (!pwsz)
100 {
101 DPRINT1("Could not allocate string buffer\n");
102 ASSERT(FALSE); // FIXME
103 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
104 return NULL;
105 }
106
107 /* Copy display driver names */
108 pGraphicsDevice->pDiplayDrivers = pwsz;
109 RtlCopyMemory(pGraphicsDevice->pDiplayDrivers,
110 pustrDiplayDrivers->Buffer,
111 pustrDiplayDrivers->Length);
112
113 /* Copy description */
114 pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR);
115 RtlCopyMemory(pGraphicsDevice->pwszDescription,
116 pustrDescription->Buffer,
117 pustrDescription->Length + sizeof(WCHAR));
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 DPRINT("trying driver: %ls\n", pwsz);
132 /* Try to load the display driver */
133 pldev = EngLoadImageEx(pwsz, LDEV_DEVICE_DISPLAY);
134 if (!pldev)
135 {
136 DPRINT1("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 DPRINT1("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 DPRINT1("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 DPRINT1("No devmode list\n");
185 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
186 return NULL;
187 }
188
189 /* Loop through all DEVMODEINFOs */
190 for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0;
191 pdminfo;
192 pdminfo = pdminfo->pdmiNext)
193 {
194 /* Calculate End of the DEVMODEs */
195 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
196
197 /* Loop through the DEVMODEs */
198 for (pdm = pdminfo->adevmode;
199 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
200 pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
201 {
202 /* Compare with the default entry */
203 if (pdm->dmBitsPerPel == pdmDefault->dmBitsPerPel &&
204 pdm->dmPelsWidth == pdmDefault->dmPelsWidth &&
205 pdm->dmPelsHeight == pdmDefault->dmPelsHeight &&
206 pdm->dmDisplayFrequency == pdmDefault->dmDisplayFrequency)
207 {
208 pGraphicsDevice->iDefaultMode = i;
209 pGraphicsDevice->iCurrentMode = i;
210 DPRINT("Found default entry: %lu '%ls'\n", i, pdm->dmDeviceName);
211 }
212
213 /* Initialize the entry */
214 pGraphicsDevice->pDevModeList[i].dwFlags = 0;
215 pGraphicsDevice->pDevModeList[i].pdm = pdm;
216 i++;
217 }
218 }
219
220 /* Lock loader */
221 EngAcquireSemaphore(ghsemGraphicsDeviceList);
222
223 /* Insert the device into the global list */
224 pGraphicsDevice->pNextGraphicsDevice = NULL;
225 if (gpGraphicsDeviceLast)
226 gpGraphicsDeviceLast->pNextGraphicsDevice = pGraphicsDevice;
227 gpGraphicsDeviceLast = pGraphicsDevice;
228 if (!gpGraphicsDeviceFirst)
229 gpGraphicsDeviceFirst = pGraphicsDevice;
230
231 /* Increment device number */
232 giDevNum++;
233
234 /* Unlock loader */
235 EngReleaseSemaphore(ghsemGraphicsDeviceList);
236 DPRINT("Prepared %lu modes for %ls\n", cModes, pGraphicsDevice->pwszDescription);
237
238 return pGraphicsDevice;
239 }
240
241
242 PGRAPHICS_DEVICE
243 NTAPI
244 EngpFindGraphicsDevice(
245 PUNICODE_STRING pustrDevice,
246 ULONG iDevNum,
247 DWORD dwFlags)
248 {
249 UNICODE_STRING ustrCurrent;
250 PGRAPHICS_DEVICE pGraphicsDevice;
251 ULONG i;
252 DPRINT("EngpFindGraphicsDevice('%wZ', %lu, 0x%lx)\n",
253 pustrDevice, iDevNum, dwFlags);
254
255 /* Lock list */
256 EngAcquireSemaphore(ghsemGraphicsDeviceList);
257
258 if (pustrDevice && pustrDevice->Buffer)
259 {
260 /* Loop through the list of devices */
261 for (pGraphicsDevice = gpGraphicsDeviceFirst;
262 pGraphicsDevice;
263 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
264 {
265 /* Compare the device name */
266 RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
267 if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
268 {
269 break;
270 }
271 }
272 }
273 else
274 {
275 /* Loop through the list of devices */
276 for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
277 pGraphicsDevice && i < iDevNum;
278 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
279 }
280
281 /* Unlock list */
282 EngReleaseSemaphore(ghsemGraphicsDeviceList);
283
284 return pGraphicsDevice;
285 }
286
287
288 static
289 NTSTATUS
290 EngpFileIoRequest(
291 PFILE_OBJECT pFileObject,
292 ULONG ulMajorFunction,
293 LPVOID lpBuffer,
294 SIZE_T nBufferSize,
295 ULONGLONG ullStartOffset,
296 OUT PULONG_PTR lpInformation)
297 {
298 PDEVICE_OBJECT pDeviceObject;
299 KEVENT Event;
300 PIRP pIrp;
301 IO_STATUS_BLOCK Iosb;
302 NTSTATUS Status;
303 LARGE_INTEGER liStartOffset;
304
305 /* Get corresponding device object */
306 pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
307 if (!pDeviceObject)
308 {
309 return STATUS_INVALID_PARAMETER;
310 }
311
312 /* Initialize an event */
313 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
314
315 /* Build IRP */
316 liStartOffset.QuadPart = ullStartOffset;
317 pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
318 pDeviceObject,
319 lpBuffer,
320 (ULONG)nBufferSize,
321 &liStartOffset,
322 &Event,
323 &Iosb);
324 if (!pIrp)
325 {
326 return STATUS_INSUFFICIENT_RESOURCES;
327 }
328
329 /* Call the driver */
330 Status = IoCallDriver(pDeviceObject, pIrp);
331
332 /* Wait if neccessary */
333 if (STATUS_PENDING == Status)
334 {
335 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
336 Status = Iosb.Status;
337 }
338
339 /* Return information to the caller about the operation. */
340 *lpInformation = Iosb.Information;
341
342 /* Return NTSTATUS */
343 return Status;
344 }
345
346 VOID
347 APIENTRY
348 EngFileWrite(
349 IN PFILE_OBJECT pFileObject,
350 IN PVOID lpBuffer,
351 IN SIZE_T nLength,
352 IN PSIZE_T lpBytesWritten)
353 {
354 EngpFileIoRequest(pFileObject,
355 IRP_MJ_WRITE,
356 lpBuffer,
357 nLength,
358 0,
359 lpBytesWritten);
360 }
361
362 NTSTATUS
363 APIENTRY
364 EngFileIoControl(
365 IN PFILE_OBJECT pFileObject,
366 IN DWORD dwIoControlCode,
367 IN PVOID lpInBuffer,
368 IN SIZE_T nInBufferSize,
369 OUT PVOID lpOutBuffer,
370 IN SIZE_T nOutBufferSize,
371 OUT PULONG_PTR lpInformation)
372 {
373 PDEVICE_OBJECT pDeviceObject;
374 KEVENT Event;
375 PIRP pIrp;
376 IO_STATUS_BLOCK Iosb;
377 NTSTATUS Status;
378
379 /* Get corresponding device object */
380 pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
381 if (!pDeviceObject)
382 {
383 return STATUS_INVALID_PARAMETER;
384 }
385
386 /* Initialize an event */
387 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
388
389 /* Build IO control IRP */
390 pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
391 pDeviceObject,
392 lpInBuffer,
393 (ULONG)nInBufferSize,
394 lpOutBuffer,
395 (ULONG)nOutBufferSize,
396 FALSE,
397 &Event,
398 &Iosb);
399 if (!pIrp)
400 {
401 return STATUS_INSUFFICIENT_RESOURCES;
402 }
403
404 /* Call the driver */
405 Status = IoCallDriver(pDeviceObject, pIrp);
406
407 /* Wait if neccessary */
408 if (Status == STATUS_PENDING)
409 {
410 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
411 Status = Iosb.Status;
412 }
413
414 /* Return information to the caller about the operation. */
415 *lpInformation = Iosb.Information;
416
417 /* This function returns NTSTATUS */
418 return Status;
419 }
420
421 /*
422 * @implemented
423 */
424 DWORD APIENTRY
425 EngDeviceIoControl(
426 _In_ HANDLE hDevice,
427 _In_ DWORD dwIoControlCode,
428 _In_opt_bytecount_(cjInBufferSize) LPVOID lpInBuffer,
429 _In_ DWORD cjInBufferSize,
430 _Out_opt_bytecap_(cjOutBufferSize) LPVOID lpOutBuffer,
431 _In_ DWORD cjOutBufferSize,
432 _Out_ LPDWORD lpBytesReturned)
433 {
434 PIRP Irp;
435 NTSTATUS Status;
436 KEVENT Event;
437 IO_STATUS_BLOCK Iosb;
438 PDEVICE_OBJECT DeviceObject;
439
440 DPRINT("EngDeviceIoControl() called\n");
441
442 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
443
444 DeviceObject = (PDEVICE_OBJECT) hDevice;
445
446 Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
447 DeviceObject,
448 lpInBuffer,
449 cjInBufferSize,
450 lpOutBuffer,
451 cjOutBufferSize,
452 FALSE,
453 &Event,
454 &Iosb);
455 if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
456
457 Status = IoCallDriver(DeviceObject, Irp);
458
459 if (Status == STATUS_PENDING)
460 {
461 (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
462 Status = Iosb.Status;
463 }
464
465 DPRINT("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
466 Iosb.Information);
467
468 /* Return information to the caller about the operation. */
469 *lpBytesReturned = (DWORD)Iosb.Information;
470
471 /* Convert NT status values to win32 error codes. */
472 switch (Status)
473 {
474 case STATUS_INSUFFICIENT_RESOURCES:
475 return ERROR_NOT_ENOUGH_MEMORY;
476
477 case STATUS_BUFFER_OVERFLOW:
478 return ERROR_MORE_DATA;
479
480 case STATUS_NOT_IMPLEMENTED:
481 return ERROR_INVALID_FUNCTION;
482
483 case STATUS_INVALID_PARAMETER:
484 return ERROR_INVALID_PARAMETER;
485
486 case STATUS_BUFFER_TOO_SMALL:
487 return ERROR_INSUFFICIENT_BUFFER;
488
489 case STATUS_DEVICE_DOES_NOT_EXIST:
490 return ERROR_DEV_NOT_EXIST;
491
492 case STATUS_PENDING:
493 return ERROR_IO_PENDING;
494 }
495
496 return Status;
497 }
498
499 /* EOF */