/* * \brief D3m input service * \author Norman Feske * \author Christian Helmuth * \date 2012-01-25 * * D3m supports merging the input events of multiple devices into one * stream of events. Each driver corresponds to an event 'Source'. When the * driver announces the "Input" session interface, the corresponding * 'Source' is added to the 'Source_registry'. The d3m input serves queries * all sources registered at the source registry for input and merges the * streams of events. */ /* * Copyright (C) 2012-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. */ #ifndef _INPUT_SERVICE_H_ #define _INPUT_SERVICE_H_ /* Genode includes */ #include #include #include #include #include namespace Input { class Source : public Genode::List::Element { public: class Source_unavailable { }; private: Genode::Root_capability _root; Input::Session_capability _session; Input::Session_client _client; Event *_ev_buf; Genode::size_t _ev_buf_max; /* in events */ /** * Open input session via the specified root capability */ static Session_capability _request_session(Genode::Root_capability root) { const char *args = "ram_quota=8K"; try { using namespace Genode; return static_cap_cast (Root_client(root).session(args, Genode::Affinity())); } catch (...) { throw Source_unavailable(); } } public: /** * Constructor * * At construction time, the '_client' gets initialized using the * default-initialized (invalid) '_session' capability. The * 'Source::session' function is not usaable before * re-initializing the object via the 'connect' function. */ Source() : _client(_session), _ev_buf(0), _ev_buf_max(0) { } /** * Called when the driver announces the "Input" service */ void connect(Genode::Root_capability root) { _root = root; _session = _request_session(_root); _client = Session_client(_session); /* locally map input-event buffer */ Genode::Dataspace_capability ds_cap = _client.dataspace(); _ev_buf = Genode::env()->rm_session()->attach(ds_cap); _ev_buf_max = Genode::Dataspace_client(ds_cap).size() / sizeof(Event); } /** * Return true is 'session()' is ready to use */ bool connected() const { return _session.valid(); } /** * Return true if input is pending */ bool input_pending() const { return connected() && _client.is_pending(); } /** * Return event buffer */ Event const *ev_buf() const { return _ev_buf; } /** * Flush input events */ Genode::size_t flush() { return _client.flush(); } }; struct Source_registry { private: Genode::Lock _lock; Genode::List _sources; public: /** * Register a new source of input events * * This function is called once for each driver, when the driver * announced its "Input" service. By adding the new source, the * driver's input events become visible to the d3m input session. */ void add_source(Source *entry) { Genode::Lock::Guard guard(_lock); _sources.insert(entry); } bool any_source_has_pending_input() { Genode::Lock::Guard guard(_lock); for (Source *e = _sources.first(); e; e = e->next()) if (e->connected() && e->input_pending()) return true; return false; } /** * Flush all input events from all available sources * * This function merges the input-event streams of all sources into * one. * * \param dst destination event buffer * \param dst_max capacility of event buffer, in number of events * * \return total number of available input events */ Genode::size_t flush_sources(Event *dst, Genode::size_t dst_max) const { Genode::size_t dst_count = 0; for (Source *e = _sources.first(); e; e = e->next()) { if (!e->connected() || !e->input_pending()) continue; /* * Copy input events from driver to client buffer */ Event const *src = e->ev_buf(); Genode::size_t const src_max = e->flush(); for (Genode::size_t i = 0; i < src_max; i++, dst_count++) { if (dst_count == dst_max) { PERR("client input-buffer overflow"); return dst_count; } dst[dst_count] = src[i]; } } return dst_count; } }; /***************************** ** Input service front end ** *****************************/ class Session_component : public Genode::Rpc_object { private: Source_registry &_source_registry; /* * Input event buffer shared with the client */ enum { MAX_EVENTS = 1000 }; Genode::Attached_ram_dataspace _ev_ds; public: Session_component(Source_registry &source_registry) : _source_registry(source_registry), _ev_ds(Genode::env()->ram_session(), MAX_EVENTS*sizeof(Event)) { } /***************************** ** Input-session interface ** *****************************/ Genode::Dataspace_capability dataspace() { return _ev_ds.cap(); } bool is_pending() const { return _source_registry.any_source_has_pending_input(); } int flush() { return _source_registry.flush_sources(_ev_ds.local_addr(), MAX_EVENTS); } }; /** * Shortcut for single-client root component */ typedef Genode::Root_component Root_component; class Root : public Root_component { private: Source_registry &_source_registry; protected: Session_component *_create_session(const char *args) { try { return new (md_alloc()) Session_component(_source_registry); } catch (Genode::Allocator::Out_of_memory()) { PERR("Out of memory"); throw Genode::Root::Quota_exceeded(); } catch (...) { PERR("Exception in construction of NIC slave"); throw; } } public: Root(Genode::Rpc_entrypoint *session_ep, Genode::Allocator *md_alloc, Source_registry &source_registry) : Root_component(session_ep, md_alloc), _source_registry(source_registry) { } }; } #endif /* _INPUT_SERVICE_H_ */