Index: uspace/drv/bus/usb/ehci/Makefile
===================================================================
--- uspace/drv/bus/usb/ehci/Makefile	(revision 3f2cb177bf701fa37a3f0251f87d80699c618e2f)
+++ uspace/drv/bus/usb/ehci/Makefile	(revision 6602e97a7ebe60de7d5763ff25ddd5637d6bc950)
@@ -51,4 +51,5 @@
 	hc.c \
 	hw_struct/queue_head.c \
+	hw_struct/transfer_descriptor.c \
 	main.c \
 	res.c
Index: uspace/drv/bus/usb/ehci/ehci_batch.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 3f2cb177bf701fa37a3f0251f87d80699c618e2f)
+++ uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 6602e97a7ebe60de7d5763ff25ddd5637d6bc950)
@@ -46,4 +46,10 @@
 #include "utils/malloc32.h"
 
+/* The buffer pointer list in the qTD is long enough to support a maximum
+ * transfer size of 20K bytes. This case occurs when all five buffer pointers
+ * are used and the first offset is zero. A qTD handles a 16Kbyte buffer
+ * with any starting buffer alignment. EHCI specs p. 87 (pdf p. 97) */
+#define EHCI_TD_MAX_TRANSFER   (16 * 1024)
+
 static void (*const batch_setup[])(ehci_transfer_batch_t*, usb_direction_t);
 
@@ -60,5 +66,5 @@
 		    ehci_endpoint_get(ehci_batch->usb_batch->ep);
 		assert(ehci_ep);
-		for (unsigned i = 0; i < ehci_batch->td_count; ++i) {
+		for (size_t i = 0; i < ehci_batch->td_count; ++i) {
 			if (ehci_batch->tds[i] != ehci_ep->td)
 				free32(ehci_batch->tds[i]);
@@ -105,7 +111,8 @@
 	}
 	link_initialize(&ehci_batch->link);
-//	ehci_batch->td_count =
-//	    (usb_batch->buffer_size + EHCI_TD_MAX_TRANSFER - 1)
-//	    / EHCI_TD_MAX_TRANSFER;
+	ehci_batch->td_count =
+	    (usb_batch->buffer_size + EHCI_TD_MAX_TRANSFER - 1)
+	    / EHCI_TD_MAX_TRANSFER;
+
 	/* Control transfer need Setup and Status stage */
 	if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
@@ -113,6 +120,5 @@
 	}
 
