mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Created framework for video drivers.
This supports dynamic loading and unloading of graphics drivers, mode switching and detection and flexible kernel access to the framebuffer.
This commit is contained in:
parent
d27a2bdcbf
commit
1ce55af846
4 changed files with 506 additions and 0 deletions
|
@ -117,6 +117,7 @@ vgatextbuffer.o \
|
|||
descriptors.o \
|
||||
device.o \
|
||||
refcount.o \
|
||||
video.o \
|
||||
vga.o \
|
||||
kernelinfo.o \
|
||||
elf.o \
|
||||
|
|
68
sortix/include/sortix/kernel/video.h
Normal file
68
sortix/include/sortix/kernel/video.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*******************************************************************************
|
||||
|
||||
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/>.
|
||||
|
||||
video.h
|
||||
Framework for Sortix video drivers.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_VIDEO_H
|
||||
#define SORTIX_VIDEO_H
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class TextBuffer;
|
||||
class TextBufferHandle;
|
||||
|
||||
bool ReadParamString(const char* str, ...);
|
||||
|
||||
class VideoDriver
|
||||
{
|
||||
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 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;
|
||||
|
||||
};
|
||||
|
||||
namespace Video {
|
||||
|
||||
void Init(TextBufferHandle* textbufhandle);
|
||||
bool RegisterDriver(const char* name, VideoDriver* driver);
|
||||
char* GetCurrentMode();
|
||||
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);
|
||||
|
||||
} // namespace Video
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
|
@ -33,6 +33,7 @@
|
|||
#include <libmaxsi/format.h>
|
||||
#include <sortix/kernel/log.h>
|
||||
#include <sortix/kernel/panic.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
#include "kernelinfo.h"
|
||||
#include "x86-family/gdt.h"
|
||||
#include "time.h"
|
||||
|
@ -216,6 +217,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
// Set up the initial ram disk.
|
||||
InitRD::Init(initrd, initrdsize);
|
||||
|
||||
// Initialize the Video Driver framework.
|
||||
Video::Init(&textbufhandle);
|
||||
|
||||
// Search for PCI devices and load their drivers.
|
||||
PCI::Init();
|
||||
|
||||
|
|
433
sortix/video.cpp
Normal file
433
sortix/video.cpp
Normal file
|
@ -0,0 +1,433 @@
|
|||
/*******************************************************************************
|
||||
|
||||
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/>.
|
||||
|
||||
video.cpp
|
||||
Framework for Sortix video drivers.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
#include <stdarg.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include <libmaxsi/string.h>
|
||||
|
||||
using namespace Maxsi;
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
bool ReadParamString(const char* str, ...)
|
||||
{
|
||||
if ( String::Seek(str, '\n') ) { Error::Set(EINVAL); }
|
||||
const char* keyname;
|
||||
va_list args;
|
||||
while ( *str )
|
||||
{
|
||||
size_t varlen = String::Reject(str, ",");
|
||||
if ( !varlen ) { str++; continue; }
|
||||
size_t namelen = String::Reject(str, "=");
|
||||
if ( !namelen ) { Error::Set(EINVAL); goto cleanup; }
|
||||
if ( !str[namelen] ) { Error::Set(EINVAL); goto cleanup; }
|
||||
if ( varlen < namelen ) { Error::Set(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*)) )
|
||||
{
|
||||
char** nameptr = va_arg(args, char**);
|
||||
if ( String::Compare(keyname, name) ) { continue; }
|
||||
*nameptr = value;
|
||||
break;
|
||||
}
|
||||
va_end(args);
|
||||
if ( !keyname ) { delete[] value; }
|
||||
delete[] name;
|
||||
str += varlen;
|
||||
str += String::Accept(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);
|
||||
|
||||
struct DriverEntry
|
||||
{
|
||||
char* name;
|
||||
VideoDriver* driver;
|
||||
unsigned long flags;
|
||||
size_t id;
|
||||
};
|
||||
|
||||
size_t numdrivers;
|
||||
size_t driverslen;
|
||||
DriverEntry* drivers;
|
||||
|
||||
size_t nummodes;
|
||||
size_t modeslen;
|
||||
char** modes;
|
||||
|
||||
char* currentmode;
|
||||
size_t currentdrvid;
|
||||
bool newdrivers;
|
||||
|
||||
kthread_mutex_t videolock;
|
||||
TextBufferHandle* textbufhandle;
|
||||
|
||||
void Init(TextBufferHandle* thetextbufhandle)
|
||||
{
|
||||
videolock = KTHREAD_MUTEX_INITIALIZER;
|
||||
textbufhandle = thetextbufhandle;
|
||||
numdrivers = driverslen = 0;
|
||||
drivers = NULL;
|
||||
nummodes = modeslen = 0;
|
||||
modes = NULL;
|
||||
currentdrvid = SIZE_MAX;
|
||||
newdrivers = false;
|
||||
currentmode = NULL;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
size_t newdriverslen = driverslen ? 2 * driverslen : 8UL;
|
||||
DriverEntry* newdrivers = new DriverEntry[newdriverslen];
|
||||
if ( !newdrivers ) { return false; }
|
||||
Memory::Copy(newdrivers, drivers, sizeof(*drivers) * numdrivers);
|
||||
delete[] drivers; drivers = newdrivers;
|
||||
driverslen = driverslen;
|
||||
}
|
||||
|
||||
char* drivername = String::Clone(name);
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExpandModesArray(size_t needed)
|
||||
{
|
||||
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; }
|
||||
Memory::Copy(newmodes, modes, sizeof(char*) * nummodes);
|
||||
delete[] modes; modes = newmodes;
|
||||
modeslen = newmodeslen;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void UpdateModes()
|
||||
{
|
||||
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 ( !String::Compare(drivername, drivers[i].name) )
|
||||
{
|
||||
return drivers + i;
|
||||
}
|
||||
}
|
||||
Error::Set(ENODRV);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool StartUpDriver(VideoDriver* driver, const char* drivername)
|
||||
{
|
||||
if ( !driver->StartUp() )
|
||||
{
|
||||
int errnum = Error::Last();
|
||||
Log::PrintF("Error: Video driver '%s' was unable to startup\n",
|
||||
drivername);
|
||||
Error::Set(errnum);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ShutDownDriver(VideoDriver* driver, const char* drivername)
|
||||
{
|
||||
textbufhandle->Replace(NULL);
|
||||
if ( !driver->ShutDown() )
|
||||
{
|
||||
int errnum = Error::Last();
|
||||
Log::PrintF("Warning: Video driver '%s' did not shutdown cleanly\n",
|
||||
drivername);
|
||||
Error::Set(errnum);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DriverModeAction(VideoDriver* driver, const char* drivername,
|
||||
const char* mode, const char* action)
|
||||
{
|
||||
textbufhandle->Replace(NULL);
|
||||
if ( !driver->SwitchMode(mode) )
|
||||
{
|
||||
int errnum = Error::Last();
|
||||
Log::PrintF("Error: Video driver '%s' could not %s mode '%s'\n",
|
||||
drivername, action, mode);
|
||||
Error::Set(errnum);
|
||||
return false;
|
||||
}
|
||||
textbufhandle->Replace(driver->CreateTextBuffer());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SwitchDriverMode(VideoDriver* driver, const char* drivername,
|
||||
const char* mode)
|
||||
{
|
||||
return DriverModeAction(driver, drivername, mode, "switch to");
|
||||
}
|
||||
|
||||
|
||||
static bool RestoreDriverMode(VideoDriver* driver, const char* drivername,
|
||||
const char* mode)
|
||||
{
|
||||
return DriverModeAction(driver, drivername, mode, "restore");
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
DriverEntry* prevdrvent = CurrentDriverEntry();
|
||||
VideoDriver* prevdriver = prevdrvent ? prevdrvent->driver : NULL;
|
||||
const char* prevdrivername = prevdrvent ? prevdrvent->name : NULL;
|
||||
|
||||
VideoDriver* newdriver = newdrvent->driver;
|
||||
const char* newdrivername = newdrvent->name;
|
||||
|
||||
char* newcurrentmode = String::Clone(newmode);
|
||||
if ( !newcurrentmode ) { return false; }
|
||||
|
||||
if ( prevdriver == newdriver )
|
||||
{
|
||||
if ( !SwitchDriverMode(newdriver, newdrivername, newmode) )
|
||||
{
|
||||
delete[] newcurrentmode;
|
||||
return false;
|
||||
}
|
||||
delete[] currentmode;
|
||||
currentmode = newcurrentmode;
|
||||
return true;
|
||||
}
|
||||
|
||||
int errnum = 0;
|
||||
|
||||
if ( prevdriver ) { ShutDownDriver(prevdriver, prevdrivername); }
|
||||
|
||||
char* prevmode = currentmode; currentmode = NULL;
|
||||
currentdrvid = SIZE_MAX;
|
||||
|
||||
if ( !StartUpDriver(newdriver, newdrivername) )
|
||||
{
|
||||
errnum = Error::Last();
|
||||
goto restore_prev_driver;
|
||||
}
|
||||
|
||||
currentdrvid = newdrvent->id;
|
||||
|
||||
if ( !SwitchDriverMode(newdriver, newdrivername, newmode) )
|
||||
{
|
||||
errnum = Error::Last();
|
||||
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");
|
||||
Error::Set(errnum); // Return the original error, not the last one.
|
||||
return false;
|
||||
}
|
||||
|
||||
char* GetCurrentMode()
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
UpdateModes();
|
||||
return String::Clone(currentmode ? currentmode : "driver=none");
|
||||
}
|
||||
|
||||
char** GetModes(size_t* modesnum)
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
UpdateModes();
|
||||
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 j = 0; j < i; j++ )
|
||||
{
|
||||
delete[] result[j];
|
||||
}
|
||||
delete[] result;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
*modesnum = nummodes;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SwitchMode(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 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);
|
||||
}
|
||||
|
||||
off_t FrameSize()
|
||||
{
|
||||
ScopedLock lock(&videolock);
|
||||
DriverEntry* drvent = CurrentDriverEntry();
|
||||
if ( !drvent ) { Error::Set(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 ) { Error::Set(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 ) { Error::Set(EINVAL); return -1; }
|
||||
return drvent->driver->WriteAt(off, buf, count);
|
||||
}
|
||||
|
||||
} // namespace Video
|
||||
|
||||
} // namespace Sortix
|
Loading…
Reference in a new issue