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