reactos/subsystems/win32/win32k/eng/device.c

478 lines
13 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: GDI Driver Device Functions
* FILE: subsys/win32k/eng/device.c
* PROGRAMER: Jason Filby
* Timo Kreuzer
*/
#include <w32k.h>
#define NDEBUG
#include <debug.h>
static PGRAPHICS_DEVICE gpGraphicsDeviceFirst = NULL;
static PGRAPHICS_DEVICE gpGraphicsDeviceLast = NULL;
static HSEMAPHORE ghsemGraphicsDeviceList;
static ULONG giDevNum = 1;
BOOL
NTAPI
InitDeviceImpl()
{
ghsemGraphicsDeviceList = EngCreateSemaphore();
if (!ghsemGraphicsDeviceList)
return FALSE;
return TRUE;
}
PGRAPHICS_DEVICE
NTAPI
EngpRegisterGraphicsDevice(
PUNICODE_STRING pustrDeviceName,
PUNICODE_STRING pustrDiplayDrivers,
PUNICODE_STRING pustrDescription,
PDEVMODEW pdmDefault)
{
PGRAPHICS_DEVICE pGraphicsDevice;
PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject;
NTSTATUS Status;
PWSTR pwsz;
ULONG i, cj, cModes = 0;
BOOL bEnable = TRUE;
PDEVMODEINFO pdminfo;
PDEVMODEW pdm, pdmEnd;
PLDEVOBJ pldev;
DPRINT1("EngpRegisterGraphicsDevice(%S)\n", pustrDeviceName->Buffer);
/* Allocate a GRAPHICS_DEVICE structure */
pGraphicsDevice = ExAllocatePoolWithTag(PagedPool,
sizeof(GRAPHICS_DEVICE),
GDITAG_GDEVICE);
if (!pGraphicsDevice)
{
DPRINT1("ExAllocatePoolWithTag failed\n");
return NULL;
}
/* Try to open the driver */
Status = IoGetDeviceObjectPointer(pustrDeviceName,
FILE_READ_DATA | FILE_WRITE_DATA,
&pFileObject,
&pDeviceObject);
if (!NT_SUCCESS(Status))
{
DPRINT1("Could not open driver, 0x%lx\n", Status);
ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
return NULL;
}
/* Enable the device */
EngFileWrite(pFileObject, &bEnable, sizeof(BOOL), &cj);
/* Copy the device and file object pointers */
pGraphicsDevice->DeviceObject = pDeviceObject;
pGraphicsDevice->FileObject = pFileObject;
/* Copy device name */
wcsncpy(pGraphicsDevice->szNtDeviceName,
pustrDeviceName->Buffer,
sizeof(pGraphicsDevice->szNtDeviceName) / sizeof(WCHAR));
/* Create a win device name (FIXME: virtual devices!) */
swprintf(pGraphicsDevice->szWinDeviceName, L"\\\\.\\VIDEO%d", (CHAR)giDevNum);
/* Allocate a buffer for the strings */
cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR);
pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP);
if (!pwsz)
{
DPRINT1("Could not allocate string buffer\n");
ASSERT(FALSE); // FIXME
}
/* Copy display driver names */
pGraphicsDevice->pDiplayDrivers = pwsz;
RtlCopyMemory(pGraphicsDevice->pDiplayDrivers,
pustrDiplayDrivers->Buffer,
pustrDiplayDrivers->Length);
/* Copy description */
pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR);
RtlCopyMemory(pGraphicsDevice->pwszDescription,
pustrDescription->Buffer,
pustrDescription->Length + sizeof(WCHAR));
/* Initialize the pdevmodeInfo list and default index */
pGraphicsDevice->pdevmodeInfo = NULL;
pGraphicsDevice->iDefaultMode = 0;
pGraphicsDevice->iCurrentMode = 0;
// FIXME: initialize state flags
pGraphicsDevice->StateFlags = 0;
/* Loop through the driver names */
for (; *pwsz; pwsz += wcslen(pwsz) + 1)
{
DPRINT1("trying driver: %ls\n", pwsz);
/* Try to load the display driver */
pldev = EngLoadDriver(pwsz, LDEV_DEVICE_DISPLAY);
if (!pldev)
{
DPRINT1("Could not load driver: '%ls'\n", pwsz);
continue;
}
/* Get the mode list from the driver */
pdminfo = LDEVOBJ_pdmiGetModes(pldev, pDeviceObject);
if (!pdminfo)
{
DPRINT1("Could not get mode list for '%ls'\n", pwsz);
continue;
}
/* Attach the mode info to the device */
pdminfo->pdmiNext = pGraphicsDevice->pdevmodeInfo;
pGraphicsDevice->pdevmodeInfo = pdminfo;
/* Count DEVMODEs */
pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
for (pdm = pdminfo->adevmode;
pdm + 1 <= pdmEnd;
pdm = (DEVMODEW*)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
{
cModes++;
}
// FIXME: release the driver again until it's used?
}
if (!pGraphicsDevice->pdevmodeInfo || cModes == 0)
{
DPRINT1("No devmodes\n");
ExFreePool(pGraphicsDevice);
return NULL;
}
/* Allocate an index buffer */
pGraphicsDevice->cDevModes = cModes;
pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool,
cModes * sizeof(DEVMODEENTRY),
GDITAG_GDEVICE);
if (!pGraphicsDevice->pDevModeList)
{
DPRINT1("No devmode list\n");
ExFreePool(pGraphicsDevice);
return NULL;
}
/* Loop through all DEVMODEINFOs */
for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0;
pdminfo;
pdminfo = pdminfo->pdmiNext)
{
/* Calculate End of the DEVMODEs */
pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
/* Loop through the DEVMODEs */
for (pdm = pdminfo->adevmode;
pdm + 1 <= pdmEnd;
pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
{
/* Compare with the default entry */
if (pdm->dmBitsPerPel == pdmDefault->dmBitsPerPel &&
pdm->dmPelsWidth == pdmDefault->dmPelsWidth &&
pdm->dmPelsHeight == pdmDefault->dmPelsHeight &&
pdm->dmDisplayFrequency == pdmDefault->dmDisplayFrequency)
{
pGraphicsDevice->iDefaultMode = i;
pGraphicsDevice->iCurrentMode = i;
DPRINT1("Found default entry: %ld '%ls'\n", i, pdm->dmDeviceName);
}
/* Initialize the entry */
pGraphicsDevice->pDevModeList[i].dwFlags = 0;
pGraphicsDevice->pDevModeList[i].pdm = pdm;
i++;
}
}
/* Lock loader */
EngAcquireSemaphore(ghsemGraphicsDeviceList);
/* Insert the device into the global list */
pGraphicsDevice->pNextGraphicsDevice = gpGraphicsDeviceLast;
gpGraphicsDeviceLast = pGraphicsDevice;
if (!gpGraphicsDeviceFirst)
gpGraphicsDeviceFirst = pGraphicsDevice;
/* Increment device number */
giDevNum++;
/* Unlock loader */
EngReleaseSemaphore(ghsemGraphicsDeviceList);
DPRINT1("Prepared %ld modes for %ls\n", cModes, pGraphicsDevice->pwszDescription);
return pGraphicsDevice;
}
PGRAPHICS_DEVICE
NTAPI
EngpFindGraphicsDevice(
PUNICODE_STRING pustrDevice,
ULONG iDevNum,
DWORD dwFlags)
{
UNICODE_STRING ustrCurrent;
PGRAPHICS_DEVICE pGraphicsDevice;
ULONG i;
/* Lock list */
EngAcquireSemaphore(ghsemGraphicsDeviceList);
if (pustrDevice)
{
/* Loop through the list of devices */
for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
pGraphicsDevice;
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++)
{
/* Compare the device name */
RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
{
break;
}
}
}
else
{
/* Loop through the list of devices */
for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
pGraphicsDevice && i < iDevNum;
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
}
/* Unlock list */
EngReleaseSemaphore(ghsemGraphicsDeviceList);
return pGraphicsDevice;
}
static
NTSTATUS
EngpFileIoRequest(
PFILE_OBJECT pFileObject,
ULONG ulMajorFunction,
LPVOID lpBuffer,
DWORD nBufferSize,
ULONGLONG ullStartOffset,
OUT LPDWORD lpInformation)
{
PDEVICE_OBJECT pDeviceObject;
KEVENT Event;
PIRP pIrp;
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
LARGE_INTEGER liStartOffset;
/* Get corresponding device object */
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
if (!pDeviceObject)
{
return STATUS_INVALID_PARAMETER;
}
/* Initialize an event */
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
/* Build IRP */
liStartOffset.QuadPart = ullStartOffset;
pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
pDeviceObject,
lpBuffer,
nBufferSize,
&liStartOffset,
&Event,
&Iosb);
if (!pIrp)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Call the driver */
Status = IoCallDriver(pDeviceObject, pIrp);
/* Wait if neccessary */
if (STATUS_PENDING == Status)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
Status = Iosb.Status;
}
/* Return information to the caller about the operation. */
*lpInformation = Iosb.Information;
/* Return NTSTATUS */
return Status;
}
VOID
APIENTRY
EngFileWrite(
IN PFILE_OBJECT pFileObject,
IN PVOID lpBuffer,
IN SIZE_T nLength,
IN PSIZE_T lpBytesWritten)
{
EngpFileIoRequest(pFileObject,
IRP_MJ_WRITE,
lpBuffer,
nLength,
0,
lpBytesWritten);
}
NTSTATUS
APIENTRY
EngFileIoControl(
IN PFILE_OBJECT pFileObject,
IN DWORD dwIoControlCode,
IN PVOID lpInBuffer,
IN SIZE_T nInBufferSize,
OUT PVOID lpOutBuffer,
IN SIZE_T nOutBufferSize,
OUT LPDWORD lpInformation)
{
PDEVICE_OBJECT pDeviceObject;
KEVENT Event;
PIRP pIrp;
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
/* Get corresponding device object */
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
if (!pDeviceObject)
{
return STATUS_INVALID_PARAMETER;
}
/* Initialize an event */
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
/* Build IO control IRP */
pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
pDeviceObject,
lpInBuffer,
nInBufferSize,
lpOutBuffer,
nOutBufferSize,
FALSE,
&Event,
&Iosb);
if (!pIrp)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Call the driver */
Status = IoCallDriver(pDeviceObject, pIrp);
/* Wait if neccessary */
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
Status = Iosb.Status;
}
/* Return information to the caller about the operation. */
*lpInformation = Iosb.Information;
/* This function returns NTSTATUS */
return Status;
}
/*
* @implemented
*/
DWORD APIENTRY
EngDeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
DWORD *lpBytesReturned)
{
PIRP Irp;
NTSTATUS Status;
KEVENT Event;
IO_STATUS_BLOCK Iosb;
PDEVICE_OBJECT DeviceObject;
DPRINT("EngDeviceIoControl() called\n");
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
DeviceObject = (PDEVICE_OBJECT) hDevice;
Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
DeviceObject,
lpInBuffer,
nInBufferSize,
lpOutBuffer,
nOutBufferSize, FALSE, &Event, &Iosb);
if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
(VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
Status = Iosb.Status;
}
DPRINT("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
Iosb.Information);
/* Return information to the caller about the operation. */
*lpBytesReturned = Iosb.Information;
/* Convert NT status values to win32 error codes. */
switch (Status)
{
case STATUS_INSUFFICIENT_RESOURCES:
return ERROR_NOT_ENOUGH_MEMORY;
case STATUS_BUFFER_OVERFLOW:
return ERROR_MORE_DATA;
case STATUS_NOT_IMPLEMENTED:
return ERROR_INVALID_FUNCTION;
case STATUS_INVALID_PARAMETER:
return ERROR_INVALID_PARAMETER;
case STATUS_BUFFER_TOO_SMALL:
return ERROR_INSUFFICIENT_BUFFER;
case STATUS_DEVICE_DOES_NOT_EXIST:
return ERROR_DEV_NOT_EXIST;
case STATUS_PENDING:
return ERROR_IO_PENDING;
}
return Status;
}
/* EOF */