mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Refactor kernel graphics support.
This commit is contained in:
parent
8f7c72abc1
commit
ee13ffa715
10 changed files with 893 additions and 1392 deletions
|
@ -75,7 +75,6 @@ $(CPUOBJS) \
|
|||
addralloc.o \
|
||||
alarm.o \
|
||||
ata.o \
|
||||
bga.o \
|
||||
calltrace.o \
|
||||
clock.o \
|
||||
com.o \
|
||||
|
@ -85,7 +84,6 @@ $(CPU)/kthread.o \
|
|||
crc32.o \
|
||||
debugger.o \
|
||||
descriptor.o \
|
||||
dispmsg.o \
|
||||
dtable.o \
|
||||
elf.o \
|
||||
fcache.o \
|
||||
|
@ -96,6 +94,7 @@ fs/null.o \
|
|||
fs/user.o \
|
||||
fs/util.o \
|
||||
fs/zero.o \
|
||||
gpu/bga/bga.o \
|
||||
identity.o \
|
||||
initrd.o \
|
||||
inode.o \
|
||||
|
|
585
kernel/bga.cpp
585
kernel/bga.cpp
|
@ -1,585 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix 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 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
bga.cpp
|
||||
Driver for the Bochs VBE Extensions.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sortix/mman.h>
|
||||
|
||||
#include <sortix/kernel/addralloc.h>
|
||||
#include <sortix/kernel/cpu.h>
|
||||
#include <sortix/kernel/ioport.h>
|
||||
#include <sortix/kernel/kernel.h>
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include <sortix/kernel/pci.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/string.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
|
||||
#include "x86-family/memorymanagement.h"
|
||||
#include "lfbtextbuffer.h"
|
||||
#include "bga.h"
|
||||
|
||||
namespace Sortix {
|
||||
namespace BGA {
|
||||
|
||||
const bool TEST_RES_BY_TRYING = false;
|
||||
|
||||
const uint16_t VBE_DISPI_INDEX_ID = 0;
|
||||
const uint16_t VBE_DISPI_INDEX_XRES = 1;
|
||||
const uint16_t VBE_DISPI_INDEX_YRES = 2;
|
||||
const uint16_t VBE_DISPI_INDEX_BPP = 3;
|
||||
const uint16_t VBE_DISPI_INDEX_ENABLE = 4;
|
||||
const uint16_t VBE_DISPI_INDEX_BANK = 5;
|
||||
const uint16_t VBE_DISPI_INDEX_VIRT_WIDTH = 6;
|
||||
const uint16_t VBE_DISPI_INDEX_VIRT_HEIGHT = 7;
|
||||
const uint16_t VBE_DISPI_INDEX_X_OFFSET = 8;
|
||||
const uint16_t VBE_DISPI_INDEX_Y_OFFSET = 9;
|
||||
|
||||
const uint16_t VBE_DISPI_IOPORT_INDEX = 0x01CE;
|
||||
const uint16_t VBE_DISPI_IOPORT_DATA = 0x01CF;
|
||||
|
||||
const uint16_t VBE_DISPI_BPP_4 = 0x04;
|
||||
const uint16_t VBE_DISPI_BPP_8 = 0x08;
|
||||
const uint16_t VBE_DISPI_BPP_15 = 0x0F;
|
||||
const uint16_t VBE_DISPI_BPP_16 = 0x10;
|
||||
const uint16_t VBE_DISPI_BPP_24 = 0x18;
|
||||
const uint16_t VBE_DISPI_BPP_32 = 0x20;
|
||||
|
||||
const uint16_t VBE_DISPI_DISABLED = 0x00;
|
||||
const uint16_t VBE_DISPI_ENABLED = 0x01;
|
||||
const uint16_t VBE_DISPI_GETCAPS = 0x02;
|
||||
const uint16_t VBE_DISPI_8BIT_DAC = 0x20;
|
||||
const uint16_t VBE_DISPI_LFB_ENABLED = 0x40;
|
||||
const uint16_t VBE_DISPI_NOCLEARMEM = 0x80;
|
||||
|
||||
const uint16_t VBE_MIN_SUP_VERSION = 0xB0C0;
|
||||
const uint16_t VBE_MIN_POS_VERSION = 0xB0C0;
|
||||
const uint16_t VBE_MAX_POS_VERSION = 0xB0CF;
|
||||
|
||||
const size_t VBE_BANK_SIZE = 64UL * 1024UL;
|
||||
volatile uint8_t* const VBE_VIDEO_MEM = (volatile uint8_t*) 0xA0000;
|
||||
|
||||
static addr_t ParseDevBar0(uint32_t devaddr)
|
||||
{
|
||||
pcibar_t bar = PCI::GetBAR(devaddr, 0);
|
||||
return bar.addr();
|
||||
}
|
||||
|
||||
addr_t DetectBGAFramebuffer()
|
||||
{
|
||||
uint32_t devaddr;
|
||||
pcifind_t pcifind;
|
||||
|
||||
// Search for the bochs BGA device and compatible.
|
||||
memset(&pcifind, 255, sizeof(pcifind));
|
||||
pcifind.vendorid = 0x1234;
|
||||
pcifind.deviceid = 0x1111;
|
||||
if ( (devaddr = PCI::SearchForDevices(pcifind, 0)) )
|
||||
return ParseDevBar0(devaddr);
|
||||
|
||||
// Search for a generic VGA compatible device.
|
||||
memset(&pcifind, 255, sizeof(pcifind));
|
||||
pcifind.classid = 0x03;
|
||||
pcifind.subclassid = 0x00;
|
||||
pcifind.progif = 0x00;
|
||||
if ( (devaddr = PCI::SearchForDevices(pcifind)) )
|
||||
return ParseDevBar0(devaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t version;
|
||||
uint16_t maxbpp;
|
||||
uint16_t maxxres;
|
||||
uint16_t maxyres;
|
||||
|
||||
uint16_t curbpp;
|
||||
uint16_t curxres;
|
||||
uint16_t curyres;
|
||||
uint16_t curbank;
|
||||
|
||||
addr_t bgaframebuffer;
|
||||
|
||||
void WriteRegister(uint16_t index, uint16_t value)
|
||||
{
|
||||
outport16(VBE_DISPI_IOPORT_INDEX, index);
|
||||
outport16(VBE_DISPI_IOPORT_DATA, value);
|
||||
}
|
||||
|
||||
uint16_t ReadRegister(uint16_t index)
|
||||
{
|
||||
outport16(VBE_DISPI_IOPORT_INDEX, index);
|
||||
return inport16(VBE_DISPI_IOPORT_DATA);
|
||||
}
|
||||
|
||||
uint16_t GetCapability(uint16_t index)
|
||||
{
|
||||
uint16_t wasenabled = ReadRegister(VBE_DISPI_INDEX_ENABLE);
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, wasenabled | VBE_DISPI_GETCAPS);
|
||||
uint16_t cap = ReadRegister(index);
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, wasenabled);
|
||||
return cap;
|
||||
}
|
||||
|
||||
bool SetVideoMode(uint16_t width, uint16_t height, uint16_t depth, bool keep)
|
||||
{
|
||||
bool uselinear = true;
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
|
||||
WriteRegister(VBE_DISPI_INDEX_XRES, width);
|
||||
WriteRegister(VBE_DISPI_INDEX_YRES, height);
|
||||
WriteRegister(VBE_DISPI_INDEX_BPP, depth);
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED |
|
||||
(uselinear ? VBE_DISPI_LFB_ENABLED : 0) |
|
||||
(keep ? VBE_DISPI_NOCLEARMEM : 0));
|
||||
curbpp = depth;
|
||||
curxres = width;
|
||||
curyres = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsStandardResolution(uint16_t width, uint16_t height, uint16_t depth)
|
||||
{
|
||||
if ( depth != VBE_DISPI_BPP_32 ) { return false; }
|
||||
if ( width == 800 && height == 600 ) { return true; }
|
||||
if ( width == 1024 && height == 768 ) { return true; }
|
||||
if ( width == 1280 && height == 720 ) { return true; }
|
||||
if ( width == 1280 && height == 1024 ) { return true; }
|
||||
if ( width == 1600 && height == 900 ) { return true; }
|
||||
if ( width == 1920 && height == 1080 ) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Need a better method of detecting available/desired resolutions.
|
||||
bool SupportsResolution(uint16_t width, uint16_t height, uint16_t depth)
|
||||
{
|
||||
if ( !width || !height || !depth ) { return false; }
|
||||
if ( maxxres < width || maxyres < height || maxbpp < depth ) return false;
|
||||
if ( width % 8U ) { return false; }
|
||||
uint16_t wasenabled = ReadRegister(VBE_DISPI_INDEX_ENABLE);
|
||||
if ( width == curxres && height == curyres && depth == curbpp) return true;
|
||||
if ( !TEST_RES_BY_TRYING ) { return true; }
|
||||
SetVideoMode(width, height, depth, true);
|
||||
uint16_t newxres = ReadRegister(VBE_DISPI_INDEX_XRES);
|
||||
uint16_t newyres = ReadRegister(VBE_DISPI_INDEX_XRES);
|
||||
uint16_t newbpp = ReadRegister(VBE_DISPI_INDEX_BPP);
|
||||
bool result = newxres != curxres || newyres != curyres || newbpp != curbpp;
|
||||
SetVideoMode(curxres, curyres, curbpp, true);
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, wasenabled);
|
||||
return result;
|
||||
}
|
||||
|
||||
class BGADriver : public VideoDriver
|
||||
{
|
||||
public:
|
||||
BGADriver();
|
||||
virtual ~BGADriver();
|
||||
|
||||
public:
|
||||
virtual bool StartUp();
|
||||
virtual bool ShutDown();
|
||||
virtual char* GetCurrentMode() const;
|
||||
virtual bool SwitchMode(const char* mode);
|
||||
virtual bool Supports(const char* mode) const;
|
||||
virtual char** GetModes(size_t* nummodes) const;
|
||||
virtual off_t FrameSize() const;
|
||||
virtual ssize_t WriteAt(off_t off, const void* buf, size_t count);
|
||||
virtual ssize_t ReadAt(off_t off, void* buf, size_t count);
|
||||
virtual TextBuffer* CreateTextBuffer();
|
||||
|
||||
private:
|
||||
bool MapVideoMemory(size_t size);
|
||||
bool MapVideoMemoryRange(addr_t mapat, size_t from, size_t to);
|
||||
bool IncreaseVirtual(size_t new_size);
|
||||
bool DetectModes() const;
|
||||
|
||||
private:
|
||||
mutable size_t nummodes;
|
||||
mutable char** modes;
|
||||
char* curmode;
|
||||
size_t lfbmapped;
|
||||
size_t framesize;
|
||||
addralloc_t addr_allocation;
|
||||
|
||||
};
|
||||
|
||||
BGADriver::BGADriver()
|
||||
{
|
||||
nummodes = 0;
|
||||
modes = NULL;
|
||||
curmode = NULL;
|
||||
lfbmapped = 0;
|
||||
framesize = 0;
|
||||
memset(&addr_allocation, 0, sizeof(addr_allocation));
|
||||
}
|
||||
|
||||
BGADriver::~BGADriver()
|
||||
{
|
||||
MapVideoMemory(0);
|
||||
FreeKernelAddress(&addr_allocation);
|
||||
for ( size_t i = 0; i < nummodes; i++ )
|
||||
{
|
||||
delete[] modes[i];
|
||||
}
|
||||
delete[] modes;
|
||||
delete[] curmode;
|
||||
}
|
||||
|
||||
bool BGADriver::MapVideoMemoryRange(addr_t mapat, size_t from, size_t to)
|
||||
{
|
||||
addr_t phys = bgaframebuffer;
|
||||
const addr_t mtype = Memory::PAT_WC;
|
||||
for ( size_t i = from; i < to; i += Page::Size() )
|
||||
if ( !Memory::MapPAT(phys+i, mapat+i, PROT_KWRITE | PROT_KREAD, mtype) )
|
||||
{
|
||||
Log::PrintF("Error: Insufficient memory to map BGA framebuffer "
|
||||
"onto kernel address space: needed 0x%zx bytes but "
|
||||
"only 0x%zx was available at this point.\n", to, i);
|
||||
for ( size_t n = from; n < i; n += Page::Size() )
|
||||
Memory::Unmap(mapat + n);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BGADriver::IncreaseVirtual(size_t new_size)
|
||||
{
|
||||
new_size = Page::AlignUp(new_size);
|
||||
assert(addr_allocation.size < new_size);
|
||||
|
||||
addralloc_t new_addralloc;
|
||||
if ( !AllocateKernelAddress(&new_addralloc, new_size) )
|
||||
{
|
||||
Log::PrintF("Error: Insufficient virtual address space for BGA "
|
||||
"frame of size 0x%zx bytes, only 0x%zx was available.\n",
|
||||
new_size, addr_allocation.size);
|
||||
return false;
|
||||
}
|
||||
|
||||
addr_t old_mapat = addr_allocation.from;
|
||||
addr_t new_mapat = new_addralloc.from;
|
||||
|
||||
if ( !MapVideoMemoryRange(new_mapat, 0, new_size) )
|
||||
{
|
||||
FreeKernelAddress(&addr_allocation);
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < lfbmapped; i += Page::Size() )
|
||||
Memory::Unmap(old_mapat + i);
|
||||
|
||||
FreeKernelAddress(&addr_allocation);
|
||||
|
||||
lfbmapped = new_size;
|
||||
addr_allocation = new_addralloc;
|
||||
|
||||
Memory::Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BGADriver::MapVideoMemory(size_t size)
|
||||
{
|
||||
size = Page::AlignUp(size);
|
||||
|
||||
if ( size == lfbmapped )
|
||||
return true;
|
||||
|
||||
if ( addr_allocation.size < size )
|
||||
return IncreaseVirtual(size);
|
||||
|
||||
addr_t mapat = addr_allocation.from;
|
||||
for ( size_t i = size; i < lfbmapped; i+= Page::Size() )
|
||||
Memory::Unmap(mapat + i);
|
||||
|
||||
lfbmapped = size;
|
||||
Memory::Flush();
|
||||
|
||||
if ( !size )
|
||||
FreeKernelAddress(&addr_allocation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BGADriver::StartUp()
|
||||
{
|
||||
if ( !modes && !DetectModes() ) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BGADriver::ShutDown()
|
||||
{
|
||||
MapVideoMemory(0);
|
||||
if ( curmode )
|
||||
{
|
||||
delete[] curmode; curmode = NULL;
|
||||
errno = ENOSYS;
|
||||
return false; // TODO: Return to VGA Text Mode.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char* BGADriver::GetCurrentMode() const
|
||||
{
|
||||
if ( !curmode ) { errno = EINVAL; return NULL; }
|
||||
return String::Clone(curmode);
|
||||
}
|
||||
|
||||
bool BGADriver::SwitchMode(const char* mode)
|
||||
{
|
||||
bool result = false;
|
||||
char* modeclone = String::Clone(mode);
|
||||
if ( !modeclone )
|
||||
return NULL;
|
||||
char* xstr = NULL;
|
||||
char* ystr = NULL;
|
||||
char* bppstr = NULL;
|
||||
if ( !ReadParamString(mode, "width", &xstr, "height", &ystr,
|
||||
"bpp", &bppstr, "STOP") )
|
||||
{
|
||||
delete[] modeclone;
|
||||
return false;
|
||||
}
|
||||
uint16_t xres = xstr ? atoi(xstr) : 0;
|
||||
uint16_t yres = ystr ? atoi(ystr) : 0;
|
||||
uint16_t bpp = bppstr ? atoi(bppstr) : 32;
|
||||
size_t newframesize = (size_t) xres * (size_t) yres * (size_t) bpp/8UL;
|
||||
|
||||
// If the current resolution uses more memory than the new one, keep it
|
||||
// around in case setting the video mode failed.
|
||||
if ( MapVideoMemory(newframesize < lfbmapped ? lfbmapped : newframesize) &&
|
||||
SetVideoMode(xres, yres, bpp, false) )
|
||||
{
|
||||
delete[] curmode;
|
||||
curmode = modeclone;
|
||||
modeclone = NULL;
|
||||
// We can now truncate the amount of memory to what we really need.
|
||||
MapVideoMemory(framesize = newframesize);
|
||||
result = true;
|
||||
}
|
||||
|
||||
delete[] xstr;
|
||||
delete[] ystr;
|
||||
delete[] bppstr;
|
||||
delete[] modeclone;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BGADriver::Supports(const char* mode) const
|
||||
{
|
||||
char* xstr = NULL;
|
||||
char* ystr = NULL;
|
||||
char* bppstr = NULL;
|
||||
if ( !ReadParamString(mode, "width", &xstr, "height", &ystr,
|
||||
"bpp", &bppstr, NULL, NULL) ) { return false; }
|
||||
uint16_t xres = xstr ? atoi(xstr) : 0;
|
||||
uint16_t yres = ystr ? atoi(ystr) : 0;
|
||||
uint16_t bpp = bppstr ? atoi(bppstr) : 0;
|
||||
bool result = SupportsResolution(xres, yres, bpp);
|
||||
delete[] xstr;
|
||||
delete[] ystr;
|
||||
delete[] bppstr;
|
||||
return result;
|
||||
}
|
||||
|
||||
char** BGADriver::GetModes(size_t* retnum) const
|
||||
{
|
||||
if ( !modes && !DetectModes() ) { return NULL; }
|
||||
char** result = new char*[nummodes];
|
||||
if ( !result ) { return NULL; }
|
||||
for ( size_t i = 0; i < nummodes; i++ )
|
||||
{
|
||||
result[i] = String::Clone(modes[i]);
|
||||
if ( !result[i] )
|
||||
{
|
||||
for ( size_t n = 0; n < i; i++ ) { delete[] result[n]; }
|
||||
delete[] result;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
*retnum = nummodes;
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t BGADriver::FrameSize() const
|
||||
{
|
||||
return curxres * curyres * (curbpp / 8UL);
|
||||
}
|
||||
|
||||
ssize_t BGADriver::WriteAt(off_t off, const void* buf, size_t count)
|
||||
{
|
||||
uint8_t* frame = (uint8_t*) addr_allocation.from;
|
||||
if ( (off_t) framesize <= off )
|
||||
return 0;
|
||||
if ( framesize < off + count )
|
||||
count = framesize - off;
|
||||
memcpy(frame + off, buf, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
ssize_t BGADriver::ReadAt(off_t off, void* buf, size_t count)
|
||||
{
|
||||
const uint8_t* frame = (const uint8_t*) addr_allocation.from;
|
||||
if ( (off_t) framesize <= off )
|
||||
return 0;
|
||||
if ( framesize < off + count )
|
||||
count = framesize - off;
|
||||
memcpy(buf, frame + off, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
bool BGADriver::DetectModes() const
|
||||
{
|
||||
nummodes = 0;
|
||||
unsigned bpp = VBE_DISPI_BPP_32;
|
||||
for ( unsigned w = 0; w < maxxres; w += 4U )
|
||||
{
|
||||
for ( unsigned h = 0; h < maxyres; h += 4UL )
|
||||
{
|
||||
if ( !IsStandardResolution(w, h, bpp) ) { continue; }
|
||||
if ( !SupportsResolution(w, h, bpp) ) { continue; }
|
||||
nummodes++;
|
||||
}
|
||||
}
|
||||
modes = new char*[nummodes];
|
||||
if ( !modes ) { return false; }
|
||||
memset(modes, 0, sizeof(char*) * nummodes);
|
||||
size_t curmodeid = 0;
|
||||
for ( unsigned w = 0; w < maxxres; w += 4U )
|
||||
{
|
||||
for ( unsigned h = 0; h < maxyres; h += 4UL )
|
||||
{
|
||||
if ( !IsStandardResolution(w, h, bpp) ) { continue; }
|
||||
if ( !SupportsResolution(w, h, bpp) ) { continue; }
|
||||
char bppstr[64];
|
||||
char xresstr[64];
|
||||
char yresstr[64];
|
||||
snprintf(bppstr, 64, "%u", bpp);
|
||||
snprintf(xresstr, 64, "%u", w);
|
||||
snprintf(yresstr, 64, "%u", h);
|
||||
char* modestr = String::Combine(6, "width=", xresstr, ",height=",
|
||||
yresstr, ",bpp=", bppstr);
|
||||
if ( !modestr ) { return false; }
|
||||
modes[curmodeid++] = modestr;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TextBuffer* BGADriver::CreateTextBuffer()
|
||||
{
|
||||
uint8_t* lfb = (uint8_t*) addr_allocation.from;
|
||||
uint32_t lfbformat = curbpp;
|
||||
size_t scansize = curxres * curbpp / 8UL;
|
||||
return CreateLFBTextBuffer(lfb, lfbformat, curxres, curyres, scansize);
|
||||
}
|
||||
|
||||
static uint16_t ProbeBGAVersion()
|
||||
{
|
||||
// First see if the register is in the legal range.
|
||||
uint16_t ver = ReadRegister(VBE_DISPI_INDEX_ID);
|
||||
if ( ver < VBE_MIN_POS_VERSION )
|
||||
return 0;
|
||||
if ( ver > VBE_MAX_POS_VERSION )
|
||||
return 0;
|
||||
|
||||
// The bootloader or BIOS may have set the current version to less than what
|
||||
// really is supported. If we a version number to the register, we can read
|
||||
// it back only if it is supported.
|
||||
|
||||
// If the register accepts an invalid version number, don't trust it and we
|
||||
// may be in danger if an unrelated type of register is using this IO port.
|
||||
// Since that is unlikely, just assume we got a real BGA device.
|
||||
WriteRegister(VBE_DISPI_INDEX_ID, 0xFFFF);
|
||||
if ( ReadRegister(VBE_DISPI_INDEX_ID) == 0xFFFF )
|
||||
{
|
||||
WriteRegister(VBE_DISPI_INDEX_ID, ver);
|
||||
Log::PrintF("Warning: Found what appears to be BGA hardware, but it "
|
||||
"behaves differently when attempting to scan what version "
|
||||
"it conforms to. ");
|
||||
if ( ver < VBE_MIN_SUP_VERSION )
|
||||
{
|
||||
Log::PrintF("The hardware is by default set to an old unsupported "
|
||||
"version, this driver will not use it.\n");
|
||||
return 0;
|
||||
}
|
||||
Log::PrintF("The driver will use this hardware (even though it may not "
|
||||
"be BGA hardware) and bad things may happen.\n");
|
||||
return ver;
|
||||
}
|
||||
|
||||
// Attempt to query all possible version ids.
|
||||
for ( uint16_t i = ver; i < VBE_MAX_POS_VERSION; i++ )
|
||||
{
|
||||
WriteRegister(VBE_DISPI_INDEX_ID, i);
|
||||
if ( ReadRegister(VBE_DISPI_INDEX_ID) == i )
|
||||
ver = i;
|
||||
}
|
||||
|
||||
return ver;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
if ( !(version = ProbeBGAVersion()) )
|
||||
return;
|
||||
|
||||
curbpp = 0;
|
||||
curxres = 0;
|
||||
curyres = 0;
|
||||
curbank = 0xFFFF;
|
||||
|
||||
maxbpp = GetCapability(VBE_DISPI_INDEX_BPP);
|
||||
maxxres = GetCapability(VBE_DISPI_INDEX_XRES);
|
||||
maxyres = GetCapability(VBE_DISPI_INDEX_YRES);
|
||||
|
||||
if ( !(bgaframebuffer = DetectBGAFramebuffer()) )
|
||||
{
|
||||
Log::PrintF("BGA support detected but no PCI device could be found "
|
||||
"determines the location of the framebuffer. Rather than "
|
||||
"guessing it is at 0xE0000000, this driver shuts down to "
|
||||
"avoid corrupting possible memory there.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
BGADriver* bgadriver = new BGADriver;
|
||||
if ( !bgadriver )
|
||||
{
|
||||
Log::PrintF("Unable to allocate BGA driver, but hardware present\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Video::RegisterDriver("bga", bgadriver) )
|
||||
{
|
||||
Log::PrintF("Unable to register BGA driver, but hardware present\n");
|
||||
delete bgadriver;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace BGA
|
||||
} // namespace Sortix
|
|
@ -1,342 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix 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 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
dispmsg.cpp
|
||||
User-space message-based interface for video framework access.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sortix/display.h>
|
||||
#include <sortix/syscallnum.h>
|
||||
|
||||
#include <sortix/kernel/kernel.h>
|
||||
#include <sortix/kernel/string.h>
|
||||
#include <sortix/kernel/syscall.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
|
||||
#include "dispmsg.h"
|
||||
|
||||
namespace Sortix {
|
||||
namespace DisplayMessage {
|
||||
|
||||
const uint64_t ONE_AND_ONLY_DEVICE = 0;
|
||||
const uint64_t ONE_AND_ONLY_CONNECTOR = 0;
|
||||
|
||||
__attribute__((unused))
|
||||
static char* StringOfCrtcMode(struct dispmsg_crtc_mode mode)
|
||||
{
|
||||
char bppstr[sizeof(mode.fb_format) * 3];
|
||||
char xresstr[sizeof(mode.view_xres) * 3];
|
||||
char yresstr[sizeof(mode.view_yres) * 3];
|
||||
char magicstr[sizeof(mode.magic) * 3];
|
||||
snprintf(bppstr, sizeof(bppstr), "%ju", (uintmax_t) mode.fb_format);
|
||||
snprintf(xresstr, sizeof(xresstr), "%ju", (uintmax_t) mode.view_xres);
|
||||
snprintf(yresstr, sizeof(yresstr), "%ju", (uintmax_t) mode.view_yres);
|
||||
snprintf(magicstr, sizeof(magicstr), "%ju", (uintmax_t) mode.magic);
|
||||
char* drivername = Video::GetDriverName(mode.driver_index);
|
||||
if ( !drivername )
|
||||
return NULL;
|
||||
char* ret = String::Combine(10,
|
||||
"driver=", drivername, ","
|
||||
"bpp=", bppstr, ","
|
||||
"width=", xresstr, ","
|
||||
"height=", yresstr, ","
|
||||
"modeid=", magicstr);
|
||||
delete[] drivername;
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
struct dispmsg_crtc_mode CrtcModeOfString(const char* string, uint64_t driverid)
|
||||
{
|
||||
char* xstr = NULL;
|
||||
char* ystr = NULL;
|
||||
char* bppstr = NULL;
|
||||
char* modeidstr = NULL;
|
||||
char* unsupportedstr = NULL;
|
||||
struct dispmsg_crtc_mode ret;
|
||||
ret.control = 0;
|
||||
if ( !strcmp(string, "driver=none") )
|
||||
return ret;
|
||||
if ( !ReadParamString(string,
|
||||
"width", &xstr,
|
||||
"height", &ystr,
|
||||
"bpp", &bppstr,
|
||||
"modeid", &modeidstr,
|
||||
"unsupported", &unsupportedstr,
|
||||
NULL, NULL) )
|
||||
return ret;
|
||||
ret.driver_index = driverid;
|
||||
ret.view_xres = xstr ? atol(xstr) : 0;
|
||||
delete[] xstr;
|
||||
ret.view_yres = ystr ? atol(ystr) : 0;
|
||||
delete[] ystr;
|
||||
ret.fb_format = bppstr ? atol(bppstr) : 0;
|
||||
delete[] bppstr;
|
||||
ret.magic = modeidstr ? atoll(modeidstr) : 0;
|
||||
delete[] modeidstr;
|
||||
if ( !unsupportedstr )
|
||||
ret.control = 1;
|
||||
delete[] unsupportedstr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static bool TransmitString(struct dispmsg_string* dest, const char* str)
|
||||
{
|
||||
size_t size = strlen(str) + 1;
|
||||
size_t dest_size = dest->byte_size;
|
||||
dest->byte_size = size;
|
||||
if ( dest_size < size )
|
||||
return errno = ERANGE, false;
|
||||
strcpy(dest->str, str);
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static char* ReceiveString(struct dispmsg_string* src)
|
||||
{
|
||||
if ( !src->byte_size )
|
||||
bad_input:
|
||||
return errno = EINVAL, (char*) NULL;
|
||||
char* ret = new char[src->byte_size];
|
||||
if ( !ret )
|
||||
return NULL;
|
||||
memcpy(ret, src->str, src->byte_size);
|
||||
if ( ret[src->byte_size-1] != '\0' ) { delete[] ret; goto bad_input; }
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int EnumerateDevices(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_enumerate_devices) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_enumerate_devices* msg =
|
||||
(struct dispmsg_enumerate_devices*) ptr;
|
||||
// TODO: HACK: Only one device is currently supported by our backend.
|
||||
size_t num_devices = 1;
|
||||
if ( msg->devices_length < num_devices )
|
||||
{
|
||||
msg->devices_length = num_devices;
|
||||
return errno = ERANGE, -1;
|
||||
}
|
||||
msg->devices[0] = ONE_AND_ONLY_DEVICE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetDriverCount(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_get_driver_count) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_get_driver_count* msg =
|
||||
(struct dispmsg_get_driver_count*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
msg->driver_count = Video::GetNumDrivers();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetDriverName(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_get_driver_name) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_get_driver_name* msg =
|
||||
(struct dispmsg_get_driver_name* ) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
char* name = Video::GetDriverName(msg->driver_index);
|
||||
if ( !name )
|
||||
return -1;
|
||||
bool success = TransmitString(&msg->name, name);
|
||||
delete[] name;
|
||||
return success ? 0 : -1;
|
||||
}
|
||||
|
||||
static int GetDriver(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_get_driver) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_get_driver* msg = (struct dispmsg_get_driver*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
msg->driver_index = Video::GetCurrentDriverIndex();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SetDriver(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_set_driver) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_set_driver* msg = (struct dispmsg_set_driver*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
return errno = ENOSYS, -1;
|
||||
}
|
||||
|
||||
static int SetCrtcMode(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_set_crtc_mode) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_set_crtc_mode* msg = (struct dispmsg_set_crtc_mode*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
if ( msg->connector != ONE_AND_ONLY_CONNECTOR )
|
||||
return errno = EINVAL, -1;
|
||||
char* modestr = StringOfCrtcMode(msg->mode);
|
||||
if ( !modestr )
|
||||
return -1;
|
||||
bool success = Video::SwitchMode(modestr);
|
||||
delete[] modestr;
|
||||
return success ? 0 : -1;
|
||||
}
|
||||
|
||||
static int GetCrtcMode(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_get_crtc_mode) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_get_crtc_mode* msg = (struct dispmsg_get_crtc_mode*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
if ( msg->connector != ONE_AND_ONLY_CONNECTOR )
|
||||
return errno = EINVAL, -1;
|
||||
char* modestr = Video::GetCurrentMode();
|
||||
if ( !modestr )
|
||||
return -1;
|
||||
msg->mode = CrtcModeOfString(modestr, Video::GetCurrentDriverIndex());
|
||||
delete[] modestr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetCrtcModes(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_get_crtc_modes) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_get_crtc_modes* msg = (struct dispmsg_get_crtc_modes*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
if ( msg->connector != ONE_AND_ONLY_CONNECTOR )
|
||||
return errno = EINVAL, -1;
|
||||
size_t nummodes;
|
||||
char** modes = Video::GetModes(&nummodes);
|
||||
if ( !modes )
|
||||
return -1;
|
||||
size_t dest_length = msg->modes_length;
|
||||
|
||||
int ret;
|
||||
if ( nummodes <= dest_length )
|
||||
{
|
||||
ret = 0;
|
||||
for ( size_t i = 0; i < nummodes; i++ )
|
||||
{
|
||||
const char* modestr = modes[i];
|
||||
uint64_t driver_index = Video::LookupDriverIndexOfMode(modestr);
|
||||
msg->modes[i] = CrtcModeOfString(modestr, driver_index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = ERANGE;
|
||||
ret = -1;
|
||||
}
|
||||
msg->modes_length = nummodes;
|
||||
for ( size_t i = 0; i < nummodes; i++ )
|
||||
delete[] modes[i];
|
||||
delete[] modes;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int GetMemorySize(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_get_memory_size) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_get_memory_size* msg =
|
||||
(struct dispmsg_get_memory_size*) ptr;
|
||||
msg->memory_size = Video::FrameSize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WriteMemory(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_write_memory) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_write_memory* msg = (struct dispmsg_write_memory*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
if ( OFF_MAX < msg->offset )
|
||||
return errno = EOVERFLOW, -1;
|
||||
off_t offset = msg->offset;
|
||||
if ( Video::WriteAt(offset, msg->src, msg->size) < 0 )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ReadMemory(void* ptr, size_t size)
|
||||
{
|
||||
if ( size != sizeof(struct dispmsg_read_memory) )
|
||||
return errno = EINVAL, -1;
|
||||
struct dispmsg_read_memory* msg = (struct dispmsg_read_memory*) ptr;
|
||||
if ( msg->device != ONE_AND_ONLY_DEVICE )
|
||||
return errno = EINVAL, -1;
|
||||
if ( OFF_MAX < msg->offset )
|
||||
return errno = EOVERFLOW, -1;
|
||||
off_t offset = msg->offset;
|
||||
if ( Video::ReadAt(offset, msg->dst, msg->size) < 0 )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Secure this system call against bad user-space pointers.
|
||||
static int sys_dispmsg_issue(void* ptr, size_t size)
|
||||
{
|
||||
struct dispmsg_header* hdr = (struct dispmsg_header*) ptr;
|
||||
if ( size < sizeof(*hdr) )
|
||||
return errno = EINVAL, -1;
|
||||
switch ( hdr->msgid )
|
||||
{
|
||||
case DISPMSG_ENUMERATE_DEVICES: return EnumerateDevices(ptr, size);
|
||||
case DISPMSG_GET_DRIVER_COUNT: return GetDriverCount(ptr, size);
|
||||
case DISPMSG_GET_DRIVER_NAME: return GetDriverName(ptr, size);
|
||||
case DISPMSG_GET_DRIVER: return GetDriver(ptr, size);
|
||||
case DISPMSG_SET_DRIVER: return SetDriver(ptr, size);
|
||||
case DISPMSG_SET_CRTC_MODE: return SetCrtcMode(ptr, size);
|
||||
case DISPMSG_GET_CRTC_MODE: return GetCrtcMode(ptr, size);
|
||||
case DISPMSG_GET_CRTC_MODES: return GetCrtcModes(ptr, size);
|
||||
case DISPMSG_GET_MEMORY_SIZE: return GetMemorySize(ptr, size);
|
||||
case DISPMSG_WRITE_MEMORY: return WriteMemory(ptr, size);
|
||||
case DISPMSG_READ_MEMORY: return ReadMemory(ptr, size);
|
||||
default:
|
||||
return errno = ENOSYS, -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
Syscall::Register(SYSCALL_DISPMSG_ISSUE, (void*) sys_dispmsg_issue);
|
||||
}
|
||||
|
||||
} // namespace DisplayMessage
|
||||
} // namespace Sortix
|
|
@ -1,36 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix 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 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
dispmsg.h
|
||||
User-space message-based interface for video framework access.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_DISPMSG_H
|
||||
#define SORTIX_DISPMSG_H
|
||||
|
||||
namespace Sortix {
|
||||
namespace DisplayMessage {
|
||||
|
||||
void Init();
|
||||
|
||||
} // namespace DisplayMessage
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
528
kernel/gpu/bga/bga.cpp
Normal file
528
kernel/gpu/bga/bga.cpp
Normal file
|
@ -0,0 +1,528 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix 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 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
gpu/bga/bga.cpp
|
||||
Driver for the Bochs VBE Extensions.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sortix/mman.h>
|
||||
|
||||
#include <sortix/kernel/addralloc.h>
|
||||
#include <sortix/kernel/cpu.h>
|
||||
#include <sortix/kernel/ioport.h>
|
||||
#include <sortix/kernel/kernel.h>
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include <sortix/kernel/pci.h>
|
||||
#include <sortix/kernel/pci-mmio.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/string.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
|
||||
#include "x86-family/memorymanagement.h"
|
||||
#include "lfbtextbuffer.h"
|
||||
#include "bga.h"
|
||||
|
||||
namespace Sortix {
|
||||
namespace BGA {
|
||||
|
||||
const uint16_t VBE_DISPI_INDEX_ID = 0;
|
||||
const uint16_t VBE_DISPI_INDEX_XRES = 1;
|
||||
const uint16_t VBE_DISPI_INDEX_YRES = 2;
|
||||
const uint16_t VBE_DISPI_INDEX_BPP = 3;
|
||||
const uint16_t VBE_DISPI_INDEX_ENABLE = 4;
|
||||
const uint16_t VBE_DISPI_INDEX_BANK = 5;
|
||||
const uint16_t VBE_DISPI_INDEX_VIRT_WIDTH = 6;
|
||||
const uint16_t VBE_DISPI_INDEX_VIRT_HEIGHT = 7;
|
||||
const uint16_t VBE_DISPI_INDEX_X_OFFSET = 8;
|
||||
const uint16_t VBE_DISPI_INDEX_Y_OFFSET = 9;
|
||||
const uint16_t VBE_DISPI_NUM_REGISTERS = 10;
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
const uint16_t VBE_DISPI_IOPORT_INDEX = 0x01CE;
|
||||
const uint16_t VBE_DISPI_IOPORT_DATA = 0x01CF;
|
||||
#endif
|
||||
|
||||
const uint16_t VBE_DISPI_BPP_4 = 0x04;
|
||||
const uint16_t VBE_DISPI_BPP_8 = 0x08;
|
||||
const uint16_t VBE_DISPI_BPP_15 = 0x0F;
|
||||
const uint16_t VBE_DISPI_BPP_16 = 0x10;
|
||||
const uint16_t VBE_DISPI_BPP_24 = 0x18;
|
||||
const uint16_t VBE_DISPI_BPP_32 = 0x20;
|
||||
|
||||
const uint16_t VBE_DISPI_DISABLED = 0x00;
|
||||
const uint16_t VBE_DISPI_ENABLED = 0x01;
|
||||
const uint16_t VBE_DISPI_GETCAPS = 0x02;
|
||||
const uint16_t VBE_DISPI_8BIT_DAC = 0x20;
|
||||
const uint16_t VBE_DISPI_LFB_ENABLED = 0x40;
|
||||
const uint16_t VBE_DISPI_NOCLEARMEM = 0x80;
|
||||
|
||||
const uint16_t VBE_MIN_SUP_VERSION = 0xB0C0;
|
||||
const uint16_t VBE_MIN_POS_VERSION = 0xB0C0;
|
||||
const uint16_t VBE_MAX_POS_VERSION = 0xB0CF;
|
||||
|
||||
static bool IsStandardResolution(uint16_t width, uint16_t height, uint16_t depth)
|
||||
{
|
||||
if ( depth != VBE_DISPI_BPP_32 ) { return false; }
|
||||
if ( width == 720 && height == 400 ) { return true; }
|
||||
if ( width == 800 && height == 600 ) { return true; }
|
||||
if ( width == 1024 && height == 768 ) { return true; }
|
||||
if ( width == 1280 && height == 720 ) { return true; }
|
||||
if ( width == 1280 && height == 1024 ) { return true; }
|
||||
if ( width == 1600 && height == 900 ) { return true; }
|
||||
if ( width == 1920 && height == 1080 ) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
class BGADevice : public VideoDevice
|
||||
{
|
||||
public:
|
||||
BGADevice(uint32_t devaddr, addralloc_t fb_alloc, addralloc_t mmio_alloc);
|
||||
virtual ~BGADevice();
|
||||
|
||||
public:
|
||||
virtual struct dispmsg_crtc_mode GetCurrentMode(uint64_t connector) const;
|
||||
virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode);
|
||||
virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const;
|
||||
virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* num_modes) const;
|
||||
virtual off_t FrameSize() const;
|
||||
virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count);
|
||||
virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count);
|
||||
virtual TextBuffer* CreateTextBuffer(uint64_t connector);
|
||||
|
||||
public:
|
||||
bool Initialize();
|
||||
|
||||
private:
|
||||
bool DetectModes() const;
|
||||
uint16_t WriteRegister(uint16_t index, uint16_t value);
|
||||
uint16_t ReadRegister(uint16_t index);
|
||||
uint16_t GetCapability(uint16_t index);
|
||||
bool SetVideoMode(uint16_t width, uint16_t height, uint16_t depth, bool keep);
|
||||
bool SupportsResolution(uint16_t width, uint16_t height, uint16_t depth);
|
||||
|
||||
private:
|
||||
mutable size_t num_modes;
|
||||
mutable struct dispmsg_crtc_mode* modes;
|
||||
struct dispmsg_crtc_mode current_mode;
|
||||
addralloc_t fb_alloc;
|
||||
addralloc_t mmio_alloc;
|
||||
uint32_t devaddr;
|
||||
uint16_t version;
|
||||
uint16_t maxbpp;
|
||||
uint16_t maxxres;
|
||||
uint16_t maxyres;
|
||||
|
||||
};
|
||||
|
||||
BGADevice::BGADevice(uint32_t devaddr, addralloc_t fb_alloc, addralloc_t mmio_alloc) :
|
||||
fb_alloc(fb_alloc), mmio_alloc(mmio_alloc), devaddr(devaddr)
|
||||
{
|
||||
num_modes = 0;
|
||||
modes = NULL;
|
||||
memset(¤t_mode, 0, sizeof(current_mode));
|
||||
version = 0;
|
||||
maxbpp = 0;
|
||||
maxxres = 0;
|
||||
maxyres = 0;
|
||||
}
|
||||
|
||||
BGADevice::~BGADevice()
|
||||
{
|
||||
UnmapPCIBar(&fb_alloc);
|
||||
UnmapPCIBar(&mmio_alloc);
|
||||
delete[] modes;
|
||||
}
|
||||
|
||||
uint16_t BGADevice::WriteRegister(uint16_t index, uint16_t value)
|
||||
{
|
||||
assert(index < VBE_DISPI_NUM_REGISTERS);
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if ( mmio_alloc.size == 0 )
|
||||
{
|
||||
outport16(VBE_DISPI_IOPORT_INDEX, index);
|
||||
return outport16(VBE_DISPI_IOPORT_DATA, value);
|
||||
}
|
||||
#endif
|
||||
volatile little_uint16_t* regs =
|
||||
(volatile little_uint16_t*) (mmio_alloc.from + 0x500);
|
||||
return regs[index] = value;
|
||||
}
|
||||
|
||||
uint16_t BGADevice::ReadRegister(uint16_t index)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if ( mmio_alloc.size == 0 )
|
||||
{
|
||||
outport16(VBE_DISPI_IOPORT_INDEX, index);
|
||||
return inport16(VBE_DISPI_IOPORT_DATA);
|
||||
}
|
||||
#endif
|
||||
assert(index < VBE_DISPI_NUM_REGISTERS);
|
||||
volatile little_uint16_t* regs =
|
||||
(volatile little_uint16_t*) (mmio_alloc.from + 0x500);
|
||||
return regs[index];
|
||||
}
|
||||
|
||||
uint16_t BGADevice::GetCapability(uint16_t index)
|
||||
{
|
||||
uint16_t was_enabled = ReadRegister(VBE_DISPI_INDEX_ENABLE);
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, was_enabled | VBE_DISPI_GETCAPS);
|
||||
uint16_t cap = ReadRegister(index);
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, was_enabled);
|
||||
return cap;
|
||||
}
|
||||
|
||||
bool BGADevice::Initialize()
|
||||
{
|
||||
if ( (version = ReadRegister(VBE_DISPI_INDEX_ID)) < VBE_MIN_SUP_VERSION )
|
||||
{
|
||||
Log::PrintF("[BGA device @ PCI:0x%X] Hardware version 0x%X is too old, "
|
||||
"minimum version supported is 0x%X\n",
|
||||
devaddr, version, VBE_MIN_SUP_VERSION);
|
||||
return false;
|
||||
}
|
||||
|
||||
maxbpp = GetCapability(VBE_DISPI_INDEX_BPP);
|
||||
maxxres = GetCapability(VBE_DISPI_INDEX_XRES);
|
||||
maxyres = GetCapability(VBE_DISPI_INDEX_YRES);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BGADevice::SetVideoMode(uint16_t width, uint16_t height, uint16_t depth, bool keep)
|
||||
{
|
||||
bool uselinear = true;
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
|
||||
WriteRegister(VBE_DISPI_INDEX_XRES, width);
|
||||
WriteRegister(VBE_DISPI_INDEX_YRES, height);
|
||||
WriteRegister(VBE_DISPI_INDEX_BPP, depth);
|
||||
WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED |
|
||||
(uselinear ? VBE_DISPI_LFB_ENABLED : 0) |
|
||||
(keep ? VBE_DISPI_NOCLEARMEM : 0));
|
||||
// TODO: How do we verify the video mode was *actually* set?
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Need a better method of detecting available/desired resolutions.
|
||||
bool BGADevice::SupportsResolution(uint16_t width, uint16_t height, uint16_t depth)
|
||||
{
|
||||
if ( !width || !height || !depth )
|
||||
return false;
|
||||
if ( maxxres < width || maxyres < height || maxbpp < depth )
|
||||
return false;
|
||||
// TODO: Is this actually a restriction?
|
||||
if ( width % 8U )
|
||||
return false;
|
||||
// TODO: Can we determine this more closely in advance? Perhaps if the
|
||||
// framebuffer we will be using is larger than video memory?
|
||||
return true;
|
||||
}
|
||||
|
||||
struct dispmsg_crtc_mode BGADevice::GetCurrentMode(uint64_t connector) const
|
||||
{
|
||||
if ( connector != 0 )
|
||||
{
|
||||
errno = EINVAL;
|
||||
struct dispmsg_crtc_mode mode;
|
||||
memset(&mode, 0, sizeof(mode));
|
||||
return mode;
|
||||
}
|
||||
|
||||
return current_mode;
|
||||
}
|
||||
|
||||
bool BGADevice::SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode)
|
||||
{
|
||||
if ( !Supports(connector, mode) )
|
||||
return false;
|
||||
|
||||
if ( connector != 0 )
|
||||
return errno = EINVAL, false;
|
||||
|
||||
size_t new_framesize = (size_t) mode.view_xres *
|
||||
(size_t) mode.view_yres *
|
||||
((size_t) mode.fb_format + 7) / 8UL;
|
||||
// TODO: Use a better error code than ENOSPC?
|
||||
if ( fb_alloc.size < new_framesize )
|
||||
return errno = ENOSPC, false;
|
||||
|
||||
if ( !SetVideoMode(mode.view_xres, mode.view_yres, mode.fb_format, false) )
|
||||
return false;
|
||||
|
||||
current_mode = mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BGADevice::Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const
|
||||
{
|
||||
if ( connector != 0 )
|
||||
return errno = EINVAL, false;
|
||||
|
||||
if ( mode.control & DISPMSG_CONTROL_VGA )
|
||||
return errno = EINVAL, false;
|
||||
if ( !(mode.control & DISPMSG_CONTROL_VALID) )
|
||||
return errno = EINVAL, false;
|
||||
|
||||
if ( UINT16_MAX < mode.view_xres )
|
||||
return errno = EINVAL, false;
|
||||
if ( UINT16_MAX < mode.view_yres )
|
||||
return errno = EINVAL, false;
|
||||
// TODO: This is wrong, list the right values as above.
|
||||
if ( mode.fb_format != VBE_DISPI_BPP_4 &&
|
||||
mode.fb_format != VBE_DISPI_BPP_8 &&
|
||||
mode.fb_format != VBE_DISPI_BPP_15 &&
|
||||
mode.fb_format != VBE_DISPI_BPP_16 &&
|
||||
mode.fb_format != VBE_DISPI_BPP_24 &&
|
||||
mode.fb_format != VBE_DISPI_BPP_32 )
|
||||
return errno = EINVAL, false;
|
||||
|
||||
// TODO: This is disabled because its support needs to be verified, see the
|
||||
// framebuffer size calculation above?
|
||||
if ( mode.fb_format != VBE_DISPI_BPP_32 )
|
||||
return errno = ENOSYS, false;
|
||||
|
||||
return ((BGADevice*) this)->SupportsResolution(mode.view_xres, mode.view_yres, mode.fb_format);
|
||||
}
|
||||
|
||||
struct dispmsg_crtc_mode* BGADevice::GetModes(uint64_t connector, size_t* retnum) const
|
||||
{
|
||||
if ( connector != 0 )
|
||||
return errno = EINVAL, (struct dispmsg_crtc_mode*) NULL;
|
||||
|
||||
if ( !modes && !DetectModes() )
|
||||
return NULL;
|
||||
struct dispmsg_crtc_mode* result = new struct dispmsg_crtc_mode[num_modes];
|
||||
if ( !result )
|
||||
return NULL;
|
||||
for ( size_t i = 0; i < num_modes; i++ )
|
||||
result[i] = modes[i];
|
||||
*retnum = num_modes;
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t BGADevice::FrameSize() const
|
||||
{
|
||||
return (off_t) fb_alloc.size;
|
||||
}
|
||||
|
||||
ssize_t BGADevice::WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count)
|
||||
{
|
||||
uint8_t* frame = (uint8_t*) fb_alloc.from;
|
||||
if ( (off_t) fb_alloc.size <= off )
|
||||
return 0;
|
||||
if ( fb_alloc.size < off + count )
|
||||
count = fb_alloc.size - off;
|
||||
if ( !ctx->copy_from_src(frame + off, buf, count) )
|
||||
return -1;
|
||||
return count;
|
||||
}
|
||||
|
||||
ssize_t BGADevice::ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count)
|
||||
{
|
||||
const uint8_t* frame = (const uint8_t*) fb_alloc.from;
|
||||
if ( (off_t) fb_alloc.size <= off )
|
||||
return 0;
|
||||
if ( fb_alloc.size < off + count )
|
||||
count = fb_alloc.size - off;
|
||||
if ( !ctx->copy_to_dest(buf, frame + off, count) )
|
||||
return -1;
|
||||
return count;
|
||||
}
|
||||
|
||||
bool BGADevice::DetectModes() const
|
||||
{
|
||||
num_modes = 0;
|
||||
unsigned bpp = VBE_DISPI_BPP_32;
|
||||
for ( unsigned w = 0; w < maxxres; w += 4U )
|
||||
{
|
||||
for ( unsigned h = 0; h < maxyres; h += 4UL )
|
||||
{
|
||||
if ( !IsStandardResolution(w, h, bpp) )
|
||||
continue;
|
||||
if ( !((BGADevice*) this)->SupportsResolution(w, h, bpp) )
|
||||
continue;
|
||||
num_modes++;
|
||||
}
|
||||
}
|
||||
num_modes++;
|
||||
modes = new struct dispmsg_crtc_mode[num_modes];
|
||||
if ( !modes )
|
||||
return false;
|
||||
memset(modes, 0, sizeof(char*) * num_modes);
|
||||
size_t current_mode_id = 0;
|
||||
for ( unsigned w = 0; w < maxxres; w += 4U )
|
||||
{
|
||||
for ( unsigned h = 0; h < maxyres; h += 4UL )
|
||||
{
|
||||
if ( !IsStandardResolution(w, h, bpp) )
|
||||
continue;
|
||||
if ( !((BGADevice*) this)->SupportsResolution(w, h, bpp) )
|
||||
continue;
|
||||
struct dispmsg_crtc_mode mode;
|
||||
memset(&mode, 0, sizeof(mode));
|
||||
mode.view_xres = w;
|
||||
mode.view_yres = h;
|
||||
mode.fb_format = bpp;
|
||||
mode.control = DISPMSG_CONTROL_VALID;
|
||||
modes[current_mode_id++] = mode;
|
||||
}
|
||||
}
|
||||
|
||||
struct dispmsg_crtc_mode any_mode;
|
||||
memset(&any_mode, 0, sizeof(any_mode));
|
||||
any_mode.view_xres = 0;
|
||||
any_mode.view_yres = 0;
|
||||
any_mode.fb_format = 0;
|
||||
any_mode.control = DISPMSG_CONTROL_OTHER_RESOLUTIONS;
|
||||
modes[num_modes-1] = any_mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TextBuffer* BGADevice::CreateTextBuffer(uint64_t connector)
|
||||
{
|
||||
if ( connector != 0 )
|
||||
return errno = EINVAL, (TextBuffer*) NULL;
|
||||
|
||||
uint8_t* lfb = (uint8_t*) fb_alloc.from;
|
||||
uint32_t lfbformat = current_mode.fb_format;
|
||||
size_t scansize = current_mode.view_xres * current_mode.fb_format / 8UL;
|
||||
return CreateLFBTextBuffer(lfb, lfbformat, current_mode.view_xres, current_mode.view_yres, scansize);
|
||||
}
|
||||
|
||||
static void TryInitializeDevice(uint32_t devaddr)
|
||||
{
|
||||
pciid_t id = PCI::GetDeviceId(devaddr);
|
||||
|
||||
bool is_qemu_bga = id.vendorid == 0x1234 && id.deviceid == 0x1111;
|
||||
bool is_vbox_bga = id.vendorid == 0x80EE && id.deviceid == 0xBEEF;
|
||||
|
||||
(void) is_qemu_bga;
|
||||
(void) is_vbox_bga;
|
||||
|
||||
pcibar_t fb_bar;
|
||||
pcibar_t mmio_bar;
|
||||
addralloc_t fb_alloc;
|
||||
addralloc_t mmio_alloc;
|
||||
bool has_mmio = false;
|
||||
bool fallback_ioport = false;
|
||||
|
||||
fb_bar = PCI::GetBAR(devaddr, 0);
|
||||
if ( !MapPCIBAR(&fb_alloc, fb_bar, MAP_PCI_BAR_WRITE_COMBINE) )
|
||||
{
|
||||
Log::PrintF("[BGA device @ PCI:0x%X] Framebuffer could not be mapped: %s\n",
|
||||
devaddr, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_qemu_bga )
|
||||
mmio_bar = PCI::GetBAR(devaddr, 2);
|
||||
|
||||
if ( is_qemu_bga && mmio_bar.is_mmio() && 4096 <= mmio_bar.size() )
|
||||
{
|
||||
has_mmio = true;
|
||||
|
||||
if ( !MapPCIBAR(&mmio_alloc, mmio_bar, MAP_PCI_BAR_WRITE_COMBINE) )
|
||||
{
|
||||
Log::PrintF("[BGA device @ PCI:0x%X] Memory-mapped registers could not be mapped: %s\n",
|
||||
devaddr, strerror(errno));
|
||||
UnmapPCIBar(&fb_alloc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// This device doesn't come with its own set of registers, so we have to
|
||||
// assume that the global BGA io port registers are available and that
|
||||
// only a single such device is present (since two concurrent devices)
|
||||
// could not exist then. This is only available on x86-family systems.
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
fallback_ioport = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( !has_mmio && !fallback_ioport )
|
||||
{
|
||||
Log::PrintF("[BGA device @ PCI:0x%X] Device provides no registers.\n",
|
||||
devaddr);
|
||||
UnmapPCIBar(&fb_alloc);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( fallback_ioport )
|
||||
memset(&mmio_alloc, 0, sizeof(mmio_alloc));
|
||||
|
||||
BGADevice* bga_device = new BGADevice(devaddr, fb_alloc, mmio_alloc);
|
||||
if ( !bga_device )
|
||||
{
|
||||
Log::PrintF("[BGA device @ PCI:0x%X] Unable to allocate driver structure: %s\n",
|
||||
devaddr, strerror(errno));
|
||||
UnmapPCIBar(&mmio_alloc);
|
||||
UnmapPCIBar(&fb_alloc);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !bga_device->Initialize() )
|
||||
{
|
||||
delete bga_device;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Video::RegisterDevice("bga", bga_device) )
|
||||
{
|
||||
Log::PrintF("[BGA device @ PCI:0x%X] Unable to register device: %s\n",
|
||||
devaddr, strerror(errno));
|
||||
delete bga_device;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
pcifind_t bga_pcifind;
|
||||
memset(&bga_pcifind, 255, sizeof(bga_pcifind));
|
||||
bga_pcifind.vendorid = 0x1234;
|
||||
bga_pcifind.deviceid = 0x1111;
|
||||
|
||||
uint32_t devaddr = 0;
|
||||
while ( (devaddr = PCI::SearchForDevices(bga_pcifind, devaddr)) )
|
||||
TryInitializeDevice(devaddr);
|
||||
|
||||
memset(&bga_pcifind, 255, sizeof(bga_pcifind));
|
||||
bga_pcifind.vendorid = 0x80EE;
|
||||
bga_pcifind.deviceid = 0xBEEF;
|
||||
|
||||
devaddr = 0;
|
||||
while ( (devaddr = PCI::SearchForDevices(bga_pcifind, devaddr)) )
|
||||
TryInitializeDevice(devaddr);
|
||||
}
|
||||
|
||||
} // namespace BGA
|
||||
} // namespace Sortix
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -17,13 +17,13 @@
|
|||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
bga.h
|
||||
gpu/bga/bga.h
|
||||
Driver for the Bochs VBE Extensions.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_BGA_H
|
||||
#define SORTIX_BGA_H
|
||||
#ifndef SORTIX_GPU_BGA_BGA_H
|
||||
#define SORTIX_GPU_BGA_BGA_H
|
||||
|
||||
namespace Sortix {
|
||||
namespace BGA {
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -32,6 +32,10 @@
|
|||
|
||||
__BEGIN_DECLS
|
||||
|
||||
const uint32_t DISPMSG_CONTROL_VALID = 1 << 0;
|
||||
const uint32_t DISPMSG_CONTROL_VGA = 1 << 1;
|
||||
const uint32_t DISPMSG_CONTROL_OTHER_RESOLUTIONS = 1 << 2;
|
||||
|
||||
struct dispmsg_string
|
||||
{
|
||||
size_t byte_size; // Including the terminating NUL-byte.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -22,11 +22,14 @@
|
|||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_VIDEO_H
|
||||
#define SORTIX_VIDEO_H
|
||||
#ifndef INCLUDE_SORTIX_KERNEL_VIDEO_H
|
||||
#define INCLUDE_SORTIX_KERNEL_VIDEO_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sortix/display.h>
|
||||
|
||||
#include <sortix/kernel/ioctx.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
@ -34,43 +37,30 @@ namespace Sortix {
|
|||
class TextBuffer;
|
||||
class TextBufferHandle;
|
||||
|
||||
bool ReadParamString(const char* str, ...);
|
||||
|
||||
class VideoDriver
|
||||
class VideoDevice
|
||||
{
|
||||
public:
|
||||
virtual ~VideoDriver() { }
|
||||
virtual bool StartUp() = 0;
|
||||
virtual bool ShutDown() = 0;
|
||||
virtual char* GetCurrentMode() const = 0;
|
||||
virtual bool SwitchMode(const char* mode) = 0;
|
||||
virtual bool Supports(const char* mode) const = 0;
|
||||
virtual char** GetModes(size_t* nummodes) const = 0;
|
||||
virtual ~VideoDevice() { }
|
||||
virtual struct dispmsg_crtc_mode GetCurrentMode(uint64_t connector) const = 0;
|
||||
virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode) = 0;
|
||||
virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const = 0;
|
||||
virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* nummodes) const = 0;
|
||||
virtual off_t FrameSize() const = 0;
|
||||
virtual ssize_t WriteAt(off_t off, const void* buf, size_t count) = 0;
|
||||
virtual ssize_t ReadAt(off_t off, void* buf, size_t count) = 0;
|
||||
virtual TextBuffer* CreateTextBuffer() = 0;
|
||||
virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count) = 0;
|
||||
virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count) = 0;
|
||||
virtual TextBuffer* CreateTextBuffer(uint64_t connector) = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
namespace Sortix {
|
||||
namespace Video {
|
||||
|
||||
void Init(Ref<TextBufferHandle> textbufhandle);
|
||||
bool RegisterDriver(const char* name, VideoDriver* driver);
|
||||
char* GetCurrentMode();
|
||||
char* GetDriverName(size_t index);
|
||||
size_t GetCurrentDriverIndex();
|
||||
size_t GetNumDrivers();
|
||||
size_t LookupDriverIndexOfMode(const char* mode);
|
||||
bool Supports(const char* mode);
|
||||
bool SwitchMode(const char* mode);
|
||||
char** GetModes(size_t* modesnum);
|
||||
off_t FrameSize();
|
||||
ssize_t WriteAt(off_t off, const void* buf, size_t count);
|
||||
ssize_t ReadAt(off_t off, void* buf, size_t count);
|
||||
bool RegisterDevice(const char* name, VideoDevice* device);
|
||||
|
||||
} // namespace Video
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
||||
|
|
|
@ -70,15 +70,14 @@
|
|||
|
||||
#include "alarm.h"
|
||||
#include "ata.h"
|
||||
#include "bga.h"
|
||||
#include "com.h"
|
||||
#include "dispmsg.h"
|
||||
#include "elf.h"
|
||||
#include "fs/full.h"
|
||||
#include "fs/kram.h"
|
||||
#include "fs/null.h"
|
||||
#include "fs/user.h"
|
||||
#include "fs/zero.h"
|
||||
#include "gpu/bga/bga.h"
|
||||
#include "identity.h"
|
||||
#include "initrd.h"
|
||||
#include "io.h"
|
||||
|
@ -432,9 +431,6 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
// Initialize the scheduler.
|
||||
Scheduler::Init();
|
||||
|
||||
// Initialize the Display Message framework.
|
||||
DisplayMessage::Init();
|
||||
|
||||
// Now that the base system has been loaded, it's time to go threaded. First
|
||||
// we create an object that represents this process.
|
||||
Ref<ProcessTable> ptable(new ProcessTable());
|
||||
|
|
715
kernel/video.cpp
715
kernel/video.cpp
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -26,459 +26,406 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sortix/kernel/copy.h>
|
||||
#include <sortix/kernel/kernel.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/string.h>
|
||||
#include <sortix/kernel/syscall.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
bool ReadParamString(const char* str, ...)
|
||||
{
|
||||
if ( strchr(str, '\n') ) { errno = EINVAL; }
|
||||
const char* keyname;
|
||||
va_list args;
|
||||
while ( *str )
|
||||
{
|
||||
size_t varlen = strcspn(str, ",");
|
||||
if ( !varlen ) { str++; continue; }
|
||||
size_t namelen = strcspn(str, "=");
|
||||
if ( !namelen ) { errno = EINVAL; goto cleanup; }
|
||||
if ( !str[namelen] ) { errno = EINVAL; goto cleanup; }
|
||||
if ( varlen < namelen ) { errno = EINVAL; goto cleanup; }
|
||||
size_t valuelen = varlen - 1 /*=*/ - namelen;
|
||||
char* name = String::Substring(str, 0, namelen);
|
||||
if ( !name ) { goto cleanup; }
|
||||
char* value = String::Substring(str, namelen+1, valuelen);
|
||||
if ( !value ) { delete[] name; goto cleanup; }
|
||||
va_start(args, str);
|
||||
while ( (keyname = va_arg(args, const char*)) )
|
||||
{
|
||||
if ( strcmp(keyname, "STOP") == 0 )
|
||||
break;
|
||||
char** nameptr = va_arg(args, char**);
|
||||
if ( strcmp(keyname, name) ) { continue; }
|
||||
*nameptr = value;
|
||||
break;
|
||||
}
|
||||
va_end(args);
|
||||
if ( !keyname ) { delete[] value; }
|
||||
delete[] name;
|
||||
str += varlen;
|
||||
str += strspn(str, ",");
|
||||
}
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
va_start(args, str);
|
||||
while ( (keyname = va_arg(args, const char*)) )
|
||||
{
|
||||
char** nameptr = va_arg(args, char**);
|
||||
delete[] *nameptr; *nameptr = NULL;
|
||||
}
|
||||
va_end(args);
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace Video {
|
||||
|
||||
const unsigned long DRIVER_GOT_MODES = (1UL<<0UL);
|
||||
const uint64_t ONE_AND_ONLY_DEVICE = 0;
|
||||
const uint64_t ONE_AND_ONLY_CONNECTOR = 0;
|
||||
|
||||
struct DriverEntry
|
||||
kthread_mutex_t video_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
struct DeviceEntry
|
||||
{
|
||||
char* name;
|
||||
VideoDriver* driver;
|
||||
unsigned long flags;
|
||||
size_t id;
|
||||
VideoDevice* device;
|
||||
};
|
||||
|
||||
size_t numdrivers;
|
||||
size_t driverslen;
|
||||
DriverEntry* drivers;
|
||||
size_t num_devices = 0;
|
||||
size_t devices_length = 0;
|
||||
DeviceEntry* devices = NULL;
|
||||
|
||||
size_t nummodes;
|
||||
size_t modeslen;
|
||||
char** modes;
|
||||
|
||||
char* currentmode;
|
||||
size_t currentdrvid;
|
||||
bool newdrivers;
|
||||
|
||||
kthread_mutex_t videolock;
|
||||
Ref<TextBufferHandle> textbufhandle;
|
||||
|
||||
void Init(Ref<TextBufferHandle> thetextbufhandle)
|
||||
bool RegisterDevice(const char* name, VideoDevice* device)
|
||||
{
|
||||
videolock = KTHREAD_MUTEX_INITIALIZER;
|
||||
textbufhandle = thetextbufhandle;
|
||||
numdrivers = driverslen = 0;
|
||||
drivers = NULL;
|
||||
nummodes = modeslen = 0;
|
||||
modes = NULL;
|
||||
currentdrvid = SIZE_MAX;
|
||||
newdrivers = false;
|
||||
currentmode = NULL;
|
||||
}
|
||||
|
||||
size_t GetCurrentDriverIndex()
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
return currentdrvid;
|
||||
}
|
||||
|
||||
size_t GetNumDrivers()
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
return numdrivers;
|
||||
}
|
||||
|
||||
char* GetDriverName(size_t index)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
if ( numdrivers <= index || !drivers[index].name )
|
||||
return String::Clone("none");
|
||||
return String::Clone(drivers[index].name);
|
||||
}
|
||||
|
||||
static DriverEntry* CurrentDriverEntry()
|
||||
{
|
||||
if ( currentdrvid == SIZE_MAX ) { return NULL; }
|
||||
return drivers + currentdrvid;
|
||||
}
|
||||
|
||||
bool RegisterDriver(const char* name, VideoDriver* driver)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
if ( numdrivers == driverslen )
|
||||
ScopedLock lock(&video_lock);
|
||||
if ( num_devices == devices_length )
|
||||
{
|
||||
size_t newdriverslen = driverslen ? 2 * driverslen : 8UL;
|
||||
DriverEntry* newdrivers = new DriverEntry[newdriverslen];
|
||||
if ( !newdrivers ) { return false; }
|
||||
memcpy(newdrivers, drivers, sizeof(*drivers) * numdrivers);
|
||||
delete[] drivers; drivers = newdrivers;
|
||||
driverslen = driverslen;
|
||||
size_t newdevices_length = devices_length ? 2 * devices_length : 8UL;
|
||||
DeviceEntry* newdevices = new DeviceEntry[newdevices_length];
|
||||
if ( !newdevices )
|
||||
return false;
|
||||
memcpy(newdevices, devices, sizeof(*devices) * num_devices);
|
||||
delete[] devices; devices = newdevices;
|
||||
devices_length = devices_length;
|
||||
}
|
||||
|
||||
char* drivername = String::Clone(name);
|
||||
if ( !drivername ) { return false; }
|
||||
if ( !drivername )
|
||||
return false;
|
||||
|
||||
size_t index = numdrivers++;
|
||||
drivers[index].name = drivername;
|
||||
drivers[index].driver = driver;
|
||||
drivers[index].flags = 0;
|
||||
drivers[index].id = index;
|
||||
newdrivers = true;
|
||||
size_t index = num_devices++;
|
||||
devices[index].name = drivername;
|
||||
devices[index].device = device;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExpandModesArray(size_t needed)
|
||||
__attribute__((unused))
|
||||
static bool TransmitString(struct dispmsg_string* dest, const char* str)
|
||||
{
|
||||
size_t modesneeded = nummodes + needed;
|
||||
if ( modesneeded <= modeslen ) { return true; }
|
||||
size_t newmodeslen = 2 * modeslen;
|
||||
if ( newmodeslen < modesneeded ) { newmodeslen = modesneeded; }
|
||||
char** newmodes = new char*[newmodeslen];
|
||||
if ( !newmodes ) { return false; }
|
||||
memcpy(newmodes, modes, sizeof(char*) * nummodes);
|
||||
delete[] modes; modes = newmodes;
|
||||
modeslen = newmodeslen;
|
||||
return true;
|
||||
size_t size = strlen(str) + 1;
|
||||
size_t dest_size = dest->byte_size;
|
||||
dest->byte_size = size;
|
||||
if ( dest_size < size )
|
||||
return errno = ERANGE, false;
|
||||
return CopyToUser(dest->str, str, size);
|
||||
}
|
||||
|
||||
static void UpdateModes()
|
||||
__attribute__((unused))
|
||||
static char* ReceiveString(struct dispmsg_string* src)
|
||||
{
|
||||
if ( !newdrivers ) { return; }
|
||||
bool allsuccess = true;
|
||||
for ( size_t i = 0; i < numdrivers; i++ )
|
||||
{
|
||||
bool success = false;
|
||||
if ( drivers[i].flags & DRIVER_GOT_MODES ) { continue; }
|
||||
const char* drivername = drivers[i].name;
|
||||
VideoDriver* driver = drivers[i].driver;
|
||||
size_t prevnummodes = nummodes;
|
||||
size_t drvnummodes = 0;
|
||||
char** drvmodes = driver->GetModes(&drvnummodes);
|
||||
if ( !drvmodes ) { goto cleanup_error; }
|
||||
if ( !ExpandModesArray(drvnummodes) ) { goto cleanup_drvmodes; }
|
||||
for ( size_t n = 0; n < drvnummodes; n++ )
|
||||
{
|
||||
char* modestr = String::Combine(4, "driver=", drivername,
|
||||
",", drvmodes[n]);
|
||||
if ( !modestr ) { goto cleanup_newmodes; }
|
||||
modes[nummodes++] = modestr;
|
||||
}
|
||||
success = true;
|
||||
drivers[i].flags |= DRIVER_GOT_MODES;
|
||||
cleanup_newmodes:
|
||||
for ( size_t i = prevnummodes; !success && i < nummodes; i++ )
|
||||
delete[] modes[i];
|
||||
if ( !success ) { nummodes = prevnummodes; }
|
||||
cleanup_drvmodes:
|
||||
for ( size_t n = 0; n < drvnummodes; n++ ) { delete[] drvmodes[n]; }
|
||||
delete[] drvmodes;
|
||||
cleanup_error:
|
||||
allsuccess &= success;
|
||||
}
|
||||
newdrivers = !allsuccess;
|
||||
}
|
||||
|
||||
static DriverEntry* GetDriverEntry(const char* drivername)
|
||||
{
|
||||
for ( size_t i = 0; i < numdrivers; i++ )
|
||||
{
|
||||
if ( !strcmp(drivername, drivers[i].name) )
|
||||
{
|
||||
return drivers + i;
|
||||
}
|
||||
}
|
||||
errno = ENODRV;
|
||||
if ( !src->byte_size )
|
||||
return errno = EINVAL, (char*) NULL;
|
||||
char* ret = new char[src->byte_size];
|
||||
if ( !ret )
|
||||
return NULL;
|
||||
if ( !CopyFromUser(ret, src->str, src->byte_size) )
|
||||
return NULL;
|
||||
if ( ret[src->byte_size-1] != '\0' )
|
||||
{
|
||||
delete[] ret;
|
||||
return errno = EINVAL, (char*) NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool StartUpDriver(VideoDriver* driver, const char* drivername)
|
||||
static int EnumerateDevices(void* ptr, size_t size)
|
||||
{
|
||||
if ( !driver->StartUp() )
|
||||
{
|
||||
int errnum = errno;
|
||||
Log::PrintF("Error: Video driver '%s' was unable to startup\n",
|
||||
drivername);
|
||||
errno = errnum;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
struct dispmsg_enumerate_devices msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
size_t requested_num_devices = msg.devices_length;
|
||||
msg.devices_length = num_devices;
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
if ( requested_num_devices < num_devices )
|
||||
return errno = ERANGE, -1;
|
||||
|
||||
for ( uint64_t i = 0; i < num_devices; i++ )
|
||||
if ( !CopyToUser(&msg.devices[i], &i, sizeof(i)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ShutDownDriver(VideoDriver* driver, const char* drivername)
|
||||
static int GetDriverCount(void* ptr, size_t size)
|
||||
{
|
||||
textbufhandle->Replace(NULL);
|
||||
if ( !driver->ShutDown() )
|
||||
{
|
||||
int errnum = errno;
|
||||
Log::PrintF("Warning: Video driver '%s' did not shutdown cleanly\n",
|
||||
drivername);
|
||||
errno = errnum;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
struct dispmsg_get_driver_count msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
msg.driver_count = 1;
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool DriverModeAction(VideoDriver* driver, const char* drivername,
|
||||
const char* mode, const char* action)
|
||||
static int GetDriverName(void* ptr, size_t size)
|
||||
{
|
||||
textbufhandle->Replace(NULL);
|
||||
if ( !driver->SwitchMode(mode) )
|
||||
{
|
||||
int errnum = errno;
|
||||
Log::PrintF("Error: Video driver '%s' could not %s mode '%s'\n",
|
||||
drivername, action, mode);
|
||||
errno = errnum;
|
||||
return false;
|
||||
}
|
||||
textbufhandle->Replace(driver->CreateTextBuffer());
|
||||
return true;
|
||||
struct dispmsg_get_driver_name msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
DeviceEntry* device_entry = &devices[msg.device];
|
||||
if ( !TransmitString(&msg.name, device_entry->name) )
|
||||
return -1;
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool SwitchDriverMode(VideoDriver* driver, const char* drivername,
|
||||
const char* mode)
|
||||
static int GetDriver(void* ptr, size_t size)
|
||||
{
|
||||
return DriverModeAction(driver, drivername, mode, "switch to");
|
||||
struct dispmsg_get_driver msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
msg.driver_index = 0;
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool RestoreDriverMode(VideoDriver* driver, const char* drivername,
|
||||
const char* mode)
|
||||
static int SetDriver(void* ptr, size_t size)
|
||||
{
|
||||
return DriverModeAction(driver, drivername, mode, "restore");
|
||||
struct dispmsg_set_driver msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
if ( msg.driver_index != 0 )
|
||||
return errno = EINVAL, -1;
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Attempts to use the specific driver and mode, if an error occurs, it will
|
||||
// attempt to reload the previous driver and mode. If that fails, we are kinda
|
||||
// screwed and the video adapter is left in an undefined state.
|
||||
static bool DoSwitchMode(DriverEntry* newdrvent, const char* newmode)
|
||||
static int SetCrtcMode(void* ptr, size_t size)
|
||||
{
|
||||
DriverEntry* prevdrvent = CurrentDriverEntry();
|
||||
VideoDriver* prevdriver = prevdrvent ? prevdrvent->driver : NULL;
|
||||
const char* prevdrivername = prevdrvent ? prevdrvent->name : NULL;
|
||||
struct dispmsg_set_crtc_mode msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
VideoDriver* newdriver = newdrvent->driver;
|
||||
const char* newdrivername = newdrvent->name;
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
char* newcurrentmode = String::Clone(newmode);
|
||||
if ( !newcurrentmode ) { return false; }
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
if ( prevdriver == newdriver )
|
||||
{
|
||||
if ( !SwitchDriverMode(newdriver, newdrivername, newmode) )
|
||||
{
|
||||
delete[] newcurrentmode;
|
||||
return false;
|
||||
}
|
||||
delete[] currentmode;
|
||||
currentmode = newcurrentmode;
|
||||
return true;
|
||||
}
|
||||
DeviceEntry* device_entry = &devices[msg.device];
|
||||
VideoDevice* device = device_entry->device;
|
||||
if ( !device->SwitchMode(msg.connector, msg.mode) )
|
||||
return -1;
|
||||
|
||||
int errnum = 0;
|
||||
// TODO: This could potentially fail.
|
||||
if ( msg.device == ONE_AND_ONLY_DEVICE &&
|
||||
msg.connector == ONE_AND_ONLY_CONNECTOR )
|
||||
textbufhandle->Replace(device->CreateTextBuffer(msg.connector));
|
||||
|
||||
if ( prevdriver ) { ShutDownDriver(prevdriver, prevdrivername); }
|
||||
// No need to respond.
|
||||
|
||||
char* prevmode = currentmode; currentmode = NULL;
|
||||
currentdrvid = SIZE_MAX;
|
||||
|
||||
if ( !StartUpDriver(newdriver, newdrivername) )
|
||||
{
|
||||
errnum = errno;
|
||||
goto restore_prev_driver;
|
||||
}
|
||||
|
||||
currentdrvid = newdrvent->id;
|
||||
|
||||
if ( !SwitchDriverMode(newdriver, newdrivername, newmode) )
|
||||
{
|
||||
errnum = errno;
|
||||
ShutDownDriver(newdriver, newdrivername);
|
||||
currentdrvid = SIZE_MAX;
|
||||
goto restore_prev_driver;
|
||||
}
|
||||
|
||||
currentmode = newcurrentmode;
|
||||
delete[] prevmode;
|
||||
|
||||
return true;
|
||||
|
||||
restore_prev_driver:
|
||||
delete[] newcurrentmode;
|
||||
if ( !prevdriver ) { goto error_out; }
|
||||
if ( !StartUpDriver(prevdriver, prevdrivername) ) { goto error_out; }
|
||||
|
||||
currentdrvid = prevdrvent->id;
|
||||
|
||||
if ( !RestoreDriverMode(prevdriver, prevdrivername, prevmode) )
|
||||
{
|
||||
ShutDownDriver(prevdriver, prevdrivername);
|
||||
currentdrvid = SIZE_MAX;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
Log::PrintF("Successfully restored video driver '%s' mode '%s'\n",
|
||||
prevdrivername, prevmode);
|
||||
|
||||
error_out:
|
||||
if ( currentdrvid == SIZE_MAX )
|
||||
Log::PrintF("Warning: Could not fall back upon a video driver\n");
|
||||
errno = errnum; // Return the original error, not the last one.
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* GetCurrentMode()
|
||||
static int GetCrtcMode(void* ptr, size_t size)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
UpdateModes();
|
||||
return String::Clone(currentmode ? currentmode : "driver=none");
|
||||
struct dispmsg_get_crtc_mode msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
DeviceEntry* device_entry = &devices[msg.device];
|
||||
VideoDevice* device = device_entry->device;
|
||||
|
||||
// TODO: There is no real way to detect failure here.
|
||||
errno = 0;
|
||||
struct dispmsg_crtc_mode mode = device->GetCurrentMode(msg.connector);
|
||||
if ( !(mode.control & DISPMSG_CONTROL_VALID) && errno != 0 )
|
||||
return -1;
|
||||
|
||||
msg.mode = mode;
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char** GetModes(size_t* modesnum)
|
||||
static int GetCrtcModes(void* ptr, size_t size)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
UpdateModes();
|
||||
char** result = new char*[nummodes];
|
||||
if ( !result ) { return NULL; }
|
||||
struct dispmsg_get_crtc_modes msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
DeviceEntry* device_entry = &devices[msg.device];
|
||||
VideoDevice* device = device_entry->device;
|
||||
|
||||
size_t nummodes;
|
||||
struct dispmsg_crtc_mode* modes = device->GetModes(msg.connector, &nummodes);
|
||||
if ( !modes )
|
||||
return -1;
|
||||
|
||||
size_t requested_modes = msg.modes_length;
|
||||
msg.modes_length = nummodes;
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
if ( requested_modes < nummodes )
|
||||
{
|
||||
delete[] modes;
|
||||
return errno = ERANGE, -1;
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < nummodes; i++ )
|
||||
{
|
||||
result[i] = String::Clone(modes[i]);
|
||||
if ( !result[i] )
|
||||
if ( !CopyToUser(&msg.modes[i], &modes[i], sizeof(modes[i])) )
|
||||
{
|
||||
for ( size_t j = 0; j < i; j++ )
|
||||
delete[] modes;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] modes;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetMemorySize(void* ptr, size_t size)
|
||||
{
|
||||
struct dispmsg_get_memory_size msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
DeviceEntry* device_entry = &devices[msg.device];
|
||||
VideoDevice* device = device_entry->device;
|
||||
|
||||
msg.memory_size = device->FrameSize();
|
||||
|
||||
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WriteMemory(void* ptr, size_t size)
|
||||
{
|
||||
struct dispmsg_write_memory msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
DeviceEntry* device_entry = &devices[msg.device];
|
||||
VideoDevice* device = device_entry->device;
|
||||
|
||||
ioctx_t ctx; SetupUserIOCtx(&ctx);
|
||||
if ( device->WriteAt(&ctx, msg.offset, msg.src, msg.size) < 0 )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ReadMemory(void* ptr, size_t size)
|
||||
{
|
||||
struct dispmsg_read_memory msg;
|
||||
if ( size != sizeof(msg) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
|
||||
return -1;
|
||||
|
||||
ScopedLock lock(&video_lock);
|
||||
|
||||
if ( num_devices <= msg.device )
|
||||
return errno = ENODEV, -1;
|
||||
|
||||
DeviceEntry* device_entry = &devices[msg.device];
|
||||
VideoDevice* device = device_entry->device;
|
||||
|
||||
ioctx_t ctx; SetupUserIOCtx(&ctx);
|
||||
if ( device->ReadAt(&ctx, msg.offset, msg.dst, msg.size) < 0 )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_dispmsg_issue(void* ptr, size_t size)
|
||||
{
|
||||
struct dispmsg_header hdr;
|
||||
if ( size < sizeof(hdr) )
|
||||
return errno = EINVAL, -1;
|
||||
if ( !CopyFromUser(&hdr, ptr, sizeof(hdr)) )
|
||||
return -1;
|
||||
switch ( hdr.msgid )
|
||||
{
|
||||
delete[] result[j];
|
||||
case DISPMSG_ENUMERATE_DEVICES: return EnumerateDevices(ptr, size);
|
||||
case DISPMSG_GET_DRIVER_COUNT: return GetDriverCount(ptr, size);
|
||||
case DISPMSG_GET_DRIVER_NAME: return GetDriverName(ptr, size);
|
||||
case DISPMSG_GET_DRIVER: return GetDriver(ptr, size);
|
||||
case DISPMSG_SET_DRIVER: return SetDriver(ptr, size);
|
||||
case DISPMSG_SET_CRTC_MODE: return SetCrtcMode(ptr, size);
|
||||
case DISPMSG_GET_CRTC_MODE: return GetCrtcMode(ptr, size);
|
||||
case DISPMSG_GET_CRTC_MODES: return GetCrtcModes(ptr, size);
|
||||
case DISPMSG_GET_MEMORY_SIZE: return GetMemorySize(ptr, size);
|
||||
case DISPMSG_WRITE_MEMORY: return WriteMemory(ptr, size);
|
||||
case DISPMSG_READ_MEMORY: return ReadMemory(ptr, size);
|
||||
default:
|
||||
return errno = ENOSYS, -1;
|
||||
}
|
||||
delete[] result;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
*modesnum = nummodes;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SwitchMode(const char* mode)
|
||||
void Init(Ref<TextBufferHandle> thetextbufhandle)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
UpdateModes();
|
||||
char* drivername = NULL;
|
||||
if ( !ReadParamString(mode, "driver", &drivername, NULL) ) { return false; }
|
||||
if ( !strcmp(drivername, "none") )
|
||||
{
|
||||
DriverEntry* driverentry = CurrentDriverEntry();
|
||||
if ( !driverentry )
|
||||
return true;
|
||||
ShutDownDriver(driverentry->driver, driverentry->name);
|
||||
currentdrvid = SIZE_MAX;
|
||||
return true;
|
||||
}
|
||||
DriverEntry* drvent = GetDriverEntry(drivername);
|
||||
delete[] drivername;
|
||||
if ( !drvent ) { return false; }
|
||||
return DoSwitchMode(drvent, mode);
|
||||
}
|
||||
|
||||
bool Supports(const char* mode)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
UpdateModes();
|
||||
char* drivername = NULL;
|
||||
if ( !ReadParamString(mode, "driver", &drivername, NULL) ) { return false; }
|
||||
DriverEntry* drvent = GetDriverEntry(drivername);
|
||||
delete[] drivername;
|
||||
if ( !drvent ) { return false; }
|
||||
return drvent->driver->Supports(mode);
|
||||
}
|
||||
|
||||
size_t LookupDriverIndexOfMode(const char* mode)
|
||||
{
|
||||
const char* needle = "driver=";
|
||||
size_t needlelen = strlen(needle);
|
||||
while ( *mode )
|
||||
{
|
||||
if ( !strncmp(mode, needle, needlelen) )
|
||||
{
|
||||
const char* name = mode + needlelen;
|
||||
size_t namelen = strcspn(name, ",");
|
||||
ScopedLock lock(&videolock);
|
||||
for ( size_t i = 0; i < numdrivers; i++ )
|
||||
if ( !strncmp(drivers[i].name, name, namelen) )
|
||||
return i;
|
||||
return SIZE_MAX;
|
||||
}
|
||||
mode += strcspn(mode, ",") + 1;
|
||||
}
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
off_t FrameSize()
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
DriverEntry* drvent = CurrentDriverEntry();
|
||||
if ( !drvent ) { errno = EINVAL; return -1; }
|
||||
return drvent->driver->FrameSize();
|
||||
}
|
||||
|
||||
ssize_t WriteAt(off_t off, const void* buf, size_t count)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
DriverEntry* drvent = CurrentDriverEntry();
|
||||
if ( !drvent ) { errno = EINVAL; return -1; }
|
||||
return drvent->driver->WriteAt(off, buf, count);
|
||||
}
|
||||
|
||||
ssize_t ReadAt(off_t off, void* buf, size_t count)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
DriverEntry* drvent = CurrentDriverEntry();
|
||||
if ( !drvent ) { errno = EINVAL; return -1; }
|
||||
return drvent->driver->ReadAt(off, buf, count);
|
||||
textbufhandle = thetextbufhandle;
|
||||
Syscall::Register(SYSCALL_DISPMSG_ISSUE, (void*) sys_dispmsg_issue);
|
||||
}
|
||||
|
||||
} // namespace Video
|
||||
|
||||
} // namespace Sortix
|
||||
|
|
Loading…
Reference in a new issue