Index: abi/include/abi/ipc/methods.h
===================================================================
--- abi/include/abi/ipc/methods.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ abi/include/abi/ipc/methods.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -59,4 +59,26 @@
 	IPC_M_PHONE_HUNGUP = 0,
 
+	/** Clone connection.
+	 *
+	 * The calling task clones one of its phones for the callee.
+	 *
+	 * - ARG1 - The caller sets ARG1 to the phone of the cloned connection.
+	 *        - The callee gets the new phone from ARG1.
+	 *
+	 * - on answer, the callee acknowledges the new connection by sending EOK back
+	 *   or the kernel closes it
+	 */
+	IPC_M_CONNECTION_CLONE,
+
+	/** Protocol for establishing a cloned connection.
+	 *
+	 * Through this call, the recipient learns about the new cloned connection.
+	 *
+	 * - ARG5 - the kernel sets ARG5 to contain the hash of the used phone
+	 * - on answer, the callee acknowledges the new connection by sending EOK back
+	 *   or the kernel closes it
+	 */
+	IPC_M_CLONE_ESTABLISH,
+
 	/** Protocol for initializing callback connections.
 	 *
Index: abi/include/abi/syscall.h
===================================================================
--- abi/include/abi/syscall.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ abi/include/abi/syscall.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -81,4 +81,5 @@
 	SYS_PERM_REVOKE,
 	
+	SYS_DEVICE_ASSIGN_DEVNO,
 	SYS_PHYSMEM_MAP,
 	SYS_PHYSMEM_UNMAP,
Index: kernel/Makefile
===================================================================
--- kernel/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/Makefile	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -209,4 +209,5 @@
 	generic/src/ddi/ddi.c \
 	generic/src/ddi/irq.c \
+	generic/src/ddi/device.c \
 	generic/src/debug/symtab.c \
 	generic/src/debug/stacktrace.c \
@@ -271,6 +272,8 @@
 	generic/src/ipc/sysipc.c \
 	generic/src/ipc/sysipc_ops.c \
+	generic/src/ipc/ops/clnestab.c \
 	generic/src/ipc/ops/conctmeto.c \
 	generic/src/ipc/ops/concttome.c \
+	generic/src/ipc/ops/connclone.c \
 	generic/src/ipc/ops/dataread.c \
 	generic/src/ipc/ops/datawrite.c \
@@ -283,5 +286,4 @@
 	generic/src/ipc/irq.c \
 	generic/src/ipc/event.c \
-	generic/src/cap/cap.c \
 	generic/src/security/perm.c \
 	generic/src/sysinfo/sysinfo.c \
Index: kernel/arch/arm32/src/interrupt.c
===================================================================
--- kernel/arch/arm32/src/interrupt.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/arm32/src/interrupt.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -38,4 +38,5 @@
 #include <arch/machine_func.h>
 #include <ddi/irq.h>
+#include <ddi/device.h>
 #include <interrupt.h>
 
Index: kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c
===================================================================
--- kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -44,4 +44,5 @@
 #include <mm/km.h>
 #include <ddi/ddi.h>
+#include <ddi/device.h>
 
 static void bbxm_init(void);
@@ -116,4 +117,5 @@
 	static irq_t timer_irq;
 	irq_initialize(&timer_irq);
+	timer_irq.devno = device_assign_devno();
 	timer_irq.inr = AMDM37x_GPT1_IRQ;
 	timer_irq.claim = bb_timer_irq_claim;
Index: kernel/arch/arm32/src/mach/beaglebone/beaglebone.c
===================================================================
--- kernel/arch/arm32/src/mach/beaglebone/beaglebone.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/arm32/src/mach/beaglebone/beaglebone.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -46,4 +46,5 @@
 #include <interrupt.h>
 #include <ddi/ddi.h>
+#include <ddi/device.h>
 #include <mm/km.h>
 
@@ -128,4 +129,5 @@
 	static irq_t timer_irq;
 	irq_initialize(&timer_irq);
+	timer_irq.devno = device_assign_devno();
 	timer_irq.inr = AM335x_DMTIMER2_IRQ;
 	timer_irq.claim = bbone_timer_irq_claim;
Index: kernel/arch/arm32/src/mach/gta02/gta02.c
===================================================================
--- kernel/arch/arm32/src/mach/gta02/gta02.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/arm32/src/mach/gta02/gta02.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -48,4 +48,5 @@
 #include <interrupt.h>
 #include <ddi/ddi.h>
+#include <ddi/device.h>
 #include <log.h>
 
@@ -240,4 +241,5 @@
 {
 	irq_initialize(&gta02_timer_irq);
+	gta02_timer_irq.devno = device_assign_devno();
 	gta02_timer_irq.inr = GTA02_TIMER_IRQ;
 	gta02_timer_irq.claim = gta02_timer_irq_claim;
Index: kernel/arch/arm32/src/mach/integratorcp/integratorcp.c
===================================================================
--- kernel/arch/arm32/src/mach/integratorcp/integratorcp.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/arm32/src/mach/integratorcp/integratorcp.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -43,4 +43,5 @@
 #include <console/console.h>
 #include <sysinfo/sysinfo.h>
+#include <ddi/device.h>
 #include <mm/page.h>
 #include <mm/frame.h>
@@ -206,4 +207,5 @@
 {
 	irq_initialize(&icp.timer_irq);
+	icp.timer_irq.devno = device_assign_devno();
 	icp.timer_irq.inr = ICP_TIMER_IRQ;
 	icp.timer_irq.claim = icp_timer_claim;
Index: kernel/arch/arm32/src/mach/raspberrypi/raspberrypi.c
===================================================================
--- kernel/arch/arm32/src/mach/raspberrypi/raspberrypi.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/arm32/src/mach/raspberrypi/raspberrypi.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -50,4 +50,5 @@
 #include <interrupt.h>
 #include <ddi/ddi.h>
+#include <ddi/device.h>
 
 #define RPI_DEFAULT_MEMORY_START	0
@@ -117,4 +118,5 @@
 	static irq_t timer_irq;
 	irq_initialize(&timer_irq);
+	timer_irq.devno = device_assign_devno();
 	timer_irq.inr = BCM2835_TIMER1_IRQ;
 	timer_irq.claim = raspberrypi_timer_irq_claim;
Index: kernel/arch/ia32/src/drivers/i8254.c
===================================================================
--- kernel/arch/ia32/src/drivers/i8254.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/ia32/src/drivers/i8254.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -51,4 +51,5 @@
 #include <arch.h>
 #include <ddi/irq.h>
+#include <ddi/device.h>
 
 #define CLK_PORT1  ((ioport8_t *) 0x40U)
@@ -85,4 +86,5 @@
 	irq_initialize(&i8254_irq);
 	i8254_irq.preack = true;
+	i8254_irq.devno = device_assign_devno();
 	i8254_irq.inr = IRQ_CLK;
 	i8254_irq.claim = i8254_claim;
Index: kernel/arch/ia32/src/smp/apic.c
===================================================================
--- kernel/arch/ia32/src/smp/apic.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/ia32/src/smp/apic.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -47,4 +47,5 @@
 #include <arch.h>
 #include <ddi/irq.h>
+#include <ddi/device.h>
 
 #ifdef CONFIG_SMP
@@ -189,4 +190,5 @@
 	irq_initialize(&l_apic_timer_irq);
 	l_apic_timer_irq.preack = true;
+	l_apic_timer_irq.devno = device_assign_devno();
 	l_apic_timer_irq.inr = IRQ_CLK;
 	l_apic_timer_irq.claim = l_apic_timer_claim;
Index: kernel/arch/ia64/src/drivers/it.c
===================================================================
--- kernel/arch/ia64/src/drivers/it.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/ia64/src/drivers/it.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -42,4 +42,5 @@
 #include <time/clock.h>
 #include <ddi/irq.h>
+#include <ddi/device.h>
 #include <arch.h>
 
@@ -65,4 +66,5 @@
 		irq_initialize(&it_irq);
 		it_irq.inr = INTERRUPT_TIMER;
+		it_irq.devno = device_assign_devno();
 		it_irq.claim = it_claim;
 		it_irq.handler = it_interrupt;
Index: kernel/arch/mips32/src/interrupt.c
===================================================================
--- kernel/arch/mips32/src/interrupt.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/mips32/src/interrupt.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -41,4 +41,5 @@
 #include <time/clock.h>
 #include <ipc/sysipc.h>
+#include <ddi/device.h>
 
 #define IRQ_COUNT   8
@@ -174,4 +175,5 @@
 	
 	irq_initialize(&timer_irq);
+	timer_irq.devno = device_assign_devno();
 	timer_irq.inr = TIMER_IRQ;
 	timer_irq.claim = timer_claim;
@@ -184,4 +186,5 @@
 #ifdef MACHINE_msim
 	irq_initialize(&dorder_irq);
+	dorder_irq.devno = device_assign_devno();
 	dorder_irq.inr = DORDER_IRQ;
 	dorder_irq.claim = dorder_claim;
Index: kernel/arch/sparc64/src/drivers/niagara.c
===================================================================
--- kernel/arch/sparc64/src/drivers/niagara.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/sparc64/src/drivers/niagara.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -39,4 +39,5 @@
 #include <console/console.h>
 #include <ddi/ddi.h>
+#include <ddi/device.h>
 #include <arch/asm.h>
 #include <arch.h>
Index: kernel/genarch/src/drivers/dsrln/dsrlnin.c
===================================================================
--- kernel/genarch/src/drivers/dsrln/dsrlnin.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/dsrln/dsrlnin.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -40,4 +40,5 @@
 #include <mm/slab.h>
 #include <arch/asm.h>
+#include <ddi/device.h>
 
 static irq_ownership_t dsrlnin_claim(irq_t *irq)
@@ -63,4 +64,5 @@
 		
 		irq_initialize(&instance->irq);
+		instance->irq.devno = device_assign_devno();
 		instance->irq.inr = inr;
 		instance->irq.claim = dsrlnin_claim;
Index: kernel/genarch/src/drivers/i8042/i8042.c
===================================================================
--- kernel/genarch/src/drivers/i8042/i8042.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/i8042/i8042.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -44,4 +44,5 @@
 #include <console/chardev.h>
 #include <mm/slab.h>
+#include <ddi/device.h>
 #include <time/delay.h>
 
@@ -112,4 +113,5 @@
 		
 		irq_initialize(&instance->irq);
+		instance->irq.devno = device_assign_devno();
 		instance->irq.inr = inr;
 		instance->irq.claim = i8042_claim;
Index: kernel/genarch/src/drivers/ns16550/ns16550.c
===================================================================
--- kernel/genarch/src/drivers/ns16550/ns16550.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/ns16550/ns16550.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -41,4 +41,5 @@
 #include <console/chardev.h>
 #include <mm/slab.h>
+#include <ddi/device.h>
 #include <str.h>
 
@@ -137,4 +138,5 @@
 		
 		irq_initialize(&instance->irq);
+		instance->irq.devno = device_assign_devno();
 		instance->irq.inr = inr;
 		instance->irq.claim = ns16550_claim;
Index: kernel/genarch/src/drivers/omap/uart.c
===================================================================
--- kernel/genarch/src/drivers/omap/uart.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/omap/uart.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -37,4 +37,5 @@
 #include <assert.h>
 #include <genarch/drivers/omap/uart.h>
+#include <ddi/device.h>
 #include <str.h>
 #include <mm/km.h>
@@ -160,4 +161,5 @@
 	/* Initialize IRQ */
 	irq_initialize(&uart->irq);
+	uart->irq.devno = device_assign_devno();
 	uart->irq.inr = interrupt;
 	uart->irq.claim = omap_uart_claim;
Index: kernel/genarch/src/drivers/pl011/pl011.c
===================================================================
--- kernel/genarch/src/drivers/pl011/pl011.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/pl011/pl011.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -39,4 +39,5 @@
 #include <console/chardev.h>
 #include <console/console.h>
+#include <ddi/device.h>
 #include <arch/asm.h>
 #include <mm/slab.h>
@@ -126,4 +127,5 @@
 	/* Initialize IRQ */
 	irq_initialize(&uart->irq);
+	uart->irq.devno = device_assign_devno();
 	uart->irq.inr = interrupt;
 	uart->irq.claim = pl011_uart_claim;
Index: kernel/genarch/src/drivers/pl050/pl050.c
===================================================================
--- kernel/genarch/src/drivers/pl050/pl050.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/pl050/pl050.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -41,4 +41,5 @@
 #include <console/chardev.h>
 #include <mm/slab.h>
+#include <ddi/device.h>
 
 #define PL050_KEY_RELEASE 0xF0
@@ -86,4 +87,5 @@
 
 		irq_initialize(&instance->irq);
+		instance->irq.devno = device_assign_devno();
 		instance->irq.inr = inr;
 		instance->irq.claim = pl050_claim;
Index: kernel/genarch/src/drivers/s3c24xx/uart.c
===================================================================
--- kernel/genarch/src/drivers/s3c24xx/uart.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/s3c24xx/uart.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -42,4 +42,5 @@
 #include <console/chardev.h>
 #include <console/console.h>
+#include <ddi/device.h>
 #include <arch/asm.h>
 #include <mm/slab.h>
@@ -122,4 +123,5 @@
 	/* Initialize IRQ structure. */
 	irq_initialize(&uart->irq);
+	uart->irq.devno = device_assign_devno();
 	uart->irq.inr = inr;
 	uart->irq.claim = s3c24xx_uart_claim;
Index: kernel/genarch/src/drivers/via-cuda/cuda.c
===================================================================
--- kernel/genarch/src/drivers/via-cuda/cuda.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/genarch/src/drivers/via-cuda/cuda.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -40,4 +40,5 @@
 #include <arch/asm.h>
 #include <mm/slab.h>
+#include <ddi/device.h>
 #include <synch/spinlock.h>
 #include <mem.h>
@@ -105,4 +106,5 @@
 
 		irq_initialize(&instance->irq);
+		instance->irq.devno = device_assign_devno();
 		instance->irq.inr = inr;
 		instance->irq.claim = cuda_claim;
Index: kernel/generic/include/adt/hash_table.h
===================================================================
--- kernel/generic/include/adt/hash_table.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/adt/hash_table.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -82,5 +82,4 @@
 extern link_t *hash_table_find(hash_table_t *h, sysarg_t key[]);
 extern void hash_table_remove(hash_table_t *h, sysarg_t key[], size_t keys);
-extern void hash_table_remove_item(hash_table_t *h, link_t *item);
 
 #endif
Index: rnel/generic/include/cap/cap.h
===================================================================
--- kernel/generic/include/cap/cap.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ 	(revision )
@@ -1,129 +1,0 @@
-/*
- * Copyright (c) 2017 Jakub Jermar
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup generic
- * @{
- */
-/** @file
- */
-
-#ifndef KERN_CAP_H_
-#define KERN_CAP_H_
-
-#include <typedefs.h>
-#include <adt/list.h>
-#include <synch/mutex.h>
-#include <atomic.h>
-
-#define MAX_CAPS  64
-
-typedef int cap_handle_t;
-
-typedef enum {
-	CAP_STATE_FREE,
-	CAP_STATE_ALLOCATED,
-	CAP_STATE_PUBLISHED
-} cap_state_t;
-
-typedef enum {
-	KOBJECT_TYPE_PHONE,
-	KOBJECT_TYPE_IRQ,
-	KOBJECT_TYPE_MAX
-} kobject_type_t;
-
-struct task;
-struct phone;
-struct irq;
-
-struct kobject;
-typedef struct kobject_ops {
-	bool (*reclaim)(struct kobject *);
-	void (*destroy)(void *);
-} kobject_ops_t;
-
-/*
- * Everything in kobject_t except for the atomic reference count is imutable.
- */
-typedef struct kobject {
-	kobject_type_t type;
-	atomic_t refcnt;
-
-	kobject_ops_t *ops;
-
-	union {
-		void *raw;
-		struct phone *phone;
-		struct irq *irq;
-	};
-} kobject_t;
-
-/*
- * A cap_t may only be accessed under the protection of the cap_info_t lock.
- */
-typedef struct cap {
-	cap_state_t state;
-
-	cap_handle_t handle;
-
-	/* Link to the task's capabilities of the same kobject type. */
-	link_t link;
-
-	/* The underlying kernel object. */
-	kobject_t *kobject;
-} cap_t;
-
-typedef struct cap_info {
-	mutex_t lock;
-
-	list_t type_list[KOBJECT_TYPE_MAX];
-
-	cap_t *caps;
-} cap_info_t;
-
-extern void caps_task_alloc(struct task *);
-extern void caps_task_free(struct task *);
-extern void caps_task_init(struct task *);
-extern bool caps_apply_to_kobject_type(struct task *, kobject_type_t,
-    bool (*)(cap_t *, void *), void *);
-
-extern void cap_initialize(cap_t *, cap_handle_t);
-extern cap_handle_t cap_alloc(struct task *);
-extern void cap_publish(struct task *, cap_handle_t, kobject_t *);
-extern kobject_t *cap_unpublish(struct task *, cap_handle_t, kobject_type_t);
-extern void cap_free(struct task *, cap_handle_t);
-
-extern void kobject_initialize(kobject_t *, kobject_type_t, void *,
-    kobject_ops_t *);
-extern kobject_t *kobject_get(struct task *, cap_handle_t, kobject_type_t);
-extern void kobject_add_ref(kobject_t *);
-extern void kobject_put(kobject_t *);
-
-#endif
-
-/** @}
- */
Index: kernel/generic/include/ddi/device.h
===================================================================
--- kernel/generic/include/ddi/device.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
+++ kernel/generic/include/ddi/device.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2006 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericddi
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_DEVICE_H_
+#define KERN_DEVICE_H_
+
+#include <typedefs.h>
+
+extern devno_t device_assign_devno(void);
+extern sysarg_t sys_device_assign_devno(void);
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/ddi/irq.h
===================================================================
--- kernel/generic/include/ddi/irq.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/ddi/irq.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -43,15 +43,4 @@
 #include <proc/task.h>
 #include <ipc/ipc.h>
-#include <mm/slab.h>
-
-typedef enum {
-	IRQ_HT_KEY_INR,
-	IRQ_HT_KEY_MODE
-} irq_ht_key_t;
-
-typedef enum {
-	IRQ_HT_MODE_CLAIM,
-	IRQ_HT_MODE_NO_CLAIM
-} irq_ht_mode_t;
 
 typedef enum {
@@ -81,6 +70,4 @@
 	/** When false, notifications are not sent. */
 	bool notify;
-	/** True if the structure is in irq_uspace_hash_table_table */
-	bool hashed_in;
 	/** Answerbox for notifications. */
 	answerbox_t *answerbox;
@@ -89,8 +76,14 @@
 	/** Arguments that will be sent if the IRQ is claimed. */
 	uint32_t scratch[IPC_CALL_LEN];
-	/** Top-half IRQ code. */
+	/** Top-half pseudocode. */
 	irq_code_t *code;
 	/** Counter. */
 	size_t counter;
+	
+	/**
+	 * Link between IRQs that are notifying the same answerbox. The list is
+	 * protected by the answerbox irq_lock.
+	 */
+	link_t link;
 } ipc_notif_cfg_t;
 