-	/* We need an extra place for TD that was left at ED */
-	ehci_batch->tds = calloc(ehci_batch->td_count + 1, sizeof(td_t*));
+	ehci_batch->tds = calloc(ehci_batch->td_count, sizeof(td_t*));
 	if (!ehci_batch->tds) {
 		usb_log_error("Failed to allocate EHCI transfer descriptors.");
@@ -122,7 +128,6 @@
 	/* Add TD left over by the previous transfer */
 	ehci_batch->qh = ehci_endpoint_get(usb_batch->ep)->qh;
-	ehci_batch->tds[0] = ehci_endpoint_get(usb_batch->ep)->td;
-
-	for (unsigned i = 1; i <= ehci_batch->td_count; ++i) {
+
+	for (unsigned i = 0; i < ehci_batch->td_count; ++i) {
 		ehci_batch->tds[i] = malloc32(sizeof(td_t));
 		if (!ehci_batch->tds[i]) {
@@ -133,8 +138,5 @@
 
 
-	/* NOTE: EHCI is capable of handling buffer that crosses page boundaries
-	 * it is, however, not capable of handling buffer that occupies more
-	 * than two pages (the first page is computed using start pointer, the
-	 * other using the end pointer) */
+	/* Mix setup stage and data together, we have enough space */
         if (usb_batch->setup_size + usb_batch->buffer_size > 0) {
 		/* Use one buffer for setup and data stage */
@@ -182,10 +184,11 @@
 	usb_log_debug("Batch %p checking %zu td(s) for completion.\n",
 	    ehci_batch->usb_batch, ehci_batch->td_count);
-#if 0
-	usb_log_debug2("ED: %08x:%08x:%08x:%08x.\n",
-	    ehci_batch->ed->status, ehci_batch->ed->td_head,
-	    ehci_batch->ed->td_tail, ehci_batch->ed->next);
-
-	if (!ed_inactive(ehci_batch->ed) && ed_transfer_pending(ehci_batch->ed))
+
+	usb_log_debug2("QH: %08x:%08x:%08x:%08x:%08x:%08x.\n",
+	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
+	    ehci_batch->qh->status, ehci_batch->qh->current,
+	    ehci_batch->qh->next, ehci_batch->qh->alternate);
+
+	if (!qh_halted(ehci_batch->qh) && qh_transfer_pending(ehci_batch->qh))
 		return false;
 
@@ -197,14 +200,11 @@
 	    ehci_batch->usb_batch->buffer_size;
 
-	/* Assume we will leave the last(unused) TD behind */
-	unsigned leave_td = ehci_batch->td_count;
-
 	/* Check all TDs */
 	for (size_t i = 0; i < ehci_batch->td_count; ++i) {
 		assert(ehci_batch->tds[i] != NULL);
-		usb_log_debug("TD %zu: %08x:%08x:%08x:%08x.\n", i,
-		    ehci_batch->tds[i]->status, ehci_batch->tds[i]->cbp,
-		    ehci_batch->tds[i]->next, ehci_batch->tds[i]->be);
-
+		usb_log_debug("TD %zu: %08x:%08x:%08x.", i,
+		    ehci_batch->tds[i]->status, ehci_batch->tds[i]->next,
+		    ehci_batch->tds[i]->alternate);
+#if 0
 		ehci_batch->usb_batch->error = td_error(ehci_batch->tds[i]);
 		if (ehci_batch->usb_batch->error == EOK) {
@@ -251,8 +251,10 @@
 			break;
 		}
-	}
+#endif
+	}
+
 	assert(ehci_batch->usb_batch->transfered_size <=
 	    ehci_batch->usb_batch->buffer_size);
-
+#if 0
 	/* Store the remaining TD */
 	ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ehci_batch->usb_batch->ep);
@@ -291,8 +293,9 @@
 	assert(ehci_batch->usb_batch);
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
-#if 0
-	usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.\n", ehci_batch->ed,
-	    ehci_batch->ed->status, ehci_batch->ed->td_tail,
-	    ehci_batch->ed->td_head, ehci_batch->ed->next);
+
+	usb_log_debug2("Control QH: %08x:%08x:%08x:%08x:%08x:%08x",
+	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
+	    ehci_batch->qh->status, ehci_batch->qh->current,
+	    ehci_batch->qh->next, ehci_batch->qh->alternate);
 	static const usb_direction_t reverse_dir[] = {
 		[USB_DIRECTION_IN]  = USB_DIRECTION_OUT,
@@ -309,7 +312,7 @@
 	    ehci_batch->tds[0], ehci_batch->tds[1], USB_DIRECTION_BOTH,
 	    buffer, ehci_batch->usb_batch->setup_size, toggle);
-	usb_log_debug("Created CONTROL SETUP TD: %08x:%08x:%08x:%08x.\n",
-	    ehci_batch->tds[0]->status, ehci_batch->tds[0]->cbp,
-	    ehci_batch->tds[0]->next, ehci_batch->tds[0]->be);
+	usb_log_debug("Created CONTROL SETUP TD: %08x:%08x:%08x",
+	    ehci_batch->tds[0]->status, ehci_batch->tds[0]->next,
+	    ehci_batch->tds[0]->alternate);
 	buffer += ehci_batch->usb_batch->setup_size;
 
@@ -325,9 +328,8 @@
 		    ehci_batch->tds[td_current + 1],
 		    data_dir, buffer, transfer_size, toggle);
-		usb_log_debug("Created CONTROL DATA TD: %08x:%08x:%08x:%08x.\n",
+		usb_log_debug("Created CONTROL DATA TD: %08x:%08x:%08x",
 		    ehci_batch->tds[td_current]->status,
-		    ehci_batch->tds[td_current]->cbp,
 		    ehci_batch->tds[td_current]->next,
-		    ehci_batch->tds[td_current]->be);
+		    ehci_batch->tds[td_current]->alternate);
 
 		buffer += transfer_size;
@@ -341,10 +343,9 @@
 	td_init(ehci_batch->tds[td_current], ehci_batch->tds[td_current + 1],
 	    status_dir, NULL, 0, 1);
-	usb_log_debug("Created CONTROL STATUS TD: %08x:%08x:%08x:%08x.\n",
+	usb_log_debug("Created CONTROL STATUS TD: %08x:%08x:%08x",
 	    ehci_batch->tds[td_current]->status,
-	    ehci_batch->tds[td_current]->cbp,
 	    ehci_batch->tds[td_current]->next,
-	    ehci_batch->tds[td_current]->be);
-#endif
+	    ehci_batch->tds[td_current]->alternate);
+
 	usb_log_debug2(
 	    "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.\n", \
@@ -368,8 +369,9 @@
 	assert(ehci_batch->usb_batch);
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
-#if 0
-	usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.\n", ehci_batch->ed,
-	    ehci_batch->ed->status, ehci_batch->ed->td_tail,
-	    ehci_batch->ed->td_head, ehci_batch->ed->next);
+
+	usb_log_debug2("Control QH: %08x:%08x:%08x:%08x:%08x:%08x",
+	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
+	    ehci_batch->qh->status, ehci_batch->qh->current,
+	    ehci_batch->qh->next, ehci_batch->qh->alternate);
 
 	size_t td_current = 0;
@@ -384,9 +386,8 @@
 		    dir, buffer, transfer_size, -1);
 
