2012-05-24 21:31:02 +02:00
|
|
|
/*
|
|
|
|
* \brief Signal context for IRQ's
|
|
|
|
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
|
|
|
|
* \date 2012-05-23
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 21:44:47 +01:00
|
|
|
* Copyright (C) 2012-2013 Genode Labs GmbH
|
2012-05-24 21:31:02 +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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
#include <lx_emul.h>
|
2012-08-03 18:32:51 +02:00
|
|
|
|
2012-05-24 21:31:02 +02:00
|
|
|
extern "C" {
|
|
|
|
#include <dde_kit/interrupt.h>
|
|
|
|
}
|
|
|
|
|
|
|
|
/* our local incarnation of sender and receiver */
|
|
|
|
static Signal_helper *_signal = 0;
|
2012-08-03 18:32:51 +02:00
|
|
|
static Genode::Lock _irq_sync(Genode::Lock::LOCKED);
|
|
|
|
static Genode::Lock _irq_wait(Genode::Lock::LOCKED);
|
2012-05-24 21:31:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This contains the Linux-driver handlers
|
|
|
|
*/
|
|
|
|
struct Irq_handler : Genode::List<Irq_handler>::Element
|
|
|
|
{
|
|
|
|
void *dev; /* Linux device */
|
|
|
|
irq_handler_t handler; /* Linux handler */
|
|
|
|
|
|
|
|
Irq_handler(void *dev, irq_handler_t handler)
|
|
|
|
: dev(dev), handler(handler) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-08-03 18:32:51 +02:00
|
|
|
|
2012-05-24 21:31:02 +02:00
|
|
|
/**
|
|
|
|
* Signal context for IRQs
|
|
|
|
*/
|
|
|
|
class Irq_context : public Driver_context,
|
|
|
|
public Genode::List<Irq_context>::Element
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2012-10-24 16:03:31 +02:00
|
|
|
typedef Genode::List<Irq_context>::Element LE;
|
|
|
|
|
2012-05-24 21:31:02 +02:00
|
|
|
unsigned int _irq; /* IRQ number */
|
|
|
|
Genode::List<Irq_handler> _handler_list; /* List of registered handlers */
|
|
|
|
Genode::Signal_context_capability _ctx_cap; /* capability for this context */
|
|
|
|
|
|
|
|
static Genode::List<Irq_context> *_list()
|
|
|
|
{
|
|
|
|
static Genode::List<Irq_context> _l;
|
|
|
|
return &_l;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find context for given IRQ number
|
|
|
|
*/
|
|
|
|
static Irq_context *_find_ctx(unsigned int irq)
|
|
|
|
{
|
2012-10-24 16:03:31 +02:00
|
|
|
|
|
|
|
for (Irq_context *i = _list()->first(); i; i = i->LE::next())
|
2012-05-24 21:31:02 +02:00
|
|
|
if (i->_irq == irq)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called by the DDE kit upon IRQ */
|
|
|
|
static void _dde_handler(void *irq)
|
|
|
|
{
|
2012-08-27 16:43:13 +02:00
|
|
|
/*
|
|
|
|
* Make sure there is only one interrupt handled at a time, since dde_kit
|
|
|
|
* will use one thread per IRQ
|
|
|
|
*/
|
|
|
|
static Genode::Lock handler_lock;
|
|
|
|
Genode::Lock::Guard guard(handler_lock);
|
|
|
|
|
2012-08-03 18:32:51 +02:00
|
|
|
/* unlock if main thread is waiting */
|
|
|
|
_irq_wait.unlock();
|
|
|
|
|
2012-05-24 21:31:02 +02:00
|
|
|
Irq_context *ctx = static_cast<Irq_context *>(irq);
|
|
|
|
|
|
|
|
/* set context & submit signal */
|
|
|
|
_signal->sender()->context(ctx->_ctx_cap);
|
|
|
|
_signal->sender()->submit();
|
2012-07-26 19:15:35 +02:00
|
|
|
|
2012-08-03 18:32:51 +02:00
|
|
|
/* wait for interrupt to get acked at device side */
|
|
|
|
_irq_sync.lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call one IRQ handler
|
|
|
|
*/
|
|
|
|
inline bool _handle_one(Irq_handler *h)
|
|
|
|
{
|
|
|
|
bool handled = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It might be that the next interrupt triggers right after the device has
|
|
|
|
* acknowledged the IRQ
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
if (h->handler(_irq, h->dev) != IRQ_HANDLED)
|
|
|
|
return handled;
|
|
|
|
|
|
|
|
handled = true;
|
|
|
|
|
|
|
|
} while (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call all handlers registered for this context
|
|
|
|
*/
|
|
|
|
bool _handle()
|
|
|
|
{
|
|
|
|
bool handled = false;
|
|
|
|
|
|
|
|
/* report IRQ to all clients */
|
|
|
|
for (Irq_handler *h = _handler_list.first(); h; h = h->next()) {
|
|
|
|
|
2012-08-27 16:43:13 +02:00
|
|
|
handled |= _handle_one(h);
|
|
|
|
dde_kit_log(DEBUG_IRQ, "IRQ: %u ret: %u h: %p dev: %p", _irq, handled, h->handler, h->dev);
|
2012-08-03 18:32:51 +02:00
|
|
|
}
|
2012-08-27 16:43:13 +02:00
|
|
|
|
2012-08-03 18:32:51 +02:00
|
|
|
/* interrupt should be acked at device now */
|
|
|
|
_irq_sync.unlock();
|
2012-08-27 16:43:13 +02:00
|
|
|
|
|
|
|
if (handled)
|
|
|
|
Routine::schedule_all();
|
|
|
|
|
2012-08-03 18:32:51 +02:00
|
|
|
return handled;
|
2012-05-24 21:31:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Irq_context(unsigned int irq)
|
|
|
|
: _irq(irq),
|
|
|
|
_ctx_cap(_signal->receiver()->manage(this))
|
|
|
|
{
|
|
|
|
/* register at DDE (shared) */
|
2012-07-18 15:40:43 +02:00
|
|
|
int ret = dde_kit_interrupt_attach(_irq, 0, 0, _dde_handler, this);
|
|
|
|
if (ret)
|
|
|
|
PERR("Interrupt attach return %d for IRQ %u", ret, irq);
|
|
|
|
|
2012-05-24 21:31:02 +02:00
|
|
|
dde_kit_interrupt_enable(_irq);
|
|
|
|
_list()->insert(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-03 18:32:51 +02:00
|
|
|
inline void handle() { _handle(); }
|
2012-06-20 08:42:16 +02:00
|
|
|
const char *debug() { return "Irq_context"; }
|
2012-08-03 18:32:51 +02:00
|
|
|
|
2012-05-24 21:31:02 +02:00
|
|
|
/**
|
|
|
|
* Request an IRQ
|
|
|
|
*/
|
|
|
|
static void request_irq(unsigned int irq, irq_handler_t handler, void *dev)
|
|
|
|
{
|
|
|
|
Irq_handler *h = new(Genode::env()->heap()) Irq_handler(dev, handler);
|
|
|
|
Irq_context *ctx = _find_ctx(irq);
|
|
|
|
|
|
|
|
/* if this IRQ is not registered */
|
|
|
|
if (!ctx)
|
|
|
|
ctx = new (Genode::env()->heap()) Irq_context(irq);
|
|
|
|
|
|
|
|
/* register Linux handler */
|
|
|
|
ctx->_handler_list.insert(h);
|
|
|
|
}
|
2012-08-03 18:32:51 +02:00
|
|
|
|
|
|
|
static bool check_irq()
|
|
|
|
{
|
|
|
|
bool handled = false;
|
2012-10-24 16:03:31 +02:00
|
|
|
for (Irq_context *i = _list()->first(); i; i = i->LE::next())
|
2012-08-03 18:32:51 +02:00
|
|
|
handled |= i->_handle();
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wait()
|
|
|
|
{
|
|
|
|
_irq_wait.lock();
|
|
|
|
check_irq();
|
|
|
|
}
|
2012-05-24 21:31:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void Irq::init(Genode::Signal_receiver *recv) {
|
|
|
|
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
|
|
|
|
|
|
|
|
|
2012-08-03 18:32:51 +02:00
|
|
|
void Irq::check_irq()
|
|
|
|
{
|
|
|
|
if (!Irq_context::check_irq())
|
|
|
|
Irq_context::wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-24 21:31:02 +02:00
|
|
|
/***********************
|
|
|
|
** linux/interrupt.h **
|
|
|
|
***********************/
|
|
|
|
|
|
|
|
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
|
|
|
|
const char *name, void *dev)
|
|
|
|
{
|
2012-08-03 18:32:51 +02:00
|
|
|
dde_kit_log(DEBUG_IRQ, "Request irq %u handler %p", irq, handler);
|
2012-05-24 21:31:02 +02:00
|
|
|
Irq_context::request_irq(irq, handler, dev);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-08-03 18:32:51 +02:00
|
|
|
|