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