Index: uspace/drv/uhci-hcd/batch.c
===================================================================
--- uspace/drv/uhci-hcd/batch.c	(revision 7ac73a4b0be58a18822a76290c8e2578cbc50448)
+++ uspace/drv/uhci-hcd/batch.c	(revision 00db345ac0b5fb5841cf7eaac2d0a1eba5487690)
@@ -47,6 +47,7 @@
 static int batch_schedule(batch_t *instance);
 
-static void batch_control(
-    batch_t *instance, int data_stage, int status_stage);
+static void batch_control(batch_t *instance,
+    usb_packet_id data_stage, usb_packet_id status_stage);
+static void batch_data(batch_t *instance, usb_packet_id pid);
 static void batch_call_in(batch_t *instance);
 static void batch_call_out(batch_t *instance);
@@ -83,5 +84,5 @@
 	}
 
-	instance->tds = malloc32(sizeof(transfer_descriptor_t) * instance->packets);
+	instance->tds = malloc32(sizeof(td_t) * instance->packets);
 	if (instance->tds == NULL) {
 		usb_log_error("Failed to allocate transfer descriptors.\n");
@@ -90,5 +91,5 @@
 		return NULL;
 	}
-	bzero(instance->tds, sizeof(transfer_descriptor_t) * instance->packets);
+	bzero(instance->tds, sizeof(td_t) * instance->packets);
 
 	const size_t transport_size = max_packet_size * instance->packets;
@@ -151,18 +152,22 @@
 	size_t i = 0;
 	for (;i < instance->packets; ++i) {
-		if (transfer_descriptor_is_active(&instance->tds[i])) {
+		if (td_is_active(&instance->tds[i])) {
 			return false;
 		}
-		instance->error = transfer_descriptor_status(&instance->tds[i]);
+
+		instance->error = td_status(&instance->tds[i]);
 		if (instance->error != EOK) {
+			usb_log_debug("Batch(%p) found error TD(%d):%x.\n",
+			    instance, i, instance->tds[i].status);
 			if (i > 0)
-				instance->transfered_size -= instance->setup_size;
-			usb_log_debug("Batch(%p) found error TD(%d):%x.\n",
-			  instance, i, instance->tds[i].status);
+				goto substract_ret;
 			return true;
 		}
-		instance->transfered_size +=
-		    transfer_descriptor_actual_size(&instance->tds[i]);
-	}
+
+		instance->transfered_size += td_act_size(&instance->tds[i]);
+		if (td_is_short(&instance->tds[i]))
+			goto substract_ret;
+	}
+substract_ret:
 	instance->transfered_size -= instance->setup_size;
 	return true;
