-
John Hodge authored15442765
bochsvbe.c 8.58 KiB
/**
* Acess2 Bochs graphics adapter Driver
* - By John Hodge (thePowersGang)
*
* bochsvbe.c
* - Driver core
*/
#define DEBUG 0
#define VERSION VER2(0,10)
#include <acess.h>
#include <errno.h>
#include <modules.h>
#include <vfs.h>
#include <fs_devfs.h>
#include <drv_pci.h>
#include <api_drv_video.h>
// === TYPES ===
typedef struct sBGA_Mode {
Uint16 width;
Uint16 height;
Uint16 bpp;
Uint32 fbSize;
} tBGA_Mode;
// === CONSTANTS ===
#define BGA_LFB_MAXSIZE (1024*768*4)
#define VBE_DISPI_BANK_ADDRESS 0xA0000
#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_LFB_ENABLED 0x40
#define VBE_DISPI_NOCLEARMEM 0x80
enum {
VBE_DISPI_INDEX_ID,
VBE_DISPI_INDEX_XRES,
VBE_DISPI_INDEX_YRES,
VBE_DISPI_INDEX_BPP,
VBE_DISPI_INDEX_ENABLE,
VBE_DISPI_INDEX_BANK,
VBE_DISPI_INDEX_VIRT_WIDTH,
VBE_DISPI_INDEX_VIRT_HEIGHT,
VBE_DISPI_INDEX_X_OFFSET,
VBE_DISPI_INDEX_Y_OFFSET
};
extern void MM_DumpTables(tVAddr Start, tVAddr End);
// === PROTOTYPES ===
// Driver
int BGA_Install(char **Arguments);
void BGA_Uninstall();
// Internal
void BGA_int_WriteRegister(Uint16 reg, Uint16 value);
Uint16 BGA_int_ReadRegister(Uint16 reg);
void BGA_int_SetBank(Uint16 bank);
void BGA_int_SetMode(Uint16 width, Uint16 height);
int BGA_int_UpdateMode(int id);
int BGA_int_FindMode(tVideo_IOCtl_Mode *info);
int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);
int BGA_int_MapFB(void *Dest);
// Filesystem
Uint64 BGA_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer);
Uint64 BGA_Write(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer);
int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data);
// === GLOBALS ===
MODULE_DEFINE(0, VERSION, BochsGA, BGA_Install, NULL, "PCI", NULL);
tDevFS_Driver gBGA_DriverStruct = {
NULL, "BochsGA",
{
.Read = BGA_Read,
.Write = BGA_Write,
.IOCtl = BGA_IOCtl
}
};
int giBGA_CurrentMode = -1;
tVideo_IOCtl_Pos gBGA_CursorPos = {-1,-1};
Uint *gBGA_Framebuffer;
const tBGA_Mode *gpBGA_CurrentMode;
const tBGA_Mode gBGA_Modes[] = {
{640,480,32, 640*480*4},
{800,600,32, 800*600*4},
{1024,768,32, 1024*768*4}
};
#define BGA_MODE_COUNT (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0]))
tDrvUtil_Video_BufInfo gBGA_DrvUtil_BufInfo;
// === CODE ===
/**
* \fn int BGA_Install(char **Arguments)
*/
int BGA_Install(char **Arguments)
{
int version = 0;
tPAddr base;
tPCIDev dev;
// Check BGA Version
version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);
LOG("version = 0x%x", version);
// NOTE: This driver was written for BGA versions >= 0xBOC2
// NOTE: However, Qemu is braindead and doesn't return the actual version
if( version != 0xB0C0 && ((version & 0xFFF0) != 0xB0C0 || version < 0xB0C2) ) {
Log_Warning("BGA", "Bochs Adapter Version is not compatible (need >= 0xB0C2), instead 0x%x", version);
return MODULE_ERR_NOTNEEDED;
}
// Get framebuffer base
dev = PCI_GetDevice(0x1234, 0x1111, 0);
if(dev == -1)
base = VBE_DISPI_LFB_PHYSICAL_ADDRESS;
else
base = PCI_GetBAR(dev, 0);
// Map Framebuffer to hardware address
gBGA_Framebuffer = (void *) MM_MapHWPages(base, 768); // 768 pages (3Mb)
// Install Device
if( DevFS_AddDevice( &gBGA_DriverStruct ) == -1 )
{
Log_Warning("BGA", "Unable to register with DevFS, maybe already loaded?");
return MODULE_ERR_MISC;
}
return MODULE_ERR_OK;
}
/**
* \brief Clean up driver resources before destruction
*/
void BGA_Uninstall(void)
{
DevFS_DelDevice( &gBGA_DriverStruct );
MM_UnmapHWPages( (tVAddr)gBGA_Framebuffer, 768 );
}
/**
* \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
* \brief Read from the framebuffer
*/
Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
{
// Check Mode
if(giBGA_CurrentMode == -1) return -1;
// Check Offset and Length against Framebuffer Size
if(off+len > gpBGA_CurrentMode->fbSize)
return -1;
// Copy from Framebuffer
memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);
return len;
}
/**
* \brief Write to the framebuffer
*/
Uint64 BGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
{
if( giBGA_CurrentMode == -1 ) BGA_int_UpdateMode(0);
return DrvUtil_Video_WriteLFB(&gBGA_DrvUtil_BufInfo, Offset, Length, Buffer);
}
const char *csaBGA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};
/**
* \brief Handle messages to the device
*/
int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data)
{
int ret = -2;
ENTER("pNode iId pData", Node, ID, Data);
switch(ID)
{
BASE_IOCTLS(DRV_TYPE_VIDEO, "BochsGA", VERSION, csaBGA_IOCtls);
case VIDEO_IOCTL_GETSETMODE:
if( Data ) BGA_int_UpdateMode(*(int*)(Data));
ret = giBGA_CurrentMode;
break;
case VIDEO_IOCTL_FINDMODE:
ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data);
break;
case VIDEO_IOCTL_MODEINFO:
ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data);
break;
case VIDEO_IOCTL_SETBUFFORMAT:
ret = gBGA_DrvUtil_BufInfo.BufferFormat;
if(Data)
gBGA_DrvUtil_BufInfo.BufferFormat = *(int*)Data;
break;
case VIDEO_IOCTL_SETCURSOR:
gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x;
gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y;
break;
default:
LEAVE('i', -2);
return -2;
}
LEAVE('i', ret);
return ret;
}
//== Internal Functions ==
/**
* \brief Writes to a BGA register
*/
void BGA_int_WriteRegister(Uint16 reg, Uint16 value)
{
outw(VBE_DISPI_IOPORT_INDEX, reg);
outw(VBE_DISPI_IOPORT_DATA, value);
}
Uint16 BGA_int_ReadRegister(Uint16 reg)
{
outw(VBE_DISPI_IOPORT_INDEX, reg);
return inw(VBE_DISPI_IOPORT_DATA);
}
/**
* \brief Sets the video mode (32bpp only)
*/
void BGA_int_SetMode(Uint16 Width, Uint16 Height)
{
ENTER("iWidth iHeight", Width, Height);
BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, Width);
BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, Height);
BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, 32);
BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);
MAGIC_BREAK();
LEAVE('-');
}
/**
* \fn int BGA_int_UpdateMode(int id)
* \brief Set current vide mode given a mode id
*/
int BGA_int_UpdateMode(int id)
{
// Sanity Check
if(id < 0 || id >= BGA_MODE_COUNT) return -1;
BGA_int_SetMode(
gBGA_Modes[id].width,
gBGA_Modes[id].height);
gBGA_DrvUtil_BufInfo.Framebuffer = gBGA_Framebuffer;
gBGA_DrvUtil_BufInfo.Pitch = gBGA_Modes[id].width * 4;
gBGA_DrvUtil_BufInfo.Width = gBGA_Modes[id].width;
gBGA_DrvUtil_BufInfo.Height = gBGA_Modes[id].height;
gBGA_DrvUtil_BufInfo.Depth = gBGA_Modes[id].bpp;
giBGA_CurrentMode = id;
gpBGA_CurrentMode = &gBGA_Modes[id];
return id;
}
/**
* \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)
* \brief Find a mode matching the given options
*/
int BGA_int_FindMode(tVideo_IOCtl_Mode *info)
{
int i;
int best = 0, bestFactor = 1000;
int tmp;
int rqdProduct = info->width * info->height;
ENTER("pinfo", info);
LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);
for(i = 0; i < BGA_MODE_COUNT; i++)
{
#if DEBUG >= 2
LOG("Mode %i (%ix%i,%ibpp), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp);
#endif
if( gBGA_Modes[i].bpp != info->bpp )
continue ;
// Ooh! A perfect match
if(gBGA_Modes[i].width == info->width && gBGA_Modes[i].height == info->height)
{
#if DEBUG >= 2
LOG("Perfect");
#endif
best = i;
break;
}
// If not, how close are we?
tmp = gBGA_Modes[i].width * gBGA_Modes[i].height - rqdProduct;
tmp = tmp < 0 ? -tmp : tmp; // tmp = ABS(tmp)
#if DEBUG >= 2
LOG("tmp = %i", tmp);
#endif
if(tmp < bestFactor)
{
bestFactor = tmp;
best = i;
}
}
info->id = best;
info->width = gBGA_Modes[best].width;
info->height = gBGA_Modes[best].height;
info->bpp = gBGA_Modes[best].bpp;
LEAVE('i', best);
return best;
}
/**
* \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)
* \brief Get mode information
*/
int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)
{
// Sanity Check
//if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) {
// return -EINVAL;
//}
if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1;
info->width = gBGA_Modes[info->id].width;
info->height = gBGA_Modes[info->id].height;
info->bpp = gBGA_Modes[info->id].bpp;
return 1;
}