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