Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision 24c41ba116f95ed1d1214737917cffc3db92868d)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision cc63815c45bec69f85b3455ff1f4c5dcc15c228c)
@@ -63,5 +63,5 @@
  * @return Error code.
  */
-static int register_endpoint(ddf_fun_t *fun, usb_pipe_desc_t *pipe_desc,
+static errno_t register_endpoint(ddf_fun_t *fun, usb_pipe_desc_t *pipe_desc,
      const usb_endpoint_descriptors_t *ep_desc)
 {
@@ -97,5 +97,5 @@
   * @return Error code.
   */
-static int unregister_endpoint(ddf_fun_t *fun, const usb_pipe_desc_t *pipe_desc)
+static errno_t unregister_endpoint(ddf_fun_t *fun, const usb_pipe_desc_t *pipe_desc)
 {
 	assert(fun);
@@ -110,5 +110,8 @@
 		return ENOENT;
 
-	return bus_endpoint_remove(ep);
+	const errno_t err = bus_endpoint_remove(ep);
+
+	endpoint_del_ref(ep);
+	return err;
 }
 
@@ -118,5 +121,5 @@
  * @param fun DDF function of the device (hub) requesting the address.
  */
-static int default_address_reservation(ddf_fun_t *fun, bool reserve)
+static errno_t default_address_reservation(ddf_fun_t *fun, bool reserve)
 {
 	assert(fun);
@@ -142,5 +145,5 @@
  * @param speed USB speed of the new device
  */
-static int device_enumerate(ddf_fun_t *fun, unsigned port, usb_speed_t speed)
+static errno_t device_enumerate(ddf_fun_t *fun, unsigned port, usb_speed_t speed)
 {
 	assert(fun);
@@ -152,5 +155,5 @@
 	assert(hub);
 
-	int err;
+	errno_t err;
 
 	if (!usb_speed_is_valid(speed))
@@ -195,5 +198,5 @@
 }
 
-static int device_remove(ddf_fun_t *fun, unsigned port)
+static errno_t device_remove(ddf_fun_t *fun, unsigned port)
 {
 	assert(fun);
@@ -235,5 +238,5 @@
  * @return Error code.
  */
-static int get_device_description(ddf_fun_t *fun, usb_device_desc_t *desc)
+static errno_t get_device_description(ddf_fun_t *fun, usb_device_desc_t *desc)
 {
 	assert(fun);
@@ -254,7 +257,10 @@
 }
 
-/** Inbound communication interface function.
+/**
+ * Transfer issuing interface function.
+ *
  * @param fun DDF function.
  * @param target Communication target.
+ * @param dir Communication direction.
  * @param setup_data Data to use in setup stage (control transfers).
  * @param data Pointer to data buffer.
@@ -264,6 +270,6 @@
  * @return Error code.
  */
-static int dev_read(ddf_fun_t *fun, usb_target_t target,
-    uint64_t setup_data, char *data, size_t size,
+static errno_t transfer(ddf_fun_t *fun, usb_target_t target,
+    usb_direction_t dir, uint64_t setup_data, char *data, size_t size,
     usbhc_iface_transfer_callback_t callback, void *arg)
 {
@@ -283,41 +289,9 @@
 		return EBADMEM;
 
-	return bus_device_send_batch(dev, target, USB_DIRECTION_IN,
-	    data, size, setup_data,
-	    callback, arg, "READ");
-}
-
-/** Outbound communication interface function.
- * @param fun DDF function.
- * @param target Communication target.
- * @param setup_data Data to use in setup stage (control transfers).
- * @param data Pointer to data buffer.
- * @param size Size of the data buffer.
- * @param callback Function to call on communication end.
- * @param arg Argument passed to the callback function.
- * @return Error code.
- */
-static int dev_write(ddf_fun_t *fun, usb_target_t target,
-    uint64_t setup_data, const char *data, size_t size,
-    usbhc_iface_transfer_callback_t callback, void *arg)
-{
-	assert(fun);
-	device_t *dev = ddf_fun_data_get(fun);
-	assert(dev);
-
-	target.address = dev->address;
-
-	if (!usb_target_is_valid(&target))
-		return EINVAL;
-
-	if (size > 0 && data == NULL)
-		return EBADMEM;
-
-	if (!callback && arg)
-		return EBADMEM;
-
-	return bus_device_send_batch(dev, target, USB_DIRECTION_OUT,
+	const char *name = (dir == USB_DIRECTION_IN) ? "READ" : "WRITE";
+
+	return bus_device_send_batch(dev, target, dir,
 	    (char *) data, size, setup_data,
-	    callback, arg, "WRITE");
+	    callback, arg, name);
 }
 
@@ -337,6 +311,5 @@
 	.unregister_endpoint = unregister_endpoint,
 
-	.read = dev_read,
-	.write = dev_write,
+	.transfer = transfer,
 };
 
@@ -370,5 +343,5 @@
 
 /* This is a copy of lib/usbdev/src/recognise.c */
-static int create_match_ids(match_id_list_t *l,
+static errno_t create_match_ids(match_id_list_t *l,
     usb_standard_device_descriptor_t *d)
 {
@@ -427,7 +400,7 @@
 }
 
-int hcd_ddf_setup_match_ids(device_t *device, usb_standard_device_descriptor_t *desc)
-{
-	int err;
+errno_t hcd_ddf_setup_match_ids(device_t *device, usb_standard_device_descriptor_t *desc)
+{
+	errno_t err;
 	match_id_list_t mids;
 
@@ -457,5 +430,5 @@
  * This function does all the ddf work for hc driver.
  */
-int hcd_ddf_setup_hc(ddf_dev_t *device, size_t size)
+errno_t hcd_ddf_setup_hc(ddf_dev_t *device, size_t size)
 {
 	assert(device);
@@ -468,5 +441,5 @@
 	instance->ddf_dev = device;
 
-	int ret = ENOMEM;
+	errno_t ret = ENOMEM;
 	instance->ctl_fun = ddf_fun_create(device, fun_exposed, "ctl");
 	if (!instance->ctl_fun) {
@@ -510,5 +483,5 @@
  * @return Error code.
  */
-int hcd_ddf_enable_interrupt(hc_device_t *hcd, int inum)
+errno_t hcd_ddf_enable_interrupt(hc_device_t *hcd, int inum)
 {
 	async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
@@ -519,5 +492,5 @@
 }
 
-int hcd_ddf_get_registers(hc_device_t *hcd, hw_res_list_parsed_t *hw_res)
+errno_t hcd_ddf_get_registers(hc_device_t *hcd, hw_res_list_parsed_t *hw_res)
 {
 	async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
@@ -526,5 +499,5 @@
 
 	hw_res_list_parsed_init(hw_res);
-	const int ret = hw_res_get_list_parsed(parent_sess, hw_res, 0);
+	const errno_t ret = hw_res_get_list_parsed(parent_sess, hw_res, 0);
 	if (ret != EOK)
 		hw_res_list_parsed_clean(hw_res);
Index: uspace/lib/usbhost/src/dma_buffer.c
===================================================================
--- uspace/lib/usbhost/src/dma_buffer.c	(revision 24c41ba116f95ed1d1214737917cffc3db92868d)
+++ 	(revision )
@@ -1,123 +1,0 @@
-/*
- * Copyright (c) 2013 Jan Vesely
- * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
- * 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 libusbhost
- * @{
- */
-/** @file
- */
-
-#include <align.h>
-#include <as.h>
-#include <ddi.h>
-#include <stddef.h>
-
-#include "dma_buffer.h"
-
-dma_policy_t dma_policy_default = {
-	.use64 = false,
-	.alignment = PAGE_SIZE,
-};
-
-/**
- * Allocate a DMA buffer.
- *
- * @param[in] db dma_buffer_t structure to fill
- * @param[in] policy dma_policy_t structure to guide
- * @param[in] size Size of the required memory space
- * @return Error code.
- */
-int dma_buffer_alloc_policy(dma_buffer_t *db, size_t size, dma_policy_t policy)
-{
-	assert(db);
-
-	if (policy.alignment > PAGE_SIZE)
-		return EINVAL;
-
-	const size_t aligned_size = ALIGN_UP(size, policy.alignment);
-	const size_t real_size = ALIGN_UP(aligned_size, PAGE_SIZE);
-	const uintptr_t flags = policy.use64 ? 0 : DMAMEM_4GiB;
-
-	uintptr_t phys;
-	void *address = AS_AREA_ANY;
-
-	const int ret = dmamem_map_anonymous(real_size,
-	    flags, AS_AREA_READ | AS_AREA_WRITE, 0,
-	    &phys, &address);
-
-	if (ret == EOK) {
-		/* Poison, accessing it should be enough to make sure
-		 * the location is mapped, but poison works better */
-		memset(address, 0x5, real_size);
-		db->virt = address;
-		db->phys = phys;
-	}
-	return ret;
-}
-
-/**
- * Allocate a DMA buffer using the default policy.
- *
- * @param[in] db dma_buffer_t structure to fill
- * @param[in] size Size of the required memory space
- * @return Error code.
- */
-int dma_buffer_alloc(dma_buffer_t *db, size_t size)
-{
-	return dma_buffer_alloc_policy(db, size, dma_policy_default);
-}
-
-
-/**
- * Free a DMA buffer.
- *
- * @param[in] db dma_buffer_t structure buffer of which will be freed
- */
-void dma_buffer_free(dma_buffer_t *db)
-{
-	if (db->virt) {
-		dmamem_unmap_anonymous(db->virt);
-		db->virt = NULL;
-		db->phys = 0;
-	}
-}
-
-/**
- * Convert a pointer inside a buffer to physical address.
- *
- * @param[in] db Buffer at which virt is pointing
- * @param[in] virt Pointer somewhere inside db
- */
-uintptr_t dma_buffer_phys(const dma_buffer_t *db, void *virt)
-{
-	return db->phys + (virt - db->virt);
-}
-
-/**
- * @}
- */
Index: uspace/lib/usbhost/src/hcd.c
===================================================================
--- uspace/lib/usbhost/src/hcd.c	(revision 24c41ba116f95ed1d1214737917cffc3db92868d)
+++ uspace/lib/usbhost/src/hcd.c	(revision cc63815c45bec69f85b3455ff1f4c5dcc15c228c)
@@ -95,5 +95,5 @@
  * lookup.
  */
-static void irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev)
+static void irq_handler(ipc_call_t *call, ddf_dev_t *dev)
 {
 	assert(dev);
@@ -107,5 +107,5 @@
  * Worker for the HW interrupt replacement fibril.
  */
-static int interrupt_polling(void *arg)
+static errno_t interrupt_polling(void *arg)
 {
 	bus_t *bus = arg;
@@ -154,5 +154,5 @@
  * @return Negative error code.
  */
-static int hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+static errno_t hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
 {
 	assert(hcd);
@@ -162,6 +162,8 @@
 		return ENOTSUP;
 
-	const int irq = hc_driver->irq_code_gen(&irq_code, hcd, hw_res);
-	if (irq < 0) {
+	int irq;
+	errno_t ret;
+	ret = hc_driver->irq_code_gen(&irq_code, hcd, hw_res, &irq);
+	if (ret != EOK) {
 		usb_log_error("Failed to generate IRQ code: %s.",
 		    str_error(irq));
@@ -170,7 +172,8 @@
 
 	/* Register handler to avoid interrupt lockup */
-	const int irq_cap = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code);
+	int irq_cap;
+	ret = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code, &irq_cap);
 	irq_code_clean(&irq_code);
-	if (irq_cap < 0) {
+	if (ret != EOK) {
 		usb_log_error("Failed to register interrupt handler: %s.",
 		    str_error(irq_cap));
@@ -179,5 +182,5 @@
 
 	/* Enable interrupts */
-	int ret = hcd_ddf_enable_interrupt(hcd, irq);
+	ret = hcd_ddf_enable_interrupt(hcd, irq);
 	if (ret != EOK) {
 		usb_log_error("Failed to enable interrupts: %s.",
@@ -202,7 +205,7 @@
  * @return Error code
  */
-int hc_dev_add(ddf_dev_t *device)
-{
-	int ret = EOK;
+errno_t hc_dev_add(ddf_dev_t *device)
+{
+	errno_t ret = EOK;
 	assert(device);
 
@@ -308,7 +311,7 @@
 }
 
-int hc_dev_remove(ddf_dev_t *dev)
-{
-	int err;
+errno_t hc_dev_remove(ddf_dev_t *dev)
+{
+	errno_t err;
 	hc_device_t *hcd = dev_to_hcd(dev);
 
@@ -330,7 +333,7 @@
 }
 
-int hc_dev_gone(ddf_dev_t *dev)
-{
-	int err = ENOTSUP;
+errno_t hc_dev_gone(ddf_dev_t *dev)
+{
+	errno_t err = ENOTSUP;
 	hc_device_t *hcd = dev_to_hcd(dev);
 
@@ -343,5 +346,5 @@
 }
 
-int hc_fun_online(ddf_fun_t *fun)
+errno_t hc_fun_online(ddf_fun_t *fun)
 {
 	assert(fun);
Index: uspace/lib/usbhost/src/utility.c
===================================================================
--- uspace/lib/usbhost/src/utility.c	(revision 24c41ba116f95ed1d1214737917cffc3db92868d)
+++ uspace/lib/usbhost/src/utility.c	(revision cc63815c45bec69f85b3455ff1f4c5dcc15c228c)
@@ -284,4 +284,98 @@
 }
 
+typedef struct joinable_fibril {
+	fid_t fid;
+	void *arg;
+	fibril_worker_t worker;
+
+	bool running;
+	fibril_mutex_t guard;
+	fibril_condvar_t dead_cv;
+} joinable_fibril_t;
+
+static int joinable_fibril_worker(void *arg)
+{
+	joinable_fibril_t *jf = arg;
+
+	jf->worker(jf->arg);
+
+	fibril_mutex_lock(&jf->guard);
+	jf->running = false;
+	fibril_mutex_unlock(&jf->guard);
+	fibril_condvar_broadcast(&jf->dead_cv);
+	return 0;
+}
+
+/**
+ * Create a fibril that is joinable. Similar to fibril_create.
+ */
+joinable_fibril_t *joinable_fibril_create(fibril_worker_t worker, void *arg)
+{
+	joinable_fibril_t *jf = calloc(1, sizeof(joinable_fibril_t));
+	if (!jf)
+		return NULL;
+
+	jf->worker = worker;
+	jf->arg = arg;
+	fibril_mutex_initialize(&jf->guard);
+	fibril_condvar_initialize(&jf->dead_cv);
+
+	if (joinable_fibril_recreate(jf)) {
+		free(jf);
+		return NULL;
+	}
+
+	return jf;
+}
+
+/**
+ * Start a joinable fibril. Similar to fibril_add_ready.
+ */
+void joinable_fibril_start(joinable_fibril_t *jf)
+{
+	assert(jf);
+	assert(!jf->running);
+
+	jf->running = true;
+	fibril_add_ready(jf->fid);
+}
+
+/**
+ * Join a joinable fibril. Not similar to anything, obviously.
+ */
+void joinable_fibril_join(joinable_fibril_t *jf)
+{
+	assert(jf);
+
+	fibril_mutex_lock(&jf->guard);
+	while (jf->running)
+		fibril_condvar_wait(&jf->dead_cv, &jf->guard);
+	fibril_mutex_unlock(&jf->guard);
+
+	jf->fid = 0;
+}
+
+/**
+ * Reinitialize a joinable fibril.
+ */
+errno_t joinable_fibril_recreate(joinable_fibril_t *jf)
+{
+	assert(!jf->fid);
+
+	jf->fid = fibril_create(joinable_fibril_worker, jf);
+	return jf->fid ? EOK : ENOMEM;
+}
+
+/**
+ * Regular fibrils clean after themselves, joinable fibrils cannot.
+ */
+void joinable_fibril_destroy(joinable_fibril_t *jf)
+{
+	if (jf) {
+		joinable_fibril_join(jf);
+		free(jf);
+	}
+}
+
 /**
  * @}