-		usb_log_debug("Created DATA TD: %08x:%08x:%08x:%08x.\n",
+		usb_log_debug("Created DATA TD: %08x:%08x:%08x",
 		    ehci_batch->tds[td_current]->status,
-		    ehci_batch->tds[td_current]->cbp,
 		    ehci_batch->tds[td_current]->next,
-		    ehci_batch->tds[td_current]->be);
+		    ehci_batch->tds[td_current]->alternate);
 
 		buffer += transfer_size;
@@ -395,7 +396,7 @@
 		++td_current;
 	}
-#endif
+
 	usb_log_debug2(
-	    "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.\n", \
+	    "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized",
 	    ehci_batch->usb_batch,
 	    usb_str_transfer_type(ehci_batch->usb_batch->ep->transfer_type),
Index: uspace/drv/bus/usb/ehci/hw_struct/queue_head.h
===================================================================
--- uspace/drv/bus/usb/ehci/hw_struct/queue_head.h	(revision 3f2cb177bf701fa37a3f0251f87d80699c618e2f)
+++ uspace/drv/bus/usb/ehci/hw_struct/queue_head.h	(revision 6602e97a7ebe60de7d5763ff25ddd5637d6bc950)
@@ -178,4 +178,22 @@
 }
 
+static inline bool qh_halted(const qh_t *qh)
+{
+	assert(qh);
+	return (EHCI_MEM32_RD(qh->status) & QH_STATUS_HALTED_FLAG);
+}
+
+static inline void qh_halt(qh_t *qh)
+{
+	assert(qh);
+	EHCI_MEM32_SET(qh->status, QH_STATUS_HALTED_FLAG);
+}
+
+static inline bool qh_transfer_pending(const qh_t *qh)
+{
+	assert(qh);
+	return !(EHCI_MEM32_RD(qh->next) & LINK_POINTER_TERMINATE_FLAG);
+}
+
 
 void qh_init(qh_t *instance, const endpoint_t *ep);