@@ -98,5 +91,6 @@
  *
  * If one device has multiple interrupts, there will be multiple irq_t
- * instantions.
+ * instantions with the same devno.
+ *
  */
 typedef struct irq {
@@ -118,4 +112,7 @@
 	 */
 	bool preack;
+	
+	/** Unique device number. -1 if not yet assigned. */
+	devno_t devno;
 	
 	/** Actual IRQ number. -1 if not yet assigned. */
@@ -142,6 +139,4 @@
 extern hash_table_t irq_uspace_hash_table;
 
-extern slab_cache_t *irq_slab;
-
 extern inr_t last_inr;
 
Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/ipc/ipc.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -42,6 +42,6 @@
 #include <abi/proc/task.h>
 #include <typedefs.h>
-#include <mm/slab.h>
-#include <cap/cap.h>
+
+#define IPC_MAX_PHONES  64
 
 struct answerbox;
@@ -62,5 +62,5 @@
 
 /** Structure identifying phone (in TASK structure) */
-typedef struct phone {
+typedef struct {
 	mutex_t lock;
 	link_t link;
@@ -69,5 +69,4 @@
 	ipc_phone_state_t state;
 	atomic_t active_calls;
-	kobject_t *kobject;
 } phone_t;
 
@@ -95,4 +94,6 @@
 	/** Notifications from IRQ handlers. */
 	list_t irq_notifs;
+	/** IRQs with notifications to this answerbox. */
+	list_t irq_list;
 } answerbox_t;
 
@@ -169,6 +170,4 @@
 } call_t;
 
-extern slab_cache_t *phone_slab;
-
 extern answerbox_t *ipc_phone_0;
 
Index: kernel/generic/include/ipc/ipcrsc.h
===================================================================
--- kernel/generic/include/ipc/ipcrsc.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/ipc/ipcrsc.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -38,10 +38,10 @@
 #include <proc/task.h>
 #include <ipc/ipc.h>
-#include <cap/cap.h>
 
 extern call_t *get_call(sysarg_t);
-extern cap_handle_t phone_alloc(task_t *);
-extern bool phone_connect(cap_handle_t, answerbox_t *);
-extern void phone_dealloc(cap_handle_t);
+extern int phone_get(sysarg_t, phone_t **);
+extern int phone_alloc(task_t *);
+extern bool phone_connect(int, answerbox_t *);
+extern void phone_dealloc(int);
 
 #endif
Index: kernel/generic/include/ipc/irq.h
===================================================================
--- kernel/generic/include/ipc/irq.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/ipc/irq.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -47,10 +47,12 @@
 #include <adt/list.h>
 
+extern int ipc_irq_subscribe(answerbox_t *, inr_t, devno_t, sysarg_t,
+    irq_code_t *);
 
 extern irq_ownership_t ipc_irq_top_half_claim(irq_t *);
 extern void ipc_irq_top_half_handler(irq_t *);
 
-extern int ipc_irq_subscribe(answerbox_t *, inr_t, sysarg_t, irq_code_t *);
-extern int ipc_irq_unsubscribe(answerbox_t *, int);
+extern int ipc_irq_unsubscribe(answerbox_t *, inr_t, devno_t);
+extern void ipc_irq_cleanup(answerbox_t *);
 
 /*
Index: kernel/generic/include/ipc/sysipc.h
===================================================================
--- kernel/generic/include/ipc/sysipc.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/ipc/sysipc.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -56,6 +56,6 @@
 extern sysarg_t sys_ipc_hangup(sysarg_t);
 
-extern sysarg_t sys_ipc_irq_subscribe(inr_t, sysarg_t, irq_code_t *);
-extern sysarg_t sys_ipc_irq_unsubscribe(sysarg_t);
+extern sysarg_t sys_ipc_irq_subscribe(inr_t, devno_t, sysarg_t, irq_code_t *);
+extern sysarg_t sys_ipc_irq_unsubscribe(inr_t, devno_t);
 
 #ifdef __32_BITS__
Index: kernel/generic/include/proc/task.h
===================================================================
--- kernel/generic/include/proc/task.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/proc/task.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -60,5 +60,4 @@
 #include <abi/sysinfo.h>
 #include <arch.h>
-#include <cap/cap.h>
 
 #define TASK                 THE->task
@@ -66,5 +65,4 @@
 
 struct thread;
-struct cap;
 
 /** Task structure. */
@@ -97,7 +95,4 @@
 	/** Task permissions. */
 	perm_t perms;
-
-	/** Capabilities */
-	cap_info_t *cap_info;
 	
 	/* IPC stuff */
@@ -105,4 +100,7 @@
 	/** Receiving communication endpoint */
 	answerbox_t answerbox;
+
+	/** Sending communication endpoints */
+	phone_t phones[IPC_MAX_PHONES];
 
 	/** Spinlock protecting the active_calls list. */
Index: kernel/generic/include/typedefs.h
===================================================================
--- kernel/generic/include/typedefs.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/include/typedefs.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -49,4 +49,5 @@
 
 typedef int32_t inr_t;
+typedef int32_t devno_t;
 
 typedef volatile uint8_t ioport8_t;
Index: kernel/generic/src/adt/hash_table.c
===================================================================
--- kernel/generic/src/adt/hash_table.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/adt/hash_table.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -190,19 +190,4 @@
 }
 
-/** Remove an existing item from hash table.
- *
- * @param h     Hash table.
- * @param item  Item to remove from the hash table.
- */
-void hash_table_remove_item(hash_table_t *h, link_t *item)
-{
-	assert(h);
-	assert(h->op);
-	
-	list_remove(item);
-	if (h->op->remove_callback)
-		h->op->remove_callback(item);
-}
-
 /** @}
  */
Index: rnel/generic/src/cap/cap.c
===================================================================
--- kernel/generic/src/cap/cap.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ 	(revision )
@@ -1,359 +1,0 @@
-/*
- * Copyright (c) 2017 Jakub Jermar
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup generic
- * @{
- */
-/** @file
- */
-
-/*
- * HelenOS capabilities are task-local names for references to kernel objects.
- * Kernel objects are reference-counted wrappers for a select group of objects
- * allocated in and by the kernel that can be made accessible to userspace in a
- * controlled way via integer handles.
- *
- * A kernel object (kobject_t) encapsulates one of the following raw objects:
- *
- * - IPC phone
- * - IRQ object
- *
- * A capability (cap_t) is either free, allocated or published. Free
- * capabilities can be allocated, which reserves the capability handle in the
- * task-local capability space. Allocated capabilities can be published, which
- * associates them with an existing kernel object. Userspace can only access
- * published capabilities.
- *
- * A published capability may get unpublished, which disassociates it from the
- * underlying kernel object and puts it back into the allocated state. An
- * allocated capability can be freed to become available for future use.
- *
- * There is a 1:1 correspondence between a kernel object (kobject_t) and the
- * actual raw object it encapsulates. A kernel object (kobject_t) may have
- * multiple references, either implicit from one or more capabilities (cap_t),
- * even from capabilities in different tasks, or explicit as a result of
- * creating a new reference from a capability handle using kobject_get(), or
- * creating a new reference from an already existing reference by
- * kobject_add_ref() or as a result of unpublishing a capability and
- * disassociating it from its kobject_t using cap_unpublish().
- *
- * As kernel objects are reference-counted, they get automatically destroyed
- * when their last reference is dropped in kobject_put(). The idea is that
- * whenever a kernel object is inserted into some sort of a container (e.g. a
- * list or hash table), its reference count should be incremented via
- * kobject_get() or kobject_add_ref(). When the kernel object is removed from
- * the container, the reference count should go down via a call to
- * kobject_put().
- */
-
-#include <cap/cap.h>
-#include <proc/task.h>
-#include <synch/mutex.h>
-#include <abi/errno.h>
-#include <mm/slab.h>
-#include <adt/list.h>
-
-static kobject_t *cap_unpublish_locked(task_t *, cap_handle_t, kobject_type_t);
-
-/** Initialize capability and associate it with its handle
- *
- * @param cap     Address of the capability.
- * @param handle  Capability handle.
- */
-void cap_initialize(cap_t *cap, cap_handle_t handle)
-{
-	cap->state = CAP_STATE_FREE;
-	cap->handle = handle;
-	link_initialize(&cap->link);
-}
-
-/** Allocate the capability info structure
- *
- * @param task  Task for which to allocate the info structure.
- */
-void caps_task_alloc(task_t *task)
-{
-	task->cap_info = (cap_info_t *) malloc(sizeof(cap_info_t), 0);
-	task->cap_info->caps = malloc(sizeof(cap_t) * MAX_CAPS, 0);
-}
-
-/** Initialize the capability info structure
- *
- * @param task  Task for which to initialize the info structure.
- */
-void caps_task_init(task_t *task)
-{
-	mutex_initialize(&task->cap_info->lock, MUTEX_PASSIVE);
-
-	for (kobject_type_t t = 0; t < KOBJECT_TYPE_MAX; t++)
-		list_initialize(&task->cap_info->type_list[t]);
-
-	for (cap_handle_t h = 0; h < MAX_CAPS; h++)
-		cap_initialize(&task->cap_info->caps[h], h);
-}
-
-/** Deallocate the capability info structure
- *
- * @param task  Task from which to deallocate the info structure.
- */
-void caps_task_free(task_t *task)
-{
-	free(task->cap_info->caps);
-	free(task->cap_info);
-}
-
-/** Invoke callback function on task's capabilites of given type
- *
- * @param task  Task where the invocation should take place.
- * @param type  Kernel object type of the task's capabilities that will be
- *              subject to the callback invocation.
- * @param cb    Callback function.
- * @param arg   Argument for the callback function.
- *
- * @return True if the callback was called on all matching capabilities.
- * @return False if the callback was applied only partially.
- */
-bool caps_apply_to_kobject_type(task_t *task, kobject_type_t type,
-    bool (*cb)(cap_t *, void *), void *arg)
-{
-	bool done = true;
-
-	mutex_lock(&task->cap_info->lock);
-	list_foreach_safe(task->cap_info->type_list[type], cur, next) {
-		cap_t *cap = list_get_instance(cur, cap_t, link);
-		done = cb(cap, arg);
-		if (!done)
-			break;
-	}
-	mutex_unlock(&task->cap_info->lock);
-
-	return done;
-}
-
-/** Get capability using capability handle
- *
- * @param task    Task whose capability to get.
- * @param handle  Capability handle of the desired capability.
- * @param state   State in which the capability must be.
- *
- * @return Address of the desired capability if it exists and its state matches.
- * @return NULL if no such capability exists or it's in a different state.
- */
-static cap_t *cap_get(task_t *task, cap_handle_t handle, cap_state_t state)
-{
-	assert(mutex_locked(&task->cap_info->lock));
-
-	if ((handle < 0) || (handle >= MAX_CAPS))
-		return NULL;
-	if (task->cap_info->caps[handle].state != state)
-		return NULL;
-	return &task->cap_info->caps[handle];
-}
-
-/** Allocate new capability
- *
- * @param task  Task for which to allocate the new capability.
- *
- * @return New capability handle on success.
- * @return Negative error code in case of error.
- */
-cap_handle_t cap_alloc(task_t *task)
-{
-	mutex_lock(&task->cap_info->lock);
-	for (cap_handle_t handle = 0; handle < MAX_CAPS; handle++) {
-		cap_t *cap = &task->cap_info->caps[handle];
-		/* See if the capability should be garbage-collected */
-		if (cap->state == CAP_STATE_PUBLISHED &&
-		    cap->kobject->ops->reclaim &&
-		    cap->kobject->ops->reclaim(cap->kobject)) {
-			kobject_t *kobj = cap_unpublish_locked(task, handle,
-			    cap->kobject->type);
-			kobject_put(kobj);
-			cap_initialize(&task->cap_info->caps[handle], handle);
-		}
-		if (cap->state == CAP_STATE_FREE) {
-			cap->state = CAP_STATE_ALLOCATED;
-			mutex_unlock(&task->cap_info->lock);
-			return handle;
-		}
-	}
-	mutex_unlock(&task->cap_info->lock);
-
-	return ELIMIT;
-}
-
-/** Publish allocated capability
- *
- * The kernel object is moved into the capability. In other words, its reference
- * is handed over to the capability. Once published, userspace can access and
- * manipulate the capability.
- *
- * @param task    Task in which to publish the capability.
- * @param handle  Capability handle.
- * @param kobj    Kernel object.
- */
-void
-cap_publish(task_t *task, cap_handle_t handle, kobject_t *kobj)
-{
-	mutex_lock(&task->cap_info->lock);
-	cap_t *cap = cap_get(task, handle, CAP_STATE_ALLOCATED);
-	assert(cap);
-	cap->state = CAP_STATE_PUBLISHED;
-	/* Hand over kobj's reference to cap */
-	cap->kobject = kobj;
-	list_append(&cap->link, &task->cap_info->type_list[kobj->type]);
-	mutex_unlock(&task->cap_info->lock);
-}
-
-static kobject_t *
-cap_unpublish_locked(task_t *task, cap_handle_t handle, kobject_type_t type)
-{
-	kobject_t *kobj = NULL;
-
-	cap_t *cap = cap_get(task, handle, CAP_STATE_PUBLISHED);
-	if (cap) {
-		if (cap->kobject->type == type) {
-			/* Hand over cap's reference to kobj */
-			kobj = cap->kobject;
-			cap->kobject = NULL;
-			list_remove(&cap->link);
-			cap->state = CAP_STATE_ALLOCATED;
-		}
-	}
-
-	return kobj;
-}
-
-/** Unpublish published capability
- *
- * The kernel object is moved out of the capability. In other words, the
- * capability's reference to the objects is handed over to the kernel object
- * pointer returned by this function. Once unpublished, the capability does not
- * refer to any kernel object anymore.
- *
- * @param task    Task in which to unpublish the capability.
- * @param handle  Capability handle.
- * @param type    Kernel object type of the object associated with the
- *                capability.
- */
-kobject_t *cap_unpublish(task_t *task, cap_handle_t handle, kobject_type_t type)
-{
-
-	mutex_lock(&task->cap_info->lock);
-	kobject_t *kobj = cap_unpublish_locked(task, handle, type);
-	mutex_unlock(&task->cap_info->lock);
-
-	return kobj;
-}
-
-/** Free allocated capability
- *
- * @param task    Task in which to free the capability.
- * @param handle  Capability handle.
- */
-void cap_free(task_t *task, cap_handle_t handle)
-{
-	assert(handle >= 0);
-	assert(handle < MAX_CAPS);
-	assert(task->cap_info->caps[handle].state == CAP_STATE_ALLOCATED);
-
-	mutex_lock(&task->cap_info->lock);
-	cap_initialize(&task->cap_info->caps[handle], handle);
-	mutex_unlock(&task->cap_info->lock);
-}
-
-/** Initialize kernel object
- *
- * @param kobj  Kernel object to initialize.
- * @param type  Type of the kernel object.
- * @param raw   Raw pointer to the encapsulated object.
- * @param ops   Pointer to kernel object operations for the respective type.
- */
-void kobject_initialize(kobject_t *kobj, kobject_type_t type, void *raw,
-    kobject_ops_t *ops)
-{
-	atomic_set(&kobj->refcnt, 1);
-	kobj->type = type;
-	kobj->raw = raw;
-	kobj->ops = ops;
-}
-
-/** Get new reference to kernel object from capability
- *
- * @param task    Task from which to get the reference.
- * @param handle  Capability handle.
- * @param type    Kernel object type of the object associated with the
- *                capability referenced by handle.
- *
- * @return Kernel object with incremented reference count on success.
- * @return NULL if there is no matching capability or kernel object.
- */
-kobject_t *
-kobject_get(struct task *task, cap_handle_t handle, kobject_type_t type)
-{
-	kobject_t *kobj = NULL;
-
-	mutex_lock(&task->cap_info->lock);
-	cap_t *cap = cap_get(task, handle, CAP_STATE_PUBLISHED);
-	if (cap) {
-		if (cap->kobject->type == type) {
-			kobj = cap->kobject;
-			atomic_inc(&kobj->refcnt);
-		}
-	}
-	mutex_unlock(&task->cap_info->lock);
-
-	return kobj;
-}
-
-/** Record new reference
- *
- * @param kobj  Kernel object from which the new reference is created.
- */
-void kobject_add_ref(kobject_t *kobj)
-{
-	atomic_inc(&kobj->refcnt);
-}
-
-/** Drop reference to kernel object
- *
- * The encapsulated object and the kobject_t wrapper are both destroyed when the
- * last reference is dropped.
- *
- * @param kobj  Kernel object whose reference to drop.
- */
-void kobject_put(kobject_t *kobj)
-{
-	if (atomic_postdec(&kobj->refcnt) == 1) {
-		kobj->ops->destroy(kobj->raw);
-		free(kobj);
-	}
-}
-
-/** @}
- */
Index: kernel/generic/src/console/kconsole.c
===================================================================
--- kernel/generic/src/console/kconsole.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/console/kconsole.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -55,4 +55,5 @@
 #include <str.h>
 #include <sysinfo/sysinfo.h>
+#include <ddi/device.h>
 #include <symtab.h>
 #include <errno.h>