@@ -192,56 +197,75 @@
 {
 	assert(instance);
-
+	batch_data(instance, USB_PID_IN);
+	instance->next_step = batch_call_in_and_dispose;
+	usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
+	batch_schedule(instance);
+}
+/*----------------------------------------------------------------------------*/
+void batch_interrupt_out(batch_t *instance)
+{
+	assert(instance);
+	memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
+	batch_data(instance, USB_PID_OUT);
+	instance->next_step = batch_call_out_and_dispose;
+	usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
+	batch_schedule(instance);
+}
+/*----------------------------------------------------------------------------*/
+void batch_bulk_in(batch_t *instance)
+{
+	assert(instance);
+	batch_data(instance, USB_PID_IN);
+	instance->next_step = batch_call_in_and_dispose;
+	usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
+	batch_schedule(instance);
+}
+/*----------------------------------------------------------------------------*/
+void batch_bulk_out(batch_t *instance)
+{
+	assert(instance);
+	memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
+	batch_data(instance, USB_PID_OUT);
+	instance->next_step = batch_call_out_and_dispose;
+	usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
+	batch_schedule(instance);
+}
+/*----------------------------------------------------------------------------*/
+static void batch_data(batch_t *instance, usb_packet_id pid)
+{
+	assert(instance);
 	const bool low_speed = instance->speed == USB_SPEED_LOW;
 	int toggle = 1;
-	size_t i = 0;
-	for (;i < instance->packets; ++i) {
+
+	size_t packet = 0;
+	size_t remain_size = instance->buffer_size;
+	while (remain_size > 0) {
 		char *data =
-		    instance->transport_buffer + (i  * instance->max_packet_size);
-		transfer_descriptor_t *next = (i + 1) < instance->packets ?
-		    &instance->tds[i + 1] : NULL;
+		    instance->transport_buffer + instance->buffer_size
+		    - remain_size;
+
 		toggle = 1 - toggle;
 
-		transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
-		    instance->max_packet_size, toggle, false, low_speed,
-		    instance->target, USB_PID_IN, data, next);
-	}
-
-	instance->tds[i - 1].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
-
-	instance->next_step = batch_call_in_and_dispose;
-	usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
-	batch_schedule(instance);
-}
-/*----------------------------------------------------------------------------*/
-void batch_interrupt_out(batch_t *instance)
-{
-	assert(instance);
-	memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
-
-	const bool low_speed = instance->speed == USB_SPEED_LOW;
-	int toggle = 1;
-	size_t i = 0;
-	for (;i < instance->packets; ++i) {
-		char *data =
-		    instance->transport_buffer + (i  * instance->max_packet_size);
-		transfer_descriptor_t *next = (i + 1) < instance->packets ?
-		    &instance->tds[i + 1] : NULL;
-		toggle = 1 - toggle;
-
-		transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
-		    instance->max_packet_size, toggle++, false, low_speed,
-		    instance->target, USB_PID_OUT, data, next);
-	}
-
-	instance->tds[i - 1].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
-
-	instance->next_step = batch_call_out_and_dispose;
-	usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
-	batch_schedule(instance);
-}
-/*----------------------------------------------------------------------------*/
-static void batch_control(
-    batch_t *instance, int data_stage, int status_stage)
+		const size_t packet_size =
+		    (instance->max_packet_size > remain_size) ?
+		    remain_size : instance->max_packet_size;
+
+		td_init(&instance->tds[packet],
+		    DEFAULT_ERROR_COUNT, packet_size, toggle, false, low_speed,
+		    instance->target, pid, data,
+		    &instance->tds[packet + 1]);
+
+		++packet;
+		assert(packet <= instance->packets);
+		assert(packet_size <= remain_size);
+		remain_size -= packet_size;
+	}
+
+	instance->tds[packet - 1].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
+	instance->tds[packet - 1].next = 0 | LINK_POINTER_TERMINATE_FLAG;
+}
+/*----------------------------------------------------------------------------*/
+static void batch_control(batch_t *instance,
+   usb_packet_id data_stage, usb_packet_id status_stage)
 {
 	assert(instance);
@@ -250,5 +274,5 @@
 	int toggle = 0;
 	/* setup stage */
-	transfer_descriptor_init(instance->tds, DEFAULT_ERROR_COUNT,
+	td_init(instance->tds, DEFAULT_ERROR_COUNT,
 	    instance->setup_size, toggle, false, low_speed, instance->target,
 	    USB_PID_SETUP, instance->setup_buffer, &instance->tds[1]);
@@ -268,5 +292,5 @@
 		    remain_size : instance->max_packet_size;
 
-		transfer_descriptor_init(&instance->tds[packet],
+		td_init(&instance->tds[packet],
 		    DEFAULT_ERROR_COUNT, packet_size, toggle, false, low_speed,
 		    instance->target, data_stage, data,
@@ -281,5 +305,5 @@
 	/* status stage */
 	assert(packet == instance->packets - 1);
-	transfer_descriptor_init(&instance->tds[packet], DEFAULT_ERROR_COUNT,
+	td_init(&instance->tds[packet], DEFAULT_ERROR_COUNT,
 	    0, 1, false, low_speed, instance->target, status_stage, NULL, NULL);
 
Index: uspace/drv/uhci-hcd/batch.h
===================================================================
--- uspace/drv/uhci-hcd/batch.h	(revision 7ac73a4b0be58a18822a76290c8e2578cbc50448)
+++ uspace/drv/uhci-hcd/batch.h	(revision 00db345ac0b5fb5841cf7eaac2d0a1eba5487690)
@@ -65,5 +65,5 @@
 	ddf_fun_t *fun;
 	queue_head_t *qh;
-	transfer_descriptor_t *tds;
+	td_t *tds;
 	void (*next_step)(struct batch*);
 } batch_t;
@@ -86,14 +86,7 @@
 void batch_interrupt_out(batch_t *instance);
 
-/* DEPRECATED FUNCTIONS NEEDED BY THE OLD API */
-void batch_control_setup_old(batch_t *instance);
+void batch_bulk_in(batch_t *instance);
 
-void batch_control_write_data_old(batch_t *instance);
-
-void batch_control_read_data_old(batch_t *instance);
-
-void batch_control_write_status_old(batch_t *instance);
-
-void batch_control_read_status_old(batch_t *instance);
+void batch_bulk_out(batch_t *instance);
 #endif
 /**
Index: uspace/drv/uhci-hcd/iface.c
===================================================================
--- uspace/drv/uhci-hcd/iface.c	(revision 7ac73a4b0be58a18822a76290c8e2578cbc50448)
+++ uspace/drv/uhci-hcd/iface.c	(revision 00db345ac0b5fb5841cf7eaac2d0a1eba5487690)
@@ -140,4 +140,43 @@
 }
 /*----------------------------------------------------------------------------*/
+static int bulk_out(ddf_fun_t *fun, usb_target_t target,
+    size_t max_packet_size, void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	assert(fun);
+	uhci_t *hc = fun_to_uhci(fun);
+	assert(hc);
+	usb_speed_t speed = device_keeper_speed(&hc->device_manager, target.address);
+
+	usb_log_debug("Bulk OUT %d:%d %zu(%zu).\n",
+	    target.address, target.endpoint, size, max_packet_size);
+
+	batch_t *batch = batch_get(fun, target, USB_TRANSFER_BULK,
+	    max_packet_size, speed, data, size, NULL, 0, NULL, callback, arg);
+	if (!batch)
+		return ENOMEM;
+	batch_bulk_out(batch);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+static int bulk_in(ddf_fun_t *fun, usb_target_t target,
+    size_t max_packet_size, void *data, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	assert(fun);
+	uhci_t *hc = fun_to_uhci(fun);
+	assert(hc);
+	usb_speed_t speed = device_keeper_speed(&hc->device_manager, target.address);
+	usb_log_debug("Bulk IN %d:%d %zu(%zu).\n",
+	    target.address, target.endpoint, size, max_packet_size);
+
+	batch_t *batch = batch_get(fun, target, USB_TRANSFER_BULK,
+	    max_packet_size, speed, data, size, NULL, 0, callback, NULL, arg);
+	if (!batch)
+		return ENOMEM;
+	batch_bulk_in(batch);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
 static int control_write(ddf_fun_t *fun, usb_target_t target,
     size_t max_packet_size,
@@ -181,6 +220,4 @@
 	return EOK;
 }
-
-
 /*----------------------------------------------------------------------------*/
 usbhc_iface_t uhci_iface = {
@@ -194,4 +231,7 @@
 	.interrupt_in = interrupt_in,
 
+	.bulk_in = bulk_in,
+	.bulk_out = bulk_out,
+
 	.control_read = control_read,
 	.control_write = control_write,
Index: uspace/drv/uhci-hcd/uhci.c
===================================================================
--- uspace/drv/uhci-hcd/uhci.c	(revision 7ac73a4b0be58a18822a76290c8e2578cbc50448)
+++ uspace/drv/uhci-hcd/uhci.c	(revision 00db345ac0b5fb5841cf7eaac2d0a1eba5487690)
@@ -336,5 +336,5 @@
 		uhci_interrupt(instance, status);
 		pio_write_16(&instance->registers->usbsts, 0x1f);
-		async_usleep(UHCI_CLEANER_TIMEOUT * 5);
+		async_usleep(UHCI_CLEANER_TIMEOUT);
 	}
 	return EOK;
Index: uspace/drv/uhci-hcd/uhci_struct/transfer_descriptor.c
===================================================================
--- uspace/drv/uhci-hcd/uhci_struct/transfer_descriptor.c	(revision 7ac73a4b0be58a18822a76290c8e2578cbc50448)
+++ uspace/drv/uhci-hcd/uhci_struct/transfer_descriptor.c	(revision 00db345ac0b5fb5841cf7eaac2d0a1eba5487690)
@@ -38,9 +38,10 @@
 #include "utils/malloc32.h"
 
-void transfer_descriptor_init(transfer_descriptor_t *instance,
-    int error_count, size_t size, bool toggle, bool isochronous, bool low_speed,
-    usb_target_t target, int pid, void *buffer, transfer_descriptor_t *next)
+void td_init(td_t *instance, int err_count, size_t size, bool toggle, bool iso,
+    bool low_speed, usb_target_t target, usb_packet_id pid, void *buffer, td_t *next)
 {
 	assert(instance);
+	assert(size < 1024);
+	assert((pid == USB_PID_SETUP) || (pid == USB_PID_IN) || (pid == USB_PID_OUT));
 
 	instance->next = 0
@@ -49,15 +50,19 @@
 
 	instance->status = 0
-	  | ((error_count & TD_STATUS_ERROR_COUNT_MASK) << TD_STATUS_ERROR_COUNT_POS)
-		| (low_speed ? TD_STATUS_LOW_SPEED_FLAG : 0)
-	  | TD_STATUS_ERROR_ACTIVE;
+	    | ((err_count & TD_STATUS_ERROR_COUNT_MASK) << TD_STATUS_ERROR_COUNT_POS)
+	    | (low_speed ? TD_STATUS_LOW_SPEED_FLAG : 0)
+	    | (iso ? TD_STATUS_ISOCHRONOUS_FLAG : 0)
+	    | TD_STATUS_ERROR_ACTIVE;
 
-	assert(size < 1024);
+	if (pid == USB_PID_IN && !iso) {
+		instance->status |= TD_STATUS_SPD_FLAG;
+	}
+
 	instance->device = 0
-		| (((size - 1) & TD_DEVICE_MAXLEN_MASK) << TD_DEVICE_MAXLEN_POS)
-		| (toggle ? TD_DEVICE_DATA_TOGGLE_ONE_FLAG : 0)
-		| ((target.address & TD_DEVICE_ADDRESS_MASK) << TD_DEVICE_ADDRESS_POS)
-		| ((target.endpoint & TD_DEVICE_ENDPOINT_MASK) << TD_DEVICE_ENDPOINT_POS)
-		| ((pid & TD_DEVICE_PID_MASK) << TD_DEVICE_PID_POS);
+	    | (((size - 1) & TD_DEVICE_MAXLEN_MASK) << TD_DEVICE_MAXLEN_POS)
+	    | (toggle ? TD_DEVICE_DATA_TOGGLE_ONE_FLAG : 0)
+	    | ((target.address & TD_DEVICE_ADDRESS_MASK) << TD_DEVICE_ADDRESS_POS)
+	    | ((target.endpoint & TD_DEVICE_ENDPOINT_MASK) << TD_DEVICE_ENDPOINT_POS)
+	    | ((pid & TD_DEVICE_PID_MASK) << TD_DEVICE_PID_POS);
 
 	instance->buffer_ptr = 0;
@@ -68,9 +73,9 @@
 
 	usb_log_debug2("Created TD: %X:%X:%X:%X(%p).\n",
-		instance->next, instance->status, instance->device,
-	  instance->buffer_ptr, buffer);
+	    instance->next, instance->status, instance->device,
+	    instance->buffer_ptr, buffer);
 }
 /*----------------------------------------------------------------------------*/
-int transfer_descriptor_status(transfer_descriptor_t *instance)
+int td_status(td_t *instance)
 {
 	assert(instance);
Index: uspace/drv/uhci-hcd/uhci_struct/transfer_descriptor.h
===================================================================
--- uspace/drv/uhci-hcd/uhci_struct/transfer_descriptor.h	(revision 7ac73a4b0be58a18822a76290c8e2578cbc50448)
+++ uspace/drv/uhci-hcd/uhci_struct/transfer_descriptor.h	(revision 00db345ac0b5fb5841cf7eaac2d0a1eba5487690)
@@ -88,23 +88,32 @@
 	 * we don't use it anyway
 	 */
-} __attribute__((packed)) transfer_descriptor_t;
+} __attribute__((packed)) td_t;
 
 
-void transfer_descriptor_init(transfer_descriptor_t *instance,
-    int error_count, size_t size, bool toggle, bool isochronous, bool low_speed,
-    usb_target_t target, int pid, void *buffer, transfer_descriptor_t * next);
+void td_init(td_t *instance, int error_count, size_t size, bool toggle, bool iso,
+    bool low_speed, usb_target_t target, usb_packet_id pid, void *buffer,
+    td_t *next);
 
-int transfer_descriptor_status(transfer_descriptor_t *instance);
+int td_status(td_t *instance);
 
-static inline size_t transfer_descriptor_actual_size(
-    transfer_descriptor_t *instance)
+static inline size_t td_act_size(td_t *instance)
 {
 	assert(instance);
 	return
-	    ((instance->status >> TD_STATUS_ACTLEN_POS) + 1) & TD_STATUS_ACTLEN_MASK;
+	    ((instance->status >> TD_STATUS_ACTLEN_POS) + 1)
+	    & TD_STATUS_ACTLEN_MASK;
 }
 
-static inline bool transfer_descriptor_is_active(
-    transfer_descriptor_t *instance)
+static inline bool td_is_short(td_t *instance)
+{
+	const size_t act_size = td_act_size(instance);
+	const size_t max_size =
+	    ((instance->device >> TD_DEVICE_MAXLEN_POS) + 1)
+	    & TD_DEVICE_MAXLEN_MASK;
+	return
+	    (instance->status | TD_STATUS_SPD_FLAG) && act_size < max_size;
+}
+
+static inline bool td_is_active(td_t *instance)
 {
 	assert(instance);
