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