Index: kernel/generic/src/ddi/device.c
===================================================================
--- kernel/generic/src/ddi/device.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
+++ kernel/generic/src/ddi/device.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2006 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericddi
+ * @{
+ */
+/**
+ * @file
+ * @brief Device numbers.
+ */
+
+#include <assert.h>
+#include <typedefs.h>
+#include <ddi/device.h>
+#include <atomic.h>
+#include <debug.h>
+
+static atomic_t last;
+
+/** Assign new device number.
+ *
+ * @return Unique device number.
+ */
+devno_t device_assign_devno(void)
+{
+	devno_t devno = (devno_t) atomic_postinc(&last);
+	assert(devno >= 0);
+	
+	return devno;
+}
+
+sysarg_t sys_device_assign_devno(void)
+{
+	return (sysarg_t) device_assign_devno();
+}
+
+/** @}
+ */
Index: kernel/generic/src/ddi/irq.c
===================================================================
--- kernel/generic/src/ddi/irq.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ddi/irq.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -32,9 +32,34 @@
 /**
  * @file
- * @brief IRQ dispatcher
- *
- * This file provides means of connecting IRQs with respective device drivers
- * and logic for dispatching interrupts to IRQ handlers defined by those
- * drivers.
+ * @brief IRQ dispatcher.
+ *
+ * This file provides means of connecting IRQs with particular devices and logic
+ * for dispatching interrupts to IRQ handlers defined by those devices.
+ *
+ * This code is designed to support:
+ * - multiple devices sharing single IRQ
+ * - multiple IRQs per single device
+ * - multiple instances of the same device
+ *
+ *
+ * Note about architectures.
+ *
+ * Some architectures have the term IRQ well defined. Examples of such
+ * architectures include amd64, ia32 and mips32. Some other architectures, such
+ * as sparc64, don't use the term at all. In those cases, we boldly step forward
+ * and define what an IRQ is.
+ *
+ * The implementation is generic enough and still allows the architectures to
+ * use the hardware layout effectively.  For instance, on amd64 and ia32, where
+ * there is only 16 IRQs, the irq_hash_table can be optimized to a
+ * one-dimensional array. Next, when it is known that the IRQ numbers (aka
+ * INR's) are unique, the claim functions can always return IRQ_ACCEPT.
+ *
+ *
+ * Note about the irq_hash_table.
+ *
+ * The hash table is configured to use two keys: inr and devno.  However, the
+ * hash index is computed only from inr. Moreover, if devno is -1, the match is
+ * based on the return value of the claim() function instead of on devno.
  */
 
@@ -49,7 +74,8 @@
 #include <arch.h>
 
-slab_cache_t *irq_slab = NULL;
-
-/** Spinlock protecting the kernel IRQ hash table
+#define KEY_INR    0
+#define KEY_DEVNO  1
+
+/** Spinlock protecting the kernel IRQ hash table.
  *
  * This lock must be taken only when interrupts are disabled.
@@ -61,5 +87,5 @@
 static hash_table_t irq_kernel_hash_table;
 
-/** Spinlock protecting the uspace IRQ hash table
+/** Spinlock protecting the uspace IRQ hash table.
  *
  * This lock must be taken only when interrupts are disabled.
@@ -68,7 +94,11 @@
 IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
 
-/** The uspace IRQ hash table */
+/** The uspace IRQ hash table. */
 hash_table_t irq_uspace_hash_table;
 
+/**
+ * Hash table operations for cases when we know that there will be collisions
+ * between different keys.
+ */
 static size_t irq_ht_hash(sysarg_t *key);
 static bool irq_ht_compare(sysarg_t *key, size_t keys, link_t *item);
@@ -81,14 +111,29 @@
 };
 
-/** Number of buckets in either of the hash tables */
+/**
+ * Hash table operations for cases when we know that there will be no collisions
+ * between different keys.  However, there might be still collisions among
+ * elements with single key (sharing of one IRQ).
+ */
+static size_t irq_lin_hash(sysarg_t *key);
+static bool irq_lin_compare(sysarg_t *key, size_t keys, link_t *item);
+static void irq_lin_remove(link_t *item);
+
+static hash_table_operations_t irq_lin_ops = {
+	.hash = irq_lin_hash,
+	.compare = irq_lin_compare,
+	.remove_callback = irq_lin_remove,
+};
+
+/** Number of buckets in either of the hash tables. */
 static size_t buckets;
 
-/** Last valid INR */
+/** Last valid INR. */
 inr_t last_inr = 0;
 
-/** Initialize IRQ subsystem
- *
- * @param inrs    Numbers of unique IRQ numbers or INRs.
- * @param chains  Number of buckets in the hash table.
+/** Initialize IRQ subsystem.
+ *
+ * @param inrs   Numbers of unique IRQ numbers or INRs.
+ * @param chains Number of chains in the hash table.
  *
  */
@@ -98,15 +143,26 @@
 	last_inr = inrs - 1;
 
-	irq_slab = slab_cache_create("irq_t", sizeof(irq_t), 0, NULL, NULL,
-	    FRAME_ATOMIC);
-	assert(irq_slab);
-
-	hash_table_create(&irq_uspace_hash_table, chains, 2, &irq_ht_ops);
-	hash_table_create(&irq_kernel_hash_table, chains, 2, &irq_ht_ops);
-}
-
-/** Initialize one IRQ structure
- *
- * @param irq  Pointer to the IRQ structure to be initialized.
+	/*
+	 * Be smart about the choice of the hash table operations.  In cases in
+	 * which inrs equals the requested number of chains (i.e. where there is
+	 * no collision between different keys), we can use optimized set of
+	 * operations.
+	 */
+	if (inrs == chains) {
+		hash_table_create(&irq_uspace_hash_table, chains, 2,
+		    &irq_lin_ops);
+		hash_table_create(&irq_kernel_hash_table, chains, 2,
+		    &irq_lin_ops);
+	} else {
+		hash_table_create(&irq_uspace_hash_table, chains, 2,
+		    &irq_ht_ops);
+		hash_table_create(&irq_kernel_hash_table, chains, 2,
+		    &irq_ht_ops);
+	}
+}
+
+/** Initialize one IRQ structure.
+ *
+ * @param irq Pointer to the IRQ structure to be initialized.
  *
  */
@@ -116,15 +172,17 @@
 	link_initialize(&irq->link);
 	irq_spinlock_initialize(&irq->lock, "irq.lock");
+	link_initialize(&irq->notif_cfg.link);
 	irq->inr = -1;
+	irq->devno = -1;
 	
 	irq_initialize_arch(irq);
 }
 
-/** Register IRQ for device
+/** Register IRQ for device.
  *
  * The irq structure must be filled with information about the interrupt source
  * and with the claim() function pointer and handler() function pointer.
  *
- * @param irq  IRQ structure belonging to a device.
+ * @param irq IRQ structure belonging to a device.
  *
  */
@@ -132,6 +190,6 @@
 {
 	sysarg_t key[] = {
-		[IRQ_HT_KEY_INR] = (sysarg_t) irq->inr,
-		[IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_NO_CLAIM 
+		(sysarg_t) irq->inr,
+		(sysarg_t) irq->devno
 	};
 	
@@ -143,11 +201,13 @@
 }
 
-/** Search and lock the uspace IRQ hash table */
+/** Search and lock the uspace IRQ hash table.
+ *
+ */
 static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
 {
 	link_t *lnk;
 	sysarg_t key[] = {
-		[IRQ_HT_KEY_INR] = (sysarg_t) inr,
-		[IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_CLAIM
+		(sysarg_t) inr,
+		(sysarg_t) -1    /* Search will use claim() instead of devno */
 	};
 	
@@ -164,11 +224,13 @@
 }
 
-/** Search and lock the kernel IRQ hash table */
+/** Search and lock the kernel IRQ hash table.
+ *
+ */
 static irq_t *irq_dispatch_and_lock_kernel(inr_t inr)
 {
 	link_t *lnk;
 	sysarg_t key[] = {
-		[IRQ_HT_KEY_INR] = (sysarg_t) inr,
-		[IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_CLAIM
+		(sysarg_t) inr,
+		(sysarg_t) -1    /* Search will use claim() instead of devno */
 	};
 	
@@ -185,5 +247,5 @@
 }
 
-/** Dispatch the IRQ
+/** Dispatch the IRQ.
  *
  * We assume this function is only called from interrupt context (i.e. that
@@ -193,5 +255,5 @@
  * return with interrupts disabled and holding the respective structure.
  *
- * @param inr  Interrupt number (aka inr or irq).
+ * @param inr Interrupt number (aka inr or irq).
  *
  * @return IRQ structure of the respective device
@@ -223,8 +285,12 @@
 }
 
-/** Compute hash index for the key
- *
- * @param key  The first of the keys is inr and the second is mode. Only inr is
- *             used to compute the hash.
+/** Compute hash index for the key.
+ *
+ * This function computes hash index into the IRQ hash table for which there can
+ * be collisions between different INRs.
+ *
+ * The devno is not used to compute the hash.
+ *
+ * @param key The first of the keys is inr and the second is devno or -1.
  *
  * @return Index into the hash table.
@@ -233,21 +299,25 @@
 size_t irq_ht_hash(sysarg_t key[])
 {
-	inr_t inr = (inr_t) key[IRQ_HT_KEY_INR];
+	inr_t inr = (inr_t) key[KEY_INR];
 	return inr % buckets;
 }
 
-/** Compare hash table element with a key
- *
- * If mode is IRQ_HT_MODE_CLAIM, the result of the claim() function is used for
- * the match. Otherwise the key does not match.
+/** Compare hash table element with a key.
+ *
+ * There are two things to note about this function.  First, it is used for the
+ * more complex architecture setup in which there are way too many interrupt
+ * numbers (i.e. inr's) to arrange the hash table so that collisions occur only
+ * among same inrs of different devnos. So the explicit check for inr match must
+ * be done.  Second, if devno is -1, the second key (i.e. devno) is not used for
+ * the match and the result of the claim() function is used instead.
  *
  * This function assumes interrupts are already disabled.
  *
- * @param key   Keys (i.e. inr and mode).
- * @param keys  This is 2. 
- * @param item  The item to compare the key with.
- *
- * @return True on match
- * @return False on no match
+ * @param key  Keys (i.e. inr and devno).
+ * @param keys This is 2. 
+ * @param item The item to compare the key with.
+ *
+ * @return true on match
+ * @return false on no match
  *
  */
@@ -255,16 +325,17 @@
 {
 	irq_t *irq = hash_table_get_instance(item, irq_t, link);
-	inr_t inr = (inr_t) key[IRQ_HT_KEY_INR];
-	irq_ht_mode_t mode = (irq_ht_mode_t) key[IRQ_HT_KEY_MODE];
+	inr_t inr = (inr_t) key[KEY_INR];
+	devno_t devno = (devno_t) key[KEY_DEVNO];
 	
 	bool rv;
 	
 	irq_spinlock_lock(&irq->lock, false);
-	if (mode == IRQ_HT_MODE_CLAIM) {
+	if (devno == -1) {
 		/* Invoked by irq_dispatch_and_lock(). */
-		rv = ((irq->inr == inr) && (irq->claim(irq) == IRQ_ACCEPT));
+		rv = ((irq->inr == inr) &&
+		    (irq->claim(irq) == IRQ_ACCEPT));
 	} else {
 		/* Invoked by irq_find_and_lock(). */
-		rv = false;
+		rv = ((irq->inr == inr) && (irq->devno == devno));
 	}
 	
@@ -276,7 +347,7 @@
 }
 
-/** Unlock IRQ structure after hash_table_remove()
- *
- * @param lnk  Link in the removed and locked IRQ structure.
+/** Unlock IRQ structure after hash_table_remove().
+ *
+ * @param lnk Link in the removed and locked IRQ structure.
  */
 void irq_ht_remove(link_t *lnk)
@@ -287,4 +358,73 @@
 }
 
+/** Compute hash index for the key.
+ *
+ * This function computes hash index into the IRQ hash table for which there are
+ * no collisions between different INRs.
+ *
+ * @param key The first of the keys is inr and the second is devno or -1.
+ *
+ * @return Index into the hash table.
+ *
+ */
+size_t irq_lin_hash(sysarg_t key[])
+{
+	inr_t inr = (inr_t) key[KEY_INR];
+	return inr;
+}
+
+/** Compare hash table element with a key.
+ *
+ * There are two things to note about this function.  First, it is used for the
+ * less complex architecture setup in which there are not too many interrupt
+ * numbers (i.e. inr's) to arrange the hash table so that collisions occur only
+ * among same inrs of different devnos. So the explicit check for inr match is
+ * not done.  Second, if devno is -1, the second key (i.e. devno) is not used
+ * for the match and the result of the claim() function is used instead.
+ *
+ * This function assumes interrupts are already disabled.
+ *
+ * @param key  Keys (i.e. inr and devno).
+ * @param keys This is 2. 
+ * @param item The item to compare the key with.
+ *
+ * @return true on match
+ * @return false on no match
+ *
+ */
+bool irq_lin_compare(sysarg_t key[], size_t keys, link_t *item)
+{
+	irq_t *irq = list_get_instance(item, irq_t, link);
+	devno_t devno = (devno_t) key[KEY_DEVNO];
+	bool rv;
+	
+	irq_spinlock_lock(&irq->lock, false);
+	if (devno == -1) {
+		/* Invoked by irq_dispatch_and_lock() */
+		rv = (irq->claim(irq) == IRQ_ACCEPT);
+	} else {
+		/* Invoked by irq_find_and_lock() */
+		rv = (irq->devno == devno);
+	}
+	
+	/* unlock only on non-match */
+	if (!rv)
+		irq_spinlock_unlock(&irq->lock, false);
+	
+	return rv;
+}
+
+/** Unlock IRQ structure after hash_table_remove().
+ *
+ * @param lnk Link in the removed and locked IRQ structure.
+ *
+ */
+void irq_lin_remove(link_t *lnk)
+{
+	irq_t *irq __attribute__((unused))
+	    = hash_table_get_instance(lnk, irq_t, link);
+	irq_spinlock_unlock(&irq->lock, false);
+}
+
 /** @}
  */
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/ipc.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -43,5 +43,4 @@
 #include <synch/waitq.h>
 #include <ipc/ipc.h>
-#include <ipc/ipcrsc.h>
 #include <abi/ipc/methods.h>
 #include <ipc/kbox.h>
@@ -59,5 +58,4 @@
 #include <arch/interrupt.h>
 #include <ipc/irq.h>
-#include <cap/cap.h>
 
 static void ipc_forget_call(call_t *);
@@ -66,8 +64,6 @@
 answerbox_t *ipc_phone_0 = NULL;
 
-static slab_cache_t *call_slab;
-static slab_cache_t *answerbox_slab;
-
-slab_cache_t *phone_slab = NULL; 
+static slab_cache_t *ipc_call_slab;
+static slab_cache_t *ipc_answerbox_slab;
 
 /** Initialize a call structure.
@@ -97,5 +93,5 @@
 		if (call->buffer)
 			free(call->buffer);
-		slab_free(call_slab, call);
+		slab_free(ipc_call_slab, call);
 	}
 }
@@ -114,5 +110,5 @@
 call_t *ipc_call_alloc(unsigned int flags)
 {
-	call_t *call = slab_alloc(call_slab, flags);
+	call_t *call = slab_alloc(ipc_call_slab, flags);
 	if (call) {
 		_ipc_call_init(call);
@@ -149,4 +145,5 @@
 	list_initialize(&box->answers);
 	list_initialize(&box->irq_notifs);
+	list_initialize(&box->irq_list);
 	box->task = task;
 }
@@ -154,9 +151,7 @@
 /** Connect a phone to an answerbox.
  *
- * This function must be passed a reference to phone->kobject.
- *
- * @param phone  Initialized phone structure.
- * @param box    Initialized answerbox structure.
- * @return       True if the phone was connected, false otherwise.
+ * @param phone Initialized phone structure.
+ * @param box   Initialized answerbox structure.
+ * @return      True if the phone was connected, false otherwise.
  */
 bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
@@ -171,5 +166,4 @@
 		phone->state = IPC_PHONE_CONNECTED;
 		phone->callee = box;
-		/* Pass phone->kobject reference to box->connected_phones */
 		list_append(&phone->link, &box->connected_phones);
 	}
@@ -177,9 +171,4 @@
 	irq_spinlock_unlock(&box->lock, true);
 	mutex_unlock(&phone->lock);
-
-	if (!active) {
-		/* We still have phone->kobject's reference; drop it */
-		kobject_put(phone->kobject);
-	}
 
 	return active;
@@ -199,5 +188,4 @@
 	phone->state = IPC_PHONE_FREE;
 	atomic_set(&phone->active_calls, 0);
-	phone->kobject = NULL;
 }
 
@@ -212,5 +200,5 @@
 int ipc_call_sync(phone_t *phone, call_t *request)
 {
-	answerbox_t *mybox = slab_alloc(answerbox_slab, 0);
+	answerbox_t *mybox = slab_alloc(ipc_answerbox_slab, 0);
 	ipc_answerbox_init(mybox, TASK);
 	
@@ -220,5 +208,5 @@
 	int rc = ipc_call(phone, request);
 	if (rc != EOK) {
-		slab_free(answerbox_slab, mybox);
+		slab_free(ipc_answerbox_slab, mybox);
 		return rc;
 	}
@@ -267,5 +255,5 @@
 	assert(!answer || request == answer);
 	
-	slab_free(answerbox_slab, mybox);
+	slab_free(ipc_answerbox_slab, mybox);
 	return rc;
 }
@@ -465,7 +453,4 @@
 		list_remove(&phone->link);
 		irq_spinlock_unlock(&box->lock, true);
-
-		/* Drop the answerbox reference */
-		kobject_put(phone->kobject);
 		
 		call_t *call = ipc_call_alloc(0);
@@ -670,6 +655,4 @@
 
 			task_release(phone->caller);
-
-			kobject_put(phone->kobject);
 			
 			/* Must start again */
@@ -678,5 +661,4 @@
 		
 		mutex_unlock(&phone->lock);
-		kobject_put(phone->kobject);
 	}
 	
@@ -725,5 +707,5 @@
 		 * Nota bene: there may still be answers waiting for pick up.
 		 */
-		spinlock_unlock(&TASK->active_calls_lock);
+		spinlock_unlock(&TASK->active_calls_lock);	
 		return;	
 	}
@@ -738,5 +720,5 @@
 		 * call on the list.
 		 */
-		spinlock_unlock(&TASK->active_calls_lock);
+		spinlock_unlock(&TASK->active_calls_lock);	
 		goto restart;
 	}
@@ -745,50 +727,4 @@
 
 	goto restart;
-}
-
-static bool phone_cap_wait_cb(cap_t *cap, void *arg)
-{
-	phone_t *phone = cap->kobject->phone;
-	bool *restart = (bool *) arg;
-
-	mutex_lock(&phone->lock);
-	if ((phone->state == IPC_PHONE_HUNGUP) &&
-	    (atomic_get(&phone->active_calls) == 0)) {
-		phone->state = IPC_PHONE_FREE;
-		phone->callee = NULL;
-	}
-
-	/*
-	 * We might have had some IPC_PHONE_CONNECTING phones at the beginning
-	 * of ipc_cleanup(). Depending on whether these were forgotten or
-	 * answered, they will eventually enter the IPC_PHONE_FREE or
-	 * IPC_PHONE_CONNECTED states, respectively.  In the latter case, the
-	 * other side may slam the open phones at any time, in which case we
-	 * will get an IPC_PHONE_SLAMMED phone.
-	 */
-	if ((phone->state == IPC_PHONE_CONNECTED) ||
-	    (phone->state == IPC_PHONE_SLAMMED)) {
-		mutex_unlock(&phone->lock);
-		ipc_phone_hangup(phone);
-		/*
-		 * Now there may be one extra active call, which needs to be
-		 * forgotten.
-		 */
-		ipc_forget_all_active_calls();
-		*restart = true;
-		return false;
-	}
-
-	/*
-	 * If the hangup succeeded, it has sent a HANGUP message, the IPC is now
-	 * in HUNGUP state, we wait for the reply to come
-	 */
-	if (phone->state != IPC_PHONE_FREE) {
-		mutex_unlock(&phone->lock);
-		return false;
-	}
-
-	mutex_unlock(&phone->lock);
-	return true;
 }
 
