mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 02:01:38 +00:00
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
This commit is contained in:
parent
f2a627c107
commit
449f647e58
50
repos/dde_linux/src/include/lx_emul/pin.h
Normal file
50
repos/dde_linux/src/include/lx_emul/pin.h
Normal file
@ -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_ */
|
231
repos/dde_linux/src/lib/lx_emul/pin.cc
Normal file
231
repos/dde_linux/src/lib/lx_emul/pin.cc
Normal file
@ -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 <base/registry.h>
|
||||
#include <pin_control_session/connection.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
#include <lx_emul/pin.h>
|
||||
#include <lx_kit/env.h>
|
||||
|
||||
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<Pin_control::Connection> _control { };
|
||||
Constructible<Irq_connection> _irq { };
|
||||
|
||||
Io_signal_handler<Pin> _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<Registered<Pin> > _registry { };
|
||||
|
||||
Pin_irq_number last_irq { };
|
||||
|
||||
Pins(Env &env, Allocator &alloc, Global_irq_controller &gic)
|
||||
:
|
||||
_env(env), _alloc(alloc), _gic(gic)
|
||||
{ }
|
||||
|
||||
template <typename FN>
|
||||
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<Pin>(_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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user