Index: uspace/drv/ohci/Makefile
===================================================================
--- uspace/drv/ohci/Makefile	(revision 6bec59bee36bbb7344227073a8e47f3537a0b284)
+++ uspace/drv/ohci/Makefile	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
@@ -33,11 +33,13 @@
 
 SOURCES = \
+	batch.c \
+	hc.c \
 	iface.c \
-	batch.c \
 	main.c \
-	hc.c \
 	ohci.c \
+	pci.c \
 	root_hub.c \
-	pci.c
+	transfer_list.c
+
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/ohci/batch.c
===================================================================
--- uspace/drv/ohci/batch.c	(revision 6bec59bee36bbb7344227073a8e47f3537a0b284)
+++ uspace/drv/ohci/batch.c	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
@@ -93,4 +93,10 @@
 }
 /*----------------------------------------------------------------------------*/
+bool batch_is_complete(usb_transfer_batch_t *instance)
+{
+	// TODO: implement
+	return false;
+}
+/*----------------------------------------------------------------------------*/
 void batch_control_write(usb_transfer_batch_t *instance)
 {
@@ -151,4 +157,9 @@
 }
 /*----------------------------------------------------------------------------*/
+ed_t * batch_ed(usb_transfer_batch_t *instance)
+{
+	return NULL;
+}
+/*----------------------------------------------------------------------------*/
 /** Helper function calls callback and correctly disposes of batch structure.
  *
Index: uspace/drv/ohci/batch.h
===================================================================
--- uspace/drv/ohci/batch.h	(revision 6bec59bee36bbb7344227073a8e47f3537a0b284)
+++ uspace/drv/ohci/batch.h	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
@@ -41,4 +41,5 @@
 #include <usb/host/batch.h>
 
+#include "hw_struct/endpoint_descriptor.h"
 
 usb_transfer_batch_t * batch_get(
@@ -65,4 +66,5 @@
 void batch_bulk_out(usb_transfer_batch_t *instance);
 
+ed_t * batch_ed(usb_transfer_batch_t *instance);
 #endif
 /**
Index: uspace/drv/ohci/hw_struct/endpoint_descriptor.h
===================================================================
--- uspace/drv/ohci/hw_struct/endpoint_descriptor.h	(revision 6bec59bee36bbb7344227073a8e47f3537a0b284)
+++ uspace/drv/ohci/hw_struct/endpoint_descriptor.h	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
@@ -35,5 +35,8 @@
 #define DRV_OHCI_HW_STRUCT_ENDPOINT_DESCRIPTOR_H
 
+#include <assert.h>
 #include <stdint.h>
+
+#include "utils/malloc32.h"
 
 #include "completion_codes.h"
@@ -71,4 +74,21 @@
 #define ED_NEXT_PTR_SHIFT (0)
 } __attribute__((packed)) ed_t;
+
+static inline void ed_init_dummy(ed_t *instance)
+{
+	assert(instance);
+	bzero(instance, sizeof(ed_t));
+	instance->status |= ED_STATUS_K_FLAG;
+}
+
+static inline void ed_append_ed(ed_t *instance, ed_t *next)
+{
+	assert(instance);
+	assert(next);
+	uint32_t pa = addr_to_phys(next);
+	assert((pa & ED_NEXT_PTR_MASK) << ED_NEXT_PTR_SHIFT == pa);
+	instance->next = pa;
+}
+
 #endif
 /**
Index: uspace/drv/ohci/transfer_list.c
===================================================================
--- uspace/drv/ohci/transfer_list.c	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
+++ uspace/drv/ohci/transfer_list.c	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
@@ -0,0 +1,219 @@
+/*
+ * 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 drvusbohci
+ * @{
+ */
+/** @file
+ * @brief OHCI driver transfer list implementation
+ */
+#include <errno.h>
+#include <usb/debug.h>
+
+#include "transfer_list.h"
+
+static void transfer_list_remove_batch(
+    transfer_list_t *instance, usb_transfer_batch_t *batch);
+/*----------------------------------------------------------------------------*/
+/** Initialize transfer list structures.
+ *
+ * @param[in] instance Memory place to use.
+ * @param[in] name Name of the new list.
+ * @return Error code
+ *
+ * Allocates memory for internal qh_t structure.
+ */
+int transfer_list_init(transfer_list_t *instance, const char *name)
+{
+	assert(instance);
+	instance->name = name;
+	instance->list_head = malloc32(sizeof(ed_t));
+	if (!instance->list_head) {
+		usb_log_error("Failed to allocate list head.\n");
+		return ENOMEM;
+	}
+	instance->list_head_pa = addr_to_phys(instance->list_head);
+	usb_log_debug2("Transfer list %s setup with ED: %p(%p).\n",
+	    name, instance->list_head, instance->list_head_pa);
+
+	ed_init_dummy(instance->list_head);
+	list_initialize(&instance->batch_list);
+	fibril_mutex_initialize(&instance->guard);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+/** Set the next list in transfer list chain.
+ *
+ * @param[in] instance List to lead.
+ * @param[in] next List to append.
+ * @return Error code
+ *
+ * Does not check whether this replaces an existing list .
+ */
+void transfer_list_set_next(transfer_list_t *instance, transfer_list_t *next)
+{
+	assert(instance);
+	assert(next);
+	/* Set both queue_head.next to point to the follower */
+	ed_append_ed(instance->list_head, next->list_head);
+}
+/*----------------------------------------------------------------------------*/
+/** Submit transfer batch to the list and queue.
+ *
+ * @param[in] instance List to use.
+ * @param[in] batch Transfer batch to submit.
+ * @return Error code
+ *
+ * The batch is added to the end of the list and queue.
+ */
+void transfer_list_add_batch(
+    transfer_list_t *instance, usb_transfer_batch_t *batch)
+{
+	assert(instance);
+	assert(batch);
+	usb_log_debug2("Queue %s: Adding batch(%p).\n", instance->name, batch);
+
+	fibril_mutex_lock(&instance->guard);
+
+	ed_t *last_ed = NULL;
+	/* Add to the hardware queue. */
+	if (list_empty(&instance->batch_list)) {
+		/* There is nothing scheduled */
+		last_ed = instance->list_head;
+	} else {
+		/* There is something scheduled */
+		usb_transfer_batch_t *last = list_get_instance(
+		    instance->batch_list.prev, usb_transfer_batch_t, link);
+		last_ed = batch_ed(last);
+	}
+	/* keep link */
+	batch_ed(batch)->next = last_ed->next;
+	ed_append_ed(last_ed, batch_ed(batch));
+
+	asm volatile ("": : :"memory");
+
+	/* Add to the driver list */
+	list_append(&batch->link, &instance->batch_list);
+
+	usb_transfer_batch_t *first = list_get_instance(
+	    instance->batch_list.next, usb_transfer_batch_t, link);
+	usb_log_debug("Batch(%p) added to queue %s, first is %p.\n",
+		batch, instance->name, first);
+	fibril_mutex_unlock(&instance->guard);
+}
+/*----------------------------------------------------------------------------*/
+/** Create list for finished batches.
+ *
+ * @param[in] instance List to use.
+ * @param[in] done list to fill
+ */
+void transfer_list_remove_finished(transfer_list_t *instance, link_t *done)
+{
+	assert(instance);
+	assert(done);
+
+	fibril_mutex_lock(&instance->guard);
+	link_t *current = instance->batch_list.next;
+	while (current != &instance->batch_list) {
+		link_t *next = current->next;
+		usb_transfer_batch_t *batch =
+		    list_get_instance(current, usb_transfer_batch_t, link);
+
+		if (batch_is_complete(batch)) {
+			/* Save for post-processing */
+			transfer_list_remove_batch(instance, batch);
+			list_append(current, done);
+		}
+		current = next;
+	}
+	fibril_mutex_unlock(&instance->guard);
+}
+/*----------------------------------------------------------------------------*/
+/** Walk the list and abort all batches.
+ *
+ * @param[in] instance List to use.
+ */
+void transfer_list_abort_all(transfer_list_t *instance)
+{
+	fibril_mutex_lock(&instance->guard);
+	while (!list_empty(&instance->batch_list)) {
+		link_t *current = instance->batch_list.next;
+		usb_transfer_batch_t *batch =
+		    list_get_instance(current, usb_transfer_batch_t, link);
+		transfer_list_remove_batch(instance, batch);
+		usb_transfer_batch_finish_error(batch, EIO);
+	}
+	fibril_mutex_unlock(&instance->guard);
+}
+/*----------------------------------------------------------------------------*/
+/** Remove a transfer batch from the list and queue.
+ *
+ * @param[in] instance List to use.
+ * @param[in] batch Transfer batch to remove.
+ * @return Error code
+ *
+ * Does not lock the transfer list, caller is responsible for that.
+ */
+void transfer_list_remove_batch(
+    transfer_list_t *instance, usb_transfer_batch_t *batch)
+{
+	assert(instance);
+	assert(instance->list_head);
+	assert(batch);
+	assert(batch_ed(batch));
+	assert(fibril_mutex_is_locked(&instance->guard));
+
+	usb_log_debug2(
+	    "Queue %s: removing batch(%p).\n", instance->name, batch);
+
+	const char *qpos = NULL;
+	/* Remove from the hardware queue */
+	if (instance->batch_list.next == &batch->link) {
+		/* I'm the first one here */
+		assert((instance->list_head->next & ED_NEXT_PTR_MASK)
+		    == addr_to_phys(batch_ed(batch)));
+		instance->list_head->next = batch_ed(batch)->next;
+		qpos = "FIRST";
+	} else {
+		usb_transfer_batch_t *prev =
+		    list_get_instance(
+		        batch->link.prev, usb_transfer_batch_t, link);
+		assert((batch_ed(prev)->next & ED_NEXT_PTR_MASK)
+		    == addr_to_phys(batch_ed(batch)));
+		batch_ed(prev)->next = batch_ed(batch)->next;
+		qpos = "NOT FIRST";
+	}
+	asm volatile ("": : :"memory");
+	usb_log_debug("Batch(%p) removed (%s) from %s, next %x.\n",
+	    batch, qpos, instance->name, batch_ed(batch)->next);
+
+	/* Remove from the batch list */
+	list_remove(&batch->link);
+}
+/**
+ * @}
+ */
Index: uspace/drv/ohci/transfer_list.h
===================================================================
--- uspace/drv/ohci/transfer_list.h	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
+++ uspace/drv/ohci/transfer_list.h	(revision f98b8269484d60177e1e59042e7ea84533bfeb61)
@@ -0,0 +1,76 @@
+/*
+ * 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 drvusbohci
+ * @{
+ */
+/** @file
+ * @brief OHCI driver transfer list structure
+ */
+#ifndef DRV_OHCI_TRANSFER_LIST_H
+#define DRV_OHCI_TRANSFER_LIST_H
+
+#include <fibril_synch.h>
+
+#include "batch.h"
+#include "hw_struct/endpoint_descriptor.h"
+#include "utils/malloc32.h"
+
+typedef struct transfer_list
+{
+	fibril_mutex_t guard;
+	ed_t *list_head;
+	uint32_t list_head_pa;
+	const char *name;
+	link_t batch_list;
+} transfer_list_t;
+
+/** Dispose transfer list structures.
+ *
+ * @param[in] instance Memory place to use.
+ *
+ * Frees memory for internal qh_t structure.
+ */
+static inline void transfer_list_fini(transfer_list_t *instance)
+{
+	assert(instance);
+	free32(instance->list_head);
+}
+
+int transfer_list_init(transfer_list_t *instance, const char *name);
+
+void transfer_list_set_next(transfer_list_t *instance, transfer_list_t *next);
+
+void transfer_list_add_batch(transfer_list_t *instance, usb_transfer_batch_t *batch);
+
+void transfer_list_remove_finished(transfer_list_t *instance, link_t *done);
+
+void transfer_list_abort_all(transfer_list_t *instance);
+#endif
+/**
+ * @}
+ */