Index: uspace/drv/bus/usb/ehci/hw_struct/transfer_descriptor.c
===================================================================
--- uspace/drv/bus/usb/ehci/hw_struct/transfer_descriptor.c	(revision 6602e97a7ebe60de7d5763ff25ddd5637d6bc950)
+++ uspace/drv/bus/usb/ehci/hw_struct/transfer_descriptor.c	(revision 6602e97a7ebe60de7d5763ff25ddd5637d6bc950)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2014 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 drvusbehci
+ * @{
+ */
+/** @file
+ * @brief EHCI driver
+ */
+
+#include <assert.h>
+#include <macros.h>
+#include <mem.h>
+
+#include <usb/usb.h>
+
+#include "../utils/malloc32.h"
+//#include "completion_codes.h"
+#include "mem_access.h"
+#include "transfer_descriptor.h"
+
+/** USB direction to EHCI TD values translation table */
+static const uint32_t dir[] = {
+	[USB_DIRECTION_IN] = TD_STATUS_PID_IN,
+	[USB_DIRECTION_OUT] = TD_STATUS_PID_OUT,
+	[USB_DIRECTION_BOTH] = TD_STATUS_PID_SETUP,
+};
+
+/**
+ * Initialize EHCI TD.
+ * @param instance TD structure to initialize.
+ * @param next Next TD in ED list.
+ * @param direction Used to determine PID, BOTH means setup PID.
+ * @param buffer Pointer to the first byte of transferred data.
+ * @param size Size of the buffer.
+ * @param toggle Toggle bit value, use 0/1 to set explicitly,
+ *        any other value means that ED toggle will be used.
+ */
+void td_init(td_t *instance, const td_t *next,
+    usb_direction_t direction, const void *buffer, size_t size, int toggle)
+{
+	assert(instance);
+	memset(instance, 0, sizeof(td_t));
+	/* Set PID and Total size */
+	assert((size & TD_STATUS_TOTAL_MASK) == size);
+	EHCI_MEM32_WR(instance->status,
+	    ((dir[direction] & TD_STATUS_PID_MASK) << TD_STATUS_PID_SHIFT) |
+	    ((size & TD_STATUS_TOTAL_MASK) << TD_STATUS_TOTAL_SHIFT));
+
+	if (toggle == 0 || toggle == 1) {
+		EHCI_MEM32_SET(instance->status,
+		    toggle ? TD_STATUS_TOGGLE_FLAG : 0);
+	}
+
+	if (buffer != NULL) {
+		assert(size != 0);
+		for (unsigned i = 0; (i < ARRAY_SIZE(instance->buffer_pointer))
+		    && size; ++i) {
+			const uintptr_t page =
+			    (addr_to_phys(buffer) & TD_BUFFER_POINTER_MASK);
+			const size_t offset =
+			    ((uintptr_t)buffer & TD_BUFFER_POINTER_OFFSET_MASK);
+			assert(offset == 0 || i == 0);
+			size -= min((4096 - offset), size);
+			EHCI_MEM32_WR(instance->buffer_pointer[i],
+			    page | offset);
+		}
+	}
+
+	EHCI_MEM32_WR(instance->next, LINK_POINTER_TD(addr_to_phys(next)));
+}
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/ehci/hw_struct/transfer_descriptor.h
===================================================================
--- uspace/drv/bus/usb/ehci/hw_struct/transfer_descriptor.h	(revision 3f2cb177bf701fa37a3f0251f87d80699c618e2f)
+++ uspace/drv/bus/usb/ehci/hw_struct/transfer_descriptor.h	(revision 6602e97a7ebe60de7d5763ff25ddd5637d6bc950)
@@ -67,10 +67,13 @@
 
 	volatile uint32_t buffer_pointer[5];
-#define SITD_BUFFER_POINTER_MASK   0xfffff000
+#define TD_BUFFER_POINTER_MASK   0xfffff000
 /* Only the first page pointer */
-#define SITD_BUFFER_POINTER_CURRENT_MASK    0xfff
-#define SITD_BUFFER_POINTER_CURRENT_SHIFT   0
+#define TD_BUFFER_POINTER_OFFSET_MASK    0xfff
 
 } td_t;
+
+void td_init(td_t *td, const td_t *next, usb_direction_t dir, const void * buf,
+    size_t buf_size, int toggle);
+
 #endif
 /**
