1989 lines
53 KiB
C
1989 lines
53 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vesa.c
|
|
|
|
Abstract:
|
|
|
|
This module implements VESA support.
|
|
|
|
Author:
|
|
|
|
Erick Smith (ericks) Sep. 2000
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "dderror.h"
|
|
#include "devioctl.h"
|
|
#include "miniport.h"
|
|
|
|
#include "ntddvdeo.h"
|
|
#include "video.h"
|
|
#include "vga.h"
|
|
#include "vesa.h"
|
|
|
|
#if defined(ALLOC_PRAGMA)
|
|
#pragma alloc_text(PAGE,ValidateVbeInfo)
|
|
#pragma alloc_text(PAGE,InitializeModeTable)
|
|
#pragma alloc_text(PAGE,UpdateRegistry)
|
|
#pragma alloc_text(PAGE,VgaSaveHardwareState)
|
|
#pragma alloc_text(PAGE,VesaSaveHardwareState)
|
|
#pragma alloc_text(PAGE,GetVideoMemoryBaseAddress)
|
|
#pragma alloc_text(PAGE,RaiseToPower2)
|
|
#pragma alloc_text(PAGE,RaiseToPower2Ulong)
|
|
#pragma alloc_text(PAGE,IsPower2)
|
|
#pragma alloc_text(PAGE,VBESetMode)
|
|
#pragma alloc_text(PAGE,VBEGetMode)
|
|
#pragma alloc_text(PAGE,VBEGetModeInfo)
|
|
#pragma alloc_text(PAGE,VBESaveState)
|
|
#pragma alloc_text(PAGE,VBERestoreState)
|
|
#pragma alloc_text(PAGE,VBESetDisplayWindow)
|
|
#pragma alloc_text(PAGE,VBEGetDisplayWindow)
|
|
#pragma alloc_text(PAGE,VBEGetScanLineLength)
|
|
#pragma alloc_text(PAGE,IsSavedModeVesa)
|
|
#pragma alloc_text(PAGE,VesaSaveHardwareState)
|
|
#pragma alloc_text(PAGE,VesaRestoreHardwareState)
|
|
#pragma alloc_text(PAGE,SaveFrameBuffer)
|
|
#pragma alloc_text(PAGE,RestoreFrameBuffer)
|
|
#endif
|
|
|
|
USHORT
|
|
RaiseToPower2(
|
|
USHORT x
|
|
)
|
|
|
|
{
|
|
USHORT Mask = x;
|
|
|
|
if (Mask & (Mask - 1)) {
|
|
|
|
Mask = 1;
|
|
|
|
while (Mask < x && Mask != 0) {
|
|
Mask <<= 1;
|
|
}
|
|
}
|
|
|
|
return Mask;
|
|
}
|
|
|
|
ULONG
|
|
RaiseToPower2Ulong(
|
|
ULONG x
|
|
)
|
|
|
|
{
|
|
ULONG Mask = x;
|
|
|
|
if (Mask & (Mask - 1)) {
|
|
|
|
Mask = 1;
|
|
|
|
while (Mask < x && Mask != 0) {
|
|
Mask <<= 1;
|
|
}
|
|
}
|
|
|
|
return Mask;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsPower2(
|
|
USHORT x
|
|
)
|
|
|
|
{
|
|
return( !(x & (x- 1)) );
|
|
}
|
|
|
|
VOID
|
|
UpdateRegistry(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
PWSTR ValueName,
|
|
PUCHAR Value
|
|
)
|
|
|
|
/*++
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Len = (strlen(Value) + 1) * 2;
|
|
PWSTR WideString;
|
|
|
|
WideString = VideoPortAllocatePool(hwDeviceExtension,
|
|
VpPagedPool,
|
|
Len,
|
|
' agV');
|
|
|
|
if (WideString) {
|
|
|
|
PWSTR ptr = WideString;
|
|
|
|
while(*Value) {
|
|
*ptr++ = (WCHAR) *Value++;
|
|
}
|
|
*ptr = 0;
|
|
|
|
VideoPortSetRegistryParameters(hwDeviceExtension,
|
|
ValueName,
|
|
WideString,
|
|
Len);
|
|
|
|
VideoPortFreePool(hwDeviceExtension, WideString);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
IsVesaBiosOk(
|
|
PVIDEO_PORT_INT10_INTERFACE pInt10,
|
|
USHORT OemSoftwareRev,
|
|
PUCHAR OemVendorName,
|
|
PUCHAR OemProductName,
|
|
PUCHAR OemProductRev
|
|
)
|
|
|
|
{
|
|
|
|
VideoDebugPrint((1, "OemSoftwareRev = %d\n", OemSoftwareRev));
|
|
VideoDebugPrint((1, "OemVendorName = '%s'\n", OemVendorName));
|
|
VideoDebugPrint((1, "OemProductName = '%s'\n", OemProductName));
|
|
VideoDebugPrint((1, "OemProductRev = '%s'\n", OemProductRev));
|
|
|
|
//
|
|
// The ATI ArtX boxes currently have a VESA Bios bug where
|
|
// they indicate they support linear mode access when
|
|
// they don't. Fail these boards.
|
|
//
|
|
|
|
if ((strcmp(OemProductName, "ATI S1-370TL") == 0) ||
|
|
(strcmp(OemProductName, "ArtX I") == 0)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Several 3dfx boards have buggy vesa bioses. The mode set
|
|
// works, but the display is corrupted.
|
|
//
|
|
|
|
if ((strcmp(OemProductName, "Voodoo4 4500 ") == 0) ||
|
|
(strcmp(OemProductName, "Voodoo3 3000 LC ") == 0) ||
|
|
(strcmp(OemProductName, "Voodoo3 2000 LC ") == 0) ||
|
|
(strcmp(OemProductName, "3Dfx Banshee") == 0)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Matrox G100 boards with rev 1.05 bioses can't set vesa modes.
|
|
// We hang in the bios.
|
|
//
|
|
|
|
if ((strcmp(OemProductName, "MGA-G100") == 0) &&
|
|
(OemSoftwareRev == 0x105)) {
|
|
|
|
//
|
|
// We must also disable 800x600 16 color mode for this
|
|
// device. This makes the assumption that the mode we
|
|
// are deleting is the last mode in our table.
|
|
//
|
|
|
|
NumVideoModes--;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We have seen at least on SIS 5597 part which returns a bad
|
|
// linear address. Lets disable these parts.
|
|
//
|
|
|
|
if (strcmp(OemProductName, "SiS 5597") == 0) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We found a bad nVidia GeForce MX part. It hangs in the bios
|
|
// on boot.
|
|
//
|
|
|
|
if ((strcmp(OemVendorName, "NVidia Corporation") == 0) &&
|
|
(strcmp(OemProductName, "NV11 (GeForce2) Board") == 0) &&
|
|
(strcmp(OemProductRev, "Chip Rev B2") == 0) &&
|
|
(OemSoftwareRev == 0x311)) {
|
|
|
|
//
|
|
// This bios "may" be buggy, but in an effort to not kill
|
|
// vesa support on all GeForce MX boards, lets also look at
|
|
// the version string embedded in the bios.
|
|
//
|
|
// We know the bad bios's have the following string at location
|
|
// c000:0159:
|
|
//
|
|
// "Version 3.11.01.24N16"
|
|
//
|
|
// Lets read from this location and try to match on this string
|
|
//
|
|
// NOTE: This call requires that the VDM memory be allocated
|
|
// with Int10AllocateBuffer. Since our calling function has
|
|
// this allocated, we are ok.
|
|
//
|
|
|
|
UCHAR Version[22];
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
(USHORT)0xC000,
|
|
(USHORT)0x0159,
|
|
Version,
|
|
21) != NO_ERROR) {
|
|
return FALSE;
|
|
}
|
|
|
|
Version[21] = 0;
|
|
|
|
if (strcmp(Version, "Version 3.11.01.24N16") == 0) {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
ValidateVbeInfo(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
PVGA_INFO_BLOCK InfoBlock
|
|
)
|
|
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
This routine makes the assumption that the InfoBlock is still
|
|
valid in the VDM transfer area.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVIDEO_PORT_INT10_INTERFACE pInt10;
|
|
BOOLEAN UseVesa = FALSE;
|
|
|
|
pInt10 = &hwDeviceExtension->Int10;
|
|
|
|
if (InfoBlock->VesaSignature == 'ASEV') {
|
|
|
|
PUCHAR OemString;
|
|
UCHAR OemStringBuffer[80];
|
|
UCHAR OemVendorName[80];
|
|
UCHAR OemProductName[80];
|
|
UCHAR OemProductRev[80];
|
|
ULONG MemorySize;
|
|
|
|
//
|
|
// Capture OEM String
|
|
//
|
|
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
(USHORT)SEG(InfoBlock->OemStringPtr),
|
|
(USHORT)OFF(InfoBlock->OemStringPtr),
|
|
OemStringBuffer,
|
|
80) != NO_ERROR) {
|
|
goto FallOut;
|
|
}
|
|
|
|
OemStringBuffer[79] = 0;
|
|
OemString = OemStringBuffer;
|
|
|
|
VideoDebugPrint((1, "*********************************************\n"));
|
|
VideoDebugPrint((1, " VBE Signature: VESA\n"));
|
|
VideoDebugPrint((1, " VBE Version: %d.%02d\n",
|
|
InfoBlock->VbeVersion >> 8,
|
|
InfoBlock->VbeVersion & 0xff));
|
|
VideoDebugPrint((1, " OEM String: %s\n",
|
|
OemString));
|
|
|
|
if (InfoBlock->TotalMemory < 16) {
|
|
|
|
//
|
|
// If less than 1 meg, display in KB
|
|
//
|
|
|
|
VideoDebugPrint((1, " Total Memory: %dKB\n",
|
|
InfoBlock->TotalMemory * 64));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Else display in MB
|
|
//
|
|
|
|
VideoDebugPrint((1, " Total Memory: %dMB\n",
|
|
InfoBlock->TotalMemory / 16));
|
|
}
|
|
|
|
if (InfoBlock->VbeVersion >= 0x102) {
|
|
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
(USHORT)SEG(InfoBlock->OemVendorNamePtr),
|
|
(USHORT)OFF(InfoBlock->OemVendorNamePtr),
|
|
OemVendorName,
|
|
80) != NO_ERROR){
|
|
goto FallOut;
|
|
}
|
|
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
(USHORT)SEG(InfoBlock->OemProductNamePtr),
|
|
(USHORT)OFF(InfoBlock->OemProductNamePtr),
|
|
OemProductName,
|
|
80) != NO_ERROR){
|
|
goto FallOut;
|
|
}
|
|
|
|
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
(USHORT)SEG(InfoBlock->OemProductRevPtr),
|
|
(USHORT)OFF(InfoBlock->OemProductRevPtr),
|
|
OemProductRev,
|
|
80) != NO_ERROR){
|
|
goto FallOut;
|
|
}
|
|
|
|
|
|
OemVendorName[79] = 0;
|
|
OemProductName[79] = 0;
|
|
OemProductRev[79] = 0;
|
|
|
|
VideoDebugPrint((1, " OEM Software Rev: %d.%02d\n",
|
|
InfoBlock->OemSoftwareRev >> 8,
|
|
InfoBlock->OemSoftwareRev & 0xff));
|
|
VideoDebugPrint((1, " OEM Vendor Name: %s\n", OemVendorName));
|
|
VideoDebugPrint((1, " OEM Product Name: %s\n", OemProductName));
|
|
VideoDebugPrint((1, " OEM Product Rev: %s\n", OemProductRev));
|
|
|
|
UseVesa = IsVesaBiosOk(pInt10,
|
|
InfoBlock->OemSoftwareRev,
|
|
OemVendorName,
|
|
OemProductName,
|
|
OemProductRev);
|
|
|
|
}
|
|
|
|
VideoDebugPrint((1, "*********************************************\n"));
|
|
|
|
#if defined(PLUG_AND_PLAY)
|
|
|
|
//
|
|
// It would be nice if we could dump the following info into the
|
|
// registry. But as the GDI code currently stands, if we add
|
|
// ChipType or AdapterString info into the registry, we lose
|
|
// fullscreen support. This has to do with the way GDI currently
|
|
// determines the fullscreen device.
|
|
//
|
|
// For now, lets just not add this registry info.
|
|
//
|
|
|
|
UpdateRegistry(hwDeviceExtension,
|
|
L"HardwareInformation.ChipType",
|
|
OemString);
|
|
|
|
//
|
|
// Adapter String MUST be VGA. Without it, the system won't
|
|
// recognize this driver as the VGA driver.
|
|
//
|
|
|
|
UpdateRegistry(hwDeviceExtension,
|
|
L"HardwareInformation.AdapterString",
|
|
"VGA");
|
|
|
|
UpdateRegistry(hwDeviceExtension,
|
|
L"HardwareInformation.DacType",
|
|
(InfoBlock->Capabilities & VBE_CAP_DAC_WIDTH_8BPP)
|
|
? "8 bit" : "6 bit");
|
|
|
|
UpdateRegistry(hwDeviceExtension,
|
|
L"HardwareInformation.BiosString",
|
|
OemProductRev);
|
|
|
|
//
|
|
// Store memory size in registry
|
|
//
|
|
|
|
MemorySize = InfoBlock->TotalMemory << 16;
|
|
|
|
VideoPortSetRegistryParameters(hwDeviceExtension,
|
|
L"HardwareInformation.MemorySize",
|
|
&MemorySize,
|
|
sizeof(ULONG));
|
|
#endif
|
|
|
|
} else {
|
|
|
|
VideoDebugPrint((0, "Invalid VBE Info Block.\n"));
|
|
}
|
|
|
|
FallOut:
|
|
|
|
return UseVesa;
|
|
}
|
|
|
|
VOID
|
|
InitializeModeTable(
|
|
PVOID HwDeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does one time initialization of the device.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - Pointer to the miniport driver's adapter information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension = HwDeviceExtension;
|
|
INT10_BIOS_ARGUMENTS BiosArguments;
|
|
PVGA_INFO_BLOCK InfoBlock;
|
|
PMODE_INFO_BLOCK ModeBlock;
|
|
PUSHORT ModeTable;
|
|
PUSHORT ModePtr;
|
|
ULONG NumVesaModes;
|
|
PVIDEOMODE VideoModePtr;
|
|
LONG TotalMemory;
|
|
ULONG VideoMemoryRequired;
|
|
USHORT VbeVersion;
|
|
PULONG Memory;
|
|
ULONG AdditionalModes = 0;
|
|
|
|
USHORT VdmSeg, VdmOff;
|
|
VP_STATUS Status;
|
|
PVIDEO_PORT_INT10_INTERFACE pInt10;
|
|
ULONG Length = 0x1000;
|
|
|
|
BOOLEAN LinearModeSupported;
|
|
BOOLEAN ModeValid;
|
|
|
|
VgaModeList = ModesVGA;
|
|
|
|
#if !defined(PLUG_AND_PLAY)
|
|
|
|
//
|
|
// To avoid problems on high-end servers with bus-relative resources
|
|
// being reported by the VESA BIOS that we will not be able to translate
|
|
// without PnP support, use the specially defined boot flag NOVESA to
|
|
// disable anything more than legacy VGA.
|
|
// Zero the key int10 fields just as if VESA support was unavailable.
|
|
//
|
|
if(VideoPortIsNoVesa()){
|
|
|
|
hwDeviceExtension->Int10.Size = 0;
|
|
hwDeviceExtension->Int10.Version = 0;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
hwDeviceExtension->Int10.Size = sizeof(VIDEO_PORT_INT10_INTERFACE);
|
|
hwDeviceExtension->Int10.Version = 1;
|
|
|
|
Status = VideoPortQueryServices(hwDeviceExtension,
|
|
VideoPortServicesInt10,
|
|
(PINTERFACE)&hwDeviceExtension->Int10);
|
|
if(Status != NO_ERROR){
|
|
|
|
//
|
|
// Set these fields to zero so that later we know the int10
|
|
// interface is not available
|
|
//
|
|
|
|
hwDeviceExtension->Int10.Size = 0;
|
|
hwDeviceExtension->Int10.Version = 0;
|
|
return;
|
|
}
|
|
|
|
pInt10 = &hwDeviceExtension->Int10;
|
|
|
|
pInt10->InterfaceReference(pInt10->Context);
|
|
|
|
//
|
|
// Get a chunk of memory in VDM area to use for buffers.
|
|
//
|
|
|
|
Status = pInt10->Int10AllocateBuffer(pInt10->Context,
|
|
&VdmSeg,
|
|
&VdmOff,
|
|
&Length);
|
|
|
|
if (Status != NO_ERROR) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate Memory
|
|
//
|
|
|
|
InfoBlock = VideoPortAllocatePool(hwDeviceExtension,
|
|
VpPagedPool,
|
|
sizeof(VGA_INFO_BLOCK) +
|
|
sizeof(MODE_INFO_BLOCK) +
|
|
256 +
|
|
2, // space for a 0xffff terminator
|
|
' agV');
|
|
|
|
if (InfoBlock) {
|
|
|
|
ModeBlock = (PMODE_INFO_BLOCK)((ULONG_PTR)InfoBlock + sizeof(VGA_INFO_BLOCK));
|
|
ModeTable = (PUSHORT)((ULONG_PTR)ModeBlock + sizeof(MODE_INFO_BLOCK));
|
|
|
|
ModeTable[128] = 0xffff; // make sure we have a mode terminator
|
|
|
|
//
|
|
// Get VESA mode information
|
|
//
|
|
|
|
InfoBlock->VesaSignature = '2EBV';
|
|
|
|
if(pInt10->Int10WriteMemory(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff,
|
|
InfoBlock,
|
|
sizeof(VGA_INFO_BLOCK)) != NO_ERROR){
|
|
goto FallOut;
|
|
}
|
|
|
|
//
|
|
// Get SuperVGA support info
|
|
//
|
|
|
|
BiosArguments.Eax = 0x4f00;
|
|
BiosArguments.Edi = VdmOff;
|
|
BiosArguments.SegEs = VdmSeg;
|
|
|
|
if(pInt10->Int10CallBios(pInt10->Context,
|
|
&BiosArguments) != NO_ERROR ||
|
|
|
|
!VESA_SUCCESS(BiosArguments.Eax)) {
|
|
|
|
goto FallOut;
|
|
}
|
|
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff,
|
|
InfoBlock,
|
|
sizeof(VGA_INFO_BLOCK)) != NO_ERROR) {
|
|
goto FallOut;
|
|
}
|
|
|
|
TotalMemory = InfoBlock->TotalMemory * 0x10000;
|
|
VbeVersion = InfoBlock->VbeVersion;
|
|
|
|
//
|
|
// NOTE: We must call ValidateVbeInfo while the info block
|
|
// is still in the transfer area.
|
|
//
|
|
|
|
if (ValidateVbeInfo(hwDeviceExtension, InfoBlock)) {
|
|
|
|
//
|
|
// Capture the list of mode numbers
|
|
//
|
|
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
(USHORT)(InfoBlock->VideoModePtr >> 16),
|
|
(USHORT)(InfoBlock->VideoModePtr & 0xffff),
|
|
ModeTable,
|
|
256) != NO_ERROR) {
|
|
goto FallOut;
|
|
}
|
|
|
|
{
|
|
BOOLEAN Mode800x600x4Supported = FALSE;
|
|
|
|
//
|
|
// Count the number of VESA modes, and allocate memory for the
|
|
// mode list. The mode list is terminated by a -1.
|
|
//
|
|
|
|
ModePtr = ModeTable;
|
|
NumVesaModes = 0;
|
|
|
|
while (*ModePtr != 0xffff) {
|
|
|
|
if((*ModePtr & 0x1ff) == 0x102 ||
|
|
(*ModePtr & 0x1ff) == 0x6a ) {
|
|
|
|
Mode800x600x4Supported = TRUE;
|
|
}
|
|
|
|
NumVesaModes++;
|
|
ModePtr++;
|
|
}
|
|
|
|
//
|
|
// We disable 800x600 16 color mode unless this mode
|
|
// is in the mode list. This makes the assumption that
|
|
// the 800x600x16 mode is the last mode in our table.
|
|
//
|
|
|
|
if(!Mode800x600x4Supported) {
|
|
|
|
NumVideoModes--;
|
|
}
|
|
}
|
|
|
|
if (NumVesaModes == 128) {
|
|
|
|
//
|
|
// Something is wrong. We hit our hard coded terminator.
|
|
// Don't try to use vesa modes.
|
|
//
|
|
|
|
goto FallOut;
|
|
|
|
}
|
|
|
|
VgaModeList = VideoPortAllocatePool(hwDeviceExtension,
|
|
VpPagedPool,
|
|
(NumVesaModes + NumVideoModes) *
|
|
sizeof(VIDEOMODE),
|
|
' agV');
|
|
|
|
if (VgaModeList == NULL) {
|
|
|
|
VideoDebugPrint((0, "failed to allocate %d bytes.\n",
|
|
(NumVesaModes + NumVideoModes) * sizeof(VIDEOMODE)));
|
|
|
|
VgaModeList = ModesVGA;
|
|
|
|
//
|
|
// Perform clean up.
|
|
//
|
|
|
|
VideoPortFreePool(hwDeviceExtension, InfoBlock);
|
|
|
|
pInt10->Int10FreeBuffer(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Copy the existing constant VGA modes into our mode list table.
|
|
//
|
|
|
|
memmove(VgaModeList, ModesVGA, sizeof(VIDEOMODE) * NumVideoModes);
|
|
|
|
//
|
|
// Now populate the rest of the table based on the VESA mode
|
|
// table.
|
|
//
|
|
|
|
VideoModePtr = VgaModeList + NumVideoModes;
|
|
ModePtr = ModeTable;
|
|
|
|
while (NumVesaModes--) {
|
|
|
|
ModeValid = FALSE;
|
|
|
|
//
|
|
// Get info about the VESA mode.
|
|
//
|
|
|
|
BiosArguments.Eax = 0x4f01;
|
|
BiosArguments.Ecx = *ModePtr;
|
|
BiosArguments.Edi = VdmOff;
|
|
BiosArguments.SegEs = VdmSeg;
|
|
|
|
Status = pInt10->Int10CallBios(pInt10->Context,
|
|
&BiosArguments);
|
|
if(Status == NO_ERROR &&
|
|
VESA_SUCCESS(BiosArguments.Eax) &&
|
|
pInt10->Int10ReadMemory(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff,
|
|
ModeBlock,
|
|
sizeof(MODE_INFO_BLOCK)) == NO_ERROR){
|
|
|
|
//
|
|
// Make sure that this is a graphics mode, and
|
|
// that it is supported by this hardware.
|
|
//
|
|
|
|
if ((ModeBlock->ModeAttributes & 0x11) == 0x11) {
|
|
|
|
if ((VbeVersion >= 0x200) &&
|
|
(ModeBlock->PhysBasePtr) &&
|
|
(ModeBlock->ModeAttributes & 0x80)) {
|
|
|
|
LinearModeSupported = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Make sure banked modes are supported
|
|
//
|
|
|
|
ASSERT((ModeBlock->ModeAttributes & 0x40) == 0);
|
|
LinearModeSupported = FALSE;
|
|
}
|
|
|
|
//
|
|
// Only include this mode if the following are true:
|
|
//
|
|
// 1. The mode is an 8bpp or higher mode
|
|
// 2. The resolution is 640x480 or greater
|
|
//
|
|
|
|
if ((ModeBlock->XResolution >= 640) &&
|
|
(ModeBlock->YResolution >= 480) &&
|
|
(ModeBlock->NumberOfPlanes != 0) &&
|
|
(ModeBlock->BitsPerPixel >= 8)) {
|
|
|
|
//
|
|
// Fill in the video mode structure.
|
|
//
|
|
|
|
memset(VideoModePtr, 0, sizeof(VIDEOMODE));
|
|
|
|
if (ModeBlock->ModeAttributes & 0x08) {
|
|
VideoModePtr->fbType |= VIDEO_MODE_COLOR;
|
|
}
|
|
|
|
if (ModeBlock->ModeAttributes & 0x10) {
|
|
VideoModePtr->fbType |= VIDEO_MODE_GRAPHICS;
|
|
}
|
|
|
|
VideoModePtr->numPlanes = ModeBlock->NumberOfPlanes;
|
|
VideoModePtr->bitsPerPlane = ModeBlock->BitsPerPixel /
|
|
ModeBlock->NumberOfPlanes;
|
|
|
|
if (VideoModePtr->bitsPerPlane == 16) {
|
|
|
|
//
|
|
// Check to see if this is really a 15 bpp mode
|
|
//
|
|
|
|
if (ModeBlock->GreenMaskSize == 5) {
|
|
VideoModePtr->bitsPerPlane = 15;
|
|
}
|
|
}
|
|
|
|
if (ModeBlock->XCharSize) {
|
|
VideoModePtr->col = ModeBlock->XResolution / ModeBlock->XCharSize;
|
|
} else {
|
|
VideoModePtr->col = 80;
|
|
}
|
|
|
|
if (ModeBlock->YCharSize) {
|
|
VideoModePtr->row = ModeBlock->YResolution / ModeBlock->YCharSize;
|
|
} else {
|
|
VideoModePtr->row = 25;
|
|
}
|
|
|
|
VideoModePtr->hres = ModeBlock->XResolution;
|
|
VideoModePtr->vres = ModeBlock->YResolution;
|
|
VideoModePtr->frequency = 1;
|
|
VideoModePtr->Int10ModeNumber = (((ULONG)*ModePtr) << 16) | 0x00004f02;
|
|
VideoModePtr->Granularity = ModeBlock->WinGranularity << 10;
|
|
VideoModePtr->NonVgaHardware = (ModeBlock->ModeAttributes & 0x20) ? TRUE : FALSE;
|
|
|
|
|
|
if (LinearModeSupported) {
|
|
|
|
if ((VbeVersion >= 0x300) && ModeBlock->LinBytesPerScanLine) {
|
|
VideoModePtr->wbytes = ModeBlock->LinBytesPerScanLine;
|
|
} else {
|
|
VideoModePtr->wbytes = ModeBlock->BytesPerScanLine;
|
|
}
|
|
|
|
//
|
|
// We first try to round up VideoMemoryRequired
|
|
// to power of 2 so that we'll have a better
|
|
// chance to get it mapped as write combined
|
|
// on systems where MTRR is the only mechanism
|
|
// for such mappings. If the rounded up value
|
|
// is larger than the size of on-board memory
|
|
// we'll at least round it up to page boundary
|
|
//
|
|
|
|
VideoMemoryRequired = RaiseToPower2Ulong(VideoModePtr->wbytes * VideoModePtr->vres);
|
|
|
|
if(VideoMemoryRequired > (ULONG)TotalMemory) {
|
|
VideoMemoryRequired =
|
|
(VideoModePtr->wbytes * VideoModePtr->vres + 0x1000 - 1) & ~(0x1000 - 1);
|
|
}
|
|
|
|
VideoModePtr->sbytes = VideoMemoryRequired;
|
|
VideoModePtr->PixelsPerScan = VideoModePtr->hres;
|
|
VideoModePtr->banktype = NoBanking;
|
|
VideoModePtr->Int10ModeNumber |= 0x40000000;
|
|
VideoModePtr->MemoryBase = ModeBlock->PhysBasePtr;
|
|
VideoModePtr->MemoryLength = VideoMemoryRequired;
|
|
VideoModePtr->FrameOffset = 0;
|
|
VideoModePtr->FrameLength = VideoMemoryRequired;
|
|
VideoModePtr->fbType |= VIDEO_MODE_LINEAR;
|
|
|
|
} else {
|
|
|
|
VideoModePtr->wbytes = RaiseToPower2(ModeBlock->BytesPerScanLine);
|
|
|
|
//
|
|
// Round up to bank boundary if possible.
|
|
//
|
|
|
|
VideoMemoryRequired =
|
|
(VideoModePtr->wbytes * VideoModePtr->vres + 0x10000 - 1) & ~(0x10000 - 1);
|
|
|
|
if(VideoMemoryRequired > (ULONG)TotalMemory) {
|
|
|
|
//
|
|
// Round up to page boundary.
|
|
//
|
|
|
|
VideoMemoryRequired =
|
|
(VideoModePtr->wbytes * VideoModePtr->vres + 0x1000 - 1) & ~(0x1000 - 1);
|
|
}
|
|
|
|
VideoModePtr->sbytes = VideoMemoryRequired;
|
|
VideoModePtr->PixelsPerScan = RaiseToPower2(VideoModePtr->hres);
|
|
VideoModePtr->banktype = VideoBanked1RW;
|
|
VideoModePtr->MemoryBase = 0xa0000;
|
|
VideoModePtr->MemoryLength = 0x10000;
|
|
VideoModePtr->FrameOffset = 0;
|
|
VideoModePtr->FrameLength = 0x10000;
|
|
VideoModePtr->fbType |= VIDEO_MODE_BANKED;
|
|
|
|
}
|
|
|
|
if (ModeBlock->ModeAttributes & 0x40) {
|
|
VideoModePtr->banktype = NormalBanking;
|
|
}
|
|
|
|
//
|
|
// Make sure there is enough memory for the mode
|
|
//
|
|
|
|
if ((VideoModePtr->wbytes * VideoModePtr->vres) <= TotalMemory) {
|
|
ModeValid = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ModeValid) {
|
|
|
|
VideoDebugPrint((1, "Supported: %dx%dx%dbpp\n",
|
|
VideoModePtr->hres,
|
|
VideoModePtr->vres,
|
|
VideoModePtr->bitsPerPlane));
|
|
|
|
VideoModePtr++;
|
|
AdditionalModes++;
|
|
|
|
} else {
|
|
|
|
VideoDebugPrint((1, "Rejecting: %dx%dx%dbpp\n",
|
|
ModeBlock->XResolution,
|
|
ModeBlock->YResolution,
|
|
ModeBlock->BitsPerPixel));
|
|
}
|
|
|
|
ModePtr++;
|
|
}
|
|
|
|
//
|
|
// Lets check to see if we can map the memory for one of these modes.
|
|
// If not, don't support the extended modes.
|
|
//
|
|
// Note: this is a temporary hack, until I can implent the correct
|
|
// fix.
|
|
//
|
|
|
|
VideoModePtr--;
|
|
|
|
if (IS_LINEAR_MODE(VideoModePtr)) {
|
|
|
|
PHYSICAL_ADDRESS Address;
|
|
UCHAR inIoSpace = 0;
|
|
|
|
Address.LowPart = VideoModePtr->MemoryBase;
|
|
Address.HighPart = 0;
|
|
|
|
#if defined(PLUG_AND_PLAY)
|
|
inIoSpace |= VIDEO_MEMORY_SPACE_P6CACHE;
|
|
#endif
|
|
Memory = VideoPortGetDeviceBase(hwDeviceExtension,
|
|
Address,
|
|
0x1000,
|
|
inIoSpace);
|
|
|
|
if (Memory) {
|
|
|
|
VideoPortFreeDeviceBase(hwDeviceExtension, Memory);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We can't map the memory, so don't expose the extra modes.
|
|
//
|
|
|
|
VideoDebugPrint((0, "vga.sys: Mapping 0x%x failed\n", VideoModePtr->MemoryBase));
|
|
AdditionalModes = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
FallOut:
|
|
|
|
VideoPortFreePool(hwDeviceExtension, InfoBlock);
|
|
}
|
|
|
|
pInt10->Int10FreeBuffer(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff);
|
|
|
|
NumVideoModes += AdditionalModes;
|
|
|
|
}
|
|
|
|
ULONG
|
|
GetVideoMemoryBaseAddress(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
PVIDEOMODE pRequestedMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine get the base address of the framebuffer of a given mode
|
|
|
|
Return Value:
|
|
|
|
Base address of framebuffer
|
|
|
|
--*/
|
|
|
|
{
|
|
PMODE_INFO_BLOCK ModeBlock;
|
|
ULONG Length = 0x1000;
|
|
INT10_BIOS_ARGUMENTS BiosArguments;
|
|
PVIDEO_PORT_INT10_INTERFACE pInt10;
|
|
ULONG RetValue = 0;
|
|
USHORT VdmSeg;
|
|
USHORT VdmOff;
|
|
|
|
//
|
|
// If this is not a vesa mode, just return the saved base address
|
|
//
|
|
|
|
if (pRequestedMode->fbType & VIDEO_MODE_BANKED) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
pInt10 = &hwDeviceExtension->Int10;
|
|
|
|
if(!(pInt10->Size)) {
|
|
|
|
//
|
|
// This structure should be initialized in VgaInitialize
|
|
// If this function get called before VgaInitialize, just return 0;
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
ModeBlock = VideoPortAllocatePool(hwDeviceExtension,
|
|
VpPagedPool,
|
|
sizeof(MODE_INFO_BLOCK),
|
|
' agV');
|
|
|
|
if(!ModeBlock) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (pInt10->Int10AllocateBuffer(pInt10->Context,
|
|
&VdmSeg,
|
|
&VdmOff,
|
|
&Length) != NO_ERROR) {
|
|
|
|
VideoPortFreePool(hwDeviceExtension, ModeBlock);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Get info about the VESA mode.
|
|
//
|
|
|
|
BiosArguments.Eax = 0x4f01;
|
|
BiosArguments.Ecx = pRequestedMode->Int10ModeNumber >> 16;
|
|
BiosArguments.Edi = VdmOff;
|
|
BiosArguments.SegEs = VdmSeg;
|
|
|
|
if(pInt10->Int10CallBios(pInt10->Context,
|
|
&BiosArguments) == NO_ERROR &&
|
|
|
|
VESA_SUCCESS(BiosArguments.Eax)) {
|
|
|
|
//
|
|
// Copy the mode information out of the csrss process
|
|
//
|
|
|
|
if(pInt10->Int10ReadMemory(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff,
|
|
ModeBlock,
|
|
sizeof(MODE_INFO_BLOCK)) == NO_ERROR){
|
|
|
|
RetValue = ModeBlock->PhysBasePtr;
|
|
}
|
|
|
|
}
|
|
|
|
pInt10->Int10FreeBuffer(pInt10->Context, VdmSeg, VdmOff);
|
|
VideoPortFreePool(hwDeviceExtension, ModeBlock);
|
|
return( RetValue );
|
|
|
|
}
|
|
|
|
VP_STATUS
|
|
VBEGetModeInfo(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
USHORT ModeNumber,
|
|
PMODE_INFO_BLOCK ModeInfoBlock
|
|
)
|
|
{
|
|
INT10_BIOS_ARGUMENTS Int10BiosArguments;
|
|
PVIDEO_PORT_INT10_INTERFACE pInt10;
|
|
VP_STATUS status = ERROR_INVALID_PARAMETER;
|
|
USHORT VdmSeg;
|
|
USHORT VdmOff;
|
|
ULONG Length = 0x1000;
|
|
|
|
pInt10 = &hwDeviceExtension->Int10;
|
|
|
|
if(pInt10->Size &&
|
|
pInt10->Int10AllocateBuffer(pInt10->Context,
|
|
&VdmSeg,
|
|
&VdmOff,
|
|
&Length) == NO_ERROR) {
|
|
|
|
Int10BiosArguments.Eax = VBE_GET_MODE_INFO;
|
|
Int10BiosArguments.Ecx = ModeNumber;
|
|
Int10BiosArguments.Edi = VdmOff;
|
|
Int10BiosArguments.SegEs = VdmSeg;
|
|
|
|
status = pInt10->Int10CallBios(pInt10->Context, &Int10BiosArguments);
|
|
|
|
if (status == NO_ERROR &&
|
|
VESA_SUCCESS(Int10BiosArguments.Eax)) {
|
|
|
|
//
|
|
// Copy the mode information out of the csrss process
|
|
//
|
|
|
|
status = pInt10->Int10ReadMemory(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff,
|
|
ModeInfoBlock,
|
|
sizeof(MODE_INFO_BLOCK));
|
|
|
|
}
|
|
|
|
pInt10->Int10FreeBuffer(pInt10->Context, VdmSeg, VdmOff);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VP_STATUS
|
|
VBESetMode(
|
|
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
USHORT VesaModeNumber
|
|
)
|
|
{
|
|
VIDEO_X86_BIOS_ARGUMENTS biosArguments;
|
|
VP_STATUS status;
|
|
|
|
biosArguments.Eax = VBE_SET_MODE;
|
|
biosArguments.Ebx = VesaModeNumber;
|
|
|
|
status = VideoPortInt10(HwDeviceExtension, &biosArguments);
|
|
|
|
if ((status == NO_ERROR) &&
|
|
VESA_SUCCESS(biosArguments.Eax)) {
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
USHORT
|
|
VBEGetMode(
|
|
PHW_DEVICE_EXTENSION HwDeviceExtension
|
|
)
|
|
{
|
|
VIDEO_X86_BIOS_ARGUMENTS biosArguments;
|
|
VP_STATUS status;
|
|
|
|
biosArguments.Eax = VBE_GET_MODE;
|
|
|
|
status = VideoPortInt10(HwDeviceExtension, &biosArguments);
|
|
|
|
if ((status == NO_ERROR) &&
|
|
(VESA_SUCCESS(biosArguments.Eax))) {
|
|
|
|
return (USHORT)(biosArguments.Ebx & 0x0000FFFF) ;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
VBESaveState(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
PCHAR StateBuffer
|
|
)
|
|
{
|
|
INT10_BIOS_ARGUMENTS Int10BiosArguments;
|
|
PVIDEO_PORT_INT10_INTERFACE pInt10;
|
|
VP_STATUS status;
|
|
ULONG Size;
|
|
USHORT VdmSeg;
|
|
USHORT VdmOff;
|
|
ULONG Length = 0x1000;
|
|
|
|
pInt10 = &hwDeviceExtension->Int10;
|
|
|
|
if(!(pInt10->Size)) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
Int10BiosArguments.Eax = VBE_SAVE_RESTORE_STATE;
|
|
Int10BiosArguments.Edx = 0x0;
|
|
|
|
//
|
|
// Save all the state
|
|
//
|
|
|
|
Int10BiosArguments.Ecx = 0x0F;
|
|
|
|
status = pInt10->Int10CallBios(pInt10->Context, &Int10BiosArguments);
|
|
|
|
if (status != NO_ERROR ||
|
|
!VESA_SUCCESS(Int10BiosArguments.Eax)) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
Size = (Int10BiosArguments.Ebx & 0xffff) << 6 ;
|
|
|
|
//
|
|
// if StateBuffer is NULL, the caller is only want to know the size
|
|
// of the buffer needed to store the state
|
|
//
|
|
|
|
if (StateBuffer == NULL) {
|
|
return Size;
|
|
}
|
|
|
|
if (pInt10->Int10AllocateBuffer(pInt10->Context,
|
|
&VdmSeg,
|
|
&VdmOff,
|
|
&Length) == NO_ERROR) {
|
|
|
|
Int10BiosArguments.Eax = VBE_SAVE_RESTORE_STATE;
|
|
Int10BiosArguments.Edx = 0x1;
|
|
Int10BiosArguments.Ecx = 0x0F;
|
|
Int10BiosArguments.Ebx = VdmOff;
|
|
Int10BiosArguments.SegEs = VdmSeg;
|
|
|
|
status = pInt10->Int10CallBios(pInt10->Context, &Int10BiosArguments);
|
|
|
|
if (status == NO_ERROR &&
|
|
VESA_SUCCESS(Int10BiosArguments.Eax)) {
|
|
|
|
//
|
|
// Copy the state data of the csrss process
|
|
//
|
|
|
|
status = pInt10->Int10ReadMemory(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff,
|
|
StateBuffer,
|
|
Size);
|
|
if (status != NO_ERROR) {
|
|
|
|
Size = 0;
|
|
}
|
|
}
|
|
|
|
pInt10->Int10FreeBuffer(pInt10->Context, VdmSeg, VdmOff);
|
|
|
|
} else {
|
|
|
|
Size = 0;
|
|
}
|
|
|
|
return Size;
|
|
}
|
|
|
|
VP_STATUS
|
|
VBERestoreState(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
PCHAR StateBuffer,
|
|
ULONG Size
|
|
)
|
|
{
|
|
INT10_BIOS_ARGUMENTS Int10BiosArguments;
|
|
PVIDEO_PORT_INT10_INTERFACE pInt10;
|
|
VP_STATUS status = ERROR_INVALID_PARAMETER;
|
|
USHORT VdmSeg;
|
|
USHORT VdmOff;
|
|
ULONG Length = 0x1000;
|
|
|
|
pInt10 = &hwDeviceExtension->Int10;
|
|
|
|
if(!(pInt10->Size)) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (pInt10->Int10AllocateBuffer(pInt10->Context,
|
|
&VdmSeg,
|
|
&VdmOff,
|
|
&Length) == NO_ERROR) {
|
|
|
|
//
|
|
// Copy the state data to the csrss process
|
|
//
|
|
|
|
status = pInt10->Int10WriteMemory(pInt10->Context,
|
|
VdmSeg,
|
|
VdmOff,
|
|
StateBuffer,
|
|
Size);
|
|
|
|
if (status == NO_ERROR) {
|
|
|
|
Int10BiosArguments.Eax = VBE_SAVE_RESTORE_STATE;
|
|
Int10BiosArguments.Edx = 0x2;
|
|
Int10BiosArguments.Ecx = 0x0f;
|
|
Int10BiosArguments.Ebx = VdmOff;
|
|
Int10BiosArguments.SegEs = VdmSeg;
|
|
|
|
status = pInt10->Int10CallBios(pInt10->Context, &Int10BiosArguments);
|
|
|
|
if (status != NO_ERROR ||
|
|
!VESA_SUCCESS(Int10BiosArguments.Eax)) {
|
|
|
|
status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
pInt10->Int10FreeBuffer(pInt10->Context, VdmSeg, VdmOff);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VP_STATUS
|
|
VBESetDisplayWindow(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
UCHAR WindowSelect,
|
|
USHORT WindowNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine set the position of the specified window in the
|
|
frame buffer memory
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension
|
|
Pointer to the miniport driver's adapter information.
|
|
|
|
WindowSelect
|
|
0 for Window A and 1 for Window B
|
|
|
|
WindowNumber
|
|
Window number in video memory in window granularity units
|
|
|
|
Return Value:
|
|
|
|
VP_STATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
VIDEO_X86_BIOS_ARGUMENTS biosArguments;
|
|
VP_STATUS status;
|
|
|
|
biosArguments.Eax = VBE_WINDOW_CONTROL;
|
|
biosArguments.Ebx = WindowSelect & 0x01;
|
|
biosArguments.Edx = WindowNumber;
|
|
|
|
status = VideoPortInt10(hwDeviceExtension, &biosArguments);
|
|
|
|
if ((status != NO_ERROR) ||
|
|
(!VESA_SUCCESS(biosArguments.Eax))) {
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
USHORT
|
|
VBEGetDisplayWindow(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
UCHAR WindowSelect
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine set the position of the specified window in the
|
|
frame buffer memory
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension
|
|
Pointer to the miniport driver's adapter information.
|
|
|
|
WindowSelect
|
|
0 for Window A and 1 for Window B
|
|
|
|
Return Value:
|
|
|
|
Window number in video memory in window granularity units
|
|
|
|
--*/
|
|
|
|
{
|
|
VIDEO_X86_BIOS_ARGUMENTS biosArguments;
|
|
VP_STATUS status;
|
|
|
|
biosArguments.Eax = VBE_WINDOW_CONTROL;
|
|
biosArguments.Ebx = (WindowSelect & 0x1) | 0x100;
|
|
|
|
status = VideoPortInt10(hwDeviceExtension, &biosArguments);
|
|
|
|
if ((status != NO_ERROR) ||
|
|
(!VESA_SUCCESS(biosArguments.Eax))) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
return ((USHORT)(biosArguments.Edx & 0xFFFF));
|
|
}
|
|
|
|
USHORT
|
|
VBEGetScanLineLength(
|
|
PHW_DEVICE_EXTENSION HwDeviceExtension
|
|
)
|
|
{
|
|
VIDEO_X86_BIOS_ARGUMENTS biosArguments;
|
|
VP_STATUS status;
|
|
|
|
biosArguments.Eax = VBE_SCANLINE;
|
|
biosArguments.Ebx = 0x1;
|
|
|
|
status = VideoPortInt10(HwDeviceExtension, &biosArguments);
|
|
|
|
if ((status == NO_ERROR) &&
|
|
(VESA_SUCCESS(biosArguments.Eax))) {
|
|
|
|
return (USHORT)(biosArguments.Ebx & 0x0000FFFF) ;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
VP_STATUS
|
|
VesaSaveHardwareState(
|
|
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
PVIDEO_HARDWARE_STATE HardwareState,
|
|
ULONG HardwareStateSize,
|
|
USHORT ModeNumber
|
|
)
|
|
{
|
|
PVIDEO_HARDWARE_STATE_HEADER hardwareStateHeader;
|
|
VP_STATUS status;
|
|
ULONG FrameBufferSize;
|
|
PMODE_INFO_BLOCK ModeInfoBlock;
|
|
PVESA_INFO pVesaInfo;
|
|
|
|
//
|
|
// See if the buffer is big enough to hold the hardware state structure.
|
|
// (This is only the HardwareState structure itself, not the buffer it
|
|
// points to.)
|
|
//
|
|
|
|
if (HardwareStateSize < sizeof(VIDEO_HARDWARE_STATE) ) {
|
|
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
}
|
|
|
|
|
|
hardwareStateHeader =
|
|
(PVIDEO_HARDWARE_STATE_HEADER) HardwareState->StateHeader;
|
|
|
|
//
|
|
// Zero out the structure
|
|
//
|
|
|
|
VideoPortZeroMemory((PVOID) hardwareStateHeader,
|
|
sizeof(VIDEO_HARDWARE_STATE_HEADER));
|
|
|
|
//
|
|
// Set the Header field
|
|
//
|
|
|
|
hardwareStateHeader->Length = sizeof(VIDEO_HARDWARE_STATE_HEADER);
|
|
hardwareStateHeader->VGAStateFlags |= VIDEO_STATE_UNEMULATED_VGA_STATE;
|
|
|
|
hardwareStateHeader->VesaInfoOffset =
|
|
(sizeof(VIDEO_HARDWARE_STATE_HEADER) + 7) & ~7;
|
|
|
|
pVesaInfo = (PVESA_INFO)((PCHAR)hardwareStateHeader +
|
|
hardwareStateHeader->VesaInfoOffset);
|
|
|
|
//
|
|
// Check the size needed to store hardware state
|
|
//
|
|
|
|
if (!(pVesaInfo->HardwareStateSize =
|
|
VBESaveState(HwDeviceExtension, NULL))) {
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// In the case the size needed is too big just retrun failure
|
|
// This should not happen in reality
|
|
//
|
|
|
|
if( VGA_TOTAL_STATE_SIZE < hardwareStateHeader->VesaInfoOffset +
|
|
sizeof(VESA_INFO) +
|
|
pVesaInfo->HardwareStateSize) {
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Save hardware state
|
|
//
|
|
|
|
if (pVesaInfo->HardwareStateSize !=
|
|
VBESaveState(HwDeviceExtension, pVesaInfo->HardwareState)) {
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
pVesaInfo->ModeNumber = ModeNumber;
|
|
|
|
ModeInfoBlock = &(pVesaInfo->ModeInfoBlock);
|
|
|
|
//
|
|
// Retrieve mode info
|
|
//
|
|
|
|
if( VBEGetModeInfo(HwDeviceExtension,
|
|
ModeNumber,
|
|
ModeInfoBlock) != NO_ERROR) {
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Save framebuffer
|
|
//
|
|
|
|
hardwareStateHeader->FrameBufferData =
|
|
SaveFrameBuffer(HwDeviceExtension, pVesaInfo);
|
|
|
|
if(hardwareStateHeader->FrameBufferData) {
|
|
|
|
return NO_ERROR;
|
|
|
|
} else {
|
|
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
PCHAR
|
|
SaveFrameBuffer(
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension,
|
|
PVESA_INFO pVesaInfo
|
|
)
|
|
{
|
|
ULONG FrameBufferSize, BankSize, CopySize, LeftSize, k = 1;
|
|
USHORT i;
|
|
PCHAR FrameBufferData, pFrameBuffer;
|
|
PHYSICAL_ADDRESS FBPhysicalAddress;
|
|
PMODE_INFO_BLOCK ModeInfoBlock;
|
|
UCHAR inIoSpace = 0;
|
|
|
|
ModeInfoBlock = (PMODE_INFO_BLOCK) &(pVesaInfo->ModeInfoBlock);
|
|
|
|
//
|
|
// We'll try to get the current value of scanline size just in case a DOS
|
|
// app changed it. But we stay on the value we have if the vesa function
|
|
// is not supported or failed.
|
|
//
|
|
|
|
i = VBEGetScanLineLength(hwDeviceExtension);
|
|
|
|
if(i) {
|
|
|
|
ModeInfoBlock->BytesPerScanLine = i;
|
|
}
|
|
|
|
//
|
|
// 1) Calculate Framebuffer size
|
|
//
|
|
|
|
//
|
|
// Check if it is graphics or text mode. For text mode we simply
|
|
// assume a size of 32k
|
|
//
|
|
|
|
if (ModeInfoBlock->ModeAttributes & 0x10) {
|
|
|
|
FrameBufferSize = ModeInfoBlock->BytesPerScanLine *
|
|
ModeInfoBlock->YResolution;
|
|
|
|
} else {
|
|
|
|
FrameBufferSize = 0x8000;
|
|
}
|
|
|
|
pVesaInfo->FrameBufferSize = FrameBufferSize;
|
|
|
|
//
|
|
// 2) Determine the location and the size to be mapped and map it
|
|
//
|
|
|
|
if (!(ModeInfoBlock->ModeAttributes & 0x10)) {
|
|
|
|
//
|
|
// This is a text mode
|
|
//
|
|
|
|
FBPhysicalAddress.HighPart = 0;
|
|
FBPhysicalAddress.LowPart = ModeInfoBlock->WinASegment << 4;
|
|
|
|
if( FBPhysicalAddress.LowPart == 0) {
|
|
|
|
FBPhysicalAddress.LowPart = 0xB8000;
|
|
}
|
|
|
|
BankSize = 0x8000;
|
|
|
|
} else if (pVesaInfo->ModeNumber & 0x4000) {
|
|
|
|
//
|
|
// Linear framebuffer can be viewed as one large bank
|
|
//
|
|
|
|
FBPhysicalAddress.LowPart = ModeInfoBlock->PhysBasePtr;
|
|
FBPhysicalAddress.HighPart = 0;
|
|
BankSize = FrameBufferSize;
|
|
|
|
#if defined(PLUG_AND_PLAY)
|
|
inIoSpace |= VIDEO_MEMORY_SPACE_P6CACHE;
|
|
#endif
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a banked mode
|
|
//
|
|
|
|
FBPhysicalAddress.HighPart = 0;
|
|
FBPhysicalAddress.LowPart = ModeInfoBlock->WinASegment << 4;
|
|
|
|
if( FBPhysicalAddress.LowPart == 0) {
|
|
|
|
FBPhysicalAddress.LowPart = 0xA0000;
|
|
}
|
|
|
|
BankSize = 1024 * ModeInfoBlock->WinSize;
|
|
|
|
//
|
|
// The bank size shouldn't exceed 64k. But we'd better guard
|
|
// the bad BIOS
|
|
//
|
|
|
|
if(BankSize > 0x10000 || BankSize == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// k will be used later to translate the window number
|
|
// in the unit of WinSize to the window number in the
|
|
// unit of WinGranularity
|
|
//
|
|
|
|
if (ModeInfoBlock->WinGranularity) {
|
|
|
|
k = ModeInfoBlock->WinSize/ModeInfoBlock->WinGranularity;
|
|
}
|
|
}
|
|
|
|
if(( pFrameBuffer = VideoPortGetDeviceBase(hwDeviceExtension,
|
|
FBPhysicalAddress,
|
|
BankSize,
|
|
inIoSpace)) == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// 3) Allocate memory for framebuffer data
|
|
//
|
|
|
|
if((FrameBufferData = VideoPortAllocatePool(hwDeviceExtension,
|
|
VpPagedPool,
|
|
FrameBufferSize,
|
|
' agV')) == NULL) {
|
|
|
|
VideoPortFreeDeviceBase(hwDeviceExtension, pFrameBuffer);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// 4) Save famebuffer data
|
|
//
|
|
|
|
LeftSize = FrameBufferSize;
|
|
|
|
for ( i = 0; LeftSize > 0; i++ ) {
|
|
|
|
if (!(pVesaInfo->ModeNumber & 0x4000)) {
|
|
|
|
//
|
|
// If this is a banked mode, switch to the right bank.
|
|
// We set both Window A and B, as some VBEs have these
|
|
// set as separately available read and write windows.
|
|
//
|
|
|
|
VBESetDisplayWindow(hwDeviceExtension, 0, i * (USHORT)k);
|
|
VBESetDisplayWindow(hwDeviceExtension, 1, i * (USHORT)k);
|
|
}
|
|
|
|
CopySize = (LeftSize < BankSize) ? LeftSize : BankSize;
|
|
|
|
VideoPortMoveMemory(FrameBufferData + i * BankSize,
|
|
pFrameBuffer,
|
|
CopySize);
|
|
|
|
LeftSize -= CopySize;
|
|
}
|
|
|
|
//
|
|
// 5) Relese resource
|
|
//
|
|
|
|
VideoPortFreeDeviceBase(hwDeviceExtension, pFrameBuffer);
|
|
|
|
return FrameBufferData;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsSavedModeVesa(
|
|
PVIDEO_HARDWARE_STATE HardwareState
|
|
)
|
|
{
|
|
PVIDEO_HARDWARE_STATE_HEADER hardwareStateHeader;
|
|
|
|
hardwareStateHeader =
|
|
(PVIDEO_HARDWARE_STATE_HEADER) HardwareState->StateHeader;
|
|
|
|
if (hardwareStateHeader->Length == sizeof(VIDEO_HARDWARE_STATE_HEADER) &&
|
|
hardwareStateHeader->VesaInfoOffset ) {
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
VP_STATUS
|
|
VesaRestoreHardwareState(
|
|
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
PVIDEO_HARDWARE_STATE HardwareState,
|
|
ULONG HardwareStateSize
|
|
)
|
|
{
|
|
|
|
VIDEO_X86_BIOS_ARGUMENTS biosArguments;
|
|
PVIDEO_HARDWARE_STATE_HEADER hardwareStateHeader;
|
|
PMODE_INFO_BLOCK ModeInfoBlock;
|
|
PVESA_INFO pVesaInfo;
|
|
VP_STATUS status;
|
|
|
|
hardwareStateHeader =
|
|
(PVIDEO_HARDWARE_STATE_HEADER) HardwareState->StateHeader;
|
|
|
|
pVesaInfo = (PVESA_INFO)((PCHAR)hardwareStateHeader +
|
|
hardwareStateHeader->VesaInfoOffset);
|
|
|
|
//
|
|
//
|
|
// 1) set the original mode
|
|
// 2) restore hardware state
|
|
//
|
|
// Please note that both steps are necessary
|
|
//
|
|
|
|
//
|
|
// We always use default CRTC value
|
|
//
|
|
|
|
VBESetMode (HwDeviceExtension, pVesaInfo->ModeNumber & (~0x800));
|
|
|
|
if ( VBERestoreState(HwDeviceExtension,
|
|
pVesaInfo->HardwareState,
|
|
pVesaInfo->HardwareStateSize) != NO_ERROR ) {
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
ModeInfoBlock = (PMODE_INFO_BLOCK) &(pVesaInfo->ModeInfoBlock);
|
|
|
|
//
|
|
// Restore framebuffer data
|
|
//
|
|
|
|
if(RestoreFrameBuffer(HwDeviceExtension,
|
|
pVesaInfo,
|
|
hardwareStateHeader->FrameBufferData)) {
|
|
|
|
hardwareStateHeader->FrameBufferData = 0;
|
|
return NO_ERROR;
|
|
|
|
} else {
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
RestoreFrameBuffer(
|
|
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
PVESA_INFO pVesaInfo,
|
|
PCHAR FrameBufferData
|
|
)
|
|
{
|
|
ULONG FrameBufferSize, BankSize, CopySize, LeftSize, k;
|
|
PHYSICAL_ADDRESS FBPhysicalAddress;
|
|
USHORT i, WinA, WinB;
|
|
PCHAR pFrameBuffer;
|
|
PMODE_INFO_BLOCK ModeInfoBlock;
|
|
UCHAR inIoSpace = 0;
|
|
|
|
if(!FrameBufferData) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
ModeInfoBlock = (PMODE_INFO_BLOCK) &(pVesaInfo->ModeInfoBlock);
|
|
|
|
//
|
|
// 1) Get Framebuffer size
|
|
//
|
|
|
|
FrameBufferSize = pVesaInfo->FrameBufferSize;
|
|
|
|
if (!FrameBufferSize) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// 2) Determine the location and the size to be mapped and map it
|
|
//
|
|
|
|
if (!(ModeInfoBlock->ModeAttributes & 0x10)) {
|
|
|
|
//
|
|
// This is a text mode
|
|
//
|
|
|
|
FBPhysicalAddress.HighPart = 0;
|
|
FBPhysicalAddress.LowPart = ModeInfoBlock->WinASegment << 4;
|
|
|
|
if( FBPhysicalAddress.LowPart == 0) {
|
|
|
|
FBPhysicalAddress.LowPart = 0xB8000;
|
|
}
|
|
|
|
BankSize = 0x8000;
|
|
|
|
} else if (pVesaInfo->ModeNumber & 0x4000) {
|
|
|
|
//
|
|
// Linear framebuffer can be viewed as one large bank
|
|
//
|
|
|
|
FBPhysicalAddress.LowPart = ModeInfoBlock->PhysBasePtr;
|
|
FBPhysicalAddress.HighPart = 0;
|
|
BankSize = FrameBufferSize;
|
|
|
|
#if defined(PLUG_AND_PLAY)
|
|
inIoSpace |= VIDEO_MEMORY_SPACE_P6CACHE;
|
|
#endif
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a banked mode
|
|
//
|
|
|
|
FBPhysicalAddress.HighPart = 0;
|
|
FBPhysicalAddress.LowPart = ModeInfoBlock->WinASegment << 4;
|
|
|
|
if( FBPhysicalAddress.LowPart == 0) {
|
|
|
|
FBPhysicalAddress.LowPart = 0xA0000;
|
|
}
|
|
|
|
BankSize = 1024 * ModeInfoBlock->WinSize;
|
|
|
|
//
|
|
// The bank size shouldn't exceed 64k. But we'd better guard
|
|
// the bad BIOS
|
|
//
|
|
|
|
if(BankSize > 0x10000 || BankSize == 0) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// k will be used later to translate the window number
|
|
// in the unit of WinSize to the window number in the
|
|
// unit of WinGranularity
|
|
//
|
|
|
|
if (ModeInfoBlock->WinGranularity) {
|
|
|
|
k = ModeInfoBlock->WinSize/ModeInfoBlock->WinGranularity;
|
|
|
|
} else {
|
|
|
|
k = 1;
|
|
}
|
|
|
|
}
|
|
|
|
if((pFrameBuffer = VideoPortGetDeviceBase(HwDeviceExtension,
|
|
FBPhysicalAddress,
|
|
FrameBufferSize,
|
|
inIoSpace)) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// 3) Restore framebuffer data
|
|
//
|
|
|
|
//
|
|
// For banked mode we need to save the current bank number before
|
|
// we change it.
|
|
//
|
|
|
|
if (!(pVesaInfo->ModeNumber & 0x4000)) {
|
|
|
|
//
|
|
// We need to save the curren window number for banked mode
|
|
//
|
|
|
|
WinA = VBEGetDisplayWindow(HwDeviceExtension, 0);
|
|
WinB = VBEGetDisplayWindow(HwDeviceExtension, 1);
|
|
|
|
}
|
|
|
|
LeftSize = FrameBufferSize;
|
|
|
|
for (i = 0; LeftSize > 0; i++) {
|
|
|
|
if (!(pVesaInfo->ModeNumber & 0x4000)) {
|
|
|
|
//
|
|
// This is a banked mode.
|
|
//
|
|
// We need set both Window A and B, as some VBEs have these
|
|
// set as separately available read and write windows.
|
|
//
|
|
|
|
VBESetDisplayWindow(HwDeviceExtension, 0, i * (USHORT)k);
|
|
VBESetDisplayWindow(HwDeviceExtension, 1, i * (USHORT)k);
|
|
}
|
|
|
|
CopySize = (LeftSize < BankSize) ? LeftSize : BankSize;
|
|
|
|
VideoPortMoveMemory(pFrameBuffer,
|
|
FrameBufferData + i * BankSize,
|
|
CopySize);
|
|
|
|
LeftSize -= CopySize;
|
|
}
|
|
|
|
if (!(pVesaInfo->ModeNumber & 0x4000)) {
|
|
|
|
//
|
|
// For banked mode we need to restore the window number after
|
|
// we changed it.
|
|
//
|
|
|
|
VBESetDisplayWindow(HwDeviceExtension, 0, WinA);
|
|
VBESetDisplayWindow(HwDeviceExtension, 1, WinB);
|
|
}
|
|
|
|
|
|
//
|
|
// 4) Relese resource
|
|
//
|
|
|
|
VideoPortFreeDeviceBase(HwDeviceExtension, pFrameBuffer);
|
|
VideoPortFreePool(HwDeviceExtension, FrameBufferData);
|
|
|
|
return FrameBufferSize;
|
|
}
|