Index: uspace/lib/c/include/bitops.h
===================================================================
--- uspace/lib/c/include/bitops.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/c/include/bitops.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -54,4 +54,8 @@
 #define BIT_RANGE_EXTRACT(type, hi, lo, value) \
     (((value) >> (lo)) & BIT_RRANGE(type, (hi) - (lo) + 1))
+
+/** Insert @a value between bits @a hi .. @a lo. */
+#define BIT_RANGE_INSERT(type, hi, lo, value) \
+    (((value) & BIT_RRANGE(type, (hi) - (lo) + 1)) << (lo))
 
 /** Return position of first non-zero bit from left (i.e. [log_2(arg)]).
Index: uspace/lib/c/include/byteorder.h
===================================================================
--- uspace/lib/c/include/byteorder.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/c/include/byteorder.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -85,4 +85,9 @@
 #define ntohl(n)  uint32_t_be2host((n))
 
+#define uint8_t_be2host(n)  (n)
+#define uint8_t_le2host(n)  (n)
+#define host2uint8_t_be(n)  (n)
+#define host2uint8_t_le(n)  (n)
+
 static inline uint64_t uint64_t_byteorder_swap(uint64_t n)
 {
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -44,11 +44,11 @@
 	/** Audio device pcm buffer interface */
 	AUDIO_PCM_BUFFER_IFACE,
-	
+
 	/** Network interface controller interface */
 	NIC_DEV_IFACE,
-		
+
 	/** IEEE 802.11 interface controller interface */
 	IEEE80211_DEV_IFACE,
-	
+
 	/** Interface provided by any PCI device. */
 	PCI_DEV_IFACE,
@@ -56,5 +56,7 @@
 	/** Interface provided by any USB device. */
 	USB_DEV_IFACE,
-	/** Interface provided by USB host controller. */
+	/** Interface provided by USB diagnostic devices. */
+	USBDIAG_DEV_IFACE,
+	/** Interface provided by USB host controller to USB device. */
 	USBHC_DEV_IFACE,
 	/** Interface provided by USB HID devices. */
Index: uspace/lib/drv/Makefile
===================================================================
--- uspace/lib/drv/Makefile	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/Makefile	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -47,4 +47,5 @@
 	generic/remote_usb.c \
 	generic/remote_pci.c \
+	generic/remote_usbdiag.c \
 	generic/remote_usbhc.c \
 	generic/remote_usbhid.c \
Index: uspace/lib/drv/generic/dev_iface.c
===================================================================
--- uspace/lib/drv/generic/dev_iface.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/generic/dev_iface.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -48,4 +48,5 @@
 #include "remote_ieee80211.h"
 #include "remote_usb.h"
+#include "remote_usbdiag.h"
 #include "remote_usbhc.h"
 #include "remote_usbhid.h"
@@ -65,4 +66,5 @@
 		[PCI_DEV_IFACE] = &remote_pci_iface,
 		[USB_DEV_IFACE] = &remote_usb_iface,
+		[USBDIAG_DEV_IFACE] = &remote_usbdiag_iface,
 		[USBHC_DEV_IFACE] = &remote_usbhc_iface,
 		[USBHID_DEV_IFACE] = &remote_usbhid_iface,
@@ -85,5 +87,5 @@
 	if (iface_method_idx >= rem_iface->method_count)
 		return NULL;
-	
+
 	return rem_iface->methods[iface_method_idx];
 }
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/generic/driver.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -971,5 +971,5 @@
 }
 
-int ddf_driver_main(const driver_t *drv)
+errno_t ddf_driver_main(const driver_t *drv)
 {
 	/*
Index: uspace/lib/drv/generic/private/remote_usbdiag.h
===================================================================
--- uspace/lib/drv/generic/private/remote_usbdiag.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/drv/generic/private/remote_usbdiag.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Petr Manek
+ * 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 libdrv
+* @{
+*/
+/** @file
+*/
+
+#ifndef LIBDRV_REMOTE_USBDIAG_H_
+#define LIBDRV_REMOTE_USBDIAG_H_
+
+extern remote_iface_t remote_usbdiag_iface;
+
+#endif
+
+/**
+* @}
+*/
Index: uspace/lib/drv/generic/private/remote_usbhc.h
===================================================================
--- uspace/lib/drv/generic/private/remote_usbhc.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/generic/private/remote_usbhc.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Vojtech Horky
+ * Copyright (c) 2017 Ondrej Hlavaty
  * All rights reserved.
  *
Index: uspace/lib/drv/generic/remote_usb.c
===================================================================
--- uspace/lib/drv/generic/remote_usb.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/generic/remote_usb.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -61,14 +61,5 @@
 
 typedef enum {
-	IPC_M_USB_GET_MY_INTERFACE,
-	IPC_M_USB_GET_MY_DEVICE_HANDLE,
-	IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
-	IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
-	IPC_M_USB_DEVICE_ENUMERATE,
-	IPC_M_USB_DEVICE_REMOVE,
-	IPC_M_USB_REGISTER_ENDPOINT,
-	IPC_M_USB_UNREGISTER_ENDPOINT,
-	IPC_M_USB_READ,
-	IPC_M_USB_WRITE,
+	IPC_M_USB_GET_MY_DESCRIPTION,
 } usb_iface_funcs_t;
 
@@ -79,235 +70,28 @@
  * @return Error code.
  */
-errno_t usb_get_my_interface(async_exch_t *exch, int *usb_iface)
-{
-	if (!exch)
-		return EBADMEM;
-	sysarg_t iface_no;
-	const errno_t ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_GET_MY_INTERFACE, &iface_no);
-	if (ret == EOK && usb_iface)
-		*usb_iface = (int)iface_no;
-	return ret;
-}
-
-/** Tell devman handle of the usb device function.
- *
- * @param[in]  exch   IPC communication exchange
- * @param[out] handle devman handle of the HC used by the target device.
- *
- * @return Error code.
- *
- */
-errno_t usb_get_my_device_handle(async_exch_t *exch, devman_handle_t *handle)
-{
-	devman_handle_t h = 0;
-	const errno_t ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_GET_MY_DEVICE_HANDLE, &h);
-	if (ret == EOK && handle)
-		*handle = (devman_handle_t)h;
-	return ret;
-}
-
-/** Reserve default USB address.
- * @param[in] exch IPC communication exchange
- * @param[in] speed Communication speed of the newly attached device
- * @return Error code.
- */
-errno_t usb_reserve_default_address(async_exch_t *exch, usb_speed_t speed)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_RESERVE_DEFAULT_ADDRESS, speed);
-}
-
-/** Release default USB address.
- *
- * @param[in] exch IPC communication exchange
- *
- * @return Error code.
- *
- */
-errno_t usb_release_default_address(async_exch_t *exch)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_1_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
-}
-
-/** Trigger USB device enumeration
- *
- * @param[in]  exch   IPC communication exchange
- * @param[out] handle Identifier of the newly added device (if successful)
- *
- * @return Error code.
- *
- */
-errno_t usb_device_enumerate(async_exch_t *exch, unsigned port)
-{
-	if (!exch)
-		return EBADMEM;
-	const errno_t ret = async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_DEVICE_ENUMERATE, port);
-	return ret;
-}
-
-/** Trigger USB device enumeration
- *
- * @param[in] exch   IPC communication exchange
- * @param[in] handle Identifier of the device
- *
- * @return Error code.
- *
- */
-errno_t usb_device_remove(async_exch_t *exch, unsigned port)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_DEVICE_REMOVE, port);
-}
-
-static_assert(sizeof(sysarg_t) >= 4);
-
-typedef union {
-	uint8_t arr[sizeof(sysarg_t)];
-	sysarg_t arg;
-} pack8_t;
-
-errno_t usb_register_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
-    usb_transfer_type_t type, usb_direction_t direction,
-    size_t mps, unsigned packets, unsigned interval)
-{
-	if (!exch)
-		return EBADMEM;
-	pack8_t pack;
-	pack.arr[0] = type;
-	pack.arr[1] = direction;
-	pack.arr[2] = interval;
-	pack.arr[3] = packets;
-
-	return async_req_4_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_REGISTER_ENDPOINT, endpoint, pack.arg, mps);
-
-}
-
-errno_t usb_unregister_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
-    usb_direction_t direction)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_3_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_UNREGISTER_ENDPOINT, endpoint, direction);
-}
-
-errno_t usb_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
-    void *data, size_t size, size_t *rec_size)
+errno_t usb_get_my_description(async_exch_t *exch, usb_device_desc_t *desc)
 {
 	if (!exch)
 		return EBADMEM;
 
-	if (size == 0 && setup == 0)
-		return EOK;
+	usb_device_desc_t tmp_desc;
 
-	/* Make call identifying target USB device and type of transfer. */
-	aid_t opening_request = async_send_4(exch,
-	    DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_READ, endpoint,
-	    (setup & UINT32_MAX), (setup >> 32), NULL);
-
-	if (opening_request == 0) {
-		return ENOMEM;
-	}
-
-	/* Retrieve the data. */
-	ipc_call_t data_request_call;
-	aid_t data_request =
-	    async_data_read(exch, data, size, &data_request_call);
-
-	if (data_request == 0) {
-		// FIXME: How to let the other side know that we want to abort?
-		async_forget(opening_request);
-		return ENOMEM;
-	}
-
-	/* Wait for the answer. */
-	errno_t data_request_rc;
-	errno_t opening_request_rc;
-	async_wait_for(data_request, &data_request_rc);
-	async_wait_for(opening_request, &opening_request_rc);
-
-	if (data_request_rc != EOK) {
-		/* Prefer the return code of the opening request. */
-		if (opening_request_rc != EOK) {
-			return (errno_t) opening_request_rc;
-		} else {
-			return (errno_t) data_request_rc;
-		}
-	}
-	if (opening_request_rc != EOK) {
-		return (errno_t) opening_request_rc;
-	}
-
-	*rec_size = IPC_GET_ARG2(data_request_call);
-	return EOK;
+	const errno_t ret = async_req_1_5(exch, DEV_IFACE_ID(USB_DEV_IFACE),
+	    IPC_M_USB_GET_MY_DESCRIPTION,
+	    (sysarg_t *) &tmp_desc.address,
+	    (sysarg_t *) &tmp_desc.depth,
+	    (sysarg_t *) &tmp_desc.speed,
+	    &tmp_desc.handle,
+	    (sysarg_t *) &tmp_desc.iface);
+	if (ret == EOK && desc)
+		*desc = tmp_desc;
+	return ret;
 }
 
-errno_t usb_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
-    const void *data, size_t size)
-{
-	if (!exch)
-		return EBADMEM;
-
-	if (size == 0 && setup == 0)
-		return EOK;
-
-	aid_t opening_request = async_send_5(exch,
-	    DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
-	    (setup & UINT32_MAX), (setup >> 32), NULL);
-
-	if (opening_request == 0) {
-		return ENOMEM;
-	}
-
-	/* Send the data if any. */
-	if (size > 0) {
-		const errno_t ret = async_data_write_start(exch, data, size);
-		if (ret != EOK) {
-			async_forget(opening_request);
-			return ret;
-		}
-	}
-
-	/* Wait for the answer. */
-	errno_t opening_request_rc;
-	async_wait_for(opening_request, &opening_request_rc);
-
-	return (errno_t) opening_request_rc;
-}
-
-static void remote_usb_get_my_interface(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_get_my_device_handle(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
-static void remote_usb_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
+static void remote_usb_get_my_description(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
 
 /** Remote USB interface operations. */
 static const remote_iface_func_ptr_t remote_usb_iface_ops [] = {
-	[IPC_M_USB_GET_MY_INTERFACE] = remote_usb_get_my_interface,
-	[IPC_M_USB_GET_MY_DEVICE_HANDLE] = remote_usb_get_my_device_handle,
-	[IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usb_reserve_default_address,
-	[IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usb_release_default_address,
-	[IPC_M_USB_DEVICE_ENUMERATE] = remote_usb_device_enumerate,
-	[IPC_M_USB_DEVICE_REMOVE] = remote_usb_device_remove,
-	[IPC_M_USB_REGISTER_ENDPOINT] = remote_usb_register_endpoint,
-	[IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usb_unregister_endpoint,
-	[IPC_M_USB_READ] = remote_usb_read,
-	[IPC_M_USB_WRITE] = remote_usb_write,
+	[IPC_M_USB_GET_MY_DESCRIPTION] = remote_usb_get_my_description,
 };
 
@@ -319,304 +103,28 @@
 };
 
-void remote_usb_get_my_interface(ddf_fun_t *fun, void *iface,
+void remote_usb_get_my_description(ddf_fun_t *fun, void *iface,
     ipc_callid_t callid, ipc_call_t *call)
 {
 	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
 
-	if (usb_iface->get_my_interface == NULL) {
+	if (usb_iface->get_my_description == NULL) {
 		async_answer_0(callid, ENOTSUP);
 		return;
 	}
 
-	int iface_no;
-	const errno_t ret = usb_iface->get_my_interface(fun, &iface_no);
+	usb_device_desc_t desc;
+	const errno_t ret = usb_iface->get_my_description(fun, &desc);
 	if (ret != EOK) {
 		async_answer_0(callid, ret);
 	} else {
-		async_answer_1(callid, EOK, iface_no);
+		async_answer_5(callid, EOK,
+		    (sysarg_t) desc.address,
+		    (sysarg_t) desc.depth,
+		    (sysarg_t) desc.speed,
+		    desc.handle,
+		    desc.iface);
 	}
 }
 
-void remote_usb_get_my_device_handle(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->get_my_device_handle == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	devman_handle_t handle;
-	const errno_t ret = usb_iface->get_my_device_handle(fun, &handle);
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	}
-
-	async_answer_1(callid, EOK, (sysarg_t) handle);
-}
-
-void remote_usb_reserve_default_address(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->reserve_default_address == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
-	const errno_t ret = usb_iface->reserve_default_address(fun, speed);
-	async_answer_0(callid, ret);
-}
-
-void remote_usb_release_default_address(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->release_default_address == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const errno_t ret = usb_iface->release_default_address(fun);
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_device_enumerate(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->device_enumerate == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const unsigned port = DEV_IPC_GET_ARG1(*call);
-	const errno_t ret = usb_iface->device_enumerate(fun, port);
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_device_remove(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->device_remove == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const unsigned port = DEV_IPC_GET_ARG1(*call);
-	const errno_t ret = usb_iface->device_remove(fun, port);
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_register_endpoint(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (!usb_iface->register_endpoint) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_endpoint_t endpoint = DEV_IPC_GET_ARG1(*call);
-	const pack8_t pack = { .arg = DEV_IPC_GET_ARG2(*call)};
-	const size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
-
-	const usb_transfer_type_t transfer_type = pack.arr[0];
-	const usb_direction_t direction = pack.arr[1];
-	unsigned packets = pack.arr[2];
-	unsigned interval = pack.arr[3];
-
-	const errno_t ret = usb_iface->register_endpoint(fun, endpoint,
-	    transfer_type, direction, max_packet_size, packets, interval);
-
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_unregister_endpoint(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (!usb_iface->unregister_endpoint) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG1(*call);
-	usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG2(*call);
-
-	errno_t rc = usb_iface->unregister_endpoint(fun, endpoint, direction);
-
-	async_answer_0(callid, rc);
-}
-
-typedef struct {
-	ipc_callid_t caller;
-	ipc_callid_t data_caller;
-	void *buffer;
-} async_transaction_t;
-
-static void async_transaction_destroy(async_transaction_t *trans)
-{
-	if (trans == NULL) {
-		return;
-	}
-	if (trans->buffer != NULL) {
-		free(trans->buffer);
-	}
-
-	free(trans);
-}
-
-static async_transaction_t *async_transaction_create(ipc_callid_t caller)
-{
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	if (trans == NULL) {
-		return NULL;
-	}
-
-	trans->caller = caller;
-	trans->data_caller = 0;
-	trans->buffer = NULL;
-
-	return trans;
-}
-
-static void callback_out(errno_t outcome, void *arg)
-{
-	async_transaction_t *trans = arg;
-
-	async_answer_0(trans->caller, outcome);
-
-	async_transaction_destroy(trans);
-}
-
-static void callback_in(errno_t outcome, size_t actual_size, void *arg)
-{
-	async_transaction_t *trans = (async_transaction_t *)arg;
-
-	if (outcome != EOK) {
-		async_answer_0(trans->caller, outcome);
-		if (trans->data_caller) {
-			async_answer_0(trans->data_caller, EINTR);
-		}
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	if (trans->data_caller) {
-		async_data_read_finalize(trans->data_caller,
-		    trans->buffer, actual_size);
-	}
-
-	async_answer_0(trans->caller, EOK);
-
-	async_transaction_destroy(trans);
-}
-
-void remote_usb_read(
-    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
-{
-	assert(fun);
-	assert(iface);
-	assert(call);
-
-	const usb_iface_t *usb_iface = iface;
-
-	if (!usb_iface->read) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
-	const uint64_t setup =
-	    ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
-	    (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
-
-	async_transaction_t *trans = async_transaction_create(callid);
-	if (trans == NULL) {
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	size_t size = 0;
-	if (!async_data_read_receive(&trans->data_caller, &size)) {
-		async_answer_0(callid, EPARTY);
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	trans->buffer = malloc(size);
-	if (trans->buffer == NULL) {
-		async_answer_0(trans->data_caller, ENOMEM);
-		async_answer_0(callid, ENOMEM);
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	const errno_t rc = usb_iface->read(
-	    fun, ep, setup, trans->buffer, size, callback_in, trans);
-
-	if (rc != EOK) {
-		async_answer_0(trans->data_caller, rc);
-		async_answer_0(callid, rc);
-		async_transaction_destroy(trans);
-	}
-}
-
-void remote_usb_write(
-    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
-{
-	assert(fun);
-	assert(iface);
-	assert(call);
-
-	const usb_iface_t *usb_iface = iface;
-
-	if (!usb_iface->write) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
-	const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
-	const uint64_t setup =
-	    ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
-	    (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
-
-	async_transaction_t *trans = async_transaction_create(callid);
-	if (trans == NULL) {
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	size_t size = 0;
-	if (data_buffer_len > 0) {
-		const errno_t rc = async_data_write_accept(&trans->buffer, false,
-		    1, data_buffer_len, 0, &size);
-
-		if (rc != EOK) {
-			async_answer_0(callid, rc);
-			async_transaction_destroy(trans);
-			return;
-		}
-	}
-
-	const errno_t rc = usb_iface->write(
-	    fun, ep, setup, trans->buffer, size, callback_out, trans);
-
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_transaction_destroy(trans);
-	}
-}
 /**
  * @}
Index: uspace/lib/drv/generic/remote_usbdiag.c
===================================================================
--- uspace/lib/drv/generic/remote_usbdiag.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/drv/generic/remote_usbdiag.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2017 Petr Manek
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ * USB diagnostic device remote interface.
+ */
+
+#include <async.h>
+#include <assert.h>
+#include <macros.h>
+#include <errno.h>
+#include <devman.h>
+
+#include "usbdiag_iface.h"
+
+typedef enum {
+	IPC_M_USBDIAG_TEST_IN,
+	IPC_M_USBDIAG_TEST_OUT,
+} usb_iface_funcs_t;
+
+async_sess_t *usbdiag_connect(devman_handle_t handle)
+{
+	return devman_device_connect(handle, IPC_FLAG_BLOCKING);
+}
+
+void usbdiag_disconnect(async_sess_t *sess)
+{
+	if (sess)
+		async_hangup(sess);
+}
+
+errno_t usbdiag_test_in(async_exch_t *exch, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
+{
+	if (!exch)
+		return EBADMEM;
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_TEST_IN, &answer);
+
+	errno_t rc = async_data_write_start(exch, params, sizeof(usbdiag_test_params_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_read_start(exch, results, sizeof(usbdiag_test_results_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	async_exchange_end(exch);
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return (errno_t) retval;
+}
+
+errno_t usbdiag_test_out(async_exch_t *exch, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
+{
+	if (!exch)
+		return EBADMEM;
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_TEST_OUT, &answer);
+
+	errno_t rc = async_data_write_start(exch, params, sizeof(usbdiag_test_params_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_read_start(exch, results, sizeof(usbdiag_test_results_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	async_exchange_end(exch);
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return (errno_t) retval;
+}
+
+static void remote_usbdiag_test_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbdiag_test_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+
+/** Remote USB diagnostic interface operations. */
+static const remote_iface_func_ptr_t remote_usbdiag_iface_ops [] = {
+	[IPC_M_USBDIAG_TEST_IN] = remote_usbdiag_test_in,
+	[IPC_M_USBDIAG_TEST_OUT] = remote_usbdiag_test_out
+};
+
+/** Remote USB diagnostic interface structure. */
+const remote_iface_t remote_usbdiag_iface = {
+	.method_count = ARRAY_SIZE(remote_usbdiag_iface_ops),
+	.methods = remote_usbdiag_iface_ops,
+};
+
+void remote_usbdiag_test_in(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbdiag_iface_t *diag_iface = (usbdiag_iface_t *) iface;
+
+	size_t size;
+	ipc_callid_t data_callid;
+	if (!async_data_write_receive(&data_callid, &size)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	if (size != sizeof(usbdiag_test_params_t)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	usbdiag_test_params_t params;
+	if (async_data_write_finalize(data_callid, &params, size) != EOK) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	usbdiag_test_results_t results;
+	const errno_t ret = !diag_iface->test_in ? ENOTSUP : diag_iface->test_in(fun, &params, &results);
+
+	if (ret != EOK) {
+		async_answer_0(callid, ret);
+		return;
+	}
+
+	if (!async_data_read_receive(&data_callid, &size)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	if (size != sizeof(usbdiag_test_results_t)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	if (async_data_read_finalize(data_callid, &results, size) != EOK) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	async_answer_0(callid, ret);
+}
+
+void remote_usbdiag_test_out(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbdiag_iface_t *diag_iface = (usbdiag_iface_t *) iface;
+
+	size_t size;
+	ipc_callid_t data_callid;
+	if (!async_data_write_receive(&data_callid, &size)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	if (size != sizeof(usbdiag_test_params_t)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	usbdiag_test_params_t params;
+	if (async_data_write_finalize(data_callid, &params, size) != EOK) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	usbdiag_test_results_t results;
+	const errno_t ret = !diag_iface->test_out ? ENOTSUP : diag_iface->test_out(fun, &params, &results);
+
+	if (ret != EOK) {
+		async_answer_0(callid, ret);
+		return;
+	}
+
+	if (!async_data_read_receive(&data_callid, &size)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	if (size != sizeof(usbdiag_test_results_t)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	if (async_data_read_finalize(data_callid, &results, size) != EOK) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	async_answer_0(callid, ret);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2010-2011 Vojtech Horky
+ * Copyright (c) 2010 Vojtech Horky
  * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty
  * All rights reserved.
  *
@@ -35,83 +36,91 @@
 
 #include <async.h>
+#include <macros.h>
 #include <errno.h>
-#include <assert.h>
-#include <macros.h>
+#include <devman.h>
+#include <as.h>
 
 #include "usbhc_iface.h"
 #include "ddf/driver.h"
 
-#define USB_MAX_PAYLOAD_SIZE 1020
-
-/** IPC methods for communication with HC through DDF interface.
- *
- * Notes for async methods:
- *
- * Methods for sending data to device (OUT transactions)
- * - e.g. IPC_M_USBHC_INTERRUPT_OUT -
- * always use the same semantics:
- * - first, IPC call with given method is made
- *   - argument #1 is target address
- *   - argument #2 is target endpoint
- *   - argument #3 is max packet size of the endpoint
- * - this call is immediately followed by IPC data write (from caller)
- * - the initial call (and the whole transaction) is answer after the
- *   transaction is scheduled by the HC and acknowledged by the device
- *   or immediately after error is detected
- * - the answer carries only the error code
- *
- * Methods for retrieving data from device (IN transactions)
- * - e.g. IPC_M_USBHC_INTERRUPT_IN -
- * also use the same semantics:
- * - first, IPC call with given method is made
- *   - argument #1 is target address
- *   - argument #2 is target endpoint
- * - this call is immediately followed by IPC data read (async version)
- * - the call is not answered until the device returns some data (or until
- *   error occurs)
- *
- * Some special methods (NO-DATA transactions) do not send any data. These
- * might behave as both OUT or IN transactions because communication parts
- * where actual buffers are exchanged are omitted.
- **
- * For all these methods, wrap functions exists. Important rule: functions
- * for IN transactions have (as parameters) buffers where retrieved data
- * will be stored. These buffers must be already allocated and shall not be
- * touch until the transaction is completed
- * (e.g. not before calling usb_wait_for() with appropriate handle).
- * OUT transactions buffers can be freed immediately after call is dispatched
- * (i.e. after return from wrapping function).
- *
- */
+
 typedef enum {
-	/** Get data from device.
-	 * See explanation at usb_iface_funcs_t (IN transaction).
-	 */
-	IPC_M_USBHC_READ,
-
-	/** Send data to device.
-	 * See explanation at usb_iface_funcs_t (OUT transaction).
-	 */
-	IPC_M_USBHC_WRITE,
+	IPC_M_USB_DEFAULT_ADDRESS_RESERVATION,
+	IPC_M_USB_DEVICE_ENUMERATE,
+	IPC_M_USB_DEVICE_REMOVE,
+	IPC_M_USB_REGISTER_ENDPOINT,
+	IPC_M_USB_UNREGISTER_ENDPOINT,
+	IPC_M_USB_TRANSFER,
 } usbhc_iface_funcs_t;
 
-errno_t usbhc_read(async_exch_t *exch, usb_address_t address,
-    usb_endpoint_t endpoint, uint64_t setup, void *data, size_t size,
-    size_t *rec_size)
-{
-	if (!exch)
-		return EBADMEM;
-
-	if (size == 0 && setup == 0)
-		return EOK;
-
-	const usb_target_t target =
-	    {{ .address = address, .endpoint = endpoint }};
-
-	/* Make call identifying target USB device and type of transfer. */
-	aid_t opening_request = async_send_4(exch,
-	    DEV_IFACE_ID(USBHC_DEV_IFACE),
-	    IPC_M_USBHC_READ, target.packed,
-	    (setup & UINT32_MAX), (setup >> 32), NULL);
+/** Reserve default USB address.
+ * @param[in] exch IPC communication exchange
+ * @return Error code.
+ */
+errno_t usbhc_reserve_default_address(async_exch_t *exch)
+{
+	if (!exch)
+		return EBADMEM;
+	return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION, true);
+}
+
+/** Release default USB address.
+ *
+ * @param[in] exch IPC communication exchange
+ *
+ * @return Error code.
+ */
+errno_t usbhc_release_default_address(async_exch_t *exch)
+{
+	if (!exch)
+		return EBADMEM;
+	return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION, false);
+}
+
+/**
+ * Trigger USB device enumeration
+ *
+ * @param[in] exch IPC communication exchange
+ * @param[in] port Port number at which the device is attached
+ * @param[in] speed Communication speed of the newly attached device
+ *
+ * @return Error code.
+ */
+errno_t usbhc_device_enumerate(async_exch_t *exch, unsigned port, usb_speed_t speed)
+{
+	if (!exch)
+		return EBADMEM;
+	const errno_t ret = async_req_3_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
+	    IPC_M_USB_DEVICE_ENUMERATE, port, speed);
+	return ret;
+}
+
+/** Trigger USB device enumeration
+ *
+ * @param[in] exch   IPC communication exchange
+ * @param[in] handle Identifier of the device
+ *
+ * @return Error code.
+ *
+ */
+errno_t usbhc_device_remove(async_exch_t *exch, unsigned port)
+{
+	if (!exch)
+		return EBADMEM;
+	return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
+	    IPC_M_USB_DEVICE_REMOVE, port);
+}
+
+errno_t usbhc_register_endpoint(async_exch_t *exch, usb_pipe_desc_t *pipe_desc,
+    const usb_endpoint_descriptors_t *desc)
+{
+	if (!exch)
+		return EBADMEM;
+
+	if (!desc)
+		return EINVAL;
+
+	aid_t opening_request = async_send_1(exch,
+	    DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_REGISTER_ENDPOINT, NULL);
 
 	if (opening_request == 0) {
@@ -119,52 +128,36 @@
 	}
 
-	/* Retrieve the data. */
-	ipc_call_t data_request_call;
-	aid_t data_request =
-	    async_data_read(exch, data, size, &data_request_call);
-
-	if (data_request == 0) {
-		// FIXME: How to let the other side know that we want to abort?
+	errno_t ret = async_data_write_start(exch, desc, sizeof(*desc));
+	if (ret != EOK) {
 		async_forget(opening_request);
-		return ENOMEM;
+		return ret;
 	}
 
 	/* Wait for the answer. */
-	errno_t data_request_rc;
 	errno_t opening_request_rc;
-	async_wait_for(data_request, &data_request_rc);
 	async_wait_for(opening_request, &opening_request_rc);
 
-	if (data_request_rc != EOK) {
-		/* Prefer the return code of the opening request. */
-		if (opening_request_rc != EOK) {
-			return (errno_t) opening_request_rc;
-		} else {
-			return (errno_t) data_request_rc;
-		}
-	}
-	if (opening_request_rc != EOK) {
+	if (opening_request_rc)
 		return (errno_t) opening_request_rc;
-	}
-
-	*rec_size = IPC_GET_ARG2(data_request_call);
+
+	usb_pipe_desc_t dest;
+	ret = async_data_read_start(exch, &dest, sizeof(dest));
+	if (ret != EOK) {
+		return ret;
+	}
+
+	if (pipe_desc)
+		*pipe_desc = dest;
+
 	return EOK;
 }
 
-errno_t usbhc_write(async_exch_t *exch, usb_address_t address,
-    usb_endpoint_t endpoint, uint64_t setup, const void *data, size_t size)
-{
-	if (!exch)
-		return EBADMEM;
-
-	if (size == 0 && setup == 0)
-		return EOK;
-
-	const usb_target_t target =
-	    {{ .address = address, .endpoint = endpoint }};
-
-	aid_t opening_request = async_send_5(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
-	    IPC_M_USBHC_WRITE, target.packed, size,
-	    (setup & UINT32_MAX), (setup >> 32), NULL);
+errno_t usbhc_unregister_endpoint(async_exch_t *exch, const usb_pipe_desc_t *pipe_desc)
+{
+	if (!exch)
+		return EBADMEM;
+
+	aid_t opening_request = async_send_1(exch,
+		DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_UNREGISTER_ENDPOINT, NULL);
 
 	if (opening_request == 0) {
@@ -172,7 +165,50 @@
 	}
 
-	/* Send the data if any. */
-	if (size > 0) {
-		const errno_t ret = async_data_write_start(exch, data, size);
+	const errno_t ret = async_data_write_start(exch, pipe_desc, sizeof(*pipe_desc));
+	if (ret != EOK) {
+		async_forget(opening_request);
+		return ret;
+	}
+
+	/* Wait for the answer. */
+	errno_t opening_request_rc;
+	async_wait_for(opening_request, &opening_request_rc);
+
+	return (errno_t) opening_request_rc;
+}
+
+/**
+ * Issue a USB transfer with a data contained in memory area. That area is
+ * temporarily shared with the HC.
+ */
+errno_t usbhc_transfer(async_exch_t *exch,
+    const usbhc_iface_transfer_request_t *req, size_t *transferred)
+{
+	if (transferred)
+		*transferred = 0;
+
+	if (!exch)
+		return EBADMEM;
+
+	ipc_call_t call;
+
+	aid_t opening_request = async_send_1(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
+	    IPC_M_USB_TRANSFER, &call);
+
+	if (opening_request == 0)
+		return ENOMEM;
+
+	const errno_t ret = async_data_write_start(exch, req, sizeof(*req));
+	if (ret != EOK) {
+		async_forget(opening_request);
+		return ret;
+	}
+
+	/* Share the data, if any. */
+	if (req->size > 0) {
+		unsigned flags = (req->dir == USB_DIRECTION_IN)
+			? AS_AREA_WRITE : AS_AREA_READ;
+
+		const errno_t ret = async_share_out_start(exch, req->buffer.virt, flags);
 		if (ret != EOK) {
 			async_forget(opening_request);
@@ -185,88 +221,88 @@
 	async_wait_for(opening_request, &opening_request_rc);
 
+	if (transferred)
+		*transferred = IPC_GET_ARG1(call);
+
 	return (errno_t) opening_request_rc;
 }
 
-static void remote_usbhc_read(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbhc_write(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-
-/** Remote USB host controller interface operations. */
-static const remote_iface_func_ptr_t remote_usbhc_iface_ops[] = {
-	[IPC_M_USBHC_READ] = remote_usbhc_read,
-	[IPC_M_USBHC_WRITE] = remote_usbhc_write,
+static void remote_usbhc_default_address_reservation(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
+
+/** Remote USB interface operations. */
+static const remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
+	[IPC_M_USB_DEFAULT_ADDRESS_RESERVATION] = remote_usbhc_default_address_reservation,
+	[IPC_M_USB_DEVICE_ENUMERATE] = remote_usbhc_device_enumerate,
+	[IPC_M_USB_DEVICE_REMOVE] = remote_usbhc_device_remove,
+	[IPC_M_USB_REGISTER_ENDPOINT] = remote_usbhc_register_endpoint,
+	[IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usbhc_unregister_endpoint,
+	[IPC_M_USB_TRANSFER] = remote_usbhc_transfer,
 };
 
-/** Remote USB host controller interface structure.
+/** Remote USB interface structure.
  */
 const remote_iface_t remote_usbhc_iface = {
 	.method_count = ARRAY_SIZE(remote_usbhc_iface_ops),
-	.methods = remote_usbhc_iface_ops
+	.methods = remote_usbhc_iface_ops,
 };
 
 typedef struct {
 	ipc_callid_t caller;
-	ipc_callid_t data_caller;
-	void *buffer;
+	usbhc_iface_transfer_request_t request;
 } async_transaction_t;
 
-static void async_transaction_destroy(async_transaction_t *trans)
-{
-	if (trans == NULL)
-		return;
-	
-	if (trans->buffer != NULL)
-		free(trans->buffer);
-	
-	free(trans);
-}
-
-static async_transaction_t *async_transaction_create(ipc_callid_t caller)
-{
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	if (trans == NULL) {
-		return NULL;
-	}
-
-	trans->caller = caller;
-	trans->data_caller = 0;
-	trans->buffer = NULL;
-
-	return trans;
-}
-
-static void callback_out(errno_t outcome, void *arg)
-{
-	async_transaction_t *trans = arg;
-
-	async_answer_0(trans->caller, outcome);
-
-	async_transaction_destroy(trans);
-}
-
-static void callback_in(errno_t outcome, size_t actual_size, void *arg)
-{
-	async_transaction_t *trans = (async_transaction_t *)arg;
-
-	if (outcome != EOK) {
-		async_answer_0(trans->caller, outcome);
-		if (trans->data_caller) {
-			async_answer_0(trans->data_caller, EINTR);
-		}
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	if (trans->data_caller) {
-		async_data_read_finalize(trans->data_caller,
-		    trans->buffer, actual_size);
-	}
-
-	async_answer_0(trans->caller, EOK);
-
-	async_transaction_destroy(trans);
-}
-
-void remote_usbhc_read(
-    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+void remote_usbhc_default_address_reservation(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
+
+	if (usbhc_iface->default_address_reservation == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const bool reserve = IPC_GET_ARG2(*call);
+	const errno_t ret = usbhc_iface->default_address_reservation(fun, reserve);
+	async_answer_0(callid, ret);
+}
+
+
+static void remote_usbhc_device_enumerate(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
+
+	if (usbhc_iface->device_enumerate == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned port = DEV_IPC_GET_ARG1(*call);
+	usb_speed_t speed = DEV_IPC_GET_ARG2(*call);
+	const errno_t ret = usbhc_iface->device_enumerate(fun, port, speed);
+	async_answer_0(callid, ret);
+}
+
+static void remote_usbhc_device_remove(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
+
+	if (usbhc_iface->device_remove == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned port = DEV_IPC_GET_ARG1(*call);
+	const errno_t ret = usbhc_iface->device_remove(fun, port);
+	async_answer_0(callid, ret);
+}
+
+static void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
 {
 	assert(fun);
@@ -274,15 +310,151 @@
 	assert(call);
 
-	const usbhc_iface_t *hc_iface = iface;
-
-	if (!hc_iface->read) {
+	const usbhc_iface_t *usbhc_iface = iface;
+
+	if (!usbhc_iface->register_endpoint) {
 		async_answer_0(callid, ENOTSUP);
 		return;
 	}
 
-	const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
-	const uint64_t setup =
-	    ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
-	    (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
+	usb_endpoint_descriptors_t ep_desc;
+	ipc_callid_t data_callid;
+	size_t len;
+
+	if (!async_data_write_receive(&data_callid, &len)
+	    || len != sizeof(ep_desc)) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	async_data_write_finalize(data_callid, &ep_desc, sizeof(ep_desc));
+
+	usb_pipe_desc_t pipe_desc;
+
+	const errno_t rc = usbhc_iface->register_endpoint(fun, &pipe_desc, &ep_desc);
+	async_answer_0(callid, rc);
+
+	if (!async_data_read_receive(&data_callid, &len)
+	    || len != sizeof(pipe_desc)) {
+		return;
+	}
+	async_data_read_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
+}
+
+static void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	assert(fun);
+	assert(iface);
+	assert(call);
+
+	const usbhc_iface_t *usbhc_iface = iface;
+
+	if (!usbhc_iface->unregister_endpoint) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	usb_pipe_desc_t pipe_desc;
+	ipc_callid_t data_callid;
+	size_t len;
+
+	if (!async_data_write_receive(&data_callid, &len)
+	    || len != sizeof(pipe_desc)) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	async_data_write_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
+
+	const errno_t rc = usbhc_iface->unregister_endpoint(fun, &pipe_desc);
+	async_answer_0(callid, rc);
+}
+
+static void async_transaction_destroy(async_transaction_t *trans)
+{
+	if (trans == NULL) {
+		return;
+	}
+	if (trans->request.buffer.virt != NULL) {
+		as_area_destroy(trans->request.buffer.virt);
+	}
+
+	free(trans);
+}
+
+static async_transaction_t *async_transaction_create(ipc_callid_t caller)
+{
+	async_transaction_t *trans = calloc(1, sizeof(async_transaction_t));
+
+	if (trans != NULL)
+		trans->caller = caller;
+
+	return trans;
+}
+
+static errno_t transfer_finished(void *arg, errno_t error, size_t transferred_size)
+{
+	async_transaction_t *trans = arg;
+	const errno_t err = async_answer_1(trans->caller, error, transferred_size);
+	async_transaction_destroy(trans);
+	return err;
+}
+
+static errno_t receive_memory_buffer(async_transaction_t *trans)
+{
+	assert(trans);
+	assert(trans->request.size > 0);
+
+	const size_t required_size = trans->request.offset + trans->request.size;
+	const unsigned required_flags =
+		(trans->request.dir == USB_DIRECTION_IN)
+		? AS_AREA_WRITE : AS_AREA_READ;
+
+	errno_t err;
+	ipc_callid_t data_callid;
+	size_t size;
+	unsigned flags;
+
+	if (!async_share_out_receive(&data_callid, &size, &flags))
+		return EPARTY;
+
+	if (size < required_size || (flags & required_flags) != required_flags) {
+		async_answer_0(data_callid, EINVAL);
+		return EINVAL;
+	}
+
+	if ((err = async_share_out_finalize(data_callid, &trans->request.buffer.virt)))
+		return err;
+
+	/*
+	 * As we're going to get physical addresses of the mapping, we must make
+	 * sure the memory is actually mapped. We must do it right now, because
+	 * the area might be read-only or write-only, and we may be unsure
+	 * later.
+	 */
+	if (flags & AS_AREA_READ) {
+		char foo = 0;
+		volatile const char *buf = trans->request.buffer.virt + trans->request.offset;
+		for (size_t i = 0; i < size; i += PAGE_SIZE)
+			foo += buf[i];
+	} else {
+		volatile char *buf = trans->request.buffer.virt + trans->request.offset;
+		for (size_t i = 0; i < size; i += PAGE_SIZE)
+			buf[i] = 0xff;
+	}
+
+	return EOK;
+}
+
+void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	assert(fun);
+	assert(iface);
+	assert(call);
+
+	const usbhc_iface_t *usbhc_iface = iface;
+
+	if (!usbhc_iface->transfer) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
 
 	async_transaction_t *trans = async_transaction_create(callid);
@@ -292,75 +464,38 @@
 	}
 
-	size_t size = 0;
-	if (!async_data_read_receive(&trans->data_caller, &size)) {
-		async_answer_0(callid, EPARTY);
-		return;
-	}
-
-	trans->buffer = malloc(size);
-	if (trans->buffer == NULL) {
-		async_answer_0(trans->data_caller, ENOMEM);
-		async_answer_0(callid, ENOMEM);
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	const errno_t rc = hc_iface->read(
-	    fun, target, setup, trans->buffer, size, callback_in, trans);
-
-	if (rc != EOK) {
-		async_answer_0(trans->data_caller, rc);
-		async_answer_0(callid, rc);
-		async_transaction_destroy(trans);
-	}
-}
-
-void remote_usbhc_write(
-    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
-{
-	assert(fun);
-	assert(iface);
-	assert(call);
-
-	const usbhc_iface_t *hc_iface = iface;
-
-	if (!hc_iface->write) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
-	const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
-	const uint64_t setup =
-	    ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
-	    (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
-
-	async_transaction_t *trans = async_transaction_create(callid);
-	if (trans == NULL) {
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	size_t size = 0;
-	if (data_buffer_len > 0) {
-		const errno_t rc = async_data_write_accept(&trans->buffer, false,
-		    1, USB_MAX_PAYLOAD_SIZE,
-		    0, &size);
-
-		if (rc != EOK) {
-			async_answer_0(callid, rc);
-			async_transaction_destroy(trans);
-			return;
-		}
-	}
-
-	const errno_t rc = hc_iface->write(
-	    fun, target, setup, trans->buffer, size, callback_out, trans);
-
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_transaction_destroy(trans);
-	}
-}
+	errno_t err = EPARTY;
+
+	ipc_callid_t data_callid;
+	size_t len;
+	if (!async_data_write_receive(&data_callid, &len)
+	    || len != sizeof(trans->request)) {
+		async_answer_0(data_callid, EINVAL);
+		goto err;
+	}
+
+	if ((err = async_data_write_finalize(data_callid,
+			    &trans->request, sizeof(trans->request))))
+		goto err;
+
+	if (trans->request.size > 0) {
+		if ((err = receive_memory_buffer(trans)))
+			goto err;
+	} else {
+		/* The value was valid on the other side, for us, its garbage. */
+		trans->request.buffer.virt = NULL;
+	}
+
+	if ((err = usbhc_iface->transfer(fun, &trans->request,
+	    &transfer_finished, trans)))
+		goto err;
+
+	/* The call will be answered asynchronously by the callback. */
+	return;
+
+err:
+	async_answer_0(callid, err);
+	async_transaction_destroy(trans);
+}
+
 /**
  * @}
Index: uspace/lib/drv/include/usb_iface.h
===================================================================
--- uspace/lib/drv/include/usb_iface.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/include/usb_iface.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -32,5 +32,5 @@
  */
 /** @file
- * @brief USB interface definition.
+ * @brief USB device interface definition.
  */
 
@@ -40,54 +40,15 @@
 #include "ddf/driver.h"
 #include <async.h>
+#include <usbhc_iface.h>
 
 typedef async_sess_t usb_dev_session_t;
 
-/** USB speeds. */
-typedef enum {
-	/** USB 1.1 low speed (1.5Mbits/s). */
-	USB_SPEED_LOW,
-	/** USB 1.1 full speed (12Mbits/s). */
-	USB_SPEED_FULL,
-	/** USB 2.0 high speed (480Mbits/s). */
-	USB_SPEED_HIGH,
-	/** Psuedo-speed serving as a boundary. */
-	USB_SPEED_MAX
-} usb_speed_t;
-
-/** USB endpoint number type.
- * Negative values could be used to indicate error.
- */
-typedef int16_t usb_endpoint_t;
-
-/** USB address type.
- * Negative values could be used to indicate error.
- */
-typedef int16_t usb_address_t;
-
-/** USB transfer type. */
-typedef enum {
-	USB_TRANSFER_CONTROL = 0,
-	USB_TRANSFER_ISOCHRONOUS = 1,
-	USB_TRANSFER_BULK = 2,
-	USB_TRANSFER_INTERRUPT = 3
-} usb_transfer_type_t;
-
-/** USB data transfer direction. */
-typedef enum {
-	USB_DIRECTION_IN,
-	USB_DIRECTION_OUT,
-	USB_DIRECTION_BOTH
-} usb_direction_t;
-
-/** USB complete address type.
- * Pair address + endpoint is identification of transaction recipient.
- */
-typedef union {
-	struct {
-		usb_address_t address;
-		usb_endpoint_t endpoint;
-	} __attribute__((packed));
-	uint32_t packed;
-} usb_target_t;
+typedef struct {
+	usb_address_t address;		/** Current USB address */
+	uint8_t depth;			/** Depth in the hub hiearchy */
+	usb_speed_t speed;		/** Speed of the device */
+	devman_handle_t handle;		/** Handle to DDF function of the HC driver */
+	int iface;			/** Interface set by multi interface driver, -1 if none */
+} usb_device_desc_t;
 
 extern usb_dev_session_t *usb_dev_connect(devman_handle_t);
@@ -95,48 +56,9 @@
 extern void usb_dev_disconnect(usb_dev_session_t *);
 
-extern errno_t usb_get_my_interface(async_exch_t *, int *);
-extern errno_t usb_get_my_device_handle(async_exch_t *, devman_handle_t *);
-
-extern errno_t usb_reserve_default_address(async_exch_t *, usb_speed_t);
-extern errno_t usb_release_default_address(async_exch_t *);
-
-extern errno_t usb_device_enumerate(async_exch_t *, unsigned port);
-extern errno_t usb_device_remove(async_exch_t *, unsigned port);
-
-extern errno_t usb_register_endpoint(async_exch_t *, usb_endpoint_t,
-    usb_transfer_type_t, usb_direction_t, size_t, unsigned, unsigned);
-extern errno_t usb_unregister_endpoint(async_exch_t *, usb_endpoint_t,
-    usb_direction_t);
-extern errno_t usb_read(async_exch_t *, usb_endpoint_t, uint64_t, void *, size_t,
-    size_t *);
-extern errno_t usb_write(async_exch_t *, usb_endpoint_t, uint64_t, const void *,
-    size_t);
-
-/** Callback for outgoing transfer. */
-typedef void (*usb_iface_transfer_out_callback_t)(errno_t, void *);
-
-/** Callback for incoming transfer. */
-typedef void (*usb_iface_transfer_in_callback_t)(errno_t, size_t, void *);
+extern errno_t usb_get_my_description(async_exch_t *, usb_device_desc_t *);
 
 /** USB device communication interface. */
 typedef struct {
-	errno_t (*get_my_interface)(ddf_fun_t *, int *);
-	errno_t (*get_my_device_handle)(ddf_fun_t *, devman_handle_t *);
-
-	errno_t (*reserve_default_address)(ddf_fun_t *, usb_speed_t);
-	errno_t (*release_default_address)(ddf_fun_t *);
-
-	errno_t (*device_enumerate)(ddf_fun_t *, unsigned);
-	errno_t (*device_remove)(ddf_fun_t *, unsigned);
-
-	errno_t (*register_endpoint)(ddf_fun_t *, usb_endpoint_t,
-	    usb_transfer_type_t, usb_direction_t, size_t, unsigned, unsigned);
-	errno_t (*unregister_endpoint)(ddf_fun_t *, usb_endpoint_t,
-	    usb_direction_t);
-
-	errno_t (*read)(ddf_fun_t *, usb_endpoint_t, uint64_t, uint8_t *, size_t,
-	    usb_iface_transfer_in_callback_t, void *);
-	errno_t (*write)(ddf_fun_t *, usb_endpoint_t, uint64_t, const uint8_t *,
-	    size_t, usb_iface_transfer_out_callback_t, void *);
+	errno_t (*get_my_description)(ddf_fun_t *, usb_device_desc_t *);
 } usb_iface_t;
 
Index: uspace/lib/drv/include/usbdiag_iface.h
===================================================================
--- uspace/lib/drv/include/usbdiag_iface.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/drv/include/usbdiag_iface.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 Petr Manek
+ * 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 libdrv
+* @addtogroup usb
+* @{
+*/
+/** @file
+* @brief USB diagnostic device interface definition.
+*/
+
+#ifndef LIBDRV_USBDIAG_IFACE_H_
+#define LIBDRV_USBDIAG_IFACE_H_
+
+#include <async.h>
+#include <usbhc_iface.h>
+#include "ddf/driver.h"
+
+#define USBDIAG_CATEGORY "usbdiag"
+
+/** Milliseconds */
+typedef unsigned long usbdiag_dur_t;
+
+/** Test parameters. */
+typedef struct usbdiag_test_params {
+	usb_transfer_type_t transfer_type;
+	size_t transfer_size;
+	usbdiag_dur_t min_duration;
+	bool validate_data;
+} usbdiag_test_params_t;
+
+/** Test results. */
+typedef struct usbdiag_test_results {
+	usbdiag_dur_t act_duration;
+	uint32_t transfer_count;
+	size_t transfer_size;
+} usbdiag_test_results_t;
+
+async_sess_t *usbdiag_connect(devman_handle_t);
+void usbdiag_disconnect(async_sess_t*);
+
+errno_t usbdiag_test_in(async_exch_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
+errno_t usbdiag_test_out(async_exch_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
+
+/** USB diagnostic device communication interface. */
+typedef struct {
+	errno_t (*test_in)(ddf_fun_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
+	errno_t (*test_out)(ddf_fun_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
+} usbdiag_iface_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/usbhc_iface.h
===================================================================
--- uspace/lib/drv/include/usbhc_iface.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/drv/include/usbhc_iface.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2010 Vojtech Horky
- * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty
  * All rights reserved.
  *
@@ -32,7 +32,7 @@
  * @{
  */
-
 /** @file
- * @brief USB host controller interface definition.
+ * @brief USB host controler interface definition. This is the interface of
+ * USB host controller function, which can be used by usb device drivers.
  */
 
@@ -41,26 +41,160 @@
 
 #include "ddf/driver.h"
-#include <usb_iface.h>
-#include <stdbool.h>
-
-extern errno_t usbhc_read(async_exch_t *, usb_address_t, usb_endpoint_t,
-    uint64_t, void *, size_t, size_t *);
-extern errno_t usbhc_write(async_exch_t *, usb_address_t, usb_endpoint_t,
-    uint64_t, const void *, size_t);
-
-/** Callback for outgoing transfer. */
-typedef void (*usbhc_iface_transfer_out_callback_t)(errno_t, void *);
-
-/** Callback for incoming transfer. */
-typedef void (*usbhc_iface_transfer_in_callback_t)(errno_t, size_t, void *);
-
-/** USB host controller communication interface. */
+#include <async.h>
+
+/** USB speeds. */
+typedef enum {
+	/** USB 1.1 low speed (1.5Mbits/s). */
+	USB_SPEED_LOW,
+	/** USB 1.1 full speed (12Mbits/s). */
+	USB_SPEED_FULL,
+	/** USB 2.0 high speed (480Mbits/s). */
+	USB_SPEED_HIGH,
+	/** USB 3.0 super speed (5Gbits/s). */
+	USB_SPEED_SUPER,
+	/** Psuedo-speed serving as a boundary. */
+	USB_SPEED_MAX
+} usb_speed_t;
+
+/** USB endpoint number type.
+ * Negative values could be used to indicate error.
+ */
+typedef uint16_t usb_endpoint_t;
+
+/** USB address type.
+ * Negative values could be used to indicate error.
+ */
+typedef uint16_t usb_address_t;
+
+/**
+ * USB Stream ID type.
+ */
+typedef uint16_t usb_stream_t;
+
+/** USB transfer type. */
+typedef enum {
+	USB_TRANSFER_CONTROL = 0,
+	USB_TRANSFER_ISOCHRONOUS = 1,
+	USB_TRANSFER_BULK = 2,
+	USB_TRANSFER_INTERRUPT = 3,
+} usb_transfer_type_t;
+
+#define USB_TRANSFER_COUNT  (USB_TRANSFER_INTERRUPT + 1)
+
+/** USB data transfer direction. */
+typedef enum {
+	USB_DIRECTION_IN,
+	USB_DIRECTION_OUT,
+	USB_DIRECTION_BOTH,
+} usb_direction_t;
+
+#define USB_DIRECTION_COUNT  (USB_DIRECTION_BOTH + 1)
+
+/** USB complete address type.
+ * Pair address + endpoint is identification of transaction recipient.
+ */
+typedef union {
+	struct {
+		usb_address_t address;
+		usb_endpoint_t endpoint;
+		usb_stream_t stream;
+	} __attribute__((packed));
+	uint64_t packed;
+} usb_target_t;
+
+// FIXME: DMA buffers shall be part of libdrv anyway.
+typedef uintptr_t dma_policy_t;
+
+typedef struct dma_buffer {
+	void *virt;
+	dma_policy_t policy;
+} dma_buffer_t;
+
+typedef struct usb_pipe_desc {
+	/** Endpoint number. */
+	usb_endpoint_t endpoint_no;
+
+	/** Endpoint transfer type. */
+	usb_transfer_type_t transfer_type;
+
+	/** Endpoint direction. */
+	usb_direction_t direction;
+
+	/**
+	 * Maximum size of one transfer. Non-periodic endpoints may handle
+	 * bigger transfers, but those can be split into multiple USB transfers.
+	 */
+	size_t max_transfer_size;
+
+	/** Constraints on buffers to be transferred without copying */
+	dma_policy_t transfer_buffer_policy;
+} usb_pipe_desc_t;
+
+typedef struct usb_pipe_transfer_request {
+	usb_direction_t dir;
+	usb_endpoint_t endpoint;
+	usb_stream_t stream;
+
+	uint64_t setup;			/**< Valid iff the transfer is of control type */
+
+	/**
+	 * The DMA buffer to share. Must be at least offset + size large. Is
+	 * patched after being transmitted over IPC, so the pointer is still
+	 * valid.
+	 */
+	dma_buffer_t buffer;
+	size_t offset;			/**< Offset to the buffer */
+	size_t size;			/**< Requested size. */
+} usbhc_iface_transfer_request_t;
+
+/** This structure follows standard endpoint descriptor + superspeed companion
+ * descriptor, and exists to avoid dependency of libdrv on libusb. Keep the
+ * internal fields named exactly like their source (because we want to use the
+ * same macros to access them).
+ * Callers shall fill it with bare contents of respective descriptors (in usb endianity).
+ */
+typedef struct usb_endpoint_descriptors {
+	struct {
+		uint8_t endpoint_address;
+		uint8_t attributes;
+		uint16_t max_packet_size;
+		uint8_t poll_interval;
+	} endpoint;
+
+	/* Superspeed companion descriptor */
+	struct companion_desc_t {
+		uint8_t max_burst;
+		uint8_t attributes;
+		uint16_t bytes_per_interval;
+	} companion;
+} usb_endpoint_descriptors_t;
+
+extern errno_t usbhc_reserve_default_address(async_exch_t *);
+extern errno_t usbhc_release_default_address(async_exch_t *);
+
+extern errno_t usbhc_device_enumerate(async_exch_t *, unsigned, usb_speed_t);
+extern errno_t usbhc_device_remove(async_exch_t *, unsigned);
+
+extern errno_t usbhc_register_endpoint(async_exch_t *, usb_pipe_desc_t *, const usb_endpoint_descriptors_t *);
+extern errno_t usbhc_unregister_endpoint(async_exch_t *, const usb_pipe_desc_t *);
+
+extern errno_t usbhc_transfer(async_exch_t *, const usbhc_iface_transfer_request_t *, size_t *);
+
+/** Callback for outgoing transfer */
+typedef errno_t (*usbhc_iface_transfer_callback_t)(void *, int, size_t);
+
+/** USB device communication interface. */
 typedef struct {
-	errno_t (*read)(ddf_fun_t *, usb_target_t, uint64_t, uint8_t *, size_t,
-	    usbhc_iface_transfer_in_callback_t, void *);
-	errno_t (*write)(ddf_fun_t *, usb_target_t, uint64_t, const uint8_t *,
-	    size_t, usbhc_iface_transfer_out_callback_t, void *);
+	errno_t (*default_address_reservation)(ddf_fun_t *, bool);
+
+	errno_t (*device_enumerate)(ddf_fun_t *, unsigned, usb_speed_t);
+	errno_t (*device_remove)(ddf_fun_t *, unsigned);
+
+	errno_t (*register_endpoint)(ddf_fun_t *, usb_pipe_desc_t *, const usb_endpoint_descriptors_t *);
+	errno_t (*unregister_endpoint)(ddf_fun_t *, const usb_pipe_desc_t *);
+
+	errno_t (*transfer)(ddf_fun_t *, const usbhc_iface_transfer_request_t *,
+	    usbhc_iface_transfer_callback_t, void *);
 } usbhc_iface_t;
-
 
 #endif
Index: uspace/lib/pcm/src/format.c
===================================================================
--- uspace/lib/pcm/src/format.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/pcm/src/format.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -41,9 +41,4 @@
 
 #include "format.h"
-
-#define uint8_t_le2host(x) (x)
-#define host2uint8_t_le(x) (x)
-#define uint8_t_be2host(x) (x)
-#define host2uint8_t_be(x) (x)
 
 #define int8_t_le2host(x) (x)
Index: uspace/lib/usb/Makefile
===================================================================
--- uspace/lib/usb/Makefile	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usb/Makefile	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -35,5 +35,7 @@
 	src/dev.c \
 	src/debug.c \
+	src/dma_buffer.c \
 	src/dump.c \
+	src/port.c \
 	src/usb.c
 
Index: uspace/lib/usb/include/usb/classes/hub.h
===================================================================
--- uspace/lib/usb/include/usb/classes/hub.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usb/include/usb/classes/hub.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -48,21 +48,58 @@
 	USB_HUB_FEATURE_HUB_OVER_CURRENT = 1,
 	USB_HUB_FEATURE_PORT_CONNECTION = 0,
-	USB_HUB_FEATURE_PORT_ENABLE = 1,
-	USB_HUB_FEATURE_PORT_SUSPEND = 2,
+	USB2_HUB_FEATURE_PORT_ENABLE = 1,
+	USB2_HUB_FEATURE_PORT_SUSPEND = 2,
 	USB_HUB_FEATURE_PORT_OVER_CURRENT = 3,
 	USB_HUB_FEATURE_PORT_RESET = 4,
+	USB3_HUB_FEATURE_PORT_LINK_STATE = 5,
 	USB_HUB_FEATURE_PORT_POWER = 8,
-	USB_HUB_FEATURE_PORT_LOW_SPEED = 9,
-	USB_HUB_FEATURE_PORT_HIGH_SPEED = 10,
+	USB2_HUB_FEATURE_PORT_LOW_SPEED = 9,
 	USB_HUB_FEATURE_C_PORT_CONNECTION = 16,
-	USB_HUB_FEATURE_C_PORT_ENABLE = 17,
-	USB_HUB_FEATURE_C_PORT_SUSPEND = 18,
+	USB2_HUB_FEATURE_C_PORT_ENABLE = 17,
+	USB2_HUB_FEATURE_C_PORT_SUSPEND = 18,
 	USB_HUB_FEATURE_C_PORT_OVER_CURRENT = 19,
 	USB_HUB_FEATURE_C_PORT_RESET = 20,
-	USB_HUB_FEATURE_PORT_TEST = 21,
-	USB_HUB_FEATURE_PORT_INDICATOR = 22
+	USB2_HUB_FEATURE_PORT_TEST = 21,
+	USB2_HUB_FEATURE_PORT_INDICATOR = 22,
+	USB3_HUB_FEATURE_C_PORT_LINK_STATE = 25,
+	USB3_HUB_FEATURE_BH_PORT_RESET = 28,
+	USB3_HUB_FEATURE_C_BH_PORT_RESET = 29,
 	/* USB_HUB_FEATURE_ = , */
 } usb_hub_class_feature_t;
 
+/**
+ * Dword holding port status and changes flags.
+ *
+ * For more information refer to tables 11-15 and 11-16 in
+ * "Universal Serial Bus Specification Revision 1.1" pages 274 and 277
+ * (290 and 293 in pdf)
+ *
+ * Beware that definition of bits changed between USB 2 and 3,
+ * so some fields are prefixed with USB2 or USB3 instead.
+ */
+typedef uint32_t usb_port_status_t;
+
+#define USB_HUB_PORT_STATUS_BIT(bit)  (uint32_usb2host(1 << (bit)))
+#define USB_HUB_PORT_STATUS_CONNECTION		USB_HUB_PORT_STATUS_BIT(0)
+#define USB_HUB_PORT_STATUS_ENABLE		USB_HUB_PORT_STATUS_BIT(1)
+#define USB2_HUB_PORT_STATUS_SUSPEND		USB_HUB_PORT_STATUS_BIT(2)
+#define USB_HUB_PORT_STATUS_OC			USB_HUB_PORT_STATUS_BIT(3)
+#define USB_HUB_PORT_STATUS_RESET		USB_HUB_PORT_STATUS_BIT(4)
+
+#define USB2_HUB_PORT_STATUS_POWER		USB_HUB_PORT_STATUS_BIT(8)
+#define USB2_HUB_PORT_STATUS_LOW_SPEED		USB_HUB_PORT_STATUS_BIT(9)
+#define USB3_HUB_PORT_STATUS_POWER		USB_HUB_PORT_STATUS_BIT(9)
+#define USB2_HUB_PORT_STATUS_HIGH_SPEED		USB_HUB_PORT_STATUS_BIT(10)
+#define USB2_HUB_PORT_STATUS_TEST		USB_HUB_PORT_STATUS_BIT(11)
+#define USB2_HUB_PORT_STATUS_INDICATOR		USB_HUB_PORT_STATUS_BIT(12)
+
+#define USB_HUB_PORT_STATUS_C_CONNECTION	USB_HUB_PORT_STATUS_BIT(16)
+#define USB2_HUB_PORT_STATUS_C_ENABLE		USB_HUB_PORT_STATUS_BIT(17)
+#define USB2_HUB_PORT_STATUS_C_SUSPEND		USB_HUB_PORT_STATUS_BIT(18)
+#define USB_HUB_PORT_STATUS_C_OC		USB_HUB_PORT_STATUS_BIT(19)
+#define USB_HUB_PORT_STATUS_C_RESET		USB_HUB_PORT_STATUS_BIT(20)
+#define USB3_HUB_PORT_STATUS_C_BH_RESET		USB_HUB_PORT_STATUS_BIT(21)
+#define USB3_HUB_PORT_STATUS_C_LINK_STATE	USB_HUB_PORT_STATUS_BIT(22)
+#define USB3_HUB_PORT_STATUS_C_CONFIG_ERROR	USB_HUB_PORT_STATUS_BIT(23)
 
 /** Header of standard hub descriptor without the "variadic" part. */
@@ -71,5 +108,5 @@
 	uint8_t length;
 
-	/** Descriptor type (0x29). */
+	/** Descriptor type (0x29 or 0x2a for superspeed hub). */
 	uint8_t descriptor_type;
 
@@ -116,4 +153,6 @@
 #define HUB_CHAR_OC_PER_PORT_FLAG       (1 << 3)
 #define HUB_CHAR_NO_OC_FLAG             (1 << 4)
+
+/* These are invalid for superspeed hub */
 #define HUB_CHAR_TT_THINK_16            (1 << 5)
 #define HUB_CHAR_TT_THINK_8             (1 << 6)
@@ -143,20 +182,28 @@
 #define STATUS_BYTES(ports) ((1 + ports + 7) / 8)
 
-/**	@brief usb hub specific request types.
- *
- *	For more information see Universal Serial Bus Specification Revision 1.1 chapter 11.16.2
+/**
+ * @brief usb hub specific request types.
  */
 typedef enum {
-    /**	This request resets a value reported in the hub status.	*/
+    /** This request resets a value reported in the hub status. */
     USB_HUB_REQ_TYPE_CLEAR_HUB_FEATURE = 0x20,
     /** This request resets a value reported in the port status. */
     USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE = 0x23,
-    /** This is an optional per-port diagnostic request that returns the bus state value, as sampled at the last EOF2 point. */
+    /**
+     * This is an optional per-port diagnostic request that returns the bus
+     * state value, as sampled at the last EOF2 point.
+     */
     USB_HUB_REQ_TYPE_GET_STATE = 0xA3,
     /** This request returns the hub descriptor. */
     USB_HUB_REQ_TYPE_GET_DESCRIPTOR = 0xA0,
-    /** This request returns the current hub status and the states that have changed since the previous acknowledgment. */
+    /**
+     * This request returns the current hub status and the states that have
+     * changed since the previous acknowledgment.
+     */
     USB_HUB_REQ_TYPE_GET_HUB_STATUS = 0xA0,
-    /** This request returns the current port status and the current value of the port status change bits. */
+    /**
+     * This request returns the current port status and the current value of the
+     * port status change bits.
+     */
     USB_HUB_REQ_TYPE_GET_PORT_STATUS = 0xA3,
     /** This request overwrites the hub descriptor. */
@@ -164,6 +211,11 @@
     /** This request sets a value reported in the hub status. */
     USB_HUB_REQ_TYPE_SET_HUB_FEATURE = 0x20,
+    /**
+     * This request sets the value that the hub uses to determine the index
+     * into the Route String Index for the hub.
+     */
+    USB_HUB_REQ_TYPE_SET_HUB_DEPTH = 0x20,
     /** This request sets a value reported in the port status. */
-    USB_HUB_REQ_TYPE_SET_PORT_FEATURE = 0x23
+    USB_HUB_REQ_TYPE_SET_PORT_FEATURE = 0x23,
 } usb_hub_bm_request_type_t;
 
@@ -191,8 +243,10 @@
     /** */
     USB_HUB_STOP_TT = 11,
+    /** USB 3+ only */
+    USB_HUB_REQUEST_SET_HUB_DEPTH = 12,
 } usb_hub_request_t;
 
 /**
- *	Maximum size of usb hub descriptor in bytes
+ * Maximum size of usb hub descriptor in bytes
  */
 /* 7 (basic size) + 2*32 (port bitmasks) */
Index: uspace/lib/usb/include/usb/descriptor.h
===================================================================
--- uspace/lib/usb/include/usb/descriptor.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usb/include/usb/descriptor.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -49,4 +49,10 @@
 	USB_DESCTYPE_OTHER_SPEED_CONFIGURATION = 7,
 	USB_DESCTYPE_INTERFACE_POWER = 8,
+	/* USB 3.0 types */
+	USB_DESCTYPE_OTG = 9,
+	USB_DESCTYPE_DEBUG = 0xa,
+	USB_DESCTYPE_IFACE_ASSOC = 0xb,
+	USB_DESCTYPE_BOS = 0xf,
+	USB_DESCTYPE_DEVICE_CAP = 0x10,
 	/* Class specific */
 	USB_DESCTYPE_HID = 0x21,
@@ -54,4 +60,6 @@
 	USB_DESCTYPE_HID_PHYSICAL = 0x23,
 	USB_DESCTYPE_HUB = 0x29,
+	USB_DESCTYPE_SSPEED_HUB = 0x2a,
+	USB_DESCTYPE_SSPEED_EP_COMPANION = 0x30
 	/* USB_DESCTYPE_ = */
 } usb_descriptor_type_t;
@@ -193,8 +201,12 @@
 	/** Endpoint address together with data flow direction. */
 	uint8_t endpoint_address;
+#define USB_ED_GET_EP(ed)	((ed).endpoint_address & 0xf)
+#define USB_ED_GET_DIR(ed)	(!(((ed).endpoint_address >> 7) & 0x1))
+
 	/** Endpoint attributes.
 	 * Includes transfer type (usb_transfer_type_t).
 	 */
 	uint8_t attributes;
+#define USB_ED_GET_TRANSFER_TYPE(ed)	((ed).attributes & 0x3)
 	/** Maximum packet size.
 	 * Lower 10 bits represent the actuall size
@@ -202,18 +214,44 @@
 	 * HS INT and ISO transfers. */
 	uint16_t max_packet_size;
-
-#define ED_MPS_PACKET_SIZE_MASK  0x3ff
-#define ED_MPS_PACKET_SIZE_GET(value) \
-	((value) & ED_MPS_PACKET_SIZE_MASK)
-#define ED_MPS_TRANS_OPPORTUNITIES_GET(value) \
-	((((value) >> 10) & 0x3) + 1)
-
-	/** Polling interval in milliseconds.
-	 * Ignored for bulk and control endpoints.
-	 * Isochronous endpoints must use value 1.
-	 * Interrupt endpoints any value from 1 to 255.
+#define USB_ED_GET_MPS(ed) \
+	(uint16_usb2host((ed).max_packet_size) & 0x7ff)
+#define USB_ED_GET_ADD_OPPS(ed) \
+	((uint16_usb2host((ed).max_packet_size) >> 11) & 0x3)
+	/** Polling interval. Different semantics for various (speed, type)
+	 * pairs.
 	 */
 	uint8_t poll_interval;
 } __attribute__ ((packed)) usb_standard_endpoint_descriptor_t;
+
+/** Superspeed USB endpoint companion descriptor.
+ * See USB 3 specification, section 9.6.7.
+ */
+typedef struct {
+	/** Size of this descriptor in bytes */
+	uint8_t length;
+	/** Descriptor type (USB_DESCTYPE_SSPEED_EP_COMPANION). */
+	uint8_t descriptor_type;
+	/** The maximum number of packets the endpoint can send
+	 * or receive as part of a burst. Valid values are from 0 to 15.
+	 * The endpoint can only burst max_burst + 1 packets at a time.
+	 */
+	uint8_t max_burst;
+	/** Valid only for bulk and isochronous endpoints.
+	 * For bulk endpoints, this field contains the amount of streams
+	 * supported by the endpoint.
+	 * For isochronous endpoints, this field contains maximum
+	 * number of packets supported within a service interval.
+	 * Warning: the values returned by macros may not make any sense
+	 * for specific endpoint types.
+	 */
+	uint8_t attributes;
+#define USB_SSC_MAX_STREAMS(sscd) ((sscd).attributes & 0x1f)
+#define USB_SSC_MULT(sscd) ((sscd).attributes & 0x3)
+	/** The total number of bytes this endpoint will transfer
+	 * every service interval (SI).
+	 * This field is only valid for periodic endpoints.
+	 */
+	uint16_t bytes_per_interval;
+} __attribute__ ((packed)) usb_superspeed_endpoint_companion_descriptor_t;
 
 /** Part of standard USB HID descriptor specifying one class descriptor.
Index: uspace/lib/usb/include/usb/dma_buffer.h
===================================================================
--- uspace/lib/usb/include/usb/dma_buffer.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usb/include/usb/dma_buffer.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,115 @@
+/*
+ * 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 libusb
+ * @{
+ */
+/** @file
+ * @brief USB host controller library: DMA buffer helpers
+ *
+ * Simplifies handling of buffers accessible to hardware. Defines properties of
+ * such buffer, which can be communicated through IPC to allow higher layers to
+ * allocate a buffer that is ready to be passed to HW right away (after being
+ * shared through IPC).
+ *
+ * Currently, it is possible to allocate either completely contiguous buffers
+ * (with dma_map_anonymous) or arbitrary memory (with as_area_create). Shall the
+ * kernel be updated, this is a subject of major optimization of memory usage.
+ * The other way to do it without the kernel is building an userspace IO vector
+ * in a similar way how QEMU does it.
+ *
+ * The structures themselves are defined in usbhc_iface, because they need to be
+ * passed through IPC.
+ */
+#ifndef LIB_USB_DMA_BUFFER
+#define LIB_USB_DMA_BUFFER
+
+#include <as.h>
+#include <bitops.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <usbhc_iface.h>
+
+/**
+ * The DMA policy describes properties of the buffer. It is used in two
+ * different contexts. Either it represents requirements, which shall be
+ * satisfied to avoid copying the buffer to a more strict one. Or, it is the
+ * actual property of the buffer, which can be more strict than requested. It
+ * always holds that more bits set means more restrictive policy, and that by
+ * computing a bitwise OR one gets the restriction that holds for both.
+ *
+ * The high bits of a DMA policy represent a physical contiguity. If bit i is
+ * set, it means that chunks of a size 2^(i+1) are contiguous in memory. It
+ * shall never happen that bit i > j is set when j is not.
+ *
+ * The previous applies for i >= PAGE_WIDTH. Lower bits are used as bit flags.
+ */
+#define DMA_POLICY_FLAGS_MASK		(PAGE_SIZE - 1)
+#define DMA_POLICY_CHUNK_SIZE_MASK	(~DMA_POLICY_FLAGS_MASK)
+
+#define DMA_POLICY_4GiB	(1<<0)		/**< Must use only 32-bit addresses */
+
+#define DMA_POLICY_STRICT		(-1UL)
+#define DMA_POLICY_DEFAULT		DMA_POLICY_STRICT
+
+extern dma_policy_t dma_policy_create(unsigned, size_t);
+
+/**
+ * Get mask which defines bits of offset in chunk.
+ */
+static inline size_t dma_policy_chunk_mask(const dma_policy_t policy)
+{
+	return policy | DMA_POLICY_FLAGS_MASK;
+}
+
+extern errno_t dma_buffer_alloc(dma_buffer_t *db, size_t size);
+extern errno_t dma_buffer_alloc_policy(dma_buffer_t *, size_t, dma_policy_t);
+extern void dma_buffer_free(dma_buffer_t *);
+
+extern uintptr_t dma_buffer_phys(const dma_buffer_t *, const void *);
+
+static inline uintptr_t dma_buffer_phys_base(const dma_buffer_t *db)
+{
+	return dma_buffer_phys(db, db->virt);
+}
+
+extern errno_t dma_buffer_lock(dma_buffer_t *, void *, size_t);
+extern void dma_buffer_unlock(dma_buffer_t *, size_t);
+
+extern void dma_buffer_acquire(dma_buffer_t *);
+extern void dma_buffer_release(dma_buffer_t *);
+
+static inline bool dma_buffer_is_set(const dma_buffer_t *db)
+{
+	return !!db->virt;
+}
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usb/include/usb/port.h
===================================================================
--- uspace/lib/usb/include/usb/port.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usb/include/usb/port.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018 HelenOS xHCI team
+ * 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 libusb
+ * @{
+ */
+/** @file
+ * An USB hub port state machine.
+ *
+ * This helper structure solves a repeated problem in USB world: management of
+ * USB ports. A port is an object which receives events (connect, disconnect,
+ * reset) which are to be handled in an asynchronous way. The tricky part is
+ * that response to events has to wait for different events - the most notable
+ * being USB 2 port requiring port reset to be enabled. This problem is solved
+ * by launching separate fibril for taking the port up.
+ *
+ * This subsystem abstracts the rather complicated state machine, and offers
+ * a simple interface to announce events and leave the fibril management on the
+ * library.
+ */
+
+#ifndef LIB_USB_PORT_H
+#define LIB_USB_PORT_H
+
+#include <fibril_synch.h>
+#include <usb/usb.h>
+#include <errno.h>
+
+typedef enum {
+	PORT_DISABLED,	/* No device connected. Fibril not running. */
+	PORT_ENUMERATED,/* Device enumerated. Fibril finished succesfully. */
+	PORT_CONNECTING,/* A connected event received, fibril running. */
+	PORT_DISCONNECTING,/* A disconnected event received, fibril running. */
+	PORT_ERROR,	/* An error "in-progress". Fibril still running. */
+} usb_port_state_t;
+
+typedef struct usb_port {
+	/** Guarding all fields. Is locked in the connected op. */
+	fibril_mutex_t guard;
+	/** Current state of the port */
+	usb_port_state_t state;
+	/** CV signalled on fibril exit. */
+	fibril_condvar_t finished_cv;
+	/** CV signalled on enabled event. */
+	fibril_condvar_t enabled_cv;
+} usb_port_t;
+
+/**
+ * Callback to run the enumeration routine.
+ * Called in separate fibril with guard locked.
+ */
+typedef int (*usb_port_enumerate_t)(usb_port_t *);
+
+/**
+ * Callback to run the enumeration routine. Called in the caller fibril,
+ */
+typedef void (*usb_port_remove_t)(usb_port_t *);
+
+/* Following methods are intended to be called "from outside". */
+void usb_port_init(usb_port_t *);
+int usb_port_connected(usb_port_t *, usb_port_enumerate_t);
+void usb_port_enabled(usb_port_t *);
+void usb_port_disabled(usb_port_t *, usb_port_remove_t);
+void usb_port_fini(usb_port_t *);
+
+/* And these are to be called from the connected handler. */
+int usb_port_condvar_wait_timeout(usb_port_t *port, fibril_condvar_t *, suseconds_t);
+
+/**
+ * Wait for the enabled event to come.
+ *
+ * @return Error code:
+ *	EINTR if the device was disconnected in the meantime.
+ *	ETIMEOUT if the enabled event didn't come in 2 seconds
+ */
+static inline int usb_port_wait_for_enabled(usb_port_t *port)
+{
+	return usb_port_condvar_wait_timeout(port, &port->enabled_cv, 2000000);
+}
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/include/usb/request.h
===================================================================
--- uspace/lib/usb/include/usb/request.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usb/include/usb/request.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -71,4 +71,7 @@
 #define USB_ENDPOINT_STATUS_HALTED ((uint16_t)(1 << 0))
 
+/** Size of the USB setup packet */
+#define USB_SETUP_PACKET_SIZE 8
+
 /** Device request setup packet.
  * The setup packet describes the request.
@@ -82,4 +85,5 @@
 #define SETUP_REQUEST_TYPE_DEVICE_TO_HOST (1 << 7)
 #define SETUP_REQUEST_TYPE_HOST_TO_DEVICE (0 << 7)
+#define SETUP_REQUEST_TYPE_IS_DEVICE_TO_HOST(rt) ((rt) & (1 << 7))
 #define SETUP_REQUEST_TYPE_GET_TYPE(rt) ((rt >> 5) & 0x3)
 #define SETUP_REQUEST_TYPE_GET_RECIPIENT(rec) (rec & 0x1f)
@@ -108,8 +112,29 @@
 } __attribute__ ((packed)) usb_device_request_setup_packet_t;
 
-static_assert(sizeof(usb_device_request_setup_packet_t) == 8);
+static_assert(sizeof(usb_device_request_setup_packet_t) == USB_SETUP_PACKET_SIZE);
 
-int usb_request_needs_toggle_reset(
-    const usb_device_request_setup_packet_t *request);
+#define GET_DEVICE_DESC(size) \
+{ \
+	.request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
+	    | (USB_REQUEST_TYPE_STANDARD << 5) \
+	    | USB_REQUEST_RECIPIENT_DEVICE, \
+	.request = USB_DEVREQ_GET_DESCRIPTOR, \
+	.value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
+	.index = uint16_host2usb(0), \
+	.length = uint16_host2usb(size), \
+};
+
+#define SET_ADDRESS(address) \
+{ \
+	.request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
+	    | (USB_REQUEST_TYPE_STANDARD << 5) \
+	    | USB_REQUEST_RECIPIENT_DEVICE, \
+	.request = USB_DEVREQ_SET_ADDRESS, \
+	.value = uint16_host2usb(address), \
+	.index = uint16_host2usb(0), \
+	.length = uint16_host2usb(0), \
+};
+
+#define CTRL_PIPE_MIN_PACKET_SIZE 8
 
 #endif
Index: uspace/lib/usb/include/usb/usb.h
===================================================================
--- uspace/lib/usb/include/usb/usb.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usb/include/usb/usb.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -39,5 +39,5 @@
 #include <stdint.h>
 #include <types/common.h>
-#include <usb_iface.h>
+#include <usbhc_iface.h>
 
 /** Convert 16bit value from native (host) endianness to USB endianness. */
@@ -61,4 +61,9 @@
 {
 	return (s == USB_SPEED_FULL) || (s == USB_SPEED_LOW);
+}
+
+static inline bool usb_speed_is_valid(const usb_speed_t s)
+{
+	return (s >= USB_SPEED_LOW) && (s < USB_SPEED_MAX);
 }
 
@@ -97,5 +102,5 @@
 static inline bool usb_address_is_valid(usb_address_t a)
 {
-	return (a >= USB_ADDRESS_DEFAULT) && (a <= USB11_ADDRESS_MAX);
+	return a <= USB11_ADDRESS_MAX;
 }
 
@@ -103,6 +108,9 @@
 #define USB_ENDPOINT_DEFAULT_CONTROL 0
 
-/** Maximum endpoint number in USB 1.1. */
-#define USB11_ENDPOINT_MAX 16
+/** Maximum endpoint number in USB */
+#define USB_ENDPOINT_MAX 16
+
+/** There might be two directions for every endpoint number (except 0) */
+#define USB_ENDPOINT_COUNT (2 * USB_ENDPOINT_MAX)
 
 /** Check USB endpoint for allowed values.
@@ -115,17 +123,19 @@
 static inline bool usb_endpoint_is_valid(usb_endpoint_t ep)
 {
-	return (ep >= USB_ENDPOINT_DEFAULT_CONTROL) &&
-	    (ep < USB11_ENDPOINT_MAX);
+	return ep < USB_ENDPOINT_MAX;
 }
 
-/** Check USB target for allowed values (address and endpoint).
+/**
+ * Check USB target for allowed values (address, endpoint, stream).
  *
  * @param target.
  * @return True, if values are wihtin limits, false otherwise.
  */
-static inline bool usb_target_is_valid(usb_target_t target)
+static inline bool usb_target_is_valid(const usb_target_t *target)
 {
-	return usb_address_is_valid(target.address) &&
-	    usb_endpoint_is_valid(target.endpoint);
+	return usb_address_is_valid(target->address) &&
+	    usb_endpoint_is_valid(target->endpoint);
+
+	// A 16-bit Stream ID is always valid.
 }
 
@@ -136,14 +146,9 @@
  * @return Whether @p a and @p b points to the same pipe on the same device.
  */
-static inline int usb_target_same(usb_target_t a, usb_target_t b)
+static inline bool usb_target_same(usb_target_t a, usb_target_t b)
 {
 	return (a.address == b.address)
 	    && (a.endpoint == b.endpoint);
 }
-
-/** General handle type.
- * Used by various USB functions as opaque handle.
- */
-typedef sysarg_t usb_handle_t;
 
 /** USB packet identifier. */
Index: uspace/lib/usb/src/dma_buffer.c
===================================================================
--- uspace/lib/usb/src/dma_buffer.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usb/src/dma_buffer.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,248 @@
+/*
+ * 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 libusb
+ * @{
+ */
+/** @file
+ */
+
+#include <align.h>
+#include <as.h>
+#include <ddi.h>
+#include <stddef.h>
+
+#include "usb/dma_buffer.h"
+
+dma_policy_t dma_policy_create(unsigned flags, size_t chunk_size)
+{
+	assert((chunk_size & (chunk_size - 1)) == 0); /* Check if power of 2 */
+	assert(chunk_size >= PAGE_SIZE || chunk_size == 0);
+
+	return ((chunk_size - 1) & DMA_POLICY_CHUNK_SIZE_MASK)
+		| (flags & DMA_POLICY_FLAGS_MASK);
+}
+
+/**
+ * As the driver is typically using only a few buffers at once, we cache the
+ * physical mapping to avoid calling the kernel unnecessarily often. This cache
+ * is global for a task.
+ *
+ * TODO: "few" is currently limited to one.
+ */
+static struct {
+	const void *last;
+	uintptr_t phys;
+} phys_mapping_cache = { 0 };
+
+static void cache_insert(const void *v, uintptr_t p)
+{
+	phys_mapping_cache.last = v;
+	phys_mapping_cache.phys = p;
+}
+
+static void cache_evict(const void *v)
+{
+	if (phys_mapping_cache.last == v)
+		phys_mapping_cache.last = NULL;
+}
+
+static bool cache_find(const void *v, uintptr_t *p)
+{
+	*p = phys_mapping_cache.phys;
+	return phys_mapping_cache.last == v;
+}
+
+/**
+ * Allocate a DMA buffer.
+ *
+ * @param[in] db dma_buffer_t structure to fill
+ * @param[in] size Size of the required memory space
+ * @param[in] policy dma_policy_t flags to guide the allocation
+ * @return Error code.
+ */
+errno_t dma_buffer_alloc_policy(dma_buffer_t *db, size_t size, dma_policy_t policy)
+{
+	assert(db);
+
+	const size_t real_size = ALIGN_UP(size, PAGE_SIZE);
+	const bool need_4gib = !!(policy & DMA_POLICY_4GiB);
+
+	const uintptr_t flags = need_4gib ? DMAMEM_4GiB : 0;
+
+	uintptr_t phys;
+	void *address = AS_AREA_ANY;
+
+	const int err = dmamem_map_anonymous(real_size,
+	    flags, AS_AREA_READ | AS_AREA_WRITE, 0,
+	    &phys, &address);
+	if (err)
+		return err;
+
+	/* Access the pages to force mapping */
+	volatile char *buf = address;
+	for (size_t i = 0; i < size; i += PAGE_SIZE)
+		buf[i] = 0xff;
+
+	db->virt = address;
+	db->policy = dma_policy_create(policy, 0);
+	cache_insert(db->virt, phys);
+
+	return EOK;
+}
+
+
+/**
+ * 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.
+ */
+errno_t 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->policy = 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, const void *virt)
+{
+	const size_t chunk_mask = dma_policy_chunk_mask(db->policy);
+	const uintptr_t offset = (virt - db->virt) & chunk_mask;
+	const void *chunk_base = virt - offset;
+
+	uintptr_t phys;
+
+	if (!cache_find(chunk_base, &phys)) {
+		if (as_get_physical_mapping(chunk_base, &phys))
+			return 0;
+		cache_insert(chunk_base, phys);
+	}
+
+	return phys + offset;
+}
+
+static bool dma_buffer_is_4gib(dma_buffer_t *db, size_t size)
+{
+	if (sizeof(uintptr_t) <= 32)
+		return true;
+
+	const size_t chunk_size = dma_policy_chunk_mask(db->policy) + 1;
+	const size_t chunks = chunk_size ? 1 : size / chunk_size;
+
+	for (size_t c = 0; c < chunks; c++) {
+		const void *addr = db->virt + (c * chunk_size);
+		const uintptr_t phys = dma_buffer_phys(db, addr);
+	
+		if ((phys & DMAMEM_4GiB) != 0)
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * Lock an arbitrary buffer for DMA operations, creating a DMA buffer.
+ *
+ * FIXME: To handle page-unaligned buffers, we need to calculate the base
+ *        address and lock the whole first page. But as the operation is not yet
+ *        implemented in the kernel, it doesn't matter.
+ */
+errno_t dma_buffer_lock(dma_buffer_t *db, void *virt, size_t size)
+{
+	assert(virt);
+
+	uintptr_t phys;
+
+	const errno_t err = dmamem_map(virt, size, 0, 0, &phys);
+	if (err)
+		return err;
+
+	db->virt = virt;
+	db->policy = dma_policy_create(0, PAGE_SIZE);
+	cache_insert(virt, phys);
+
+	unsigned flags = -1U;
+	if (!dma_buffer_is_4gib(db, size))
+		flags &= ~DMA_POLICY_4GiB;
+	db->policy = dma_policy_create(flags, PAGE_SIZE);
+
+	return EOK;
+}
+
+/**
+ * Unlock a buffer for DMA operations.
+ */
+void dma_buffer_unlock(dma_buffer_t *db, size_t size)
+{
+	if (db->virt) {
+		dmamem_unmap(db->virt, size);
+		db->virt = NULL;
+		db->policy = 0;
+	}
+}
+
+/**
+ * Must be called when the buffer is received over IPC. Clears potentially
+ * leftover value from different buffer mapped to the same virtual address.
+ */
+void dma_buffer_acquire(dma_buffer_t *db)
+{
+	cache_evict(db->virt);
+}
+
+/**
+ * Counterpart of acquire.
+ */
+void dma_buffer_release(dma_buffer_t *db)
+{
+	cache_evict(db->virt);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/dump.c
===================================================================
--- uspace/lib/usb/src/dump.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usb/src/dump.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -61,4 +61,6 @@
 static void usb_dump_descriptor_endpoint(FILE *, const char *, const char *,
     const uint8_t *, size_t);
+static void usb_dump_descriptor_superspeed_endpoint_companion(FILE *, const char *, const char *,
+    const uint8_t *, size_t);
 static void usb_dump_descriptor_hid(FILE *, const char *, const char *,
     const uint8_t *, size_t);
@@ -75,4 +77,5 @@
 	{ USB_DESCTYPE_INTERFACE, usb_dump_descriptor_interface },
 	{ USB_DESCTYPE_ENDPOINT, usb_dump_descriptor_endpoint },
+	{ USB_DESCTYPE_SSPEED_EP_COMPANION, usb_dump_descriptor_superspeed_endpoint_companion },
 	{ USB_DESCTYPE_HID, usb_dump_descriptor_hid },
 	{ USB_DESCTYPE_HUB, usb_dump_descriptor_hub },
@@ -238,4 +241,21 @@
 	PRINTLINE("wMaxPacketSize = %d", d->max_packet_size);
 	PRINTLINE("bInterval = %dms", d->poll_interval);
+}
+
+static void usb_dump_descriptor_superspeed_endpoint_companion(FILE *output,
+    const char *line_prefix, const char *line_suffix,
+    const uint8_t *descriptor, size_t descriptor_length)
+{
+	usb_superspeed_endpoint_companion_descriptor_t *d
+	   = (usb_superspeed_endpoint_companion_descriptor_t *) descriptor;
+	if (descriptor_length < sizeof(*d)) {
+		return;
+	}
+
+	PRINTLINE("bLength = %u", d->length);
+	PRINTLINE("bDescriptorType = 0x%02X", d->descriptor_type);
+	PRINTLINE("bMaxBurst = %u", d->max_burst);
+	PRINTLINE("bmAttributes = %d", d->attributes);
+	PRINTLINE("wBytesPerInterval = %u", d->bytes_per_interval);
 }
 
Index: uspace/lib/usb/src/port.c
===================================================================
--- uspace/lib/usb/src/port.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usb/src/port.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2018 HelenOS xHCI team
+ * 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 libusb
+ * @{
+ */
+/** @file
+ * An USB hub port state machine.
+ *
+ * This helper structure solves a repeated problem in USB world: management of
+ * USB ports. A port is an object which receives events (connect, disconnect,
+ * reset) which are to be handled in an asynchronous way. The tricky part is
+ * that response to events has to wait for different events - the most notable
+ * being USB 2 port requiring port reset to be enabled. This problem is solved
+ * by launching separate fibril for taking the port up.
+ *
+ * This subsystem abstracts the rather complicated state machine, and offers
+ * a simple interface to announce events and leave the fibril management on the
+ * library.
+ */
+
+#include <stdlib.h>
+#include <fibril.h>
+#include <assert.h>
+#include <usb/debug.h>
+
+#include <usb/port.h>
+
+void usb_port_init(usb_port_t *port)
+{
+	fibril_mutex_initialize(&port->guard);
+	fibril_condvar_initialize(&port->finished_cv);
+	fibril_condvar_initialize(&port->enabled_cv);
+}
+
+struct enumerate_worker_args {
+	usb_port_t *port;
+	usb_port_enumerate_t handler;
+};
+
+static int enumerate_worker(void *arg)
+{
+	struct enumerate_worker_args * const args = arg;
+	usb_port_t *port = args->port;
+	usb_port_enumerate_t handler = args->handler;
+	free(args);
+
+	fibril_mutex_lock(&port->guard);
+
+	if (port->state == PORT_ERROR) {
+		/* 
+		 * The device was removed faster than this fibril acquired the
+		 * mutex.
+		 */
+		port->state = PORT_DISABLED;
+		goto out;
+	}
+
+	assert(port->state == PORT_CONNECTING);
+
+	port->state = handler(port)
+		? PORT_DISABLED
+		: PORT_ENUMERATED;
+
+out:
+	fibril_condvar_broadcast(&port->finished_cv);
+	fibril_mutex_unlock(&port->guard);
+	return EOK; // This is a fibril worker. No one will read the value.
+}
+
+int usb_port_connected(usb_port_t *port, usb_port_enumerate_t handler)
+{
+	assert(port);
+	int ret = ENOMEM;
+
+	fibril_mutex_lock(&port->guard);
+
+	if (port->state != PORT_DISABLED) {
+		usb_log_warning("a connected event come for port that is not disabled.");
+		ret = EINVAL;
+		goto out;
+	}
+
+	struct enumerate_worker_args *args = malloc(sizeof(*args));
+	if (!args)
+		goto out;
+
+	fid_t fibril = fibril_create(&enumerate_worker, args);
+	if (!fibril) {
+		free(args);
+		goto out;
+	}
+
+	args->port = port;
+	args->handler = handler;
+
+	port->state = PORT_CONNECTING;
+	fibril_add_ready(fibril);
+out:
+	fibril_mutex_unlock(&port->guard);
+	return ret;
+}
+
+void usb_port_enabled(usb_port_t *port)
+{
+	assert(port);
+
+	fibril_mutex_lock(&port->guard);
+	fibril_condvar_broadcast(&port->enabled_cv);
+	fibril_mutex_unlock(&port->guard);
+}
+
+struct remove_worker_args {
+	usb_port_t *port;
+	usb_port_remove_t handler;
+};
+
+static int remove_worker(void *arg)
+{
+	struct remove_worker_args * const args = arg;
+	usb_port_t *port = args->port;
+	usb_port_remove_t handler = args->handler;
+	free(args);
+
+	fibril_mutex_lock(&port->guard);
+	assert(port->state == PORT_DISCONNECTING);
+
+	handler(port);
+
+	port->state = PORT_DISABLED;
+	fibril_condvar_broadcast(&port->finished_cv);
+	fibril_mutex_unlock(&port->guard);
+	return EOK;
+}
+
+static void fork_remove_worker(usb_port_t *port, usb_port_remove_t handler)
+{
+	struct remove_worker_args *args = malloc(sizeof(*args));
+	if (!args)
+		return;
+
+	fid_t fibril = fibril_create(&remove_worker, args);
+	if (!fibril) {
+		free(args);
+		return;
+	}
+
+	args->port = port;
+	args->handler = handler;
+
+	port->state = PORT_DISCONNECTING;
+	fibril_add_ready(fibril);
+}
+
+void usb_port_disabled(usb_port_t *port, usb_port_remove_t handler)
+{
+	assert(port);
+	fibril_mutex_lock(&port->guard);
+
+	switch (port->state) {
+	case PORT_ENUMERATED:
+		fork_remove_worker(port, handler);
+		break;
+
+	case PORT_CONNECTING:
+		port->state = PORT_ERROR;
+		fibril_condvar_broadcast(&port->enabled_cv);
+		/* fallthrough */
+	case PORT_ERROR:
+		fibril_condvar_wait(&port->finished_cv, &port->guard);
+		/* fallthrough */
+	case PORT_DISCONNECTING:
+	case PORT_DISABLED:
+		break;
+	}
+
+	fibril_mutex_unlock(&port->guard);
+}
+
+void usb_port_fini(usb_port_t *port)
+{
+	assert(port);
+
+	fibril_mutex_lock(&port->guard);
+	switch (port->state) {
+	case PORT_ENUMERATED:
+		/*
+		 * We shall inform the HC that the device is gone.
+		 * However, we can't wait for it, because if the device is hub,
+		 * it would have to use the same IPC handling fibril as we do.
+		 * But we cannot even defer it to another fibril, because then
+		 * the HC would assume our driver didn't cleanup properly, and
+		 * will remove those devices by himself.
+		 *
+		 * So the solutions seems to be to behave like a bad driver and
+		 * leave the work for HC.
+		 */
+		port->state = PORT_DISABLED;
+		/* fallthrough */
+	case PORT_DISABLED:
+		break;
+
+	/* We first have to stop the fibril in progress. */
+	case PORT_CONNECTING:
+		port->state = PORT_ERROR;
+		fibril_condvar_broadcast(&port->enabled_cv);
+		/* fallthrough */
+	case PORT_ERROR:
+	case PORT_DISCONNECTING:
+		fibril_condvar_wait(&port->finished_cv, &port->guard);
+		break;
+	}
+	fibril_mutex_unlock(&port->guard);
+}
+
+int usb_port_condvar_wait_timeout(usb_port_t *port, fibril_condvar_t *cv, suseconds_t timeout)
+{
+	assert(port);
+	assert(port->state == PORT_CONNECTING);
+	assert(fibril_mutex_is_locked(&port->guard));
+
+	if (fibril_condvar_wait_timeout(cv, &port->guard, timeout))
+		return ETIMEOUT;
+
+	return port->state == PORT_CONNECTING ? EOK : EINTR;
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/usb.c
===================================================================
--- uspace/lib/usb/src/usb.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usb/src/usb.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -44,4 +44,5 @@
 	[USB_SPEED_FULL] = "full",
 	[USB_SPEED_HIGH] = "high",
+	[USB_SPEED_SUPER] = "super",
 };
 
@@ -118,42 +119,4 @@
 }
 
-/** Check setup packet data for signs of toggle reset.
- *
- * @param[in] requst Setup requst data.
- *
- * @retval -1 No endpoints need reset.
- * @retval 0 All endpoints need reset.
- * @retval >0 Specified endpoint needs reset.
- *
- */
-int usb_request_needs_toggle_reset(
-    const usb_device_request_setup_packet_t *request)
-{
-	assert(request);
-	switch (request->request)
-	{
-	/* Clear Feature ENPOINT_STALL */
-	case USB_DEVREQ_CLEAR_FEATURE: /*resets only cleared ep */
-		/* 0x2 ( HOST to device | STANDART | TO ENPOINT) */
-		if ((request->request_type == 0x2) &&
-		    (request->value == USB_FEATURE_ENDPOINT_HALT))
-			return uint16_usb2host(request->index);
-		break;
-	case USB_DEVREQ_SET_CONFIGURATION:
-	case USB_DEVREQ_SET_INTERFACE:
-		/* Recipient must be device, this resets all endpoints,
-		 * In fact there should be no endpoints but EP 0 registered
-		 * as different interfaces use different endpoints,
-		 * unless you're changing configuration or alternative
-		 * interface of an already setup device. */
-		if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
-			return 0;
-		break;
-	default:
-		break;
-	}
-	return -1;
-}
-
 /**
  * @}
Index: uspace/lib/usbdev/include/usb/dev/device.h
===================================================================
--- uspace/lib/usbdev/include/usb/dev/device.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/include/usb/dev/device.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -42,4 +42,5 @@
 #include <usb/dev/alternate_ifaces.h>
 #include <usb/dev/pipes.h>
+#include <usbhc_iface.h>
 
 #include <assert.h>
@@ -86,9 +87,11 @@
 usb_endpoint_mapping_t * usb_device_get_mapped_ep_desc(usb_device_t *,
     const usb_endpoint_description_t *);
-usb_endpoint_mapping_t * usb_device_get_mapped_ep(usb_device_t *,
-    usb_endpoint_t);
+int usb_device_unmap_ep(usb_endpoint_mapping_t *);
 
-int usb_device_get_iface_number(usb_device_t *);
-devman_handle_t usb_device_get_devman_handle(usb_device_t *);
+usb_address_t usb_device_get_address(const usb_device_t *);
+usb_speed_t usb_device_get_depth(const usb_device_t *);
+usb_speed_t usb_device_get_speed(const usb_device_t *);
+int usb_device_get_iface_number(const usb_device_t *);
+devman_handle_t usb_device_get_devman_handle(const usb_device_t *);
 
 const usb_device_descriptors_t * usb_device_descriptors(usb_device_t *);
Index: uspace/lib/usbdev/include/usb/dev/driver.h
===================================================================
--- uspace/lib/usbdev/include/usb/dev/driver.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/include/usb/dev/driver.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -45,7 +45,11 @@
 	errno_t (*device_add)(usb_device_t *);
 	/** Callback when a device is about to be removed from the system. */
-	errno_t (*device_rem)(usb_device_t *);
+	errno_t (*device_remove)(usb_device_t *);
 	/** Callback when a device was removed from the system. */
 	errno_t (*device_gone)(usb_device_t *);
+	/** Callback asking the driver to online a specific function. */
+	errno_t (*function_online)(ddf_fun_t *);
+	/** Callback asking the driver to offline a specific function. */
+	errno_t (*function_offline)(ddf_fun_t *);
 } usb_driver_ops_t;
 
Index: uspace/lib/usbdev/include/usb/dev/pipes.h
===================================================================
--- uspace/lib/usbdev/include/usb/dev/pipes.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/include/usb/dev/pipes.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -44,25 +44,13 @@
 
 #define CTRL_PIPE_MIN_PACKET_SIZE 8
+
 /** Abstraction of a logical connection to USB device endpoint.
- * It encapsulates endpoint attributes (transfer type etc.).
+ * It contains some vital information about the pipe.
  * This endpoint must be bound with existing usb_device_connection_t
  * (i.e. the wire to send data over).
  */
 typedef struct {
-	/** Endpoint number. */
-	usb_endpoint_t endpoint_no;
-
-	/** Endpoint transfer type. */
-	usb_transfer_type_t transfer_type;
-
-	/** Endpoint direction. */
-	usb_direction_t direction;
-
-	/** Maximum packet size for the endpoint. */
-	size_t max_packet_size;
-
-	/** Number of packets per frame/uframe.
-	 * Only valid for HS INT and ISO transfers. All others should set to 1*/
-	unsigned packets;
+	/** Pipe description received from HC */
+	usb_pipe_desc_t desc;
 
 	/** Whether to automatically reset halt on the endpoint.
@@ -70,5 +58,4 @@
 	 */
 	bool auto_reset_halt;
-
 	/** The connection used for sending the data. */
 	usb_dev_session_t *bus_session;
@@ -103,4 +90,6 @@
 	/** Found descriptor fitting the description. */
 	const usb_standard_endpoint_descriptor_t *descriptor;
+	/** Relevant superspeed companion descriptor. */
+	const usb_superspeed_endpoint_companion_descriptor_t *companion_descriptor;
 	/** Interface descriptor the endpoint belongs to. */
 	const usb_standard_interface_descriptor_t *interface;
@@ -109,17 +98,18 @@
 } usb_endpoint_mapping_t;
 
-errno_t usb_pipe_initialize(usb_pipe_t *, usb_endpoint_t, usb_transfer_type_t,
-    size_t, usb_direction_t, unsigned, usb_dev_session_t *);
+errno_t usb_pipe_initialize(usb_pipe_t *, usb_dev_session_t *);
 errno_t usb_pipe_initialize_default_control(usb_pipe_t *, usb_dev_session_t *);
 
-errno_t usb_pipe_probe_default_control(usb_pipe_t *);
 errno_t usb_pipe_initialize_from_configuration(usb_endpoint_mapping_t *,
     size_t, const uint8_t *, size_t, usb_dev_session_t *);
 
-errno_t usb_pipe_register(usb_pipe_t *, unsigned);
+errno_t usb_pipe_register(usb_pipe_t *, const usb_standard_endpoint_descriptor_t *, const usb_superspeed_endpoint_companion_descriptor_t *);
 errno_t usb_pipe_unregister(usb_pipe_t *);
 
 errno_t usb_pipe_read(usb_pipe_t *, void *, size_t, size_t *);
 errno_t usb_pipe_write(usb_pipe_t *, const void *, size_t);
+
+errno_t usb_pipe_read_dma(usb_pipe_t *, void *, void *, size_t, size_t *);
+errno_t usb_pipe_write_dma(usb_pipe_t *, void *, void *, size_t);
 
 errno_t usb_pipe_control_read(usb_pipe_t *, const void *, size_t,
@@ -128,4 +118,6 @@
     const void *, size_t);
 
+void *usb_pipe_alloc_buffer(usb_pipe_t *, size_t);
+void usb_pipe_free_buffer(usb_pipe_t *, void *);
 #endif
 /**
Index: uspace/lib/usbdev/include/usb/dev/poll.h
===================================================================
--- uspace/lib/usbdev/include/usb/dev/poll.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/include/usb/dev/poll.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2011 Vojtech Horky
+ * Copyright (c) 2017 Petr Manek
  * All rights reserved.
  *
@@ -43,23 +44,23 @@
 #include <stddef.h>
 #include <stdint.h>
+#include <fibril_synch.h>
 
-/** Parameters and callbacks for automated polling. */
-typedef struct {
-	/** Level of debugging messages from auto polling.
-	 * 0 - nothing
-	 * 1 - inform about errors and polling start/end
-	 * 2 - also dump every retrieved buffer
-	 */
-	int debug;
-	/** Maximum number of consecutive errors before polling termination. */
-	size_t max_failures;
-	/** Delay between poll requests in milliseconds.
-	 * Set to negative value to use value from endpoint descriptor.
-	 */
-	int delay;
-	/** Whether to automatically try to clear the HALT feature after
-	 * the endpoint stalls.
-	 */
-	bool auto_clear_halt;
+
+/** USB automated polling. */
+typedef struct usb_polling {
+	/** Mandatory parameters - user is expected to configure these. */
+
+	/** USB device to poll. */
+	usb_device_t *device;
+
+	/** Device enpoint mapping to use for polling. */
+	usb_endpoint_mapping_t *ep_mapping;
+
+	/** Size of the recieved data. */
+	size_t request_size;
+
+	/** Data buffer of at least `request_size`. User is responsible for its allocation. */
+	uint8_t *buffer;
+
 	/** Callback when data arrives.
 	 *
@@ -72,4 +73,33 @@
 	bool (*on_data)(usb_device_t *dev, uint8_t *data, size_t data_size,
 	    void *arg);
+
+
+	/** Optional parameters - user can customize them, but they are defaulted to
+	 *  some reasonable values.
+	 */
+
+	/** Level of debugging messages from auto polling.
+	 * 0 - nothing (default)
+	 * 1 - inform about errors and polling start/end
+	 * 2 - also dump every retrieved buffer
+	 */
+	int debug;
+
+	/** Maximum number of consecutive errors before polling termination (default 3). */
+	size_t max_failures;
+
+	/** Delay between poll requests in milliseconds.
+	 * By default, value from endpoint descriptor used.
+	 */
+	int delay;
+
+	/** Whether to automatically try to clear the HALT feature after
+	 * the endpoint stalls (true by default).
+	 */
+	bool auto_clear_halt;
+
+	/** Argument to pass to callbacks (default NULL). */
+	void *arg;
+
 	/** Callback when polling is terminated.
 	 *
@@ -80,4 +110,5 @@
 	void (*on_polling_end)(usb_device_t *dev, bool due_to_errors,
 	    void *arg);
+
 	/** Callback when error occurs.
 	 *
@@ -88,24 +119,29 @@
 	 */
 	bool (*on_error)(usb_device_t *dev, errno_t err_code, void *arg);
-	/** Argument to pass to callbacks. */
-	void *arg;
-} usb_device_auto_polling_t;
 
-typedef bool (*usb_polling_callback_t)(usb_device_t *, uint8_t *, size_t, void *);
-typedef void (*usb_polling_terminted_callback_t)(usb_device_t *, bool, void *);
 
-extern errno_t usb_device_auto_polling(usb_device_t *, usb_endpoint_t,
-    const usb_device_auto_polling_t *, size_t);
+	/** Internal parameters - user is not expected to set them. Messing with them
+	 *  can result in unexpected behavior if you do not know what you are doing.
+	 */
 
-extern errno_t usb_device_auto_poll(usb_device_t *, usb_endpoint_t,
-    usb_polling_callback_t, size_t, int, usb_polling_terminted_callback_t, void *);
+	/** Fibril used for polling. */
+	fid_t fibril;
 
-extern errno_t usb_device_auto_polling_desc(usb_device_t *,
-    const usb_endpoint_description_t *, const usb_device_auto_polling_t *,
-    size_t);
+	/** True if polling is currently in operation. */
+	volatile bool running;
 
-extern errno_t usb_device_auto_poll_desc(usb_device_t *,
-    const usb_endpoint_description_t *, usb_polling_callback_t, size_t, int,
-    usb_polling_terminted_callback_t, void *);
+	/** True if polling should terminate as soon as possible. */
+	volatile bool joining;
+
+	/** Synchronization primitives for joining polling end. */
+	fibril_mutex_t guard;
+	fibril_condvar_t cv;
+} usb_polling_t;
+
+errno_t usb_polling_init(usb_polling_t *);
+void usb_polling_fini(usb_polling_t *);
+
+errno_t usb_polling_start(usb_polling_t *);
+errno_t usb_polling_join(usb_polling_t *);
 
 #endif
Index: uspace/lib/usbdev/include/usb/dev/request.h
===================================================================
--- uspace/lib/usbdev/include/usb/dev/request.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/include/usb/dev/request.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -85,5 +85,4 @@
     char **);
 
-errno_t usb_request_clear_endpoint_halt(usb_pipe_t *, uint16_t);
 errno_t usb_pipe_clear_halt(usb_pipe_t *, usb_pipe_t *);
 errno_t usb_request_get_endpoint_status(usb_pipe_t *, usb_pipe_t *, uint16_t *);
Index: uspace/lib/usbdev/src/devdrv.c
===================================================================
--- uspace/lib/usbdev/src/devdrv.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/src/devdrv.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -48,4 +48,5 @@
 #include <devman.h>
 #include <errno.h>
+#include <str_error.h>
 #include <stdlib.h>
 
@@ -56,11 +57,11 @@
 	/** Connection to device on USB bus */
 	usb_dev_session_t *bus_session;
-	
+
 	/** devman handle */
 	devman_handle_t handle;
-	
+
 	/** The default control pipe. */
 	usb_pipe_t ctrl_pipe;
-	
+
 	/** Other endpoint pipes.
 	 *
@@ -69,8 +70,17 @@
 	 */
 	usb_endpoint_mapping_t *pipes;
-	
+
 	/** Number of other endpoint pipes. */
 	size_t pipes_count;
-	
+
+	/** USB address of this device */
+	usb_address_t address;
+
+	/** Depth in the USB hub hiearchy */
+	unsigned depth;
+
+	/** USB speed of this device */
+	usb_speed_t speed;
+
 	/** Current interface.
 	 *
@@ -79,14 +89,14 @@
 	 */
 	int interface_no;
-	
+
 	/** Alternative interfaces. */
 	usb_alternate_interfaces_t alternate_interfaces;
-	
+
 	/** Some useful descriptors for USB device. */
 	usb_device_descriptors_t descriptors;
-	
+
 	/** Generic DDF device backing this one. DO NOT TOUCH! */
 	ddf_dev_t *ddf_dev;
-	
+
 	/** Custom driver data.
 	 *
@@ -146,5 +156,5 @@
 		return rc;
 	}
-	
+
 	/* Change current alternative */
 	usb_dev->alternate_interfaces.current = alternate_setting;
@@ -255,12 +265,13 @@
 
 	/* Register created pipes. */
+	unsigned pipes_registered = 0;
 	for (size_t i = 0; i < pipe_count; i++) {
 		if (pipes[i].present) {
-			rc = usb_pipe_register(&pipes[i].pipe,
-			    pipes[i].descriptor->poll_interval);
+			rc = usb_pipe_register(&pipes[i].pipe, pipes[i].descriptor, pipes[i].companion_descriptor);
 			if (rc != EOK) {
 				goto rollback_unregister_endpoints;
 			}
 		}
+		pipes_registered++;
 	}
 
@@ -277,5 +288,5 @@
 	 */
 rollback_unregister_endpoints:
-	for (size_t i = 0; i < pipe_count; i++) {
+	for (size_t i = 0; i < pipes_registered; i++) {
 		if (pipes[i].present) {
 			usb_pipe_unregister(&pipes[i].pipe);
@@ -296,13 +307,16 @@
 	assert(usb_dev);
 	assert(usb_dev->pipes || usb_dev->pipes_count == 0);
-	
+
 	/* Destroy the pipes. */
+	int rc;
 	for (size_t i = 0; i < usb_dev->pipes_count; ++i) {
-		usb_log_debug2("Unregistering pipe %zu: %spresent.\n",
+		usb_log_debug2("Unregistering pipe %zu: %spresent.",
 		    i, usb_dev->pipes[i].present ? "" : "not ");
-		if (usb_dev->pipes[i].present)
-			usb_pipe_unregister(&usb_dev->pipes[i].pipe);
-	}
-	
+
+		rc = usb_device_unmap_ep(usb_dev->pipes + i);
+		if (rc != EOK && rc != ENOENT)
+			usb_log_warning("Unregistering pipe %zu failed: %s", i, str_error(rc));
+	}
+
 	free(usb_dev->pipes);
 	usb_dev->pipes = NULL;
@@ -327,16 +341,38 @@
 }
 
-usb_endpoint_mapping_t * usb_device_get_mapped_ep(
-    usb_device_t *usb_dev, usb_endpoint_t ep)
-{
-	assert(usb_dev);
-	for (unsigned i = 0; i < usb_dev->pipes_count; ++i) {
-		if (usb_dev->pipes[i].pipe.endpoint_no == ep)
-			return &usb_dev->pipes[i];
-	}
-	return NULL;
-}
-
-int usb_device_get_iface_number(usb_device_t *usb_dev)
+int usb_device_unmap_ep(usb_endpoint_mapping_t *epm)
+{
+	assert(epm);
+
+	if (!epm->present)
+		return ENOENT;
+
+	const int rc = usb_pipe_unregister(&epm->pipe);
+	if (rc != EOK)
+		return rc;
+
+	epm->present = false;
+	return EOK;
+}
+
+usb_address_t usb_device_get_address(const usb_device_t *usb_dev)
+{
+	assert(usb_dev);
+	return usb_dev->depth;
+}
+
+unsigned usb_device_get_depth(const usb_device_t *usb_dev)
+{
+	assert(usb_dev);
+	return usb_dev->depth;
+}
+
+usb_speed_t usb_device_get_speed(const usb_device_t *usb_dev)
+{
+	assert(usb_dev);
+	return usb_dev->speed;
+}
+
+int usb_device_get_iface_number(const usb_device_t *usb_dev)
 {
 	assert(usb_dev);
@@ -344,5 +380,5 @@
 }
 
-devman_handle_t usb_device_get_devman_handle(usb_device_t *usb_dev)
+devman_handle_t usb_device_get_devman_handle(const usb_device_t *usb_dev)
 {
 	assert(usb_dev);
@@ -394,6 +430,5 @@
  */
 static errno_t usb_device_init(usb_device_t *usb_dev, ddf_dev_t *ddf_dev,
-    const usb_endpoint_description_t **endpoints, const char **errstr_ptr,
-    devman_handle_t handle, int interface_no)
+    const usb_endpoint_description_t **endpoints, const char **errstr_ptr)
 {
 	assert(usb_dev != NULL);
@@ -403,6 +438,4 @@
 
 	usb_dev->ddf_dev = ddf_dev;
-	usb_dev->handle = handle;
-	usb_dev->interface_no = interface_no;
 	usb_dev->driver_data = NULL;
 	usb_dev->descriptors.full_config = NULL;
@@ -411,5 +444,5 @@
 	usb_dev->pipes = NULL;
 
-	usb_dev->bus_session = usb_dev_connect(handle);
+	usb_dev->bus_session = usb_dev_connect(usb_dev->handle);
 
 	if (!usb_dev->bus_session) {
@@ -420,6 +453,5 @@
 	/* This pipe was registered by the hub driver,
 	 * during device initialization. */
-	errno_t rc = usb_pipe_initialize_default_control(
-	    &usb_dev->ctrl_pipe, usb_dev->bus_session);
+	errno_t rc = usb_pipe_initialize_default_control(&usb_dev->ctrl_pipe, usb_dev->bus_session);
 	if (rc != EOK) {
 		usb_dev_disconnect(usb_dev->bus_session);
@@ -440,5 +472,5 @@
 	 * it makes no sense to speak about alternate interfaces when
 	 * controlling a device. */
-	rc = usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
+	usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
 	    usb_dev->descriptors.full_config,
 	    usb_dev->descriptors.full_config_size, usb_dev->interface_no);
@@ -457,23 +489,23 @@
 }
 
-static errno_t usb_device_get_info(async_sess_t *sess, devman_handle_t *handle,
-	int *iface_no)
-{
-	assert(handle);
-	assert(iface_no);
-	
+static errno_t usb_device_get_info(async_sess_t *sess, usb_device_t *dev)
+{
+	assert(dev);
+
 	async_exch_t *exch = async_exchange_begin(sess);
 	if (!exch)
 		return EPARTY;
-	
-	errno_t ret = usb_get_my_device_handle(exch, handle);
+
+	usb_device_desc_t dev_desc;
+	const errno_t ret = usb_get_my_description(exch, &dev_desc);
+
 	if (ret == EOK) {
-		ret = usb_get_my_interface(exch, iface_no);
-		if (ret == ENOTSUP) {
-			*iface_no = -1;
-			ret = EOK;
-		}
-	}
-	
+		dev->address = dev_desc.address;
+		dev->depth = dev_desc.depth;
+		dev->speed = dev_desc.speed;
+		dev->handle = dev_desc.handle;
+		dev->interface_no = dev_desc.iface;
+	}
+
 	async_exchange_end(exch);
 	return ret;
@@ -485,14 +517,8 @@
 	assert(ddf_dev);
 	assert(err);
-
-	devman_handle_t h = 0;
-	int iface_no = -1;
 
 	async_sess_t *sess = ddf_dev_parent_sess_get(ddf_dev);
 	if (sess == NULL)
 		return ENOMEM;
-	const errno_t ret = usb_device_get_info(sess, &h, &iface_no);
-	if (ret != EOK)
-		return ret;
 
 	usb_device_t *usb_dev =
@@ -502,6 +528,10 @@
 		return ENOMEM;
 	}
-	
-	return usb_device_init(usb_dev, ddf_dev, desc, err, h, iface_no);
+
+	const errno_t ret = usb_device_get_info(sess, usb_dev);
+	if (ret != EOK)
+		return ret;
+
+	return usb_device_init(usb_dev, ddf_dev, desc, err);
 }
 
@@ -517,20 +547,19 @@
 usb_device_t * usb_device_create(devman_handle_t handle)
 {
-	devman_handle_t h = 0;
-	int iface_no = -1;
-
-	async_sess_t *sess = devman_device_connect(handle, IPC_FLAG_BLOCKING);
-	errno_t ret = usb_device_get_info(sess, &h, &iface_no);
-	if (sess)
-		async_hangup(sess);
-	if (ret != EOK)
-		return NULL;
-
 	usb_device_t *usb_dev = malloc(sizeof(usb_device_t));
 	if (!usb_dev)
 		return NULL;
 
+	async_sess_t *sess = devman_device_connect(handle, IPC_FLAG_BLOCKING);
+	errno_t ret = usb_device_get_info(sess, usb_dev);
+	if (sess)
+		async_hangup(sess);
+	if (ret != EOK) {
+		free(usb_dev);
+		return NULL;
+	}
+
 	const char* dummy = NULL;
-	ret = usb_device_init(usb_dev, NULL, NULL, &dummy, handle, iface_no);
+	ret = usb_device_init(usb_dev, NULL, NULL, &dummy);
 	if (ret != EOK) {
 		free(usb_dev);
Index: uspace/lib/usbdev/src/devpoll.c
===================================================================
--- uspace/lib/usbdev/src/devpoll.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/src/devpoll.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2011 Vojtech Horky
+ * Copyright (c) 2017 Petr Manek
  * All rights reserved.
  *
@@ -47,4 +48,5 @@
 #include <errno.h>
 #include <fibril.h>
+#include <fibril_synch.h>
 #include <stdbool.h>
 #include <stdlib.h>
@@ -53,26 +55,50 @@
 #include <stdint.h>
 
-/** Maximum number of failed consecutive requests before announcing failure. */
-#define MAX_FAILED_ATTEMPTS 3
-
-/** Data needed for polling. */
-typedef struct {
-	/** Parameters for automated polling. */
-	usb_device_auto_polling_t auto_polling;
-
-	/** USB device to poll. */
-	usb_device_t *dev;
-	/** Device enpoint mapping to use for polling. */
-	usb_endpoint_mapping_t *polling_mapping;
-	/** Size of the recieved data. */
-	size_t request_size;
-	/** Data buffer. */
-	uint8_t *buffer;
-} polling_data_t;
+
+/** Initialize the polling data structure, its internals and configuration
+ *  with default values.
+ *
+ * @param polling Valid polling data structure.
+ * @return Error code.
+ * @retval EOK Polling data structure is ready to be used.
+ */
+int usb_polling_init(usb_polling_t *polling)
+{
+	if (!polling)
+		return EBADMEM;
+
+	/* Zero out everything */
+	memset(polling, 0, sizeof(usb_polling_t));
+
+	/* Internal initializers. */
+	fibril_mutex_initialize(&polling->guard);
+	fibril_condvar_initialize(&polling->cv);
+
+	/* Default configuration. */
+	polling->auto_clear_halt = true;
+	polling->delay = -1;
+	polling->max_failures = 3;
+
+	return EOK;
+}
+
+
+/** Destroy the polling data structure.
+ *  This function does nothing but a safety check whether the polling
+ *  was joined successfully.
+ *
+ * @param polling Polling data structure.
+ */
+void usb_polling_fini(usb_polling_t *polling)
+{
+	/* Nothing done at the moment. */
+	assert(polling);
+	assert(!polling->running);
+}
 
 
 /** Polling fibril.
  *
- * @param arg Pointer to polling_data_t.
+ * @param arg Pointer to usb_polling_t.
  * @return Always EOK.
  */
@@ -80,35 +106,37 @@
 {
 	assert(arg);
-	polling_data_t *data = arg;
-	/* Helper to reduce typing. */
-	const usb_device_auto_polling_t *params = &data->auto_polling;
-
-	usb_pipe_t *pipe = &data->polling_mapping->pipe;
-
-	if (params->debug > 0) {
+	usb_polling_t *polling = arg;
+
+	fibril_mutex_lock(&polling->guard);
+	polling->running = true;
+	fibril_mutex_unlock(&polling->guard);
+
+	usb_pipe_t *pipe = &polling->ep_mapping->pipe;
+
+	if (polling->debug > 0) {
 		const usb_endpoint_mapping_t *mapping =
-		    data->polling_mapping;
+		    polling->ep_mapping;
 		usb_log_debug("Poll (%p): started polling of `%s' - " \
 		    "interface %d (%s,%d,%d), %zuB/%zu.\n",
-		    data, usb_device_get_name(data->dev),
+		    polling, usb_device_get_name(polling->device),
 		    (int) mapping->interface->interface_number,
 		    usb_str_class(mapping->interface->interface_class),
 		    (int) mapping->interface->interface_subclass,
 		    (int) mapping->interface->interface_protocol,
-		    data->request_size, pipe->max_packet_size);
+		    polling->request_size, pipe->desc.max_transfer_size);
 	}
 
 	size_t failed_attempts = 0;
-	while (failed_attempts <= params->max_failures) {
+	while (failed_attempts <= polling->max_failures) {
 		size_t actual_size;
-		const errno_t rc = usb_pipe_read(pipe, data->buffer,
-		    data->request_size, &actual_size);
+		const errno_t rc = usb_pipe_read(pipe, polling->buffer,
+		    polling->request_size, &actual_size);
 
 		if (rc == EOK) {
-			if (params->debug > 1) {
+			if (polling->debug > 1) {
 				usb_log_debug(
 				    "Poll%p: received: '%s' (%zuB).\n",
-				    data,
-				    usb_debug_str_buffer(data->buffer,
+				    polling,
+				    usb_debug_str_buffer(polling->buffer,
 				        actual_size, 16),
 				    actual_size);
@@ -117,24 +145,26 @@
 				usb_log_debug(
 				    "Poll%p: polling failed: %s.\n",
-				    data, str_error(rc));
+				    polling, str_error(rc));
 		}
 
 		/* If the pipe stalled, we can try to reset the stall. */
-		if ((rc == ESTALL) && (params->auto_clear_halt)) {
+		if (rc == ESTALL && polling->auto_clear_halt) {
 			/*
 			 * We ignore error here as this is usually a futile
 			 * attempt anyway.
 			 */
-			usb_request_clear_endpoint_halt(
-			    usb_device_get_default_pipe(data->dev),
-			    pipe->endpoint_no);
+			usb_pipe_clear_halt(
+			    usb_device_get_default_pipe(polling->device), pipe);
 		}
 
 		if (rc != EOK) {
 			++failed_attempts;
-			const bool cont = (params->on_error == NULL) ? true :
-			    params->on_error(data->dev, rc, params->arg);
-			if (!cont) {
-				failed_attempts = params->max_failures;
+			const bool carry_on = !polling->on_error ? true :
+			    polling->on_error(polling->device, rc, polling->arg);
+
+			if (!carry_on || polling->joining) {
+				/* This is user requested abort, erases failures. */
+				failed_attempts = 0;
+				break;
 			}
 			continue;
@@ -142,7 +172,7 @@
 
 		/* We have the data, execute the callback now. */
-		assert(params->on_data);
-		const bool carry_on = params->on_data(
-		    data->dev, data->buffer, actual_size, params->arg);
+		assert(polling->on_data);
+		const bool carry_on = polling->on_data(polling->device,
+		    polling->buffer, actual_size, polling->arg);
 
 		if (!carry_on) {
@@ -156,33 +186,34 @@
 
 		/* Take a rest before next request. */
-		
+
 		// FIXME TODO: This is broken, the time is in ms not us.
 		// but first we need to fix drivers to actually stop using this,
 		// since polling delay should be implemented in HC schedule
-		async_usleep(params->delay);
+		async_usleep(polling->delay);
 	}
 
 	const bool failed = failed_attempts > 0;
 
-	if (params->on_polling_end != NULL) {
-		params->on_polling_end(data->dev, failed, params->arg);
-	}
-
-	if (params->debug > 0) {
+	if (polling->on_polling_end)
+		polling->on_polling_end(polling->device, failed, polling->arg);
+
+	if (polling->debug > 0) {
 		if (failed) {
 			usb_log_error("Polling of device `%s' terminated: "
 			    "recurring failures.\n",
-			    usb_device_get_name(data->dev));
+			    usb_device_get_name(polling->device));
 		} else {
 			usb_log_debug("Polling of device `%s' terminated: "
 			    "driver request.\n",
-			    usb_device_get_name(data->dev));
+			    usb_device_get_name(polling->device));
 		}
 	}
 
-	/* Free the allocated memory. */
-	free(data->buffer);
-	free(data);
-
+	fibril_mutex_lock(&polling->guard);
+	polling->running = false;
+	fibril_mutex_unlock(&polling->guard);
+
+	/* Notify joiners, if any. */
+	fibril_condvar_broadcast(&polling->cv);
 	return EOK;
 }
@@ -198,156 +229,72 @@
  * first request would be executed prior to return from this function).
  *
- * @param dev Device to be periodically polled.
- * @param epm Endpoint mapping to use.
- * @param polling Polling settings.
- * @param request_size How many bytes to ask for in each request.
- * @param arg Custom argument (passed as is to the callbacks).
+ * @param polling Polling data structure.
  * @return Error code.
  * @retval EOK New fibril polling the device was already started.
  */
-static errno_t usb_device_auto_polling_internal(usb_device_t *dev,
-    usb_endpoint_mapping_t *epm, const usb_device_auto_polling_t *polling,
-    size_t request_size)
-{
-	if ((dev == NULL) || (polling == NULL) || (polling->on_data == NULL)) {
+errno_t usb_polling_start(usb_polling_t *polling)
+{
+	if (!polling || !polling->device || !polling->ep_mapping || !polling->on_data)
 		return EBADMEM;
-	}
-
-	if (request_size == 0)
+
+	if (!polling->request_size)
 		return EINVAL;
-	
-	if (!epm || (epm->pipe.transfer_type != USB_TRANSFER_INTERRUPT) ||
-	    (epm->pipe.direction != USB_DIRECTION_IN))
+
+	if (!polling->ep_mapping || (polling->ep_mapping->pipe.desc.transfer_type != USB_TRANSFER_INTERRUPT)
+	    || (polling->ep_mapping->pipe.desc.direction != USB_DIRECTION_IN))
 		return EINVAL;
-	
-
-	polling_data_t *polling_data = malloc(sizeof(polling_data_t));
-	if (polling_data == NULL) {
+
+	/* Negative value means use descriptor provided value. */
+	if (polling->delay < 0)
+		polling->delay = polling->ep_mapping->descriptor->poll_interval;
+
+	polling->fibril = fibril_create(polling_fibril, polling);
+	if (!polling->fibril)
 		return ENOMEM;
-	}
-
-	/* Fill-in the data. */
-	polling_data->buffer = malloc(sizeof(request_size));
-	if (polling_data->buffer == NULL) {
-		free(polling_data);
-		return ENOMEM;
-	}
-	polling_data->request_size = request_size;
-	polling_data->dev = dev;
-	polling_data->polling_mapping = epm;
-
-	/* Copy provided settings. */
-	polling_data->auto_polling = *polling;
-
-	/* Negative value means use descriptor provided value. */
-	if (polling->delay < 0) {
-		polling_data->auto_polling.delay =
-		    epm->descriptor->poll_interval;
-	}
-
-	fid_t fibril = fibril_create(polling_fibril, polling_data);
-	if (fibril == 0) {
-		free(polling_data->buffer);
-		free(polling_data);
-		return ENOMEM;
-	}
-	fibril_add_ready(fibril);
+
+	fibril_add_ready(polling->fibril);
 
 	/* Fibril launched. That fibril will free the allocated data. */
-
 	return EOK;
 }
-/** Start automatic device polling over interrupt in pipe.
- *
- * The polling settings is copied thus it is okay to destroy the structure
- * after this function returns.
- *
- * @warning There is no guarantee when the request to the device
- * will be sent for the first time (it is possible that this
- * first request would be executed prior to return from this function).
- *
- * @param dev Device to be periodically polled.
- * @param pipe_index Index of the endpoint pipe used for polling.
- * @param polling Polling settings.
- * @param req_size How many bytes to ask for in each request.
- * @param arg Custom argument (passed as is to the callbacks).
- * @return Error code.
- * @retval EOK New fibril polling the device was already started.
- */
-errno_t usb_device_auto_polling(usb_device_t *usb_dev, usb_endpoint_t ep,
-    const usb_device_auto_polling_t *polling, size_t req_size)
-{
-	usb_endpoint_mapping_t *epm = usb_device_get_mapped_ep(usb_dev, ep);
-	return usb_device_auto_polling_internal(usb_dev, epm, polling, req_size);
-}
-
-/** Start automatic device polling over interrupt in pipe.
- *
- * @warning It is up to the callback to produce delays between individual
- * requests.
- *
- * @warning There is no guarantee when the request to the device
- * will be sent for the first time (it is possible that this
- * first request would be executed prior to return from this function).
- *
- * @param dev Device to be periodically polled.
- * @param ep Endpoint  used for polling.
- * @param callback Callback when data are available.
- * @param request_size How many bytes to ask for in each request.
- * @param delay NUmber of ms to wait between queries, -1 to use descriptor val.
- * @param terminated_callback Callback when polling is terminated.
- * @param arg Custom argument (passed as is to the callbacks).
- * @return Error code.
- * @retval EOK New fibril polling the device was already started.
- */
-errno_t usb_device_auto_poll(usb_device_t *dev, usb_endpoint_t ep,
-    usb_polling_callback_t callback, size_t request_size, int delay,
-    usb_polling_terminted_callback_t terminated_callback, void *arg)
-{
-	const usb_device_auto_polling_t auto_polling = {
-		.debug = 1,
-		.auto_clear_halt = true,
-		.delay = delay,
-		.max_failures = MAX_FAILED_ATTEMPTS,
-		.on_data = callback,
-		.on_polling_end = terminated_callback,
-		.on_error = NULL,
-		.arg = arg,
-	};
-
-	usb_endpoint_mapping_t *epm = usb_device_get_mapped_ep(dev, ep);
-	return usb_device_auto_polling_internal(
-	    dev, epm, &auto_polling, request_size);
-}
-
-errno_t usb_device_auto_polling_desc(usb_device_t *usb_dev,
-    const usb_endpoint_description_t *desc,
-    const usb_device_auto_polling_t *polling, size_t req_size)
-{
-	usb_endpoint_mapping_t *epm =
-	    usb_device_get_mapped_ep_desc(usb_dev, desc);
-	return usb_device_auto_polling_internal(usb_dev, epm, polling, req_size);
-}
-
-errno_t usb_device_auto_poll_desc(usb_device_t * usb_dev,
-    const usb_endpoint_description_t *desc, usb_polling_callback_t callback,
-    size_t req_size, int delay,
-    usb_polling_terminted_callback_t terminated_callback, void *arg)
-{
-	const usb_device_auto_polling_t auto_polling = {
-		.debug = 1,
-		.auto_clear_halt = true,
-		.delay = delay,
-		.max_failures = MAX_FAILED_ATTEMPTS,
-		.on_data = callback,
-		.on_polling_end = terminated_callback,
-		.on_error = NULL,
-		.arg = arg,
-	};
-
-	usb_endpoint_mapping_t *epm =
-	    usb_device_get_mapped_ep_desc(usb_dev, desc);
-	return usb_device_auto_polling_internal(
-	    usb_dev, epm, &auto_polling, req_size);
+
+/** Close the polling pipe permanently and synchronously wait
+ *  until the automatic polling fibril terminates.
+ *
+ *  It is safe to deallocate the polling data structure (and its
+ *  data buffer) only after a successful call to this function.
+ *
+ *  @warning Call to this function will trigger execution of the
+ *  on_error() callback with EINTR error code.
+ *
+ *  @parram polling Polling data structure.
+ *  @return Error code.
+ *  @retval EOK Polling fibril has been successfully terminated.
+ */
+errno_t usb_polling_join(usb_polling_t *polling)
+{
+	errno_t rc;
+	if (!polling)
+		return EBADMEM;
+
+	/* Check if the fibril already terminated. */
+	if (!polling->running)
+		return EOK;
+
+	/* Set the flag */
+	polling->joining = true;
+
+	/* Unregister the pipe. */
+	rc = usb_device_unmap_ep(polling->ep_mapping);
+	if (rc != EOK && rc != ENOENT && rc != EHANGUP)
+		return rc;
+
+	/* Wait for the fibril to terminate. */
+	fibril_mutex_lock(&polling->guard);
+	while (polling->running)
+		fibril_condvar_wait(&polling->cv, &polling->guard);
+	fibril_mutex_unlock(&polling->guard);
+
+	return EOK;
 }
 
Index: uspace/lib/usbdev/src/dp.c
===================================================================
--- uspace/lib/usbdev/src/dp.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/src/dp.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -62,4 +62,5 @@
 	NESTING(CONFIGURATION, INTERFACE),
 	NESTING(INTERFACE, ENDPOINT),
+	NESTING(ENDPOINT, SSPEED_EP_COMPANION),
 	NESTING(INTERFACE, HUB),
 	NESTING(INTERFACE, HID),
Index: uspace/lib/usbdev/src/driver.c
===================================================================
--- uspace/lib/usbdev/src/driver.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/src/driver.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -62,5 +62,5 @@
 	errno_t rc = usb_device_create_ddf(gen_dev, driver->endpoints, &err_msg);
 	if (rc != EOK) {
-		usb_log_error("USB device `%s' init failed (%s): %s.\n",
+		usb_log_error("USB device `%s' init failed (%s): %s.",
 		    ddf_dev_get_name(gen_dev), err_msg, str_error(rc));
 		return rc;
@@ -85,11 +85,13 @@
 	assert(driver);
 	assert(driver->ops);
-	if (driver->ops->device_rem == NULL)
+	if (driver->ops->device_remove == NULL)
 		return ENOTSUP;
+
 	/* Just tell the driver to stop whatever it is doing */
 	usb_device_t *usb_dev = ddf_dev_data_get(gen_dev);
-	const errno_t ret = driver->ops->device_rem(usb_dev);
+	const errno_t ret = driver->ops->device_remove(usb_dev);
 	if (ret != EOK)
 		return ret;
+
 	usb_device_destroy_ddf(gen_dev);
 	return EOK;
@@ -117,8 +119,42 @@
 }
 
+/** Callback when the driver is asked to online a specific function.
+ *
+ * This callback is a wrapper for USB specific version of @c fun_online.
+ *
+ * @param gen_dev Device function structure as prepared by DDF.
+ * @return Error code.
+ */
+static int generic_function_online(ddf_fun_t *fun)
+{
+	assert(driver);
+	assert(driver->ops);
+	if (driver->ops->function_online == NULL)
+		return ENOTSUP;
+	return driver->ops->function_online(fun);
+}
+
+/** Callback when the driver is asked to offline a specific function.
+ *
+ * This callback is a wrapper for USB specific version of @c fun_offline.
+ *
+ * @param gen_dev Device function structure as prepared by DDF.
+ * @return Error code.
+ */
+static int generic_function_offline(ddf_fun_t *fun)
+{
+	assert(driver);
+	assert(driver->ops);
+	if (driver->ops->function_offline == NULL)
+		return ENOTSUP;
+	return driver->ops->function_offline(fun);
+}
+
 static driver_ops_t generic_driver_ops = {
 	.dev_add = generic_device_add,
 	.dev_remove = generic_device_remove,
 	.dev_gone = generic_device_gone,
+	.fun_online = generic_function_online,
+	.fun_offline = generic_function_offline,
 };
 static driver_t generic_driver = {
Index: uspace/lib/usbdev/src/pipes.c
===================================================================
--- uspace/lib/usbdev/src/pipes.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/src/pipes.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -36,8 +36,10 @@
 #include <usb/dev/request.h>
 #include <usb/usb.h>
-#include <usb_iface.h>
+#include <usb/dma_buffer.h>
 
 #include <assert.h>
+#include <bitops.h>
 #include <async.h>
+#include <as.h>
 #include <errno.h>
 #include <mem.h>
@@ -51,5 +53,5 @@
 	assert(pipe != NULL);
 
-	if (!pipe->auto_reset_halt || (pipe->endpoint_no != 0)) {
+	if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
 		return;
 	}
@@ -57,6 +59,107 @@
 	/* Prevent infinite recursion. */
 	pipe->auto_reset_halt = false;
-	usb_request_clear_endpoint_halt(pipe, 0);
+	usb_pipe_clear_halt(pipe, pipe);
 	pipe->auto_reset_halt = true;
+}
+
+/* Helper structure to avoid passing loads of arguments through */
+typedef struct {
+	usb_pipe_t *pipe;
+	usb_direction_t dir;
+	bool is_control;	// Only for checking purposes
+
+	usbhc_iface_transfer_request_t req;
+
+	size_t transferred_size;
+} transfer_t;
+
+/**
+ * Issue a transfer in a separate exchange.
+ */
+static errno_t transfer_common(transfer_t *t)
+{
+	if (!t->pipe)
+		return EBADMEM;
+
+	/* Only control writes make sense without buffer */
+	if ((t->dir != USB_DIRECTION_OUT || !t->is_control) && t->req.size == 0)
+		return EINVAL;
+
+	/* Nonzero size requires buffer */
+	if (!dma_buffer_is_set(&t->req.buffer) && t->req.size != 0)
+		return EINVAL;
+
+	/* Check expected direction */
+	if (t->pipe->desc.direction != USB_DIRECTION_BOTH &&
+	    t->pipe->desc.direction != t->dir)
+		return EBADF;
+
+	/* Check expected transfer type */
+	if ((t->pipe->desc.transfer_type == USB_TRANSFER_CONTROL) != t->is_control)
+		return EBADF;
+
+	async_exch_t *exch = async_exchange_begin(t->pipe->bus_session);
+	if (!exch)
+		return ENOMEM;
+
+	t->req.dir = t->dir;
+	t->req.endpoint = t->pipe->desc.endpoint_no;
+
+	const errno_t rc = usbhc_transfer(exch, &t->req, &t->transferred_size);
+
+	async_exchange_end(exch);
+
+	if (rc == ESTALL)
+		clear_self_endpoint_halt(t->pipe);
+
+	return rc;
+}
+
+/**
+ * Setup the transfer request inside transfer according to dma buffer provided.
+ *
+ * TODO: The buffer could have been allocated as a more strict one. Currently,
+ * we assume that the policy is just the requested one.
+ */
+static void setup_dma_buffer(transfer_t *t, void *base, void *ptr, size_t size)
+{
+	t->req.buffer.virt = base;
+	t->req.buffer.policy = t->pipe->desc.transfer_buffer_policy;
+	t->req.offset = ptr - base;
+	t->req.size = size;
+}
+
+/**
+ * Compatibility wrapper for reads/writes without preallocated buffer.
+ */
+static errno_t transfer_wrap_dma(transfer_t *t, void *buf, size_t size)
+{
+	if (size == 0) {
+		setup_dma_buffer(t, NULL, NULL, 0);
+		return transfer_common(t);
+	}
+
+	void *dma_buf = usb_pipe_alloc_buffer(t->pipe, size);
+	setup_dma_buffer(t, dma_buf, dma_buf, size);
+
+	if (t->dir == USB_DIRECTION_OUT)
+		memcpy(dma_buf, buf, size);
+
+	const errno_t err = transfer_common(t);
+
+	if (!err && t->dir == USB_DIRECTION_IN)
+		memcpy(buf, dma_buf, t->transferred_size);
+
+	usb_pipe_free_buffer(t->pipe, dma_buf);
+	return err;
+}
+
+static errno_t prepare_control(transfer_t *t, const void *setup, size_t setup_size)
+{
+	if ((setup == NULL) || (setup_size != 8))
+		return EINVAL;
+	
+	memcpy(&t->req.setup, setup, 8);
+	return EOK;
 }
 
@@ -70,45 +173,29 @@
  * @param[out] data_buffer Buffer for incoming data.
  * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
- * @param[out] data_transfered_size Number of bytes that were actually
- *                                  transfered during the DATA stage.
+ * @param[out] data_transferred_size Number of bytes that were actually
+ *                                  transferred during the DATA stage.
  * @return Error code.
  */
 errno_t usb_pipe_control_read(usb_pipe_t *pipe,
     const void *setup_buffer, size_t setup_buffer_size,
-    void *buffer, size_t buffer_size, size_t *transfered_size)
-{
-	assert(pipe);
-
-	if ((setup_buffer == NULL) || (setup_buffer_size != 8)) {
-		return EINVAL;
-	}
-
-	if ((buffer == NULL) || (buffer_size == 0)) {
-		return EINVAL;
-	}
-
-	if ((pipe->direction != USB_DIRECTION_BOTH)
-	    || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
-		return EBADF;
-	}
-
-	uint64_t setup_packet;
-	memcpy(&setup_packet, setup_buffer, 8);
-
-	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
-	size_t act_size = 0;
-	const errno_t rc = usb_read(exch, pipe->endpoint_no, setup_packet, buffer,
-	    buffer_size, &act_size);
-	async_exchange_end(exch);
-
-	if (rc == ESTALL) {
-		clear_self_endpoint_halt(pipe);
-	}
-
-	if (rc == EOK && transfered_size != NULL) {
-		*transfered_size = act_size;
-	}
-
-	return rc;
+    void *buffer, size_t buffer_size, size_t *transferred_size)
+{
+	errno_t err;
+	transfer_t transfer = {
+		.pipe = pipe,
+		.dir = USB_DIRECTION_IN,
+		.is_control = true,
+	};
+
+	if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
+		return err;
+
+	if ((err = transfer_wrap_dma(&transfer, buffer, buffer_size)))
+		return err;
+
+	if (transferred_size)
+		*transferred_size = transfer.transferred_size;
+
+	return EOK;
 }
 
@@ -129,35 +216,38 @@
 {
 	assert(pipe);
-
-	if ((setup_buffer == NULL) || (setup_buffer_size != 8)) {
-		return EINVAL;
-	}
-
-	if ((buffer == NULL) && (buffer_size > 0)) {
-		return EINVAL;
-	}
-
-	if ((buffer != NULL) && (buffer_size == 0)) {
-		return EINVAL;
-	}
-
-	if ((pipe->direction != USB_DIRECTION_BOTH)
-	    || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
-		return EBADF;
-	}
-
-	uint64_t setup_packet;
-	memcpy(&setup_packet, setup_buffer, 8);
-
-	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
-	const errno_t rc = usb_write(exch,
-	    pipe->endpoint_no, setup_packet, buffer, buffer_size);
-	async_exchange_end(exch);
-
-	if (rc == ESTALL) {
-		clear_self_endpoint_halt(pipe);
-	}
-
-	return rc;
+	errno_t err;
+	transfer_t transfer = {
+		.pipe = pipe,
+		.dir = USB_DIRECTION_OUT,
+		.is_control = true,
+	};
+
+	if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
+		return err;
+
+	return transfer_wrap_dma(&transfer, (void *) buffer, buffer_size);
+}
+
+/**
+ * Allocate a buffer for data transmission, that satisfies the constraints
+ * imposed by the host controller.
+ *
+ * @param[in] pipe Pipe for which the buffer is allocated
+ * @param[in] size Size of the required buffer
+ */
+void *usb_pipe_alloc_buffer(usb_pipe_t *pipe, size_t size)
+{
+	dma_buffer_t buf;
+	if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
+		return NULL;
+
+	return buf.virt;
+}
+
+void usb_pipe_free_buffer(usb_pipe_t *pipe, void *buffer)
+{
+	dma_buffer_t buf;
+	buf.virt = buffer;
+	dma_buffer_free(&buf);
 }
 
@@ -167,44 +257,24 @@
  * @param[out] buffer Buffer where to store the data.
  * @param[in] size Size of the buffer (in bytes).
- * @param[out] size_transfered Number of bytes that were actually transfered.
+ * @param[out] size_transferred Number of bytes that were actually transferred.
  * @return Error code.
  */
 errno_t usb_pipe_read(usb_pipe_t *pipe,
-    void *buffer, size_t size, size_t *size_transfered)
-{
-	assert(pipe);
-
-	if (buffer == NULL) {
-		return EINVAL;
-	}
-
-	if (size == 0) {
-		return EINVAL;
-	}
-
-	if (pipe->direction != USB_DIRECTION_IN) {
-		return EBADF;
-	}
-
-	if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
-		return EBADF;
-	}
-
-	/* Isochronous transfer are not supported (yet) */
-	if (pipe->transfer_type != USB_TRANSFER_INTERRUPT &&
-	    pipe->transfer_type != USB_TRANSFER_BULK)
-	    return ENOTSUP;
-
-	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
-	size_t act_size = 0;
-	const errno_t rc =
-	    usb_read(exch, pipe->endpoint_no, 0, buffer, size, &act_size);
-	async_exchange_end(exch);
-
-	if (rc == EOK && size_transfered != NULL) {
-		*size_transfered = act_size;
-	}
-
-	return rc;
+    void *buffer, size_t size, size_t *size_transferred)
+{
+	assert(pipe);
+	errno_t err;
+	transfer_t transfer = {
+		.pipe = pipe,
+		.dir = USB_DIRECTION_IN,
+	};
+
+	if ((err = transfer_wrap_dma(&transfer, buffer, size)))
+		return err;
+
+	if (size_transferred)
+		*size_transferred = transfer.transferred_size;
+
+	return EOK;
 }
 
@@ -219,26 +289,63 @@
 {
 	assert(pipe);
-
-	if (buffer == NULL || size == 0) {
-		return EINVAL;
-	}
-
-	if (pipe->direction != USB_DIRECTION_OUT) {
-		return EBADF;
-	}
-
-	if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
-		return EBADF;
-	}
-
-	/* Isochronous transfer are not supported (yet) */
-	if (pipe->transfer_type != USB_TRANSFER_INTERRUPT &&
-	    pipe->transfer_type != USB_TRANSFER_BULK)
-	    return ENOTSUP;
-
-	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
-	const errno_t rc = usb_write(exch, pipe->endpoint_no, 0, buffer, size);
-	async_exchange_end(exch);
-	return rc;
+	transfer_t transfer = {
+		.pipe = pipe,
+		.dir = USB_DIRECTION_OUT,
+	};
+
+	return transfer_wrap_dma(&transfer, (void *) buffer, size);
+}
+
+/**
+ * Request a read (in) transfer on an endpoint pipe, declaring that buffer
+ * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
+ *
+ * @param[in] pipe Pipe used for the transfer.
+ * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
+ * @param[in] size Size of the buffer (in bytes).
+ * @param[out] size_transferred Number of bytes that were actually transferred.
+ * @return Error code.
+ */
+errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size,
+    size_t *size_transferred)
+{
+	assert(pipe);
+	errno_t err;
+	transfer_t transfer = {
+		.pipe = pipe,
+		.dir = USB_DIRECTION_IN,
+	};
+
+	setup_dma_buffer(&transfer, base, ptr, size);
+
+	if ((err = transfer_common(&transfer)))
+		return err;
+
+	if (size_transferred)
+		*size_transferred = transfer.transferred_size;
+
+	return EOK;
+}
+
+/**
+ * Request a write (out) transfer on an endpoint pipe, declaring that buffer
+ * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
+ *
+ * @param[in] pipe Pipe used for the transfer.
+ * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
+ * @param[in] size Size of the buffer (in bytes).
+ * @return Error code.
+ */
+errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *base, void* ptr,  size_t size)
+{
+	assert(pipe);
+	transfer_t transfer = {
+		.pipe = pipe,
+		.dir = USB_DIRECTION_OUT,
+	};
+
+	setup_dma_buffer(&transfer, base, ptr, size);
+
+	return transfer_common(&transfer);
 }
 
@@ -246,21 +353,11 @@
  *
  * @param pipe Endpoint pipe to be initialized.
- * @param endpoint_no Endpoint number (in USB 1.1 in range 0 to 15).
- * @param transfer_type Transfer type (e.g. interrupt or bulk).
- * @param max_packet_size Maximum packet size in bytes.
- * @param direction Endpoint direction (in/out).
- * @return Error code.
- */
-errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_endpoint_t endpoint_no,
-    usb_transfer_type_t transfer_type, size_t max_packet_size,
-    usb_direction_t direction, unsigned packets, usb_dev_session_t *bus_session)
-{
-	assert(pipe);
-
-	pipe->endpoint_no = endpoint_no;
-	pipe->transfer_type = transfer_type;
-	pipe->packets = packets;
-	pipe->max_packet_size = max_packet_size;
-	pipe->direction = direction;
+ * @param bus_session Endpoint pipe to be initialized.
+ * @return Error code.
+ */
+errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
+{
+	assert(pipe);
+
 	pipe->auto_reset_halt = false;
 	pipe->bus_session = bus_session;
@@ -269,20 +366,30 @@
 }
 
-/** Initialize USB endpoint pipe as the default zero control pipe.
+static const usb_pipe_desc_t default_control_pipe = {
+	.endpoint_no = 0,
+	.transfer_type = USB_TRANSFER_CONTROL,
+	.direction = USB_DIRECTION_BOTH,
+	.max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
+	.transfer_buffer_policy = DMA_POLICY_STRICT,
+};
+
+/** Initialize USB default control pipe.
+ *
+ * This one is special because it must not be registered, it is registered automatically.
  *
  * @param pipe Endpoint pipe to be initialized.
- * @return Error code.
- */
-errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe,
-    usb_dev_session_t *bus_session)
-{
-	assert(pipe);
-
-	const errno_t rc = usb_pipe_initialize(pipe, 0, USB_TRANSFER_CONTROL,
-	    CTRL_PIPE_MIN_PACKET_SIZE, USB_DIRECTION_BOTH, 1, bus_session);
-
+ * @param bus_session Endpoint pipe to be initialized.
+ * @return Error code.
+ */
+errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
+{
+	const errno_t ret = usb_pipe_initialize(pipe, bus_session);
+	if (ret)
+		return ret;
+
+	pipe->desc = default_control_pipe;
 	pipe->auto_reset_halt = true;
 
-	return rc;
+	return EOK;
 }
 
@@ -290,8 +397,46 @@
  *
  * @param pipe Pipe to be registered.
- * @param interval Polling interval.
- * @return Error code.
- */
-errno_t usb_pipe_register(usb_pipe_t *pipe, unsigned interval)
+ * @param ep_desc Matched endpoint descriptor
+ * @param comp_desc Matched superspeed companion descriptro, if any
+ * @return Error code.
+ */
+errno_t usb_pipe_register(usb_pipe_t *pipe, const usb_standard_endpoint_descriptor_t *ep_desc, const usb_superspeed_endpoint_companion_descriptor_t *comp_desc)
+{
+	assert(pipe);
+	assert(pipe->bus_session);
+	assert(ep_desc);
+
+	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
+	if (!exch)
+		return ENOMEM;
+
+	usb_endpoint_descriptors_t descriptors;
+
+#define COPY(field) descriptors.endpoint.field = ep_desc->field
+	COPY(endpoint_address);
+	COPY(attributes);
+	COPY(max_packet_size);
+	COPY(poll_interval);
+#undef COPY
+
+#define COPY(field) descriptors.companion.field = comp_desc->field
+	if (comp_desc) {
+		COPY(max_burst);
+		COPY(attributes);
+		COPY(bytes_per_interval);
+	}
+#undef COPY
+
+	const errno_t ret = usbhc_register_endpoint(exch, &pipe->desc, &descriptors);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/** Revert endpoint registration with the host controller.
+ *
+ * @param pipe Pipe to be unregistered.
+ * @return Error code.
+ */
+errno_t usb_pipe_unregister(usb_pipe_t *pipe)
 {
 	assert(pipe);
@@ -300,29 +445,11 @@
 	if (!exch)
 		return ENOMEM;
-	const errno_t ret = usb_register_endpoint(exch, pipe->endpoint_no,
-	    pipe->transfer_type, pipe->direction, pipe->max_packet_size,
-	    pipe->packets, interval);
+
+	const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
+
 	async_exchange_end(exch);
 	return ret;
 }
 
-/** Revert endpoint registration with the host controller.
- *
- * @param pipe Pipe to be unregistered.
- * @return Error code.
- */
-errno_t usb_pipe_unregister(usb_pipe_t *pipe)
-{
-	assert(pipe);
-	assert(pipe->bus_session);
-	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
-	if (!exch)
-		return ENOMEM;
-	const errno_t ret = usb_unregister_endpoint(exch, pipe->endpoint_no,
-	    pipe->direction);
-	async_exchange_end(exch);
-	return ret;
-}
-
 /**
  * @}
Index: uspace/lib/usbdev/src/pipesinit.c
===================================================================
--- uspace/lib/usbdev/src/pipesinit.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/src/pipesinit.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -38,4 +38,5 @@
 #include <usb/dev/request.h>
 #include <usb/usb.h>
+#include <usb/debug.h>
 #include <usb/descriptor.h>
 
@@ -59,4 +60,5 @@
 	NESTING(INTERFACE, HID),
 	NESTING(HID, HID_REPORT),
+	NESTING(ENDPOINT, SSPEED_EP_COMPANION),
 	LAST_NESTING
 };
@@ -70,4 +72,14 @@
 {
 	return descriptor[1] == USB_DESCTYPE_ENDPOINT;
+}
+
+/** Tells whether given descriptor is of superspeed companion type.
+ *
+ * @param descriptor Descriptor in question.
+ * @return Whether the given descriptor is superspeed companion descriptor.
+ */
+static inline bool is_superspeed_companion_descriptor(const uint8_t *descriptor)
+{
+	return descriptor[1] == USB_DESCTYPE_SSPEED_EP_COMPANION;
 }
 
@@ -134,5 +146,6 @@
 		if (interface_number_fits
 		    && interface_setting_fits
-		    && endpoint_descriptions_fits) {
+		    && endpoint_descriptions_fits
+		    && !mapping->present) {
 			return mapping;
 		}
@@ -141,4 +154,5 @@
 		mapping_count--;
 	}
+
 	return NULL;
 }
@@ -150,4 +164,5 @@
  * @param interface Interface descriptor under which belongs the @p endpoint.
  * @param endpoint Endpoint descriptor.
+ * @param companion Superspeed companion descriptor.
  * @return Error code.
  */
@@ -156,4 +171,5 @@
     usb_standard_interface_descriptor_t *interface,
     usb_standard_endpoint_descriptor_t *endpoint_desc,
+    usb_superspeed_endpoint_companion_descriptor_t *companion_desc,
     usb_dev_session_t *bus_session)
 {
@@ -162,15 +178,7 @@
 	 * Get endpoint characteristics.
 	 */
-
-	/* Actual endpoint number is in bits 0..3 */
-	const usb_endpoint_t ep_no = endpoint_desc->endpoint_address & 0x0F;
-
 	const usb_endpoint_description_t description = {
-		/* Endpoint direction is set by bit 7 */
-		.direction = (endpoint_desc->endpoint_address & 128)
-		    ? USB_DIRECTION_IN : USB_DIRECTION_OUT,
-		/* Transfer type is in bits 0..2 and
-		 * the enum values corresponds 1:1 */
-		.transfer_type = endpoint_desc->attributes & 3,
+		.transfer_type = USB_ED_GET_TRANSFER_TYPE(*endpoint_desc),
+		.direction = USB_ED_GET_DIR(*endpoint_desc),
 
 		/* Get interface characteristics. */
@@ -194,17 +202,11 @@
 	}
 
-	errno_t rc = usb_pipe_initialize(&ep_mapping->pipe,
-	    ep_no, description.transfer_type,
-	    ED_MPS_PACKET_SIZE_GET(
-	        uint16_usb2host(endpoint_desc->max_packet_size)),
-	    description.direction,
-	    ED_MPS_TRANS_OPPORTUNITIES_GET(
-	        uint16_usb2host(endpoint_desc->max_packet_size)), bus_session);
-	if (rc != EOK) {
-		return rc;
-	}
+	errno_t err = usb_pipe_initialize(&ep_mapping->pipe, bus_session);
+	if (err)
+		return err;
 
 	ep_mapping->present = true;
 	ep_mapping->descriptor = endpoint_desc;
+	ep_mapping->companion_descriptor = companion_desc;
 	ep_mapping->interface = interface;
 
@@ -235,4 +237,12 @@
 	do {
 		if (is_endpoint_descriptor(descriptor)) {
+			/* Check if companion descriptor is present too, it should immediatelly follow. */
+			const uint8_t *companion_desc = usb_dp_get_nested_descriptor(parser,
+				parser_data, descriptor);
+			if (companion_desc && !is_superspeed_companion_descriptor(companion_desc)) {
+				/* Not what we wanted, don't pass it further. */
+				companion_desc = NULL;
+			}
+
 			(void) process_endpoint(mapping, mapping_count,
 			    (usb_standard_interface_descriptor_t *)
@@ -240,4 +250,6 @@
 			    (usb_standard_endpoint_descriptor_t *)
 			        descriptor,
+			    (usb_superspeed_endpoint_companion_descriptor_t *)
+			        companion_desc,
 			    bus_session);
 		}
@@ -288,5 +300,5 @@
 	if (config_descriptor == NULL)
 		return EBADMEM;
-	
+
 	if (config_descriptor_size <
 	    sizeof(usb_standard_configuration_descriptor_t)) {
@@ -328,51 +340,4 @@
 }
 
-/** Probe default control pipe for max packet size.
- *
- * The function tries to get the correct value of max packet size several
- * time before giving up.
- *
- * The session on the pipe shall not be started.
- *
- * @param pipe Default control pipe.
- * @return Error code.
- */
-errno_t usb_pipe_probe_default_control(usb_pipe_t *pipe)
-{
-	assert(pipe);
-	static_assert(DEV_DESCR_MAX_PACKET_SIZE_OFFSET < CTRL_PIPE_MIN_PACKET_SIZE);
-
-	if ((pipe->direction != USB_DIRECTION_BOTH) ||
-	    (pipe->transfer_type != USB_TRANSFER_CONTROL) ||
-	    (pipe->endpoint_no != 0)) {
-		return EINVAL;
-	}
-
-	uint8_t dev_descr_start[CTRL_PIPE_MIN_PACKET_SIZE];
-	size_t transferred_size;
-	errno_t rc;
-	for (size_t attempt_var = 0; attempt_var < 3; ++attempt_var) {
-		rc = usb_request_get_descriptor(pipe, USB_REQUEST_TYPE_STANDARD,
-		    USB_REQUEST_RECIPIENT_DEVICE, USB_DESCTYPE_DEVICE,
-		    0, 0, dev_descr_start, CTRL_PIPE_MIN_PACKET_SIZE,
-		    &transferred_size);
-		if (rc == EOK) {
-			if (transferred_size != CTRL_PIPE_MIN_PACKET_SIZE) {
-				rc = ELIMIT;
-				continue;
-			}
-			break;
-		}
-	}
-	if (rc != EOK) {
-		return rc;
-	}
-
-	pipe->max_packet_size
-	    = dev_descr_start[DEV_DESCR_MAX_PACKET_SIZE_OFFSET];
-
-	return EOK;
-}
-
 /**
  * @}
Index: uspace/lib/usbdev/src/request.c
===================================================================
--- uspace/lib/usbdev/src/request.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbdev/src/request.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -118,5 +118,5 @@
  * @param data_size        Size of the @p data buffer
  *                         (in native endianness).
- * @param actual_data_size Actual size of transfered data
+ * @param actual_data_size Actual size of transferred data
  *                         (in native endianness).
  *
@@ -183,12 +183,12 @@
 
 	uint16_t status_usb_endianess;
-	size_t data_transfered_size;
+	size_t data_transferred_size;
 	errno_t rc = usb_control_request_get(pipe, USB_REQUEST_TYPE_STANDARD,
 	    recipient, USB_DEVREQ_GET_STATUS, 0, uint16_host2usb(index),
-	    &status_usb_endianess, 2, &data_transfered_size);
-	if (rc != EOK) {
-		return rc;
-	}
-	if (data_transfered_size != 2) {
+	    &status_usb_endianess, 2, &data_transferred_size);
+	if (rc != EOK) {
+		return rc;
+	}
+	if (data_transferred_size != 2) {
 		return ELIMIT;
 	}
@@ -314,12 +314,12 @@
 	 */
 	uint8_t tmp_buffer;
-	size_t bytes_transfered;
+	size_t bytes_transferred;
 	rc = usb_request_get_descriptor(pipe, request_type, recipient,
 	    descriptor_type, descriptor_index, language,
-	    &tmp_buffer, sizeof(tmp_buffer), &bytes_transfered);
-	if (rc != EOK) {
-		return rc;
-	}
-	if (bytes_transfered != 1) {
+	    &tmp_buffer, sizeof(tmp_buffer), &bytes_transferred);
+	if (rc != EOK) {
+		return rc;
+	}
+	if (bytes_transferred != 1) {
 		return ELIMIT;
 	}
@@ -340,10 +340,10 @@
 	rc = usb_request_get_descriptor(pipe, request_type, recipient,
 	    descriptor_type, descriptor_index, language,
-	    buffer, size, &bytes_transfered);
+	    buffer, size, &bytes_transferred);
 	if (rc != EOK) {
 		free(buffer);
 		return rc;
 	}
-	if (bytes_transfered != size) {
+	if (bytes_transferred != size) {
 		free(buffer);
 		return ELIMIT;
@@ -824,5 +824,5 @@
  * @return Error code.
  */
-errno_t usb_request_clear_endpoint_halt(usb_pipe_t *pipe, uint16_t ep_index)
+static errno_t usb_request_clear_endpoint_halt(usb_pipe_t *pipe, uint16_t ep_index)
 {
 	return usb_request_clear_feature(pipe,
@@ -843,6 +843,8 @@
 		return EINVAL;
 	}
-	return usb_request_clear_endpoint_halt(ctrl_pipe,
-	    target_pipe->endpoint_no);
+
+	uint16_t index = target_pipe->desc.endpoint_no;
+	index |= (target_pipe->desc.direction == USB_DIRECTION_IN) << 7;
+	return usb_request_clear_endpoint_halt(ctrl_pipe, index);
 }
 
@@ -858,5 +860,5 @@
 {
 	uint16_t status_tmp;
-	uint16_t pipe_index = (uint16_t) pipe->endpoint_no;
+	uint16_t pipe_index = (uint16_t) pipe->desc.endpoint_no;
 	errno_t rc = usb_request_get_status(ctrl_pipe,
 	    USB_REQUEST_RECIPIENT_ENDPOINT, uint16_host2usb(pipe_index),
Index: uspace/lib/usbhid/src/hiddescriptor.c
===================================================================
--- uspace/lib/usbhid/src/hiddescriptor.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhid/src/hiddescriptor.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -176,5 +176,5 @@
 
 	if(report_item->usages_count > 0){
-		usages = malloc(sizeof(int32_t) * report_item->usages_count);
+		usages = malloc(sizeof(uint32_t) * report_item->usages_count);
 		memcpy(usages, report_item->usages, sizeof(int32_t) *
 				report_item->usages_count); 
@@ -247,19 +247,5 @@
 		field->size = report_item->size;
 
-		if(report_item->type == USB_HID_REPORT_TYPE_INPUT) {
-			int offset = report_item->offset + report_item->size * i;
-			int field_offset = (offset/8)*8 + (offset/8 + 1) * 8 - 
-				offset - report_item->size;
-			if(field_offset < 0) {
-				field->offset = 0;
-			}
-			else {
-				field->offset = field_offset;
-			}
-		}
-		else {
-			field->offset = report_item->offset + (i * report_item->size);
-		}
-
+		field->offset = report_item->offset + (i * report_item->size);
 
 		if(report->use_report_ids != 0) {
@@ -896,5 +882,5 @@
 {
 	if(list == NULL || list_empty(list)) {
-	    usb_log_debug("\tempty\n");
+	    usb_log_debug("\tempty");
 	    return;
 	}
@@ -902,28 +888,26 @@
         list_foreach(*list, ritems_link, usb_hid_report_field_t,
     	    report_item) {
-		usb_log_debug("\t\tOFFSET: %X\n", report_item->offset);
-		usb_log_debug("\t\tSIZE: %zu\n", report_item->size);
-		usb_log_debug("\t\tLOGMIN: %d\n",
+		usb_log_debug("\t\tOFFSET: %u", report_item->offset);
+		usb_log_debug("\t\tSIZE: %zu", report_item->size);
+		usb_log_debug("\t\tLOGMIN: %d",
 			report_item->logical_minimum);
-		usb_log_debug("\t\tLOGMAX: %d\n",
+		usb_log_debug("\t\tLOGMAX: %d",
 			report_item->logical_maximum);
-		usb_log_debug("\t\tPHYMIN: %d\n",
+		usb_log_debug("\t\tPHYMIN: %d",
 			report_item->physical_minimum);
-		usb_log_debug("\t\tPHYMAX: %d\n",
+		usb_log_debug("\t\tPHYMAX: %d",
 			report_item->physical_maximum);
-		usb_log_debug("\t\ttUSAGEMIN: %X\n",
+		usb_log_debug("\t\ttUSAGEMIN: %X",
 			report_item->usage_minimum);
-		usb_log_debug("\t\tUSAGEMAX: %X\n",
+		usb_log_debug("\t\tUSAGEMAX: %X",
 			       report_item->usage_maximum);
-		usb_log_debug("\t\tUSAGES COUNT: %zu\n",
+		usb_log_debug("\t\tUSAGES COUNT: %zu",
 			report_item->usages_count);
 
-		usb_log_debug("\t\tVALUE: %X\n", report_item->value);
-		usb_log_debug("\t\ttUSAGE: %X\n", report_item->usage);
-		usb_log_debug("\t\tUSAGE PAGE: %X\n", report_item->usage_page);
+		usb_log_debug("\t\tVALUE: %X", report_item->value);
+		usb_log_debug("\t\ttUSAGE: %X", report_item->usage);
+		usb_log_debug("\t\tUSAGE PAGE: %X", report_item->usage_page);
 
 		usb_hid_print_usage_path(report_item->collection_path);
-
-		usb_log_debug("\n");
 	}
 }
@@ -943,12 +927,12 @@
 	list_foreach(report->reports, reports_link,
 	    usb_hid_report_description_t, report_des) {
-		usb_log_debug("Report ID: %d\n", report_des->report_id);
-		usb_log_debug("\tType: %d\n", report_des->type);
-		usb_log_debug("\tLength: %zu\n", report_des->bit_length);
-		usb_log_debug("\tB Size: %zu\n",
+		usb_log_debug("Report ID: %d", report_des->report_id);
+		usb_log_debug("\tType: %d", report_des->type);
+		usb_log_debug("\tLength: %zu", report_des->bit_length);
+		usb_log_debug("\tB Size: %zu",
 			usb_hid_report_byte_size(report,
 				report_des->report_id,
 				report_des->type));
-		usb_log_debug("\tItems: %zu\n", report_des->item_length);
+		usb_log_debug("\tItems: %zu", report_des->item_length);
 
 		usb_hid_descriptor_print_list(&report_des->report_items);
Index: uspace/lib/usbhid/src/hidparser.c
===================================================================
--- uspace/lib/usbhid/src/hidparser.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhid/src/hidparser.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -40,4 +40,6 @@
 #include <usb/debug.h>
 #include <assert.h>
+#include <bitops.h>
+#include <macros.h>
 
 
@@ -199,12 +201,4 @@
 int usb_hid_translate_data(usb_hid_report_field_t *item, const uint8_t *data)
 {
-	int resolution;
-	int offset;
-	int part_size;
-	
-	int32_t value = 0;
-	int32_t mask = 0;
-	const uint8_t *foo = 0;
-
 	/* now only short tags are allowed */
 	if (item->size > 32) {
@@ -214,51 +208,44 @@
 	if ((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
 		item->physical_minimum = item->logical_minimum;
-		item->physical_maximum = item->logical_maximum;			
-	}
-	
-
+		item->physical_maximum = item->logical_maximum;
+	}
+
+	int resolution;
 	if (item->physical_maximum == item->physical_minimum) {
 	    resolution = 1;
 	} else {
-	    resolution = (item->logical_maximum - item->logical_minimum) / 
-		((item->physical_maximum - item->physical_minimum) * 
+	    resolution = (item->logical_maximum - item->logical_minimum) /
+		((item->physical_maximum - item->physical_minimum) *
 		(usb_pow(10, (item->unit_exponent))));
 	}
 
-	offset = item->offset;
-	// FIXME
-	if ((size_t) (offset / 8) != (size_t) ((offset+item->size - 1) / 8)) {
-		
-		part_size = 0;
-
-		size_t i = 0;
-		for (i = (size_t) (offset / 8);
-		    i <= (size_t) (offset + item->size - 1) / 8; i++) {
-			if (i == (size_t) (offset / 8)) {
-				/* the higher one */
-				part_size = 8 - (offset % 8);
-				foo = data + i;
-				mask =  ((1 << (item->size - part_size)) - 1);
-				value = (*foo & mask);
-			} else if (i == ((offset + item->size - 1) / 8)) {
-				/* the lower one */
-				foo = data + i;
-				mask = ((1 << (item->size - part_size)) - 1) <<
-				    (8 - (item->size - part_size));
-
-				value = (((*foo & mask) >> (8 - 
-				    (item->size - part_size))) << part_size) + 
-				    value;
-			} else {
-				value = (*(data + 1) << (part_size + 8)) +
-				    value;
-				part_size += 8;
-			}
-		}
-	} else {		
-		foo = data + (offset / 8);
-		mask = ((1 << item->size) - 1) <<
-		    (8 - ((offset % 8) + item->size));
-		value = (*foo & mask) >> (8 - ((offset % 8) + item->size));
+	int32_t value = 0;
+
+	/* First, skip all bytes we don't care */
+	data += item->offset / 8;
+
+	int bits = item->size;
+	int taken = 0;
+
+	/* Than we take the higher bits from the LSB */
+	const unsigned bit_offset = item->offset % 8;
+	const int lsb_bits = min(bits, 8);
+
+	value |= (*data >> bit_offset) & BIT_RRANGE(uint8_t, lsb_bits);
+	bits -= lsb_bits;
+	taken += lsb_bits;
+	data++;
+
+	/* Then there may be bytes, which we take as a whole. */
+	while (bits > 8) {
+		value |= *data << taken;
+		taken += 8;
+		bits -= 8;
+		data++;
+	}
+
+	/* And, finally, lower bits from HSB. */
+	if (bits > 0) {
+		value |= (*data & BIT_RRANGE(uint8_t, bits)) << taken;
 	}
 
@@ -366,5 +353,5 @@
 		length = report_item->size;
 		
-		usb_log_debug("\ttranslated value: %x\n", value);
+		usb_log_debug("\ttranslated value: %x", value);
 
 		if ((offset / 8) == ((offset + length - 1) / 8)) {
@@ -437,5 +424,5 @@
 
 	if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags)) {
-		ret = item->logical_minimum;
+		return item->logical_minimum;
 	}
 
Index: uspace/lib/usbhid/src/hidpath.c
===================================================================
--- uspace/lib/usbhid/src/hidpath.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhid/src/hidpath.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -173,13 +173,13 @@
 void usb_hid_print_usage_path(usb_hid_report_path_t *path)
 {
-	usb_log_debug("USAGE_PATH FOR RId(%d):\n", path->report_id);
-	usb_log_debug("\tLENGTH: %d\n", path->depth);
+	usb_log_debug("USAGE_PATH FOR RId(%d):", path->report_id);
+	usb_log_debug("\tLENGTH: %d", path->depth);
 
 	list_foreach(path->items, rpath_items_link,
 	    usb_hid_report_usage_path_t, path_item) {
 
-		usb_log_debug("\tUSAGE_PAGE: %X\n", path_item->usage_page);
-		usb_log_debug("\tUSAGE: %X\n", path_item->usage);
-		usb_log_debug("\tFLAGS: %d\n", path_item->flags);
+		usb_log_debug("\tUSAGE_PAGE: %X", path_item->usage_page);
+		usb_log_debug("\tUSAGE: %X", path_item->usage);
+		usb_log_debug("\tFLAGS: %d", path_item->flags);
 	}
 }
Index: uspace/lib/usbhid/src/hidreport.c
===================================================================
--- uspace/lib/usbhid/src/hidreport.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhid/src/hidreport.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -84,5 +84,5 @@
 	
 	if (d == NULL) {
-		usb_log_error("The %d. interface descriptor not found!\n",
+		usb_log_error("The %d. interface descriptor not found!",
 		    usb_device_get_iface_number(dev));
 		return ENOENT;
@@ -104,5 +104,5 @@
 	
 	if (d == NULL) {
-		usb_log_fatal("No HID descriptor found!\n");
+		usb_log_fatal("No HID descriptor found!");
 		return ENOENT;
 	}
@@ -130,5 +130,5 @@
 	}
 	
-	usb_log_debug("Getting Report descriptor, expected size: %u\n", length);
+	usb_log_debug("Getting Report descriptor, expected size: %u", length);
 	
 	/*
@@ -156,5 +156,5 @@
 	*size = length;
 	
-	usb_log_debug("Done.\n");
+	usb_log_debug("Done.");
 	
 	return EOK;
@@ -178,5 +178,5 @@
 	
 	if (rc != EOK) {
-		usb_log_error("Problem with getting Report descriptor: %s.\n",
+		usb_log_error("Problem with getting Report descriptor: %s.",
 		    str_error(rc));
 		if (*report_desc != NULL) {
@@ -191,5 +191,5 @@
 	rc = usb_hid_parse_report_descriptor(report, *report_desc, *report_size);
 	if (rc != EOK) {
-		usb_log_error("Problem parsing Report descriptor: %s.\n",
+		usb_log_error("Problem parsing Report descriptor: %s.",
 		    str_error(rc));
 		free(*report_desc);
Index: uspace/lib/usbhid/src/hidreq.c
===================================================================
--- uspace/lib/usbhid/src/hidreq.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhid/src/hidreq.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -62,5 +62,5 @@
 {
 	if (ctrl_pipe == NULL) {
-		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
+		usb_log_warning("usbhid_req_set_report(): no pipe given.");
 		return EINVAL;
 	}
@@ -82,5 +82,5 @@
 	value |= (type << 8);
 
-	usb_log_debug("Sending Set Report request to the device.\n");
+	usb_log_debug("Sending Set Report request to the device.");
 	
 	rc = usb_control_request_set(ctrl_pipe,
@@ -112,5 +112,5 @@
 {
 	if (ctrl_pipe == NULL) {
-		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
+		usb_log_warning("usbhid_req_set_report(): no pipe given.");
 		return EINVAL;
 	}
@@ -160,5 +160,5 @@
 {
 	if (ctrl_pipe == NULL) {
-		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
+		usb_log_warning("usbhid_req_set_report(): no pipe given.");
 		return EINVAL;
 	}
@@ -215,5 +215,5 @@
 {
 	if (ctrl_pipe == NULL) {
-		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
+		usb_log_warning("usbhid_req_set_report(): no pipe given.");
 		return EINVAL;
 	}
@@ -235,5 +235,5 @@
 	value |= (type << 8);
 	
-	usb_log_debug("Sending Get Report request to the device.\n");
+	usb_log_debug("Sending Get Report request to the device.");
 	
 	rc = usb_control_request_get(ctrl_pipe, 
@@ -266,5 +266,5 @@
 {
 	if (ctrl_pipe == NULL) {
-		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
+		usb_log_warning("usbhid_req_set_report(): no pipe given.");
 		return EINVAL;
 	}
@@ -300,5 +300,5 @@
 	
 	if (actual_size != 1) {
-		usb_log_warning("Wrong data size: %zu, expected: 1.\n",
+		usb_log_warning("Wrong data size: %zu, expected: 1.",
 			actual_size);
 		return ELIMIT;
@@ -327,5 +327,5 @@
 {
 	if (ctrl_pipe == NULL) {
-		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
+		usb_log_warning("usbhid_req_set_report(): no pipe given.");
 		return EINVAL;
 	}
@@ -363,5 +363,5 @@
 	
 	if (actual_size != 1) {
-		usb_log_warning("Wrong data size: %zu, expected: 1.\n",
+		usb_log_warning("Wrong data size: %zu, expected: 1.",
 			actual_size);
 		return ELIMIT;
Index: uspace/lib/usbhost/Makefile
===================================================================
--- uspace/lib/usbhost/Makefile	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/Makefile	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -37,5 +37,8 @@
 	src/endpoint.c \
 	src/hcd.c \
-	src/usb_bus.c \
+	src/bus.c \
+	src/usb2_bus.c \
+	src/bandwidth.c \
+	src/utility.c \
 	src/usb_transfer_batch.c
 
Index: uspace/lib/usbhost/include/usb/host/bandwidth.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bandwidth.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/include/usb/host/bandwidth.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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
+ *
+ * Bandwidth calculation functions. Shared among uhci, ohci and ehci drivers.
+ */
+
+#ifndef LIBUSBHOST_HOST_BANDWIDTH_H
+#define LIBUSBHOST_HOST_BANDWIDTH_H
+
+#include <usb/usb.h>
+
+#include <stddef.h>
+
+typedef struct endpoint endpoint_t;
+
+typedef size_t (*endpoint_count_bw_t)(endpoint_t *);
+
+typedef struct {
+	size_t available_bandwidth;
+	endpoint_count_bw_t count_bw;
+} bandwidth_accounting_t;
+
+extern const bandwidth_accounting_t bandwidth_accounting_usb11;
+extern const bandwidth_accounting_t bandwidth_accounting_usb2;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/include/usb/host/bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bus.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/include/usb/host/bus.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,190 @@
+/*
+ * 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
+ * Virtual base for usb bus implementations.
+ *
+ * The purpose of this structure is to keep information about connected devices
+ * and endpoints, manage available bandwidth and the toggle bit flipping.
+ *
+ * The generic implementation is provided for USB 1 and 2 in usb2_bus.c. Some
+ * details in [OUE]HCI are solved through overriding some functions. XHCI does
+ * not need the bookkeeping functionality, because addresses are managed by HC
+ * itself.
+ */
+#ifndef LIBUSBHOST_HOST_BUS_H
+#define LIBUSBHOST_HOST_BUS_H
+
+#include <assert.h>
+#include <fibril_synch.h>
+#include <stdbool.h>
+#include <usb/host/hcd.h>
+#include <usb/request.h>
+#include <usb/usb.h>
+#include <usbhc_iface.h>
+
+typedef struct hcd hcd_t;
+typedef struct endpoint endpoint_t;
+typedef struct bus bus_t;
+typedef struct ddf_fun ddf_fun_t;
+typedef struct usb_transfer_batch usb_transfer_batch_t;
+
+typedef struct device {
+	/* Device tree keeping */
+	link_t link;
+	list_t devices;
+	fibril_mutex_t guard;
+
+	/* Associated DDF function, if any */
+	ddf_fun_t *fun;
+
+	/* Invalid for the roothub device */
+	unsigned port;
+
+	/** Hub under which this device is connected */
+	struct device *hub;
+
+	/** USB Tier of the device */
+	uint8_t tier;
+
+	/* Transaction translator */
+	struct {
+		device_t *dev;
+		unsigned port;
+	} tt;
+
+	/* The following are not set by the library */
+	usb_speed_t speed;
+	usb_address_t address;
+	endpoint_t *endpoints [USB_ENDPOINT_COUNT];
+
+	/* Managing bus */
+	bus_t *bus;
+
+	/** True if the device can add new endpoints and schedule transfers. */
+	volatile bool online;
+
+	/* This structure is meant to be extended by overriding. */
+} device_t;
+
+typedef struct bus_ops bus_ops_t;
+
+/**
+ * Operations structure serving as an interface of hc driver for the library
+ * (and the rest of the system).
+ */
+struct bus_ops {
+	/* Global operations on the bus */
+	void (*interrupt)(bus_t *, uint32_t);
+	int (*status)(bus_t *, uint32_t *);
+
+	/* Operations on device */
+	int (*device_enumerate)(device_t *);
+	void (*device_gone)(device_t *);
+	int (*device_online)(device_t *);			/**< Optional */
+	void (*device_offline)(device_t *);			/**< Optional */
+	endpoint_t *(*endpoint_create)(device_t *, const usb_endpoint_descriptors_t *);
+
+	/* Operations on endpoint */
+	int (*endpoint_register)(endpoint_t *);
+	void (*endpoint_unregister)(endpoint_t *);
+	void (*endpoint_destroy)(endpoint_t *);			/**< Optional */
+	usb_transfer_batch_t *(*batch_create)(endpoint_t *);	/**< Optional */
+
+	/* Operations on batch */
+	int (*batch_schedule)(usb_transfer_batch_t *);
+	void (*batch_destroy)(usb_transfer_batch_t *);		/**< Optional */
+};
+
+/** Endpoint management structure */
+typedef struct bus {
+	/* Synchronization of ops */
+	fibril_mutex_t guard;
+
+	/* Size of the device_t extended structure */
+	size_t device_size;
+
+	/* Do not call directly, ops are synchronized. */
+	const bus_ops_t *ops;
+
+	/* Reserving default address */
+	device_t *default_address_owner;
+	fibril_condvar_t default_address_cv;
+
+	/* This structure is meant to be extended by overriding. */
+} bus_t;
+
+void bus_init(bus_t *, size_t);
+int bus_device_init(device_t *, bus_t *);
+
+int bus_device_set_default_name(device_t *);
+
+int bus_device_enumerate(device_t *);
+void bus_device_gone(device_t *);
+
+int bus_device_online(device_t *);
+int bus_device_offline(device_t *);
+
+/**
+ * A proforma to USB transfer batch. As opposed to transfer batch, which is
+ * supposed to be a dynamic structrure, this one is static and descriptive only.
+ * Its fields are copied to the final batch.
+ */
+typedef struct transfer_request {
+	usb_target_t target;
+	usb_direction_t dir;
+
+	dma_buffer_t buffer;
+	size_t offset, size;
+	uint64_t setup;
+
+	usbhc_iface_transfer_callback_t on_complete;
+	void *arg;
+
+	const char *name;
+} transfer_request_t;
+
+int bus_issue_transfer(device_t *, const transfer_request_t *);
+
+errno_t bus_device_send_batch_sync(device_t *, usb_target_t,
+    usb_direction_t direction, char *, size_t, uint64_t,
+    const char *, size_t *);
+
+int bus_endpoint_add(device_t *, const usb_endpoint_descriptors_t *, endpoint_t **);
+endpoint_t *bus_find_endpoint(device_t *, usb_endpoint_t, usb_direction_t);
+int bus_endpoint_remove(endpoint_t *);
+
+int bus_reserve_default_address(bus_t *, device_t *);
+void bus_release_default_address(bus_t *, device_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/include/usb/host/ddf_helpers.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/ddf_helpers.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/include/usb/host/ddf_helpers.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -37,45 +37,25 @@
 #define LIBUSBHOST_HOST_DDF_HELPERS_H
 
-#include <usb/host/hcd.h>
-#include <usb/host/usb_bus.h>
+#include <ddf/driver.h>
+#include <ddf/interrupt.h>
 #include <usb/usb.h>
 
-#include <ddf/driver.h>
-#include <ddf/interrupt.h>
-#include <device/hw_res_parsed.h>
+#include <usb/host/hcd.h>
+#include <usb/descriptor.h>
 
-typedef errno_t (*driver_init_t)(hcd_t *, const hw_res_list_parsed_t *, bool);
-typedef void (*driver_fini_t)(hcd_t *);
-typedef errno_t (*claim_t)(ddf_dev_t *);
-typedef errno_t (*irq_code_gen_t)(irq_code_t *, const hw_res_list_parsed_t *, int *);
 
-typedef struct {
-	hcd_ops_t ops;
-	claim_t claim;
-	usb_speed_t hc_speed;
-	driver_init_t init;
-	driver_fini_t fini;
-	interrupt_handler_t *irq_handler;
-	irq_code_gen_t irq_code_gen;
-	const char *name;
-} ddf_hc_driver_t;
+errno_t hcd_ddf_setup_hc(ddf_dev_t *, size_t);
+void hcd_ddf_clean_hc(hc_device_t *);
 
-errno_t hcd_ddf_add_hc(ddf_dev_t *device, const ddf_hc_driver_t *driver);
 
-errno_t hcd_ddf_setup_hc(ddf_dev_t *device, usb_speed_t max_speed,
-    size_t bw, bw_count_func_t bw_count);
-void hcd_ddf_clean_hc(ddf_dev_t *device);
-errno_t hcd_ddf_setup_root_hub(ddf_dev_t *device);
+device_t *hcd_ddf_fun_create(hc_device_t *, usb_speed_t);
+void hcd_ddf_fun_destroy(device_t *);
 
-hcd_t *dev_to_hcd(ddf_dev_t *dev);
+errno_t hcd_ddf_setup_match_ids(device_t *, usb_standard_device_descriptor_t *);
 
-errno_t hcd_ddf_enable_interrupt(ddf_dev_t *device, int);
-errno_t hcd_ddf_get_registers(ddf_dev_t *device, hw_res_list_parsed_t *hw_res);
-errno_t hcd_ddf_setup_interrupts(ddf_dev_t *device,
-    const hw_res_list_parsed_t *hw_res,
-    interrupt_handler_t handler,
-    errno_t (*gen_irq_code)(irq_code_t *, const hw_res_list_parsed_t *, int *),
-    cap_handle_t *handle);
-void ddf_hcd_gen_irq_handler(ipc_call_t *call, ddf_dev_t *dev);
+errno_t hcd_ddf_enable_interrupt(hc_device_t *hcd, int);
+errno_t hcd_ddf_get_registers(hc_device_t *hcd, hw_res_list_parsed_t *hw_res);
+
+void hcd_ddf_gen_irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev);
 
 #endif
Index: uspace/lib/usbhost/include/usb/host/endpoint.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/endpoint.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/include/usb/host/endpoint.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -32,23 +32,61 @@
 /** @file
  *
+ * Endpoint structure is tightly coupled to the bus. The bus controls the
+ * life-cycle of endpoint. In order to keep endpoints lightweight, operations
+ * on endpoints are part of the bus structure.
+ *
  */
 #ifndef LIBUSBHOST_HOST_ENDPOINT_H
 #define LIBUSBHOST_HOST_ENDPOINT_H
 
+#include <adt/list.h>
+#include <atomic.h>
+#include <fibril_synch.h>
 #include <stdbool.h>
-#include <adt/list.h>
-#include <fibril_synch.h>
+#include <sys/time.h>
 #include <usb/usb.h>
-#include <atomic.h>
+#include <usb/host/bus.h>
+#include <usbhc_iface.h>
 
-/** Host controller side endpoint structure. */
+typedef struct bus bus_t;
+typedef struct device device_t;
+typedef struct transfer_request transfer_request_t;
+typedef struct usb_transfer_batch usb_transfer_batch_t;
+
+/**
+ * Host controller side endpoint structure.
+ *
+ * This structure, though reference-counted, is very fragile. It is responsible
+ * for synchronizing transfer batch scheduling and completion.
+ *
+ * To avoid situations, in which two locks must be obtained to schedule/finish
+ * a transfer, the endpoint inherits a lock from the outside. Because the
+ * concrete instance of mutex can be unknown at the time of initialization,
+ * the HC shall pass the right lock at the time of onlining the endpoint.
+ *
+ * The fields used for scheduling (online, active_batch) are to be used only
+ * under that guard and by functions designed for this purpose. The driver can
+ * also completely avoid using this mechanism, in which case it is on its own in
+ * question of transfer aborting.
+ *
+ * Relevant information can be found in the documentation of HelenOS xHCI
+ * project.
+ */
 typedef struct endpoint {
+	/** USB device */
+	device_t *device;
 	/** Reference count. */
-	atomic_t refcnt;	
-	/** Part of linked list. */
-	link_t link;
-	/** USB address. */
-	usb_address_t address;
-	/** USB endpoint number. */
+	atomic_t refcnt;
+
+	/** An inherited guard */
+	fibril_mutex_t *guard;
+	/** Whether it's allowed to schedule on this endpoint */
+	bool online;
+	/** The currently active transfer batch. */
+	usb_transfer_batch_t *active_batch;
+	/** Signals change of active status. */
+	fibril_condvar_t avail;
+
+	/** Endpoint number */
 	usb_endpoint_t endpoint;
 	/** Communication direction. */
@@ -56,65 +94,43 @@
 	/** USB transfer type. */
 	usb_transfer_type_t transfer_type;
-	/** Communication speed. */
-	usb_speed_t speed;
-	/** Maximum size of data packets. */
+	/** Maximum size of one packet */
 	size_t max_packet_size;
-	/** Additional opportunities per uframe */
-	unsigned packets;
-	/** Necessary bandwidth. */
-	size_t bandwidth;
-	/** Value of the toggle bit. */
-	unsigned toggle:1;
-	/** True if there is a batch using this scheduled for this endpoint. */
-	volatile bool active;
-	/** Protects resources and active status changes. */
-	fibril_mutex_t guard;
-	/** Signals change of active status. */
-	fibril_condvar_t avail;
-	/** High speed TT data */
-	struct {
-		usb_address_t address;
-		unsigned port;
-	} tt;
-	/** Optional device specific data. */
-	struct {
-		/** Device specific data. */
-		void *data;
-		/** Callback to get the value of toggle bit. */
-		int (*toggle_get)(void *);
-		/** Callback to set the value of toggle bit. */
-		void (*toggle_set)(void *, int);
-	} hc_data;
+
+	/** Maximum size of one transfer */
+	size_t max_transfer_size;
+
+	/* Policies for transfer buffers */
+	dma_policy_t transfer_buffer_policy;		/**< A hint for optimal performance. */
+	dma_policy_t required_transfer_buffer_policy;	/**< Enforced by the library. */
+
+	/**
+	 * Number of packets that can be sent in one service interval
+	 * (not necessarily uframe, despite its name)
+	 */
+	unsigned packets_per_uframe;
+
+	/* This structure is meant to be extended by overriding. */
 } endpoint_t;
 
-extern endpoint_t *endpoint_create(usb_address_t, usb_endpoint_t,
-    usb_direction_t, usb_transfer_type_t, usb_speed_t, size_t, unsigned int,
-    size_t, usb_address_t, unsigned int);
-extern void endpoint_destroy(endpoint_t *);
+extern void endpoint_init(endpoint_t *, device_t *, const usb_endpoint_descriptors_t *);
 
 extern void endpoint_add_ref(endpoint_t *);
 extern void endpoint_del_ref(endpoint_t *);
 
-extern void endpoint_set_hc_data(endpoint_t *, void *, int (*)(void *),
-    void (*)(void *, int));
-extern void endpoint_clear_hc_data(endpoint_t *);
+extern void endpoint_set_online(endpoint_t *, fibril_mutex_t *);
+extern void endpoint_set_offline_locked(endpoint_t *);
 
-extern void endpoint_use(endpoint_t *);
-extern void endpoint_release(endpoint_t *);
+extern void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t);
+extern int endpoint_activate_locked(endpoint_t *, usb_transfer_batch_t *);
+extern void endpoint_deactivate_locked(endpoint_t *);
 
-extern int endpoint_toggle_get(endpoint_t *);
-extern void endpoint_toggle_set(endpoint_t *, int);
+int endpoint_send_batch(endpoint_t *, const transfer_request_t *);
 
-/** list_get_instance wrapper.
- *
- * @param item Pointer to link member.
- *
- * @return Pointer to endpoint_t structure.
- *
- */
-static inline endpoint_t * endpoint_get_instance(link_t *item)
+static inline bus_t *endpoint_get_bus(endpoint_t *ep)
 {
-	return item ? list_get_instance(item, endpoint_t, link) : NULL;
+	device_t * const device = ep->device;
+	return device ? device->bus : NULL;
 }
+
 #endif
 
Index: uspace/lib/usbhost/include/usb/host/hcd.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/hcd.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/include/usb/host/hcd.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -37,92 +37,76 @@
 #define LIBUSBHOST_HOST_HCD_H
 
-#include <usb/host/endpoint.h>
-#include <usb/host/usb_bus.h>
-#include <usb/host/usb_transfer_batch.h>
-#include <usb/usb.h>
+#include <ddf/driver.h>
+#include <usb/request.h>
 
-#include <assert.h>
-#include <usbhc_iface.h>
-#include <stddef.h>
-#include <stdint.h>
+typedef struct hw_resource_list_parsed hw_res_list_parsed_t;
+typedef struct bus bus_t;
+typedef struct device device_t;
 
-typedef struct hcd hcd_t;
+/* Treat this header as read-only in driver code.
+ * It could be opaque, but why to complicate matters.
+ */
+typedef struct hc_device {
+	/* Bus instance */
+	bus_t *bus;
 
-typedef errno_t (*schedule_hook_t)(hcd_t *, usb_transfer_batch_t *);
-typedef errno_t (*ep_add_hook_t)(hcd_t *, endpoint_t *);
-typedef void (*ep_remove_hook_t)(hcd_t *, endpoint_t *);
-typedef void (*interrupt_hook_t)(hcd_t *, uint32_t);
-typedef errno_t (*status_hook_t)(hcd_t *, uint32_t *);
+	/* Managed DDF device */
+	ddf_dev_t *ddf_dev;
 
-typedef struct {
-	/** Transfer scheduling, implement in device driver. */
-	schedule_hook_t schedule;
-	/** Hook called upon registering new endpoint. */
-	ep_add_hook_t ep_add_hook;
-	/** Hook called upon removing of an endpoint. */
-	ep_remove_hook_t ep_remove_hook;
-	/** Hook to be called on device interrupt, passes ARG1 */
-	interrupt_hook_t irq_hook;
-	/** Periodic polling hook */
-	status_hook_t status_hook;
-} hcd_ops_t;
+	/* Control function */
+	ddf_fun_t *ctl_fun;
 
-/** Generic host controller driver structure. */
-struct hcd {
-	/** Endpoint manager. */
-	usb_bus_t bus;
+	/* Result of enabling HW IRQs */
+	int irq_cap;
 
 	/** Interrupt replacement fibril */
 	fid_t polling_fibril;
 
-	/** Driver implementation */
-	hcd_ops_t ops;
-	/** Device specific driver data. */
-	void * driver_data;
-};
+	/* This structure is meant to be extended by driver code. */
+} hc_device_t;
 
-extern void hcd_init(hcd_t *, usb_speed_t, size_t, bw_count_func_t);
+typedef struct hc_driver {
+	const char *name;
 
-static inline void hcd_set_implementation(hcd_t *hcd, void *data,
-    const hcd_ops_t *ops)
-{
-	assert(hcd);
-	if (ops) {
-		hcd->driver_data = data;
-		hcd->ops = *ops;
-	} else {
-		memset(&hcd->ops, 0, sizeof(hcd->ops));
-	}
+	/** Size of the device data to be allocated, and passed as the
+	 * hc_device_t. */
+	size_t hc_device_size;
+
+	/** Initialize device structures. */
+	int (*hc_add)(hc_device_t *, const hw_res_list_parsed_t *);
+
+	/** Generate IRQ code to handle interrupts. */
+	int (*irq_code_gen)(irq_code_t *, hc_device_t *, const hw_res_list_parsed_t *, int *);
+
+	/** Claim device from BIOS. */
+	int (*claim)(hc_device_t *);
+
+	/** Start the host controller. */
+	int (*start)(hc_device_t *);
+
+	/** Setup the virtual roothub. */
+	int (*setup_root_hub)(hc_device_t *);
+
+	/** Stop the host controller (after start has been called) */
+	int (*stop)(hc_device_t *);
+
+	/** HC was asked to be removed (after hc_add has been called) */
+	int (*hc_remove)(hc_device_t *);
+
+	/** HC is gone. */
+	int (*hc_gone)(hc_device_t *);
+} hc_driver_t;
+
+/* Drivers should call this before leaving hc_add */
+static inline void hc_device_setup(hc_device_t *hcd, bus_t *bus) {
+	hcd->bus = bus;
 }
 
-static inline void * hcd_get_driver_data(hcd_t *hcd)
+static inline hc_device_t *dev_to_hcd(ddf_dev_t *dev)
 {
-	assert(hcd);
-	return hcd->driver_data;
+	return ddf_dev_data_get(dev);
 }
 
-extern errno_t hcd_request_address(hcd_t *, usb_speed_t, usb_address_t *);
-
-extern errno_t hcd_release_address(hcd_t *, usb_address_t);
-
-extern errno_t hcd_reserve_default_address(hcd_t *, usb_speed_t);
-
-static inline errno_t hcd_release_default_address(hcd_t *hcd)
-{
-	return hcd_release_address(hcd, USB_ADDRESS_DEFAULT);
-}
-
-extern errno_t hcd_add_ep(hcd_t *, usb_target_t, usb_direction_t,
-    usb_transfer_type_t, size_t, unsigned int, size_t, usb_address_t,
-    unsigned int);
-
-extern errno_t hcd_remove_ep(hcd_t *, usb_target_t, usb_direction_t);
-
-extern errno_t hcd_send_batch(hcd_t *, usb_target_t, usb_direction_t, void *,
-    size_t, uint64_t, usbhc_iface_transfer_in_callback_t,
-    usbhc_iface_transfer_out_callback_t, void *, const char *);
-
-extern errno_t hcd_send_batch_sync(hcd_t *, usb_target_t, usb_direction_t,
-    void *, size_t, uint64_t, const char *, size_t *);
+extern errno_t hc_driver_main(const hc_driver_t *);
 
 #endif
Index: uspace/lib/usbhost/include/usb/host/usb2_bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb2_bus.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/include/usb/host/usb2_bus.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 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
+ *
+ * Bus implementation common for OHCI, UHCI and EHCI.
+ */
+#ifndef LIBUSBHOST_HOST_USB2_BUS_H
+#define LIBUSBHOST_HOST_USB2_BUS_H
+
+#include <adt/list.h>
+#include <stdbool.h>
+#include <usb/usb.h>
+
+#include <usb/host/bus.h>
+#include <usb/host/bandwidth.h>
+
+typedef struct usb2_bus usb2_bus_t;
+typedef struct endpoint endpoint_t;
+
+/** Endpoint and bandwidth management structure */
+typedef struct usb2_bus_helper {
+	/** Map of occupied addresses */
+	bool address_occupied [USB_ADDRESS_COUNT];
+	/** The last reserved address */
+	usb_address_t last_address;
+
+	/** Size of the bandwidth pool */
+	size_t free_bw;
+
+	/* Configured bandwidth accounting */
+	const bandwidth_accounting_t *bw_accounting;
+} usb2_bus_helper_t;
+
+extern void usb2_bus_helper_init(usb2_bus_helper_t *, const bandwidth_accounting_t *);
+
+extern int usb2_bus_device_enumerate(usb2_bus_helper_t *, device_t *);
+extern void usb2_bus_device_gone(usb2_bus_helper_t *, device_t *);
+
+extern int usb2_bus_endpoint_register(usb2_bus_helper_t *, endpoint_t *);
+extern void usb2_bus_endpoint_unregister(usb2_bus_helper_t *, endpoint_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/include/usb/host/usb_bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_bus.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ 	(revision )
@@ -1,120 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * 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
- * Device keeper structure and functions.
- *
- * Typical USB host controller needs to keep track of various settings for
- * each device that is connected to it.
- * State of toggle bit, device speed etc. etc.
- * This structure shall simplify the management.
- */
-#ifndef LIBUSBHOST_HOST_USB_ENDPOINT_MANAGER_H
-#define LIBUSBHOST_HOST_USB_ENDPOINT_MANAGER_H
-
-#include <usb/host/endpoint.h>
-#include <usb/usb.h>
-
-#include <adt/list.h>
-#include <fibril_synch.h>
-#include <stdbool.h>
-
-
-/** Bytes per second in FULL SPEED */
-#define BANDWIDTH_TOTAL_USB11 (12000000 / 8)
-/** 90% of total bandwidth is available for periodic transfers */
-#define BANDWIDTH_AVAILABLE_USB11 ((BANDWIDTH_TOTAL_USB11 / 10) * 9)
-
-//TODO: Implement
-#define BANDWIDTH_AVAILABLE_USB20  1
-
-typedef size_t (*bw_count_func_t)(usb_speed_t, usb_transfer_type_t, size_t, size_t);
-typedef void (*ep_remove_callback_t)(endpoint_t *, void *);
-typedef errno_t (*ep_add_callback_t)(endpoint_t *, void *);
-
-/** Endpoint management structure */
-typedef struct usb_bus {
-	struct {
-		usb_speed_t speed;      /**< Device speed */
-		bool occupied;          /**< The address is in use. */
-		list_t endpoint_list;   /**< Store endpoint_t instances */
-	} devices[USB_ADDRESS_COUNT];
-	/** Prevents races accessing lists */
-	fibril_mutex_t guard;
-	/** Size of the bandwidth pool */
-	size_t free_bw;
-	/** Use this function to count bw required by EP */
-	bw_count_func_t bw_count;
-	/** Maximum speed allowed. */
-	usb_speed_t max_speed;
-	/** The last reserved address */
-	usb_address_t last_address;
-} usb_bus_t;
-
-
-size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size);
-size_t bandwidth_count_usb20(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size);
-
-errno_t usb_bus_init(usb_bus_t *instance,
-    size_t available_bandwidth, bw_count_func_t bw_count, usb_speed_t max_speed);
-
-errno_t usb_bus_register_ep(usb_bus_t *instance, endpoint_t *ep, size_t data_size);
-
-errno_t usb_bus_unregister_ep(usb_bus_t *instance, endpoint_t *ep);
-
-endpoint_t *usb_bus_find_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t ep, usb_direction_t direction);
-
-errno_t usb_bus_add_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
-    size_t data_size, ep_add_callback_t callback, void *arg,
-    usb_address_t tt_address, unsigned tt_port);
-
-errno_t usb_bus_remove_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    ep_remove_callback_t callback, void *arg);
-
-errno_t usb_bus_reset_toggle(usb_bus_t *instance, usb_target_t target, bool all);
-
-errno_t usb_bus_remove_address(usb_bus_t *instance,
-    usb_address_t address, ep_remove_callback_t callback, void *arg);
-
-errno_t usb_bus_request_address(usb_bus_t *instance,
-    usb_address_t *address, bool strict, usb_speed_t speed);
-
-errno_t usb_bus_get_speed(usb_bus_t *instance,
-    usb_address_t address, usb_speed_t *speed);
-#endif
-/**
- * @}
- */
Index: uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -37,50 +37,64 @@
 #define LIBUSBHOST_HOST_USB_TRANSFER_BATCH_H
 
-#include <usb/host/endpoint.h>
-#include <usb/usb.h>
-
-#include <assert.h>
+#include <atomic.h>
+#include <errno.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <usb/dma_buffer.h>
+#include <usb/request.h>
+#include <usb/usb.h>
 #include <usbhc_iface.h>
 
-#define USB_SETUP_PACKET_SIZE 8
+#include <usb/host/hcd.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/bus.h>
+
+typedef struct endpoint endpoint_t;
+typedef struct bus bus_t;
 
 /** Structure stores additional data needed for communication with EP */
 typedef struct usb_transfer_batch {
+	/** Target for communication */
+	usb_target_t target;
+	/** Direction of the transfer */
+	usb_direction_t dir;
+
 	/** Endpoint used for communication */
 	endpoint_t *ep;
-	/** Function called on completion (IN version) */
-	usbhc_iface_transfer_in_callback_t callback_in;
-	/** Function called on completion (OUT version) */
-	usbhc_iface_transfer_out_callback_t callback_out;
-	/** Argument to pass to the completion function */
-	void *arg;
-	/** Place for data to send/receive */
-	char *buffer;
-	/** Size of memory pointed to by buffer member */
-	size_t buffer_size;
+
 	/** Place to store SETUP data needed by control transfers */
-	char setup_buffer[USB_SETUP_PACKET_SIZE];
-	/** Used portion of setup_buffer member
-	 *
-	 * SETUP buffer must be 8 bytes for control transfers and is left
-	 * unused for all other transfers. Thus, this field is either 0 or 8.
+	union {
+		char buffer [USB_SETUP_PACKET_SIZE];
+		usb_device_request_setup_packet_t packet;
+		uint64_t packed;
+	} setup;
+
+	/** DMA buffer with enforced policy */
+	dma_buffer_t dma_buffer;
+	/** Size of memory buffer */
+	size_t offset, size;
+
+	/**
+	 * In case a bounce buffer is allocated, the original buffer must to be
+	 * stored to be filled after the IN transaction is finished.
 	 */
-	size_t setup_size;
+	char *original_buffer;
+	bool is_bounced;
 
-	/** Actually used portion of the buffer
-	 * This member is never accessed by functions provided in this header,
-	 * with the exception of usb_transfer_batch_finish. For external use.
-	 */
-	size_t transfered_size;
-	/** Indicates success/failure of the communication
-	 * This member is never accessed by functions provided in this header,
-	 * with the exception of usb_transfer_batch_finish. For external use.
-	 */
+	/** Indicates success/failure of the communication */
 	errno_t error;
+	/** Actually used portion of the buffer */
+	size_t transferred_size;
+
+	/** Function called on completion */
+	usbhc_iface_transfer_callback_t on_complete;
+	/** Arbitrary data for the handler */
+	void *on_complete_data;
 } usb_transfer_batch_t;
 
-/** Printf formatting string for dumping usb_transfer_batch_t. */
+/**
+ * Printf formatting string for dumping usb_transfer_batch_t.
+ *  [address:endpoint speed transfer_type-direction buffer_sizeB/max_packet_size]
+ * */
 #define USB_TRANSFER_BATCH_FMT "[%d:%d %s %s-%s %zuB/%zu]"
 
@@ -89,62 +103,27 @@
  */
 #define USB_TRANSFER_BATCH_ARGS(batch) \
-	(batch).ep->address, (batch).ep->endpoint, \
-	usb_str_speed((batch).ep->speed), \
+	((batch).ep->device->address), ((batch).ep->endpoint), \
+	usb_str_speed((batch).ep->device->speed), \
 	usb_str_transfer_type_short((batch).ep->transfer_type), \
-	usb_str_direction((batch).ep->direction), \
-	(batch).buffer_size, (batch).ep->max_packet_size
+	usb_str_direction((batch).dir), \
+	(batch).size, (batch).ep->max_packet_size
 
+/** Wrapper for bus operation. */
+usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *);
 
-usb_transfer_batch_t * usb_transfer_batch_create(
-    endpoint_t *ep,
-    char *buffer,
-    size_t buffer_size,
-    uint64_t setup_buffer,
-    usbhc_iface_transfer_in_callback_t func_in,
-    usbhc_iface_transfer_out_callback_t func_out,
-    void *arg
-);
-void usb_transfer_batch_destroy(usb_transfer_batch_t *instance);
+/** Batch initializer. */
+void usb_transfer_batch_init(usb_transfer_batch_t *, endpoint_t *);
 
-void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
-    const void* data, size_t size, errno_t error);
+/** Buffer handling */
+bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *);
+errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *);
 
-/** Finish batch using stored error value and transferred size.
- *
- * @param[in] instance Batch structure to use.
- * @param[in] data Data to copy to the output buffer.
+/** Batch finalization. */
+void usb_transfer_batch_finish(usb_transfer_batch_t *);
+
+/** To be called from outside only when the transfer is not going to be finished
+ * (i.o.w. until successfuly scheduling)
  */
-static inline void usb_transfer_batch_finish(
-    const usb_transfer_batch_t *instance, const void* data)
-{
-	assert(instance);
-	usb_transfer_batch_finish_error(
-	    instance, data, instance->transfered_size, instance->error);
-}
-
-/** Determine batch direction based on the callbacks present
- * @param[in] instance Batch structure to use, non-null.
- * @return USB_DIRECTION_IN, or USB_DIRECTION_OUT.
- */
-static inline usb_direction_t usb_transfer_batch_direction(
-    const usb_transfer_batch_t *instance)
-{
-	assert(instance);
-	if (instance->callback_in) {
-		assert(instance->callback_out == NULL);
-		assert(instance->ep == NULL
-		    || instance->ep->transfer_type == USB_TRANSFER_CONTROL
-		    || instance->ep->direction == USB_DIRECTION_IN);
-		return USB_DIRECTION_IN;
-	}
-	if (instance->callback_out) {
-		assert(instance->callback_in == NULL);
-		assert(instance->ep == NULL
-		    || instance->ep->transfer_type == USB_TRANSFER_CONTROL
-		    || instance->ep->direction == USB_DIRECTION_OUT);
-		return USB_DIRECTION_OUT;
-	}
-	assert(false);
-}
+void usb_transfer_batch_destroy(usb_transfer_batch_t *);
 
 #endif
Index: uspace/lib/usbhost/include/usb/host/utility.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/utility.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/include/usb/host/utility.h	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,71 @@
+/*
+ * 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 drvusbehci
+ * @{
+ */
+/** @file
+ * @brief USB host controller library: Utility functions.
+ *
+ * A mix of various functions that makes life of USB HC driver developers
+ * easier.
+ */
+#ifndef LIB_USBHOST_UTILITY
+#define LIB_USBHOST_UTILITY
+
+#include <usb/host/bus.h>
+#include <usb/host/usb_transfer_batch.h>
+#include <usb/descriptor.h>
+#include <usb/classes/hub.h>
+#include <usb/request.h>
+
+typedef void (*endpoint_reset_toggle_t)(endpoint_t *);
+
+uint16_t hc_get_ep0_initial_mps(usb_speed_t);
+int hc_get_ep0_max_packet_size(uint16_t *, device_t *);
+void hc_reset_toggles(const usb_transfer_batch_t *batch, endpoint_reset_toggle_t);
+int hc_setup_virtual_root_hub(hc_device_t *, usb_speed_t);
+int hc_get_device_desc(device_t *, usb_standard_device_descriptor_t *);
+int hc_get_hub_desc(device_t *, usb_hub_descriptor_header_t *);
+int hc_device_explore(device_t *);
+
+/** Joinable fibril */
+
+typedef int (*fibril_worker_t)(void *);
+typedef struct joinable_fibril joinable_fibril_t;
+
+joinable_fibril_t *joinable_fibril_create(fibril_worker_t, void *);
+void joinable_fibril_start(joinable_fibril_t *);
+void joinable_fibril_join(joinable_fibril_t *);
+void joinable_fibril_destroy(joinable_fibril_t *);
+errno_t joinable_fibril_recreate(joinable_fibril_t *);
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/src/bandwidth.c
===================================================================
--- uspace/lib/usbhost/src/bandwidth.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/src/bandwidth.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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
+ *
+ * Bandwidth calculation functions. Shared among uhci, ohci and ehci drivers.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "endpoint.h"
+#include "bus.h"
+
+#include "bandwidth.h"
+
+/** Bytes per second in FULL SPEED */
+#define BANDWIDTH_TOTAL_USB11 (12000000 / 8)
+/** 90% of total bandwidth is available for periodic transfers */
+#define BANDWIDTH_AVAILABLE_USB11 ((BANDWIDTH_TOTAL_USB11 * 9) / 10)
+
+/**
+ * Calculate bandwidth that needs to be reserved for communication with EP.
+ * Calculation follows USB 1.1 specification.
+ *
+ * @param ep An endpoint for which the bandwidth is to be counted
+ */
+static size_t bandwidth_count_usb11(endpoint_t *ep)
+{
+	assert(ep);
+	assert(ep->device);
+
+	const usb_transfer_type_t type = ep->transfer_type;
+
+	/* We care about bandwidth only for interrupt and isochronous. */
+	if ((type != USB_TRANSFER_INTERRUPT)
+	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
+		return 0;
+	}
+
+	const size_t max_packet_size = ep->max_packet_size;
+	const size_t packet_count = ep->packets_per_uframe;
+
+	/* TODO: It may be that ISO and INT transfers use only one packet per
+	 * transaction, but I did not find text in USB spec to confirm this */
+	/* NOTE: All data packets will be considered to be max_packet_size */
+	switch (ep->device->speed)
+	{
+	case USB_SPEED_LOW:
+		assert(type == USB_TRANSFER_INTERRUPT);
+		/* Protocol overhead 13B
+		 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
+		 * CRC bytes, and a 3-byte interpacket delay)
+		 * see USB spec page 45-46. */
+		/* Speed penalty 8: low speed is 8-times slower*/
+		return packet_count * (13 + max_packet_size) * 8;
+	case USB_SPEED_FULL:
+		/* Interrupt transfer overhead see above
+		 * or page 45 of USB spec */
+		if (type == USB_TRANSFER_INTERRUPT)
+			return packet_count * (13 + max_packet_size);
+
+		assert(type == USB_TRANSFER_ISOCHRONOUS);
+		/* Protocol overhead 9B
+		 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
+		 * bytes, and a 1-byte interpacket delay)
+		 * see USB spec page 42 */
+		return packet_count * (9 + max_packet_size);
+	default:
+		return 0;
+	}
+}
+
+const bandwidth_accounting_t bandwidth_accounting_usb11 = {
+	.available_bandwidth = BANDWIDTH_AVAILABLE_USB11,
+	.count_bw = &bandwidth_count_usb11,
+};
+
+/** Number of nanoseconds in one microframe */
+#define BANDWIDTH_TOTAL_USB2 (125000)
+/** 90% of total bandwidth is available for periodic transfers */
+#define BANDWIDTH_AVAILABLE_USB2  ((BANDWIDTH_TOTAL_USB2 * 9) / 10)
+
+/**
+ * Calculate bandwidth that needs to be reserved for communication with EP.
+ * Calculation follows USB 2.0 specification, chapter 5.11.3.
+ *
+ * FIXME: Interrupt transfers shall be probably divided by their polling interval.
+ *
+ * @param ep An endpoint for which the bandwidth is to be counted
+ * @return Number of nanoseconds transaction with @c size bytes payload will
+ *         take.
+ */
+static size_t bandwidth_count_usb2(endpoint_t *ep)
+{
+	assert(ep);
+	assert(ep->device);
+
+	const usb_transfer_type_t type = ep->transfer_type;
+
+	/* We care about bandwidth only for interrupt and isochronous. */
+	if ((type != USB_TRANSFER_INTERRUPT)
+	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
+		return 0;
+	}
+
+	// FIXME: Come up with some upper bound for these (in ns).
+	const size_t host_delay = 0;
+	const size_t hub_ls_setup = 0;
+
+	// Approx. Floor(3.167 + BitStuffTime(Data_bc))
+	const size_t base_time = (ep->max_transfer_size * 8 + 19) / 6;
+
+	switch (ep->device->speed) {
+	case USB_SPEED_LOW:
+		if (ep->direction == USB_DIRECTION_IN)
+			return 64060 + (2 * hub_ls_setup) + (677 * base_time) + host_delay;
+		else
+			return 64107 + (2 * hub_ls_setup) + (667 * base_time) + host_delay;
+
+	case USB_SPEED_FULL:
+		if (ep->transfer_type == USB_TRANSFER_INTERRUPT)
+			return 9107 + 84 * base_time + host_delay;
+
+		if (ep->direction == USB_DIRECTION_IN)
+			return 7268 + 84 * base_time + host_delay;
+		else
+			return 6265 + 84 * base_time + host_delay;
+
+	case USB_SPEED_HIGH:
+		if (ep->transfer_type == USB_TRANSFER_INTERRUPT)
+			return (3648 + 25 * base_time + 11) / 12 + host_delay;
+		else
+			return (5280 + 25 * base_time + 11) / 12 + host_delay;
+
+	default:
+		return 0;
+	}
+}
+
+const bandwidth_accounting_t bandwidth_accounting_usb2 = {
+	.available_bandwidth = BANDWIDTH_AVAILABLE_USB2,
+	.count_bw = &bandwidth_count_usb2,
+};
Index: uspace/lib/usbhost/src/bus.c
===================================================================
--- uspace/lib/usbhost/src/bus.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/src/bus.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,697 @@
+/*
+ * 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
+ *
+ * The Bus is a structure that serves as an interface of the HC driver
+ * implementation for the usbhost library. Every HC driver that uses libusbhost
+ * must use a bus_t (or its child), fill it with bus_ops and present it to the
+ * library. The library then handles the DDF interface and translates it to the
+ * bus callbacks.
+ */
+
+#include <ddf/driver.h>
+#include <errno.h>
+#include <mem.h>
+#include <macros.h>
+#include <stdio.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/dma_buffer.h>
+
+#include "endpoint.h"
+#include "bus.h"
+
+/**
+ * Initializes the base bus structure.
+ */
+void bus_init(bus_t *bus, size_t device_size)
+{
+	assert(bus);
+	assert(device_size >= sizeof(device_t));
+	memset(bus, 0, sizeof(bus_t));
+
+	fibril_mutex_initialize(&bus->guard);
+	bus->device_size = device_size;
+}
+
+/**
+ * Initialize the device_t structure belonging to a bus.
+ */
+int bus_device_init(device_t *dev, bus_t *bus)
+{
+	assert(bus);
+
+	memset(dev, 0, sizeof(*dev));
+
+	dev->bus = bus;
+
+	link_initialize(&dev->link);
+	list_initialize(&dev->devices);
+	fibril_mutex_initialize(&dev->guard);
+
+	return EOK;
+}
+
+/**
+ * Create a name of the ddf function node.
+ */
+int bus_device_set_default_name(device_t *dev)
+{
+	assert(dev);
+	assert(dev->fun);
+
+	char buf[10] = { 0 }; /* usbxyz-ss */
+	snprintf(buf, sizeof(buf), "usb%u-%cs",
+	    dev->address, usb_str_speed(dev->speed)[0]);
+
+	return ddf_fun_set_name(dev->fun, buf);
+}
+
+/**
+ * Setup devices Transaction Translation.
+ *
+ * This applies for Low/Full speed devices under High speed hub only. Other
+ * devices just inherit TT from the hub.
+ *
+ * Roothub must be handled specially.
+ */
+static void device_setup_tt(device_t *dev)
+{
+	if (!dev->hub)
+		return;
+
+	if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
+		/* For LS devices under HS hub */
+		dev->tt.dev = dev->hub;
+		dev->tt.port = dev->port;
+	}
+	else {
+		/* Inherit hub's TT */
+		dev->tt = dev->hub->tt;
+	}
+}
+
+/**
+ * Invoke the device_enumerate bus operation.
+ *
+ * There's no need to synchronize here, because no one knows the device yet.
+ */
+int bus_device_enumerate(device_t *dev)
+{
+	assert(dev);
+
+	if (!dev->bus->ops->device_enumerate)
+		return ENOTSUP;
+
+	if (dev->online)
+		return EINVAL;
+
+	device_setup_tt(dev);
+
+	const int r = dev->bus->ops->device_enumerate(dev);
+	if (r)
+		return r;
+
+	dev->online = true;
+
+	if (dev->hub) {
+		fibril_mutex_lock(&dev->hub->guard);
+		list_append(&dev->link, &dev->hub->devices);
+		fibril_mutex_unlock(&dev->hub->guard);
+	}
+
+	return EOK;
+}
+
+/**
+ * Clean endpoints and children that could have been left behind after
+ * asking the driver of device to offline/remove a device.
+ *
+ * Note that EP0's lifetime is shared with the device, and as such is not
+ * touched.
+ */
+static void device_clean_ep_children(device_t *dev, const char *op)
+{
+	assert(fibril_mutex_is_locked(&dev->guard));
+
+	/* Unregister endpoints left behind. */
+	for (usb_endpoint_t i = 1; i < USB_ENDPOINT_MAX; ++i) {
+		if (!dev->endpoints[i])
+			continue;
+
+		usb_log_warning("USB device '%s' driver left endpoint %u registered after %s.",
+		    ddf_fun_get_name(dev->fun), i, op);
+
+		endpoint_t * const ep = dev->endpoints[i];
+		endpoint_add_ref(ep);
+
+		fibril_mutex_unlock(&dev->guard);
+		const int err = bus_endpoint_remove(ep);
+		if (err)
+			usb_log_warning("Endpoint %u cannot be removed. "
+			    "Some deffered cleanup was faster?", ep->endpoint);
+
+		endpoint_del_ref(ep);
+		fibril_mutex_lock(&dev->guard);
+	}
+
+	for (usb_endpoint_t i = 1; i < USB_ENDPOINT_MAX; ++i)
+		assert(dev->endpoints[i] == NULL);
+
+	/* Remove also orphaned children. */
+	while (!list_empty(&dev->devices)) {
+		device_t * const child = list_get_instance(list_first(&dev->devices), device_t, link);
+
+		/*
+		 * This is not an error condition, as devices cannot remove
+		 * their children devices while they are removed, because for
+		 * DDF, they are siblings.
+		 */
+		usb_log_debug("USB device '%s' driver left device '%s' behind after %s.",
+		    ddf_fun_get_name(dev->fun), ddf_fun_get_name(child->fun), op);
+
+		/*
+		 * The child node won't disappear, because its parent's driver
+		 * is already dead. And the child will need the guard to remove
+		 * itself from the list.
+		 */
+		fibril_mutex_unlock(&dev->guard);
+		bus_device_gone(child);
+		fibril_mutex_lock(&dev->guard);
+	}
+	assert(list_empty(&dev->devices));
+}
+
+/**
+ * Resolve a USB device that is gone.
+ */
+void bus_device_gone(device_t *dev)
+{
+	assert(dev);
+	assert(dev->fun != NULL);
+
+	const bus_ops_t *ops = dev->bus->ops;
+
+	/* First, block new transfers and operations. */
+	fibril_mutex_lock(&dev->guard);
+	dev->online = false;
+
+	/* Unbinding will need guard unlocked. */
+	fibril_mutex_unlock(&dev->guard);
+
+	/* Remove our device from our hub's children. */
+	if (dev->hub) {
+		fibril_mutex_lock(&dev->hub->guard);
+		list_remove(&dev->link);
+		fibril_mutex_unlock(&dev->hub->guard);
+	}
+
+	/*
+	 * Unbind the DDF function. That will result in dev_gone called in
+	 * driver, which shall destroy its pipes and remove its children.
+	 */
+	const int err = ddf_fun_unbind(dev->fun);
+	if (err) {
+		usb_log_error("Failed to unbind USB device '%s': %s",
+		    ddf_fun_get_name(dev->fun), str_error(err));
+		return;
+	}
+
+	/* Remove what driver left behind */
+	fibril_mutex_lock(&dev->guard);
+	device_clean_ep_children(dev, "removing");
+
+	/* Tell the HC to release its resources. */
+	if (ops->device_gone)
+		ops->device_gone(dev);
+
+	/* Check whether the driver didn't forgot EP0 */
+	if (dev->endpoints[0]) {
+		if (ops->endpoint_unregister)
+			ops->endpoint_unregister(dev->endpoints[0]);
+		/* Release the EP0 bus reference */
+		endpoint_del_ref(dev->endpoints[0]);
+	}
+
+	/* Destroy the function, freeing also the device, unlocking mutex. */
+	ddf_fun_destroy(dev->fun);
+}
+
+/**
+ * The user wants this device back online.
+ */
+int bus_device_online(device_t *dev)
+{
+	int rc;
+	assert(dev);
+
+	fibril_mutex_lock(&dev->guard);
+	if (dev->online) {
+		rc = EINVAL;
+		goto err_lock;
+	}
+
+	/* First, tell the HC driver. */
+	const bus_ops_t *ops = dev->bus->ops;
+	if (ops->device_online && (rc = ops->device_online(dev))) {
+		usb_log_warning("Host controller failed to make device '%s' online: %s",
+		    ddf_fun_get_name(dev->fun), str_error(rc));
+		goto err_lock;
+	}
+
+	/* Allow creation of new endpoints and communication with the device. */
+	dev->online = true;
+
+	/* Onlining will need the guard */
+	fibril_mutex_unlock(&dev->guard);
+
+	if ((rc = ddf_fun_online(dev->fun))) {
+		usb_log_warning("Failed to take device '%s' online: %s",
+		    ddf_fun_get_name(dev->fun), str_error(rc));
+		goto err;
+	}
+
+	usb_log_info("USB Device '%s' is now online.", ddf_fun_get_name(dev->fun));
+	return EOK;
+
+err_lock:
+	fibril_mutex_unlock(&dev->guard);
+err:
+	return rc;
+}
+
+/**
+ * The user requested to take this device offline.
+ */
+int bus_device_offline(device_t *dev)
+{
+	int rc;
+	assert(dev);
+
+	/* Make sure we're the one who offlines this device */
+	if (!dev->online) {
+		rc = ENOENT;
+		goto err;
+	}
+
+	/*
+	 * XXX: If the device is removed/offlined just now, this can fail on
+	 * assertion. We most probably need some kind of enum status field to
+	 * make the synchronization work.
+	 */
+	
+	/* Tear down all drivers working with the device. */
+	if ((rc = ddf_fun_offline(dev->fun))) {
+		goto err;
+	}
+
+	fibril_mutex_lock(&dev->guard);
+	dev->online = false;
+	device_clean_ep_children(dev, "offlining");
+
+	/* Tell also the HC driver. */
+	const bus_ops_t *ops = dev->bus->ops;
+	if (ops->device_offline)
+		ops->device_offline(dev);
+
+	fibril_mutex_unlock(&dev->guard);
+	usb_log_info("USB Device '%s' is now offline.", ddf_fun_get_name(dev->fun));
+	return EOK;
+
+err:
+	return rc;
+}
+
+/**
+ * Calculate an index to the endpoint array.
+ * For the default control endpoint 0, it must return 0.
+ * For different arguments, the result is stable but not defined.
+ */
+static size_t bus_endpoint_index(usb_endpoint_t ep, usb_direction_t dir)
+{
+	return 2 * ep + (dir == USB_DIRECTION_OUT);
+}
+
+/**
+ * Create and register new endpoint to the bus.
+ *
+ * @param[in] device The device of which the endpoint shall be created
+ * @param[in] desc Endpoint descriptors as reported by the device
+ * @param[out] out_ep The resulting new endpoint reference, if any. Can be NULL.
+ */
+int bus_endpoint_add(device_t *device, const usb_endpoint_descriptors_t *desc, endpoint_t **out_ep)
+{
+	int err;
+	assert(device);
+
+	bus_t *bus = device->bus;
+
+	if (!bus->ops->endpoint_register)
+		return ENOTSUP;
+
+	endpoint_t *ep;
+	if (bus->ops->endpoint_create) {
+		ep = bus->ops->endpoint_create(device, desc);
+		if (!ep)
+			return ENOMEM;
+	} else {
+		ep = calloc(1, sizeof(endpoint_t));
+		if (!ep)
+			return ENOMEM;
+		endpoint_init(ep, device, desc);
+	}
+
+	assert((ep->required_transfer_buffer_policy & ~ep->transfer_buffer_policy) == 0);
+
+	/* Bus reference */
+	endpoint_add_ref(ep);
+
+	const size_t idx = bus_endpoint_index(ep->endpoint, ep->direction);
+	if (idx >= ARRAY_SIZE(device->endpoints)) {
+		usb_log_warning("Invalid endpoint description (ep no %u out of "
+		    "bounds)", ep->endpoint);
+		goto drop;
+	}
+
+	if (ep->max_transfer_size == 0) {
+		usb_log_warning("Invalid endpoint description (mps %zu, "
+			"%u packets)", ep->max_packet_size, ep->packets_per_uframe);
+		goto drop;
+	}
+
+	usb_log_debug("Register endpoint %d:%d %s-%s %zuB.",
+	    device->address, ep->endpoint,
+	    usb_str_transfer_type(ep->transfer_type),
+	    usb_str_direction(ep->direction),
+	    ep->max_transfer_size);
+
+	fibril_mutex_lock(&device->guard);
+	if (!device->online && ep->endpoint != 0) {
+		err = EAGAIN;
+	} else if (device->endpoints[idx] != NULL) {
+		err = EEXIST;
+	} else {
+		err = bus->ops->endpoint_register(ep);
+		if (!err)
+			device->endpoints[idx] = ep;
+	}
+	fibril_mutex_unlock(&device->guard);
+	if (err) {
+		endpoint_del_ref(ep);
+		return err;
+	}
+
+	if (out_ep) {
+		/* Exporting reference */
+		endpoint_add_ref(ep);
+		*out_ep = ep;
+	}
+
+	return EOK;
+drop:
+	/* Bus reference */
+	endpoint_del_ref(ep);
+	return EINVAL;
+}
+
+/**
+ * Search for an endpoint. Returns a reference.
+ */
+endpoint_t *bus_find_endpoint(device_t *device, usb_endpoint_t endpoint,
+    usb_direction_t dir)
+{
+	assert(device);
+
+	const size_t idx = bus_endpoint_index(endpoint, dir);
+	const size_t ctrl_idx = bus_endpoint_index(endpoint, USB_DIRECTION_BOTH);
+
+	endpoint_t *ep = NULL;
+
+	fibril_mutex_lock(&device->guard);
+	if (idx < ARRAY_SIZE(device->endpoints))
+		ep = device->endpoints[idx];
+	/*
+	 * If the endpoint was not found, it's still possible it is a control
+	 * endpoint having direction BOTH.
+	 */
+	if (!ep && ctrl_idx < ARRAY_SIZE(device->endpoints)) {
+		ep = device->endpoints[ctrl_idx];
+		if (ep && ep->transfer_type != USB_TRANSFER_CONTROL)
+			ep = NULL;
+	}
+	if (ep) {
+		/* Exporting reference */
+		endpoint_add_ref(ep);
+	}
+	fibril_mutex_unlock(&device->guard);
+	return ep;
+}
+
+/**
+ * Remove an endpoint from the device.
+ */
+int bus_endpoint_remove(endpoint_t *ep)
+{
+	assert(ep);
+	assert(ep->device);
+
+	device_t *device = ep->device;
+	if (!device)
+		return ENOENT;
+
+	bus_t *bus = device->bus;
+
+	if (!bus->ops->endpoint_unregister)
+		return ENOTSUP;
+
+	usb_log_debug("Unregister endpoint %d:%d %s-%s %zuB.",
+	    device->address, ep->endpoint,
+	    usb_str_transfer_type(ep->transfer_type),
+	    usb_str_direction(ep->direction),
+	    ep->max_transfer_size);
+
+	const size_t idx = bus_endpoint_index(ep->endpoint, ep->direction);
+
+	if (idx >= ARRAY_SIZE(device->endpoints))
+		return EINVAL;
+
+	fibril_mutex_lock(&device->guard);
+
+	/* Check whether the endpoint is registered */
+	if (device->endpoints[idx] != ep) {
+		fibril_mutex_unlock(&device->guard);
+		return EINVAL;
+	}
+
+	bus->ops->endpoint_unregister(ep);
+	device->endpoints[idx] = NULL;
+	fibril_mutex_unlock(&device->guard);
+
+	/* Bus reference */
+	endpoint_del_ref(ep);
+
+	return EOK;
+}
+
+/**
+ * Reserve the default address on the bus for the specified device (hub).
+ */
+int bus_reserve_default_address(bus_t *bus, device_t *dev)
+{
+	assert(bus);
+
+	int err;
+	fibril_mutex_lock(&bus->guard);
+	if (bus->default_address_owner != NULL) {
+		err = (bus->default_address_owner == dev) ? EINVAL : EAGAIN;
+	} else {
+		bus->default_address_owner = dev;
+		err = EOK;
+	}
+	fibril_mutex_unlock(&bus->guard);
+	return err;
+}
+
+/**
+ * Release the default address.
+ */
+void bus_release_default_address(bus_t *bus, device_t *dev)
+{
+	assert(bus);
+
+	fibril_mutex_lock(&bus->guard);
+	if (bus->default_address_owner != dev) {
+		usb_log_error("Device %d tried to release default address, "
+		    "which is not reserved for it.", dev->address);
+	} else {
+		bus->default_address_owner = NULL;
+	}
+	fibril_mutex_unlock(&bus->guard);
+}
+
+/**
+ * Assert some conditions on transfer request. As the request is an entity of
+ * HC driver only, we can force these conditions harder. Invalid values from
+ * devices shall be caught on DDF interface already.
+ */
+static void check_request(const transfer_request_t *request)
+{
+	assert(usb_target_is_valid(&request->target));
+	assert(request->dir != USB_DIRECTION_BOTH);
+	/* Non-zero offset => size is non-zero */
+	assert(request->offset == 0 || request->size != 0);
+	/* Non-zero size => buffer is set */
+	assert(request->size == 0 || dma_buffer_is_set(&request->buffer));
+	/* Non-null arg => callback is set */
+	assert(request->arg == NULL || request->on_complete != NULL);
+	assert(request->name);
+}
+
+/**
+ * Initiate a transfer with given device.
+ *
+ * @return Error code.
+ */
+int bus_issue_transfer(device_t *device, const transfer_request_t *request)
+{
+	assert(device);
+	assert(request);
+
+	check_request(request);
+	assert(device->address == request->target.address);
+
+	/* Temporary reference */
+	endpoint_t *ep = bus_find_endpoint(device, request->target.endpoint, request->dir);
+	if (ep == NULL) {
+		usb_log_error("Endpoint(%d:%d) not registered for %s.",
+		    device->address, request->target.endpoint, request->name);
+		return ENOENT;
+	}
+
+	assert(ep->device == device);
+
+	const int err = endpoint_send_batch(ep, request);
+
+	/* Temporary reference */
+	endpoint_del_ref(ep);
+
+	return err;
+}
+
+/**
+ * A structure to pass data from the completion callback to the caller.
+ */
+typedef struct {
+	fibril_mutex_t done_mtx;
+	fibril_condvar_t done_cv;
+	bool done;
+
+	size_t transferred_size;
+	int error;
+} sync_data_t;
+
+/**
+ * Callback for finishing the transfer. Wake the issuing thread.
+ */
+static int sync_transfer_complete(void *arg, int error, size_t transferred_size)
+{
+	sync_data_t *d = arg;
+	assert(d);
+	d->transferred_size = transferred_size;
+	d->error = error;
+	fibril_mutex_lock(&d->done_mtx);
+	d->done = true;
+	fibril_condvar_broadcast(&d->done_cv);
+	fibril_mutex_unlock(&d->done_mtx);
+	return EOK;
+}
+
+/**
+ * Issue a transfer on the bus, wait for the result.
+ *
+ * @param device Device for which to send the batch
+ * @param target The target of the transfer.
+ * @param direction A direction of the transfer.
+ * @param data A pointer to the data buffer.
+ * @param size Size of the data buffer.
+ * @param setup_data Data to use in the setup stage (Control communication type)
+ * @param name Communication identifier (for nicer output).
+ */
+errno_t bus_device_send_batch_sync(device_t *device, usb_target_t target,
+    usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
+    const char *name, size_t *transferred_size)
+{
+	int err;
+	sync_data_t sd = { .done = false };
+	fibril_mutex_initialize(&sd.done_mtx);
+	fibril_condvar_initialize(&sd.done_cv);
+
+	transfer_request_t request = {
+		.target = target,
+		.dir = direction,
+		.offset = ((uintptr_t) data) % PAGE_SIZE,
+		.size = size,
+		.setup = setup_data,
+		.on_complete = sync_transfer_complete,
+		.arg = &sd,
+		.name = name,
+	};
+
+	if (data &&
+	    (err = dma_buffer_lock(&request.buffer, data - request.offset, size)))
+		return err;
+
+	if ((err = bus_issue_transfer(device, &request))) {
+		dma_buffer_unlock(&request.buffer, size);
+		return err;
+	}
+
+	/*
+	 * Note: There are requests that are completed synchronously. It is not
+	 *       therefore possible to just lock the mutex before and wait.
+	 */
+	fibril_mutex_lock(&sd.done_mtx);
+	while (!sd.done)
+		fibril_condvar_wait(&sd.done_cv, &sd.done_mtx);
+	fibril_mutex_unlock(&sd.done_mtx);
+
+	dma_buffer_unlock(&request.buffer, size);
+
+	if (transferred_size)
+		*transferred_size = sd.transferred_size;
+
+	return sd.error;
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -31,12 +31,6 @@
  */
 /** @file
- *
- */
-
-#include <usb/classes/classes.h>
-#include <usb/debug.h>
-#include <usb/descriptor.h>
-#include <usb/request.h>
-#include <usb/usb.h>
+ * Helpers to work with the DDF interface.
+ */
 
 #include <adt/list.h>
@@ -47,174 +41,228 @@
 #include <device/hw_res_parsed.h>
 #include <errno.h>
-#include <fibril_synch.h>
-#include <macros.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <str_error.h>
+#include <usb/classes/classes.h>
+#include <usb/debug.h>
+#include <usb/descriptor.h>
+#include <usb/usb.h>
+#include <usb/dma_buffer.h>
 #include <usb_iface.h>
+#include <usbhc_iface.h>
+
+#include "bus.h"
+#include "endpoint.h"
 
 #include "ddf_helpers.h"
 
-#define CTRL_PIPE_MIN_PACKET_SIZE 8
-
-typedef struct usb_dev {
-	link_t link;
-	list_t devices;
-	fibril_mutex_t guard;
-	ddf_fun_t *fun;
-	usb_address_t address;
-	usb_speed_t speed;
-	usb_address_t tt_address;
-	unsigned port;
-} usb_dev_t;
-
-typedef struct hc_dev {
-	ddf_fun_t *ctl_fun;
-	hcd_t hcd;
-	usb_dev_t *root_hub;
-} hc_dev_t;
-
-static hc_dev_t *dev_to_hc_dev(ddf_dev_t *dev)
-{
-	return ddf_dev_data_get(dev);
-}
-
-hcd_t *dev_to_hcd(ddf_dev_t *dev)
-{
-	hc_dev_t *hc_dev = dev_to_hc_dev(dev);
-	if (!hc_dev) {
-		usb_log_error("Invalid HCD device.\n");
-		return NULL;
-	}
-	return &hc_dev->hcd;
-}
-
-
-static errno_t hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
-static errno_t hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
-
-
-/* DDF INTERFACE */
-
-/** Register endpoint interface function.
- * @param fun DDF function.
- * @param address USB address of the device.
- * @param endpoint USB endpoint number to be registered.
- * @param transfer_type Endpoint's transfer type.
- * @param direction USB communication direction the endpoint is capable of.
- * @param max_packet_size Maximu size of packets the endpoint accepts.
- * @param interval Preferred timeout between communication.
+/**
+ * DDF usbhc_iface callback. Passes the endpoint descriptors, fills the pipe
+ * descriptor according to the contents of the endpoint.
+ *
+ * @param[in] fun DDF function of the device in question.
+ * @param[out] pipe_desc The pipe descriptor to be filled.
+ * @param[in] endpoint_desc Endpoint descriptors from the device.
  * @return Error code.
  */
-static errno_t register_endpoint(
-    ddf_fun_t *fun, usb_endpoint_t endpoint,
-    usb_transfer_type_t transfer_type, usb_direction_t direction,
-    size_t max_packet_size, unsigned packets, unsigned interval)
-{
-	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
+static errno_t register_endpoint(ddf_fun_t *fun, usb_pipe_desc_t *pipe_desc,
+     const usb_endpoint_descriptors_t *ep_desc)
+{
+	assert(fun);
+	hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
+	assert(hcd->bus);
 	assert(dev);
-	const size_t size = max_packet_size;
-	const usb_target_t target =
-	    {{.address = dev->address, .endpoint = endpoint}};
-
-	usb_log_debug("Register endpoint %d:%d %s-%s %zuB %ums.\n",
-	    dev->address, endpoint, usb_str_transfer_type(transfer_type),
-	    usb_str_direction(direction), max_packet_size, interval);
-
-	return hcd_add_ep(hcd, target, direction, transfer_type,
-	    max_packet_size, packets, size, dev->tt_address, dev->port);
-}
-
-/** Unregister endpoint interface function.
- * @param fun DDF function.
- * @param address USB address of the endpoint.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction of the enpdoint to unregister.
+
+	endpoint_t *ep;
+	const int err = bus_endpoint_add(dev, ep_desc, &ep);
+	if (err)
+		return err;
+
+	if (pipe_desc) {
+		pipe_desc->endpoint_no = ep->endpoint;
+		pipe_desc->direction = ep->direction;
+		pipe_desc->transfer_type = ep->transfer_type;
+		pipe_desc->max_transfer_size = ep->max_transfer_size;
+		pipe_desc->transfer_buffer_policy = ep->transfer_buffer_policy;
+	}
+	endpoint_del_ref(ep);
+
+	return EOK;
+}
+
+ /**
+  * DDF usbhc_iface callback. Unregister endpoint that makes the other end of
+  * the pipe described.
+  *
+  * @param fun DDF function of the device in question.
+  * @param pipe_desc Pipe description.
+  * @return Error code.
+  */
+static errno_t unregister_endpoint(ddf_fun_t *fun, const usb_pipe_desc_t *pipe_desc)
+{
+	assert(fun);
+	hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(hcd);
+	assert(hcd->bus);
+	assert(dev);
+
+	endpoint_t *ep = bus_find_endpoint(dev, pipe_desc->endpoint_no, pipe_desc->direction);
+	if (!ep)
+		return ENOENT;
+
+	const errno_t err = bus_endpoint_remove(ep);
+
+	endpoint_del_ref(ep);
+	return err;
+}
+
+/**
+ * DDF usbhc_iface callback. Calls the respective bus operation directly.
+ *
+ * @param fun DDF function of the device (hub) requesting the address.
+ */
+static errno_t default_address_reservation(ddf_fun_t *fun, bool reserve)
+{
+	assert(fun);
+	hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(hcd);
+	assert(hcd->bus);
+	assert(dev);
+
+	usb_log_debug("Device %d %s default address", dev->address, reserve ? "requested" : "releasing");
+	if (reserve) {
+		return bus_reserve_default_address(hcd->bus, dev);
+	} else {
+		bus_release_default_address(hcd->bus, dev);
+		return EOK;
+	}
+}
+
+/**
+ * DDF usbhc_iface callback. Calls the bus operation directly.
+ *
+ * @param fun DDF function of the device (hub) requesting the address.
+ * @param speed USB speed of the new device
+ */
+static errno_t device_enumerate(ddf_fun_t *fun, unsigned port, usb_speed_t speed)
+{
+	assert(fun);
+	ddf_dev_t *hc = ddf_fun_get_dev(fun);
+	assert(hc);
+	hc_device_t *hcd = dev_to_hcd(hc);
+	assert(hcd);
+	device_t *hub = ddf_fun_data_get(fun);
+	assert(hub);
+
+	errno_t err;
+
+	if (!usb_speed_is_valid(speed))
+		return EINVAL;
+
+	usb_log_debug("Hub %d reported a new %s speed device on port: %u",
+	    hub->address, usb_str_speed(speed), port);
+
+	device_t *dev = hcd_ddf_fun_create(hcd, speed);
+	if (!dev) {
+		usb_log_error("Failed to create USB device function.");
+		return ENOMEM;
+	}
+
+	dev->hub = hub;
+	dev->tier = hub->tier + 1;
+	dev->port = port;
+	dev->speed = speed;
+
+	if ((err = bus_device_enumerate(dev))) {
+		usb_log_error("Failed to initialize USB dev memory structures.");
+		goto err_usb_dev;
+	}
+
+	/* If the driver didn't name the dev when enumerating,
+	 * do it in some generic way.
+	 */
+	if (!ddf_fun_get_name(dev->fun)) {
+		bus_device_set_default_name(dev);
+	}
+
+	if ((err = ddf_fun_bind(dev->fun))) {
+		usb_log_error("Device(%d): Failed to register: %s.", dev->address, str_error(err));
+		goto err_usb_dev;
+	}
+
+	return EOK;
+
+err_usb_dev:
+	hcd_ddf_fun_destroy(dev);
+	return err;
+}
+
+static errno_t device_remove(ddf_fun_t *fun, unsigned port)
+{
+	assert(fun);
+	device_t *hub = ddf_fun_data_get(fun);
+	assert(hub);
+	usb_log_debug("Hub `%s' reported removal of device on port %u",
+	    ddf_fun_get_name(fun), port);
+
+	device_t *victim = NULL;
+
+	fibril_mutex_lock(&hub->guard);
+	list_foreach(hub->devices, link, device_t, it) {
+		if (it->port == port) {
+			victim = it;
+			break;
+		}
+	}
+	fibril_mutex_unlock(&hub->guard);
+
+	if (!victim) {
+		usb_log_warning("Hub '%s' tried to remove non-existent"
+		    " device.", ddf_fun_get_name(fun));
+		return ENOENT;
+	}
+
+	assert(victim->fun);
+	assert(victim->port == port);
+	assert(victim->hub == hub);
+
+	bus_device_gone(victim);
+	return EOK;
+}
+
+/**
+ * Gets description of the device that is calling.
+ *
+ * @param[in] fun Device function.
+ * @param[out] desc Device descriptor to be filled.
  * @return Error code.
  */
-static errno_t unregister_endpoint(
-    ddf_fun_t *fun, usb_endpoint_t endpoint, usb_direction_t direction)
-{
-	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
-	assert(hcd);
+static errno_t get_device_description(ddf_fun_t *fun, usb_device_desc_t *desc)
+{
+	assert(fun);
+	device_t *dev = ddf_fun_data_get(fun);
 	assert(dev);
-	const usb_target_t target =
-	    {{.address = dev->address, .endpoint = endpoint}};
-	usb_log_debug("Unregister endpoint %d:%d %s.\n",
-	    dev->address, endpoint, usb_str_direction(direction));
-	return hcd_remove_ep(hcd, target, direction);
-}
-
-static errno_t reserve_default_address(ddf_fun_t *fun, usb_speed_t speed)
-{
-	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
-	assert(hcd);
-	assert(dev);
-
-	usb_log_debug("Device %d requested default address at %s speed\n",
-	    dev->address, usb_str_speed(speed));
-	return hcd_reserve_default_address(hcd, speed);
-}
-
-static errno_t release_default_address(ddf_fun_t *fun)
-{
-	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
-	assert(hcd);
-	assert(dev);
-
-	usb_log_debug("Device %d released default address\n", dev->address);
-	return hcd_release_default_address(hcd);
-}
-
-static errno_t device_enumerate(ddf_fun_t *fun, unsigned port)
-{
-	assert(fun);
-	ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
-	usb_dev_t *dev = ddf_fun_data_get(fun);
-	assert(ddf_dev);
-	assert(dev);
-	usb_log_debug("Hub %d reported a new USB device on port: %u\n",
-	    dev->address, port);
-	return hcd_ddf_new_device(ddf_dev, dev, port);
-}
-
-static errno_t device_remove(ddf_fun_t *fun, unsigned port)
-{
-	assert(fun);
-	ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
-	usb_dev_t *dev = ddf_fun_data_get(fun);
-	assert(ddf_dev);
-	assert(dev);
-	usb_log_debug("Hub `%s' reported removal of device on port %u\n",
-	    ddf_fun_get_name(fun), port);
-	return hcd_ddf_remove_device(ddf_dev, dev, port);
-}
-
-/** Gets handle of the respective device.
- *
- * @param[in] fun Device function.
- * @param[out] handle Place to write the handle.
- * @return Error code.
- */
-static errno_t get_my_device_handle(ddf_fun_t *fun, devman_handle_t *handle)
-{
-	assert(fun);
-	if (handle)
-		*handle = ddf_fun_get_handle(fun);
-	return EOK;
-}
-
-/** Inbound communication interface function.
+
+	if (!desc)
+		return EOK;
+
+	*desc = (usb_device_desc_t) {
+		.address = dev->address,
+		.depth = dev->tier,
+		.speed = dev->speed,
+		.handle = ddf_fun_get_handle(fun),
+		.iface = -1,
+	};
+	return EOK;
+}
+
+/**
+ * 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.
@@ -224,52 +272,53 @@
  * @return Error code.
  */
-static errno_t dev_read(ddf_fun_t *fun, usb_endpoint_t endpoint,
-    uint64_t setup_data, uint8_t *data, size_t size,
-    usbhc_iface_transfer_in_callback_t callback, void *arg)
-{
-	assert(fun);
-	usb_dev_t *usb_dev = ddf_fun_data_get(fun);
-	assert(usb_dev);
+static errno_t transfer(ddf_fun_t *fun,
+    const usbhc_iface_transfer_request_t *ifreq,
+    usbhc_iface_transfer_callback_t callback, void *arg)
+{
+	assert(fun);
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+
 	const usb_target_t target = {{
-	    .address =  usb_dev->address,
-	    .endpoint = endpoint,
+		.address = dev->address,
+		.endpoint = ifreq->endpoint,
+		.stream = ifreq->stream,
 	}};
-	return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)), target,
-	    USB_DIRECTION_IN, data, size, setup_data, callback, NULL, 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 errno_t dev_write(ddf_fun_t *fun, usb_endpoint_t endpoint,
-    uint64_t setup_data, const uint8_t *data, size_t size,
-    usbhc_iface_transfer_out_callback_t callback, void *arg)
-{
-	assert(fun);
-	usb_dev_t *usb_dev = ddf_fun_data_get(fun);
-	assert(usb_dev);
-	const usb_target_t target = {{
-	    .address =  usb_dev->address,
-	    .endpoint = endpoint,
-	}};
-	return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)),
-	    target, USB_DIRECTION_OUT, (uint8_t*)data, size, setup_data, NULL,
-	    callback, arg, "WRITE");
+
+	if (!usb_target_is_valid(&target))
+		return EINVAL;
+
+	if (ifreq->offset > 0 && ifreq->size == 0)
+		return EINVAL;
+
+	if (ifreq->size > 0 && !dma_buffer_is_set(&ifreq->buffer))
+		return EBADMEM;
+
+	if (!callback && arg)
+		return EBADMEM;
+
+	const transfer_request_t request = {
+		.target = target,
+		.dir = ifreq->dir,
+		.buffer = ifreq->buffer,
+		.offset = ifreq->offset,
+		.size = ifreq->size,
+		.setup = ifreq->setup,
+		.on_complete = callback,
+		.arg = arg,
+		.name = (ifreq->dir == USB_DIRECTION_IN) ? "READ" : "WRITE",
+	};
+
+	return bus_issue_transfer(dev, &request);
 }
 
 /** USB device interface */
 static usb_iface_t usb_iface = {
-	.get_my_device_handle = get_my_device_handle,
-
-	.reserve_default_address = reserve_default_address,
-	.release_default_address = release_default_address,
+	.get_my_description = get_device_description,
+};
+
+/** USB host controller interface */
+static usbhc_iface_t usbhc_iface = {
+	.default_address_reservation = default_address_reservation,
 
 	.device_enumerate = device_enumerate,
@@ -279,6 +328,5 @@
 	.unregister_endpoint = unregister_endpoint,
 
-	.read = dev_read,
-	.write = dev_write,
+	.transfer = transfer,
 };
 
@@ -286,86 +334,9 @@
 static ddf_dev_ops_t usb_ops = {
 	.interfaces[USB_DEV_IFACE] = &usb_iface,
+	.interfaces[USBHC_DEV_IFACE] = &usbhc_iface,
 };
 
 
 /* DDF HELPERS */
-
-#define GET_DEVICE_DESC(size) \
-{ \
-	.request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
-	    | (USB_REQUEST_TYPE_STANDARD << 5) \
-	    | USB_REQUEST_RECIPIENT_DEVICE, \
-	.request = USB_DEVREQ_GET_DESCRIPTOR, \
-	.value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
-	.index = uint16_host2usb(0), \
-	.length = uint16_host2usb(size), \
-};
-
-#define SET_ADDRESS(address) \
-{ \
-	.request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
-	    | (USB_REQUEST_TYPE_STANDARD << 5) \
-	    | USB_REQUEST_RECIPIENT_DEVICE, \
-	.request = USB_DEVREQ_SET_ADDRESS, \
-	.value = uint16_host2usb(address), \
-	.index = uint16_host2usb(0), \
-	.length = uint16_host2usb(0), \
-};
-
-static errno_t hcd_ddf_add_device(ddf_dev_t *parent, usb_dev_t *hub_dev,
-    unsigned port, usb_address_t address, usb_speed_t speed, const char *name,
-    const match_id_list_t *mids)
-{
-	assert(parent);
-
-	char default_name[10] = { 0 }; /* usbxyz-ss */
-	if (!name) {
-		snprintf(default_name, sizeof(default_name) - 1,
-		    "usb%u-%cs", address, usb_str_speed(speed)[0]);
-		name = default_name;
-	}
-
-	ddf_fun_t *fun = ddf_fun_create(parent, fun_inner, name);
-	if (!fun)
-		return ENOMEM;
-	usb_dev_t *info = ddf_fun_data_alloc(fun, sizeof(usb_dev_t));
-	if (!info) {
-		ddf_fun_destroy(fun);
-		return ENOMEM;
-	}
-	info->address = address;
-	info->speed = speed;
-	info->fun = fun;
-	info->port = port;
-	info->tt_address = hub_dev ? hub_dev->tt_address : -1;
-	link_initialize(&info->link);
-	list_initialize(&info->devices);
-	fibril_mutex_initialize(&info->guard);
-
-	if (hub_dev && hub_dev->speed == USB_SPEED_HIGH && usb_speed_is_11(speed))
-		info->tt_address = hub_dev->address;
-
-	ddf_fun_set_ops(fun, &usb_ops);
-	list_foreach(mids->ids, link, const match_id_t, mid) {
-		ddf_fun_add_match_id(fun, mid->id, mid->score);
-	}
-
-	errno_t ret = ddf_fun_bind(fun);
-	if (ret != EOK) {
-		ddf_fun_destroy(fun);
-		return ret;
-	}
-
-	if (hub_dev) {
-		fibril_mutex_lock(&hub_dev->guard);
-		list_append(&info->link, &hub_dev->devices);
-		fibril_mutex_unlock(&hub_dev->guard);
-	} else {
-		hc_dev_t *hc_dev = dev_to_hc_dev(parent);
-		assert(hc_dev->root_hub == NULL);
-		hc_dev->root_hub = info;
-	}
-	return EOK;
-}
 
 #define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
@@ -394,5 +365,5 @@
 	assert(l);
 	assert(d);
-	
+
 	if (d->vendor_id != 0) {
 		/* First, with release number. */
@@ -401,5 +372,5 @@
 		    d->vendor_id, d->product_id, (d->device_version >> 8),
 		    (d->device_version & 0xff));
-	
+
 		/* Next, without release number. */
 		ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
@@ -415,220 +386,53 @@
 
 	return EOK;
-
-}
-
-static errno_t hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub,
-    unsigned port)
-{
-	assert(device);
-
-	hcd_t *hcd = dev_to_hcd(device);
-	assert(hcd);
-
-	hc_dev_t *hc_dev = dev_to_hc_dev(device);
-	assert(hc_dev);
-
-	fibril_mutex_lock(&hub->guard);
-
-	usb_dev_t *victim = NULL;
-
-	list_foreach(hub->devices, link, usb_dev_t, it) {
-		if (it->port == port) {
-			victim = it;
-			break;
-		}
-	}
-	if (victim) {
-		assert(victim->port == port);
-		list_remove(&victim->link);
-		fibril_mutex_unlock(&hub->guard);
-		const errno_t ret = ddf_fun_unbind(victim->fun);
-		if (ret == EOK) {
-			usb_address_t address = victim->address;
-			ddf_fun_destroy(victim->fun);
-			hcd_release_address(hcd, address);
-		} else {
-			usb_log_warning("Failed to unbind device `%s': %s\n",
-			    ddf_fun_get_name(victim->fun), str_error(ret));
-		}
-		return EOK;
-	}
-	fibril_mutex_unlock(&hub->guard);
-	return ENOENT;
-}
-
-static errno_t hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port)
-{
-	assert(device);
-
-	hcd_t *hcd = dev_to_hcd(device);
-	assert(hcd);
-
-	usb_speed_t speed = USB_SPEED_MAX;
-
-	/* This checks whether the default address is reserved and gets speed */
-	errno_t ret = usb_bus_get_speed(&hcd->bus, USB_ADDRESS_DEFAULT, &speed);
-	if (ret != EOK) {
-		usb_log_error("Failed to verify speed: %s.", str_error(ret));
-		return ret;
-	}
-
-	usb_log_debug("Found new %s speed USB device.", usb_str_speed(speed));
-
-	static const usb_target_t default_target = {{
-		.address = USB_ADDRESS_DEFAULT,
-		.endpoint = 0,
-	}};
-
-	usb_address_t address;
-	ret = hcd_request_address(hcd, speed, &address);
-	if (ret != EOK) {
-		usb_log_error("Failed to reserve new address: %s.",
-		    str_error(ret));
-		return ret;
-	}
-
-	usb_log_debug("Reserved new address: %d\n", address);
-
-	const usb_target_t target = {{
-		.address = address,
-		.endpoint = 0,
-	}};
-
-	const usb_address_t tt_address = hub ? hub->tt_address : -1;
-
-	/* Add default pipe on default address */
-	usb_log_debug("Device(%d): Adding default target(0:0)\n", address);
-	ret = hcd_add_ep(hcd,
-	    default_target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
-	    CTRL_PIPE_MIN_PACKET_SIZE, CTRL_PIPE_MIN_PACKET_SIZE, 1,
-	    tt_address, port);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to add default target: %s.",
-		    address, str_error(ret));
-		hcd_release_address(hcd, address);
-		return ret;
-	}
-
-	/* Get max packet size for default pipe */
-	usb_standard_device_descriptor_t desc = { 0 };
-	const usb_device_request_setup_packet_t get_device_desc_8 =
-	    GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
-
-	// TODO CALLBACKS
-	usb_log_debug("Device(%d): Requesting first 8B of device descriptor.",
-	    address);
-	size_t got;
-	ret = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_IN,
-	    &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
-	    "read first 8 bytes of dev descriptor", &got);
-
-	if (ret == EOK && got != CTRL_PIPE_MIN_PACKET_SIZE) {
-		ret = EOVERFLOW;
-	}
-
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to get 8B of dev descr: %s.",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, address);
-		return ret;
-	}
-
-	/* Register EP on the new address */
-	usb_log_debug("Device(%d): Registering control EP.", address);
-	ret = hcd_add_ep(hcd, target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
-	    ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
-	    ED_MPS_TRANS_OPPORTUNITIES_GET(uint16_usb2host(desc.max_packet_size)),
-	    ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
-	    tt_address, port);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to register EP0: %s",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, address);
-		return ret;
-	}
-
-	/* Set new address */
-	const usb_device_request_setup_packet_t set_address =
-	    SET_ADDRESS(target.address);
-
-	usb_log_debug("Device(%d): Setting USB address.", address);
-	ret = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_OUT,
-	    NULL, 0, *(uint64_t *)&set_address, "set address", &got);
-
-	usb_log_debug("Device(%d): Removing default (0:0) EP.", address);
-	hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
-
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to set new address: %s.",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, address);
-		return ret;
-	}
-
-	/* Get std device descriptor */
-	const usb_device_request_setup_packet_t get_device_desc =
-	    GET_DEVICE_DESC(sizeof(desc));
-
-	usb_log_debug("Device(%d): Requesting full device descriptor.",
-	    address);
-	ret = hcd_send_batch_sync(hcd, target, USB_DIRECTION_IN,
-	    &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
-	    "read device descriptor", &got);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to set get dev descriptor: %s",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, target.address);
-		return ret;
-	}
+}
+
+device_t *hcd_ddf_fun_create(hc_device_t *hc, usb_speed_t speed)
+{
+	/* Create DDF function for the new device */
+	ddf_fun_t *fun = ddf_fun_create(hc->ddf_dev, fun_inner, NULL);
+	if (!fun)
+		return NULL;
+
+	ddf_fun_set_ops(fun, &usb_ops);
+
+	/* Create USB device node for the new device */
+	device_t *dev = ddf_fun_data_alloc(fun, hc->bus->device_size);
+	if (!dev) {
+		ddf_fun_destroy(fun);
+		return NULL;
+	}
+
+	bus_device_init(dev, hc->bus);
+	dev->fun = fun;
+	dev->speed = speed;
+	return dev;
+}
+
+void hcd_ddf_fun_destroy(device_t *dev)
+{
+	assert(dev);
+	assert(dev->fun);
+	ddf_fun_destroy(dev->fun);
+}
+
+errno_t hcd_ddf_setup_match_ids(device_t *device, usb_standard_device_descriptor_t *desc)
+{
+	errno_t err;
+	match_id_list_t mids;
+
+	init_match_ids(&mids);
 
 	/* Create match ids from the device descriptor */
-	match_id_list_t mids;
-	init_match_ids(&mids);
-
-	usb_log_debug("Device(%d): Creating match IDs.", address);
-	ret = create_match_ids(&mids, &desc);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to create match ids: %s",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, target.address);
-		return ret;
-	}
-
-	/* Register device */
-	usb_log_debug("Device(%d): Registering DDF device.", address);
-	ret = hcd_ddf_add_device(device, hub, port, address, speed, NULL, &mids);
-	clean_match_ids(&mids);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to register: %s.",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, target.address);
-	}
-
-	return ret;
-}
-
-/** Announce root hub to the DDF
- *
- * @param[in] device Host controller ddf device
- * @return Error code
- */
-errno_t hcd_ddf_setup_root_hub(ddf_dev_t *device)
-{
-	assert(device);
-	hcd_t *hcd = dev_to_hcd(device);
-	assert(hcd);
-
-	hcd_reserve_default_address(hcd, hcd->bus.max_speed);
-	const errno_t ret = hcd_ddf_new_device(device, NULL, 0);
-	hcd_release_default_address(hcd);
-	return ret;
+	usb_log_debug("Device(%d): Creating match IDs.", device->address);
+	if ((err = create_match_ids(&mids, desc))) {
+		return err;
+	}
+
+	list_foreach(mids.ids, link, const match_id_t, mid) {
+		ddf_fun_add_match_id(device->fun, mid->id, mid->score);
+	}
+
+	return EOK;
 }
 
@@ -643,21 +447,19 @@
  * This function does all the ddf work for hc driver.
  */
-errno_t hcd_ddf_setup_hc(ddf_dev_t *device, usb_speed_t max_speed,
-    size_t bw, bw_count_func_t bw_count)
+errno_t hcd_ddf_setup_hc(ddf_dev_t *device, size_t size)
 {
 	assert(device);
 
-	hc_dev_t *instance = ddf_dev_data_alloc(device, sizeof(hc_dev_t));
+	hc_device_t *instance = ddf_dev_data_alloc(device, size);
 	if (instance == NULL) {
-		usb_log_error("Failed to allocate HCD ddf structure.\n");
+		usb_log_error("Failed to allocate HCD ddf structure.");
 		return ENOMEM;
 	}
-	instance->root_hub = NULL;
-	hcd_init(&instance->hcd, max_speed, bw, bw_count);
+	instance->ddf_dev = device;
 
 	errno_t ret = ENOMEM;
 	instance->ctl_fun = ddf_fun_create(device, fun_exposed, "ctl");
 	if (!instance->ctl_fun) {
-		usb_log_error("Failed to create HCD ddf fun.\n");
+		usb_log_error("Failed to create HCD ddf fun.");
 		goto err_destroy_fun;
 	}
@@ -665,5 +467,5 @@
 	ret = ddf_fun_bind(instance->ctl_fun);
 	if (ret != EOK) {
-		usb_log_error("Failed to bind ctl_fun: %s.\n", str_error(ret));
+		usb_log_error("Failed to bind ctl_fun: %s.", str_error(ret));
 		goto err_destroy_fun;
 	}
@@ -671,5 +473,5 @@
 	ret = ddf_fun_add_to_category(instance->ctl_fun, USB_HC_CATEGORY);
 	if (ret != EOK) {
-		usb_log_error("Failed to add fun to category: %s.\n",
+		usb_log_error("Failed to add fun to category: %s.",
 		    str_error(ret));
 		ddf_fun_unbind(instance->ctl_fun);
@@ -686,15 +488,10 @@
 }
 
-void hcd_ddf_clean_hc(ddf_dev_t *device)
-{
-	assert(device);
-	hc_dev_t *hc = dev_to_hc_dev(device);
-	assert(hc);
-	const errno_t ret = ddf_fun_unbind(hc->ctl_fun);
-	if (ret == EOK)
-		ddf_fun_destroy(hc->ctl_fun);
-}
-
-//TODO: Cache parent session in HCD
+void hcd_ddf_clean_hc(hc_device_t *hcd)
+{
+	if (ddf_fun_unbind(hcd->ctl_fun) == EOK)
+		ddf_fun_destroy(hcd->ctl_fun);
+}
+
 /** Call the parent driver with a request to enable interrupt
  *
@@ -703,7 +500,7 @@
  * @return Error code.
  */
-errno_t hcd_ddf_enable_interrupt(ddf_dev_t *device, int inum)
-{
-	async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
+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);
 	if (parent_sess == NULL)
 		return EIO;
@@ -712,8 +509,7 @@
 }
 
-//TODO: Cache parent session in HCD
-errno_t hcd_ddf_get_registers(ddf_dev_t *device, hw_res_list_parsed_t *hw_res)
-{
-	async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
+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);
 	if (parent_sess == NULL)
 		return EIO;
@@ -726,221 +522,4 @@
 }
 
-// TODO: move this someplace else
-static inline void irq_code_clean(irq_code_t *code)
-{
-	if (code) {
-		free(code->ranges);
-		free(code->cmds);
-		code->ranges = NULL;
-		code->cmds = NULL;
-		code->rangecount = 0;
-		code->cmdcount = 0;
-	}
-}
-
-/** Register interrupt handler
- *
- * @param[in] device Host controller DDF device
- * @param[in] regs Register range
- * @param[in] irq Interrupt number
- * @paran[in] handler Interrupt handler
- * @param[in] gen_irq_code IRQ code generator.
- *
- * @param[out] handle  IRQ capability handle on success.
- *
- * @return Error code.
- */
-errno_t hcd_ddf_setup_interrupts(ddf_dev_t *device,
-    const hw_res_list_parsed_t *hw_res,
-    interrupt_handler_t handler,
-    errno_t (*gen_irq_code)(irq_code_t *, const hw_res_list_parsed_t *, int *),
-    cap_handle_t *handle)
-{
-
-	assert(device);
-	if (!handler || !gen_irq_code)
-		return ENOTSUP;
-
-	irq_code_t irq_code = {0};
-
-	int irq;
-	errno_t ret = gen_irq_code(&irq_code, hw_res, &irq);
-	if (ret != EOK) {
-		usb_log_error("Failed to generate IRQ code: %s.\n",
-		    str_error(ret));
-		return ret;
-	}
-
-	/* Register handler to avoid interrupt lockup */
-	ret = register_interrupt_handler(device, irq, handler,
-	    &irq_code, handle);
-	irq_code_clean(&irq_code);
-	if (ret != EOK) {
-		usb_log_error("Failed to register interrupt handler: %s.\n",
-		    str_error(ret));
-		return ret;
-	}
-
-	/* Enable interrupts */
-	ret = hcd_ddf_enable_interrupt(device, irq);
-	if (ret != EOK) {
-		usb_log_error("Failed to register interrupt handler: %s.\n",
-		    str_error(ret));
-		unregister_interrupt_handler(device, *handle);
-	}
-	return ret;
-}
-
-/** IRQ handling callback, forward status from call to diver structure.
- *
- * @param[in] dev DDF instance of the device to use.
- * @param[in] call Pointer to the call from kernel.
- */
-void ddf_hcd_gen_irq_handler(ipc_call_t *call, ddf_dev_t *dev)
-{
-	assert(dev);
-	hcd_t *hcd = dev_to_hcd(dev);
-	if (!hcd || !hcd->ops.irq_hook) {
-		usb_log_error("Interrupt on not yet initialized device.\n");
-		return;
-	}
-	const uint32_t status = IPC_GET_ARG1(*call);
-	hcd->ops.irq_hook(hcd, status);
-}
-
-static errno_t interrupt_polling(void *arg)
-{
-	hcd_t *hcd = arg;
-	assert(hcd);
-	if (!hcd->ops.status_hook || !hcd->ops.irq_hook)
-		return ENOTSUP;
-	uint32_t status = 0;
-	while (hcd->ops.status_hook(hcd, &status) == EOK) {
-		hcd->ops.irq_hook(hcd, status);
-		status = 0;
-		/* We should wait 1 frame - 1ms here, but this polling is a
-		 * lame crutch anyway so don't hog the system. 10ms is still
-		 * good enough for emergency mode */
-		async_usleep(10000);
-	}
-	return EOK;
-}
-
-/** Initialize hc and rh DDF structures and their respective drivers.
- *
- * @param device DDF instance of the device to use
- * @param speed Maximum supported speed
- * @param bw Available bandwidth (arbitrary units)
- * @param bw_count Bandwidth computing function
- * @param irq_handler IRQ handling function
- * @param gen_irq_code Function to generate IRQ pseudocode
- *                     (it needs to return used irq number)
- * @param driver_init Function to initialize HC driver
- * @param driver_fini Function to cleanup HC driver
- * @return Error code
- *
- * This function does all the preparatory work for hc and rh drivers:
- *  - gets device's hw resources
- *  - attempts to enable interrupts
- *  - registers interrupt handler
- *  - calls driver specific initialization
- *  - registers root hub
- */
-errno_t hcd_ddf_add_hc(ddf_dev_t *device, const ddf_hc_driver_t *driver)
-{
-	assert(driver);
-	static const struct { size_t bw; bw_count_func_t bw_count; }bw[] = {
-	    [USB_SPEED_FULL] = { .bw = BANDWIDTH_AVAILABLE_USB11,
-	                         .bw_count = bandwidth_count_usb11 },
-	    [USB_SPEED_HIGH] = { .bw = BANDWIDTH_AVAILABLE_USB11,
-	                         .bw_count = bandwidth_count_usb11 },
-	};
-
-	errno_t ret = EOK;
-	const usb_speed_t speed = driver->hc_speed;
-	if (speed >= ARRAY_SIZE(bw) || bw[speed].bw == 0) {
-		usb_log_error("Driver `%s' reported unsupported speed: %s",
-		    driver->name, usb_str_speed(speed));
-		return ENOTSUP;
-	}
-
-	hw_res_list_parsed_t hw_res;
-	ret = hcd_ddf_get_registers(device, &hw_res);
-	if (ret != EOK) {
-		usb_log_error("Failed to get register memory addresses "
-		    "for `%s': %s.\n", ddf_dev_get_name(device),
-		    str_error(ret));
-		return ret;
-	}
-
-	ret = hcd_ddf_setup_hc(device, speed, bw[speed].bw, bw[speed].bw_count);
-	if (ret != EOK) {
-		usb_log_error("Failed to setup generic HCD.\n");
-		hw_res_list_parsed_clean(&hw_res);
-		return ret;
-	}
-
-	interrupt_handler_t *irq_handler =
-	    driver->irq_handler ? driver->irq_handler : ddf_hcd_gen_irq_handler;
-	int irq_cap;
-	errno_t irq_ret = hcd_ddf_setup_interrupts(device, &hw_res,
-	    irq_handler, driver->irq_code_gen, &irq_cap);
-	bool irqs_enabled = (irq_ret == EOK);
-	if (irqs_enabled) {
-		usb_log_debug("Hw interrupts enabled.\n");
-	}
-
-	if (driver->claim) {
-		ret = driver->claim(device);
-		if (ret != EOK) {
-			usb_log_error("Failed to claim `%s' for driver `%s'",
-			    ddf_dev_get_name(device), driver->name);
-			return ret;
-		}
-	}
-
-
-	/* Init hw driver */
-	hcd_t *hcd = dev_to_hcd(device);
-	ret = driver->init(hcd, &hw_res, irqs_enabled);
-	hw_res_list_parsed_clean(&hw_res);
-	if (ret != EOK) {
-		usb_log_error("Failed to init HCD: %s.\n", str_error(ret));
-		goto irq_unregister;
-	}
-
-	/* Need working irq replacement to setup root hub */
-	if (!irqs_enabled && hcd->ops.status_hook) {
-		hcd->polling_fibril = fibril_create(interrupt_polling, hcd);
-		if (hcd->polling_fibril == 0) {
-			usb_log_error("Failed to create polling fibril\n");
-			ret = ENOMEM;
-			goto irq_unregister;
-		}
-		fibril_add_ready(hcd->polling_fibril);
-		usb_log_warning("Failed to enable interrupts: %s."
-		    " Falling back to polling.\n", str_error(irq_ret));
-	}
-
-	/*
-	 * Creating root hub registers a new USB device so HC
-	 * needs to be ready at this time.
-	 */
-	ret = hcd_ddf_setup_root_hub(device);
-	if (ret != EOK) {
-		usb_log_error("Failed to setup HC root hub: %s.\n",
-		    str_error(ret));
-		driver->fini(dev_to_hcd(device));
-irq_unregister:
-		/* Unregistering non-existent should be ok */
-		unregister_interrupt_handler(device, irq_cap);
-		hcd_ddf_clean_hc(device);
-		return ret;
-	}
-
-	usb_log_info("Controlling new `%s' device `%s'.\n",
-	    driver->name, ddf_dev_get_name(device));
-	return EOK;
-}
 /**
  * @}
Index: uspace/lib/usbhost/src/endpoint.c
===================================================================
--- uspace/lib/usbhost/src/endpoint.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/src/endpoint.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
  * All rights reserved.
  *
@@ -34,159 +35,259 @@
  */
 
-#include <usb/host/endpoint.h>
-
 #include <assert.h>
+#include <atomic.h>
+#include <mem.h>
 #include <stdlib.h>
-#include <atomic.h>
-
-/** Allocate ad initialize endpoint_t structure.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param type USB transfer type.
- * @param speed Communication speed.
- * @param max_packet_size Maximum size of data packets.
- * @param bw Required bandwidth.
- * @return Pointer to initialized endpoint_t structure, NULL on failure.
- */
-endpoint_t * endpoint_create(usb_address_t address, usb_endpoint_t endpoint,
-    usb_direction_t direction, usb_transfer_type_t type, usb_speed_t speed,
-    size_t max_packet_size, unsigned packets, size_t bw,
-    usb_address_t tt_address, unsigned tt_p)
-{
-	endpoint_t *instance = malloc(sizeof(endpoint_t));
-	if (instance) {
-		atomic_set(&instance->refcnt, 0);
-		instance->address = address;
-		instance->endpoint = endpoint;
-		instance->direction = direction;
-		instance->transfer_type = type;
-		instance->speed = speed;
-		instance->max_packet_size = max_packet_size;
-		instance->packets = packets;
-		instance->bandwidth = bw;
-		instance->toggle = 0;
-		instance->active = false;
-		instance->tt.address = tt_address;
-		instance->tt.port = tt_p;
-		instance->hc_data.data = NULL;
-		instance->hc_data.toggle_get = NULL;
-		instance->hc_data.toggle_set = NULL;
-		link_initialize(&instance->link);
-		fibril_mutex_initialize(&instance->guard);
-		fibril_condvar_initialize(&instance->avail);
-	}
-	return instance;
-}
-
-/** Properly dispose of endpoint_t structure.
- * @param instance endpoint_t structure.
- */
-void endpoint_destroy(endpoint_t *instance)
-{
-	assert(instance);
-	assert(!instance->active);
-	assert(instance->hc_data.data == NULL);
-	free(instance);
-}
-
-void endpoint_add_ref(endpoint_t *instance)
-{
-	atomic_inc(&instance->refcnt);
-}
-
-void endpoint_del_ref(endpoint_t *instance)
-{
-	if (atomic_predec(&instance->refcnt) == 0)
-		endpoint_destroy(instance);
-}
-
-/** Set device specific data and hooks.
- * @param instance endpoint_t structure.
- * @param data device specific data.
- * @param toggle_get Hook to call when retrieving value of toggle bit.
- * @param toggle_set Hook to call when setting the value of toggle bit.
- */
-void endpoint_set_hc_data(endpoint_t *instance,
-    void *data, int (*toggle_get)(void *), void (*toggle_set)(void *, int))
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	instance->hc_data.data = data;
-	instance->hc_data.toggle_get = toggle_get;
-	instance->hc_data.toggle_set = toggle_set;
-	fibril_mutex_unlock(&instance->guard);
-}
-
-/** Clear device specific data and hooks.
- * @param instance endpoint_t structure.
- * @note This function does not free memory pointed to by data pointer.
- */
-void endpoint_clear_hc_data(endpoint_t *instance)
-{
-	assert(instance);
-	endpoint_set_hc_data(instance, NULL, NULL, NULL);
-}
-
-/** Mark the endpoint as active and block access for further fibrils.
- * @param instance endpoint_t structure.
- */
-void endpoint_use(endpoint_t *instance)
-{
-	assert(instance);
-	/* Add reference for active endpoint. */
-	endpoint_add_ref(instance);
-	fibril_mutex_lock(&instance->guard);
-	while (instance->active)
-		fibril_condvar_wait(&instance->avail, &instance->guard);
-	instance->active = true;
-	fibril_mutex_unlock(&instance->guard);
-}
-
-/** Mark the endpoint as inactive and allow access for further fibrils.
- * @param instance endpoint_t structure.
- */
-void endpoint_release(endpoint_t *instance)
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	instance->active = false;
-	fibril_mutex_unlock(&instance->guard);
-	fibril_condvar_signal(&instance->avail);
-	/* Drop reference for active endpoint. */
-	endpoint_del_ref(instance);
-}
-
-/** Get the value of toggle bit.
- * @param instance endpoint_t structure.
- * @note Will use provided hook.
- */
-int endpoint_toggle_get(endpoint_t *instance)
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	if (instance->hc_data.toggle_get)
-		instance->toggle =
-		    instance->hc_data.toggle_get(instance->hc_data.data);
-	const int ret = instance->toggle;
-	fibril_mutex_unlock(&instance->guard);
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/descriptor.h>
+#include <usb/host/hcd.h>
+#include <usb/host/utility.h>
+
+#include "usb_transfer_batch.h"
+#include "bus.h"
+
+#include "endpoint.h"
+
+/**
+ * Initialize provided endpoint structure.
+ */
+void endpoint_init(endpoint_t *ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
+{
+	memset(ep, 0, sizeof(endpoint_t));
+
+	assert(dev);
+	ep->device = dev;
+
+	atomic_set(&ep->refcnt, 0);
+	fibril_condvar_initialize(&ep->avail);
+
+	ep->endpoint = USB_ED_GET_EP(desc->endpoint);
+	ep->direction = USB_ED_GET_DIR(desc->endpoint);
+	ep->transfer_type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
+	ep->max_packet_size = USB_ED_GET_MPS(desc->endpoint);
+	ep->packets_per_uframe = USB_ED_GET_ADD_OPPS(desc->endpoint) + 1;
+
+	/** Direction both is our construct never present in descriptors */
+	if (ep->transfer_type == USB_TRANSFER_CONTROL)
+		ep->direction = USB_DIRECTION_BOTH;
+
+	ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
+	ep->transfer_buffer_policy = DMA_POLICY_STRICT;
+	ep->required_transfer_buffer_policy = DMA_POLICY_STRICT;
+}
+
+/**
+ * Get the bus endpoint belongs to.
+ */
+static inline const bus_ops_t *get_bus_ops(endpoint_t *ep)
+{
+	return ep->device->bus->ops;
+}
+
+/**
+ * Increase the reference count on endpoint.
+ */
+void endpoint_add_ref(endpoint_t *ep)
+{
+	atomic_inc(&ep->refcnt);
+}
+
+/**
+ * Call the desctruction callback. Default behavior is to free the memory directly.
+ */
+static inline void endpoint_destroy(endpoint_t *ep)
+{
+	const bus_ops_t *ops = get_bus_ops(ep);
+	if (ops->endpoint_destroy) {
+		ops->endpoint_destroy(ep);
+	} else {
+		assert(ep->active_batch == NULL);
+
+		/* Assume mostly the eps will be allocated by malloc. */
+		free(ep);
+	}
+}
+
+/**
+ * Decrease the reference count.
+ */
+void endpoint_del_ref(endpoint_t *ep)
+{
+	if (atomic_predec(&ep->refcnt) == 0) {
+		endpoint_destroy(ep);
+	}
+}
+
+/**
+ * Mark the endpoint as online. Supply a guard to be used for this endpoint
+ * synchronization.
+ */
+void endpoint_set_online(endpoint_t *ep, fibril_mutex_t *guard)
+{
+	ep->guard = guard;
+	ep->online = true;
+}
+
+/**
+ * Mark the endpoint as offline. All other fibrils waiting to activate this
+ * endpoint will be interrupted.
+ */
+void endpoint_set_offline_locked(endpoint_t *ep)
+{
+	assert(ep);
+	assert(fibril_mutex_is_locked(ep->guard));
+
+	ep->online = false;
+	fibril_condvar_broadcast(&ep->avail);
+}
+
+/**
+ * Wait until a transfer finishes. Can be used even when the endpoint is
+ * offline (and is interrupted by the endpoint going offline).
+ */
+void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t timeout)
+{
+	assert(ep);
+	assert(fibril_mutex_is_locked(ep->guard));
+
+	if (ep->active_batch == NULL)
+		return;
+
+	fibril_condvar_wait_timeout(&ep->avail, ep->guard, timeout);
+}
+
+/**
+ * Mark the endpoint as active and block access for further fibrils. If the
+ * endpoint is already active, it will block on ep->avail condvar.
+ *
+ * Call only under endpoint guard. After you activate the endpoint and release
+ * the guard, you must assume that particular transfer is already
+ * finished/aborted.
+ *
+ * Activation and deactivation is not done by the library to maximize
+ * performance. The HC might want to prepare some memory buffers prior to
+ * interfering with other world.
+ *
+ * @param batch Transfer batch this endpoint is blocked by.
+ */
+int endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
+{
+	assert(ep);
+	assert(batch);
+	assert(batch->ep == ep);
+	assert(ep->guard);
+	assert(fibril_mutex_is_locked(ep->guard));
+
+	while (ep->online && ep->active_batch != NULL)
+		fibril_condvar_wait(&ep->avail, ep->guard);
+
+	if (!ep->online)
+		return EINTR;
+
+	assert(ep->active_batch == NULL);
+	ep->active_batch = batch;
+	return EOK;
+}
+
+/**
+ * Mark the endpoint as inactive and allow access for further fibrils.
+ */
+void endpoint_deactivate_locked(endpoint_t *ep)
+{
+	assert(ep);
+	assert(fibril_mutex_is_locked(ep->guard));
+
+	ep->active_batch = NULL;
+	fibril_condvar_signal(&ep->avail);
+}
+
+/**
+ * Initiate a transfer on an endpoint. Creates a transfer batch, checks the
+ * bandwidth requirements and schedules the batch.
+ *
+ * @param endpoint Endpoint for which to send the batch
+ */
+errno_t endpoint_send_batch(endpoint_t *ep, const transfer_request_t *req)
+{
+	assert(ep);
+	assert(req);
+
+	if (ep->transfer_type == USB_TRANSFER_CONTROL) {
+		usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, req->name,
+		    req->target.address, req->target.endpoint,
+		    req->size, ep->max_packet_size,
+		    req->setup);
+	} else {
+		usb_log_debug("%s %d:%d %zu/%zuB", req->name,
+		    req->target.address, req->target.endpoint,
+		    req->size, ep->max_packet_size);
+	}
+
+	device_t * const device = ep->device;
+	if (!device) {
+		usb_log_warning("Endpoint detached");
+		return EAGAIN;
+	}
+
+	const bus_ops_t *ops = device->bus->ops;
+	if (!ops->batch_schedule) {
+		usb_log_error("HCD does not implement scheduler.");
+		return ENOTSUP;
+	}
+
+	size_t size = req->size;
+	/*
+	 * Limit transfers with reserved bandwidth to the amount reserved.
+	 * OUT transfers are rejected, IN can be just trimmed in advance.
+	 */
+	if (size > ep->max_transfer_size &&
+	    (ep->transfer_type == USB_TRANSFER_INTERRUPT
+	     || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)) {
+		if (req->dir == USB_DIRECTION_OUT)
+			return ENOSPC;
+		else
+			size = ep->max_transfer_size;
+	}
+
+	/* Offline devices don't schedule transfers other than on EP0. */
+	if (!device->online && ep->endpoint > 0)
+		return EAGAIN;
+
+	usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
+	if (!batch) {
+		usb_log_error("Failed to create transfer batch.");
+		return ENOMEM;
+	}
+
+	batch->target = req->target;
+	batch->setup.packed = req->setup;
+	batch->dir = req->dir;
+	batch->size = size;
+	batch->offset = req->offset;
+	batch->dma_buffer = req->buffer;
+
+	dma_buffer_acquire(&batch->dma_buffer);
+
+	if (batch->offset != 0) {
+		usb_log_debug("A transfer with nonzero offset requested.");
+		usb_transfer_batch_bounce(batch);
+	}
+
+	if (usb_transfer_batch_bounce_required(batch))
+		usb_transfer_batch_bounce(batch);
+
+	batch->on_complete = req->on_complete;
+	batch->on_complete_data = req->arg;
+
+	const int ret = ops->batch_schedule(batch);
+	if (ret != EOK) {
+		usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
+		usb_transfer_batch_destroy(batch);
+	}
+
 	return ret;
 }
 
-/** Set the value of toggle bit.
- * @param instance endpoint_t structure.
- * @note Will use provided hook.
- */
-void endpoint_toggle_set(endpoint_t *instance, int toggle)
-{
-	assert(instance);
-	assert(toggle == 0 || toggle == 1);
-	fibril_mutex_lock(&instance->guard);
-	instance->toggle = toggle;
-	if (instance->hc_data.toggle_set)
-		instance->hc_data.toggle_set(instance->hc_data.data, toggle);
-	fibril_mutex_unlock(&instance->guard);
-}
-
 /**
  * @}
Index: uspace/lib/usbhost/src/hcd.c
===================================================================
--- uspace/lib/usbhost/src/hcd.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/src/hcd.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -32,258 +32,338 @@
 /** @file
  *
- */
-
-#include <usb/debug.h>
-#include <usb/request.h>
+ * Host controller driver framework. Encapsulates DDF device of HC to an
+ * hc_device_t, which is passed to driver implementing hc_driver_t.
+ */
 
 #include <assert.h>
 #include <async.h>
+#include <ddf/interrupt.h>
 #include <errno.h>
+#include <macros.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/descriptor.h>
+#include <usb/request.h>
 #include <usb_iface.h>
 
+#include "bus.h"
+#include "ddf_helpers.h"
+#include "endpoint.h"
+#include "usb_transfer_batch.h"
+
 #include "hcd.h"
 
-/** Calls ep_add_hook upon endpoint registration.
- * @param ep Endpoint to be registered.
- * @param arg hcd_t in disguise.
- * @return Error code.
- */
-static errno_t register_helper(endpoint_t *ep, void *arg)
-{
-	hcd_t *hcd = arg;
-	assert(ep);
+int hc_dev_add(ddf_dev_t *);
+int hc_dev_remove(ddf_dev_t *);
+int hc_dev_gone(ddf_dev_t *);
+int hc_fun_online(ddf_fun_t *);
+int hc_fun_offline(ddf_fun_t *);
+
+static driver_ops_t hc_driver_ops = {
+	.dev_add = hc_dev_add,
+	.dev_remove = hc_dev_remove,
+	.dev_gone = hc_dev_gone,
+	.fun_online = hc_fun_online,
+	.fun_offline = hc_fun_offline,
+};
+
+static const hc_driver_t *hc_driver;
+
+/**
+ * The main HC driver routine.
+ */
+int hc_driver_main(const hc_driver_t *driver)
+{
+	driver_t ddf_driver = {
+		.name = driver->name,
+		.driver_ops = &hc_driver_ops,
+	};
+
+	/* Remember ops to call. */
+	hc_driver = driver;
+
+	return ddf_driver_main(&ddf_driver);
+}
+
+/**
+ * IRQ handling callback. Call the bus operation.
+ *
+ * Currently, there is a bus ops lookup to find the interrupt handler. So far,
+ * the mechanism is too flexible, as it allows different instances of HC to
+ * have different IRQ handlers, disallowing us to optimize the lookup here.
+ * TODO: Make the bus mechanism less flexible in irq handling and remove the
+ * lookup.
+ */
+static void irq_handler(ipc_call_t *call, ddf_dev_t *dev)
+{
+	assert(dev);
+	hc_device_t *hcd = dev_to_hcd(dev);
+
+	const uint32_t status = IPC_GET_ARG1(*call);
+	hcd->bus->ops->interrupt(hcd->bus, status);
+}
+
+/**
+ * Worker for the HW interrupt replacement fibril.
+ */
+static errno_t interrupt_polling(void *arg)
+{
+	bus_t *bus = arg;
+	assert(bus);
+
+	if (!bus->ops->interrupt || !bus->ops->status)
+		return ENOTSUP;
+
+	uint32_t status = 0;
+	while (bus->ops->status(bus, &status) == EOK) {
+		bus->ops->interrupt(bus, status);
+		status = 0;
+		/* We should wait 1 frame - 1ms here, but this polling is a
+		 * lame crutch anyway so don't hog the system. 10ms is still
+		 * good enough for emergency mode */
+		async_usleep(10000);
+	}
+	return EOK;
+}
+
+/**
+ * Clean the IRQ code bottom-half.
+ */
+static inline void irq_code_clean(irq_code_t *code)
+{
+	if (code) {
+		free(code->ranges);
+		free(code->cmds);
+		code->ranges = NULL;
+		code->cmds = NULL;
+		code->rangecount = 0;
+		code->cmdcount = 0;
+	}
+}
+
+/**
+ * Register an interrupt handler. If there is a callback to setup the bottom half,
+ * invoke it and register it. Register for notifications.
+ *
+ * If this method fails, a polling fibril is started instead.
+ *
+ * @param[in] hcd Host controller device.
+ * @param[in] hw_res Resources to be used.
+ *
+ * @return IRQ capability handle on success.
+ * @return Negative error code.
+ */
+static errno_t hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
 	assert(hcd);
-	if (hcd->ops.ep_add_hook)
-		return hcd->ops.ep_add_hook(hcd, ep);
+	irq_code_t irq_code = {0};
+
+	if (!hc_driver->irq_code_gen)
+		return ENOTSUP;
+
+	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));
+		return irq;
+	}
+
+	/* Register handler to avoid interrupt lockup */
+	int irq_cap;
+	ret = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code, &irq_cap);
+	irq_code_clean(&irq_code);
+	if (ret != EOK) {
+		usb_log_error("Failed to register interrupt handler: %s.",
+		    str_error(irq_cap));
+		return irq_cap;
+	}
+
+	/* Enable interrupts */
+	ret = hcd_ddf_enable_interrupt(hcd, irq);
+	if (ret != EOK) {
+		usb_log_error("Failed to enable interrupts: %s.",
+		    str_error(ret));
+		unregister_interrupt_handler(hcd->ddf_dev, irq_cap);
+		return ret;
+	}
+	return irq_cap;
+}
+
+/**
+ * Initialize HC in memory of the driver.
+ *
+ * This function does all the preparatory work for hc and rh drivers:
+ *  - gets device's hw resources
+ *  - attempts to enable interrupts
+ *  - registers interrupt handler
+ *  - calls driver specific initialization
+ *  - registers root hub
+ *
+ * @param device DDF instance of the device to use
+ * @return Error code
+ */
+errno_t hc_dev_add(ddf_dev_t *device)
+{
+	errno_t ret = EOK;
+	assert(device);
+
+	if (!hc_driver->hc_add) {
+		usb_log_error("Driver '%s' does not support adding devices.", hc_driver->name);
+		return ENOTSUP;
+	}
+
+	ret = hcd_ddf_setup_hc(device, hc_driver->hc_device_size);
+	if (ret != EOK) {
+		usb_log_error("Failed to setup HC device.");
+		return ret;
+	}
+
+	hc_device_t *hcd = dev_to_hcd(device);
+
+	hw_res_list_parsed_t hw_res;
+	ret = hcd_ddf_get_registers(hcd, &hw_res);
+	if (ret != EOK) {
+		usb_log_error("Failed to get register memory addresses "
+		    "for `%s': %s.", ddf_dev_get_name(device),
+		    str_error(ret));
+		goto err_hcd;
+	}
+
+	ret = hc_driver->hc_add(hcd, &hw_res);
+	if (ret != EOK) {
+		usb_log_error("Failed to init HCD.");
+		goto err_hw_res;
+	}
+
+	assert(hcd->bus);
+
+	/* Setup interrupts  */
+	hcd->irq_cap = hcd_ddf_setup_interrupts(hcd, &hw_res);
+	if (hcd->irq_cap >= 0) {
+		usb_log_debug("Hw interrupts enabled.");
+	}
+
+	/* Claim the device from BIOS */
+	if (hc_driver->claim)
+		ret = hc_driver->claim(hcd);
+	if (ret != EOK) {
+		usb_log_error("Failed to claim `%s' for `%s': %s",
+		    ddf_dev_get_name(device), hc_driver->name, str_error(ret));
+		goto err_irq;
+	}
+
+	/* Start hw */
+	if (hc_driver->start)
+		ret = hc_driver->start(hcd);
+	if (ret != EOK) {
+		usb_log_error("Failed to start HCD: %s.", str_error(ret));
+		goto err_irq;
+	}
+
+	const bus_ops_t *ops = hcd->bus->ops;
+
+	/* Need working irq replacement to setup root hub */
+	if (hcd->irq_cap < 0 && ops->status) {
+		hcd->polling_fibril = fibril_create(interrupt_polling, hcd->bus);
+		if (!hcd->polling_fibril) {
+			usb_log_error("Failed to create polling fibril");
+			ret = ENOMEM;
+			goto err_started;
+		}
+		fibril_add_ready(hcd->polling_fibril);
+		usb_log_warning("Failed to enable interrupts: %s."
+		    " Falling back to polling.", str_error(hcd->irq_cap));
+	}
+
+	/*
+	 * Creating root hub registers a new USB device so HC
+	 * needs to be ready at this time.
+	 */
+	if (hc_driver->setup_root_hub)
+		ret = hc_driver->setup_root_hub(hcd);
+	if (ret != EOK) {
+		usb_log_error("Failed to setup HC root hub: %s.",
+		    str_error(ret));
+		goto err_polling;
+	}
+
+	usb_log_info("Controlling new `%s' device `%s'.",
+	   hc_driver->name, ddf_dev_get_name(device));
 	return EOK;
-}
-
-/** Calls ep_remove_hook upon endpoint removal.
- * @param ep Endpoint to be unregistered.
- * @param arg hcd_t in disguise.
- */
-static void unregister_helper(endpoint_t *ep, void *arg)
-{
-	hcd_t *hcd = arg;
-	assert(ep);
-	assert(hcd);
-	if (hcd->ops.ep_remove_hook)
-		hcd->ops.ep_remove_hook(hcd, ep);
-}
-
-/** Calls ep_remove_hook upon endpoint removal. Prints warning.
- *  * @param ep Endpoint to be unregistered.
- *   * @param arg hcd_t in disguise.
- *    */
-static void unregister_helper_warn(endpoint_t *ep, void *arg)
-{
-        assert(ep);
-        usb_log_warning("Endpoint %d:%d %s was left behind, removing.\n",
-            ep->address, ep->endpoint, usb_str_direction(ep->direction));
-	unregister_helper(ep, arg);
-}
-
-
-/** Initialize hcd_t structure.
- * Initializes device and endpoint managers. Sets data and hook pointer to NULL.
- *
- * @param hcd hcd_t structure to initialize, non-null.
- * @param max_speed Maximum supported USB speed (full, high).
- * @param bandwidth Available bandwidth, passed to endpoint manager.
- * @param bw_count Bandwidth compute function, passed to endpoint manager.
- */
-void hcd_init(hcd_t *hcd, usb_speed_t max_speed, size_t bandwidth,
-    bw_count_func_t bw_count)
-{
-	assert(hcd);
-	usb_bus_init(&hcd->bus, bandwidth, bw_count, max_speed);
-
-	hcd_set_implementation(hcd, NULL, NULL);
-}
-
-errno_t hcd_request_address(hcd_t *hcd, usb_speed_t speed, usb_address_t *address)
-{
-	assert(hcd);
-	return usb_bus_request_address(&hcd->bus, address, false, speed);
-}
-
-errno_t hcd_release_address(hcd_t *hcd, usb_address_t address)
-{
-	assert(hcd);
-	return usb_bus_remove_address(&hcd->bus, address,
-	    unregister_helper_warn, hcd);
-}
-
-errno_t hcd_reserve_default_address(hcd_t *hcd, usb_speed_t speed)
-{
-	assert(hcd);
-	usb_address_t address = 0;
-	return usb_bus_request_address(&hcd->bus, &address, true, speed);
-}
-
-errno_t hcd_add_ep(hcd_t *hcd, usb_target_t target, usb_direction_t dir,
-    usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
-    size_t size, usb_address_t tt_address, unsigned tt_port)
-{
-	assert(hcd);
-	return usb_bus_add_ep(&hcd->bus, target.address,
-	    target.endpoint, dir, type, max_packet_size, packets, size,
-	    register_helper, hcd, tt_address, tt_port);
-}
-
-errno_t hcd_remove_ep(hcd_t *hcd, usb_target_t target, usb_direction_t dir)
-{
-	assert(hcd);
-	return usb_bus_remove_ep(&hcd->bus, target.address,
-	    target.endpoint, dir, unregister_helper, hcd);
-}
-
-
-typedef struct {
-	void *original_data;
-	usbhc_iface_transfer_out_callback_t original_callback;
-	usb_target_t target;
-	hcd_t *hcd;
-} toggle_t;
-
-static void toggle_reset_callback(errno_t retval, void *arg)
-{
-	assert(arg);
-	toggle_t *toggle = arg;
-	if (retval == EOK) {
-		usb_log_debug2("Reseting toggle on %d:%d.\n",
-		    toggle->target.address, toggle->target.endpoint);
-		usb_bus_reset_toggle(&toggle->hcd->bus,
-		    toggle->target, toggle->target.endpoint == 0);
-	}
-
-	toggle->original_callback(retval, toggle->original_data);
-}
-
-/** Prepare generic usb_transfer_batch and schedule it.
- * @param hcd Host controller driver.
- * @param fun DDF fun
- * @param target address and endpoint number.
- * @param setup_data Data to use in setup stage (Control communication type)
- * @param in Callback for device to host communication.
- * @param out Callback for host to device communication.
- * @param arg Callback parameter.
- * @param name Communication identifier (for nicer output).
- * @return Error code.
- */
-errno_t hcd_send_batch(
-    hcd_t *hcd, usb_target_t target, usb_direction_t direction,
-    void *data, size_t size, uint64_t setup_data,
-    usbhc_iface_transfer_in_callback_t in,
-    usbhc_iface_transfer_out_callback_t out, void *arg, const char* name)
-{
-	assert(hcd);
-
-	endpoint_t *ep = usb_bus_find_ep(&hcd->bus,
-	    target.address, target.endpoint, direction);
-	if (ep == NULL) {
-		usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
-		    target.address, target.endpoint, name);
-		return ENOENT;
-	}
-
-	usb_log_debug2("%s %d:%d %zu(%zu).\n",
-	    name, target.address, target.endpoint, size, ep->max_packet_size);
-
-	const size_t bw = bandwidth_count_usb11(
-	    ep->speed, ep->transfer_type, size, ep->max_packet_size);
-	/* Check if we have enough bandwidth reserved */
-	if (ep->bandwidth < bw) {
-		usb_log_error("Endpoint(%d:%d) %s needs %zu bw "
-		    "but only %zu is reserved.\n",
-		    ep->address, ep->endpoint, name, bw, ep->bandwidth);
-		return ENOSPC;
-	}
-	if (!hcd->ops.schedule) {
-		usb_log_error("HCD does not implement scheduler.\n");
-		return ENOTSUP;
-	}
-
-	/* Check for commands that reset toggle bit */
-	if (ep->transfer_type == USB_TRANSFER_CONTROL) {
-		const int reset_toggle = usb_request_needs_toggle_reset(
-		    (usb_device_request_setup_packet_t *) &setup_data);
-		if (reset_toggle >= 0) {
-			assert(out);
-			toggle_t *toggle = malloc(sizeof(toggle_t));
-			if (!toggle)
-				return ENOMEM;
-			toggle->target.address = target.address;
-			toggle->target.endpoint = reset_toggle;
-			toggle->original_callback = out;
-			toggle->original_data = arg;
-			toggle->hcd = hcd;
-
-			arg = toggle;
-			out = toggle_reset_callback;
-		}
-	}
-
-	usb_transfer_batch_t *batch = usb_transfer_batch_create(
-	    ep, data, size, setup_data, in, out, arg);
-	if (!batch) {
-		usb_log_error("Failed to create transfer batch.\n");
-		return ENOMEM;
-	}
-
-	const errno_t ret = hcd->ops.schedule(hcd, batch);
-	if (ret != EOK)
-		usb_transfer_batch_destroy(batch);
-
-	/* Drop our own reference to ep. */
-	endpoint_del_ref(ep);
-
+
+err_polling:
+	// TODO: Stop the polling fibril (refactor the interrupt_polling func)
+	//
+err_started:
+	if (hc_driver->stop)
+		hc_driver->stop(hcd);
+err_irq:
+	unregister_interrupt_handler(device, hcd->irq_cap);
+	if (hc_driver->hc_remove)
+		hc_driver->hc_remove(hcd);
+err_hw_res:
+	hw_res_list_parsed_clean(&hw_res);
+err_hcd:
+	hcd_ddf_clean_hc(hcd);
 	return ret;
 }
 
-typedef struct {
-	volatile unsigned done;
-	errno_t ret;
-	size_t size;
-} sync_data_t;
-
-static void transfer_in_cb(errno_t ret, size_t size, void* data)
-{
-	sync_data_t *d = data;
-	assert(d);
-	d->ret = ret;
-	d->done = 1;
-	d->size = size;
-}
-
-static void transfer_out_cb(errno_t ret, void* data)
-{
-	sync_data_t *d = data;
-	assert(data);
-	d->ret = ret;
-	d->done = 1;
-}
-
-/** this is really ugly version of sync usb communication */
-errno_t hcd_send_batch_sync(
-    hcd_t *hcd, usb_target_t target, usb_direction_t dir,
-    void *data, size_t size, uint64_t setup_data, const char* name, size_t *out_size)
-{
-	assert(hcd);
-	sync_data_t sd = { .done = 0, .ret = EBUSY, .size = size };
-
-	const errno_t ret = hcd_send_batch(hcd, target, dir, data, size, setup_data,
-	    dir == USB_DIRECTION_IN ? transfer_in_cb : NULL,
-	    dir == USB_DIRECTION_OUT ? transfer_out_cb : NULL, &sd, name);
-	if (ret != EOK)
-		return ret;
-
-	while (!sd.done) {
-		async_usleep(1000);
-	}
-
-	if (sd.ret == EOK)
-		*out_size = sd.size;
-	return sd.ret;
+errno_t hc_dev_remove(ddf_dev_t *dev)
+{
+	errno_t err;
+	hc_device_t *hcd = dev_to_hcd(dev);
+
+	if (hc_driver->stop)
+		if ((err = hc_driver->stop(hcd)))
+			return err;
+
+	unregister_interrupt_handler(dev, hcd->irq_cap);
+
+	if (hc_driver->hc_remove)
+		if ((err = hc_driver->hc_remove(hcd)))
+			return err;
+
+	hcd_ddf_clean_hc(hcd);
+
+	// TODO probably not complete
+
+	return EOK;
+}
+
+errno_t hc_dev_gone(ddf_dev_t *dev)
+{
+	errno_t err = ENOTSUP;
+	hc_device_t *hcd = dev_to_hcd(dev);
+
+	if (hc_driver->hc_gone)
+		err = hc_driver->hc_gone(hcd);
+
+	hcd_ddf_clean_hc(hcd);
+
+	return err;
+}
+
+errno_t hc_fun_online(ddf_fun_t *fun)
+{
+	assert(fun);
+
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+
+	usb_log_info("Device(%d): Requested to be brought online.", dev->address);
+	return bus_device_online(dev);
+}
+
+int hc_fun_offline(ddf_fun_t *fun)
+{
+	assert(fun);
+
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+
+	usb_log_info("Device(%d): Requested to be taken offline.", dev->address);
+	return bus_device_offline(dev);
 }
 
Index: uspace/lib/usbhost/src/usb2_bus.c
===================================================================
--- uspace/lib/usbhost/src/usb2_bus.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/src/usb2_bus.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2011 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
+ *
+ * A bus_t implementation for USB 2 and lower. Implements USB 2 enumeration and
+ * configurable bandwidth counting.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <macros.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/descriptor.h>
+#include <usb/request.h>
+#include <usb/host/utility.h>
+#include <usb/usb.h>
+
+#include "endpoint.h"
+#include "ddf_helpers.h"
+
+#include "usb2_bus.h"
+
+/**
+ * Request a new address. A free address is found and marked as occupied.
+ *
+ * There's no need to synchronize this method, because it is called only with
+ * default address reserved.
+ *
+ * @param bus usb_device_manager
+ * @param addr Pointer to requested address value, place to store new address
+ */
+static int request_address(usb2_bus_helper_t *helper, usb_address_t *addr)
+{
+	// Find a free address
+	usb_address_t new_address = helper->last_address;
+	do {
+		new_address = (new_address + 1) % USB_ADDRESS_COUNT;
+		if (new_address == USB_ADDRESS_DEFAULT)
+			new_address = 1;
+		if (new_address == helper->last_address)
+			return ENOSPC;
+	} while (helper->address_occupied[new_address]);
+	helper->last_address = new_address;
+
+	*addr = new_address;
+	helper->address_occupied[*addr] = true;
+
+	return EOK;
+}
+
+/**
+ * Mark address as free.
+ */
+static void release_address(usb2_bus_helper_t *helper, usb_address_t address)
+{
+	helper->address_occupied[address] = false;
+}
+
+static const usb_target_t usb2_default_target = {{
+	.address = USB_ADDRESS_DEFAULT,
+	.endpoint = 0,
+}};
+
+/**
+ * Transition the device to the addressed state.
+ *
+ * Reserve address, configure the control EP, issue a SET_ADDRESS command.
+ * Configure the device with the new address,
+ */
+static int address_device(usb2_bus_helper_t *helper, device_t *dev)
+{
+	int err;
+
+	/* The default address is currently reserved for this device */
+	dev->address = USB_ADDRESS_DEFAULT;
+
+	/** Reserve address early, we want pretty log messages */
+	usb_address_t address = USB_ADDRESS_DEFAULT;
+	if ((err = request_address(helper, &address))) {
+		usb_log_error("Failed to reserve new address: %s.",
+		    str_error(err));
+		return err;
+	}
+	usb_log_debug("Device(%d): Reserved new address.", address);
+
+	/* Add default pipe on default address */
+	usb_log_debug("Device(%d): Adding default target (0:0)", address);
+
+	usb_endpoint_descriptors_t ep0_desc = {
+	    .endpoint.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
+	};
+
+	/* Temporary reference */
+	endpoint_t *default_ep;
+	err = bus_endpoint_add(dev, &ep0_desc, &default_ep);
+	if (err != EOK) {
+		usb_log_error("Device(%d): Failed to add default target: %s.",
+		    address, str_error(err));
+		goto err_address;
+	}
+
+	if ((err = hc_get_ep0_max_packet_size(&ep0_desc.endpoint.max_packet_size, dev)))
+		goto err_address;
+
+	/* Set new address */
+	const usb_device_request_setup_packet_t set_address = SET_ADDRESS(address);
+
+	usb_log_debug("Device(%d): Setting USB address.", address);
+	err = bus_device_send_batch_sync(dev, usb2_default_target, USB_DIRECTION_OUT,
+	    NULL, 0, *(uint64_t *)&set_address, "set address", NULL);
+	if (err) {
+		usb_log_error("Device(%d): Failed to set new address: %s.",
+		    address, str_error(err));
+		goto err_default_control_ep;
+	}
+
+	/* We need to remove ep before we change the address */
+	if ((err = bus_endpoint_remove(default_ep))) {
+		usb_log_error("Device(%d): Failed to unregister default target: %s", address, str_error(err));
+		goto err_address;
+	}
+
+	/* Temporary reference */
+	endpoint_del_ref(default_ep);
+
+	dev->address = address;
+
+	/* Register EP on the new address */
+	usb_log_debug("Device(%d): Registering control EP.", address);
+	err = bus_endpoint_add(dev, &ep0_desc, NULL);
+	if (err != EOK) {
+		usb_log_error("Device(%d): Failed to register EP0: %s",
+		    address, str_error(err));
+		goto err_address;
+	}
+
+	return EOK;
+
+err_default_control_ep:
+	bus_endpoint_remove(default_ep);
+	/* Temporary reference */
+	endpoint_del_ref(default_ep);
+err_address:
+	release_address(helper, address);
+	return err;
+}
+
+/**
+ * Enumerate a USB device. Move it to the addressed state, then explore it
+ * to create a DDF function node with proper characteristics.
+ */
+int usb2_bus_device_enumerate(usb2_bus_helper_t *helper, device_t *dev)
+{
+	int err;
+	usb_log_debug("Found new %s speed USB device.", usb_str_speed(dev->speed));
+
+	/* Assign an address to the device */
+	if ((err = address_device(helper, dev))) {
+		usb_log_error("Failed to setup address of the new device: %s", str_error(err));
+		return err;
+	}
+
+	/* Read the device descriptor, derive the match ids */
+	if ((err = hc_device_explore(dev))) {
+		usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
+		release_address(helper, dev->address);
+		return err;
+	}
+
+	return EOK;
+}
+
+void usb2_bus_device_gone(usb2_bus_helper_t *helper, device_t *dev)
+{
+	release_address(helper, dev->address);
+}
+
+/**
+ * Register an endpoint to the bus. Reserves bandwidth.
+ */
+int usb2_bus_endpoint_register(usb2_bus_helper_t *helper, endpoint_t *ep)
+{
+	assert(ep);
+	assert(fibril_mutex_is_locked(&ep->device->guard));
+
+	size_t bw = helper->bw_accounting->count_bw(ep);
+
+	/* Check for available bandwidth */
+	if (bw > helper->free_bw)
+		return ENOSPC;
+
+	helper->free_bw -= bw;
+
+	return EOK;
+}
+
+/**
+ * Release bandwidth reserved by the given endpoint.
+ */
+void usb2_bus_endpoint_unregister(usb2_bus_helper_t *helper, endpoint_t *ep)
+{
+	assert(helper);
+	assert(ep);
+
+	helper->free_bw += helper->bw_accounting->count_bw(ep);
+}
+
+/**
+ * Initialize to default state.
+ *
+ * @param helper usb_bus_helper structure, non-null.
+ * @param bw_accounting a structure defining bandwidth accounting
+ */
+void usb2_bus_helper_init(usb2_bus_helper_t *helper, const bandwidth_accounting_t *bw_accounting)
+{
+	assert(helper);
+	assert(bw_accounting);
+
+	helper->bw_accounting = bw_accounting;
+	helper->free_bw = bw_accounting->available_bandwidth;
+
+	/*
+	 * The first address allocated is for the roothub. This way, its
+	 * address will be 127, and the first connected USB device will have
+	 * address 1.
+	 */
+	helper->last_address = USB_ADDRESS_COUNT - 2;
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/src/usb_bus.c
===================================================================
--- uspace/lib/usbhost/src/usb_bus.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ 	(revision )
@@ -1,561 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * All rights eps.
- *
- * 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
- * HC Endpoint management.
- */
-
-#include <usb/host/usb_bus.h>
-#include <usb/debug.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <macros.h>
-#include <stdbool.h>
-
-
-/** Endpoint compare helper function.
- *
- * USB_DIRECTION_BOTH matches both IN and OUT.
- * @param ep Endpoint to compare, non-null.
- * @param address Tested address.
- * @param endpoint Tested endpoint number.
- * @param direction Tested direction.
- * @return True if ep can be used to communicate with given device,
- * false otherwise.
- */
-static inline bool ep_match(const endpoint_t *ep,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
-{
-	assert(ep);
-	return
-	    ((direction == ep->direction)
-	        || (ep->direction == USB_DIRECTION_BOTH)
-	        || (direction == USB_DIRECTION_BOTH))
-	    && (endpoint == ep->endpoint)
-	    && (address == ep->address);
-}
-
-/** Get list that holds endpoints for given address.
- * @param instance usb_bus structure, non-null.
- * @param addr USB address, must be >= 0.
- * @return Pointer to the appropriate list.
- */
-static list_t * get_list(usb_bus_t *instance, usb_address_t addr)
-{
-	assert(instance);
-	assert(addr >= 0);
-	return &instance->devices[addr % ARRAY_SIZE(instance->devices)].endpoint_list;
-}
-
-/** Internal search function, works on locked structure.
- * @param instance usb_bus structure, non-null.
- * @param address USB address, must be valid.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @return Pointer to endpoint_t structure representing given communication
- * target, NULL if there is no such endpoint registered.
- * @note Assumes that the internal mutex is locked.
- */
-static endpoint_t * find_locked(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
-{
-	assert(instance);
-	assert(fibril_mutex_is_locked(&instance->guard));
-	if (address < 0)
-		return NULL;
-	list_foreach(*get_list(instance, address), link, endpoint_t, ep) {
-		if (ep_match(ep, address, endpoint, direction))
-			return ep;
-	}
-	return NULL;
-}
-
-/** Get a free USB address
- *
- * @param[in] instance Device manager structure to use.
- * @param[out] address Free address.
- * @return Error code.
- */
-static errno_t usb_bus_get_free_address(usb_bus_t *instance, usb_address_t *address)
-{
-
-	usb_address_t new_address = instance->last_address;
-	do {
-		new_address = (new_address + 1) % USB_ADDRESS_COUNT;
-		if (new_address == USB_ADDRESS_DEFAULT)
-			new_address = 1;
-		if (new_address == instance->last_address)
-			return ENOSPC;
-	} while (instance->devices[new_address].occupied);
-
-	assert(new_address != USB_ADDRESS_DEFAULT);
-	instance->last_address = new_address;
-
-	*address = new_address;
-	return EOK;
-}
-
-/** Calculate bandwidth that needs to be reserved for communication with EP.
- * Calculation follows USB 1.1 specification.
- * @param speed Device's speed.
- * @param type Type of the transfer.
- * @param size Number of byte to transfer.
- * @param max_packet_size Maximum bytes in one packet.
- */
-size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size)
-{
-	/* We care about bandwidth only for interrupt and isochronous. */
-	if ((type != USB_TRANSFER_INTERRUPT)
-	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
-		return 0;
-	}
-
-	const unsigned packet_count =
-	    (size + max_packet_size - 1) / max_packet_size;
-	/* TODO: It may be that ISO and INT transfers use only one packet per
-	 * transaction, but I did not find text in USB spec to confirm this */
-	/* NOTE: All data packets will be considered to be max_packet_size */
-	switch (speed)
-	{
-	case USB_SPEED_LOW:
-		assert(type == USB_TRANSFER_INTERRUPT);
-		/* Protocol overhead 13B
-		 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
-		 * CRC bytes, and a 3-byte interpacket delay)
-		 * see USB spec page 45-46. */
-		/* Speed penalty 8: low speed is 8-times slower*/
-		return packet_count * (13 + max_packet_size) * 8;
-	case USB_SPEED_FULL:
-		/* Interrupt transfer overhead see above
-		 * or page 45 of USB spec */
-		if (type == USB_TRANSFER_INTERRUPT)
-			return packet_count * (13 + max_packet_size);
-
-		assert(type == USB_TRANSFER_ISOCHRONOUS);
-		/* Protocol overhead 9B
-		 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
-		 * bytes, and a 1-byte interpacket delay)
-		 * see USB spec page 42 */
-		return packet_count * (9 + max_packet_size);
-	default:
-		return 0;
-	}
-}
-
-/** Calculate bandwidth that needs to be reserved for communication with EP.
- * Calculation follows USB 2.0 specification.
- * @param speed Device's speed.
- * @param type Type of the transfer.
- * @param size Number of byte to transfer.
- * @param max_packet_size Maximum bytes in one packet.
- */
-size_t bandwidth_count_usb20(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size)
-{
-	/* We care about bandwidth only for interrupt and isochronous. */
-	if ((type != USB_TRANSFER_INTERRUPT)
-	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
-		return 0;
-	}
-	//TODO Implement
-	return 0;
-}
-
-/** Initialize to default state.
- * You need to provide valid bw_count function if you plan to use
- * add_endpoint/remove_endpoint pair.
- *
- * @param instance usb_bus structure, non-null.
- * @param available_bandwidth Size of the bandwidth pool.
- * @param bw_count function to use to calculate endpoint bw requirements.
- * @return Error code.
- */
-errno_t usb_bus_init(usb_bus_t *instance,
-    size_t available_bandwidth, bw_count_func_t bw_count, usb_speed_t max_speed)
-{
-	assert(instance);
-	fibril_mutex_initialize(&instance->guard);
-	instance->free_bw = available_bandwidth;
-	instance->bw_count = bw_count;
-	instance->last_address = 0;
-	instance->max_speed = max_speed;
-	for (unsigned i = 0; i < ARRAY_SIZE(instance->devices); ++i) {
-		list_initialize(&instance->devices[i].endpoint_list);
-		instance->devices[i].speed = USB_SPEED_MAX;
-		instance->devices[i].occupied = false;
-	}
-	return EOK;
-}
-
-/** Register endpoint structure.
- * Checks for duplicates.
- * @param instance usb_bus, non-null.
- * @param ep endpoint_t to register.
- * @param data_size Size of data to transfer.
- * @return Error code.
- */
-errno_t usb_bus_register_ep(usb_bus_t *instance, endpoint_t *ep, size_t data_size)
-{
-	assert(instance);
-	if (ep == NULL || ep->address < 0)
-		return EINVAL;
-
-	fibril_mutex_lock(&instance->guard);
-	/* Check for available bandwidth */
-	if (ep->bandwidth > instance->free_bw) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOSPC;
-	}
-
-	/* Check for existence */
-	const endpoint_t *endpoint =
-	    find_locked(instance, ep->address, ep->endpoint, ep->direction);
-	if (endpoint != NULL) {
-		fibril_mutex_unlock(&instance->guard);
-		return EEXIST;
-	}
-	/* Add endpoint list's reference to ep. */
-	endpoint_add_ref(ep);
-	list_append(&ep->link, get_list(instance, ep->address));
-
-	instance->free_bw -= ep->bandwidth;
-	usb_log_debug("Registered EP(%d:%d:%s:%s)\n", ep->address, ep->endpoint,
-	    usb_str_transfer_type_short(ep->transfer_type),
-	    usb_str_direction(ep->direction));
-	fibril_mutex_unlock(&instance->guard);
-	return EOK;
-}
-
-/** Unregister endpoint structure.
- * Checks for duplicates.
- * @param instance usb_bus, non-null.
- * @param ep endpoint_t to unregister.
- * @return Error code.
- */
-errno_t usb_bus_unregister_ep(usb_bus_t *instance, endpoint_t *ep)
-{
-	assert(instance);
-	if (ep == NULL || ep->address < 0)
-		return EINVAL;
-
-	fibril_mutex_lock(&instance->guard);
-	if (!list_member(&ep->link, get_list(instance, ep->address))) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOENT;
-	}
-	list_remove(&ep->link);
-	instance->free_bw += ep->bandwidth;
-	usb_log_debug("Unregistered EP(%d:%d:%s:%s)\n", ep->address,
-	    ep->endpoint, usb_str_transfer_type_short(ep->transfer_type),
-	    usb_str_direction(ep->direction));
-	/* Drop endpoint list's reference to ep. */
-	endpoint_del_ref(ep);
-	fibril_mutex_unlock(&instance->guard);
-	return EOK;
-}
-
-/** Find endpoint_t representing the given communication route.
- * @param instance usb_bus, non-null.
- * @param address
- */
-endpoint_t * usb_bus_find_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
-{
-	assert(instance);
-
-	fibril_mutex_lock(&instance->guard);
-	endpoint_t *ep = find_locked(instance, address, endpoint, direction);
-	if (ep) {
-		/* We are exporting ep to the outside world, add reference. */
-		endpoint_add_ref(ep);
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ep;
-}
-
-/** Create and register new endpoint_t structure.
- * @param instance usb_bus structure, non-null.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param type USB transfer type.
- * @param speed USB Communication speed.
- * @param max_packet_size Maximum size of data packets.
- * @param data_size Expected communication size.
- * @param callback function to call just after registering.
- * @param arg Argument to pass to the callback function.
- * @return Error code.
- */
-errno_t usb_bus_add_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
-    size_t data_size, ep_add_callback_t callback, void *arg,
-    usb_address_t tt_address, unsigned tt_port)
-{
-	assert(instance);
-	if (instance->bw_count == NULL)
-		return ENOTSUP;
-	if (!usb_address_is_valid(address))
-		return EINVAL;
-
-
-	fibril_mutex_lock(&instance->guard);
-	/* Check for speed and address */
-	if (!instance->devices[address].occupied) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOENT;
-	}
-
-	/* Check for existence */
-	endpoint_t *ep = find_locked(instance, address, endpoint, direction);
-	if (ep != NULL) {
-		fibril_mutex_unlock(&instance->guard);
-		return EEXIST;
-	}
-
-	const usb_speed_t speed = instance->devices[address].speed;
-	const size_t bw =
-	    instance->bw_count(speed, type, data_size, max_packet_size);
-
-	/* Check for available bandwidth */
-	if (bw > instance->free_bw) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOSPC;
-	}
-
-	ep = endpoint_create(address, endpoint, direction, type, speed,
-	    max_packet_size, packets, bw, tt_address, tt_port);
-	if (!ep) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOMEM;
-	}
-
-	/* Add our reference to ep. */
-	endpoint_add_ref(ep);
-
-	if (callback) {
-		const errno_t ret = callback(ep, arg);
-		if (ret != EOK) {
-			fibril_mutex_unlock(&instance->guard);
-			endpoint_del_ref(ep);
-			return ret;
-		}
-	}
-	
-	/* Add endpoint list's reference to ep. */
-	endpoint_add_ref(ep);
-	list_append(&ep->link, get_list(instance, ep->address));
-
-	instance->free_bw -= ep->bandwidth;
-	fibril_mutex_unlock(&instance->guard);
-
-	/* Drop our reference to ep. */
-	endpoint_del_ref(ep);
-
-	return EOK;
-}
-
-/** Unregister and destroy endpoint_t structure representing given route.
- * @param instance usb_bus structure, non-null.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param callback Function to call after unregister, before destruction.
- * @arg Argument to pass to the callback function.
- * @return Error code.
- */
-errno_t usb_bus_remove_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    ep_remove_callback_t callback, void *arg)
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	endpoint_t *ep = find_locked(instance, address, endpoint, direction);
-	if (ep != NULL) {
-		list_remove(&ep->link);
-		instance->free_bw += ep->bandwidth;
-	}
-	fibril_mutex_unlock(&instance->guard);
-	if (ep == NULL)
-		return ENOENT;
-
-	if (callback) {
-		callback(ep, arg);
-	}
-	/* Drop endpoint list's reference to ep. */
-	endpoint_del_ref(ep);
-	return EOK;
-}
-
-errno_t usb_bus_reset_toggle(usb_bus_t *instance, usb_target_t target, bool all)
-{
-	assert(instance);
-	if (!usb_target_is_valid(target))
-		return EINVAL;
-
-	errno_t ret = ENOENT;
-
-	fibril_mutex_lock(&instance->guard);
-	list_foreach(*get_list(instance, target.address), link, endpoint_t, ep) {
-		if ((ep->address == target.address)
-		    && (all || ep->endpoint == target.endpoint)) {
-			endpoint_toggle_set(ep, 0);
-			ret = EOK;
-		}
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
-}
-
-/** Unregister and destroy all endpoints using given address.
- * @param instance usb_bus structure, non-null.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param callback Function to call after unregister, before destruction.
- * @arg Argument to pass to the callback function.
- * @return Error code.
- */
-errno_t usb_bus_remove_address(usb_bus_t *instance,
-    usb_address_t address, ep_remove_callback_t callback, void *arg)
-{
-	assert(instance);
-	if (!usb_address_is_valid(address))
-		return EINVAL;
-
-	fibril_mutex_lock(&instance->guard);
-
-	const errno_t ret = instance->devices[address].occupied ? EOK : ENOENT;
-	instance->devices[address].occupied = false;
-
-	list_t *list = get_list(instance, address);
-	for (link_t *link = list_first(list); link != NULL; ) {
-		endpoint_t *ep = list_get_instance(link, endpoint_t, link);
-		link = list_next(link, list);
-		if (ep->address == address) {
-			list_remove(&ep->link);
-			if (callback)
-				callback(ep, arg);
-			/* Drop endpoint list's reference to ep. */
-			endpoint_del_ref(ep);
-		}
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
-}
-
-/** Request USB address.
- * @param instance usb_device_manager
- * @param[inout] address Pointer to requested address value, place to store new address
- * @parma strict Fail if the requested address is not available.
- * @return Error code.
- * @note Default address is only available in strict mode.
- */
-errno_t usb_bus_request_address(usb_bus_t *instance,
-    usb_address_t *address, bool strict, usb_speed_t speed)
-{
-	assert(instance);
-	assert(address);
-	if (speed > instance->max_speed)
-		return ENOTSUP;
-
-	if (!usb_address_is_valid(*address))
-		return EINVAL;
-
-	usb_address_t addr = *address;
-	errno_t rc;
-
-	fibril_mutex_lock(&instance->guard);
-	/* Only grant default address to strict requests */
-	if ((addr == USB_ADDRESS_DEFAULT) && !strict) {
-		rc = usb_bus_get_free_address(instance, &addr);
-		if (rc != EOK) {
-			fibril_mutex_unlock(&instance->guard);
-			return rc;
-		}
-	}
-
-	if (instance->devices[addr].occupied) {
-		if (strict) {
-			fibril_mutex_unlock(&instance->guard);
-			return ENOENT;
-		}
-		rc = usb_bus_get_free_address(instance, &addr);
-		if (rc != EOK) {
-			fibril_mutex_unlock(&instance->guard);
-			return rc;
-		}
-	}
-	if (usb_address_is_valid(addr)) {
-		assert(instance->devices[addr].occupied == false);
-		assert(addr != USB_ADDRESS_DEFAULT || strict);
-
-		instance->devices[addr].occupied = true;
-		instance->devices[addr].speed = speed;
-		*address = addr;
-		addr = 0;
-	}
-
-	fibril_mutex_unlock(&instance->guard);
-
-	*address = addr;
-	return EOK;
-}
-
-/** Get speed assigned to USB address.
- *
- * @param[in] instance Device manager structure to use.
- * @param[in] address Address the caller wants to find.
- * @param[out] speed Assigned speed.
- * @return Error code.
- */
-errno_t usb_bus_get_speed(usb_bus_t *instance, usb_address_t address,
-    usb_speed_t *speed)
-{
-	assert(instance);
-	if (!usb_address_is_valid(address)) {
-		return EINVAL;
-	}
-
-	fibril_mutex_lock(&instance->guard);
-
-	const errno_t ret = instance->devices[address].occupied ? EOK : ENOENT;
-	if (speed && instance->devices[address].occupied) {
-		*speed = instance->devices[address].speed;
-	}
-
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
-}
-/**
- * @}
- */
Index: uspace/lib/usbhost/src/usb_transfer_batch.c
===================================================================
--- uspace/lib/usbhost/src/usb_transfer_batch.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbhost/src/usb_transfer_batch.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -33,105 +33,171 @@
  */
 
-#include <usb/host/usb_transfer_batch.h>
-#include <usb/debug.h>
-
 #include <assert.h>
 #include <errno.h>
-#include <macros.h>
-#include <mem.h>
 #include <stdlib.h>
-#include <usbhc_iface.h>
-
-/** Allocate and initialize usb_transfer_batch structure.
- * @param ep endpoint used by the transfer batch.
- * @param buffer data to send/recieve.
- * @param buffer_size Size of data buffer.
- * @param setup_buffer Data to send in SETUP stage of control transfer.
- * @param func_in callback on IN transfer completion.
- * @param func_out callback on OUT transfer completion.
- * @param fun DDF function (passed to callback function).
- * @param arg Argument to pass to the callback function.
- * @param private_data driver specific per batch data.
- * @param private_data_dtor Function to properly destroy private_data.
- * @return Pointer to valid usb_transfer_batch_t structure, NULL on failure.
- */
-usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep, char *buffer,
-    size_t buffer_size,
-    uint64_t setup_buffer,
-    usbhc_iface_transfer_in_callback_t func_in,
-    usbhc_iface_transfer_out_callback_t func_out,
-    void *arg)
-{
-	if (func_in == NULL && func_out == NULL)
-		return NULL;
-	if (func_in != NULL && func_out != NULL)
-		return NULL;
-
-	usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
-	if (instance) {
-		instance->ep = ep;
-		instance->callback_in = func_in;
-		instance->callback_out = func_out;
-		instance->arg = arg;
-		instance->buffer = buffer;
-		instance->buffer_size = buffer_size;
-		instance->setup_size = 0;
-		instance->transfered_size = 0;
-		instance->error = EOK;
-		if (ep && ep->transfer_type == USB_TRANSFER_CONTROL) {
-			memcpy(instance->setup_buffer, &setup_buffer,
-			    USB_SETUP_PACKET_SIZE);
-			instance->setup_size = USB_SETUP_PACKET_SIZE;
+#include <str_error.h>
+#include <usb/debug.h>
+
+#include "endpoint.h"
+#include "bus.h"
+
+#include "usb_transfer_batch.h"
+
+/**
+ * Create a batch on a given endpoint.
+ *
+ * If the bus callback is not defined, it just creates a default batch.
+ */
+usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep)
+{
+	assert(ep);
+
+	bus_t *bus = endpoint_get_bus(ep);
+
+	if (!bus->ops->batch_create) {
+		usb_transfer_batch_t *batch = calloc(1, sizeof(usb_transfer_batch_t));
+		if (!batch)
+			return NULL;
+		usb_transfer_batch_init(batch, ep);
+		return batch;
+	}
+
+	return bus->ops->batch_create(ep);
+}
+
+/**
+ * Initialize given batch structure.
+ */
+void usb_transfer_batch_init(usb_transfer_batch_t *batch, endpoint_t *ep)
+{
+	assert(ep);
+	/* Batch reference */
+	endpoint_add_ref(ep);
+	batch->ep = ep;
+}
+
+/**
+ * Destroy the batch. If there's no bus callback, just free it.
+ */
+void usb_transfer_batch_destroy(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+
+	bus_t *bus = endpoint_get_bus(batch->ep);
+	endpoint_t *ep = batch->ep;
+
+	if (bus->ops) {
+		usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " destroying.",
+		    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+		bus->ops->batch_destroy(batch);
+	}
+	else {
+		usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.",
+		    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+		free(batch);
+	}
+
+	/* Batch reference */
+	endpoint_del_ref(ep);
+}
+
+bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *batch)
+{
+	if (!batch->size)
+		return false;
+
+	unsigned flags = batch->dma_buffer.policy & DMA_POLICY_FLAGS_MASK;
+	unsigned required_flags =
+	    batch->ep->required_transfer_buffer_policy & DMA_POLICY_FLAGS_MASK;
+
+	if (required_flags & ~flags)
+		return true;
+
+	size_t chunk_mask = dma_policy_chunk_mask(batch->dma_buffer.policy);
+	size_t required_chunk_mask =
+	     dma_policy_chunk_mask(batch->ep->required_transfer_buffer_policy);
+
+	/* If the chunks are at least as large as required, we're good */
+	if ((required_chunk_mask & ~chunk_mask) == 0)
+		return false;
+
+	size_t start_chunk = batch->offset & ~chunk_mask;
+	size_t end_chunk = (batch->offset + batch->size - 1) & ~chunk_mask;
+
+	/* The requested area crosses a chunk boundary */
+	if (start_chunk != end_chunk)
+		return true;
+
+	return false;
+}
+
+errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(!batch->is_bounced);
+
+	dma_buffer_release(&batch->dma_buffer);
+
+	batch->original_buffer = batch->dma_buffer.virt + batch->offset;
+
+	usb_log_debug("Batch(%p): Buffer cannot be used directly, "
+	    "falling back to bounce buffer!", batch);
+
+	const errno_t err = dma_buffer_alloc_policy(&batch->dma_buffer,
+	    batch->size, batch->ep->transfer_buffer_policy);
+	if (err)
+		return err;
+
+	/* Copy the data out */
+	if (batch->dir == USB_DIRECTION_OUT)
+		memcpy(batch->dma_buffer.virt,
+		    batch->original_buffer,
+		    batch->size);
+
+	batch->is_bounced = true;
+	batch->offset = 0;
+
+	return err;
+}
+
+/**
+ * Finish a transfer batch: call handler, destroy batch, release endpoint.
+ *
+ * Call only after the batch have been scheduled && completed!
+ */
+void usb_transfer_batch_finish(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+
+	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.",
+	    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+
+	if (batch->error == EOK && batch->size > 0) {
+		if (batch->is_bounced) {
+			/* We we're forced to use bounce buffer, copy it back */
+			if (batch->dir == USB_DIRECTION_IN)
+			memcpy(batch->original_buffer,
+			    batch->dma_buffer.virt,
+			    batch->transferred_size);
+
+			dma_buffer_free(&batch->dma_buffer);
 		}
-		if (instance->ep)
-			endpoint_use(instance->ep);
-	}
-	return instance;
-}
-
-/** Correctly dispose all used data structures.
- *
- * @param[in] instance Batch structure to use.
- */
-void usb_transfer_batch_destroy(usb_transfer_batch_t *instance)
-{
-	if (!instance)
-		return;
-	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.\n",
-	    instance, USB_TRANSFER_BATCH_ARGS(*instance));
-	if (instance->ep) {
-		endpoint_release(instance->ep);
-	}
-	free(instance);
-}
-
-/** Prepare data and call the right callback.
- *
- * @param[in] instance Batch structure to use.
- * @param[in] data Data to copy to the output buffer.
- * @param[in] size Size of @p data.
- * @param[in] error Error value to use.
- */
-void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
-    const void *data, size_t size, errno_t error)
-{
-	assert(instance);
-	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.\n",
-	    instance, USB_TRANSFER_BATCH_ARGS(*instance));
-
-	/* NOTE: Only one of these pointers should be set. */
-        if (instance->callback_out) {
-		instance->callback_out(error, instance->arg);
-	}
-
-        if (instance->callback_in) {
-		/* We care about the data and there are some to copy */
-		const size_t safe_size = min(size, instance->buffer_size);
-		if (data) {
-	                memcpy(instance->buffer, data, safe_size);
+		else {
+			dma_buffer_release(&batch->dma_buffer);
 		}
-		instance->callback_in(error, safe_size, instance->arg);
-	}
-}
+	}
+
+	if (batch->on_complete) {
+		const int err = batch->on_complete(batch->on_complete_data, batch->error, batch->transferred_size);
+		if (err)
+			usb_log_warning("Batch %p failed to complete: %s",
+			    batch, str_error(err));
+	}
+
+	usb_transfer_batch_destroy(batch);
+}
+
 /**
  * @}
Index: uspace/lib/usbhost/src/utility.c
===================================================================
--- uspace/lib/usbhost/src/utility.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
+++ uspace/lib/usbhost/src/utility.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -0,0 +1,386 @@
+/*
+ * 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 <macros.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/descriptor.h>
+#include <usb/request.h>
+
+#include "ddf_helpers.h"
+#include "utility.h"
+
+/**
+ * Get initial max packet size for the control endpoint 0.
+ *
+ * For LS, HS, and SS devices this value is final and fixed.
+ * For FS devices, the default value of 8 is returned. The caller needs
+ * to fetch the first 8B of the device descriptor later in the initialization
+ * process and determine whether it should be increased.
+ *
+ * @return Max packet size for EP 0 (in bytes)
+ */
+uint16_t hc_get_ep0_initial_mps(usb_speed_t speed)
+{
+	static const uint16_t mps_fixed [] = {
+		[USB_SPEED_LOW] = 8,
+		[USB_SPEED_HIGH] = 64,
+		[USB_SPEED_SUPER] = 512,
+	};
+
+	if (speed < ARRAY_SIZE(mps_fixed) && mps_fixed[speed] != 0) {
+		return mps_fixed[speed];
+	}
+	return 8; // USB_SPEED_FULL default
+}
+
+/**
+ * Get max packet size for the control endpoint 0.
+ *
+ * For LS, HS, and SS devices the corresponding fixed value is obtained.
+ * For FS devices the first 8B of the device descriptor are fetched to
+ * determine it.
+ *
+ * @return Max packet size for EP 0 (in bytes)
+ */
+int hc_get_ep0_max_packet_size(uint16_t *mps, device_t *dev)
+{
+	assert(mps);
+
+	*mps = hc_get_ep0_initial_mps(dev->speed);
+	if (dev->speed != USB_SPEED_FULL) {
+		return EOK;
+	}
+
+	const usb_target_t control_ep = {{
+		.address = dev->address,
+		.endpoint = 0,
+	}};
+
+	usb_standard_device_descriptor_t desc = { 0 };
+	const usb_device_request_setup_packet_t get_device_desc_8 =
+	    GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
+
+	usb_log_debug("Requesting first 8B of device descriptor to determine MPS.");
+	size_t got;
+	const errno_t err = bus_device_send_batch_sync(dev, control_ep,
+	    USB_DIRECTION_IN, (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE,
+	    *(uint64_t *)&get_device_desc_8,
+	    "read first 8 bytes of dev descriptor", &got);
+
+	if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
+		usb_log_error("Failed to get 8B of dev descr: %s.", str_error(err));
+		return err;
+	}
+
+	if (desc.descriptor_type != USB_DESCTYPE_DEVICE) {
+		usb_log_error("The device responded with wrong device descriptor.");
+		return EIO;
+	}
+
+	uint16_t version = uint16_usb2host(desc.usb_spec_version);
+	if (version < 0x0300) {
+		/* USB 2 and below have MPS raw in the field */
+		*mps = desc.max_packet_size;
+	} else {
+		/* USB 3 have MPS as an 2-based exponent */
+		*mps = (1 << desc.max_packet_size);
+	}
+	return EOK;
+}
+
+int hc_get_device_desc(device_t *device, usb_standard_device_descriptor_t *desc)
+{
+	const usb_target_t control_ep = {{
+		.address = device->address,
+		.endpoint = 0,
+	}};
+
+	/* Get std device descriptor */
+	const usb_device_request_setup_packet_t get_device_desc =
+	    GET_DEVICE_DESC(sizeof(*desc));
+
+	usb_log_debug("Device(%d): Requesting full device descriptor.",
+	    device->address);
+	size_t got;
+	errno_t err = bus_device_send_batch_sync(device, control_ep,
+	    USB_DIRECTION_IN, (char *) desc, sizeof(*desc),
+	    *(uint64_t *)&get_device_desc, "read device descriptor", &got);
+
+	if (!err && got != sizeof(*desc))
+		err = EOVERFLOW;
+
+	return err;
+}
+
+int hc_get_hub_desc(device_t *device, usb_hub_descriptor_header_t *desc)
+{
+	const usb_target_t control_ep = {{
+		.address = device->address,
+		.endpoint = 0,
+	}};
+
+	const usb_descriptor_type_t type = device->speed >= USB_SPEED_SUPER
+		? USB_DESCTYPE_SSPEED_HUB : USB_DESCTYPE_HUB;
+
+	const usb_device_request_setup_packet_t get_hub_desc = {
+		.request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST
+		    | (USB_REQUEST_TYPE_CLASS << 5)
+		    | USB_REQUEST_RECIPIENT_DEVICE,
+		.request = USB_DEVREQ_GET_DESCRIPTOR, \
+		.value = uint16_host2usb(type << 8), \
+		.length = sizeof(*desc),
+	};
+
+	usb_log_debug("Device(%d): Requesting hub descriptor.",
+	    device->address);
+
+	size_t got;
+	errno_t err = bus_device_send_batch_sync(device, control_ep,
+	    USB_DIRECTION_IN, (char *) desc, sizeof(*desc),
+	    *(uint64_t *)&get_hub_desc, "get hub descriptor", &got);
+
+	if (!err && got != sizeof(*desc))
+		err = EOVERFLOW;
+
+	return err;
+}
+
+int hc_device_explore(device_t *device)
+{
+	int err;
+	usb_standard_device_descriptor_t desc = { 0 };
+
+	if ((err = hc_get_device_desc(device, &desc))) {
+		usb_log_error("Device(%d): Failed to get dev descriptor: %s",
+		    device->address, str_error(err));
+		return err;
+	}
+
+	if ((err = hcd_ddf_setup_match_ids(device, &desc))) {
+		usb_log_error("Device(%d): Failed to setup match ids: %s", device->address, str_error(err));
+		return err;
+	}
+
+	return EOK;
+}
+
+/** Announce root hub to the DDF
+ *
+ * @param[in] device Host controller ddf device
+ * @return Error code
+ */
+int hc_setup_virtual_root_hub(hc_device_t *hcd, usb_speed_t rh_speed)
+{
+	int err;
+
+	assert(hcd);
+
+	device_t *dev = hcd_ddf_fun_create(hcd, rh_speed);
+	if (!dev) {
+		usb_log_error("Failed to create function for the root hub.");
+		return ENOMEM;
+	}
+
+	ddf_fun_set_name(dev->fun, "roothub");
+
+	/* Assign an address to the device */
+	if ((err = bus_device_enumerate(dev))) {
+		usb_log_error("Failed to enumerate roothub device: %s", str_error(err));
+		goto err_usb_dev;
+	}
+
+	if ((err = ddf_fun_bind(dev->fun))) {
+		usb_log_error("Failed to register roothub: %s.", str_error(err));
+		goto err_enumerated;
+	}
+
+	return EOK;
+
+err_enumerated:
+	bus_device_gone(dev);
+err_usb_dev:
+	hcd_ddf_fun_destroy(dev);
+	return err;
+}
+
+/**
+ * Check setup packet data for signs of toggle reset.
+ *
+ * @param[in] batch USB batch
+ * @param[in] reset_cb Callback to reset an endpoint
+ */
+void hc_reset_toggles(const usb_transfer_batch_t *batch, endpoint_reset_toggle_t reset_cb)
+{
+	if (batch->ep->transfer_type != USB_TRANSFER_CONTROL
+	    || batch->dir != USB_DIRECTION_OUT)
+		return;
+
+	const usb_device_request_setup_packet_t *request = &batch->setup.packet;
+	device_t * const dev = batch->ep->device;
+
+	switch (request->request)
+	{
+	/* Clear Feature ENPOINT_STALL */
+	case USB_DEVREQ_CLEAR_FEATURE: /*resets only cleared ep */
+		/* 0x2 ( HOST to device | STANDART | TO ENPOINT) */
+		if ((request->request_type == 0x2) &&
+		    (request->value == USB_FEATURE_ENDPOINT_HALT)) {
+			const uint16_t index = uint16_usb2host(request->index);
+			const usb_endpoint_t ep_num = index & 0xf;
+			const usb_direction_t dir = (index >> 7) ? USB_DIRECTION_IN : USB_DIRECTION_OUT;
+
+			endpoint_t *ep = bus_find_endpoint(dev, ep_num, dir);
+			if (ep) {
+				reset_cb(ep);
+				endpoint_del_ref(ep);
+			} else {
+				usb_log_warning("Device(%u): Resetting unregistered endpoint %u %s.", dev->address, ep_num, usb_str_direction(dir));
+			}
+		}
+		break;
+	case USB_DEVREQ_SET_CONFIGURATION:
+	case USB_DEVREQ_SET_INTERFACE:
+		/* Recipient must be device, this resets all endpoints,
+		 * In fact there should be no endpoints but EP 0 registered
+		 * as different interfaces use different endpoints,
+		 * unless you're changing configuration or alternative
+		 * interface of an already setup device. */
+		if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
+			for (usb_endpoint_t i = 0; i < 2 * USB_ENDPOINT_MAX; ++i)
+				if (dev->endpoints[i])
+					reset_cb(dev->endpoints[i]);
+		break;
+	default:
+		break;
+	}
+}
+
+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);
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usbvirt/src/ctrltransfer.c
===================================================================
--- uspace/lib/usbvirt/src/ctrltransfer.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbvirt/src/ctrltransfer.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -68,5 +68,5 @@
 		}
 
-		usb_log_debug("Control transfer: %s(%s)\n", handler->name,
+		usb_log_debug("Control transfer: %s(%s)", handler->name,
 		    usb_debug_str_buffer((uint8_t*) setup, sizeof(*setup), 0));
 		errno_t rc = handler->callback(dev, setup, data, data_sent_size);
Index: uspace/lib/usbvirt/src/ipc_hc.c
===================================================================
--- uspace/lib/usbvirt/src/ipc_hc.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbvirt/src/ipc_hc.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -50,5 +50,5 @@
  * @param data_buffer Data buffer (DATA stage of control transfer).
  * @param data_buffer_size Size of data buffer in bytes.
- * @param data_transfered_size Number of actually transferred bytes.
+ * @param data_transferred_size Number of actually transferred bytes.
  *
  * @return Error code.
@@ -57,5 +57,5 @@
 errno_t usbvirt_ipc_send_control_read(async_sess_t *sess, void *setup_buffer,
     size_t setup_buffer_size, void *data_buffer, size_t data_buffer_size,
-    size_t *data_transfered_size)
+    size_t *data_transferred_size)
 {
 	if (!sess)
@@ -111,6 +111,6 @@
 		return (errno_t) opening_request_rc;
 	
-	if (data_transfered_size != NULL)
-		*data_transfered_size = IPC_GET_ARG2(data_request_call);
+	if (data_transferred_size != NULL)
+		*data_transferred_size = IPC_GET_ARG2(data_request_call);
 	
 	return EOK;
Index: uspace/lib/usbvirt/src/transfer.c
===================================================================
--- uspace/lib/usbvirt/src/transfer.c	(revision a211838659e5f36770cab316785a73bb49c1fca5)
+++ uspace/lib/usbvirt/src/transfer.c	(revision 5ef3afdf4e5aa4f84ed956962e5facd43397a2aa)
@@ -79,5 +79,5 @@
 
 	if (rc == EFORWARD) {
-		usb_log_warning("Control transfer {%s (%s)} not handled.\n",
+		usb_log_warning("Control transfer {%s (%s)} not handled.",
 		    usb_debug_str_buffer(setup, setup_size, 10),
 		    setup_packet->request_type & 0x80
