genode/gems/src/server/d3m/input_service.h

270 lines
6.4 KiB
C++

/*
* \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 <os/attached_ram_dataspace.h>
#include <root/component.h>
#include <util/list.h>
#include <input_session/client.h>
#include <input/event.h>
namespace Input {
class Source : public Genode::List<Source>::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<Session>
(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<Source> _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<Session>
{
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<Event>(),
MAX_EVENTS);
}
};
/**
* Shortcut for single-client root component
*/
typedef Genode::Root_component<Session_component, Genode::Single_client> 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_ */