genode/repos/libports/src/drivers/framebuffer/vesa/main.cc
Christian Prochaska 4d4d3cb9bb VESA driver: choose highest resolution video mode
With this patch, the VESA driver chooses the video mode with the highest
resolution from the list of available modes if no resolution is
explicitly configured.

Fixes #1263.
2014-10-10 13:02:31 +02:00

265 lines
6.9 KiB
C++

/*
* \brief Framebuffer driver front end
* \author Norman Feske
* \author Christian Helmuth
* \author Sebastian Sumpf
* \date 2007-09-11
*/
/*
* Copyright (C) 2007-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode */
#include <base/env.h>
#include <base/sleep.h>
#include <base/rpc_server.h>
#include <root/component.h>
#include <cap_session/connection.h>
#include <framebuffer_session/framebuffer_session.h>
#include <rom_session/connection.h>
#include <util/xml_node.h>
#include <dataspace/client.h>
#include <blit/blit.h>
#include <os/config.h>
/* Local */
#include "framebuffer.h"
using namespace Genode;
/***************
** Utilities **
***************/
/**
* Determine session argument value based on config file and session arguments
*
* \param attr_name attribute name of config node
* \param args session argument string
* \param arg_name argument name
* \param default default session argument value if value is neither
* specified in config node nor in session arguments
*/
unsigned long session_arg(const char *attr_name, const char *args,
const char *arg_name, unsigned long default_value)
{
unsigned long result = default_value;
/* try to obtain value from config file */
try { Genode::config()->xml_node().attribute(attr_name).value(&result); }
catch (...) { }
/* check session argument to override value from config file */
result = Arg_string::find_arg(args, arg_name).ulong_value(result);
return result;
}
bool config_attribute(const char *attr_name)
{
bool result = false;
try {
result = Genode::config()->xml_node().attribute(attr_name).has_value("yes"); }
catch (...) {}
return result;
}
/***********************************************
** Implementation of the framebuffer service **
***********************************************/
namespace Framebuffer {
class Session_component : public Genode::Rpc_object<Session>
{
private:
unsigned _scr_width, _scr_height, _scr_mode;
bool _buffered;
/* dataspace uses a back buffer (if '_buffered' is true) */
Genode::Ram_dataspace_capability _bb_ds;
void *_bb_addr;
/* dataspace of physical frame buffer */
Genode::Dataspace_capability _fb_ds;
void *_fb_addr;
Genode::Signal_context_capability _sync_sigh;
void _refresh_buffered(int x, int y, int w, int h)
{
/* clip specified coordinates against screen boundaries */
int x2 = min(x + w - 1, (int)_scr_width - 1),
y2 = min(y + h - 1, (int)_scr_height - 1);
int x1 = max(x, 0),
y1 = max(y, 0);
if (x1 > x2 || y1 > y2) return;
/* determine bytes per pixel */
int bypp = 0;
if (_scr_mode == 16) bypp = 2;
if (!bypp) return;
/* copy pixels from back buffer to physical frame buffer */
char *src = (char *)_bb_addr + bypp*(_scr_width*y1 + x1),
*dst = (char *)_fb_addr + bypp*(_scr_width*y1 + x1);
blit(src, bypp*_scr_width, dst, bypp*_scr_width,
bypp*(x2 - x1 + 1), y2 - y1 + 1);
}
public:
/**
* Constructor
*/
Session_component(unsigned scr_width, unsigned scr_height, unsigned scr_mode,
Genode::Dataspace_capability fb_ds, bool buffered)
:
_scr_width(scr_width), _scr_height(scr_height), _scr_mode(scr_mode),
_buffered(buffered), _fb_ds(fb_ds)
{
if (!_buffered) return;
if (scr_mode != 16) {
PWRN("buffered mode not supported for mode %d", (int)scr_mode);
_buffered = false;
}
size_t buf_size = scr_width*scr_height*scr_mode/8;
try { _bb_ds = Genode::env()->ram_session()->alloc(buf_size); }
catch (...) {
PWRN("could not allocate back buffer, disabled buffered output");
_buffered = false;
}
PDBG("use buf size %zd", buf_size);
if (_buffered && _bb_ds.valid()) {
_bb_addr = Genode::env()->rm_session()->attach(_bb_ds);
_fb_addr = Genode::env()->rm_session()->attach(_fb_ds);
}
if (_buffered)
PINF("using buffered output");
}
/**
* Destructor
*/
~Session_component()
{
if (!_buffered) return;
Genode::env()->rm_session()->detach(_bb_addr);
Genode::env()->ram_session()->free(_bb_ds);
Genode::env()->rm_session()->detach(_fb_addr);
}
/***********************************
** Framebuffer session interface **
***********************************/
Dataspace_capability dataspace() override {
return _buffered ? Dataspace_capability(_bb_ds)
: Dataspace_capability(_fb_ds); }
Mode mode() const override
{
return Mode(_scr_width, _scr_height,
_scr_mode == 16 ? Mode::RGB565 : Mode::INVALID);
}
void mode_sigh(Genode::Signal_context_capability) override { }
void sync_sigh(Genode::Signal_context_capability sigh) override
{
_sync_sigh = sigh;
}
void refresh(int x, int y, int w, int h) override
{
if (_buffered)
_refresh_buffered(x, y, w, h);
if (_sync_sigh.valid())
Signal_transmitter(_sync_sigh).submit();
}
};
/**
* Shortcut for single-client root component
*/
typedef Genode::Root_component<Session_component, Genode::Single_client> Root_component;
class Root : public Root_component
{
protected:
Session_component *_create_session(const char *args) override
{
unsigned long scr_width = session_arg("width", args, "fb_width", 0),
scr_height = session_arg("height", args, "fb_height", 0),
scr_mode = session_arg("depth", args, "fb_mode", 16);
bool buffered = config_attribute("buffered");
if (Framebuffer_drv::set_mode(scr_width, scr_height, scr_mode) != 0) {
PWRN("Could not set vesa mode %lux%lu@%lu", scr_width, scr_height,
scr_mode);
throw Root::Invalid_args();
}
printf("Using video mode: %lu x %lu x %lu\n", scr_width, scr_height, scr_mode);
return new (md_alloc()) Session_component(scr_width, scr_height, scr_mode,
Framebuffer_drv::hw_framebuffer(),
buffered);
}
public:
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc)
: Root_component(session_ep, md_alloc) { }
};
}
int main(int argc, char **argv)
{
/* We need to create capabilities for sessions. Therefore, we request the
* CAP service. */
static Cap_connection cap;
/* initialize server entry point */
enum { STACK_SIZE = 8*1024 };
static Rpc_entrypoint ep(&cap, STACK_SIZE, "vesa_ep");
/* init driver back end */
if (Framebuffer_drv::init()) {
PERR("H/W driver init failed");
return 3;
}
/* entry point serving framebuffer root interface */
static Framebuffer::Root fb_root(&ep, env()->heap());
/* tell parent about the service */
env()->parent()->announce(ep.manage(&fb_root));
/* main's done - go to sleep */
sleep_forever();
return 0;
}