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