/* * \brief Framebuffer driver front end * \author Norman Feske * \author Christian Helmuth * \author Sebastian Sumpf * \date 2007-09-11 */ /* * Copyright (C) 2007-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ /* Genode includes */ #include #include #include #include #include #include #include #include #include #include /* local includes */ #include "framebuffer.h" namespace Framebuffer { struct Session_component; struct Root; struct Main; using Genode::size_t; using Genode::min; using Genode::max; using Genode::Dataspace_capability; using Genode::Attached_rom_dataspace; using Genode::Attached_ram_dataspace; } class Framebuffer::Session_component : public Genode::Rpc_object { private: Genode::Env &_env; unsigned const _scr_width, _scr_height, _scr_depth; Timer::Connection _timer { _env }; /* dataspace of physical frame buffer */ Dataspace_capability _fb_cap; void *_fb_addr; /* dataspace uses a back buffer (if '_buffered' is true) */ Genode::Constructible _bb; 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_depth == 16) bypp = 2; if (!bypp) return; /* copy pixels from back buffer to physical frame buffer */ char *src = _bb->local_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); } bool _buffered() const { return _bb.constructed(); } public: /** * Constructor */ Session_component(Genode::Env &env, unsigned scr_width, unsigned scr_height, unsigned scr_depth, Dataspace_capability fb_cap, bool buffered) : _env(env), _scr_width(scr_width), _scr_height(scr_height), _scr_depth(scr_depth), _fb_cap(fb_cap) { if (!buffered) return; if (_scr_depth != 16) { Genode::warning("buffered mode not supported for depth ", _scr_depth); return; } size_t const bb_size = _scr_width*_scr_height*_scr_depth/8; try { _bb.construct(env.ram(), env.rm(), bb_size); } catch (...) { Genode::warning("could not allocate back buffer, disabled buffered output"); return; } _fb_addr = env.rm().attach(_fb_cap); Genode::log("using buffered output"); } /** * Destructor */ ~Session_component() { if (_buffered()) { _bb.destruct(); _env.rm().detach(_fb_addr); } } /*********************************** ** Framebuffer session interface ** ***********************************/ Dataspace_capability dataspace() override { return _buffered() ? Dataspace_capability(_bb->cap()) : Dataspace_capability(_fb_cap); } Mode mode() const override { return Mode(_scr_width, _scr_height, _scr_depth == 16 ? Mode::RGB565 : Mode::INVALID); } void mode_sigh(Genode::Signal_context_capability) override { } void sync_sigh(Genode::Signal_context_capability sigh) override { _timer.sigh(sigh); _timer.trigger_periodic(10*1000); } void refresh(int x, int y, int w, int h) override { if (_buffered()) _refresh_buffered(x, y, w, h); } }; /** * Shortcut for single-client root component */ typedef Genode::Root_component Root_component; class Framebuffer::Root : public Root_component { private: Genode::Env &_env; Attached_rom_dataspace const &_config; unsigned _session_arg(char const *attr_name, char const *args, char const *arg_name, unsigned default_value) { /* try to obtain value from config file */ unsigned result = _config.xml().attribute_value(attr_name, default_value); /* check session argument to override value from config file */ return Genode::Arg_string::find_arg(args, arg_name).ulong_value(result); } protected: Session_component *_create_session(char const *args) override { unsigned scr_width = _session_arg("width", args, "fb_width", 0); unsigned scr_height = _session_arg("height", args, "fb_height", 0); unsigned const scr_depth = _session_arg("depth", args, "fb_mode", 16); bool const buffered = _config.xml().attribute_value("buffered", false); if (Framebuffer::set_mode(scr_width, scr_height, scr_depth) != 0) { Genode::warning("Could not set vesa mode ", scr_width, "x", scr_height, "@", scr_depth); throw Genode::Service_denied(); } Genode::log("using video mode: ", scr_width, "x", scr_height, "@", scr_depth); return new (md_alloc()) Session_component(_env, scr_width, scr_height, scr_depth, Framebuffer::hw_framebuffer(), buffered); } public: Root(Genode::Env &env, Genode::Allocator &alloc, Attached_rom_dataspace const &config) : Root_component(&env.ep().rpc_ep(), &alloc), _env(env), _config(config) { } }; struct Framebuffer::Main { Genode::Env &env; Genode::Heap heap { env.ram(), env.rm() }; Attached_rom_dataspace config { env, "config" }; Root root { env, heap, config }; Main(Genode::Env &env) : env(env) { try { Framebuffer::init(env, heap); } catch (...) { Genode::error("H/W driver init failed"); throw; } env.parent().announce(env.ep().manage(root)); } }; void Component::construct(Genode::Env &env) { /* XXX execute constructors of global statics */ env.exec_static_constructors(); static Framebuffer::Main inst(env); }