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