@@ -797,5 +733,5 @@
 {
 	call_t *call;
-	bool restart;
+	size_t i;
 
 restart:
@@ -804,13 +740,51 @@
 	 * Locking is needed as there may be connection handshakes in progress.
 	 */
-	restart = false;
-	if (caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_PHONE,
-	    phone_cap_wait_cb, &restart)) {
-		/* Got into cleanup */
+	for (i = 0; i < IPC_MAX_PHONES; i++) {
+		phone_t *phone = &TASK->phones[i];
+
+		mutex_lock(&phone->lock);	
+		if ((phone->state == IPC_PHONE_HUNGUP) &&
+		    (atomic_get(&phone->active_calls) == 0)) {
+			phone->state = IPC_PHONE_FREE;
+			phone->callee = NULL;
+		}
+
+		/*
+		 * We might have had some IPC_PHONE_CONNECTING phones at the
+		 * beginning of ipc_cleanup(). Depending on whether these were
+		 * forgotten or answered, they will eventually enter the
+		 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
+		 * In the latter case, the other side may slam the open phones
+		 * at any time, in which case we will get an IPC_PHONE_SLAMMED
+		 * phone.
+		 */
+		if ((phone->state == IPC_PHONE_CONNECTED) ||
+		    (phone->state == IPC_PHONE_SLAMMED)) {
+			mutex_unlock(&phone->lock);
+			ipc_phone_hangup(phone);
+			/*
+			 * Now there may be one extra active call, which needs
+			 * to be forgotten.
+			 */
+			ipc_forget_all_active_calls();
+			goto restart;
+		}
+
+		/*
+		 * If the hangup succeeded, it has sent a HANGUP message, the
+		 * IPC is now in HUNGUP state, we wait for the reply to come
+		 */
+		if (phone->state != IPC_PHONE_FREE) {
+			mutex_unlock(&phone->lock);
+			break;
+		}
+
+		mutex_unlock(&phone->lock);
+	}
+		
+	/* Got into cleanup */
+	if (i == IPC_MAX_PHONES)
 		return;
-	}
-	if (restart)
-		goto restart;
-	
+		
 	call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
 	    SYNCH_FLAGS_NONE);
@@ -821,16 +795,4 @@
 	ipc_call_free(call);
 	goto restart;
-}
-
-static bool phone_cap_cleanup_cb(cap_t *cap, void *arg)
-{
-	ipc_phone_hangup(cap->kobject->phone);
-	return true;
-}
-
-static bool irq_cap_cleanup_cb(cap_t *cap, void *arg)
-{
-	ipc_irq_unsubscribe(&TASK->answerbox, cap->handle);
-	return true;
 }
 
@@ -854,13 +816,12 @@
 
 	/* Disconnect all our phones ('ipc_phone_hangup') */
-	caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_PHONE,
-	    phone_cap_cleanup_cb, NULL);
+	for (size_t i = 0; i < IPC_MAX_PHONES; i++)
+		ipc_phone_hangup(&TASK->phones[i]);
 	
 	/* Unsubscribe from any event notifications. */
 	event_cleanup_answerbox(&TASK->answerbox);
 	
-	/* Disconnect all connected IRQs */
-	caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_IRQ, irq_cap_cleanup_cb,
-	    NULL);
+	/* Disconnect all connected irqs */
+	ipc_irq_cleanup(&TASK->answerbox);
 	
 	/* Disconnect all phones connected to our regular answerbox */
@@ -886,10 +847,8 @@
 void ipc_init(void)
 {
-	call_slab = slab_cache_create("call_t", sizeof(call_t), 0, NULL,
+	ipc_call_slab = slab_cache_create("call_t", sizeof(call_t), 0, NULL,
 	    NULL, 0);
-	phone_slab = slab_cache_create("phone_t", sizeof(phone_t), 0, NULL,
-	    NULL, 0);
-	answerbox_slab = slab_cache_create("answerbox_t", sizeof(answerbox_t),
-	    0, NULL, NULL, 0);
+	ipc_answerbox_slab = slab_cache_create("answerbox_t",
+	    sizeof(answerbox_t), 0, NULL, NULL, 0);
 }
 
@@ -926,39 +885,4 @@
 }
 
-static bool print_task_phone_cb(cap_t *cap, void *arg)
-{
-	phone_t *phone = cap->kobject->phone;
-
-	mutex_lock(&phone->lock);
-	if (phone->state != IPC_PHONE_FREE) {
-		printf("%-11d %7" PRIun " ", cap->handle,
-		    atomic_get(&phone->active_calls));
-		
-		switch (phone->state) {
-		case IPC_PHONE_CONNECTING:
-			printf("connecting");
-			break;
-		case IPC_PHONE_CONNECTED:
-			printf("connected to %" PRIu64 " (%s)",
-			    phone->callee->task->taskid,
-			    phone->callee->task->name);
-			break;
-		case IPC_PHONE_SLAMMED:
-			printf("slammed by %p", phone->callee);
-			break;
-		case IPC_PHONE_HUNGUP:
-			printf("hung up by %p", phone->callee);
-			break;
-		default:
-			break;
-		}
-		
-		printf("\n");
-	}
-	mutex_unlock(&phone->lock);
-
-	return true;
-}
-
 /** List answerbox contents.
  *
@@ -970,17 +894,53 @@
 	irq_spinlock_lock(&tasks_lock, true);
 	task_t *task = task_find_by_id(taskid);
+	
 	if (!task) {
 		irq_spinlock_unlock(&tasks_lock, true);
 		return;
 	}
-	task_hold(task);
-	irq_spinlock_unlock(&tasks_lock, true);
-	
-	printf("[phone cap] [calls] [state\n");
-	
-	caps_apply_to_kobject_type(task, KOBJECT_TYPE_PHONE,
-	    print_task_phone_cb, NULL);
-	
-	irq_spinlock_lock(&task->lock, true);
+	
+	/* Hand-over-hand locking */
+	irq_spinlock_exchange(&tasks_lock, &task->lock);
+	
+	printf("[phone id] [calls] [state\n");
+	
+	size_t i;
+	for (i = 0; i < IPC_MAX_PHONES; i++) {
+		if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
+			printf("%-10zu (mutex busy)\n", i);
+			continue;
+		}
+		
+		if (task->phones[i].state != IPC_PHONE_FREE) {
+			printf("%-10zu %7" PRIun " ", i,
+			    atomic_get(&task->phones[i].active_calls));
+			
+			switch (task->phones[i].state) {
+			case IPC_PHONE_CONNECTING:
+				printf("connecting");
+				break;
+			case IPC_PHONE_CONNECTED:
+				printf("connected to %" PRIu64 " (%s)",
+				    task->phones[i].callee->task->taskid,
+				    task->phones[i].callee->task->name);
+				break;
+			case IPC_PHONE_SLAMMED:
+				printf("slammed by %p",
+				    task->phones[i].callee);
+				break;
+			case IPC_PHONE_HUNGUP:
+				printf("hung up by %p",
+				    task->phones[i].callee);
+				break;
+			default:
+				break;
+			}
+			
+			printf("\n");
+		}
+		
+		mutex_unlock(&task->phones[i].lock);
+	}
+	
 	irq_spinlock_lock(&task->answerbox.lock, false);
 	
@@ -1004,6 +964,4 @@
 	irq_spinlock_unlock(&task->answerbox.lock, false);
 	irq_spinlock_unlock(&task->lock, true);
-
-	task_release(task);
 }
 
Index: kernel/generic/src/ipc/ipcrsc.c
===================================================================
--- kernel/generic/src/ipc/ipcrsc.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/ipcrsc.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -39,5 +39,5 @@
  *
  * The pattern of usage of the resources is:
- * - allocate empty phone capability slot, connect | deallocate slot
+ * - allocate empty phone slot, connect | deallocate slot
  * - disconnect connected phone (some messages might be on the fly)
  * - find phone in slot and send a message using phone
@@ -53,5 +53,5 @@
  *   atomic on all platforms)
  *
- * - To find an empty phone capability slot, the TASK must be locked
+ * - To find an empty phone slot, the TASK must be locked
  * - To answer a message, the answerbox must be locked
  * - The locking of phone and answerbox is done at the ipc_ level.
@@ -77,10 +77,10 @@
  *
  * *** Connect_to_me ***
- * The caller sends IPC_M_CONNECT_TO_ME.
+ * The caller sends IPC_M_CONNECT_TO_ME. 
  * The server receives an automatically opened phoneid. If it accepts
- * (RETVAL=0), it can use the phoneid immediately.  Possible race condition can
- * arise, when the client receives messages from new connection before getting
- * response for connect_to_me message. Userspace should implement handshake
- * protocol that would control it.
+ * (RETVAL=0), it can use the phoneid immediately. 
+ * Possible race condition can arise, when the client receives messages from new
+ * connection before getting response for connect_to_me message. Userspace
+ * should implement handshake protocol that would control it.
  *
  * Phone hangup
@@ -89,19 +89,18 @@
  * - The phone is disconnected (no more messages can be sent over this phone),
  *   all in-progress messages are correctly handled. The answerbox receives
- *   IPC_M_PHONE_HUNGUP call from the phone that hung up. When all async calls
- *   are answered, the phone is deallocated.
+ *   IPC_M_PHONE_HUNGUP call from the phone that hung up. When all async
+ *   calls are answered, the phone is deallocated.
  *
  * *** The answerbox hangs up (ipc_answer(EHANGUP))
- * - The phone is disconnected. EHANGUP response code is sent to the calling
- *   task. All new calls through this phone get a EHUNGUP error code, the task
- *   is expected to send an sys_ipc_hangup after cleaning up its internal
- *   structures.
- *
+ * - The phone is disconnected. EHANGUP response code is sent
+ *   to the calling task. All new calls through this phone
+ *   get a EHUNGUP error code, the task is expected to
+ *   send an sys_ipc_hangup after cleaning up its internal structures.
  *
  * Call forwarding
  *
- * The call can be forwarded, so that the answer to call is passed directly to
- * the original sender. However, this poses special problems regarding routing
- * of hangup messages.
+ * The call can be forwarded, so that the answer to call is passed directly
+ * to the original sender. However, this poses special problems regarding 
+ * routing of hangup messages.
  *
  * sys_ipc_hangup -> IPC_M_PHONE_HUNGUP
@@ -134,6 +133,4 @@
 #include <assert.h>
 #include <abi/errno.h>
-#include <cap/cap.h>
-#include <mm/slab.h>
 
 /** Find call_t * in call table according to callid.
@@ -141,8 +138,9 @@
  * @todo Some speedup (hash table?)
  *
- * @param callid Userspace hash of the call. Currently it is the call structure
- *               kernel address.
- *
- * @return NULL on not found, otherwise pointer to the call structure.
+ * @param callid Userspace hash of the call. Currently it is the call
+ *               structure kernel address.
+ *
+ * @return NULL on not found, otherwise pointer to the call
+ *         structure.
  *
  */
@@ -164,63 +162,66 @@
 }
 
