diff --git a/repos/os/include/os/pin_driver.h b/repos/os/include/os/pin_driver.h new file mode 100644 index 0000000000..18f470f91c --- /dev/null +++ b/repos/os/include/os/pin_driver.h @@ -0,0 +1,357 @@ +/* + * \brief Utilities and interfaces for implementing pin drivers + * \author Norman Feske + * \date 2021-04-20 + */ + +/* + * Copyright (C) 2021 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. + */ + +#ifndef _INCLUDE__OS__PIN_DRIVER_H_ +#define _INCLUDE__OS__PIN_DRIVER_H_ + +#include +#include +#include +#include +#include + +namespace Pin { + + using namespace Genode; + + enum class Direction { IN, OUT }; + + template struct Driver; + template struct Assignment; + + template class Root; + + template class Irq_session_component; + template class Irq_root; + + template + struct Irq_root : Pin::Root, Direction::IN> + { + using Pin::Root, Direction::IN>::Root; + }; +} + + +template +struct Pin::Driver : Interface +{ + /** + * Request pin state + */ + virtual bool pin_state(ID) const = 0; + + /** + * Set pin state + */ + virtual void pin_state(ID, bool enabled) = 0; + + /** + * Return pin ID assigned to the specified session label + * + * \throw Service_denied + */ + virtual ID assigned_pin(Session_label, Direction) const = 0; + + /** + * Inform driver that the specified pin is in use + * + * The driver may use this information to maintain a reference counter + * per pin. The specified direction allows the driver to select one of + * multiple pin declarations used for time-multiplexing a pin between input + * and output. + */ + virtual void acquire_pin(ID, Direction) { }; + + /** + * Inform driver that the specified pin is not longer in use + * + * This is the counter part of 'acquire_pin'. + * + * When releasing an output pin, the driver may respond by resetting the + * pin to its default state. + */ + virtual void release_pin(ID, Direction) = 0; + + /** + * Enable/disable interrupt for specified pin + */ + virtual void irq_enabled(ID, bool) = 0; + + /** + * Return true if IRQ is pending for specified pin + */ + virtual bool irq_pending(ID) const = 0; + + /** + * Acknowledge interrupt of specified pin + */ + virtual void ack_irq(ID) = 0; + + struct Irq_subscriber : Interface + { + ID id; + Signal_context_capability sigh; + bool outstanding_ack = false; + + void submit_irq() + { + Signal_transmitter(sigh).submit(); + outstanding_ack = true; + } + + Irq_subscriber(ID id, Signal_context_capability sigh) + : id(id), sigh(sigh) { } + }; + + Registry> irq_subscribers { }; + + void deliver_pin_irqs() + { + irq_subscribers.for_each([&] (Irq_subscriber &subscriber) { + + if (subscriber.outstanding_ack || !irq_pending(subscriber.id)) + return; + + /* mask IRQ until acked */ + irq_enabled(subscriber.id, false); + + subscriber.submit_irq(); + }); + } +}; + + +template +struct Pin::Assignment : Noncopyable +{ + Driver &driver; + + struct Target + { + ID id; + + Pin::Direction direction; + + Target(ID id, Pin::Direction dir) : id(id), direction(dir) { } + + bool operator != (Target const &other) const + { + return (id != other.id) || (direction != other.direction); + } + }; + + Constructible target { }; + + void _release() + { + if (target.constructed()) { + driver.release_pin(target->id, target->direction); + target.destruct(); + } + } + + Assignment(Driver &driver) : driver(driver) { } + + ~Assignment() { _release(); } + + enum class Update { UNCHANGED, CHANGED }; + + /** + * Trigger an update of the pin assignment + * + * This method called in response to dynamic configuration changes. + * + * \return CHANGED if assignment to physical pin was modified + */ + Update update(Session_label const &label, Pin::Direction direction) + { + bool changed = false; + try { + /* + * \throw Service_denied + */ + Target const new_target { driver.assigned_pin(label, direction), direction }; + + /* assignment changed from one pin to another */ + if (target.constructed() && (*target != new_target)) { + _release(); + changed = true; + } + + if (!target.constructed()) { + driver.acquire_pin(new_target.id, new_target.direction); + target.construct(new_target.id, new_target.direction); + changed = true; + } + } + catch (Service_denied) { _release(); } + + return changed ? Update::CHANGED : Update::UNCHANGED; + } +}; + + +/** + * Common root component for 'Pin_state' and 'Pin_control' services + * + * \param SC session-component type + */ +template +class Pin::Root : Genode::Root_component +{ + private: + + using Pin_id = typename SC::Pin_id; + using Driver = Pin::Driver; + using Session = Registered; + + Entrypoint &_ep; + + Driver &_driver; + + Registry _sessions { }; + + SC *_create_session(char const *args) override + { + return new (Root_component::md_alloc()) + Session(_sessions, _ep, + session_resources_from_args(args), + session_label_from_args(args), + session_diag_from_args(args), + _driver); + } + + public: + + Root(Env &env, Allocator &alloc, Driver &driver) + : + Genode::Root_component(env.ep(), alloc), + _ep(env.ep()), _driver(driver) + { + env.parent().announce(_ep.manage(*this)); + } + + ~Root() { _ep.dissolve(*this); } + + void update_assignments() + { + _sessions.for_each([&] (Session &session) { + session.update_assignment(); }); + } +}; + + +template +class Pin::Irq_session_component : public Session_object +{ + public: + + using Pin_id = ID; /* expected by 'Pin::Root' */ + + private: + + using Irq_subscriber = typename Driver::Irq_subscriber; + using Assignment = Pin::Assignment; + + Assignment _assignment; + + Signal_context_capability _sigh { }; + + Constructible> _subscriber { }; + + void _ack_dangling_irq() + { + if (_subscriber.constructed() && _subscriber->outstanding_ack) + ack_irq(); + } + + public: + + Irq_session_component(Entrypoint &ep, Resources const &resources, + Label const &label, Diag &diag, Driver &driver) + : + Session_object(ep, resources, label, diag), + _assignment(driver) + { + update_assignment(); + } + + ~Irq_session_component() { _ack_dangling_irq(); } + + void update_assignment() + { + if (_assignment.target.constructed()) + _assignment.driver.irq_enabled(_assignment.target->id, false); + + typename Assignment::Update const update_result = + _assignment.update(label(), Pin::Direction::IN); + + if (update_result == Assignment::Update::CHANGED) { + + _ack_dangling_irq(); + + if (_assignment.target.constructed()) + _subscriber.construct(_assignment.driver.irq_subscribers, + _assignment.target->id, _sigh); + else + _subscriber.destruct(); + } + + bool const charged = _assignment.target.constructed() + && _subscriber.constructed() + && !_subscriber->outstanding_ack; + if (charged) + _assignment.driver.irq_enabled(_assignment.target->id, true); + } + + /** + * Irq_session interface + */ + void ack_irq() override + { + if (_assignment.target.constructed()) { + _assignment.driver.ack_irq(_assignment.target->id); + _assignment.driver.irq_enabled(_assignment.target->id, true); + } + + if (_subscriber.constructed()) + _subscriber->outstanding_ack = false; + } + + /** + * Irq_session interface + */ + void sigh(Signal_context_capability sigh) override + { + _sigh = sigh; + + if (_subscriber.constructed()) + _subscriber->sigh = sigh; + + update_assignment(); + + bool initial_irq = _subscriber.constructed() + && _assignment.target.constructed() + && _assignment.driver.irq_pending(_assignment.target->id); + + if (initial_irq) + _subscriber->submit_irq(); + } + + /** + * Irq_session interface + */ + Info info() override { return Info { }; } +}; + +#endif /* _INCLUDE__OS__PIN_DRIVER_H_ */ diff --git a/repos/os/include/pin_control_session/component.h b/repos/os/include/pin_control_session/component.h new file mode 100644 index 0000000000..d82e90471c --- /dev/null +++ b/repos/os/include/pin_control_session/component.h @@ -0,0 +1,70 @@ +/* + * \brief Pin-control service + * \author Norman Feske + * \date 2021-04-20 + */ + +/* + * Copyright (C) 2021 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. + */ + +#ifndef _INCLUDE__PIN_CONTROL_SESSION__COMPONENT_H_ +#define _INCLUDE__PIN_CONTROL_SESSION__COMPONENT_H_ + +#include +#include +#include + +namespace Pin_control { + + template class Session_component; + + template + struct Root : Pin::Root, Pin::Direction::OUT> + { + using Pin::Root, Pin::Direction::OUT>::Root; + }; +} + + +template +class Pin_control::Session_component : public Session_object +{ + private: + + Pin::Assignment _assignment; + + using Session_object::label; + + public: + + using Pin_id = ID; + + Session_component(Entrypoint &ep, Resources const &resources, + Label const &label, Diag &diag, Pin::Driver &driver) + : + Session_object(ep, resources, label, diag), + _assignment(driver) + { + update_assignment(); + } + + /** + * Pin_control::Session interface + */ + void state(bool enabled) override + { + if (_assignment.target.constructed()) + _assignment.driver.pin_state(_assignment.target->id, enabled); + } + + void update_assignment() + { + _assignment.update(label(), Pin::Direction::OUT); + } +}; + +#endif /* _INCLUDE__PIN_CONTROL_SESSION__COMPONENT_H_ */ diff --git a/repos/os/include/pin_control_session/connection.h b/repos/os/include/pin_control_session/connection.h new file mode 100644 index 0000000000..3b2bf1782b --- /dev/null +++ b/repos/os/include/pin_control_session/connection.h @@ -0,0 +1,40 @@ +/* + * \brief Connection to pin-control service + * \author Norman Feske + * \date 2021-04-20 + */ + +/* + * Copyright (C) 2021 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. + */ + +#ifndef _INCLUDE__PIN_CONTROL_SESSION__CONNECTION_H_ +#define _INCLUDE__PIN_CONTROL_SESSION__CONNECTION_H_ + +#include +#include +#include + +namespace Pin_control { struct Connection; } + +struct Pin_control::Connection : private Genode::Connection, + private Rpc_client +{ + enum { RAM_QUOTA = 8*1024UL }; + + Connection(Env &env, char const *label = "") + : + Genode::Connection(env, + session(env.parent(), + "ram_quota=%u, cap_quota=%u, label=\"%s\"", + RAM_QUOTA, CAP_QUOTA, label)), + Rpc_client(cap()) + { } + + void state(bool enabled) override { call(enabled); } +}; + +#endif /* _INCLUDE__PIN_CONTROL_SESSION__CONNECTION_H_ */ diff --git a/repos/os/include/pin_control_session/pin_control_session.h b/repos/os/include/pin_control_session/pin_control_session.h new file mode 100644 index 0000000000..909c55bf22 --- /dev/null +++ b/repos/os/include/pin_control_session/pin_control_session.h @@ -0,0 +1,47 @@ +/* + * \brief Session interface to control a GPIO pin + * \author Norman Feske + * \date 2021-04-20 + */ + +/* + * Copyright (C) 2021 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. + */ + +#ifndef _INCLUDE__PIN_CONTROL_SESSION__PIN_CONTROL_SESSION_H_ +#define _INCLUDE__PIN_CONTROL_SESSION__PIN_CONTROL_SESSION_H_ + +#include +#include + +namespace Pin_control { + + using namespace Genode; + + struct Session; +} + + +struct Pin_control::Session : Genode::Session +{ + /** + * \noapi + */ + static const char *service_name() { return "Pin_control"; } + + /* + * A pin-control session consumes a dataspace capability for the server's + * session-object allocation and its session capability. + */ + enum { CAP_QUOTA = 2 }; + + virtual void state(bool) = 0; + + GENODE_RPC(Rpc_state, void, state, bool); + GENODE_RPC_INTERFACE(Rpc_state); +}; + +#endif /* _INCLUDE__PIN_CONTROL_SESSION__PIN_CONTROL_SESSION_H_ */ diff --git a/repos/os/include/pin_state_session/component.h b/repos/os/include/pin_state_session/component.h new file mode 100644 index 0000000000..de655640bd --- /dev/null +++ b/repos/os/include/pin_state_session/component.h @@ -0,0 +1,70 @@ +/* + * \brief Pin-state service + * \author Norman Feske + * \date 2021-04-20 + */ + +/* + * Copyright (C) 2021 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. + */ + +#ifndef _INCLUDE__PIN_STATE_SESSION__COMPONENT_H_ +#define _INCLUDE__PIN_STATE_SESSION__COMPONENT_H_ + +#include +#include +#include + +namespace Pin_state { + + template class Session_component; + + template + struct Root : Pin::Root, Pin::Direction::IN> + { + using Pin::Root, Pin::Direction::IN>::Root; + }; +} + + +template +class Pin_state::Session_component : public Session_object +{ + private: + + Pin::Assignment _assignment; + + public: + + using Pin_id = ID; + + Session_component(Entrypoint &ep, Resources const &resources, + Label const &label, Diag &diag, Pin::Driver &driver) + : + Session_object(ep, resources, label, diag), + _assignment(driver) + { + update_assignment(); + } + + /** + * Pin_state::Session interface + */ + bool state() const override + { + if (!_assignment.target.constructed()) + return false; + + return _assignment.driver.pin_state(_assignment.target->id); + } + + void update_assignment() + { + _assignment.update(label(), Pin::Direction::IN); + } +}; + +#endif /* _INCLUDE__PIN_STATE_SESSION__COMPONENT_H_ */ diff --git a/repos/os/include/pin_state_session/connection.h b/repos/os/include/pin_state_session/connection.h new file mode 100644 index 0000000000..f9c4ef810c --- /dev/null +++ b/repos/os/include/pin_state_session/connection.h @@ -0,0 +1,39 @@ +/* + * \brief Connection to pin-state service + * \author Norman Feske + * \date 2021-04-20 + */ + +/* + * Copyright (C) 2021 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. + */ + +#ifndef _INCLUDE__PIN_STATE_SESSION__CONNECTION_H_ +#define _INCLUDE__PIN_STATE_SESSION__CONNECTION_H_ + +#include +#include + +namespace Pin_state { struct Connection; } + +struct Pin_state::Connection : private Genode::Connection, + private Rpc_client +{ + enum { RAM_QUOTA = 8*1024UL }; + + Connection(Env &env, char const *label = "") + : + Genode::Connection(env, + session(env.parent(), + "ram_quota=%u, cap_quota=%u, label=\"%s\"", + RAM_QUOTA, CAP_QUOTA, label)), + Rpc_client(cap()) + { } + + bool state() const override { return call(); } +}; + +#endif /* _INCLUDE__PIN_STATE_SESSION__CONNECTION_H_ */ diff --git a/repos/os/include/pin_state_session/pin_state_session.h b/repos/os/include/pin_state_session/pin_state_session.h new file mode 100644 index 0000000000..9aad384e74 --- /dev/null +++ b/repos/os/include/pin_state_session/pin_state_session.h @@ -0,0 +1,47 @@ +/* + * \brief Session interface to obtain a GPIO pin state + * \author Norman Feske + * \date 2021-04-20 + */ + +/* + * Copyright (C) 2021 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. + */ + +#ifndef _INCLUDE__PIN_STATE_SESSION__PIN_STATE_SESSION_H_ +#define _INCLUDE__PIN_STATE_SESSION__PIN_STATE_SESSION_H_ + +#include +#include + +namespace Pin_state { + + using namespace Genode; + + struct Session; +} + + +struct Pin_state::Session : Genode::Session +{ + /** + * \noapi + */ + static const char *service_name() { return "Pin_state"; } + + /* + * A pin-state session consumes a dataspace capability for the server's + * session-object allocation and its session capability. + */ + enum { CAP_QUOTA = 2 }; + + virtual bool state() const = 0; + + GENODE_RPC(Rpc_state, bool, state); + GENODE_RPC_INTERFACE(Rpc_state); +}; + +#endif /* _INCLUDE__PIN_STATE_SESSION__PIN_STATE_SESSION_H_ */