diff --git a/base/src/core/include/irq_proxy.h b/base/src/core/include/irq_proxy.h
new file mode 100644
index 000000000..4d9503793
--- /dev/null
+++ b/base/src/core/include/irq_proxy.h
@@ -0,0 +1,267 @@
+/**
+ * \brief Shared-interrupt support
+ * \author Christian Helmuth
+ * \author Sebastian Sumpf
+ * \date 2009-12-15
+ */
+
+#ifndef _CORE__INCLUDE__IRQ_PROXY_H_
+#define _CORE__INCLUDE__IRQ_PROXY_H_
+
+#include
+#include
+#include
+
+
+namespace Genode {
+ class Irq_blocker;
+ template class Irq_proxy;
+ class Irq_thread_dummy;
+ class Irq_proxy_single;
+}
+
+
+class Genode::Irq_blocker : public Genode::List::Element
+{
+ private:
+
+ Lock _wait_lock;
+
+ public:
+
+ Irq_blocker() : _wait_lock(Lock::LOCKED) { }
+
+ void block() { _wait_lock.lock(); }
+ void unblock() { _wait_lock.unlock(); }
+};
+
+
+/*
+ * Proxy thread that associates to the interrupt and unblocks waiting irqctrl
+ * threads. Maybe, we should utilize our signals for interrupt delivery...
+ *
+ * XXX resources are not accounted as the interrupt is shared
+ */
+template
+class Genode::Irq_proxy : public THREAD,
+ public Genode::List >::Element
+{
+ protected:
+
+ char _name[32];
+ Lock _startup_lock;
+
+ long _irq_number;
+
+ Lock _mutex; /* protects this object */
+ int _num_sharers; /* number of clients sharing this IRQ */
+ Semaphore _sleep; /* wake me up if aspired blockers return */
+ List _blocker_list;
+ int _num_blockers; /* number of currently blocked clients */
+ bool _woken_up; /* client decided to wake me up -
+ this prevents multiple wakeups
+ to happen during initialization */
+
+
+ /***************
+ ** Interface **
+ ***************/
+
+ /**
+ * Request interrupt
+ *
+ * \return true on success
+ */
+ virtual bool _associate() = 0;
+
+ /**
+ * Wait for associated interrupt
+ */
+ virtual void _wait_for_irq() = 0;
+
+ /**
+ * Acknowledge interrupt
+ */
+ virtual void _ack_irq() = 0;
+
+ /********************
+ ** Implementation **
+ ********************/
+
+ const char *_construct_name(long irq_number)
+ {
+ snprintf(_name, sizeof(_name), "irqproxy%02lx", irq_number);
+ return _name;
+ }
+
+ void _loop()
+ {
+ /* wait for first blocker */
+ _sleep.down();
+
+ while (1) {
+ _wait_for_irq();
+
+ {
+ Lock::Guard lock_guard(_mutex);
+
+ /* inform blocked clients */
+ Irq_blocker *b;
+ while ((b = _blocker_list.first())) {
+ _blocker_list.remove(b);
+ b->unblock();
+ }
+
+ /* reset blocker state */
+ _num_blockers = 0;
+ _woken_up = false;
+ }
+
+ /*
+ * We must wait for all clients to ack their interrupt,
+ * otherwise level-triggered interrupts will occur immediately
+ * after acknowledgement. That's an inherent security problem
+ * with shared IRQs and induces problems with dynamic driver
+ * load and unload.
+ */
+ _sleep.down();
+
+ /* acknowledge previous interrupt */
+ _ack_irq();
+ }
+ }
+
+ /**
+ * Start this thread, should be called externally from derived class
+ */
+ virtual void _start()
+ {
+ THREAD::start();
+ _startup_lock.lock();
+ }
+
+ public:
+
+ Irq_proxy(long irq_number)
+ :
+ THREAD(_construct_name(irq_number)),
+ _startup_lock(Lock::LOCKED), _irq_number(irq_number),
+ _mutex(Lock::UNLOCKED), _num_sharers(0), _num_blockers(0), _woken_up(false)
+ { }
+
+ /**
+ * Thread interface
+ */
+ void entry()
+ {
+ if (_associate()) {
+ _startup_lock.unlock();
+ _loop();
+ }
+ }
+
+ /**
+ * Block until interrupt occured
+ */
+ virtual void wait_for_irq()
+ {
+ Irq_blocker blocker;
+ {
+ Lock::Guard lock_guard(_mutex);
+
+ _blocker_list.insert(&blocker);
+ _num_blockers++;
+
+ /*
+ * The proxy thread is woken up if no client woke it up before
+ * and this client is the last aspired blocker.
+ */
+ if (!_woken_up && _num_blockers == _num_sharers) {
+ _sleep.up();
+ _woken_up = true;
+ }
+ }
+ blocker.block();
+ }
+
+
+ long irq_number() const { return _irq_number; }
+
+ virtual bool add_sharer()
+ {
+ Lock::Guard lock_guard(_mutex);
+ ++_num_sharers;
+ return true;
+ }
+
+ template
+ static PROXY *get_irq_proxy(long irq_number, Range_allocator *irq_alloc = 0)
+ {
+ static List proxies;
+ static Lock proxies_lock;
+
+ Lock::Guard lock_guard(proxies_lock);
+
+ /* lookup proxy in database */
+ for (Irq_proxy *p = proxies.first(); p; p = p->next())
+ if (p->irq_number() == irq_number)
+ return static_cast(p);
+
+ /* try to create proxy */
+ if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK)
+ return false;
+
+ PROXY *new_proxy = new (env()->heap()) PROXY(irq_number);
+ proxies.insert(new_proxy);
+ return new_proxy;
+ }
+};
+
+
+/**
+ * Dummy thread
+ */
+class Genode::Irq_thread_dummy
+{
+ public:
+
+ Irq_thread_dummy(char const *name) { }
+ void start() { }
+};
+
+
+/**
+ * Non-threaded proxy that disables shared interrupts
+ */
+class Genode::Irq_proxy_single : public Genode::Irq_proxy
+{
+ protected:
+
+ void _start()
+ {
+ _associate();
+ }
+
+ public:
+
+ Irq_proxy_single(long irq_number) : Irq_proxy(irq_number) { }
+
+ bool add_sharer()
+ {
+ Lock::Guard lock_guard(_mutex);
+
+ if (_num_sharers)
+ return false;
+
+ _num_sharers = 1;
+ return true;
+ }
+
+ void wait_for_irq()
+ {
+ _wait_for_irq();
+ _ack_irq();
+ }
+};
+
+#endif /* _CORE__INCLUDE__IRQ_PROXY_H_ */