VBoxVideo.cpp revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/*
*
* Based on DDK sample code.
*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*
*/
#include "VBoxVideo.h"
#include "Helper.h"
#include <VBox/VBoxGuest.h>
#include <VBox/VBoxGuestLib.h>
#define _INC_SWPRINTF_INL_
#endif
#include <wchar.h>
#include "vboxioctl.h"
/*
* Globals for the last custom resolution set. This is important
* for system startup so that we report the last currently set
* custom resolution and Windows can use it again.
*/
ULONG gCustomXRes = 0;
ULONG gCustomYRes = 0;
ULONG gCustomBPP = 0;
{
// nowhere documented but without the following line, NT4 SP0 will choke
// our DDK is at the Win2k3 level so we have to take special measures
// for backwards compatibility
switch (vboxQueryWinVersion())
{
case WINNT4:
break;
case WIN2K:
break;
}
return rc;
}
/*+++
Routine Description:
This routine is used to read back various registry values.
Arguments:
HwDeviceExtension
Supplies a pointer to the miniport's device extension.
Context
Context value passed to the get registry parameters routine.
If this is not null assume it's a ULONG* and save the data value in it.
ValueName
Name of the value requested.
ValueData
Pointer to the requested data.
ValueLength
Length of the requested data.
Return Value:
If the variable doesn't exist return an error,
else if a context is supplied assume it's a PULONG and fill in the value
and return no error, else if the value is non-zero return an error.
---*/
{
//dprintf(("VBoxVideo::VBoxRegistryCallback: Context: %p, ValueName: %S, ValueData: %p, ValueLength: %d\n",
// Context, ValueName, ValueData, ValueLength));
if (ValueLength)
{
if (Context)
return ERROR_INVALID_PARAMETER;
return NO_ERROR;
}
else
return ERROR_INVALID_PARAMETER;
}
/*
* Global list of supported standard video modes. It will be
* filled dynamically.
*/
#define MAX_VIDEO_MODES 128
/* number of available video modes, set by VBoxBuildModesTable */
static uint32_t gNumVideoModes = 0;
/**
* Helper function to dynamically build our table of standard video
* modes. We take the amount of VRAM and create modes with standard
* geometries until we've either reached the maximum number of modes
* or the available VRAM does not allow for additional modes.
*/
{
/* we need this static counter to always have a new mode index for our */
/* custom video mode, otherwise Windows thinks there is no mode switch */
static int gInvocationCounter = 0;
/* the resolution matrix */
struct
{
} resolutionMatrix[] =
{
/* standard modes */
{ 640, 480 },
{ 800, 600 },
{ 1024, 768 },
{ 1152, 864 },
{ 1280, 960 },
{ 1280, 1024 },
{ 1400, 1050 },
{ 1600, 1200 },
{ 1920, 1440 },
/* multi screen modes with 1280x1024 */
{ 2560, 1024 },
{ 3840, 1024 },
{ 5120, 1024 },
/* multi screen modes with 1600x1200 */
{ 3200, 1200 },
{ 4800, 1200 },
{ 6400, 1200 },
};
/* there are 4 color depths: 8, 16, 24 and 32bpp and we reserve 50% of the modes for other sources */
/* size of the VRAM in bytes */
gNumVideoModes = 0;
/*
* Query the y-offset from the host
*/
#if 0 // do not support 8 bit video modes
/*
* 8 bit video modes
*/
matrixIndex = 0;
{
/* are there any modes left in the matrix? */
if (matrixIndex >= matrixSize)
break;
/* does the mode fit into the VRAM? */
{
++matrixIndex;
continue;
}
/* does the host like that mode? */
if (!vboxLikesVideoMode(resolutionMatrix[matrixIndex].xRes, resolutionMatrix[matrixIndex].yRes - yOffset, 8))
{
++matrixIndex;
continue;
}
VideoModes[gNumVideoModes].AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN |
/* a new mode has been filled in */
/* advance to the next mode matrix entry */
++matrixIndex;
}
#endif /* 0 */
/*
* 16 bit video modes
*/
matrixIndex = 0;
{
/* are there any modes left in the matrix? */
if (matrixIndex >= matrixSize)
break;
/* does the mode fit into the VRAM? */
{
++matrixIndex;
continue;
}
/* does the host like that mode? */
if (!vboxLikesVideoMode(resolutionMatrix[matrixIndex].xRes, resolutionMatrix[matrixIndex].yRes - yOffset, 16))
{
++matrixIndex;
continue;
}
VideoModes[gNumVideoModes].AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
/* a new mode has been filled in */
/* advance to the next mode matrix entry */
++matrixIndex;
}
/*
* 24 bit video modes
*/
matrixIndex = 0;
{
/* are there any modes left in the matrix? */
if (matrixIndex >= matrixSize)
break;
/* does the mode fit into the VRAM? */
{
++matrixIndex;
continue;
}
/* does the host like that mode? */
if (!vboxLikesVideoMode(resolutionMatrix[matrixIndex].xRes, resolutionMatrix[matrixIndex].yRes - yOffset, 24))
{
++matrixIndex;
continue;
}
VideoModes[gNumVideoModes].AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
/* a new mode has been filled in */
/* advance to the next mode matrix entry */
++matrixIndex;
}
/*
* 32 bit video modes
*/
matrixIndex = 0;
{
/* are there any modes left in the matrix? */
if (matrixIndex >= matrixSize)
break;
/* does the mode fit into the VRAM? */
{
++matrixIndex;
continue;
}
/* does the host like that mode? */
if (!vboxLikesVideoMode(resolutionMatrix[matrixIndex].xRes, resolutionMatrix[matrixIndex].yRes - yOffset, 32))
{
++matrixIndex;
continue;
}
VideoModes[gNumVideoModes].AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
/* a new mode has been filled in */
/* advance to the next mode matrix entry */
++matrixIndex;
}
/*
* Next, check the registry for additional modes
*/
int curKeyNo = 0;
do
{
/* check if there is space in the mode list */
if (gNumVideoModes >= MAX_VIDEO_MODES)
break;
wchar_t keyname[24];
&xres);
/* upon the first error, we give up */
break;
&yres);
/* upon the first error, we give up */
break;
&bpp);
/* upon the first error, we give up */
break;
dprintf(("VBoxVideo: custom mode %u returned: xres = %u, yres = %u, bpp = %u\n",
/* first test: do the values make sense? */
|| ( (bpp != 16)
&& (bpp != 24)
&& (bpp != 32)))
break;
/* round down width to be a multiple of 8 */
xres &= 0xFFF8;
/* second test: does it fit within our VRAM? */
break;
/* third test: does the host like the video mode? */
break;
dprintf(("VBoxVideo: adding mode from registry: xres = %d, yres = %d, bpp = %d\n", xres, yres, bpp));
/*
* Build mode entry.
* Note that we have to apply the y offset for the custom mode.
*/
switch (bpp)
{
case 16:
break;
case 24:
break;
case 32:
break;
}
VideoModes[gNumVideoModes].AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
/* next run */
curKeyNo++;
/* only support 128 modes for now */
if (curKeyNo >= 128)
break;
} while(1);
/*
* Now we ask the host for a display change request. If there's one,
* this will be appended as a special mode so that it can be used by
* the Additions service process. The mode table is guaranteed to have
* two spare entries for this mode (alternating index thus 2).
*/
{
dprintf(("VBoxVideo: adding custom video mode as #%d, current mode: %d \n", gNumVideoModes + 1, DeviceExtension->CurrentMode));
/* handle the startup case */
if (DeviceExtension->CurrentMode == 0)
{
xres = gCustomXRes;
yres = gCustomYRes;
bpp = gCustomBPP;
}
/* round down to multiple of 8 */
xres &= 0xfff8;
/* take the current values for the fields that are not set */
if (DeviceExtension->CurrentMode != 0)
{
if (!xres)
if (!yres)
if (!bpp)
}
/* does the host like that mode? */
{
/* we must have a valid video mode by now and it must fit within the VRAM */
if ( ( xres
&& yres
&& ( (bpp == 16)
|| (bpp == 24)
|| (bpp == 32)))
{
/* we need an alternating index */
if (DeviceExtension->CurrentMode != 0)
{
if (gInvocationCounter % 2)
}
/*
* Build mode entry.
* Note that we do not apply the y offset for the custom mode. It is
* only used for the predefined modes that the user can configure in
* the display properties dialog.
*/
switch (bpp)
{
case 16:
break;
case 24:
break;
case 32:
break;
}
VideoModes[gNumVideoModes].AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
/* for the startup case, we need this mode twice due to the alternating mode number */
if (DeviceExtension->CurrentMode == 0)
{
memcpy(&VideoModes[gNumVideoModes], &VideoModes[gNumVideoModes - 1], sizeof(VIDEO_MODE_INFORMATION));
}
/* store this video mode as the last custom video mode */
}
else
dprintf(("VBoxVideo: invalid parameters for special mode: (xres = %d, yres = %d, bpp = %d, vramSize = %d)\n",
}
else
dprintf(("VBoxVideo: host does not like special mode: (xres = %d, yres = %d, bpp = %d)\n",
}
}
{
{
{
0,
0
}
};
dprintf(("VBoxVideo::VBoxVideoFindAdapter\n"));
if (DispiId == VBE_DISPI_ID2)
{
dprintf(("VBoxVideo::VBoxVideoFoundAdapter: found the VBE card\n"));
/*
* Write some hardware information to registry, so that
* it's visible in Windows property dialog.
*/
L"HardwareInformation.ChipType",
sizeof(VBoxChipType));
L"HardwareInformation.DacType",
sizeof(VBoxDACType));
/*
* Query the adapter's memory size. It's a bit of a hack, we just read
* an ULONG from the data port without setting an index before.
*/
L"HardwareInformation.MemorySize",
sizeof(ULONG));
L"HardwareInformation.AdapterString",
sizeof(VBoxAdapterString));
L"HardwareInformation.BiosString",
sizeof(VBoxBiosString));
// @todo for some reason, I get an ERROR_INVALID_PARAMETER from NT4 SP0
// It does not seem to like getting me these port addresses. So I just
// pretend success to make the driver work.
/* Initialize VBoxGuest library */
// pretend success to make the driver work.
} else
{
dprintf(("VBoxVideo::VBoxVideoFindAdapter: VBE card not found, returning ERROR_DEV_NOT_EXIST\n"));
}
return rc;
}
/**
* VBoxVideoInitialize
*
* Performs the first initialization of the adapter, after the HAL has given
* up control of the video hardware to the video port driver.
*/
{
dprintf(("VBoxVideo::VBoxVideoInitialize\n"));
/* Initialize the request pointer. */
/*
* Get the last custom resolution
*/
L"CustomXRes",
&gCustomXRes);
gCustomXRes = 0;
L"CustomYRes",
&gCustomYRes);
gCustomYRes = 0;
L"CustomBPP",
&gCustomBPP);
gCustomBPP = 0;
dprintf(("VBoxVideo: got stored custom resolution %dx%dx%d\n", gCustomXRes, gCustomYRes, gCustomBPP));
return TRUE;
}
/**
* VBoxVideoStartIO
*
* Processes the specified Video Request Packet.
*/
{
// dprintf(("VBoxVideo::VBoxVideoStartIO: code %08X\n", RequestPacket->IoControlCode));
switch (RequestPacket->IoControlCode)
{
{
{
return TRUE;
}
break;
}
case IOCTL_VIDEO_RESET_DEVICE:
{
break;
}
{
{
return TRUE;
}
break;
}
{
{
return TRUE;
}
break;
}
/*
* The display driver asks us how many video modes we support
* so that it can supply an appropriate buffer for the next call.
*/
{
{
return TRUE;
}
break;
}
/*
* The display driver asks us to provide a list of supported video modes
* into a buffer it has allocated.
*/
{
if (RequestPacket->OutputBufferLength <
gNumVideoModes * sizeof(VIDEO_MODE_INFORMATION))
{
return TRUE;
}
break;
}
{
sizeof(VIDEO_CLUT))
{
return TRUE;
}
break;
}
{
{
return TRUE;
}
break;
}
// show the pointer
{
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_ENABLE_POINTER\n"));
// find out whether the host wants absolute positioning
if (vboxQueryHostWantsAbsolute())
{
// tell the host to use the guest's pointer
/* Visible and No Shape means Show the pointer.
* It is enough to init only this field.
*/
if (!Result)
dprintf(("VBoxVideo::VBoxVideoStartIO: could not hide hardware pointer -> fallback\n"));
} else
{
// fallback to software pointer
}
break;
}
// hide the pointer
{
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_DISABLE_POINTER\n"));
// find out whether the host wants absolute positioning
if (vboxQueryHostWantsAbsolute())
{
// tell the host to hide pointer
/* Enable == 0 means no shape, not visible.
* It is enough to init only this field.
*/
PointerAttributes.Enable = 0;
if (!Result)
dprintf(("VBoxVideo::VBoxVideoStartIO: could not hide hardware pointer -> fallback\n"));
} else
{
// fallback to software pointer
}
break;
}
/*
* Change the pointer shape
*/
{
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_SET_POINTER_ATTR\n"));
{
dprintf(("VBoxVideo::VBoxVideoStartIO: Input buffer too small (%d bytes)\n", RequestPacket->InputBufferLength));
return TRUE;
}
// find out whether the host wants absolute positioning
if (vboxQueryHostWantsAbsolute())
{
PVIDEO_POINTER_ATTRIBUTES pPointerAttributes = (PVIDEO_POINTER_ATTRIBUTES)RequestPacket->InputBuffer;
#if 0
dprintf(("Pointer shape information:\n"
"\tFlags: %d\n"
"\tWidth: %d\n"
"\tHeight: %d\n"
"\tWidthInBytes: %d\n"
"\tEnable: %d\n"
"\tColumn: %d\n"
"\tRow: %d\n",
dprintf(("\tBytes attached: %d\n", RequestPacket->InputBufferLength - sizeof(VIDEO_POINTER_ATTRIBUTES)));
#endif
if (!Result)
dprintf(("VBoxVideo::VBoxVideoStartIO: could not set hardware pointer -> fallback\n"));
} else
{
dprintf(("VBoxVideo::VBoxVideoStartIO: fallback to software pointer\n"));
// fallback to software pointer
}
break;
}
// query pointer information
{
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_QUERY_POINTER_ATTR\n"));
break;
}
// set the pointer position
{
/// @todo There is an issue when we disable pointer integration.
// The guest pointer will be invisible. We have to somehow cause
// the pointer attributes to be set again. But how? The same holds
// true for the opposite case where we get two pointers.
//dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_SET_POINTER_POSITION\n"));
// find out whether the host wants absolute positioning
if (vboxQueryHostWantsAbsolute())
{
// @todo we are supposed to show currently invisible pointer?
} else
{
// fallback to software pointer
}
break;
}
// query the pointer position
{
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_QUERY_POINTER_POSITION\n"));
{
dprintf(("VBoxVideo::VBoxVideoStartIO: output buffer too small!\n"));
return TRUE;
}
{
// map from 0xFFFF to the current resolution
}
if (!Result)
{
// fallback to software pointer
}
break;
}
// Determine hardware cursor capabilities. We will always report that we are
// very capable even though the host might not want to do pointer integration.
// This is done because we can still return errors on the actual calls later to
// make the display driver go to the fallback routines.
{
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_QUERY_POINTER_CAPABILITIES\n"));
{
dprintf(("VBoxVideo::VBoxVideoStartIO: output buffer too small!\n"));
return TRUE;
}
// for now we go with 64x64 cursors
// that doesn't seem to be relevant, VBoxDisp doesn't use it
break;
}
case IOCTL_VIDEO_VBVA_ENABLE:
{
int rc;
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_VBVA_ENABLE\n"));
{
dprintf(("VBoxVideo::VBoxVideoStartIO: input buffer too small: %d needed: %d!!!\n",
return FALSE;
}
{
dprintf(("VBoxVideo::VBoxVideoStartIO: output buffer too small: %d needed: %d!!!\n",
return FALSE;
}
if (VBOX_FAILURE (rc))
{
dprintf(("VBoxVideo::VBoxVideoStartIO: IOCTL_VIDEO_VBVA_ENABLE: failed to enable VBVA\n"));
return FALSE;
}
break;
}
default:
return FALSE;
}
if (Result)
else
// dprintf(("VBoxVideo::VBoxVideoStartIO: completed\n"));
return TRUE;
}
/**
* VBoxVideoReset HW
*
* Resets the video hardware.
*/
{
dprintf(("VBoxVideo::VBoxVideoResetHW\n"));
{
}
VbglTerminate ();
return TRUE;
}
/**
* VBoxVideoGetPowerState
*
* Queries whether the device can support the requested power state.
*/
{
dprintf(("VBoxVideo::VBoxVideoGetPowerState\n"));
return NO_ERROR;
}
/**
* VBoxVideoSetPowerState
*
* Sets the power state of the specified device
*/
{
dprintf(("VBoxVideo::VBoxVideoSetPowerState\n"));
return NO_ERROR;
}
/**
* VBoxVideoSetCurrentMode
*
* Sets the adapter to the specified operating mode.
*/
{
/* set the mode characteristics */
/* enable the mode */
VideoPortWritePortUshort((PUSHORT)VBE_DISPI_IOPORT_DATA, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
/** @todo read from the port to see if the mode switch was successful */
return TRUE;
}
/*
* VBoxVideoResetDevice
*
* Resets the video hardware to the default mode, to which it was initialized
* at system boot.
*/
{
dprintf(("VBoxVideo::VBoxVideoResetDevice\n"));
return TRUE;
}
/**
* VBoxVideoMapVideoMemory
*
* Maps the video hardware frame buffer and video RAM into the virtual address
* space of the requestor.
*/
{
dprintf(("VBoxVideo::VBoxVideoMapVideoMemory\n"));
) >> 3;
{
return TRUE;
}
return FALSE;
}
/**
* VBoxVideoUnmapVideoMemory
*
* Releases a mapping between the virtual address space and the adapter's
* frame buffer and video RAM.
*/
{
dprintf(("VBoxVideo::VBoxVideoUnmapVideoMemory\n"));
return TRUE;
}
/**
* VBoxVideoQueryNumAvailModes
*
* Returns the number of video modes supported by the adapter and the size
* in bytes of the video mode information, which can be used to allocate a
* buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request.
*/
{
dprintf(("VBoxVideo::VBoxVideoQueryNumAvailModes\n"));
/* calculate the video modes table */
return TRUE;
}
/**
* VBoxVideoQueryAvailModes
*
* Returns information about each video mode supported by the adapter.
*/
{
dprintf(("VBoxVideo::VBoxVideoQueryAvailModes\n"));
return TRUE;
}
/**
* VBoxVideoQueryCurrentMode
*
* Returns information about current video mode.
*/
{
dprintf(("VBoxVideo::VBoxVideoQueryCurrentMode\n"));
return TRUE;
}
/*
* VBoxVideoSetColorRegisters
*
* Sets the adapter's color registers to the specified RGB values. There
* are code paths in this function, one generic and one for VGA compatible
* controllers. The latter is needed for Bochs, where the generic one isn't
* yet implemented.
*/
{
dprintf(("VBoxVideo::VBoxVideoSetColorRegisters\n"));
return FALSE;
Entry++)
{
}
return TRUE;
}
{
dprintf(("VBoxVideo::VBoxVideoGetChildDescriptor\n"));
return ERROR_NO_MORE_DEVICES;
}
{
{
{
}
}
return;
}
{
int rc = VINF_SUCCESS;
/*
* Query the VMMDev memory pointer. There we need VBVAMemory.
*/
dprintf(("VBoxVideo::vboxVbvaEnable: VbglQueryVMMDevMemory rc = %d, pVMMDevMemory = %p\n", rc, pVMMDevMemory));
if (VBOX_SUCCESS(rc))
{
/* Allocate the memory block for VMMDevReq_VideoAccelFlush request. */
{
sizeof (VMMDevVideoAccelFlush),
if (VBOX_SUCCESS (rc))
{
}
else
{
}
}
}
else
{
}
if (VBOX_SUCCESS(rc))
{
/*
* Tell host that VBVA status is changed.
*/
sizeof (VMMDevVideoAccelEnable),
if (VBOX_SUCCESS(rc))
{
req->fu32Status = 0;
{
{
/*
* Initialize the result information and VBVA memory.
*/
{
}
else
{
}
dprintf(("VBoxVideo::vboxVbvaEnable: success.\n"));
}
else
{
dprintf(("VBoxVideo::vboxVbvaEnable: not accepted.\n"));
/* Disable VBVA for old hosts. */
req->fu32Status = 0;
}
}
else
{
if (VBOX_SUCCESS(rc))
{
}
}
}
else
{
}
}
return rc;
}