eab85151d8135f4d5fb8df059e29fcde29bcfe39
[reactos.git] / reactos / subsystems / win32 / win32k / 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 BOOL bEnable = TRUE;
51 PDEVMODEINFO pdminfo;
52 PDEVMODEW pdm, pdmEnd;
53 PLDEVOBJ pldev;
54
55 DPRINT("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 DPRINT1("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 DPRINT1("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), &cj);
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 DPRINT1("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 DPRINT("trying driver: %ls\n", pwsz);
131 /* Try to load the display driver */
132 pldev = EngLoadImageEx(pwsz, LDEV_DEVICE_DISPLAY);
133 if (!pldev)
134 {
135 DPRINT1("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 DPRINT1("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 DPRINT1("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 DPRINT1("No devmode list\n");
184 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
185 return NULL;
186 }
187
188 /* Loop through all DEVMODEINFOs */
189 for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0;
190 pdminfo;
191 pdminfo = pdminfo->pdmiNext)
192 {
193 /* Calculate End of the DEVMODEs */
194 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
195
196 /* Loop through the DEVMODEs */
197 for (pdm = pdminfo->adevmode;
198 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
199 pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
200 {
201 /* Compare with the default entry */
202 if (pdm->dmBitsPerPel == pdmDefault->dmBitsPerPel &&
203 pdm->dmPelsWidth == pdmDefault->dmPelsWidth &&
204 pdm->dmPelsHeight == pdmDefault->dmPelsHeight &&
205 pdm->dmDisplayFrequency == pdmDefault->dmDisplayFrequency)
206 {
207 pGraphicsDevice->iDefaultMode = i;
208 pGraphicsDevice->iCurrentMode = i;
209 DPRINT("Found default entry: %ld '%ls'\n", i, pdm->dmDeviceName);
210 }
211
212 /* Initialize the entry */
213 pGraphicsDevice->pDevModeList[i].dwFlags = 0;
214 pGraphicsDevice->pDevModeList[i].pdm = pdm;
215 i++;
216 }
217 }
218
219 /* Lock loader */
220 EngAcquireSemaphore(ghsemGraphicsDeviceList);
221
222 /* Insert the device into the global list */
223 pGraphicsDevice->pNextGraphicsDevice = gpGraphicsDeviceLast;
224 gpGraphicsDeviceLast = pGraphicsDevice;
225 if (!gpGraphicsDeviceFirst)
226 gpGraphicsDeviceFirst = pGraphicsDevice;
227
228 /* Increment device number */
229 giDevNum++;
230
231 /* Unlock loader */
232 EngReleaseSemaphore(ghsemGraphicsDeviceList);
233 DPRINT("Prepared %ld modes for %ls\n", cModes, pGraphicsDevice->pwszDescription);
234
235 return pGraphicsDevice;
236 }
237
238
239 PGRAPHICS_DEVICE
240 NTAPI
241 EngpFindGraphicsDevice(
242 PUNICODE_STRING pustrDevice,
243 ULONG iDevNum,
244 DWORD dwFlags)
245 {
246 UNICODE_STRING ustrCurrent;
247 PGRAPHICS_DEVICE pGraphicsDevice;
248 ULONG i;
249
250 /* Lock list */
251 EngAcquireSemaphore(ghsemGraphicsDeviceList);
252
253 if (pustrDevice)
254 {
255 /* Loop through the list of devices */
256 for (pGraphicsDevice = gpGraphicsDeviceFirst;
257 pGraphicsDevice;
258 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
259 {
260 /* Compare the device name */
261 RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
262 if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
263 {
264 break;
265 }
266 }
267 }
268 else
269 {
270 /* Loop through the list of devices */
271 for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
272 pGraphicsDevice && i < iDevNum;
273 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
274 }
275
276 /* Unlock list */
277 EngReleaseSemaphore(ghsemGraphicsDeviceList);
278
279 return pGraphicsDevice;
280 }
281
282
283 static
284 NTSTATUS
285 EngpFileIoRequest(
286 PFILE_OBJECT pFileObject,
287 ULONG ulMajorFunction,
288 LPVOID lpBuffer,
289 DWORD nBufferSize,
290 ULONGLONG ullStartOffset,
291 OUT LPDWORD lpInformation)
292 {
293 PDEVICE_OBJECT pDeviceObject;
294 KEVENT Event;
295 PIRP pIrp;
296 IO_STATUS_BLOCK Iosb;
297 NTSTATUS Status;
298 LARGE_INTEGER liStartOffset;
299
300 /* Get corresponding device object */
301 pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
302 if (!pDeviceObject)
303 {
304 return STATUS_INVALID_PARAMETER;
305 }
306
307 /* Initialize an event */
308 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
309
310 /* Build IRP */
311 liStartOffset.QuadPart = ullStartOffset;
312 pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
313 pDeviceObject,
314 lpBuffer,
315 nBufferSize,
316 &liStartOffset,
317 &Event,
318 &Iosb);
319 if (!pIrp)
320 {
321 return STATUS_INSUFFICIENT_RESOURCES;
322 }
323
324 /* Call the driver */
325 Status = IoCallDriver(pDeviceObject, pIrp);
326
327 /* Wait if neccessary */
328 if (STATUS_PENDING == Status)
329 {
330 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
331 Status = Iosb.Status;
332 }
333
334 /* Return information to the caller about the operation. */
335 *lpInformation = Iosb.Information;
336
337 /* Return NTSTATUS */
338 return Status;
339 }
340
341 VOID
342 APIENTRY
343 EngFileWrite(
344 IN PFILE_OBJECT pFileObject,
345 IN PVOID lpBuffer,
346 IN SIZE_T nLength,
347 IN PSIZE_T lpBytesWritten)
348 {
349 EngpFileIoRequest(pFileObject,
350 IRP_MJ_WRITE,
351 lpBuffer,
352 nLength,
353 0,
354 lpBytesWritten);
355 }
356
357 NTSTATUS
358 APIENTRY
359 EngFileIoControl(
360 IN PFILE_OBJECT pFileObject,
361 IN DWORD dwIoControlCode,
362 IN PVOID lpInBuffer,
363 IN SIZE_T nInBufferSize,
364 OUT PVOID lpOutBuffer,
365 IN SIZE_T nOutBufferSize,
366 OUT LPDWORD lpInformation)
367 {
368 PDEVICE_OBJECT pDeviceObject;
369 KEVENT Event;
370 PIRP pIrp;
371 IO_STATUS_BLOCK Iosb;
372 NTSTATUS Status;
373
374 /* Get corresponding device object */
375 pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
376 if (!pDeviceObject)
377 {
378 return STATUS_INVALID_PARAMETER;
379 }
380
381 /* Initialize an event */
382 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
383
384 /* Build IO control IRP */
385 pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
386 pDeviceObject,
387 lpInBuffer,
388 nInBufferSize,
389 lpOutBuffer,
390 nOutBufferSize,
391 FALSE,
392 &Event,
393 &Iosb);
394 if (!pIrp)
395 {
396 return STATUS_INSUFFICIENT_RESOURCES;
397 }
398
399 /* Call the driver */
400 Status = IoCallDriver(pDeviceObject, pIrp);
401
402 /* Wait if neccessary */
403 if (Status == STATUS_PENDING)
404 {
405 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
406 Status = Iosb.Status;
407 }
408
409 /* Return information to the caller about the operation. */
410 *lpInformation = Iosb.Information;
411
412 /* This function returns NTSTATUS */
413 return Status;
414 }
415
416 /*
417 * @implemented
418 */
419 DWORD APIENTRY
420 EngDeviceIoControl(
421 HANDLE hDevice,
422 DWORD dwIoControlCode,
423 LPVOID lpInBuffer,
424 DWORD nInBufferSize,
425 LPVOID lpOutBuffer,
426 DWORD nOutBufferSize,
427 DWORD *lpBytesReturned)
428 {
429 PIRP Irp;
430 NTSTATUS Status;
431 KEVENT Event;
432 IO_STATUS_BLOCK Iosb;
433 PDEVICE_OBJECT DeviceObject;
434
435 DPRINT("EngDeviceIoControl() called\n");
436
437 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
438
439 DeviceObject = (PDEVICE_OBJECT) hDevice;
440
441 Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
442 DeviceObject,
443 lpInBuffer,
444 nInBufferSize,
445 lpOutBuffer,
446 nOutBufferSize, FALSE, &Event, &Iosb);
447 if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
448
449 Status = IoCallDriver(DeviceObject, Irp);
450
451 if (Status == STATUS_PENDING)
452 {
453 (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
454 Status = Iosb.Status;
455 }
456
457 DPRINT("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
458 Iosb.Information);
459
460 /* Return information to the caller about the operation. */
461 *lpBytesReturned = Iosb.Information;
462
463 /* Convert NT status values to win32 error codes. */
464 switch (Status)
465 {
466 case STATUS_INSUFFICIENT_RESOURCES:
467 return ERROR_NOT_ENOUGH_MEMORY;
468
469 case STATUS_BUFFER_OVERFLOW:
470 return ERROR_MORE_DATA;
471
472 case STATUS_NOT_IMPLEMENTED:
473 return ERROR_INVALID_FUNCTION;
474
475 case STATUS_INVALID_PARAMETER:
476 return ERROR_INVALID_PARAMETER;
477
478 case STATUS_BUFFER_TOO_SMALL:
479 return ERROR_INSUFFICIENT_BUFFER;
480
481 case STATUS_DEVICE_DOES_NOT_EXIST:
482 return ERROR_DEV_NOT_EXIST;
483
484 case STATUS_PENDING:
485 return ERROR_IO_PENDING;
486 }
487
488 return Status;
489 }
490
491 /* EOF */