[WIN32K]
[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 = NULL;
224 if (gpGraphicsDeviceLast)
225 gpGraphicsDeviceLast->pNextGraphicsDevice = pGraphicsDevice;
226 gpGraphicsDeviceLast = pGraphicsDevice;
227 if (!gpGraphicsDeviceFirst)
228 gpGraphicsDeviceFirst = pGraphicsDevice;
229
230 /* Increment device number */
231 giDevNum++;
232
233 /* Unlock loader */
234 EngReleaseSemaphore(ghsemGraphicsDeviceList);
235 DPRINT("Prepared %ld modes for %ls\n", cModes, pGraphicsDevice->pwszDescription);
236
237 return pGraphicsDevice;
238 }
239
240
241 PGRAPHICS_DEVICE
242 NTAPI
243 EngpFindGraphicsDevice(
244 PUNICODE_STRING pustrDevice,
245 ULONG iDevNum,
246 DWORD dwFlags)
247 {
248 UNICODE_STRING ustrCurrent;
249 PGRAPHICS_DEVICE pGraphicsDevice;
250 ULONG i;
251 DPRINT("EngpFindGraphicsDevice('%wZ', %ld, 0x%lx)\n",
252 pustrDevice, iDevNum, dwFlags);
253
254 /* Lock list */
255 EngAcquireSemaphore(ghsemGraphicsDeviceList);
256
257 if (pustrDevice && pustrDevice->Buffer)
258 {
259 /* Loop through the list of devices */
260 for (pGraphicsDevice = gpGraphicsDeviceFirst;
261 pGraphicsDevice;
262 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
263 {
264 /* Compare the device name */
265 RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
266 if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
267 {
268 break;
269 }
270 }
271 }
272 else
273 {
274 /* Loop through the list of devices */
275 for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
276 pGraphicsDevice && i < iDevNum;
277 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
278 }
279
280 /* Unlock list */
281 EngReleaseSemaphore(ghsemGraphicsDeviceList);
282
283 return pGraphicsDevice;
284 }
285
286
287 static
288 NTSTATUS
289 EngpFileIoRequest(
290 PFILE_OBJECT pFileObject,
291 ULONG ulMajorFunction,
292 LPVOID lpBuffer,
293 DWORD nBufferSize,
294 ULONGLONG ullStartOffset,
295 OUT LPDWORD lpInformation)
296 {
297 PDEVICE_OBJECT pDeviceObject;
298 KEVENT Event;
299 PIRP pIrp;
300 IO_STATUS_BLOCK Iosb;
301 NTSTATUS Status;
302 LARGE_INTEGER liStartOffset;
303
304 /* Get corresponding device object */
305 pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
306 if (!pDeviceObject)
307 {
308 return STATUS_INVALID_PARAMETER;
309 }
310
311 /* Initialize an event */
312 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
313
314 /* Build IRP */
315 liStartOffset.QuadPart = ullStartOffset;
316 pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
317 pDeviceObject,
318 lpBuffer,
319 nBufferSize,
320 &liStartOffset,
321 &Event,
322 &Iosb);
323 if (!pIrp)
324 {
325 return STATUS_INSUFFICIENT_RESOURCES;
326 }
327
328 /* Call the driver */
329 Status = IoCallDriver(pDeviceObject, pIrp);
330
331 /* Wait if neccessary */
332 if (STATUS_PENDING == Status)
333 {
334 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
335 Status = Iosb.Status;
336 }
337
338 /* Return information to the caller about the operation. */
339 *lpInformation = Iosb.Information;
340
341 /* Return NTSTATUS */
342 return Status;
343 }
344
345 VOID
346 APIENTRY
347 EngFileWrite(
348 IN PFILE_OBJECT pFileObject,
349 IN PVOID lpBuffer,
350 IN SIZE_T nLength,
351 IN PSIZE_T lpBytesWritten)
352 {
353 EngpFileIoRequest(pFileObject,
354 IRP_MJ_WRITE,
355 lpBuffer,
356 nLength,
357 0,
358 lpBytesWritten);
359 }
360
361 NTSTATUS
362 APIENTRY
363 EngFileIoControl(
364 IN PFILE_OBJECT pFileObject,
365 IN DWORD dwIoControlCode,
366 IN PVOID lpInBuffer,
367 IN SIZE_T nInBufferSize,
368 OUT PVOID lpOutBuffer,
369 IN SIZE_T nOutBufferSize,
370 OUT LPDWORD lpInformation)
371 {
372 PDEVICE_OBJECT pDeviceObject;
373 KEVENT Event;
374 PIRP pIrp;
375 IO_STATUS_BLOCK Iosb;
376 NTSTATUS Status;
377
378 /* Get corresponding device object */
379 pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
380 if (!pDeviceObject)
381 {
382 return STATUS_INVALID_PARAMETER;
383 }
384
385 /* Initialize an event */
386 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
387
388 /* Build IO control IRP */
389 pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
390 pDeviceObject,
391 lpInBuffer,
392 nInBufferSize,
393 lpOutBuffer,
394 nOutBufferSize,
395 FALSE,
396 &Event,
397 &Iosb);
398 if (!pIrp)
399 {
400 return STATUS_INSUFFICIENT_RESOURCES;
401 }
402
403 /* Call the driver */
404 Status = IoCallDriver(pDeviceObject, pIrp);
405
406 /* Wait if neccessary */
407 if (Status == STATUS_PENDING)
408 {
409 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
410 Status = Iosb.Status;
411 }
412
413 /* Return information to the caller about the operation. */
414 *lpInformation = Iosb.Information;
415
416 /* This function returns NTSTATUS */
417 return Status;
418 }
419
420 /*
421 * @implemented
422 */
423 DWORD APIENTRY
424 EngDeviceIoControl(
425 HANDLE hDevice,
426 DWORD dwIoControlCode,
427 LPVOID lpInBuffer,
428 DWORD nInBufferSize,
429 LPVOID lpOutBuffer,
430 DWORD nOutBufferSize,
431 DWORD *lpBytesReturned)
432 {
433 PIRP Irp;
434 NTSTATUS Status;
435 KEVENT Event;
436 IO_STATUS_BLOCK Iosb;
437 PDEVICE_OBJECT DeviceObject;
438
439 DPRINT("EngDeviceIoControl() called\n");
440
441 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
442
443 DeviceObject = (PDEVICE_OBJECT) hDevice;
444
445 Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
446 DeviceObject,
447 lpInBuffer,
448 nInBufferSize,
449 lpOutBuffer,
450 nOutBufferSize, FALSE, &Event, &Iosb);
451 if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
452
453 Status = IoCallDriver(DeviceObject, Irp);
454
455 if (Status == STATUS_PENDING)
456 {
457 (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
458 Status = Iosb.Status;
459 }
460
461 DPRINT("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
462 Iosb.Information);
463
464 /* Return information to the caller about the operation. */
465 *lpBytesReturned = Iosb.Information;
466
467 /* Convert NT status values to win32 error codes. */
468 switch (Status)
469 {
470 case STATUS_INSUFFICIENT_RESOURCES:
471 return ERROR_NOT_ENOUGH_MEMORY;
472
473 case STATUS_BUFFER_OVERFLOW:
474 return ERROR_MORE_DATA;
475
476 case STATUS_NOT_IMPLEMENTED:
477 return ERROR_INVALID_FUNCTION;
478
479 case STATUS_INVALID_PARAMETER:
480 return ERROR_INVALID_PARAMETER;
481
482 case STATUS_BUFFER_TOO_SMALL:
483 return ERROR_INSUFFICIENT_BUFFER;
484
485 case STATUS_DEVICE_DOES_NOT_EXIST:
486 return ERROR_DEV_NOT_EXIST;
487
488 case STATUS_PENDING:
489 return ERROR_IO_PENDING;
490 }
491
492 return Status;
493 }
494
495 /* EOF */