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 #include <ntddvdeo.h> 12 13 DBG_DEFAULT_CHANNEL(EngDev); 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 CODE_SEG("INIT") 24 NTSTATUS 25 NTAPI 26 InitDeviceImpl(VOID) 27 { 28 ghsemGraphicsDeviceList = EngCreateSemaphore(); 29 if (!ghsemGraphicsDeviceList) 30 return STATUS_INSUFFICIENT_RESOURCES; 31 32 return STATUS_SUCCESS; 33 } 34 35 BOOLEAN 36 EngpPopulateDeviceModeList( 37 _Inout_ PGRAPHICS_DEVICE pGraphicsDevice, 38 _In_ PDEVMODEW pdmDefault) 39 { 40 PWSTR pwsz; 41 PLDEVOBJ pldev; 42 PDEVMODEINFO pdminfo; 43 PDEVMODEW pdm, pdmEnd; 44 ULONG i, cModes = 0; 45 BOOLEAN bModeMatch = FALSE; 46 47 ASSERT(pGraphicsDevice->pdevmodeInfo == NULL); 48 ASSERT(pGraphicsDevice->pDevModeList == NULL); 49 50 pwsz = pGraphicsDevice->pDiplayDrivers; 51 52 /* Loop through the driver names 53 * This is a REG_MULTI_SZ string */ 54 for (; *pwsz; pwsz += wcslen(pwsz) + 1) 55 { 56 /* Try to load the display driver */ 57 TRACE("Trying driver: %ls\n", pwsz); 58 pldev = EngLoadImageEx(pwsz, LDEV_DEVICE_DISPLAY); 59 if (!pldev) 60 { 61 ERR("Could not load driver: '%ls'\n", pwsz); 62 continue; 63 } 64 65 /* Get the mode list from the driver */ 66 pdminfo = LDEVOBJ_pdmiGetModes(pldev, pGraphicsDevice->DeviceObject); 67 if (!pdminfo) 68 { 69 ERR("Could not get mode list for '%ls'\n", pwsz); 70 continue; 71 } 72 73 /* Attach the mode info to the device */ 74 pdminfo->pdmiNext = pGraphicsDevice->pdevmodeInfo; 75 pGraphicsDevice->pdevmodeInfo = pdminfo; 76 77 /* Loop all DEVMODEs */ 78 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode); 79 for (pdm = pdminfo->adevmode; 80 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0); 81 pdm = (DEVMODEW*)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra)) 82 { 83 /* Count this DEVMODE */ 84 cModes++; 85 86 /* Some drivers like the VBox driver don't fill the dmDeviceName 87 with the name of the display driver. So fix that here. */ 88 RtlStringCbCopyW(pdm->dmDeviceName, sizeof(pdm->dmDeviceName), pwsz); 89 } 90 91 // FIXME: release the driver again until it's used? 92 } 93 94 if (!pGraphicsDevice->pdevmodeInfo || cModes == 0) 95 { 96 ERR("No devmodes\n"); 97 return FALSE; 98 } 99 100 /* Allocate an index buffer */ 101 pGraphicsDevice->cDevModes = cModes; 102 pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool, 103 cModes * sizeof(DEVMODEENTRY), 104 GDITAG_GDEVICE); 105 if (!pGraphicsDevice->pDevModeList) 106 { 107 ERR("No devmode list\n"); 108 return FALSE; 109 } 110 111 TRACE("Looking for mode %lux%lux%lu(%lu Hz)\n", 112 pdmDefault->dmPelsWidth, 113 pdmDefault->dmPelsHeight, 114 pdmDefault->dmBitsPerPel, 115 pdmDefault->dmDisplayFrequency); 116 117 /* Loop through all DEVMODEINFOs */ 118 for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0; 119 pdminfo; 120 pdminfo = pdminfo->pdmiNext) 121 { 122 /* Calculate End of the DEVMODEs */ 123 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode); 124 125 /* Loop through the DEVMODEs */ 126 for (pdm = pdminfo->adevmode; 127 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0); 128 pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra)) 129 { 130 TRACE(" %S has mode %lux%lux%lu(%lu Hz)\n", 131 pdm->dmDeviceName, 132 pdm->dmPelsWidth, 133 pdm->dmPelsHeight, 134 pdm->dmBitsPerPel, 135 pdm->dmDisplayFrequency); 136 /* Compare with the default entry */ 137 if (!bModeMatch && 138 pdm->dmBitsPerPel == pdmDefault->dmBitsPerPel && 139 pdm->dmPelsWidth == pdmDefault->dmPelsWidth && 140 pdm->dmPelsHeight == pdmDefault->dmPelsHeight) 141 { 142 pGraphicsDevice->iDefaultMode = i; 143 pGraphicsDevice->iCurrentMode = i; 144 TRACE("Found default entry: %lu '%ls'\n", i, pdm->dmDeviceName); 145 if (pdm->dmDisplayFrequency == pdmDefault->dmDisplayFrequency) 146 { 147 /* Uh oh, even the display frequency matches. */ 148 bModeMatch = TRUE; 149 } 150 } 151 152 /* Initialize the entry */ 153 pGraphicsDevice->pDevModeList[i].dwFlags = 0; 154 pGraphicsDevice->pDevModeList[i].pdm = pdm; 155 i++; 156 } 157 } 158 return TRUE; 159 } 160 161 extern VOID 162 UserRefreshDisplay(IN PPDEVOBJ ppdev); 163 164 // PVIDEO_WIN32K_CALLOUT 165 VOID 166 NTAPI 167 VideoPortCallout( 168 _In_ PVOID Params) 169 { 170 /* 171 * IMPORTANT NOTICE!! On Windows XP/2003 this function triggers the creation of 172 * a specific VideoPortCalloutThread() system thread using the same mechanism 173 * as the RIT/desktop/Ghost system threads. 174 */ 175 176 PVIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams = (PVIDEO_WIN32K_CALLBACKS_PARAMS)Params; 177 178 TRACE("VideoPortCallout(0x%p, 0x%x)\n", 179 CallbackParams, CallbackParams ? CallbackParams->CalloutType : -1); 180 181 if (!CallbackParams) 182 return; 183 184 switch (CallbackParams->CalloutType) 185 { 186 case VideoFindAdapterCallout: 187 { 188 TRACE("VideoPortCallout: VideoFindAdapterCallout called - Param = %s\n", 189 CallbackParams->Param ? "TRUE" : "FALSE"); 190 if (CallbackParams->Param == TRUE) 191 { 192 /* Re-enable the display */ 193 UserRefreshDisplay(gppdevPrimary); 194 } 195 else 196 { 197 /* Disable the display */ 198 NOTHING; // Nothing to do for the moment... 199 } 200 201 CallbackParams->Status = STATUS_SUCCESS; 202 break; 203 } 204 205 case VideoPowerNotifyCallout: 206 case VideoDisplaySwitchCallout: 207 case VideoEnumChildPdoNotifyCallout: 208 case VideoWakeupCallout: 209 case VideoChangeDisplaySettingsCallout: 210 case VideoPnpNotifyCallout: 211 case VideoDxgkDisplaySwitchCallout: 212 case VideoDxgkMonitorEventCallout: 213 case VideoDxgkFindAdapterTdrCallout: 214 ERR("VideoPortCallout: CalloutType 0x%x is UNIMPLEMENTED!\n", CallbackParams->CalloutType); 215 CallbackParams->Status = STATUS_NOT_IMPLEMENTED; 216 break; 217 218 default: 219 ERR("VideoPortCallout: Unknown CalloutType 0x%x\n", CallbackParams->CalloutType); 220 CallbackParams->Status = STATUS_UNSUCCESSFUL; 221 break; 222 } 223 } 224 225 PGRAPHICS_DEVICE 226 NTAPI 227 EngpRegisterGraphicsDevice( 228 _In_ PUNICODE_STRING pustrDeviceName, 229 _In_ PUNICODE_STRING pustrDiplayDrivers, 230 _In_ PUNICODE_STRING pustrDescription, 231 _In_ PDEVMODEW pdmDefault) 232 { 233 PGRAPHICS_DEVICE pGraphicsDevice; 234 PDEVICE_OBJECT pDeviceObject; 235 PFILE_OBJECT pFileObject; 236 NTSTATUS Status; 237 VIDEO_WIN32K_CALLBACKS Win32kCallbacks; 238 ULONG ulReturn; 239 PWSTR pwsz; 240 ULONG cj; 241 242 TRACE("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName); 243 244 /* Allocate a GRAPHICS_DEVICE structure */ 245 pGraphicsDevice = ExAllocatePoolWithTag(PagedPool, 246 sizeof(GRAPHICS_DEVICE), 247 GDITAG_GDEVICE); 248 if (!pGraphicsDevice) 249 { 250 ERR("ExAllocatePoolWithTag failed\n"); 251 return NULL; 252 } 253 254 /* Try to open and enable the device */ 255 Status = IoGetDeviceObjectPointer(pustrDeviceName, 256 FILE_READ_DATA | FILE_WRITE_DATA, 257 &pFileObject, 258 &pDeviceObject); 259 if (!NT_SUCCESS(Status)) 260 { 261 ERR("Could not open device %wZ, 0x%lx\n", pustrDeviceName, Status); 262 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE); 263 return NULL; 264 } 265 266 /* Copy the device and file object pointers */ 267 pGraphicsDevice->DeviceObject = pDeviceObject; 268 pGraphicsDevice->FileObject = pFileObject; 269 270 /* Initialize and register the device with videoprt for Win32k callbacks */ 271 Win32kCallbacks.PhysDisp = pGraphicsDevice; 272 Win32kCallbacks.Callout = VideoPortCallout; 273 // Reset the data being returned prior to the call. 274 Win32kCallbacks.bACPI = FALSE; 275 Win32kCallbacks.pPhysDeviceObject = NULL; 276 Win32kCallbacks.DualviewFlags = 0; 277 Status = (NTSTATUS)EngDeviceIoControl((HANDLE)pDeviceObject, 278 IOCTL_VIDEO_INIT_WIN32K_CALLBACKS, 279 &Win32kCallbacks, 280 sizeof(Win32kCallbacks), 281 &Win32kCallbacks, 282 sizeof(Win32kCallbacks), 283 &ulReturn); 284 if (Status != ERROR_SUCCESS) 285 { 286 ERR("EngDeviceIoControl(0x%p, IOCTL_VIDEO_INIT_WIN32K_CALLBACKS) failed, Status 0x%lx\n", 287 pDeviceObject, Status); 288 } 289 // TODO: Set flags according to the results. 290 // if (Win32kCallbacks.bACPI) 291 // if (Win32kCallbacks.DualviewFlags & ???) 292 // Win32kCallbacks.pPhysDeviceObject; 293 294 /* Copy the device name */ 295 RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName, 296 sizeof(pGraphicsDevice->szNtDeviceName), 297 pustrDeviceName->Buffer, 298 pustrDeviceName->Length); 299 300 /* Create a Win32 device name (FIXME: virtual devices!) */ 301 RtlStringCbPrintfW(pGraphicsDevice->szWinDeviceName, 302 sizeof(pGraphicsDevice->szWinDeviceName), 303 L"\\\\.\\DISPLAY%d", 304 (int)giDevNum); 305 306 /* Allocate a buffer for the strings */ 307 cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR); 308 pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP); 309 if (!pwsz) 310 { 311 ERR("Could not allocate string buffer\n"); 312 ASSERT(FALSE); // FIXME 313 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE); 314 return NULL; 315 } 316 317 /* Copy the display driver names */ 318 pGraphicsDevice->pDiplayDrivers = pwsz; 319 RtlCopyMemory(pGraphicsDevice->pDiplayDrivers, 320 pustrDiplayDrivers->Buffer, 321 pustrDiplayDrivers->Length); 322 323 /* Copy the description */ 324 pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR); 325 RtlCopyMemory(pGraphicsDevice->pwszDescription, 326 pustrDescription->Buffer, 327 pustrDescription->Length); 328 pGraphicsDevice->pwszDescription[pustrDescription->Length/sizeof(WCHAR)] = 0; 329 330 /* Initialize the pdevmodeInfo list and default index */ 331 pGraphicsDevice->pdevmodeInfo = NULL; 332 pGraphicsDevice->iDefaultMode = 0; 333 pGraphicsDevice->iCurrentMode = 0; 334 335 // FIXME: initialize state flags 336 pGraphicsDevice->StateFlags = 0; 337 338 /* Create the mode list */ 339 pGraphicsDevice->pDevModeList = NULL; 340 if (!EngpPopulateDeviceModeList(pGraphicsDevice, pdmDefault)) 341 { 342 ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE); 343 return NULL; 344 } 345 346 /* Lock loader */ 347 EngAcquireSemaphore(ghsemGraphicsDeviceList); 348 349 /* Insert the device into the global list */ 350 pGraphicsDevice->pNextGraphicsDevice = NULL; 351 if (gpGraphicsDeviceLast) 352 gpGraphicsDeviceLast->pNextGraphicsDevice = pGraphicsDevice; 353 gpGraphicsDeviceLast = pGraphicsDevice; 354 if (!gpGraphicsDeviceFirst) 355 gpGraphicsDeviceFirst = pGraphicsDevice; 356 357 /* Increment the device number */ 358 giDevNum++; 359 360 /* Unlock loader */ 361 EngReleaseSemaphore(ghsemGraphicsDeviceList); 362 TRACE("Prepared %lu modes for %ls\n", pGraphicsDevice->cDevModes, pGraphicsDevice->pwszDescription); 363 364 return pGraphicsDevice; 365 } 366 367 PGRAPHICS_DEVICE 368 NTAPI 369 EngpFindGraphicsDevice( 370 _In_opt_ PUNICODE_STRING pustrDevice, 371 _In_ ULONG iDevNum, 372 _In_ DWORD dwFlags) 373 { 374 UNICODE_STRING ustrCurrent; 375 PGRAPHICS_DEVICE pGraphicsDevice; 376 ULONG i; 377 TRACE("EngpFindGraphicsDevice('%wZ', %lu, 0x%lx)\n", 378 pustrDevice, iDevNum, dwFlags); 379 380 /* Lock list */ 381 EngAcquireSemaphore(ghsemGraphicsDeviceList); 382 383 if (pustrDevice && pustrDevice->Buffer) 384 { 385 /* Loop through the list of devices */ 386 for (pGraphicsDevice = gpGraphicsDeviceFirst; 387 pGraphicsDevice; 388 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice) 389 { 390 /* Compare the device name */ 391 RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName); 392 if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE)) 393 { 394 break; 395 } 396 } 397 } 398 else 399 { 400 /* Loop through the list of devices */ 401 for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0; 402 pGraphicsDevice && i < iDevNum; 403 pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++); 404 } 405 406 /* Unlock list */ 407 EngReleaseSemaphore(ghsemGraphicsDeviceList); 408 409 return pGraphicsDevice; 410 } 411 412 static 413 NTSTATUS 414 EngpFileIoRequest( 415 _In_ PFILE_OBJECT pFileObject, 416 _In_ ULONG ulMajorFunction, 417 _In_reads_(nBufferSize) PVOID lpBuffer, 418 _In_ SIZE_T nBufferSize, 419 _In_ ULONGLONG ullStartOffset, 420 _Out_ PULONG_PTR lpInformation) 421 { 422 PDEVICE_OBJECT pDeviceObject; 423 KEVENT Event; 424 PIRP pIrp; 425 IO_STATUS_BLOCK Iosb; 426 NTSTATUS Status; 427 LARGE_INTEGER liStartOffset; 428 429 /* Get corresponding device object */ 430 pDeviceObject = IoGetRelatedDeviceObject(pFileObject); 431 if (!pDeviceObject) 432 { 433 return STATUS_INVALID_PARAMETER; 434 } 435 436 /* Initialize an event */ 437 KeInitializeEvent(&Event, SynchronizationEvent, FALSE); 438 439 /* Build IRP */ 440 liStartOffset.QuadPart = ullStartOffset; 441 pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction, 442 pDeviceObject, 443 lpBuffer, 444 (ULONG)nBufferSize, 445 &liStartOffset, 446 &Event, 447 &Iosb); 448 if (!pIrp) 449 { 450 return STATUS_INSUFFICIENT_RESOURCES; 451 } 452 453 /* Call the driver */ 454 Status = IoCallDriver(pDeviceObject, pIrp); 455 456 /* Wait if neccessary */ 457 if (STATUS_PENDING == Status) 458 { 459 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0); 460 Status = Iosb.Status; 461 } 462 463 /* Return information to the caller about the operation. */ 464 *lpInformation = Iosb.Information; 465 466 /* Return NTSTATUS */ 467 return Status; 468 } 469 470 VOID 471 APIENTRY 472 EngFileWrite( 473 _In_ PFILE_OBJECT pFileObject, 474 _In_reads_(nLength) PVOID lpBuffer, 475 _In_ SIZE_T nLength, 476 _Out_ PSIZE_T lpBytesWritten) 477 { 478 NTSTATUS status; 479 480 status = EngpFileIoRequest(pFileObject, 481 IRP_MJ_WRITE, 482 lpBuffer, 483 nLength, 484 0, 485 lpBytesWritten); 486 if (!NT_SUCCESS(status)) 487 { 488 *lpBytesWritten = 0; 489 } 490 } 491 492 _Success_(return>=0) 493 NTSTATUS 494 APIENTRY 495 EngFileIoControl( 496 _In_ PFILE_OBJECT pFileObject, 497 _In_ DWORD dwIoControlCode, 498 _In_reads_(nInBufferSize) PVOID lpInBuffer, 499 _In_ SIZE_T nInBufferSize, 500 _Out_writes_(nOutBufferSize) PVOID lpOutBuffer, 501 _In_ SIZE_T nOutBufferSize, 502 _Out_ PULONG_PTR lpInformation) 503 { 504 PDEVICE_OBJECT pDeviceObject; 505 KEVENT Event; 506 PIRP pIrp; 507 IO_STATUS_BLOCK Iosb; 508 NTSTATUS Status; 509 510 /* Get corresponding device object */ 511 pDeviceObject = IoGetRelatedDeviceObject(pFileObject); 512 if (!pDeviceObject) 513 { 514 return STATUS_INVALID_PARAMETER; 515 } 516 517 /* Initialize an event */ 518 KeInitializeEvent(&Event, SynchronizationEvent, FALSE); 519 520 /* Build IO control IRP */ 521 pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode, 522 pDeviceObject, 523 lpInBuffer, 524 (ULONG)nInBufferSize, 525 lpOutBuffer, 526 (ULONG)nOutBufferSize, 527 FALSE, 528 &Event, 529 &Iosb); 530 if (!pIrp) 531 { 532 return STATUS_INSUFFICIENT_RESOURCES; 533 } 534 535 /* Call the driver */ 536 Status = IoCallDriver(pDeviceObject, pIrp); 537 538 /* Wait if neccessary */ 539 if (Status == STATUS_PENDING) 540 { 541 KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0); 542 Status = Iosb.Status; 543 } 544 545 /* Return information to the caller about the operation. */ 546 *lpInformation = Iosb.Information; 547 548 /* This function returns NTSTATUS */ 549 return Status; 550 } 551 552 /* 553 * @implemented 554 */ 555 _Success_(return==0) 556 DWORD 557 APIENTRY 558 EngDeviceIoControl( 559 _In_ HANDLE hDevice, 560 _In_ DWORD dwIoControlCode, 561 _In_reads_bytes_opt_(cjInBufferSize) LPVOID lpInBuffer, 562 _In_ DWORD cjInBufferSize, 563 _Out_writes_bytes_opt_(cjOutBufferSize) LPVOID lpOutBuffer, 564 _In_ DWORD cjOutBufferSize, 565 _Out_ LPDWORD lpBytesReturned) 566 { 567 PIRP Irp; 568 NTSTATUS Status; 569 KEVENT Event; 570 IO_STATUS_BLOCK Iosb; 571 PDEVICE_OBJECT DeviceObject; 572 573 TRACE("EngDeviceIoControl() called\n"); 574 575 if (!hDevice) 576 { 577 return ERROR_INVALID_HANDLE; 578 } 579 580 KeInitializeEvent(&Event, SynchronizationEvent, FALSE); 581 582 DeviceObject = (PDEVICE_OBJECT) hDevice; 583 584 Irp = IoBuildDeviceIoControlRequest(dwIoControlCode, 585 DeviceObject, 586 lpInBuffer, 587 cjInBufferSize, 588 lpOutBuffer, 589 cjOutBufferSize, 590 FALSE, 591 &Event, 592 &Iosb); 593 if (!Irp) return ERROR_NOT_ENOUGH_MEMORY; 594 595 Status = IoCallDriver(DeviceObject, Irp); 596 597 if (Status == STATUS_PENDING) 598 { 599 (VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0); 600 Status = Iosb.Status; 601 } 602 603 TRACE("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status, 604 Iosb.Information); 605 606 /* Return information to the caller about the operation. */ 607 *lpBytesReturned = (DWORD)Iosb.Information; 608 609 /* Convert NT status values to win32 error codes. */ 610 switch (Status) 611 { 612 case STATUS_INSUFFICIENT_RESOURCES: 613 return ERROR_NOT_ENOUGH_MEMORY; 614 615 case STATUS_BUFFER_OVERFLOW: 616 return ERROR_MORE_DATA; 617 618 case STATUS_NOT_IMPLEMENTED: 619 return ERROR_INVALID_FUNCTION; 620 621 case STATUS_INVALID_PARAMETER: 622 return ERROR_INVALID_PARAMETER; 623 624 case STATUS_BUFFER_TOO_SMALL: 625 return ERROR_INSUFFICIENT_BUFFER; 626 627 case STATUS_DEVICE_DOES_NOT_EXIST: 628 return ERROR_DEV_NOT_EXIST; 629 630 case STATUS_PENDING: 631 return ERROR_IO_PENDING; 632 } 633 634 return Status; 635 } 636 637 /* EOF */ 638