2004-01-10 14:39:21 +00:00
|
|
|
/*
|
|
|
|
* ReactOS VBE miniport video driver
|
|
|
|
* Copyright (C) 2004 Filip Navara
|
|
|
|
*
|
2004-06-26 22:30:34 +00:00
|
|
|
* Power Management and VBE 1.2 support
|
|
|
|
* Copyright (C) 2004 Magnus Olsen
|
|
|
|
*
|
2004-01-10 14:39:21 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
2009-10-27 10:34:16 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2004-01-10 14:39:21 +00:00
|
|
|
*
|
2004-01-10 21:46:25 +00:00
|
|
|
* TODO:
|
2004-01-10 14:39:21 +00:00
|
|
|
* - Check input parameters everywhere.
|
2004-06-26 22:30:34 +00:00
|
|
|
* - Call VideoPortVerifyAccessRanges to reserve the memory we're about
|
|
|
|
* to map.
|
2004-01-10 14:39:21 +00:00
|
|
|
*/
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
#include "vbemp.h"
|
|
|
|
|
2014-02-04 11:08:27 +00:00
|
|
|
#include <devioctl.h>
|
|
|
|
|
2008-11-27 20:05:12 +00:00
|
|
|
#undef LOWORD
|
|
|
|
#undef HIWORD
|
|
|
|
#define LOWORD(l) ((USHORT)((ULONG_PTR)(l)))
|
|
|
|
#define HIWORD(l) ((USHORT)(((ULONG_PTR)(l)>>16)&0xFFFF))
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/
|
|
|
|
|
2010-05-03 11:22:34 +00:00
|
|
|
ULONG NTAPI
|
2004-01-10 21:46:25 +00:00
|
|
|
DriverEntry(IN PVOID Context1, IN PVOID Context2)
|
|
|
|
{
|
|
|
|
VIDEO_HW_INITIALIZATION_DATA InitData;
|
|
|
|
|
|
|
|
VideoPortZeroMemory(&InitData, sizeof(InitData));
|
2007-07-05 22:06:47 +00:00
|
|
|
InitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);
|
2004-01-10 21:46:25 +00:00
|
|
|
InitData.HwFindAdapter = VBEFindAdapter;
|
|
|
|
InitData.HwInitialize = VBEInitialize;
|
|
|
|
InitData.HwStartIO = VBEStartIO;
|
2004-06-20 16:06:12 +00:00
|
|
|
InitData.HwResetHw = VBEResetHw;
|
2012-03-11 11:07:22 +00:00
|
|
|
InitData.HwGetPowerState = VBEGetPowerState;
|
|
|
|
InitData.HwSetPowerState = VBESetPowerState;
|
|
|
|
InitData.HwGetVideoChildDescriptor = VBEGetVideoChildDescriptor;
|
2004-01-10 21:46:25 +00:00
|
|
|
InitData.HwDeviceExtensionSize = sizeof(VBE_DEVICE_EXTENSION);
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
return VideoPortInitialize(Context1, Context2, &InitData, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2004-06-26 22:30:34 +00:00
|
|
|
* VBEFindAdapter
|
2004-01-10 21:46:25 +00:00
|
|
|
*
|
2004-06-26 22:30:34 +00:00
|
|
|
* Should detect a VBE compatible display adapter, but it's not possible
|
|
|
|
* to use video port Int 10 services at this time during initialization,
|
|
|
|
* so we always return NO_ERROR and do the real work in VBEInitialize.
|
2004-01-10 21:46:25 +00:00
|
|
|
*/
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2008-11-30 11:16:55 +00:00
|
|
|
VP_STATUS NTAPI
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEFindAdapter(
|
|
|
|
IN PVOID HwDeviceExtension,
|
|
|
|
IN PVOID HwContext,
|
|
|
|
IN PWSTR ArgumentString,
|
|
|
|
IN OUT PVIDEO_PORT_CONFIG_INFO ConfigInfo,
|
2004-01-10 14:39:21 +00:00
|
|
|
OUT PUCHAR Again)
|
|
|
|
{
|
2010-03-05 01:09:24 +00:00
|
|
|
if (VideoPortIsNoVesa())
|
|
|
|
return ERROR_DEV_NOT_EXIST;
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2004-06-27 07:56:44 +00:00
|
|
|
/*
|
|
|
|
* VBESortModesCallback
|
|
|
|
*
|
|
|
|
* Helper function for sorting video mode list.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
VBESortModesCallback(PVBE_MODEINFO VbeModeInfoA, PVBE_MODEINFO VbeModeInfoB)
|
|
|
|
{
|
2005-12-01 19:51:08 +00:00
|
|
|
VideoPortDebugPrint(Info, "VBESortModesCallback: %dx%dx%d / %dx%dx%d\n",
|
2004-06-27 07:56:44 +00:00
|
|
|
VbeModeInfoA->XResolution, VbeModeInfoA->YResolution,
|
|
|
|
VbeModeInfoA->BitsPerPixel,
|
|
|
|
VbeModeInfoB->XResolution, VbeModeInfoB->YResolution,
|
2005-06-05 11:05:47 +00:00
|
|
|
VbeModeInfoB->BitsPerPixel);
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-06-27 07:56:44 +00:00
|
|
|
/*
|
|
|
|
* FIXME: Until some reasonable method for changing video modes will
|
|
|
|
* be available we favor more bits per pixel. It should be changed
|
|
|
|
* later.
|
|
|
|
*/
|
2004-07-07 16:54:43 +00:00
|
|
|
if (VbeModeInfoA->BitsPerPixel < VbeModeInfoB->BitsPerPixel) return -1;
|
|
|
|
if (VbeModeInfoA->BitsPerPixel > VbeModeInfoB->BitsPerPixel) return 1;
|
2004-06-27 07:56:44 +00:00
|
|
|
if (VbeModeInfoA->XResolution < VbeModeInfoB->XResolution) return -1;
|
|
|
|
if (VbeModeInfoA->XResolution > VbeModeInfoB->XResolution) return 1;
|
|
|
|
if (VbeModeInfoA->YResolution < VbeModeInfoB->YResolution) return -1;
|
|
|
|
if (VbeModeInfoA->YResolution > VbeModeInfoB->YResolution) return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* VBESortModes
|
|
|
|
*
|
|
|
|
* Simple function for sorting the video mode list. Uses bubble sort.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
|
|
VBESortModes(PVBE_DEVICE_EXTENSION DeviceExtension)
|
|
|
|
{
|
|
|
|
BOOLEAN Finished = FALSE;
|
|
|
|
ULONG Pos;
|
|
|
|
int Result;
|
|
|
|
VBE_MODEINFO TempModeInfo;
|
2006-01-07 22:36:56 +00:00
|
|
|
USHORT TempModeNumber;
|
2004-06-27 07:56:44 +00:00
|
|
|
|
|
|
|
while (!Finished)
|
|
|
|
{
|
|
|
|
Finished = TRUE;
|
|
|
|
for (Pos = 0; Pos < DeviceExtension->ModeCount - 1; Pos++)
|
|
|
|
{
|
|
|
|
Result = VBESortModesCallback(
|
|
|
|
DeviceExtension->ModeInfo + Pos,
|
|
|
|
DeviceExtension->ModeInfo + Pos + 1);
|
|
|
|
if (Result > 0)
|
|
|
|
{
|
|
|
|
Finished = FALSE;
|
|
|
|
|
|
|
|
VideoPortMoveMemory(
|
|
|
|
&TempModeInfo,
|
|
|
|
DeviceExtension->ModeInfo + Pos,
|
|
|
|
sizeof(VBE_MODEINFO));
|
|
|
|
TempModeNumber = DeviceExtension->ModeNumbers[Pos];
|
|
|
|
|
|
|
|
VideoPortMoveMemory(
|
|
|
|
DeviceExtension->ModeInfo + Pos,
|
|
|
|
DeviceExtension->ModeInfo + Pos + 1,
|
|
|
|
sizeof(VBE_MODEINFO));
|
|
|
|
DeviceExtension->ModeNumbers[Pos] =
|
|
|
|
DeviceExtension->ModeNumbers[Pos + 1];
|
|
|
|
|
|
|
|
VideoPortMoveMemory(
|
|
|
|
DeviceExtension->ModeInfo + Pos + 1,
|
|
|
|
&TempModeInfo,
|
|
|
|
sizeof(VBE_MODEINFO));
|
|
|
|
DeviceExtension->ModeNumbers[Pos + 1] = TempModeNumber;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
/*
|
|
|
|
* VBEInitialize
|
|
|
|
*
|
|
|
|
* Performs the first initialization of the adapter, after the HAL has given
|
|
|
|
* up control of the video hardware to the video port driver.
|
|
|
|
*
|
|
|
|
* This function performs these steps:
|
|
|
|
* - Gets global VBE information and finds if VBE BIOS is present.
|
|
|
|
* - Builds the internal mode list using the list of modes provided by
|
|
|
|
* the VBE.
|
|
|
|
*/
|
|
|
|
|
2008-11-30 11:16:55 +00:00
|
|
|
BOOLEAN NTAPI
|
2004-06-26 22:30:34 +00:00
|
|
|
VBEInitialize(PVOID HwDeviceExtension)
|
|
|
|
{
|
|
|
|
INT10_BIOS_ARGUMENTS BiosRegisters;
|
|
|
|
VP_STATUS Status;
|
2005-05-08 02:16:32 +00:00
|
|
|
PVBE_DEVICE_EXTENSION VBEDeviceExtension =
|
2004-01-10 14:39:21 +00:00
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension;
|
2004-06-26 22:30:34 +00:00
|
|
|
ULONG Length;
|
|
|
|
ULONG ModeCount;
|
|
|
|
ULONG SuitableModeCount;
|
|
|
|
USHORT ModeTemp;
|
|
|
|
ULONG CurrentMode;
|
|
|
|
PVBE_MODEINFO VbeModeInfo;
|
2004-01-10 14:39:21 +00:00
|
|
|
|
|
|
|
/*
|
2004-06-26 22:30:34 +00:00
|
|
|
* Get the Int 10 interface that we will use for allocating real
|
|
|
|
* mode memory and calling the video BIOS.
|
2004-01-10 14:39:21 +00:00
|
|
|
*/
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
VBEDeviceExtension->Int10Interface.Version = VIDEO_PORT_INT10_INTERFACE_VERSION_1;
|
|
|
|
VBEDeviceExtension->Int10Interface.Size = sizeof(VIDEO_PORT_INT10_INTERFACE);
|
|
|
|
Status = VideoPortQueryServices(
|
|
|
|
HwDeviceExtension,
|
|
|
|
VideoPortServicesInt10,
|
|
|
|
(PINTERFACE)&VBEDeviceExtension->Int10Interface);
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
if (Status != NO_ERROR)
|
|
|
|
{
|
2005-12-01 19:51:08 +00:00
|
|
|
VideoPortDebugPrint(Error, "Failed to get Int 10 service functions (Status %x)\n", Status);
|
2004-06-27 07:56:44 +00:00
|
|
|
return FALSE;
|
2004-06-26 22:30:34 +00:00
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
/*
|
|
|
|
* Allocate a bit of memory that will be later used for VBE transport
|
|
|
|
* buffer. This memory must be accessible from V86 mode so it must fit
|
|
|
|
* in the first megabyte of physical memory.
|
|
|
|
*/
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
Length = 0x400;
|
|
|
|
Status = VBEDeviceExtension->Int10Interface.Int10AllocateBuffer(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
&VBEDeviceExtension->TrampolineMemorySegment,
|
|
|
|
&VBEDeviceExtension->TrampolineMemoryOffset,
|
|
|
|
&Length);
|
|
|
|
|
|
|
|
if (Status != NO_ERROR)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2005-12-01 19:51:08 +00:00
|
|
|
VideoPortDebugPrint(Error, "Failed to allocate virtual memory (Status %x)\n", Status);
|
2004-06-27 07:56:44 +00:00
|
|
|
return FALSE;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the VBE general information.
|
|
|
|
*/
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
VBEDeviceExtension->Int10Interface.Int10WriteMemory(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
VBEDeviceExtension->TrampolineMemorySegment,
|
|
|
|
VBEDeviceExtension->TrampolineMemoryOffset,
|
|
|
|
"VBE2",
|
|
|
|
4);
|
|
|
|
|
2004-01-14 16:38:15 +00:00
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
2004-06-26 22:30:34 +00:00
|
|
|
BiosRegisters.Eax = VBE_GET_CONTROLLER_INFORMATION;
|
|
|
|
BiosRegisters.Edi = VBEDeviceExtension->TrampolineMemoryOffset;
|
|
|
|
BiosRegisters.SegEs = VBEDeviceExtension->TrampolineMemorySegment;
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
VBEDeviceExtension->Int10Interface.Int10ReadMemory(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
VBEDeviceExtension->TrampolineMemorySegment,
|
|
|
|
VBEDeviceExtension->TrampolineMemoryOffset,
|
|
|
|
&VBEDeviceExtension->VbeInfo,
|
|
|
|
sizeof(VBEDeviceExtension->VbeInfo));
|
|
|
|
|
2005-12-13 00:02:07 +00:00
|
|
|
/* Verify the VBE signature. */
|
|
|
|
if (VideoPortCompareMemory(VBEDeviceExtension->VbeInfo.Signature, "VESA", 4) != 4)
|
|
|
|
{
|
2012-03-28 13:39:02 +00:00
|
|
|
VideoPortDebugPrint(Error, "No VBE BIOS present\n");
|
2005-11-23 20:37:38 +00:00
|
|
|
return FALSE;
|
2005-12-13 00:02:07 +00:00
|
|
|
}
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2005-12-01 19:51:08 +00:00
|
|
|
VideoPortDebugPrint(Trace, "VBE BIOS Present (%d.%d, %8ld Kb)\n",
|
2004-06-26 22:30:34 +00:00
|
|
|
VBEDeviceExtension->VbeInfo.Version / 0x100,
|
|
|
|
VBEDeviceExtension->VbeInfo.Version & 0xFF,
|
2005-11-23 20:23:41 +00:00
|
|
|
VBEDeviceExtension->VbeInfo.TotalMemory * 64);
|
2004-06-26 22:30:34 +00:00
|
|
|
|
|
|
|
#ifdef VBE12_SUPPORT
|
|
|
|
if (VBEDeviceExtension->VbeInfo.Version < 0x102)
|
|
|
|
#else
|
|
|
|
if (VBEDeviceExtension->VbeInfo.Version < 0x200)
|
|
|
|
#endif
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2012-03-28 13:39:02 +00:00
|
|
|
VideoPortDebugPrint(Error, "VBE BIOS present, but incompatible version %d.%d\n",
|
|
|
|
VBEDeviceExtension->VbeInfo.Version / 0x100,
|
|
|
|
VBEDeviceExtension->VbeInfo.Version & 0xFF);
|
2004-06-27 07:56:44 +00:00
|
|
|
return FALSE;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-28 13:39:02 +00:00
|
|
|
VideoPortDebugPrint(Error, "No VBE BIOS found.\n");
|
2004-06-27 07:56:44 +00:00
|
|
|
return FALSE;
|
2004-06-26 22:30:34 +00:00
|
|
|
}
|
2004-01-10 21:46:25 +00:00
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
/*
|
|
|
|
* Build a mode list here that can be later used by
|
|
|
|
* IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES and IOCTL_VIDEO_QUERY_AVAIL_MODES
|
|
|
|
* calls.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the number of supported video modes.
|
2004-06-26 22:30:34 +00:00
|
|
|
*
|
|
|
|
* No need to be map the memory. It's either in the video BIOS memory or
|
|
|
|
* in our trampoline memory. In either case the memory is already mapped.
|
2004-01-10 14:39:21 +00:00
|
|
|
*/
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
for (ModeCount = 0; ; ModeCount++)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
/* Read the VBE mode number. */
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10ReadMemory(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
HIWORD(VBEDeviceExtension->VbeInfo.VideoModePtr),
|
|
|
|
LOWORD(VBEDeviceExtension->VbeInfo.VideoModePtr) + (ModeCount << 1),
|
|
|
|
&ModeTemp,
|
|
|
|
sizeof(ModeTemp));
|
|
|
|
|
|
|
|
/* End of list? */
|
|
|
|
if (ModeTemp == 0xFFFF || ModeTemp == 0)
|
|
|
|
break;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate space for video modes information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VBEDeviceExtension->ModeInfo =
|
2007-08-20 15:17:06 +00:00
|
|
|
VideoPortAllocatePool(HwDeviceExtension, VpPagedPool, ModeCount * sizeof(VBE_MODEINFO), TAG_VBE);
|
2004-01-10 14:39:21 +00:00
|
|
|
VBEDeviceExtension->ModeNumbers =
|
2007-08-20 15:17:06 +00:00
|
|
|
VideoPortAllocatePool(HwDeviceExtension, VpPagedPool, ModeCount * sizeof(USHORT), TAG_VBE);
|
2004-01-10 14:39:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the actual mode infos.
|
|
|
|
*/
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-06-27 07:56:44 +00:00
|
|
|
for (CurrentMode = 0, SuitableModeCount = 0;
|
2004-06-26 22:30:34 +00:00
|
|
|
CurrentMode < ModeCount;
|
2004-01-10 14:39:21 +00:00
|
|
|
CurrentMode++)
|
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
/* Read the VBE mode number. */
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10ReadMemory(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
HIWORD(VBEDeviceExtension->VbeInfo.VideoModePtr),
|
|
|
|
LOWORD(VBEDeviceExtension->VbeInfo.VideoModePtr) + (CurrentMode << 1),
|
|
|
|
&ModeTemp,
|
|
|
|
sizeof(ModeTemp));
|
|
|
|
|
|
|
|
/* Call VBE BIOS to read the mode info. */
|
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
|
|
|
BiosRegisters.Eax = VBE_GET_MODE_INFORMATION;
|
|
|
|
BiosRegisters.Ecx = ModeTemp;
|
|
|
|
BiosRegisters.Edi = VBEDeviceExtension->TrampolineMemoryOffset + 0x200;
|
|
|
|
BiosRegisters.SegEs = VBEDeviceExtension->TrampolineMemorySegment;
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
|
|
|
|
|
|
|
/* Read the VBE mode info. */
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10ReadMemory(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
VBEDeviceExtension->TrampolineMemorySegment,
|
|
|
|
VBEDeviceExtension->TrampolineMemoryOffset + 0x200,
|
|
|
|
VBEDeviceExtension->ModeInfo + SuitableModeCount,
|
|
|
|
sizeof(VBE_MODEINFO));
|
|
|
|
|
|
|
|
VbeModeInfo = VBEDeviceExtension->ModeInfo + SuitableModeCount;
|
|
|
|
|
|
|
|
/* Is this mode acceptable? */
|
2008-05-21 13:47:22 +00:00
|
|
|
if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS &&
|
2004-01-10 14:39:21 +00:00
|
|
|
VbeModeInfo->XResolution >= 640 &&
|
|
|
|
VbeModeInfo->YResolution >= 480 &&
|
2004-06-27 07:56:44 +00:00
|
|
|
(VbeModeInfo->MemoryModel == VBE_MEMORYMODEL_PACKEDPIXEL ||
|
2005-12-13 00:17:08 +00:00
|
|
|
VbeModeInfo->MemoryModel == VBE_MEMORYMODEL_DIRECTCOLOR) &&
|
|
|
|
VbeModeInfo->PhysBasePtr != 0)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2004-06-27 07:56:44 +00:00
|
|
|
if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_LINEAR)
|
|
|
|
{
|
2007-08-20 15:17:06 +00:00
|
|
|
/* Bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 */
|
|
|
|
// if (ModeTemp & 0x4000)
|
|
|
|
//{
|
|
|
|
VBEDeviceExtension->ModeNumbers[SuitableModeCount] = ModeTemp | 0x4000;
|
|
|
|
SuitableModeCount++;
|
|
|
|
//}
|
2004-06-27 07:56:44 +00:00
|
|
|
}
|
|
|
|
#ifdef VBE12_SUPPORT
|
|
|
|
else
|
2007-10-19 23:21:45 +00:00
|
|
|
{
|
2007-08-20 15:17:06 +00:00
|
|
|
VBEDeviceExtension->ModeNumbers[SuitableModeCount] = ModeTemp;
|
|
|
|
SuitableModeCount++;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
2004-06-27 07:56:44 +00:00
|
|
|
#endif
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-15 23:58:10 +00:00
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
if (SuitableModeCount == 0)
|
|
|
|
{
|
2007-08-20 15:17:06 +00:00
|
|
|
|
2005-12-01 19:51:08 +00:00
|
|
|
VideoPortDebugPrint(Warn, "VBEMP: No video modes supported\n");
|
2004-06-26 22:30:34 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-06-27 07:56:44 +00:00
|
|
|
VBEDeviceExtension->ModeCount = SuitableModeCount;
|
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
/*
|
2004-06-27 07:56:44 +00:00
|
|
|
* Sort the video mode list according to resolution and bits per pixel.
|
2004-01-10 14:39:21 +00:00
|
|
|
*/
|
|
|
|
|
2004-06-27 07:56:44 +00:00
|
|
|
VBESortModes(VBEDeviceExtension);
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
2006-06-05 16:31:24 +00:00
|
|
|
* Print the supported video modes.
|
2004-01-10 21:46:25 +00:00
|
|
|
*/
|
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
for (CurrentMode = 0;
|
2004-06-26 22:30:34 +00:00
|
|
|
CurrentMode < SuitableModeCount;
|
2004-01-10 14:39:21 +00:00
|
|
|
CurrentMode++)
|
|
|
|
{
|
2005-12-01 19:51:08 +00:00
|
|
|
VideoPortDebugPrint(Trace, "%dx%dx%d\n",
|
2004-01-10 14:39:21 +00:00
|
|
|
VBEDeviceExtension->ModeInfo[CurrentMode].XResolution,
|
|
|
|
VBEDeviceExtension->ModeInfo[CurrentMode].YResolution,
|
2005-06-05 11:05:47 +00:00
|
|
|
VBEDeviceExtension->ModeInfo[CurrentMode].BitsPerPixel);
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
|
2006-06-05 16:31:24 +00:00
|
|
|
/*
|
|
|
|
* Enumerate our children.
|
|
|
|
*/
|
|
|
|
VideoPortEnumerateChildren(HwDeviceExtension, NULL);
|
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBEStartIO
|
|
|
|
*
|
|
|
|
* Processes the specified Video Request Packet.
|
|
|
|
*/
|
|
|
|
|
2008-11-30 11:16:55 +00:00
|
|
|
BOOLEAN NTAPI
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEStartIO(
|
|
|
|
PVOID HwDeviceExtension,
|
|
|
|
PVIDEO_REQUEST_PACKET RequestPacket)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN Result;
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
|
2004-01-10 14:39:21 +00:00
|
|
|
|
|
|
|
switch (RequestPacket->IoControlCode)
|
|
|
|
{
|
|
|
|
case IOCTL_VIDEO_SET_CURRENT_MODE:
|
2005-05-08 02:16:32 +00:00
|
|
|
if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE))
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2004-01-10 21:46:25 +00:00
|
|
|
Result = VBESetCurrentMode(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
|
|
|
(PVIDEO_MODE)RequestPacket->InputBuffer,
|
|
|
|
RequestPacket->StatusBlock);
|
2004-01-10 14:39:21 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IOCTL_VIDEO_RESET_DEVICE:
|
2004-01-10 21:46:25 +00:00
|
|
|
Result = VBEResetDevice(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
2004-01-10 14:39:21 +00:00
|
|
|
RequestPacket->StatusBlock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IOCTL_VIDEO_MAP_VIDEO_MEMORY:
|
|
|
|
if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MEMORY_INFORMATION) ||
|
2005-05-08 02:16:32 +00:00
|
|
|
RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2004-01-10 21:46:25 +00:00
|
|
|
Result = VBEMapVideoMemory(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
2004-01-10 14:39:21 +00:00
|
|
|
(PVIDEO_MEMORY)RequestPacket->InputBuffer,
|
|
|
|
(PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer,
|
|
|
|
RequestPacket->StatusBlock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY:
|
2005-05-08 02:16:32 +00:00
|
|
|
if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
|
2004-06-26 22:30:34 +00:00
|
|
|
{
|
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2004-05-03 17:15:14 +00:00
|
|
|
Result = VBEUnmapVideoMemory(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
2004-06-26 22:30:34 +00:00
|
|
|
(PVIDEO_MEMORY)RequestPacket->InputBuffer,
|
2004-01-10 14:39:21 +00:00
|
|
|
RequestPacket->StatusBlock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES:
|
2005-05-08 02:16:32 +00:00
|
|
|
if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES))
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2004-01-10 21:46:25 +00:00
|
|
|
Result = VBEQueryNumAvailModes(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
2004-01-10 14:39:21 +00:00
|
|
|
(PVIDEO_NUM_MODES)RequestPacket->OutputBuffer,
|
|
|
|
RequestPacket->StatusBlock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IOCTL_VIDEO_QUERY_AVAIL_MODES:
|
|
|
|
if (RequestPacket->OutputBufferLength <
|
2005-05-08 02:16:32 +00:00
|
|
|
((PVBE_DEVICE_EXTENSION)HwDeviceExtension)->ModeCount * sizeof(VIDEO_MODE_INFORMATION))
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2004-01-10 21:46:25 +00:00
|
|
|
Result = VBEQueryAvailModes(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
2004-01-10 14:39:21 +00:00
|
|
|
(PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
|
|
|
|
RequestPacket->StatusBlock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IOCTL_VIDEO_SET_COLOR_REGISTERS:
|
2004-05-22 08:53:26 +00:00
|
|
|
if (RequestPacket->InputBufferLength < sizeof(VIDEO_CLUT) ||
|
|
|
|
RequestPacket->InputBufferLength <
|
|
|
|
(((PVIDEO_CLUT)RequestPacket->InputBuffer)->NumEntries * sizeof(ULONG)) +
|
2005-06-07 16:26:14 +00:00
|
|
|
FIELD_OFFSET(VIDEO_CLUT, LookupTable))
|
2004-05-22 08:53:26 +00:00
|
|
|
{
|
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2004-01-10 21:46:25 +00:00
|
|
|
Result = VBESetColorRegisters(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
|
|
|
(PVIDEO_CLUT)RequestPacket->InputBuffer,
|
|
|
|
RequestPacket->StatusBlock);
|
2004-01-10 14:39:21 +00:00
|
|
|
break;
|
|
|
|
|
2004-01-14 16:38:15 +00:00
|
|
|
case IOCTL_VIDEO_QUERY_CURRENT_MODE:
|
2005-05-08 02:16:32 +00:00
|
|
|
if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
|
2004-05-03 17:15:14 +00:00
|
|
|
{
|
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
Result = VBEQueryCurrentMode(
|
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension,
|
|
|
|
(PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
|
|
|
|
RequestPacket->StatusBlock);
|
|
|
|
break;
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
default:
|
2006-01-07 22:36:56 +00:00
|
|
|
RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
|
2004-01-10 14:39:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
if (Result)
|
2006-01-07 22:36:56 +00:00
|
|
|
RequestPacket->StatusBlock->Status = NO_ERROR;
|
2004-01-10 14:39:21 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2004-06-20 16:06:12 +00:00
|
|
|
/*
|
|
|
|
* VBEResetHw
|
|
|
|
*
|
|
|
|
* This function is called to reset the hardware to a known state.
|
|
|
|
*/
|
|
|
|
|
2008-11-30 11:16:55 +00:00
|
|
|
BOOLEAN NTAPI
|
2004-06-20 16:06:12 +00:00
|
|
|
VBEResetHw(
|
|
|
|
PVOID DeviceExtension,
|
|
|
|
ULONG Columns,
|
|
|
|
ULONG Rows)
|
|
|
|
{
|
2009-06-04 13:54:27 +00:00
|
|
|
/* Return FALSE to let HAL reset the display with INT10 */
|
|
|
|
return FALSE;
|
2004-06-20 16:06:12 +00:00
|
|
|
}
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBEGetPowerState
|
|
|
|
*
|
|
|
|
* Queries whether the device can support the requested power state.
|
|
|
|
*/
|
|
|
|
|
2008-11-30 11:16:55 +00:00
|
|
|
VP_STATUS NTAPI
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEGetPowerState(
|
|
|
|
PVOID HwDeviceExtension,
|
|
|
|
ULONG HwId,
|
|
|
|
PVIDEO_POWER_MANAGEMENT VideoPowerControl)
|
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
INT10_BIOS_ARGUMENTS BiosRegisters;
|
2005-05-08 02:16:32 +00:00
|
|
|
PVBE_DEVICE_EXTENSION VBEDeviceExtension =
|
2004-06-26 22:30:34 +00:00
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension;
|
|
|
|
|
|
|
|
if (HwId != DISPLAY_ADAPTER_HW_ID ||
|
|
|
|
VideoPowerControl->Length < sizeof(VIDEO_POWER_MANAGEMENT))
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get general power support information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
|
|
|
BiosRegisters.Eax = VBE_POWER_MANAGEMENT_EXTENSIONS;
|
2005-05-08 02:16:32 +00:00
|
|
|
BiosRegisters.Ebx = 0;
|
2004-06-26 22:30:34 +00:00
|
|
|
BiosRegisters.Edi = 0;
|
|
|
|
BiosRegisters.SegEs = 0;
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
if ( VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_NOT_SUPPORTED)
|
2006-01-07 22:36:56 +00:00
|
|
|
return ERROR_DEV_NOT_EXIST;
|
2008-05-21 13:47:22 +00:00
|
|
|
if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
|
2005-05-08 02:16:32 +00:00
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
/*
|
|
|
|
* Get current power state.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
|
|
|
BiosRegisters.Eax = VBE_POWER_MANAGEMENT_EXTENSIONS;
|
|
|
|
BiosRegisters.Ebx = 0x2;
|
|
|
|
BiosRegisters.Edi = 0;
|
|
|
|
BiosRegisters.SegEs = 0;
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)
|
2004-06-26 22:30:34 +00:00
|
|
|
{
|
|
|
|
VideoPowerControl->DPMSVersion = BiosRegisters.Ebx & 0xFF;
|
|
|
|
switch (BiosRegisters.Ebx >> 8)
|
|
|
|
{
|
|
|
|
case 0: VideoPowerControl->PowerState = VideoPowerOn; break;
|
|
|
|
case 1: VideoPowerControl->PowerState = VideoPowerStandBy; break;
|
|
|
|
case 2: VideoPowerControl->PowerState = VideoPowerSuspend; break;
|
|
|
|
case 4: VideoPowerControl->PowerState = VideoPowerOff; break;
|
|
|
|
case 5: VideoPowerControl->PowerState = VideoPowerOn; break;
|
|
|
|
default: VideoPowerControl->PowerState = VideoPowerUnspecified;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
return ERROR_DEV_NOT_EXIST;
|
2004-01-10 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* VBESetPowerState
|
|
|
|
*
|
|
|
|
* Sets the power state of the specified device
|
|
|
|
*/
|
|
|
|
|
2008-11-30 11:16:55 +00:00
|
|
|
VP_STATUS NTAPI
|
2004-01-10 21:46:25 +00:00
|
|
|
VBESetPowerState(
|
|
|
|
PVOID HwDeviceExtension,
|
|
|
|
ULONG HwId,
|
|
|
|
PVIDEO_POWER_MANAGEMENT VideoPowerControl)
|
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
INT10_BIOS_ARGUMENTS BiosRegisters;
|
2005-05-08 02:16:32 +00:00
|
|
|
PVBE_DEVICE_EXTENSION VBEDeviceExtension =
|
2004-06-26 22:30:34 +00:00
|
|
|
(PVBE_DEVICE_EXTENSION)HwDeviceExtension;
|
|
|
|
|
|
|
|
if (HwId != DISPLAY_ADAPTER_HW_ID ||
|
|
|
|
VideoPowerControl->Length < sizeof(VIDEO_POWER_MANAGEMENT) ||
|
|
|
|
VideoPowerControl->PowerState < VideoPowerOn ||
|
|
|
|
VideoPowerControl->PowerState > VideoPowerHibernate)
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
|
|
|
|
|
|
if (VideoPowerControl->PowerState == VideoPowerHibernate)
|
|
|
|
return NO_ERROR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set current power state.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
|
|
|
BiosRegisters.Eax = VBE_POWER_MANAGEMENT_EXTENSIONS;
|
|
|
|
BiosRegisters.Ebx = 1;
|
|
|
|
BiosRegisters.Edi = 0;
|
|
|
|
BiosRegisters.SegEs = 0;
|
|
|
|
switch (VideoPowerControl->PowerState)
|
|
|
|
{
|
|
|
|
case VideoPowerStandBy: BiosRegisters.Ebx |= 0x100; break;
|
|
|
|
case VideoPowerSuspend: BiosRegisters.Ebx |= 0x200; break;
|
|
|
|
case VideoPowerOff: BiosRegisters.Ebx |= 0x400; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
VBEDeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
VBEDeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_NOT_SUPPORTED)
|
2006-01-07 22:36:56 +00:00
|
|
|
return ERROR_DEV_NOT_EXIST;
|
2008-05-21 13:47:22 +00:00
|
|
|
if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
|
2005-05-08 02:16:32 +00:00
|
|
|
return ERROR_INVALID_FUNCTION;
|
2004-06-26 22:30:34 +00:00
|
|
|
|
|
|
|
return VBE_SUCCESS;
|
2004-01-10 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* VBESetCurrentMode
|
|
|
|
*
|
|
|
|
* Sets the adapter to the specified operating mode.
|
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-01-10 21:46:25 +00:00
|
|
|
VBESetCurrentMode(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
|
|
|
PVIDEO_MODE RequestedMode,
|
|
|
|
PSTATUS_BLOCK StatusBlock)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
INT10_BIOS_ARGUMENTS BiosRegisters;
|
|
|
|
|
|
|
|
if (RequestedMode->RequestedMode >= DeviceExtension->ModeCount)
|
|
|
|
{
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
}
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2004-01-14 16:38:15 +00:00
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
2004-06-26 22:30:34 +00:00
|
|
|
BiosRegisters.Eax = VBE_SET_VBE_MODE;
|
2004-01-10 14:39:21 +00:00
|
|
|
BiosRegisters.Ebx = DeviceExtension->ModeNumbers[RequestedMode->RequestedMode];
|
2004-06-26 22:30:34 +00:00
|
|
|
DeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
DeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
DeviceExtension->CurrentMode = RequestedMode->RequestedMode;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-12-01 19:51:08 +00:00
|
|
|
VideoPortDebugPrint(Error, "VBEMP: VBESetCurrentMode failed (%x)\n", BiosRegisters.Eax);
|
2004-01-10 14:39:21 +00:00
|
|
|
DeviceExtension->CurrentMode = -1;
|
|
|
|
}
|
2004-06-26 22:30:34 +00:00
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
return VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBEResetDevice
|
|
|
|
*
|
|
|
|
* Resets the video hardware to the default mode, to which it was initialized
|
2005-05-08 02:16:32 +00:00
|
|
|
* at system boot.
|
2004-01-10 21:46:25 +00:00
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEResetDevice(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
2004-01-10 14:39:21 +00:00
|
|
|
PSTATUS_BLOCK StatusBlock)
|
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
INT10_BIOS_ARGUMENTS BiosRegisters;
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2004-01-14 16:38:15 +00:00
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
2004-06-26 22:30:34 +00:00
|
|
|
BiosRegisters.Eax = VBE_SET_VBE_MODE;
|
2004-01-10 14:39:21 +00:00
|
|
|
BiosRegisters.Ebx = 0x3;
|
2004-06-26 22:30:34 +00:00
|
|
|
DeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
DeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
return VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBEMapVideoMemory
|
|
|
|
*
|
|
|
|
* Maps the video hardware frame buffer and video RAM into the virtual address
|
2005-05-08 02:16:32 +00:00
|
|
|
* space of the requestor.
|
2004-01-10 21:46:25 +00:00
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEMapVideoMemory(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
|
|
|
PVIDEO_MEMORY RequestedAddress,
|
|
|
|
PVIDEO_MEMORY_INFORMATION MapInformation,
|
2004-01-10 14:39:21 +00:00
|
|
|
PSTATUS_BLOCK StatusBlock)
|
|
|
|
{
|
|
|
|
PHYSICAL_ADDRESS FrameBuffer;
|
2004-06-26 22:30:34 +00:00
|
|
|
ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
|
2004-01-10 14:39:21 +00:00
|
|
|
|
|
|
|
StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION);
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
if (DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].ModeAttributes &
|
|
|
|
VBE_MODEATTR_LINEAR)
|
2005-05-08 02:16:32 +00:00
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
FrameBuffer.QuadPart =
|
|
|
|
DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].PhysBasePtr;
|
|
|
|
MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
|
2004-07-07 16:54:43 +00:00
|
|
|
if (DeviceExtension->VbeInfo.Version < 0x300)
|
|
|
|
{
|
2005-05-08 02:16:32 +00:00
|
|
|
MapInformation->VideoRamLength =
|
2004-07-07 16:54:43 +00:00
|
|
|
DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].BytesPerScanLine *
|
|
|
|
DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].YResolution;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-05-08 02:16:32 +00:00
|
|
|
MapInformation->VideoRamLength =
|
2004-07-07 16:54:43 +00:00
|
|
|
DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].LinBytesPerScanLine *
|
|
|
|
DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].YResolution;
|
|
|
|
}
|
2004-06-26 22:30:34 +00:00
|
|
|
}
|
|
|
|
#ifdef VBE12_SUPPORT
|
|
|
|
else
|
|
|
|
{
|
2005-05-08 02:16:32 +00:00
|
|
|
FrameBuffer.QuadPart = 0xA0000;
|
2004-06-26 22:30:34 +00:00
|
|
|
MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
|
|
|
|
MapInformation->VideoRamLength = 0x10000;
|
|
|
|
}
|
2007-08-20 15:17:06 +00:00
|
|
|
#endif
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2004-04-29 16:55:37 +00:00
|
|
|
VideoPortMapMemory(DeviceExtension, FrameBuffer,
|
|
|
|
&MapInformation->VideoRamLength, &inIoSpace,
|
|
|
|
&MapInformation->VideoRamBase);
|
|
|
|
|
|
|
|
MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
|
|
|
|
MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
|
|
|
|
|
|
|
|
return TRUE;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBEUnmapVideoMemory
|
|
|
|
*
|
|
|
|
* Releases a mapping between the virtual address space and the adapter's
|
|
|
|
* frame buffer and video RAM.
|
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEUnmapVideoMemory(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
2004-06-26 22:30:34 +00:00
|
|
|
PVIDEO_MEMORY VideoMemory,
|
2004-01-10 14:39:21 +00:00
|
|
|
PSTATUS_BLOCK StatusBlock)
|
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
VideoPortUnmapMemory(DeviceExtension, VideoMemory->RequestedVirtualAddress,
|
2004-01-10 14:39:21 +00:00
|
|
|
NULL);
|
|
|
|
return TRUE;
|
2005-05-08 02:16:32 +00:00
|
|
|
}
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBEQueryNumAvailModes
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEQueryNumAvailModes(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
|
|
|
PVIDEO_NUM_MODES Modes,
|
|
|
|
PSTATUS_BLOCK StatusBlock)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
Modes->NumModes = DeviceExtension->ModeCount;
|
|
|
|
Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
|
|
|
|
StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2004-05-03 17:15:14 +00:00
|
|
|
/*
|
|
|
|
* VBEQueryMode
|
|
|
|
*
|
|
|
|
* Returns information about one particular video mode.
|
|
|
|
*/
|
|
|
|
|
2005-05-08 02:16:32 +00:00
|
|
|
VOID FASTCALL
|
2004-05-03 17:15:14 +00:00
|
|
|
VBEQueryMode(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
|
|
|
PVIDEO_MODE_INFORMATION VideoMode,
|
|
|
|
ULONG VideoModeId)
|
|
|
|
{
|
|
|
|
PVBE_MODEINFO VBEMode = &DeviceExtension->ModeInfo[VideoModeId];
|
2010-12-23 12:11:19 +00:00
|
|
|
ULONG dpi;
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2004-05-03 17:15:14 +00:00
|
|
|
VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
|
|
|
|
VideoMode->ModeIndex = VideoModeId;
|
|
|
|
VideoMode->VisScreenWidth = VBEMode->XResolution;
|
|
|
|
VideoMode->VisScreenHeight = VBEMode->YResolution;
|
2004-07-07 16:54:43 +00:00
|
|
|
if (DeviceExtension->VbeInfo.Version < 0x300)
|
|
|
|
VideoMode->ScreenStride = VBEMode->BytesPerScanLine;
|
|
|
|
else
|
|
|
|
VideoMode->ScreenStride = VBEMode->LinBytesPerScanLine;
|
2004-05-03 17:15:14 +00:00
|
|
|
VideoMode->NumberOfPlanes = VBEMode->NumberOfPlanes;
|
|
|
|
VideoMode->BitsPerPlane = VBEMode->BitsPerPixel / VBEMode->NumberOfPlanes;
|
2004-06-29 18:12:28 +00:00
|
|
|
VideoMode->Frequency = 1;
|
2006-01-15 20:25:53 +00:00
|
|
|
|
2010-12-23 12:11:19 +00:00
|
|
|
/* Assume 96DPI and 25.4 millimeters per inch, round to nearest */
|
|
|
|
dpi = 96;
|
|
|
|
VideoMode->XMillimeter = ((ULONGLONG)VBEMode->XResolution * 254 + (dpi * 5)) / (dpi * 10);
|
|
|
|
VideoMode->YMillimeter = ((ULONGLONG)VBEMode->YResolution * 254 + (dpi * 5)) / (dpi * 10);
|
2006-01-15 20:25:53 +00:00
|
|
|
|
2004-05-03 17:15:14 +00:00
|
|
|
if (VBEMode->BitsPerPixel > 8)
|
|
|
|
{
|
2005-05-26 19:58:58 +00:00
|
|
|
/*
|
|
|
|
* Always report 16bpp modes and not 15bpp mode...
|
|
|
|
*/
|
|
|
|
if (VBEMode->BitsPerPixel == 15 && VBEMode->NumberOfPlanes == 1)
|
|
|
|
{
|
|
|
|
VideoMode->BitsPerPlane = 16;
|
|
|
|
}
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
if (DeviceExtension->VbeInfo.Version < 0x300)
|
2004-05-03 17:15:14 +00:00
|
|
|
{
|
|
|
|
VideoMode->NumberRedBits = VBEMode->RedMaskSize;
|
|
|
|
VideoMode->NumberGreenBits = VBEMode->GreenMaskSize;
|
|
|
|
VideoMode->NumberBlueBits = VBEMode->BlueMaskSize;
|
|
|
|
VideoMode->RedMask = ((1 << VBEMode->RedMaskSize) - 1) << VBEMode->RedFieldPosition;
|
|
|
|
VideoMode->GreenMask = ((1 << VBEMode->GreenMaskSize) - 1) << VBEMode->GreenFieldPosition;
|
|
|
|
VideoMode->BlueMask = ((1 << VBEMode->BlueMaskSize) - 1) << VBEMode->BlueFieldPosition;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VideoMode->NumberRedBits = VBEMode->LinRedMaskSize;
|
|
|
|
VideoMode->NumberGreenBits = VBEMode->LinGreenMaskSize;
|
|
|
|
VideoMode->NumberBlueBits = VBEMode->LinBlueMaskSize;
|
|
|
|
VideoMode->RedMask = ((1 << VBEMode->LinRedMaskSize) - 1) << VBEMode->LinRedFieldPosition;
|
|
|
|
VideoMode->GreenMask = ((1 << VBEMode->LinGreenMaskSize) - 1) << VBEMode->LinGreenFieldPosition;
|
|
|
|
VideoMode->BlueMask = ((1 << VBEMode->LinBlueMaskSize) - 1) << VBEMode->LinBlueFieldPosition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-05-08 02:16:32 +00:00
|
|
|
VideoMode->NumberRedBits =
|
|
|
|
VideoMode->NumberGreenBits =
|
2004-05-03 17:15:14 +00:00
|
|
|
VideoMode->NumberBlueBits = 6;
|
2005-05-08 02:16:32 +00:00
|
|
|
VideoMode->RedMask =
|
|
|
|
VideoMode->GreenMask =
|
2004-05-03 17:15:14 +00:00
|
|
|
VideoMode->BlueMask = 0;
|
|
|
|
}
|
|
|
|
VideoMode->VideoMemoryBitmapWidth = VBEMode->XResolution;
|
|
|
|
VideoMode->VideoMemoryBitmapHeight = VBEMode->YResolution;
|
|
|
|
VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR |
|
|
|
|
VIDEO_MODE_NO_OFF_SCREEN;
|
|
|
|
if (VideoMode->BitsPerPlane <= 8)
|
|
|
|
VideoMode->AttributeFlags |= VIDEO_MODE_PALETTE_DRIVEN;
|
|
|
|
VideoMode->DriverSpecificAttributeFlags = 0;
|
|
|
|
}
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBEQueryAvailModes
|
|
|
|
*
|
|
|
|
* Returns information about each video mode supported by the adapter.
|
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-01-10 21:46:25 +00:00
|
|
|
VBEQueryAvailModes(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
|
|
|
PVIDEO_MODE_INFORMATION ReturnedModes,
|
|
|
|
PSTATUS_BLOCK StatusBlock)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
ULONG CurrentModeId;
|
|
|
|
PVIDEO_MODE_INFORMATION CurrentMode;
|
|
|
|
PVBE_MODEINFO CurrentVBEMode;
|
|
|
|
|
|
|
|
for (CurrentModeId = 0, CurrentMode = ReturnedModes,
|
|
|
|
CurrentVBEMode = DeviceExtension->ModeInfo;
|
|
|
|
CurrentModeId < DeviceExtension->ModeCount;
|
|
|
|
CurrentModeId++, CurrentMode++, CurrentVBEMode++)
|
|
|
|
{
|
2004-05-03 17:15:14 +00:00
|
|
|
VBEQueryMode(DeviceExtension, CurrentMode, CurrentModeId);
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StatusBlock->Information =
|
|
|
|
sizeof(VIDEO_MODE_INFORMATION) * DeviceExtension->ModeCount;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2004-05-03 17:15:14 +00:00
|
|
|
/*
|
|
|
|
* VBEQueryCurrentMode
|
|
|
|
*
|
|
|
|
* Returns information about current video mode.
|
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-05-03 17:15:14 +00:00
|
|
|
VBEQueryCurrentMode(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
|
|
|
PVIDEO_MODE_INFORMATION VideoModeInfo,
|
|
|
|
PSTATUS_BLOCK StatusBlock)
|
|
|
|
{
|
|
|
|
StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
|
|
|
|
|
|
|
|
VBEQueryMode(
|
|
|
|
DeviceExtension,
|
|
|
|
VideoModeInfo,
|
|
|
|
DeviceExtension->CurrentMode);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2004-01-10 21:46:25 +00:00
|
|
|
/*
|
|
|
|
* VBESetColorRegisters
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2006-01-07 22:36:56 +00:00
|
|
|
BOOLEAN FASTCALL
|
2004-01-10 21:46:25 +00:00
|
|
|
VBESetColorRegisters(
|
|
|
|
PVBE_DEVICE_EXTENSION DeviceExtension,
|
|
|
|
PVIDEO_CLUT ColorLookUpTable,
|
|
|
|
PSTATUS_BLOCK StatusBlock)
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
2004-06-26 22:30:34 +00:00
|
|
|
INT10_BIOS_ARGUMENTS BiosRegisters;
|
2004-04-23 20:12:57 +00:00
|
|
|
ULONG Entry;
|
|
|
|
PULONG OutputEntry;
|
2004-06-26 22:30:34 +00:00
|
|
|
ULONG OutputBuffer[256];
|
2004-01-10 14:39:21 +00:00
|
|
|
|
2004-05-22 08:53:26 +00:00
|
|
|
if (ColorLookUpTable->NumEntries + ColorLookUpTable->FirstEntry > 256)
|
|
|
|
return FALSE;
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
/*
|
|
|
|
* For VGA compatible adapters program the color registers directly.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!(DeviceExtension->VbeInfo.Capabilities & 2))
|
2004-01-10 14:39:21 +00:00
|
|
|
{
|
|
|
|
for (Entry = ColorLookUpTable->FirstEntry;
|
|
|
|
Entry < ColorLookUpTable->NumEntries + ColorLookUpTable->FirstEntry;
|
|
|
|
Entry++)
|
|
|
|
{
|
|
|
|
VideoPortWritePortUchar((PUCHAR)0x03c8, Entry);
|
|
|
|
VideoPortWritePortUchar((PUCHAR)0x03c9, ColorLookUpTable->LookupTable[Entry].RgbArray.Red);
|
|
|
|
VideoPortWritePortUchar((PUCHAR)0x03c9, ColorLookUpTable->LookupTable[Entry].RgbArray.Green);
|
|
|
|
VideoPortWritePortUchar((PUCHAR)0x03c9, ColorLookUpTable->LookupTable[Entry].RgbArray.Blue);
|
|
|
|
}
|
2004-06-26 22:30:34 +00:00
|
|
|
|
2004-01-10 14:39:21 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2005-05-08 02:16:32 +00:00
|
|
|
* We can't just copy the values, because we need to swap the Red
|
2004-04-23 20:12:57 +00:00
|
|
|
* and Blue values.
|
2004-01-10 14:39:21 +00:00
|
|
|
*/
|
|
|
|
|
2004-04-23 20:12:57 +00:00
|
|
|
for (Entry = ColorLookUpTable->FirstEntry,
|
2004-06-26 22:30:34 +00:00
|
|
|
OutputEntry = OutputBuffer;
|
2004-04-23 20:12:57 +00:00
|
|
|
Entry < ColorLookUpTable->NumEntries + ColorLookUpTable->FirstEntry;
|
|
|
|
Entry++, OutputEntry++)
|
|
|
|
{
|
|
|
|
*OutputEntry =
|
|
|
|
(ColorLookUpTable->LookupTable[Entry].RgbArray.Red << 16) |
|
|
|
|
(ColorLookUpTable->LookupTable[Entry].RgbArray.Green << 8) |
|
|
|
|
(ColorLookUpTable->LookupTable[Entry].RgbArray.Blue);
|
|
|
|
}
|
|
|
|
|
2004-06-26 22:30:34 +00:00
|
|
|
DeviceExtension->Int10Interface.Int10WriteMemory(
|
|
|
|
DeviceExtension->Int10Interface.Context,
|
|
|
|
DeviceExtension->TrampolineMemorySegment,
|
|
|
|
DeviceExtension->TrampolineMemoryOffset,
|
|
|
|
OutputBuffer,
|
|
|
|
(OutputEntry - OutputBuffer) * sizeof(ULONG));
|
|
|
|
|
|
|
|
VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
|
|
|
|
BiosRegisters.Eax = VBE_SET_GET_PALETTE_DATA;
|
2004-01-10 14:39:21 +00:00
|
|
|
BiosRegisters.Ebx = 0;
|
|
|
|
BiosRegisters.Ecx = ColorLookUpTable->NumEntries;
|
|
|
|
BiosRegisters.Edx = ColorLookUpTable->FirstEntry;
|
2004-06-26 22:30:34 +00:00
|
|
|
BiosRegisters.Edi = DeviceExtension->TrampolineMemoryOffset;
|
|
|
|
BiosRegisters.SegEs = DeviceExtension->TrampolineMemorySegment;
|
|
|
|
DeviceExtension->Int10Interface.Int10CallBios(
|
|
|
|
DeviceExtension->Int10Interface.Context,
|
|
|
|
&BiosRegisters);
|
|
|
|
|
2008-05-21 13:47:22 +00:00
|
|
|
return VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS;
|
2004-01-10 14:39:21 +00:00
|
|
|
}
|
|
|
|
}
|