-static bool phone_reclaim(kobject_t *kobj)
-{
-	bool gc = false;
-
-	mutex_lock(&kobj->phone->lock);
-	if (kobj->phone->state == IPC_PHONE_HUNGUP &&
-	    atomic_get(&kobj->phone->active_calls) == 0)
-		gc = true;
-	mutex_unlock(&kobj->phone->lock);
-
-	return gc;
-}
-
-static void phone_destroy(void *arg)
-{
-	phone_t *phone = (phone_t *) arg;
-	slab_free(phone_slab, phone);
-}
-
-static kobject_ops_t phone_kobject_ops = {
-	.reclaim = phone_reclaim,
-	.destroy = phone_destroy
-};
-
-
-/** Allocate new phone in the specified task.
- *
- * @param task  Task for which to allocate a new phone.
- *
- * @return  New phone capability handle.
- * @return  Negative error code if a new capability cannot be allocated.
- */
-cap_handle_t phone_alloc(task_t *task)
-{
-	cap_handle_t handle = cap_alloc(task);
-	if (handle >= 0) {
-		phone_t *phone = slab_alloc(phone_slab, FRAME_ATOMIC);
-		if (!phone) {
-			cap_free(TASK, handle);
-			return ENOMEM;
+/** Get phone from the current task by ID.
+ *
+ * @param phoneid Phone ID.
+ * @param phone   Place to store pointer to phone.
+ *
+ * @return EOK on success, EINVAL if ID is invalid.
+ *
+ */
+int phone_get(sysarg_t phoneid, phone_t **phone)
+{
+	if (phoneid >= IPC_MAX_PHONES)
+		return EINVAL;
+	
+	*phone = &TASK->phones[phoneid];
+	return EOK;
+}
+
+/** Allocate new phone slot in the specified task.
+ *
+ * @param task Task for which to allocate a new phone.
+ *
+ * @return New phone handle or -1 if the phone handle limit is
+ *         exceeded.
+ *
+ */
+int phone_alloc(task_t *task)
+{
+	irq_spinlock_lock(&task->lock, true);
+	
+	size_t i;
+	for (i = 0; i < IPC_MAX_PHONES; i++) {
+		phone_t *phone = &task->phones[i];
+
+		if ((phone->state == IPC_PHONE_HUNGUP) &&
+		    (atomic_get(&phone->active_calls) == 0))
+			phone->state = IPC_PHONE_FREE;
+		
+		if (phone->state == IPC_PHONE_FREE) {
+			phone->state = IPC_PHONE_CONNECTING;
+			break;
 		}
-		kobject_t *kobject = malloc(sizeof(kobject_t), FRAME_ATOMIC);
-		if (!kobject) {
-			cap_free(TASK, handle);
-			slab_free(phone_slab, phone);
-			return ENOMEM;
-		}
-
-		ipc_phone_init(phone, task);
-		phone->state = IPC_PHONE_CONNECTING;
-
-		kobject_initialize(kobject, KOBJECT_TYPE_PHONE, phone,
-		    &phone_kobject_ops);
-		phone->kobject = kobject;
-		
-		cap_publish(task, handle, kobject);
 	}
 	
-	return handle;
+	irq_spinlock_unlock(&task->lock, true);
+	
+	if (i == IPC_MAX_PHONES)
+		return -1;
+	
+	return i;
+}
+
+/** Mark a phone structure free.
+ *
+ * @param phone Phone structure to be marked free.
+ *
+ */
+static void phone_deallocp(phone_t *phone)
+{
+	assert(phone->state == IPC_PHONE_CONNECTING);
+	
+	/* Atomic operation */
+	phone->state = IPC_PHONE_FREE;
 }
 
@@ -229,36 +230,29 @@
  * All already sent messages will be correctly processed.
  *
- * @param handle Phone capability handle of the phone to be freed.
- *
- */
-void phone_dealloc(cap_handle_t handle)
-{
-	kobject_t *kobj = cap_unpublish(TASK, handle, KOBJECT_TYPE_PHONE);
-	if (!kobj)
-		return;
-	
-	assert(kobj->phone);
-	assert(kobj->phone->state == IPC_PHONE_CONNECTING);
-	
-	kobject_put(kobj);
-	cap_free(TASK, handle);
+ * @param phoneid Phone handle of the phone to be freed.
+ *
+ */
+void phone_dealloc(int phoneid)
+{
+	phone_deallocp(&TASK->phones[phoneid]);
 }
 
 /** Connect phone to a given answerbox.
  *
- * @param handle  Capability handle of the phone to be connected.
- * @param box     Answerbox to which to connect the phone.
+ * @param phoneid Phone handle to be connected.
+ * @param box     Answerbox to which to connect the phone handle.
  * @return        True if the phone was connected, false otherwise.
- */
-bool phone_connect(cap_handle_t handle, answerbox_t *box)
-{
-	kobject_t *phone_obj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
-	if (!phone_obj)
-		return false;
-	
-	assert(phone_obj->phone->state == IPC_PHONE_CONNECTING);
-	
-	/* Hand over phone_obj reference to the answerbox */
-	return ipc_phone_connect(phone_obj->phone, box);
+ *
+ * The procedure _enforces_ that the user first marks the phone
+ * busy (e.g. via phone_alloc) and then connects the phone, otherwise
+ * race condition may appear.
+ *
+ */
+bool phone_connect(int phoneid, answerbox_t *box)
+{
+	phone_t *phone = &TASK->phones[phoneid];
+	
+	assert(phone->state == IPC_PHONE_CONNECTING);
+	return ipc_phone_connect(phone, box);
 }
 
Index: kernel/generic/src/ipc/irq.c
===================================================================
--- kernel/generic/src/ipc/irq.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/irq.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -37,8 +37,7 @@
  *
  * This framework allows applications to subscribe to receive a notification
- * when an interrupt is detected. The application may provide a simple
- * 'top-half' handler as part of its registration, which can perform simple
- * operations (read/write port/memory, add information to notification IPC
- * message).
+ * when interrupt is detected. The application may provide a simple 'top-half'
+ * handler as part of its registration, which can perform simple operations
+ * (read/write port/memory, add information to notification IPC message).
  *
  * The structure of a notification message is as follows:
@@ -51,4 +50,23 @@
  * - in_phone_hash: interrupt counter (may be needed to assure correct order
  *                  in multithreaded drivers)
+ *
+ * Note on synchronization for ipc_irq_subscribe(), ipc_irq_unsubscribe(),
+ * ipc_irq_cleanup() and IRQ handlers:
+ *
+ *   By always taking all of the uspace IRQ hash table lock, IRQ structure lock
+ *   and answerbox lock, we can rule out race conditions between the
+ *   registration functions and also the cleanup function. Thus the observer can
+ *   either see the IRQ structure present in both the hash table and the
+ *   answerbox list or absent in both. Views in which the IRQ structure would be
+ *   linked in the hash table but not in the answerbox list, or vice versa, are
+ *   not possible.
+ *
+ *   By always taking the hash table lock and the IRQ structure lock, we can
+ *   rule out a scenario in which we would free up an IRQ structure, which is
+ *   still referenced by, for example, an IRQ handler. The locking scheme forces
+ *   us to lock the IRQ structure only after any progressing IRQs on that
+ *   structure are finished. Because we hold the hash table lock, we prevent new
+ *   IRQs from taking new references to the IRQ structure.
+ *
  */
 
@@ -66,5 +84,4 @@
 #include <print.h>
 #include <macros.h>
-#include <cap/cap.h>
 
 static void ranges_unmap(irq_pio_range_t *ranges, size_t rangecount)
@@ -101,5 +118,5 @@
 	}
 	
-	/* Rewrite the IRQ code addresses from physical to kernel virtual. */
+	/* Rewrite the pseudocode addresses from physical to kernel virtual. */
 	for (size_t i = 0; i < cmdcount; i++) {
 		uintptr_t addr;
@@ -159,7 +176,8 @@
 }
 
-/** Statically check the top-half IRQ code
- *
- * Check the top-half IRQ code for invalid or unsafe constructs.
+/** Statically check the top-half pseudocode
+ *
+ * Check the top-half pseudocode for invalid or unsafe
+ * constructs.
  *
  */
@@ -198,7 +216,7 @@
 }
 
-/** Free the top-half IRQ code.
- *
- * @param code Pointer to the top-half IRQ code.
+/** Free the top-half pseudocode.
+ *
+ * @param code Pointer to the top-half pseudocode.
  *
  */
@@ -213,9 +231,9 @@
 }
 
-/** Copy the top-half IRQ code from userspace into the kernel.
- *
- * @param ucode Userspace address of the top-half IRQ code.
- *
- * @return Kernel address of the copied IRQ code.
+/** Copy the top-half pseudocode from userspace into the kernel.
+ *
+ * @param ucode Userspace address of the top-half pseudocode.
+ *
+ * @return Kernel address of the copied pseudocode.
  *
  */
@@ -271,34 +289,22 @@
 }
 
-static void irq_destroy(void *arg)
-{
-	irq_t *irq = (irq_t *) arg;
-
-	/* Free up the IRQ code and associated structures. */
-	code_free(irq->notif_cfg.code);
-	slab_free(irq_slab, irq);
-}
-
-static kobject_ops_t irq_kobject_ops = {
-	.destroy = irq_destroy
-};
-
 /** Subscribe an answerbox as a receiving end for IRQ notifications.
  *
  * @param box     Receiving answerbox.
  * @param inr     IRQ number.
- * @param imethod Interface and method to be associated with the notification.
- * @param ucode   Uspace pointer to top-half IRQ code.
- *
- * @return  IRQ capability handle.
- * @return  Negative error code.
- *
- */
-int ipc_irq_subscribe(answerbox_t *box, inr_t inr, sysarg_t imethod,
-    irq_code_t *ucode)
+ * @param devno   Device number.
+ * @param imethod Interface and method to be associated with the
+ *                notification.
+ * @param ucode   Uspace pointer to top-half pseudocode.
+ *
+ * @return EOK on success or a negative error code.
+ *
+ */
+int ipc_irq_subscribe(answerbox_t *box, inr_t inr, devno_t devno,
+    sysarg_t imethod, irq_code_t *ucode)
 {
 	sysarg_t key[] = {
-		[IRQ_HT_KEY_INR] = (sysarg_t) inr,
-		[IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_NO_CLAIM
+		(sysarg_t) inr,
+		(sysarg_t) devno
 	};
 	
@@ -315,24 +321,10 @@
 	
 	/*
-	 * Allocate and populate the IRQ kernel object.
+	 * Allocate and populate the IRQ structure.
 	 */
-	cap_handle_t handle = cap_alloc(TASK);
-	if (handle < 0)
-		return handle;
-	
-	irq_t *irq = (irq_t *) slab_alloc(irq_slab, FRAME_ATOMIC);
-	if (!irq) {
-		cap_free(TASK, handle);
-		return ENOMEM;
-	}
-
-	kobject_t *kobject = malloc(sizeof(kobject_t), FRAME_ATOMIC);
-	if (!kobject) {
-		cap_free(TASK, handle);
-		slab_free(irq_slab, irq);
-		return ENOMEM;
-	}
+	irq_t *irq = malloc(sizeof(irq_t), 0);
 	
 	irq_initialize(irq);
+	irq->devno = devno;
 	irq->inr = inr;
 	irq->claim = ipc_irq_top_half_claim;
@@ -345,54 +337,164 @@
 	
 	/*
-	 * Insert the IRQ structure into the uspace IRQ hash table.
+	 * Enlist the IRQ structure in the uspace IRQ hash table and the
+	 * answerbox's list.
 	 */
 	irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
+	
+	link_t *hlp = hash_table_find(&irq_uspace_hash_table, key);
+	if (hlp) {
+		irq_t *hirq = hash_table_get_instance(hlp, irq_t, link);
+		
+		/* hirq is locked */
+		irq_spinlock_unlock(&hirq->lock, false);
+		code_free(code);
+		irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
+		
+		free(irq);
+		return EEXIST;
+	}
+	
+	/* Locking is not really necessary, but paranoid */
 	irq_spinlock_lock(&irq->lock, false);
-	
-	irq->notif_cfg.hashed_in = true;
+	irq_spinlock_lock(&box->irq_lock, false);
+	
 	hash_table_insert(&irq_uspace_hash_table, key, &irq->link);
-	
+	list_append(&irq->notif_cfg.link, &box->irq_list);
+	
+	irq_spinlock_unlock(&box->irq_lock, false);
 	irq_spinlock_unlock(&irq->lock, false);
 	irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
-
-	kobject_initialize(kobject, KOBJECT_TYPE_IRQ, irq, &irq_kobject_ops);
-	cap_publish(TASK, handle, kobject);
-	
-	return handle;
+	
+	return EOK;
 }
 
 /** Unsubscribe task from IRQ notification.
  *
- * @param box     Answerbox associated with the notification.
- * @param handle  IRQ capability handle.
+ * @param box   Answerbox associated with the notification.
+ * @param inr   IRQ number.
+ * @param devno Device number.
  *
  * @return EOK on success or a negative error code.
  *
  */
-int ipc_irq_unsubscribe(answerbox_t *box, int handle)
-{
-	kobject_t *kobj = cap_unpublish(TASK, handle, KOBJECT_TYPE_IRQ);
-	if (!kobj)
+int ipc_irq_unsubscribe(answerbox_t *box, inr_t inr, devno_t devno)
+{
+	sysarg_t key[] = {
+		(sysarg_t) inr,
+		(sysarg_t) devno
+	};
+	
+	if ((inr < 0) || (inr > last_inr))
+		return ELIMIT;
+	
+	irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
+	link_t *lnk = hash_table_find(&irq_uspace_hash_table, key);
+	if (!lnk) {
+		irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
 		return ENOENT;
-	
-	assert(kobj->irq->notif_cfg.answerbox == box);
-
+	}
+	
+	irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
+	
+	/* irq is locked */
+	irq_spinlock_lock(&box->irq_lock, false);
+	
+	assert(irq->notif_cfg.answerbox == box);
+	
+	/* Remove the IRQ from the answerbox's list. */
+	list_remove(&irq->notif_cfg.link);
+	
+	/*
+	 * We need to drop the IRQ lock now because hash_table_remove() will try
+	 * to reacquire it. That basically violates the natural locking order,
+	 * but a deadlock in hash_table_remove() is prevented by the fact that
+	 * we already held the IRQ lock and didn't drop the hash table lock in
+	 * the meantime.
+	 */
+	irq_spinlock_unlock(&irq->lock, false);
+	
+	/* Remove the IRQ from the uspace IRQ hash table. */
+	hash_table_remove(&irq_uspace_hash_table, key, 2);
+	
+	irq_spinlock_unlock(&box->irq_lock, false);
+	irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
+	
+	/* Free up the pseudo code and associated structures. */
+	code_free(irq->notif_cfg.code);
+	
+	/* Free up the IRQ structure. */
+	free(irq);
+	
+	return EOK;
+}
+
+/** Disconnect all IRQ notifications from an answerbox.
+ *
+ * This function is effective because the answerbox contains
+ * list of all irq_t structures that are subscribed to
+ * send notifications to it.
+ *
+ * @param box Answerbox for which we want to carry out the cleanup.
+ *
+ */
+void ipc_irq_cleanup(answerbox_t *box)
+{
+loop:
 	irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
-	irq_spinlock_lock(&kobj->irq->lock, false);
-	
-	if (kobj->irq->notif_cfg.hashed_in) {
-		/* Remove the IRQ from the uspace IRQ hash table. */
-		hash_table_remove_item(&irq_uspace_hash_table,
-		    &kobj->irq->link);
-		kobj->irq->notif_cfg.hashed_in = false;
-	}
-
-	/* kobj->irq->lock unlocked by the hash table remove_callback */
+	irq_spinlock_lock(&box->irq_lock, false);
+	
+	while (!list_empty(&box->irq_list)) {
+		DEADLOCK_PROBE_INIT(p_irqlock);
+		
+		irq_t *irq = list_get_instance(list_first(&box->irq_list), irq_t,
+		    notif_cfg.link);
+		
+		if (!irq_spinlock_trylock(&irq->lock)) {
+			/*
+			 * Avoid deadlock by trying again.
+			 */
+			irq_spinlock_unlock(&box->irq_lock, false);
+			irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
+			DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
+			goto loop;
+		}
+		
+		sysarg_t key[2];
+		key[0] = irq->inr;
+		key[1] = irq->devno;
+		
+		assert(irq->notif_cfg.answerbox == box);
+		
+		/* Unlist from the answerbox. */
+		list_remove(&irq->notif_cfg.link);
+		
+		/*
+		 * We need to drop the IRQ lock now because hash_table_remove()
+		 * will try to reacquire it. That basically violates the natural
+		 * locking order, but a deadlock in hash_table_remove() is
+		 * prevented by the fact that we already held the IRQ lock and
+		 * didn't drop the hash table lock in the meantime.
+		 */
+		irq_spinlock_unlock(&irq->lock, false);
+		
+		/* Remove from the hash table. */
+		hash_table_remove(&irq_uspace_hash_table, key, 2);
+		
+		/*
+		 * Release both locks so that we can free the pseudo code.
+		 */
+		irq_spinlock_unlock(&box->irq_lock, false);
+		irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
+		
+		code_free(irq->notif_cfg.code);
+		free(irq);
+		
+		/* Reacquire both locks before taking another round. */
+		irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
+		irq_spinlock_lock(&box->irq_lock, false);
+	}
+	
+	irq_spinlock_unlock(&box->irq_lock, false);
 	irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
-
-	kobject_put(kobj);
-	cap_free(TASK, handle);
-	
-	return EOK;
 }
 
@@ -414,10 +516,10 @@
 }
 
-/** Apply the top-half IRQ code to find out whether to accept the IRQ or not.
+/** Apply the top-half pseudo code to find out whether to accept the IRQ or not.
  *
  * @param irq IRQ structure.
  *
- * @return IRQ_ACCEPT if the interrupt is accepted by the IRQ code.
- * @return IRQ_DECLINE if the interrupt is not accepted byt the IRQ code.
+ * @return IRQ_ACCEPT if the interrupt is accepted by the
+ *         pseudocode, IRQ_DECLINE otherwise.
  *
  */
Index: kernel/generic/src/ipc/kbox.c
===================================================================
--- kernel/generic/src/ipc/kbox.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/kbox.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -206,5 +206,5 @@
  * cleanup code.
  *
- * @return Phone capability handle on success, or negative error code.
+ * @return Phone id on success, or negative error code.
  *
  */
@@ -236,19 +236,16 @@
 	}
 	
-	cap_handle_t phone_handle = phone_alloc(TASK);
-	if (phone_handle < 0) {
-		mutex_unlock(&task->kb.cleanup_lock);
-		return phone_handle;
-	}
-	
-	kobject_t *phone_obj = kobject_get(TASK, phone_handle,
-	    KOBJECT_TYPE_PHONE);
+	int newphid = phone_alloc(TASK);
+	if (newphid < 0) {
+		mutex_unlock(&task->kb.cleanup_lock);
+		return ELIMIT;
+	}
+	
 	/* Connect the newly allocated phone to the kbox */
-	/* Hand over phone_obj's reference to ipc_phone_connect() */
-	(void) ipc_phone_connect(phone_obj->phone, &task->kb.box);
+	(void) ipc_phone_connect(&TASK->phones[newphid], &task->kb.box);
 	
 	if (task->kb.thread != NULL) {
 		mutex_unlock(&task->kb.cleanup_lock);
-		return phone_handle;
+		return newphid;
 	}
 	
@@ -266,5 +263,5 @@
 	mutex_unlock(&task->kb.cleanup_lock);
 	
-	return phone_handle;
+	return newphid;
 }
 
