From 449f647e5837a094c89d4e17073ef8202978a681 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 5 Nov 2021 14:55:20 +0100 Subject: [PATCH] dde_linux: lx_emul interface for GPIO pin access This patch introduces the lx_emul/pin.h interface that enables GPIO stub drivers to interact with Genode's Pin_control and IRQ sessions via a simple C API. Fixes #4316 --- repos/dde_linux/src/include/lx_emul/pin.h | 50 +++++ repos/dde_linux/src/lib/lx_emul/pin.cc | 231 ++++++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 repos/dde_linux/src/include/lx_emul/pin.h create mode 100644 repos/dde_linux/src/lib/lx_emul/pin.cc diff --git a/repos/dde_linux/src/include/lx_emul/pin.h b/repos/dde_linux/src/include/lx_emul/pin.h new file mode 100644 index 0000000000..74bb5dbb43 --- /dev/null +++ b/repos/dde_linux/src/include/lx_emul/pin.h @@ -0,0 +1,50 @@ +/* + * \brief Lx_emul support for accessing GPIO pins + * \author Norman Feske + * \date 2021-11-02 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _LX_EMUL__PIN_H_ +#define _LX_EMUL__PIN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Set output state of GPIO pin + * + * \pin_name GPIO name used as label for corresponding 'Pin_control' session + */ +void lx_emul_pin_control(char const *pin_name, bool enabled); + +/** + * Request interrupt backed by an IRQ session + */ +void lx_emul_pin_irq_unmask(unsigned gic_irq, unsigned pin_irq, + char const *pin_name); + +/** + * Return pin IRQ number of most recently occurred pin interrupt + * + * This function is meant to be called by the PIO driver's interrupt handler. + */ +unsigned lx_emul_pin_last_irq(void); + +/** + * Acknowledge GPIO interrupt + */ +void lx_emul_pin_irq_ack(unsigned pin_irq); + +#ifdef __cplusplus +} +#endif + +#endif /* _LX_EMUL__PIN_H_ */ diff --git a/repos/dde_linux/src/lib/lx_emul/pin.cc b/repos/dde_linux/src/lib/lx_emul/pin.cc new file mode 100644 index 0000000000..7fb683dbfd --- /dev/null +++ b/repos/dde_linux/src/lib/lx_emul/pin.cc @@ -0,0 +1,231 @@ +/* + * \brief Lx_emul backend for accessing GPIO pins + * \author Norman Feske + * \date 2021-11-02 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +/* Genode includes */ +#include +#include +#include + +#include +#include + +namespace { + + using namespace Genode; + + class Global_irq_controller : Noncopyable + { + private: + + Lx_kit::Env &_env; + + public: + + struct Number { unsigned value; }; + + Global_irq_controller(Lx_kit::Env &env) : _env(env) { } + + void trigger_irq(Number number) + { + /* + * Mirrored from 'Lx_kit::Device::Irq::handle' + */ + _env.last_irq = number.value; + _env.scheduler.unblock_irq_handler(); + _env.scheduler.schedule(); + } + }; + + using Pin_name = Session_label; + using Gic_irq_number = Global_irq_controller::Number; + + struct Pin_irq_number { unsigned value; }; + + struct Irq_info + { + Gic_irq_number gic_irq_number; + Pin_irq_number pin_irq_number; + }; + + struct Pin_irq_handler : Interface + { + virtual void handle_pin_irq(Irq_info) = 0; + }; + + struct Pin : Interface + { + using Name = Session_label; + + Env &_env; + + Pin_irq_handler &_pin_irq_handler; + + Irq_info _irq_info { }; + + Name const name; + + Constructible _control { }; + Constructible _irq { }; + + Io_signal_handler _irq_handler { _env.ep(), *this, &Pin::_handle_irq }; + + void _handle_irq() + { + _pin_irq_handler.handle_pin_irq(_irq_info); + } + + Pin(Env &env, Name const &name, Pin_irq_handler &pin_irq_handler) + : + _env(env), _pin_irq_handler(pin_irq_handler), name(name) + { } + + void control(bool enabled) + { + if (_irq.constructed()) { + error("attempt to drive interrupt pin ", name, " as output"); + return; + } + + if (!_control.constructed()) + _control.construct(_env, name.string()); + + _control->state(enabled); + } + + void associate_with_gic_and_unmask_irq(Irq_info irq_info) + { + _control.destruct(); + + if (!_irq.constructed()) { + _irq_info = irq_info; + _irq.construct(_env, _irq_info.pin_irq_number.value); + _irq->sigh(_irq_handler); + _irq->ack_irq(); + } + } + + void ack_matching_irq(Pin_irq_number ack_pin) + { + if (ack_pin.value != _irq_info.pin_irq_number.value) + return; + + if (_irq.constructed()) + _irq->ack_irq(); + } + }; + + struct Pins : private Pin_irq_handler + { + Env &_env; + Allocator &_alloc; + + Global_irq_controller &_gic; + + Registry > _registry { }; + + Pin_irq_number last_irq { }; + + Pins(Env &env, Allocator &alloc, Global_irq_controller &gic) + : + _env(env), _alloc(alloc), _gic(gic) + { } + + template + void with_pin(Pin::Name const &name, FN const &fn) + { + + Pin_irq_handler &pin_irq_handler = *this; + + /* + * Construct 'Pin' object on demand, apply 'fn' if constructed + */ + for (unsigned i = 0; i < 2; i++) { + + bool done = false; + _registry.for_each([&] (Pin &pin) { + if (pin.name == name) { + fn(pin); + done = true; + } + }); + if (done) + break; + + new (_alloc) Registered(_registry, _env, name, pin_irq_handler); + + /* ... apply 'fn' in second iteration */ + } + } + + void handle_pin_irq(Irq_info irq_info) override + { + last_irq = irq_info.pin_irq_number; + + _gic.trigger_irq(irq_info.gic_irq_number); + } + + void irq_ack(Pin_irq_number ack_pin_number) + { + _registry.for_each([&] (Pin &pin) { + pin.ack_matching_irq(ack_pin_number); }); + } + }; +}; + + +static Pins &pins() +{ + static Global_irq_controller gic { Lx_kit::env() }; + + static Pins pins { Lx_kit::env().env, Lx_kit::env().heap, gic }; + + return pins; +} + + +extern "C" void lx_emul_pin_control(char const *pin_name, bool enabled) +{ + pins().with_pin(pin_name, [&] (Pin &pin) { + pin.control(enabled); }); +} + + +extern "C" void lx_emul_pin_irq_unmask(unsigned gic_irq, unsigned pin_irq, + char const *pin_name) +{ + /* + * Translate GIC IRQ number as known by the Linux kernel into the + * physical IRQ number expected by 'Lx_kit::Env::last_irq'. + */ + gic_irq += 32; + + pins().with_pin(pin_name, [&] (Pin &pin) { + + Irq_info const irq_info { .gic_irq_number = { gic_irq }, + .pin_irq_number = { pin_irq } }; + + pin.associate_with_gic_and_unmask_irq(irq_info); + }); +} + + +extern "C" void lx_emul_pin_irq_ack(unsigned pin_irq) +{ + pins().irq_ack( Pin_irq_number { pin_irq } ); +} + + +extern "C" unsigned lx_emul_pin_last_irq(void) +{ + return pins().last_irq.value; +}