2015-09-18 16:34:19 +02:00
|
|
|
/*
|
|
|
|
* \brief Signal context for IRQ's
|
|
|
|
* \author Josef Soentgen
|
|
|
|
* \author Christian Helmuth
|
|
|
|
* \author Stefan Kalkowski
|
|
|
|
* \date 2014-10-14
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2016-03-17 15:19:03 +01:00
|
|
|
* Copyright (C) 2014-2016 Genode Labs GmbH
|
2015-09-18 16:34:19 +02:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Genode includes */
|
2016-03-17 15:19:03 +01:00
|
|
|
#include <base/snprintf.h>
|
2015-09-18 16:34:19 +02:00
|
|
|
#include <base/tslab.h>
|
|
|
|
#include <irq_session/client.h>
|
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
/* Linux emulation environment */
|
|
|
|
#include <lx_emul.h>
|
|
|
|
|
|
|
|
/* Linux kit includes */
|
|
|
|
#include <lx_kit/irq.h>
|
|
|
|
#include <lx_kit/scheduler.h>
|
|
|
|
|
2015-09-18 16:34:19 +02:00
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
namespace Lx_kit { class Irq; }
|
2015-09-18 16:34:19 +02:00
|
|
|
|
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
class Lx_kit::Irq : public Lx::Irq
|
2015-09-18 16:34:19 +02:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper utilty for composing IRQ related names
|
|
|
|
*/
|
|
|
|
struct Name_composer
|
|
|
|
{
|
|
|
|
char name[16];
|
|
|
|
|
|
|
|
Name_composer(Platform::Device &device)
|
|
|
|
{
|
|
|
|
Genode::snprintf(name, sizeof(name),
|
|
|
|
"irq_%02x:%02x",
|
|
|
|
device.vendor_id(),
|
|
|
|
device.device_id());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This contains the Linux-driver handlers
|
|
|
|
*/
|
2016-03-17 15:19:03 +01:00
|
|
|
class Handler : public Lx_kit::List<Handler>::Element
|
2015-09-18 16:34:19 +02:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
void *_dev; /* Linux device */
|
|
|
|
irq_handler_t _handler; /* Linux handler */
|
|
|
|
irq_handler_t _thread_fn; /* Linux thread function */
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Handler(void *dev, irq_handler_t handler,
|
|
|
|
irq_handler_t thread_fn)
|
|
|
|
: _dev(dev), _handler(handler), _thread_fn(thread_fn) { }
|
|
|
|
|
|
|
|
bool handle()
|
|
|
|
{
|
|
|
|
switch (_handler(0, _dev)) {
|
|
|
|
case IRQ_WAKE_THREAD:
|
|
|
|
_thread_fn(0, _dev);
|
|
|
|
case IRQ_HANDLED:
|
|
|
|
return true;
|
|
|
|
case IRQ_NONE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Context encapsulates the handling of an IRQ
|
|
|
|
*/
|
2016-03-17 15:19:03 +01:00
|
|
|
class Context : public Lx_kit::List<Context>::Element
|
2015-09-18 16:34:19 +02:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
Name_composer _name;
|
|
|
|
Platform::Device &_dev;
|
|
|
|
Genode::Irq_session_client _irq_sess;
|
|
|
|
Lx_kit::List<Handler> _handler;
|
|
|
|
Lx::Task _task;
|
2015-09-18 16:34:19 +02:00
|
|
|
|
|
|
|
Genode::Signal_rpc_member<Context> _dispatcher;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signal handler
|
|
|
|
*/
|
|
|
|
void _handle(unsigned)
|
|
|
|
{
|
|
|
|
_task.unblock();
|
|
|
|
|
|
|
|
/* kick off scheduling */
|
|
|
|
Lx::scheduler().schedule();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _run_irq(void *args)
|
|
|
|
{
|
|
|
|
Context *ctx = static_cast<Context*>(args);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
Lx::scheduler().current()->block_and_schedule();
|
|
|
|
ctx->handle_irq();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
Context(Server::Entrypoint &ep,
|
|
|
|
Platform::Device &dev)
|
|
|
|
:
|
|
|
|
_name(dev),
|
|
|
|
_dev(dev),
|
|
|
|
_irq_sess(dev.irq(0)),
|
|
|
|
_task(_run_irq, this, _name.name, Lx::Task::PRIORITY_3, Lx::scheduler()),
|
|
|
|
_dispatcher(ep, *this, &Context::_handle)
|
|
|
|
{
|
|
|
|
_irq_sess.sigh(_dispatcher);
|
|
|
|
|
|
|
|
/* initial ack to receive further IRQ signals */
|
|
|
|
_irq_sess.ack_irq();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle IRQ
|
|
|
|
*/
|
|
|
|
void handle_irq()
|
|
|
|
{
|
|
|
|
/* report IRQ to all clients */
|
|
|
|
for (Handler *h = _handler.first(); h; h = h->next()) {
|
2016-03-17 15:19:03 +01:00
|
|
|
h->handle();
|
2015-09-18 16:34:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_irq_sess.ack_irq();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add linux handler to context
|
|
|
|
*/
|
|
|
|
void add_handler(Handler *h) { _handler.append(h); }
|
|
|
|
|
|
|
|
bool device(Platform::Device &dev) {
|
|
|
|
return (&dev == &_dev); }
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
using Context_slab = Genode::Tslab<Context, 3 * sizeof(Context)>;
|
|
|
|
using Handler_slab = Genode::Tslab<Handler, 3 * sizeof(Handler)>;
|
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
Server::Entrypoint &_ep;
|
|
|
|
Lx_kit::List<Context> _list;
|
|
|
|
Context_slab _context_alloc;
|
|
|
|
Handler_slab _handler_alloc;
|
2015-09-18 16:34:19 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Find context for given device
|
|
|
|
*/
|
|
|
|
Context *_find_context(Platform::Device &dev)
|
|
|
|
{
|
|
|
|
for (Context *i = _list.first(); i; i = i->next())
|
|
|
|
if (i->device(dev)) return i;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
Irq(Server::Entrypoint &ep, Genode::Allocator &alloc)
|
2015-09-18 16:34:19 +02:00
|
|
|
: _ep(ep),
|
2016-03-17 15:19:03 +01:00
|
|
|
_context_alloc(&alloc),
|
|
|
|
_handler_alloc(&alloc) { }
|
2015-09-18 16:34:19 +02:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
static Irq &irq(Server::Entrypoint &ep, Genode::Allocator &alloc)
|
|
|
|
{
|
|
|
|
static Irq inst(ep, alloc);
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************
|
|
|
|
** Lx::Irq interface **
|
|
|
|
***********************/
|
2015-09-18 16:34:19 +02:00
|
|
|
|
|
|
|
void request_irq(Platform::Device &dev, irq_handler_t handler,
|
2016-03-17 15:19:03 +01:00
|
|
|
void *dev_id, irq_handler_t thread_fn = 0) override
|
2015-09-18 16:34:19 +02:00
|
|
|
{
|
|
|
|
Context *ctx = _find_context(dev);
|
|
|
|
|
|
|
|
/* if this IRQ is not registered */
|
|
|
|
if (!ctx) {
|
|
|
|
ctx = new (&_context_alloc) Context(_ep, dev);
|
|
|
|
_list.insert(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* register Linux handler */
|
|
|
|
Handler *h = new (&_handler_alloc)
|
|
|
|
Handler(dev_id, handler, thread_fn);
|
|
|
|
ctx->add_handler(h);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-17 15:19:03 +01:00
|
|
|
|
|
|
|
/****************************
|
|
|
|
** Lx::Irq implementation **
|
|
|
|
****************************/
|
|
|
|
|
|
|
|
Lx::Irq &Lx::Irq::irq(Server::Entrypoint *ep, Genode::Allocator *alloc) {
|
|
|
|
return Lx_kit::Irq::irq(*ep, *alloc); }
|