Index: kernel/generic/src/ipc/ops/clnestab.c
===================================================================
--- kernel/generic/src/ipc/ops/clnestab.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
+++ kernel/generic/src/ipc/ops/clnestab.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericipc
+ * @{
+ */
+/** @file
+ */
+
+#include <ipc/sysipc_ops.h>
+#include <ipc/ipc.h>
+#include <synch/mutex.h>
+#include <abi/errno.h>
+
+static int request_preprocess(call_t *call, phone_t *phone)
+{
+	IPC_SET_ARG5(call->data, (sysarg_t) phone);
+
+	return EOK;	
+}
+
+static int answer_cleanup(call_t *answer, ipc_data_t *olddata)
+{
+	phone_t *phone = (phone_t *) IPC_GET_ARG5(*olddata);
+
+	mutex_lock(&phone->lock);
+	if (phone->state == IPC_PHONE_CONNECTED) {
+		irq_spinlock_lock(&phone->callee->lock, true);
+		list_remove(&phone->link);
+		phone->state = IPC_PHONE_SLAMMED;
+		irq_spinlock_unlock(&phone->callee->lock, true);
+	}
+	mutex_unlock(&phone->lock);
+
+	return EOK;
+}
+
+static int answer_preprocess(call_t *answer, ipc_data_t *olddata)
+{
+
+	if (IPC_GET_RETVAL(answer->data) != EOK) {
+		/*
+		 * The other party on the cloned phone rejected our request
+		 * for connection on the protocol level.  We need to break the
+		 * connection without sending IPC_M_HUNGUP back.
+		 */
+		answer_cleanup(answer, olddata);
+	}
+	
+	return EOK;
+}
+
+sysipc_ops_t ipc_m_clone_establish_ops = {
+	.request_preprocess = request_preprocess,
+	.request_forget = null_request_forget,
+	.request_process = null_request_process,
+	.answer_cleanup = answer_cleanup,
+	.answer_preprocess = answer_preprocess,
+	.answer_process = null_answer_process,
+};
+
+/** @}
+ */
Index: kernel/generic/src/ipc/ops/conctmeto.c
===================================================================
--- kernel/generic/src/ipc/ops/conctmeto.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/ops/conctmeto.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2006 Ondrej Palkovsky
- * Copyright (c) 2012 Jakub Jermar
+ * Copyright (c) 2012 Jakub Jermar 
  * All rights reserved.
  *
@@ -42,16 +42,13 @@
 static int request_preprocess(call_t *call, phone_t *phone)
 {
-	cap_handle_t phone_handle = phone_alloc(TASK);
+	int newphid = phone_alloc(TASK);
 
-	/* Remember the phone capability or the error. */
-	call->priv = phone_handle;
-	if (phone_handle < 0)
-		return phone_handle;
-
+	/* Remember the phoneid or the error. */
+	call->priv = newphid;
+	if (newphid < 0)
+		return ELIMIT;
+		
 	/* Set arg5 for server */
-	kobject_t *phone_obj = kobject_get(TASK, phone_handle,
-	    KOBJECT_TYPE_PHONE);
-	/* Hand over phone_obj's reference to ARG5 */
-	IPC_SET_ARG5(call->data, (sysarg_t) phone_obj->phone);
+	IPC_SET_ARG5(call->data, (sysarg_t) &TASK->phones[newphid]);
 
 	return EOK;
@@ -60,10 +57,5 @@
 static int request_forget(call_t *call)
 {
-	cap_handle_t phone_handle = (cap_handle_t) call->priv;
-	phone_dealloc(phone_handle);
-	/* Hand over reference from ARG5 to phone->kobject */
-	phone_t *phone = (phone_t *) IPC_GET_ARG5(call->data);
-	/* Drop phone_obj's reference */
-	kobject_put(phone->kobject);
+	phone_dealloc(call->priv);
 	return EOK;
 }
@@ -71,14 +63,9 @@
 static int answer_preprocess(call_t *answer, ipc_data_t *olddata)
 {
-	/* Hand over reference from ARG5 to phone */
 	phone_t *phone = (phone_t *) IPC_GET_ARG5(*olddata);
 
 	/* If the user accepted call, connect */
-	if (IPC_GET_RETVAL(answer->data) == EOK) {
-		/* Hand over reference from phone to the answerbox */
+	if (IPC_GET_RETVAL(answer->data) == EOK)
 		(void) ipc_phone_connect(phone, &TASK->answerbox);
-	} else {
-		kobject_put(phone->kobject);
-	}
 
 	return EOK;
@@ -87,16 +74,16 @@
 static int answer_process(call_t *answer)
 {
-	cap_handle_t phone_handle = (cap_handle_t) answer->priv;
+	int newphid = (int) answer->priv;
 
 	if (IPC_GET_RETVAL(answer->data)) {
-		if (phone_handle >= 0) {
+		if (newphid >= 0) {
 			/*
 			 * The phone was indeed allocated and now needs
 			 * to be deallocated.
 			 */
-			phone_dealloc(phone_handle);
+			phone_dealloc(newphid);
 		}
 	} else {
-		IPC_SET_ARG5(answer->data, phone_handle);
+		IPC_SET_ARG5(answer->data, newphid);
 	}
 	
Index: kernel/generic/src/ipc/ops/concttome.c
===================================================================
--- kernel/generic/src/ipc/ops/concttome.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/ops/concttome.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2006 Ondrej Palkovsky
- * Copyright (c) 2012 Jakub Jermar
+ * Copyright (c) 2012 Jakub Jermar 
  * All rights reserved.
  *
@@ -42,7 +42,7 @@
 static int request_process(call_t *call, answerbox_t *box)
 {
-	cap_handle_t phone_handle = phone_alloc(TASK);
+	int phoneid = phone_alloc(TASK);
 
-	IPC_SET_ARG5(call->data, phone_handle);
+	IPC_SET_ARG5(call->data, phoneid);
 	
 	return EOK;
@@ -51,8 +51,8 @@
 static int answer_cleanup(call_t *answer, ipc_data_t *olddata)
 {
-	cap_handle_t phone_handle = (cap_handle_t) IPC_GET_ARG5(*olddata);
+	int phoneid = (int) IPC_GET_ARG5(*olddata);
 
-	if (phone_handle >= 0)
-		phone_dealloc(phone_handle);
+	if (phoneid >= 0)
+		phone_dealloc(phoneid);
 
 	return EOK;
@@ -61,18 +61,15 @@
 static int answer_preprocess(call_t *answer, ipc_data_t *olddata)
 {
-	cap_handle_t phone_handle = (cap_handle_t) IPC_GET_ARG5(*olddata);
+	int phoneid = (int) IPC_GET_ARG5(*olddata);
 
 	if (IPC_GET_RETVAL(answer->data) != EOK) {
 		/* The connection was not accepted */
 		answer_cleanup(answer, olddata);
-	} else if (phone_handle >= 0) {
+	} else if (phoneid >= 0) {
 		/* The connection was accepted */
-		if (phone_connect(phone_handle, &answer->sender->answerbox)) {
-			/* Set 'phone hash' as ARG5 of response */
-			kobject_t *phone_obj = kobject_get(TASK, phone_handle,
-			    KOBJECT_TYPE_PHONE);
+		if (phone_connect(phoneid, &answer->sender->answerbox)) {
+			/* Set 'phone hash' as arg5 of response */
 			IPC_SET_ARG5(answer->data,
-			    (sysarg_t) phone_obj->phone);
-			kobject_put(phone_obj);
+			    (sysarg_t) &TASK->phones[phoneid]);
 		} else {
 			/* The answerbox is shutting down. */
Index: kernel/generic/src/ipc/ops/connclone.c
===================================================================
--- kernel/generic/src/ipc/ops/connclone.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
+++ kernel/generic/src/ipc/ops/connclone.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2012 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericipc
+ * @{
+ */
+/** @file
+ */
+
+#include <ipc/sysipc_ops.h>
+#include <ipc/ipc.h>
+#include <ipc/ipcrsc.h>
+#include <synch/mutex.h>
+#include <abi/errno.h>
+#include <arch.h>
+
+static void phones_lock(phone_t *p1, phone_t *p2)
+{
+	if (p1 < p2) {
+		mutex_lock(&p1->lock);
+		mutex_lock(&p2->lock);
+	} else if (p1 > p2) {
+		mutex_lock(&p2->lock);
+		mutex_lock(&p1->lock);
+	} else
+		mutex_lock(&p1->lock);
+}
+
+static void phones_unlock(phone_t *p1, phone_t *p2)
+{
+	mutex_unlock(&p1->lock);
+	if (p1 != p2)
+		mutex_unlock(&p2->lock);
+}
+
+static int request_preprocess(call_t *call, phone_t *phone)
+{
+	phone_t *cloned_phone;
+
+	if (phone_get(IPC_GET_ARG1(call->data), &cloned_phone) != EOK)
+		return ENOENT;
+		
+	phones_lock(cloned_phone, phone);
+		
+	if ((cloned_phone->state != IPC_PHONE_CONNECTED) ||
+	    phone->state != IPC_PHONE_CONNECTED) {
+		phones_unlock(cloned_phone, phone);
+		return EINVAL;
+	}
+		
+	/*
+	 * We can be pretty sure now that both tasks exist and we are
+	 * connected to them. As we continue to hold the phone locks,
+	 * we are effectively preventing them from finishing their
+	 * potential cleanup.
+	 *
+	 */
+	int newphid = phone_alloc(phone->callee->task);
+	if (newphid < 0) {
+		phones_unlock(cloned_phone, phone);
+		return ELIMIT;
+	}
+		
+	(void) ipc_phone_connect(&phone->callee->task->phones[newphid],
+	    cloned_phone->callee);
+	phones_unlock(cloned_phone, phone);
+		
+	/* Set the new phone for the callee. */
+	IPC_SET_ARG1(call->data, newphid);
+
+	return EOK;
+}
+
+static int answer_cleanup(call_t *answer, ipc_data_t *olddata)
+{
+	int phoneid = (int) IPC_GET_ARG1(*olddata);
+	phone_t *phone = &TASK->phones[phoneid];
+
+	/*
+	 * In this case, the connection was established at the request
+	 * time and therefore we need to slam the phone.  We don't
+	 * merely hangup as that would result in sending IPC_M_HUNGUP
+	 * to the third party on the other side of the cloned phone.
+	 */
+	mutex_lock(&phone->lock);
+	if (phone->state == IPC_PHONE_CONNECTED) {
+		irq_spinlock_lock(&phone->callee->lock, true);
+		list_remove(&phone->link);
+		phone->state = IPC_PHONE_SLAMMED;
+		irq_spinlock_unlock(&phone->callee->lock, true);
+	}
+	mutex_unlock(&phone->lock);
+
+	return EOK;
+}
+
+static int answer_preprocess(call_t *answer, ipc_data_t *olddata)
+{
+	if (IPC_GET_RETVAL(answer->data) != EOK) {
+		/*
+		 * The recipient of the cloned phone rejected the offer.
+		 */
+		answer_cleanup(answer, olddata);
+	}
+
+	return EOK;
+}
+
+sysipc_ops_t ipc_m_connection_clone_ops = {
+	.request_preprocess = request_preprocess,
+	.request_forget = null_request_forget,
+	.request_process = null_request_process,
+	.answer_cleanup = answer_cleanup,
+	.answer_preprocess = answer_preprocess,
+	.answer_process = null_answer_process,
+};
+
+/** @}
+ */
Index: kernel/generic/src/ipc/ops/stchngath.c
===================================================================
--- kernel/generic/src/ipc/ops/stchngath.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/ops/stchngath.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -43,26 +43,23 @@
 static int request_preprocess(call_t *call, phone_t *phone)
 {
+	phone_t *sender_phone;
 	task_t *other_task_s;
 
-	kobject_t *sender_obj = kobject_get(TASK, IPC_GET_ARG5(call->data),
-	    KOBJECT_TYPE_PHONE);
-	if (!sender_obj)
+	if (phone_get(IPC_GET_ARG5(call->data), &sender_phone) != EOK)
 		return ENOENT;
 
-	mutex_lock(&sender_obj->phone->lock);
-	if (sender_obj->phone->state != IPC_PHONE_CONNECTED) {
-		mutex_unlock(&sender_obj->phone->lock);
-		kobject_put(sender_obj);
+	mutex_lock(&sender_phone->lock);
+	if (sender_phone->state != IPC_PHONE_CONNECTED) {
+		mutex_unlock(&sender_phone->lock);
 		return EINVAL;
 	}
 
-	other_task_s = sender_obj->phone->callee->task;
+	other_task_s = sender_phone->callee->task;
 
-	mutex_unlock(&sender_obj->phone->lock);
+	mutex_unlock(&sender_phone->lock);
 
 	/* Remember the third party task hash. */
 	IPC_SET_ARG5(call->data, (sysarg_t) other_task_s);
 
-	kobject_put(sender_obj);
 	return EOK;
 }
@@ -74,23 +71,23 @@
 	if (!IPC_GET_RETVAL(answer->data)) {
 		/* The recipient authorized the change of state. */
+		phone_t *recipient_phone;
 		task_t *other_task_s;
 		task_t *other_task_r;
 
-		kobject_t *recipient_obj = kobject_get(TASK,
-		    IPC_GET_ARG1(answer->data), KOBJECT_TYPE_PHONE);
-		if (!recipient_obj) {
+		rc = phone_get(IPC_GET_ARG1(answer->data),
+		    &recipient_phone);
+		if (rc != EOK) {
 			IPC_SET_RETVAL(answer->data, ENOENT);
 			return ENOENT;
 		}
 
-		mutex_lock(&recipient_obj->phone->lock);
-		if (recipient_obj->phone->state != IPC_PHONE_CONNECTED) {
-			mutex_unlock(&recipient_obj->phone->lock);
+		mutex_lock(&recipient_phone->lock);
+		if (recipient_phone->state != IPC_PHONE_CONNECTED) {
+			mutex_unlock(&recipient_phone->lock);
 			IPC_SET_RETVAL(answer->data, EINVAL);
-			kobject_put(recipient_obj);
 			return EINVAL;
 		}
 
-		other_task_r = recipient_obj->phone->callee->task;
+		other_task_r = recipient_phone->callee->task;
 		other_task_s = (task_t *) IPC_GET_ARG5(*olddata);
 
@@ -113,6 +110,5 @@
 		}
 
-		mutex_unlock(&recipient_obj->phone->lock);
-		kobject_put(recipient_obj);
+		mutex_unlock(&recipient_phone->lock);
 	}
 
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/sysipc.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -85,4 +85,6 @@
 {
 	switch (imethod) {
+	case IPC_M_CONNECTION_CLONE:
+	case IPC_M_CLONE_ESTABLISH:
 	case IPC_M_PHONE_HUNGUP:
 		/* This message is meant only for the original recipient. */
@@ -133,4 +135,6 @@
 {
 	switch (IPC_GET_IMETHOD(call->data)) {
+	case IPC_M_CONNECTION_CLONE:
+	case IPC_M_CLONE_ESTABLISH:
 	case IPC_M_CONNECT_TO_ME:
 	case IPC_M_CONNECT_ME_TO:
@@ -260,7 +264,7 @@
 /** Make a call over IPC and wait for reply.
  *
- * @param handle       Phone capability handle for the call.
- * @param data[inout]  Structure with request/reply data.
- * @param priv         Value to be stored in call->priv.
+ * @param phoneid     Phone handle for the call.
+ * @param data[inout] Structure with request/reply data.
+ * @param priv        Value to be stored in call->priv.
  *
  * @return EOK on success.
@@ -268,8 +272,8 @@
  *
  */
-int ipc_req_internal(cap_handle_t handle, ipc_data_t *data, sysarg_t priv)
-{
-	kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
-	if (!kobj->phone)
+int ipc_req_internal(int phoneid, ipc_data_t *data, sysarg_t priv)
+{
+	phone_t *phone;
+	if (phone_get(phoneid, &phone) != EOK)
 		return ENOENT;
 	
@@ -278,5 +282,5 @@
 	memcpy(call->data.args, data->args, sizeof(data->args));
 	
-	int rc = request_preprocess(call, kobj->phone);
+	int rc = request_preprocess(call, phone);
 	if (!rc) {
 #ifdef CONFIG_UDEBUG
@@ -285,5 +289,5 @@
 
 		ipc_call_hold(call);
-		rc = ipc_call_sync(kobj->phone, call);
+		rc = ipc_call_sync(phone, call);
 		spinlock_lock(&call->forget_lock);
 		bool forgotten = call->forget;
@@ -312,6 +316,5 @@
 				assert(rc == EINTR);
 			}
-			kobject_put(kobj);
-			return rc;
+			return rc;	
 		}
 
@@ -322,5 +325,4 @@
 	memcpy(data->args, call->data.args, sizeof(data->args));
 	ipc_call_free(call);
-	kobject_put(kobj);
 	
 	return EOK;
@@ -348,10 +350,10 @@
  * the generic function sys_ipc_call_async_slow().
  *
- * @param handle   Phone capability handle for the call.
- * @param imethod  Interface and method of the call.
- * @param arg1     Service-defined payload argument.
- * @param arg2     Service-defined payload argument.
- * @param arg3     Service-defined payload argument.
- * @param arg4     Service-defined payload argument.
+ * @param phoneid Phone handle for the call.
+ * @param imethod Interface and method of the call.
+ * @param arg1    Service-defined payload argument.
+ * @param arg2    Service-defined payload argument.
+ * @param arg3    Service-defined payload argument.
+ * @param arg4    Service-defined payload argument.
  *
  * @return Call hash on success.
@@ -361,15 +363,13 @@
  *
  */
-sysarg_t sys_ipc_call_async_fast(sysarg_t handle, sysarg_t imethod,
+sysarg_t sys_ipc_call_async_fast(sysarg_t phoneid, sysarg_t imethod,
     sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
 {
-	kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
-	if (!kobj)
+	phone_t *phone;
+	if (phone_get(phoneid, &phone) != EOK)
 		return IPC_CALLRET_FATAL;
 	
-	if (check_call_limit(kobj->phone)) {
-		kobject_put(kobj);
+	if (check_call_limit(phone))
 		return IPC_CALLRET_TEMPORARY;
-	}
 	
 	call_t *call = ipc_call_alloc(0);
@@ -386,12 +386,11 @@
 	IPC_SET_ARG5(call->data, 0);
 	
-	int res = request_preprocess(call, kobj->phone);
+	int res = request_preprocess(call, phone);
 	
 	if (!res)
-		ipc_call(kobj->phone, call);
+		ipc_call(phone, call);
 	else
-		ipc_backsend_err(kobj->phone, call, res);
-	
-	kobject_put(kobj);
+		ipc_backsend_err(phone, call, res);
+	
 	return (sysarg_t) call;
 }
@@ -399,5 +398,5 @@
 /** Make an asynchronous IPC call allowing to transmit the entire payload.
  *
- * @param handle  Phone capability for the call.
+ * @param phoneid Phone handle for the call.
  * @param data    Userspace address of call data with the request.
  *
@@ -405,14 +404,12 @@
  *
  */
-sysarg_t sys_ipc_call_async_slow(sysarg_t handle, ipc_data_t *data)
-{
-	kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
-	if (!kobj)
+sysarg_t sys_ipc_call_async_slow(sysarg_t phoneid, ipc_data_t *data)
+{
+	phone_t *phone;
+	if (phone_get(phoneid, &phone) != EOK)
 		return IPC_CALLRET_FATAL;
 
-	if (check_call_limit(kobj->phone)) {
-		kobject_put(kobj);
+	if (check_call_limit(phone))
 		return IPC_CALLRET_TEMPORARY;
-	}
 
 	call_t *call = ipc_call_alloc(0);
@@ -421,16 +418,14 @@
 	if (rc != 0) {
 		ipc_call_free(call);
-		kobject_put(kobj);
 		return (sysarg_t) rc;
 	}
 	
-	int res = request_preprocess(call, kobj->phone);
+	int res = request_preprocess(call, phone);
 	
 	if (!res)
-		ipc_call(kobj->phone, call);
+		ipc_call(phone, call);
 	else
-		ipc_backsend_err(kobj->phone, call, res);
-	
-	kobject_put(kobj);
+		ipc_backsend_err(phone, call, res);
+	
 	return (sysarg_t) call;
 }
@@ -440,16 +435,16 @@
  * Common code for both the fast and the slow version.
  *
- * @param callid   Hash of the call to forward.
- * @param handle   Phone capability to use for forwarding.
- * @param imethod  New interface and method to use for the forwarded call.
- * @param arg1     New value of the first argument for the forwarded call.
- * @param arg2     New value of the second argument for the forwarded call.
- * @param arg3     New value of the third argument for the forwarded call.
- * @param arg4     New value of the fourth argument for the forwarded call.
- * @param arg5     New value of the fifth argument for the forwarded call.
- * @param mode     Flags that specify mode of the forward operation.
- * @param slow     If true, arg3, arg4 and arg5 are considered. Otherwise
- *                 the function considers only the fast version arguments:
- *                 i.e. arg1 and arg2.
+ * @param callid  Hash of the call to forward.
+ * @param phoneid Phone handle to use for forwarding.
+ * @param imethod New interface and method to use for the forwarded call.
+ * @param arg1    New value of the first argument for the forwarded call.
+ * @param arg2    New value of the second argument for the forwarded call.
+ * @param arg3    New value of the third argument for the forwarded call.
+ * @param arg4    New value of the fourth argument for the forwarded call.
+ * @param arg5    New value of the fifth argument for the forwarded call.
+ * @param mode    Flags that specify mode of the forward operation.
+ * @param slow    If true, arg3, arg4 and arg5 are considered. Otherwise
+ *                the function considers only the fast version arguments:
+ *                i.e. arg1 and arg2.
  *
  * @return 0 on succes, otherwise an error code.
@@ -458,5 +453,5 @@
  *
  */
-static sysarg_t sys_ipc_forward_common(sysarg_t callid, sysarg_t handle,
+static sysarg_t sys_ipc_forward_common(sysarg_t callid, sysarg_t phoneid,
     sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
     sysarg_t arg4, sysarg_t arg5, unsigned int mode, bool slow)
@@ -473,7 +468,7 @@
 	bool after_forward = false;
 	int rc;
-
-	kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
-	if (!kobj) {
+	phone_t *phone;
+	
+	if (phone_get(phoneid, &phone) != EOK) {
 		rc = ENOENT;
 		goto error;
@@ -521,5 +516,5 @@
 	}
 	
-	rc = ipc_forward(call, kobj->phone, &TASK->answerbox, mode);
+	rc = ipc_forward(call, phone, &TASK->answerbox, mode);
 	if (rc != EOK) {
 		after_forward = true;
@@ -527,5 +522,4 @@
 	}
 
-	kobject_put(kobj);
 	return EOK;
 
@@ -538,6 +532,4 @@
 		ipc_answer(&TASK->answerbox, call);
 
-	if (kobj)
-		kobject_put(kobj);
 	return rc;
 }
@@ -552,18 +544,18 @@
  * arguments are not set and these values are ignored.
  *
- * @param callid   Hash of the call to forward.
- * @param handle   Phone handle to use for forwarding.
- * @param imethod  New interface and method to use for the forwarded call.
- * @param arg1     New value of the first argument for the forwarded call.
- * @param arg2     New value of the second argument for the forwarded call.
- * @param mode     Flags that specify mode of the forward operation.
+ * @param callid  Hash of the call to forward.
+ * @param phoneid Phone handle to use for forwarding.
+ * @param imethod New interface and method to use for the forwarded call.
+ * @param arg1    New value of the first argument for the forwarded call.
+ * @param arg2    New value of the second argument for the forwarded call.
+ * @param mode    Flags that specify mode of the forward operation.
  *
  * @return 0 on succes, otherwise an error code.
  *
  */
-sysarg_t sys_ipc_forward_fast(sysarg_t callid, sysarg_t handle,
+sysarg_t sys_ipc_forward_fast(sysarg_t callid, sysarg_t phoneid,
     sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
 {
-	return sys_ipc_forward_common(callid, handle, imethod, arg1, arg2, 0, 0,
+	return sys_ipc_forward_common(callid, phoneid, imethod, arg1, arg2, 0, 0,
 	    0, mode, false); 
 }
@@ -579,5 +571,5 @@
  *
  * @param callid  Hash of the call to forward.
- * @param handle  Phone handle to use for forwarding.
+ * @param phoneid Phone handle to use for forwarding.
  * @param data    Userspace address of the new IPC data.
  * @param mode    Flags that specify mode of the forward operation.
@@ -586,5 +578,5 @@
  *
  */
-sysarg_t sys_ipc_forward_slow(sysarg_t callid, sysarg_t handle,
+sysarg_t sys_ipc_forward_slow(sysarg_t callid, sysarg_t phoneid,
     ipc_data_t *data, unsigned int mode)
 {
@@ -595,5 +587,5 @@
 		return (sysarg_t) rc;
 	
-	return sys_ipc_forward_common(callid, handle,
+	return sys_ipc_forward_common(callid, phoneid,
 	    IPC_GET_IMETHOD(newdata), IPC_GET_ARG1(newdata),
 	    IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata),
@@ -693,21 +685,19 @@
 /** Hang up a phone.
  *
- * @param handle  Phone capability handle of the phone to be hung up.
+ * @param Phone handle of the phone to be hung up.
  *
  * @return 0 on success or an error code.
  *
  */
-sysarg_t sys_ipc_hangup(sysarg_t handle)
-{
-	kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
-	if (!kobj)
+sysarg_t sys_ipc_hangup(sysarg_t phoneid)
+{
+	phone_t *phone;
+	
+	if (phone_get(phoneid, &phone) != EOK)
 		return ENOENT;
 	
-	if (ipc_phone_hangup(kobj->phone)) {
-		kobject_put(kobj);
+	if (ipc_phone_hangup(phone))
 		return -1;
-	}
-	
-	kobject_put(kobj);
+	
 	return 0;
 }
@@ -812,18 +802,18 @@
  *
  * @param inr     IRQ number.
+ * @param devno   Device number.
  * @param imethod Interface and method to be associated with the notification.
  * @param ucode   Uspace pointer to the top-half pseudocode.
  *
- * @return IRQ kernel object capability
- * @return EPERM
- * @return Error code returned by ipc_irq_subscribe().
- *
- */
-sysarg_t sys_ipc_irq_subscribe(inr_t inr, sysarg_t imethod, irq_code_t *ucode)
+ * @return EPERM or a return code returned by ipc_irq_subscribe().
+ *
+ */
+sysarg_t sys_ipc_irq_subscribe(inr_t inr, devno_t devno, sysarg_t imethod,
+    irq_code_t *ucode)
 {
 	if (!(perm_get(TASK) & PERM_IRQ_REG))
 		return EPERM;
 	
-	return ipc_irq_subscribe(&TASK->answerbox, inr, imethod, ucode);
+	return ipc_irq_subscribe(&TASK->answerbox, inr, devno, imethod, ucode);
 }
 
@@ -836,10 +826,10 @@
  *
  */
-sysarg_t sys_ipc_irq_unsubscribe(sysarg_t cap)
+sysarg_t sys_ipc_irq_unsubscribe(inr_t inr, devno_t devno)
 {
 	if (!(perm_get(TASK) & PERM_IRQ_REG))
 		return EPERM;
 	
-	ipc_irq_unsubscribe(&TASK->answerbox, cap);
+	ipc_irq_unsubscribe(&TASK->answerbox, inr, devno);
 	
 	return 0;
Index: kernel/generic/src/ipc/sysipc_ops.c
===================================================================
--- kernel/generic/src/ipc/sysipc_ops.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/ipc/sysipc_ops.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -38,4 +38,6 @@
 
 /* Forward declarations. */
+sysipc_ops_t ipc_m_connection_clone_ops;
+sysipc_ops_t ipc_m_clone_establish_ops;
 sysipc_ops_t ipc_m_connect_to_me_ops;
 sysipc_ops_t ipc_m_connect_me_to_ops;
@@ -49,4 +51,6 @@
 
 static sysipc_ops_t *sysipc_ops[] = {
+	[IPC_M_CONNECTION_CLONE] = &ipc_m_connection_clone_ops,
+	[IPC_M_CLONE_ESTABLISH] = &ipc_m_clone_establish_ops,
 	[IPC_M_CONNECT_TO_ME] = &ipc_m_connect_to_me_ops,
 	[IPC_M_CONNECT_ME_TO] = &ipc_m_connect_me_to_ops,
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/proc/task.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -50,5 +50,4 @@
 #include <adt/btree.h>
 #include <adt/list.h>
-#include <cap/cap.h>
 #include <ipc/ipc.h>
 #include <ipc/ipcrsc.h>
@@ -84,5 +83,4 @@
 static void task_kill_internal(task_t *);
 static int tsk_constructor(void *, unsigned int);
-static size_t tsk_destructor(void *obj);
 
 /** Initialize kernel tasks support.
@@ -94,5 +92,5 @@
 	avltree_create(&tasks_tree);
 	task_slab = slab_cache_create("task_t", sizeof(task_t), 0,
-	    tsk_constructor, tsk_destructor, 0);
+	    tsk_constructor, NULL, 0);
 }
 
@@ -169,8 +167,10 @@
 	list_initialize(&task->threads);
 	
-	caps_task_alloc(task);
-	
 	ipc_answerbox_init(&task->answerbox, task);
 	
+	size_t i;
+	for (i = 0; i < IPC_MAX_PHONES; i++)
+		ipc_phone_init(&task->phones[i], task);
+
 	spinlock_initialize(&task->active_calls_lock, "active_calls_lock");
 	list_initialize(&task->active_calls);
@@ -186,12 +186,4 @@
 }
 
-size_t tsk_destructor(void *obj)
-{
-	task_t *task = (task_t *) obj;
-	
-	caps_task_free(task);
-	return 0;
-}
-
 /** Create new task with no threads.
  *
@@ -214,6 +206,4 @@
 	task->ucycles = 0;
 	task->kcycles = 0;
-
-	caps_task_init(task);
 
 	task->ipc_info.call_sent = 0;
@@ -238,10 +228,6 @@
 	
 	if ((ipc_phone_0) &&
-	    (container_check(ipc_phone_0->task->container, task->container))) {
-		cap_handle_t phone_handle = phone_alloc(task);
-		kobject_t *phone_obj = kobject_get(task, phone_handle,
-		    KOBJECT_TYPE_PHONE);
-		(void) ipc_phone_connect(phone_obj->phone, ipc_phone_0);
-	}
+	    (container_check(ipc_phone_0->task->container, task->container)))
+		(void) ipc_phone_connect(&task->phones[0], ipc_phone_0);
 	
 	futex_task_init(task);
@@ -617,5 +603,5 @@
 	if (*additional)
 		printf("%-8" PRIu64 " %9" PRIu64 "%c %9" PRIu64 "%c "
-		    "%9" PRIua "\n", task->taskid, ucycles, usuffix, kcycles,
+		    "%9" PRIua, task->taskid, ucycles, usuffix, kcycles,
 		    ksuffix, atomic_get(&task->refcount));
 	else
@@ -623,4 +609,13 @@
 		    task->taskid, task->name, task->container, task, task->as);
 #endif
+	
+	if (*additional) {
+		size_t i;
+		for (i = 0; i < IPC_MAX_PHONES; i++) {
+			if (task->phones[i].callee)
+				printf(" %zu:%p", i, task->phones[i].callee);
+		}
+		printf("\n");
+	}
 	
 	irq_spinlock_unlock(&task->lock, false);
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/proc/thread.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -228,5 +228,5 @@
 #endif
 	
-	return STACK_FRAMES;  /* number of frames freed */
+	return 1;  /* One page freed */
 }
 
Index: kernel/generic/src/syscall/syscall.c
===================================================================
--- kernel/generic/src/syscall/syscall.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/syscall/syscall.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -45,4 +45,5 @@
 #include <arch.h>
 #include <debug.h>
+#include <ddi/device.h>
 #include <interrupt.h>
 #include <ipc/sysipc.h>
@@ -174,4 +175,5 @@
 	
 	/* DDI related syscalls. */
+	[SYS_DEVICE_ASSIGN_DEVNO] = (syshandler_t) sys_device_assign_devno,
 	[SYS_PHYSMEM_MAP] = (syshandler_t) sys_physmem_map,
 	[SYS_PHYSMEM_UNMAP] = (syshandler_t) sys_physmem_unmap,
Index: kernel/generic/src/sysinfo/sysinfo.c
===================================================================
--- kernel/generic/src/sysinfo/sysinfo.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/generic/src/sysinfo/sysinfo.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -700,5 +700,4 @@
 	
 	sysinfo_return_t ret;
-	ret.tag = SYSINFO_VAL_UNDEFINED;
 	
 	if (subtree != NULL) {
@@ -731,4 +730,7 @@
 			ret.data.size = size;
 		}
+	} else {
+		/* No item in the fixed sysinfo tree */
+		ret.tag = SYSINFO_VAL_UNDEFINED;
 	}
 	
Index: uspace/app/trace/ipc_desc.c
===================================================================
--- uspace/app/trace/ipc_desc.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/app/trace/ipc_desc.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -40,4 +40,6 @@
 	/* System methods */
 	{ IPC_M_PHONE_HUNGUP,     "PHONE_HUNGUP" },
+	{ IPC_M_CONNECTION_CLONE, "CONNECTION_CLONE" },
+	{ IPC_M_CLONE_ESTABLISH,  "CLONE_ESTABLISH" },
 	{ IPC_M_CONNECT_ME_TO,    "CONNECT_ME_TO" },
 	{ IPC_M_CONNECT_TO_ME,    "CONNECT_TO_ME" },
Index: uspace/drv/audio/hdaudio/hdaudio.c
===================================================================
--- uspace/drv/audio/hdaudio/hdaudio.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/audio/hdaudio/hdaudio.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -263,8 +263,7 @@
 	}
 
-	int irq_cap = register_interrupt_handler(dev, res.irqs.irqs[0],
+	rc = register_interrupt_handler(dev, res.irqs.irqs[0],
 	    hdaudio_interrupt, &irq_code);
-	if (irq_cap < 0) {
-		rc = irq_cap;
+	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed registering interrupt handler. (%d)",
 		    rc);
Index: uspace/drv/audio/sb16/main.c
===================================================================
--- uspace/drv/audio/sb16/main.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/audio/sb16/main.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -94,5 +94,4 @@
 	irq_cmd_t irq_cmds[irq_cmd_count];
 	irq_pio_range_t irq_ranges[1];
-	int irq_cap;
 
 	sb16_t *soft_state = ddf_dev_data_alloc(device, sizeof(sb16_t));
@@ -124,8 +123,6 @@
 	};
 
-	irq_cap = register_interrupt_handler(device, irq, irq_handler,
-	    &irq_code);
-	if (irq_cap < 0) {
-		rc = irq_cap;
+	rc = register_interrupt_handler(device, irq, irq_handler, &irq_code);
+	if (rc != EOK) {
 		ddf_log_error("Failed to register irq handler: %s.",
 		    str_error(rc));
@@ -171,5 +168,5 @@
 error:
 	if (handler_regd)
-		unregister_interrupt_handler(device, irq_cap);
+		unregister_interrupt_handler(device, irq);
 	return rc;
 }
Index: uspace/drv/block/ahci/ahci.c
===================================================================
--- uspace/drv/block/ahci/ahci.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/block/ahci/ahci.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -1185,12 +1185,12 @@
 	ct.ranges = ahci_ranges;
 	
-	int irq_cap = register_interrupt_handler(dev,
-	    hw_res_parsed.irqs.irqs[0], ahci_interrupt, &ct);
-	if (irq_cap < 0) {
+	int rc = register_interrupt_handler(dev, hw_res_parsed.irqs.irqs[0],
+	    ahci_interrupt, &ct);
+	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed registering interrupt handler.");
 		goto error_register_interrupt_handler;
 	}
 	
-	int rc = irc_enable_interrupt(hw_res_parsed.irqs.irqs[0]);
+	rc = irc_enable_interrupt(hw_res_parsed.irqs.irqs[0]);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed enable interupt.");
@@ -1202,5 +1202,5 @@
 	
 error_enable_interrupt:
-	unregister_interrupt_handler(dev, irq_cap);
+	unregister_interrupt_handler(dev, hw_res_parsed.irqs.irqs[0]);
 	
 error_register_interrupt_handler:
Index: uspace/drv/block/ddisk/ddisk.c
===================================================================
--- uspace/drv/block/ddisk/ddisk.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/block/ddisk/ddisk.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -112,6 +112,4 @@
 	ddisk_regs_t *ddisk_regs;
 
-	int irq_cap;
-
 	bd_srvs_t bds;
 } ddisk_t;
@@ -449,6 +447,4 @@
 	ddisk->bds.sarg = ddisk;
 
-	ddisk->irq_cap = -1;
-
 	/*
 	 * Enable access to ddisk's PIO registers.
@@ -503,8 +499,7 @@
 	ddisk_irq_commands[0].addr = (void *) &res_phys->status;
 	ddisk_irq_commands[3].addr = (void *) &res_phys->command;
-	ddisk->irq_cap = register_interrupt_handler(dev, ddisk->ddisk_res.irq,
+	rc = register_interrupt_handler(dev, ddisk->ddisk_res.irq,
 	    ddisk_irq_handler, &ddisk_irq_code);
-	if (ddisk->irq_cap < 0) {
-		rc = ddisk->irq_cap;
+	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed to register interrupt handler.");
 		goto error;
@@ -546,5 +541,5 @@
 	}
 
-	unregister_interrupt_handler(ddisk->dev, ddisk->irq_cap);
+	unregister_interrupt_handler(ddisk->dev, ddisk->ddisk_res.irq);
 	
 	rc = pio_disable(ddisk->ddisk_regs, sizeof(ddisk_regs_t));
Index: uspace/drv/char/i8042/i8042.c
===================================================================
--- uspace/drv/char/i8042/i8042.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/char/i8042/i8042.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -269,8 +269,7 @@
 	};
 	
-	const int irq_kbd_cap = register_interrupt_handler(ddf_dev, irq_kbd,
-	    i8042_irq_handler, &irq_code);
-	if (irq_kbd_cap < 0) {
-		rc = irq_kbd_cap;
+	rc = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler,
+	    &irq_code);
+	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed set handler for kbd: %s.",
 		    ddf_dev_get_name(ddf_dev));
@@ -278,8 +277,7 @@
 	}
 	
-	const int irq_mouse_cap = register_interrupt_handler(ddf_dev, irq_mouse,
-	    i8042_irq_handler, &irq_code);
-	if (irq_mouse_cap < 0) {
-		rc = irq_mouse_cap;
+	rc = register_interrupt_handler(ddf_dev, irq_mouse, i8042_irq_handler,
+	    &irq_code);
+	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed set handler for mouse: %s.",
 		    ddf_dev_get_name(ddf_dev));
Index: uspace/drv/char/ns8250/ns8250.c
===================================================================
--- uspace/drv/char/ns8250/ns8250.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/char/ns8250/ns8250.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -160,6 +160,4 @@
 	/** The irq assigned to this device. */
 	int irq;
-	/** IRQ capability handle */
-	int irq_cap;
 	/** The base i/o address of the devices registers. */
 	uintptr_t io_addr;
@@ -799,5 +797,5 @@
 static inline int ns8250_unregister_interrupt_handler(ns8250_t *ns)
 {
-	return unregister_interrupt_handler(ns->dev, ns->irq_cap);
+	return unregister_interrupt_handler(ns->dev, ns->irq);
 }
 
@@ -851,6 +849,5 @@
 	
 	/* Register interrupt handler. */
-	ns->irq_cap = ns8250_register_interrupt_handler(ns);
-	if (ns->irq_cap < 0) {
+	if (ns8250_register_interrupt_handler(ns) != EOK) {
 		ddf_msg(LVL_ERROR, "Failed to register interrupt handler.");
 		rc = EADDRNOTAVAIL;
Index: uspace/drv/char/pl050/pl050.c
===================================================================
--- uspace/drv/char/pl050/pl050.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/char/pl050/pl050.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -212,8 +212,7 @@
 	pl050->regs = regs;
 
-	const int irq_cap = register_interrupt_handler(pl050->dev,
-	    res.irqs.irqs[0], pl050_interrupt, &pl050_irq_code);
-	if (irq_cap < 0) {
-		rc = irq_cap;
+	rc = register_interrupt_handler(pl050->dev, res.irqs.irqs[0],
+	    pl050_interrupt, &pl050_irq_code);
+	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed registering interrupt handler. (%d)",
 		    rc);
Index: uspace/drv/nic/e1k/e1k.c
===================================================================
--- uspace/drv/nic/e1k/e1k.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/nic/e1k/e1k.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -1253,5 +1253,5 @@
  * @param nic Driver data
  *
- * @return IRQ capability handle if the handler was registered
+ * @return EOK if the handler was registered
  * @return Negative error code otherwise
  *
@@ -1268,9 +1268,9 @@
 	e1000_irq_code.cmds[2].addr = e1000->reg_base_phys + E1000_IMC;
 	
-	int cap = register_interrupt_handler(nic_get_ddf_dev(nic), e1000->irq,
-	    e1000_interrupt_handler, &e1000_irq_code);
+	int rc = register_interrupt_handler(nic_get_ddf_dev(nic),
+	    e1000->irq, e1000_interrupt_handler, &e1000_irq_code);
 	
 	fibril_mutex_unlock(&irq_reg_mutex);
-	return cap;
+	return rc;
 }
 
@@ -2152,9 +2152,7 @@
 	ddf_fun_set_ops(fun, &e1000_dev_ops);
 	
-	int irq_cap = e1000_register_int_handler(nic);
-	if (irq_cap < 0) {
-		rc = irq_cap;
+	rc = e1000_register_int_handler(nic);
+	if (rc != EOK)
 		goto err_fun_create;
-	}
 	
 	rc = e1000_initialize_rx_structure(nic);
@@ -2191,5 +2189,5 @@
 	e1000_uninitialize_rx_structure(nic);
 err_irq:
-	unregister_interrupt_handler(dev, irq_cap);
+	unregister_interrupt_handler(dev, DRIVER_DATA_DEV(dev)->irq);
 err_fun_create:
 	ddf_fun_destroy(fun);
Index: uspace/drv/nic/ne2k/ne2k.c
===================================================================
--- uspace/drv/nic/ne2k/ne2k.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/nic/ne2k/ne2k.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -160,7 +160,7 @@
 	}
 
-	int irq_cap = register_interrupt_handler(nic_get_ddf_dev(nic_data),
+	int rc = register_interrupt_handler(nic_get_ddf_dev(nic_data),
 		ne2k->irq, ne2k_interrupt_handler, &ne2k->code);
-	return irq_cap;
+	return rc;
 }
 
@@ -228,6 +228,6 @@
 	ne2k->probed = true;
 	
-	int irq_cap = ne2k_register_interrupt(nic_data);
-	if (irq_cap < 0)
+	rc = ne2k_register_interrupt(nic_data);
+	if (rc != EOK)
 		return EINVAL;
 	
Index: uspace/drv/nic/rtl8139/driver.c
===================================================================
--- uspace/drv/nic/rtl8139/driver.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/nic/rtl8139/driver.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -881,6 +881,5 @@
  *  @param nic_data  The driver data
  *
- *  @return IRQ capability handle if the handler was registered.
- *  @return Negative error code otherwise.
+ *  @return EOK if the handler was registered, negative error code otherwise
  */
 inline static int rtl8139_register_int_handler(nic_t *nic_data)
@@ -895,10 +894,10 @@
 	rtl8139_irq_code.cmds[2].addr = rtl8139->io_addr + ISR;
 	rtl8139_irq_code.cmds[3].addr = rtl8139->io_addr + IMR;
-	int cap = register_interrupt_handler(nic_get_ddf_dev(nic_data),
+	int rc = register_interrupt_handler(nic_get_ddf_dev(nic_data),
 	    rtl8139->irq, rtl8139_interrupt_handler, &rtl8139_irq_code);
 
 	RTL8139_IRQ_STRUCT_UNLOCK();
 
-	return cap;
+	return rc;
 }
 
@@ -1322,9 +1321,7 @@
 
 	/* Register interrupt handler */
-	int irq_cap = rtl8139_register_int_handler(nic_data);
-	if (irq_cap < 0) {
-		rc = irq_cap;
+	rc = rtl8139_register_int_handler(nic_data);
+	if (rc != EOK)
 		goto err_pio;
-	}
 
 	fun = ddf_fun_create(nic_get_ddf_dev(nic_data), fun_exposed, "port0");
@@ -1358,5 +1355,5 @@
 	ddf_fun_destroy(fun);
 err_srv:
-	unregister_interrupt_handler(dev, irq_cap);
+	unregister_interrupt_handler(dev, rtl8139->irq);
 err_pio:
 	// rtl8139_pio_disable(dev);
Index: uspace/drv/nic/rtl8169/driver.c
===================================================================
--- uspace/drv/nic/rtl8169/driver.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/drv/nic/rtl8169/driver.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -371,8 +371,8 @@
 	rtl8169_irq_code.cmds[2].addr = rtl8169->regs + ISR;
 	rtl8169_irq_code.cmds[3].addr = rtl8169->regs + IMR;
-	int irq_cap = register_interrupt_handler(nic_get_ddf_dev(nic_data),
+	int rc = register_interrupt_handler(nic_get_ddf_dev(nic_data),
 	    rtl8169->irq, rtl8169_irq_handler, &rtl8169_irq_code);
 
-	return irq_cap;
+	return rc;
 }
 
@@ -426,9 +426,9 @@
 		goto err_pio;
 
-	int irq_cap = rtl8169_register_int_handler(nic_data);
-	if (irq_cap < 0) {
-		rc = irq_cap;
+	rc = rtl8169_register_int_handler(nic_data);
+	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed to register IRQ handler (%d)", rc);
 		goto err_irq;
+
 	}
 
@@ -469,6 +469,6 @@
 err_srv:
 	/* XXX Disconnect from services */
-	unregister_interrupt_handler(dev, irq_cap);
 err_irq:
+	//unregister_interrupt_handler(dev, rtl8169->irq);
 err_pio:
 err_destroy:
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/generic/async.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -1048,14 +1048,14 @@
  *
  * @param inr     IRQ number.
+ * @param devno   Device number of the device generating inr.
  * @param handler Notification handler.
  * @param data    Notification handler client data.
  * @param ucode   Top-half pseudocode handler.
  *
- * @return IRQ capability handle on success.
- * @return Negative error code.
- *
- */
-int async_irq_subscribe(int inr, async_notification_handler_t handler,
-    void *data, const irq_code_t *ucode)
+ * @return Zero on success or a negative error code.
+ *
+ */
+int async_irq_subscribe(int inr, int devno,
+    async_notification_handler_t handler, void *data, const irq_code_t *ucode)
 {
 	notification_t *notification =
@@ -1077,20 +1077,21 @@
 	futex_up(&async_futex);
 	
-	return ipc_irq_subscribe(inr, imethod, ucode);
+	return ipc_irq_subscribe(inr, devno, imethod, ucode);
 }
 
 /** Unsubscribe from IRQ notification.
  *
- * @param cap     IRQ capability handle. 
+ * @param inr     IRQ number.
+ * @param devno   Device number of the device generating inr.
  *
  * @return Zero on success or a negative error code.
  *
  */
-int async_irq_unsubscribe(int cap)
+int async_irq_unsubscribe(int inr, int devno)
 {
 	// TODO: Remove entry from hash table
 	//       to avoid memory leak
 	
-	return ipc_irq_unsubscribe(cap);
+	return ipc_irq_unsubscribe(inr, devno);
 }
 
@@ -1383,4 +1384,14 @@
 		async_new_connection(call->in_task_id, in_phone_hash, callid,
 		    call, handler, data);
+		return;
+	}
+	
+	/* Cloned connection */
+	if (IPC_GET_IMETHOD(*call) == IPC_M_CLONE_ESTABLISH) {
+		// TODO: Currently ignores ports altogether
+		
+		/* Open new connection with fibril, etc. */
+		async_new_connection(call->in_task_id, IPC_GET_ARG5(*call),
+		    callid, call, fallback_port_handler, fallback_port_data);
 		return;
 	}
@@ -2100,4 +2111,76 @@
 	
 	return EOK;
+}
+
+/** Wrapper for making IPC_M_CLONE_ESTABLISH calls using the async framework.
+ *
+ * Ask for a cloned connection to some service.
+ *
+ * @param mgmt Exchange management style.
+ * @param exch Exchange for sending the message.
+ *
+ * @return New session on success or NULL on error.
+ *
+ */
+async_sess_t *async_clone_establish(exch_mgmt_t mgmt, async_exch_t *exch)
+{
+	if (exch == NULL) {
+		errno = ENOENT;
+		return NULL;
+	}
+	
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	ipc_call_t result;
+	
+	amsg_t *msg = amsg_create();
+	if (!msg) {
+		free(sess);
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	msg->dataptr = &result;
+	msg->wdata.active = true;
+	
+	ipc_call_async_0(exch->phone, IPC_M_CLONE_ESTABLISH, msg,
+	    reply_received);
+	
+	sysarg_t rc;
+	async_wait_for((aid_t) msg, &rc);
+	
+	if (rc != EOK) {
+		errno = rc;
+		free(sess);
+		return NULL;
+	}
+	
+	int phone = (int) IPC_GET_ARG5(result);
+	
+	if (phone < 0) {
+		errno = phone;
+		free(sess);
+		return NULL;
+	}
+	
+	sess->iface = 0;
+	sess->mgmt = mgmt;
+	sess->phone = phone;
+	sess->arg1 = 0;
+	sess->arg2 = 0;
+	sess->arg3 = 0;
+	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+	
+	return sess;
 }
 
@@ -3070,4 +3153,64 @@
 }
 
+/** Wrapper for sending an exchange over different exchange for cloning
+ *
+ * @param exch       Exchange to be used for sending.
+ * @param clone_exch Exchange to be cloned.
+ *
+ */
+int async_exchange_clone(async_exch_t *exch, async_exch_t *clone_exch)
+{
+	return async_req_1_0(exch, IPC_M_CONNECTION_CLONE, clone_exch->phone);
+}
+
+/** Wrapper for receiving the IPC_M_CONNECTION_CLONE calls.
+ *
+ * If the current call is IPC_M_CONNECTION_CLONE then a new
+ * async session is created for the accepted phone.
+ *
+ * @param mgmt Exchange management style.
+ *
+ * @return New async session or NULL on failure.
+ *
+ */
+async_sess_t *async_clone_receive(exch_mgmt_t mgmt)
+{
+	/* Accept the phone */
+	ipc_call_t call;
+	ipc_callid_t callid = async_get_call(&call);
+	int phone = (int) IPC_GET_ARG1(call);
+	
+	if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECTION_CLONE) ||
+	    (phone < 0)) {
+		async_answer_0(callid, EINVAL);
+		return NULL;
+	}
+	
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		async_answer_0(callid, ENOMEM);
+		return NULL;
+	}
+	
+	sess->iface = 0;
+	sess->mgmt = mgmt;
+	sess->phone = phone;
+	sess->arg1 = 0;
+	sess->arg2 = 0;
+	sess->arg3 = 0;
+	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+	
+	/* Acknowledge the cloned phone */
+	async_answer_0(callid, EOK);
+	
+	return sess;
+}
+
 /** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
  *
Index: uspace/lib/c/generic/ddi.c
===================================================================
--- uspace/lib/c/generic/ddi.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/generic/ddi.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -50,4 +50,14 @@
 #include "private/libc.h"
 
+
+/** Return unique device number.
+ *
+ * @return New unique device number.
+ *
+ */
+int device_assign_devno(void)
+{
+	return __SYSCALL0(SYS_DEVICE_ASSIGN_DEVNO);
+}
 
 /** Map a piece of physical memory to task.
Index: uspace/lib/c/generic/irq.c
===================================================================
--- uspace/lib/c/generic/irq.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/generic/irq.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -55,29 +55,32 @@
  *
  * @param inr    IRQ number.
+ * @param devno  Device number of the device generating inr.
  * @param method Use this method for notifying me.
  * @param ucode  Top-half pseudocode handler.
  *
- * @return IRQ capability handle returned by the kernel.
- * @return Error code returned by the kernel.
+ * @return Value returned by the kernel.
  *
  */
-int ipc_irq_subscribe(int inr, sysarg_t method, const irq_code_t *ucode)
+int ipc_irq_subscribe(int inr, int devno, sysarg_t method,
+    const irq_code_t *ucode)
 {
 	if (ucode == NULL)
 		ucode = &default_ucode;
 	
-	return __SYSCALL3(SYS_IPC_IRQ_SUBSCRIBE, inr, method, (sysarg_t) ucode);
+	return __SYSCALL4(SYS_IPC_IRQ_SUBSCRIBE, inr, devno, method,
+	    (sysarg_t) ucode);
 }
 
 /** Unsubscribe from IRQ notification.
  *
- * @param cap   IRQ capability handle.
+ * @param inr   IRQ number.
+ * @param devno Device number of the device generating inr.
  *
  * @return Value returned by the kernel.
  *
  */
-int ipc_irq_unsubscribe(int cap)
+int ipc_irq_unsubscribe(int inr, int devno)
 {
-	return __SYSCALL1(SYS_IPC_IRQ_UNSUBSCRIBE, cap);
+	return __SYSCALL2(SYS_IPC_IRQ_UNSUBSCRIBE, inr, devno);
 }
 
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/include/async.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -166,7 +166,7 @@
     sysarg_t, async_port_handler_t, void *, port_id_t *);
 
-extern int async_irq_subscribe(int, async_notification_handler_t, void *,
+extern int async_irq_subscribe(int, int, async_notification_handler_t, void *,
     const irq_code_t *);
-extern int async_irq_unsubscribe(int);
+extern int async_irq_unsubscribe(int, int);
 
 extern int async_event_subscribe(event_type_t, async_notification_handler_t,
@@ -343,4 +343,5 @@
     sysarg_t *, sysarg_t *);
 
+extern async_sess_t *async_clone_establish(exch_mgmt_t, async_exch_t *);
 extern async_sess_t *async_connect_me_to(exch_mgmt_t, async_exch_t *, sysarg_t,
     sysarg_t, sysarg_t);
@@ -471,4 +472,6 @@
     sysarg_t, sysarg_t, sysarg_t, ipc_call_t *);
 
+extern int async_exchange_clone(async_exch_t *, async_exch_t *);
+extern async_sess_t *async_clone_receive(exch_mgmt_t);
 extern async_sess_t *async_callback_receive(exch_mgmt_t);
 extern async_sess_t *async_callback_receive_start(exch_mgmt_t, ipc_call_t *);
Index: uspace/lib/c/include/ipc/irq.h
===================================================================
--- uspace/lib/c/include/ipc/irq.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/include/ipc/irq.h	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -39,6 +39,6 @@
 #include <abi/ddi/irq.h>
 
-extern int ipc_irq_subscribe(int, sysarg_t, const irq_code_t *);
-extern int ipc_irq_unsubscribe(int);
+extern int ipc_irq_subscribe(int, int, sysarg_t, const irq_code_t *);
+extern int ipc_irq_unsubscribe(int, int);
 
 #endif
Index: uspace/lib/drv/generic/interrupt.c
===================================================================
--- uspace/lib/drv/generic/interrupt.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/drv/generic/interrupt.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -44,13 +44,13 @@
 
 int register_interrupt_handler(ddf_dev_t *dev, int irq,
-    interrupt_handler_t *handler, const irq_code_t *irq_code)
+    interrupt_handler_t *handler, const irq_code_t *pseudocode)
 {
-	return async_irq_subscribe(irq, (async_notification_handler_t) handler,
-	    dev, irq_code);
+	return async_irq_subscribe(irq, dev->handle,
+	    (async_notification_handler_t) handler, dev, pseudocode);
 }
 
-int unregister_interrupt_handler(ddf_dev_t *dev, int cap)
+int unregister_interrupt_handler(ddf_dev_t *dev, int irq)
 {
-	return async_irq_unsubscribe(cap);
+	return async_irq_unsubscribe(irq, dev->handle);
 }
 
Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -748,6 +748,5 @@
  * @param[in] gen_irq_code IRQ code generator.
  *
- * @return IRQ capability handle on success.
- * @return Negative error code.
+ * @return EOK on success or negative error code
  */
 int hcd_ddf_setup_interrupts(ddf_dev_t *device,
@@ -771,22 +770,22 @@
 
 	/* Register handler to avoid interrupt lockup */
-	const int irq_cap = register_interrupt_handler(device, irq, handler,
-	    &irq_code);
+	int ret = register_interrupt_handler(device, irq, handler, &irq_code);
 	irq_code_clean(&irq_code);
-	if (irq_cap < 0) {
-		usb_log_error("Failed to register interrupt handler: %s.\n",
-		    str_error(irq_cap));
-		return irq_cap;
-	}
-
-	/* Enable interrupts */
-	int ret = hcd_ddf_enable_interrupts(device);
 	if (ret != EOK) {
 		usb_log_error("Failed to register interrupt handler: %s.\n",
 		    str_error(ret));
-		unregister_interrupt_handler(device, irq_cap);
-		return ret;
-	}
-	return irq_cap;
+		return ret;
+	}
+
+	/* Enable interrupts */
+	ret = hcd_ddf_enable_interrupts(device);
+	if (ret != EOK) {
+		usb_log_error("Failed to register interrupt handler: %s.\n",
+		    str_error(ret));
+		unregister_interrupt_handler(device, irq);
+		return ret;
+	}
+	assert(irq > 0);
+	return irq;
 }
 
@@ -883,8 +882,7 @@
 	interrupt_handler_t *irq_handler =
 	    driver->irq_handler ? driver->irq_handler : ddf_hcd_gen_irq_handler;
-	const int irq_cap = hcd_ddf_setup_interrupts(device, &hw_res,
-	    irq_handler, driver->irq_code_gen);
-	bool irqs_enabled = !(irq_cap < 0);
-	if (irqs_enabled) {
+	const int irq = hcd_ddf_setup_interrupts(device, &hw_res, irq_handler,
+	    driver->irq_code_gen);
+	if (!(irq < 0)) {
 		usb_log_debug("Hw interrupts enabled.\n");
 	}
@@ -901,5 +899,5 @@
 	/* Init hw driver */
 	hcd_t *hcd = dev_to_hcd(device);
-	ret = driver->init(hcd, &hw_res, irqs_enabled);
+	ret = driver->init(hcd, &hw_res, !(irq < 0));
 	hw_res_list_parsed_clean(&hw_res);
 	if (ret != EOK) {
@@ -909,5 +907,5 @@
 
 	/* Need working irq replacement to setup root hub */
-	if (!irqs_enabled && hcd->ops.status_hook) {
+	if ((irq < 0) && hcd->ops.status_hook) {
 		hcd->polling_fibril = fibril_create(interrupt_polling, hcd);
 		if (hcd->polling_fibril == 0) {
@@ -918,5 +916,5 @@
 		fibril_add_ready(hcd->polling_fibril);
 		usb_log_warning("Failed to enable interrupts: %s."
-		    " Falling back to polling.\n", str_error(irq_cap));
+		    " Falling back to polling.\n", str_error(irq));
 	}
 
@@ -932,5 +930,5 @@
 irq_unregister:
 		/* Unregistering non-existent should be ok */
-		unregister_interrupt_handler(device, irq_cap);
+		unregister_interrupt_handler(device, irq);
 		hcd_ddf_clean_hc(device);
 		return ret;
Index: uspace/srv/hid/input/port/msim.c
===================================================================
--- uspace/srv/hid/input/port/msim.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/hid/input/port/msim.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -97,5 +97,6 @@
 	msim_ranges[0].base = paddr;
 	msim_cmds[0].addr = (void *) paddr;
-	async_irq_subscribe(inr, msim_irq_handler, NULL, &msim_kbd);
+	async_irq_subscribe(inr, device_assign_devno(), msim_irq_handler, NULL,
+	    &msim_kbd);
 	
 	return 0;
Index: uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c
===================================================================
--- uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -137,5 +137,6 @@
 	    (void *) ts->paddr, inr);
 
-	async_irq_subscribe(inr, s3c24xx_ts_irq_handler, NULL, &ts_irq_code);
+	async_irq_subscribe(inr, device_assign_devno(), s3c24xx_ts_irq_handler,
+	    NULL, &ts_irq_code);
 
 	s3c24xx_ts_wait_for_int_mode(ts, updn_down);
Index: uspace/srv/hw/bus/cuda_adb/cuda_adb.c
===================================================================
--- uspace/srv/hw/bus/cuda_adb/cuda_adb.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/hw/bus/cuda_adb/cuda_adb.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -282,5 +282,6 @@
 	cuda_irq_code.ranges[0].base = (uintptr_t) instance->cuda_physical;
 	cuda_irq_code.cmds[0].addr = (void *) &((cuda_t *) instance->cuda_physical)->ifr;
-	async_irq_subscribe(10, cuda_irq_handler, NULL, &cuda_irq_code);
+	async_irq_subscribe(10, device_assign_devno(), cuda_irq_handler, NULL,
+	    &cuda_irq_code);
 
 	/* Enable SR interrupt. */
Index: uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c
===================================================================
--- uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c	(revision d076f1609141fd03fa25c7138c9260c1f6d19dbf)
@@ -193,5 +193,6 @@
 	    (void *) uart->paddr, inr);
 
-	async_irq_subscribe(inr, s3c24xx_uart_irq_handler, NULL, &uart_irq_code);
+	async_irq_subscribe(inr, device_assign_devno(), s3c24xx_uart_irq_handler,
+	    NULL, &uart_irq_code);
 
 	/* Enable FIFO, Tx trigger level: empty, Rx trigger level: 1 byte. */
