Index: uspace/app/tmon/Makefile
===================================================================
--- uspace/app/tmon/Makefile	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/app/tmon/Makefile	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -30,5 +30,5 @@
 BINARY = tmon
 
-LIBS = drv
+LIBS = drv usb
 
 SOURCES = \
@@ -36,5 +36,5 @@
 	list.c\
 	tf.c\
-	burst_tests.c\
+	tests.c\
 	resolve.c
 
Index: pace/app/tmon/burst_tests.c
===================================================================
--- uspace/app/tmon/burst_tests.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ 	(revision )
@@ -1,446 +1,0 @@
-/*
- * 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 tmon
- * @{
- */
-/**
- * @file
- * USB burst tests.
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <str.h>
-#include <str_error.h>
-#include <getopt.h>
-#include <usbdiag_iface.h>
-#include <macros.h>
-#include "commands.h"
-#include "tf.h"
-
-#define NAME   "tmon"
-#define INDENT "      "
-
-/** Generic burst test parameters. */
-typedef struct tmon_burst_test_params {
-	/** Inherited base. */
-	tmon_test_params_t base;
-	/** The count of reads/writes to perform. */
-	uint32_t cycles;
-	/** Size of single read/write. */
-	size_t size;
-} tmon_burst_test_params_t;
-
-/** Static array of long options, from which test parameters are parsed. */
-static struct option long_options[] = {
-	{"cycles", required_argument, NULL, 'n'},
-	{"size", required_argument, NULL, 's'},
-	{0, 0, NULL, 0}
-};
-
-/** String of short options, from which test parameters are parsed. */
-static const char *short_options = "n:s:";
-
-/** Common option parser for all burst tests.
- * @param[in] argc Number of arguments.
- * @param[in] argv Argument values. Must point to exactly `argc` strings.
- * @param[out] params Parsed test parameters (if successful).
- *
- * @return EOK if successful (in such case caller becomes the owner of `params`).
- */
-static int read_params(int argc, char *argv[], tmon_test_params_t **params)
-{
-	int rc;
-	tmon_burst_test_params_t *p = (tmon_burst_test_params_t *) malloc(sizeof(tmon_burst_test_params_t));
-	if (!p)
-		return ENOMEM;
-
-	// Default values.
-	p->cycles = 256;
-	p->size = 1024;
-
-	// Parse other than default values.
-	int c;
-	for (c = 0, optreset = 1, optind = 0; c != -1;) {
-		c = getopt_long(argc, argv, short_options, long_options, NULL);
-		switch (c) {
-		case -1:
-			break;
-		case 'n':
-			if (!optarg || str_uint32_t(optarg, NULL, 10, false, &p->cycles) != EOK) {
-				puts(NAME ": Invalid number of cycles.\n");
-				rc = EINVAL;
-				goto err_malloc;
-			}
-			break;
-		case 's':
-			if (!optarg || str_size_t(optarg, NULL, 10, false, &p->size) != EOK) {
-				puts(NAME ": Invalid data size.\n");
-				rc = EINVAL;
-				goto err_malloc;
-			}
-			break;
-		}
-	}
-
-	*params = (tmon_test_params_t *) p;
-	return EOK;
-
-err_malloc:
-	free(p);
-	*params = NULL;
-	return rc;
-}
-
-/** Unit of quantity used for pretty formatting. */
-typedef struct tmon_unit {
-	/** Prefix letter, which is printed before the actual unit. */
-	char prefix;
-	/** Factor of the unit. */
-	uint64_t factor;
-} tmon_unit_t;
-
-/** Static array of units with decreasing factors. */
-static const tmon_unit_t units[] = {
-	{ .prefix = 'E', .factor = ((uint64_t) 1) << 60 },
-	{ .prefix = 'P', .factor = ((uint64_t) 1) << 50 },
-	{ .prefix = 'T', .factor = ((uint64_t) 1) << 40 },
-	{ .prefix = 'G', .factor = ((uint64_t) 1) << 30 },
-	{ .prefix = 'M', .factor = ((uint64_t) 1) << 20 },
-	{ .prefix = 'k', .factor = ((uint64_t) 1) << 10 }
-};
-
-/** Format size in bytes for human reading.
- * @param[in] size The size to format.
- * @param[in] fmt Format string. Must include one double and char.
- *
- * @return Heap-allocated string if successful (caller becomes its owner), NULL otherwise.
- */
-static char * format_size(double size, const char *fmt)
-{
-	// Figure out the "tightest" unit.
-	unsigned i;
-	for (i = 0; i < ARRAY_SIZE(units); ++i) {
-		if (units[i].factor <= size)
-			break;
-	}
-
-	char prefix[] = { '\0', '\0' };
-	double factor = 1;
-
-	if (i < ARRAY_SIZE(units)) {
-		prefix[0] = units[i].prefix;
-		factor = units[i].factor;
-	}
-
-	// Format the size.
-	const double div_size = size / factor;
-
-	char *out = NULL;
-	asprintf(&out, fmt, div_size, prefix);
-
-	return out;
-}
-
-/** Print burst test parameters.
- * @param[in] params Test parameters to print.
- */
-static void print_params(const tmon_burst_test_params_t *params)
-{
-	printf(INDENT "Number of cycles: %d\n", params->cycles);
-
-	char *str_size = format_size(params->size, "%.3f %sB");
-	printf(INDENT "Data size: %s\n", str_size);
-	free(str_size);
-}
-
-/** Print burst test results.
- * @param[in] params Test parameters.
- * @param[in] duration Duration of the burst test.
- */
-static void print_results(const tmon_burst_test_params_t *params, usbdiag_dur_t duration)
-{
-	printf(INDENT "Total duration: %ld ms\n", duration);
-
-	const double dur_per_cycle = (double) duration / (double) params->cycles;
-	printf(INDENT "Duration per cycle: %.3f ms\n", dur_per_cycle);
-
-	const size_t total_size = params->size * params->cycles;
-	char *str_total_size = format_size(total_size, "%.3f %sB");
-	printf(INDENT "Total size: %s\n", str_total_size);
-	free(str_total_size);
-
-	const double speed = 1000.0 * (double) total_size / (double) duration;
-	char *str_speed = format_size(speed, "%.3f %sB/s");
-	printf(INDENT "Average speed: %s\n", str_speed);
-	free(str_speed);
-}
-
-/** Run "interrupt in" burst test.
- * @param[in] exch Open async exchange with the diagnostic device.
- * @param[in] generic_params Test parameters. Must point to 'tmon_burst_test_params_t'.
- *
- * @return Exit code
- */
-static int run_intr_in(async_exch_t *exch, const tmon_test_params_t *generic_params)
-{
-	const tmon_burst_test_params_t *params = (tmon_burst_test_params_t *) generic_params;
-	puts("Reading data from interrupt endpoint.\n");
-	print_params(params);
-
-	usbdiag_dur_t duration;
-	int rc = usbdiag_burst_intr_in(exch, params->cycles, params->size, &duration);
-	if (rc) {
-		printf(NAME ": Test failed with error: %s\n", str_error(rc));
-		return 1;
-	}
-
-	puts("Test succeeded.\n");
-	print_results(params, duration);
-	return 0;
-}
-
-/** Run "interrupt out" burst test.
- * @param[in] exch Open async exchange with the diagnostic device.
- * @param[in] generic_params Test parameters. Must point to 'tmon_burst_test_params_t'.
- *
- * @return Exit code
- */
-static int run_intr_out(async_exch_t *exch, const tmon_test_params_t *generic_params)
-{
-	const tmon_burst_test_params_t *params = (tmon_burst_test_params_t *) generic_params;
-	puts("Writing data to interrupt endpoint.\n");
-	print_params(params);
-
-	usbdiag_dur_t duration;
-	int rc = usbdiag_burst_intr_out(exch, params->cycles, params->size, &duration);
-	if (rc) {
-		printf(NAME ": Test failed with error: %s\n", str_error(rc));
-		return 1;
-	}
-
-	puts("Test succeeded.\n");
-	print_results(params, duration);
-	return 0;
-}
-
-/** Run "bulk in" burst test.
- * @param[in] exch Open async exchange with the diagnostic device.
- * @param[in] generic_params Test parameters. Must point to 'tmon_burst_test_params_t'.
- *
- * @return Exit code
- */
-static int run_bulk_in(async_exch_t *exch, const tmon_test_params_t *generic_params)
-{
-	const tmon_burst_test_params_t *params = (tmon_burst_test_params_t *) generic_params;
-	puts("Reading data from bulk endpoint.\n");
-	print_params(params);
-
-	usbdiag_dur_t duration;
-	int rc = usbdiag_burst_bulk_in(exch, params->cycles, params->size, &duration);
-	if (rc) {
-		printf(NAME ": Test failed with error: %s\n", str_error(rc));
-		return 1;
-	}
-
-	puts("Test succeeded.\n");
-	print_results(params, duration);
-	return 0;
-}
-
-/** Run "bulk out" burst test.
- * @param[in] exch Open async exchange with the diagnostic device.
- * @param[in] generic_params Test parameters. Must point to 'tmon_burst_test_params_t'.
- *
- * @return Exit code
- */
-static int run_bulk_out(async_exch_t *exch, const tmon_test_params_t *generic_params)
-{
-	const tmon_burst_test_params_t *params = (tmon_burst_test_params_t *) generic_params;
-	puts("Writing data to bulk endpoint.\n");
-	print_params(params);
-
-	usbdiag_dur_t duration;
-	int rc = usbdiag_burst_bulk_out(exch, params->cycles, params->size, &duration);
-	if (rc) {
-		printf(NAME ": Test failed with error: %s\n", str_error(rc));
-		return 1;
-	}
-
-	puts("Test succeeded.\n");
-	print_results(params, duration);
-	return 0;
-}
-
-/** Run "isochronous in" burst test.
- * @param[in] exch Open async exchange with the diagnostic device.
- * @param[in] generic_params Test parameters. Must point to 'tmon_burst_test_params_t'.
- *
- * @return Exit code
- */
-static int run_isoch_in(async_exch_t *exch, const tmon_test_params_t *generic_params)
-{
-	const tmon_burst_test_params_t *params = (tmon_burst_test_params_t *) generic_params;
-	puts("Reading data from isochronous endpoint.\n");
-	print_params(params);
-
-	usbdiag_dur_t duration;
-	int rc = usbdiag_burst_isoch_in(exch, params->cycles, params->size, &duration);
-	if (rc) {
-		printf(NAME ": Test failed with error: %s\n", str_error(rc));
-		return 1;
-	}
-
-	puts("Test succeeded.\n");
-	print_results(params, duration);
-	return 0;
-}
-
-/** Run "isochronous out" burst test.
- * @param[in] exch Open async exchange with the diagnostic device.
- * @param[in] generic_params Test parameters. Must point to 'tmon_burst_test_params_t'.
- *
- * @return Exit code
- */
-static int run_isoch_out(async_exch_t *exch, const tmon_test_params_t *generic_params)
-{
-	const tmon_burst_test_params_t *params = (tmon_burst_test_params_t *) generic_params;
-	puts("Writing data to isochronous endpoint.\n");
-	print_params(params);
-
-	usbdiag_dur_t duration;
-	int rc = usbdiag_burst_isoch_out(exch, params->cycles, params->size, &duration);
-	if (rc) {
-		printf(NAME ": Test failed with error: %s\n", str_error(rc));
-		return 1;
-	}
-
-	puts("Test succeeded.\n");
-	print_results(params, duration);
-	return 0;
-}
-
-/** Interrupt in burst test command handler.
- * @param[in] argc Number of arguments.
- * @param[in] argv Argument values. Must point to exactly `argc` strings.
- *
- * @return Exit code
- */
-int tmon_burst_intr_in(int argc, char *argv[])
-{
-	static const tmon_test_ops_t ops = {
-		.run = run_intr_in,
-		.read_params = read_params
-	};
-
-	return tmon_test_main(argc, argv, &ops);
-}
-
-/** Interrupt out burst test command handler.
- * @param[in] argc Number of arguments.
- * @param[in] argv Argument values. Must point to exactly `argc` strings.
- *
- * @return Exit code
- */
-int tmon_burst_intr_out(int argc, char *argv[])
-{
-	static const tmon_test_ops_t ops = {
-		.run = run_intr_out,
-		.read_params = read_params
-	};
-
-	return tmon_test_main(argc, argv, &ops);
-}
-
-/** Interrupt bulk burst test command handler.
- * @param[in] argc Number of arguments.
- * @param[in] argv Argument values. Must point to exactly `argc` strings.
- *
- * @return Exit code
- */
-int tmon_burst_bulk_in(int argc, char *argv[])
-{
-	static const tmon_test_ops_t ops = {
-		.run = run_bulk_in,
-		.read_params = read_params
-	};
-
-	return tmon_test_main(argc, argv, &ops);
-}
-
-/** Bulk out burst test command handler.
- * @param[in] argc Number of arguments.
- * @param[in] argv Argument values. Must point to exactly `argc` strings.
- *
- * @return Exit code
- */
-int tmon_burst_bulk_out(int argc, char *argv[])
-{
-	static const tmon_test_ops_t ops = {
-		.run = run_bulk_out,
-		.read_params = read_params
-	};
-
-	return tmon_test_main(argc, argv, &ops);
-}
-
-/** Isochronous in burst test command handler.
- * @param[in] argc Number of arguments.
- * @param[in] argv Argument values. Must point to exactly `argc` strings.
- *
- * @return Exit code
- */
-int tmon_burst_isoch_in(int argc, char *argv[])
-{
-	static const tmon_test_ops_t ops = {
-		.run = run_isoch_in,
-		.read_params = read_params
-	};
-
-	return tmon_test_main(argc, argv, &ops);
-}
-
-/** Isochronous out burst test command handler.
- * @param[in] argc Number of arguments.
- * @param[in] argv Argument values. Must point to exactly `argc` strings.
- *
- * @return Exit code
- */
-int tmon_burst_isoch_out(int argc, char *argv[])
-{
-	static const tmon_test_ops_t ops = {
-		.run = run_isoch_out,
-		.read_params = read_params
-	};
-
-	return tmon_test_main(argc, argv, &ops);
-}
-
-/** @}
- */
Index: uspace/app/tmon/commands.h
===================================================================
--- uspace/app/tmon/commands.h	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/app/tmon/commands.h	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -42,11 +42,11 @@
 int tmon_list(int, char **);
 
-/* Burst tests read/write into endpoints as fast as possible. */
-int tmon_burst_intr_in(int, char **);
-int tmon_burst_intr_out(int, char **);
-int tmon_burst_bulk_in(int, char **);
-int tmon_burst_bulk_out(int, char **);
-int tmon_burst_isoch_in(int, char **);
-int tmon_burst_isoch_out(int, char **);
+/* Tests commands differ by endpoint types. */
+int tmon_test_intr_in(int, char **);
+int tmon_test_intr_out(int, char **);
+int tmon_test_bulk_in(int, char **);
+int tmon_test_bulk_out(int, char **);
+int tmon_test_isoch_in(int, char **);
+int tmon_test_isoch_out(int, char **);
 
 #endif /* TMON_COMMANDS_H_ */
Index: uspace/app/tmon/main.c
===================================================================
--- uspace/app/tmon/main.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/app/tmon/main.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -64,31 +64,31 @@
 		.name = "test-intr-in",
 		.description = "Read from interrupt endpoint as fast as possible.",
-		.action = tmon_burst_intr_in,
+		.action = tmon_test_intr_in,
 	},
 	{
 		.name = "test-intr-out",
 		.description = "Write to interrupt endpoint as fast as possible.",
-		.action = tmon_burst_intr_out,
+		.action = tmon_test_intr_out,
 	},
 	{
 		.name = "test-bulk-in",
 		.description = "Read from bulk endpoint as fast as possible.",
-		.action = tmon_burst_bulk_in,
+		.action = tmon_test_bulk_in,
 	},
 	{
 		.name = "test-bulk-out",
 		.description = "Write to bulk endpoint as fast as possible.",
-		.action = tmon_burst_bulk_out,
+		.action = tmon_test_bulk_out,
 	},
 	{
 		.name = "test-isoch-in",
 		.description = "Read from isochronous endpoint as fast as possible.",
-		.action = tmon_burst_isoch_in,
+		.action = tmon_test_isoch_in,
 	},
 	{
 		.name = "test-isoch-out",
 		.description = "Write to isochronous endpoint as fast as possible.",
-		.action = tmon_burst_isoch_out,
-	}
+		.action = tmon_test_isoch_out,
+	},
 };
 
@@ -106,13 +106,18 @@
 static tmon_opt_t options[] = {
 	{
-		.long_name = "cycles",
-		.short_name = 'n',
-		.description = "Set the number of read/write cycles."
+		.long_name = "duration",
+		.short_name = 't',
+		.description = "Set the minimum test duration (in seconds)."
 	},
 	{
 		.long_name = "size",
 		.short_name = 's',
-		.description = "Set the data size transferred in a single cycle."
-	}
+		.description = "Set the data size (in bytes) transferred in a single cycle."
+	},
+	{
+		.long_name = "validate",
+		.short_name = 'v',
+		.description = "Validate the correctness of transferred data (impacts performance)."
+	},
 };
 
Index: uspace/app/tmon/tests.c
===================================================================
--- uspace/app/tmon/tests.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
+++ uspace/app/tmon/tests.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -0,0 +1,338 @@
+/*
+ * 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 tmon
+ * @{
+ */
+/**
+ * @file
+ * USB tests.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <str.h>
+#include <str_error.h>
+#include <getopt.h>
+#include <usb/usb.h>
+#include <usbdiag_iface.h>
+#include "commands.h"
+#include "tf.h"
+
+#define NAME   "tmon"
+#define INDENT "      "
+
+/** Static array of long options, from which test parameters are parsed. */
+static struct option long_options[] = {
+	{"duration", required_argument, NULL, 't'},
+	{"size", required_argument, NULL, 's'},
+	{"validate", required_argument, NULL, 'v'},
+	{0, 0, NULL, 0}
+};
+
+/** String of short options, from which test parameters are parsed. */
+static const char *short_options = "t:s:v";
+
+/** Common option parser for all tests.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Argument values. Must point to exactly `argc` strings.
+ * @param[out] params Parsed test parameters (if successful).
+ *
+ * @return EOK if successful (in such case caller becomes the owner of `params`).
+ */
+static int read_params(int argc, char *argv[], void **params)
+{
+	int rc;
+	usbdiag_test_params_t *p = (usbdiag_test_params_t *) malloc(sizeof(usbdiag_test_params_t));
+	if (!p)
+		return ENOMEM;
+
+	// Default values.
+	p->transfer_size = 0;
+	p->min_duration = 5000;
+	p->validate_data = false;
+
+	// Parse other than default values.
+	int c;
+	uint32_t duration_uint;
+	for (c = 0, optreset = 1, optind = 0; c != -1;) {
+		c = getopt_long(argc, argv, short_options, long_options, NULL);
+		switch (c) {
+		case -1:
+			break;
+		case 'v':
+			p->validate_data = true;
+			break;
+		case 't':
+			if (!optarg || str_uint32_t(optarg, NULL, 10, false, &duration_uint) != EOK) {
+				puts(NAME ": Invalid duration.\n");
+				rc = EINVAL;
+				goto err_malloc;
+			}
+			p->min_duration = (usbdiag_dur_t) duration_uint * 1000;
+			break;
+		case 's':
+			if (!optarg || str_size_t(optarg, NULL, 10, false, &p->transfer_size) != EOK) {
+				puts(NAME ": Invalid data size.\n");
+				rc = EINVAL;
+				goto err_malloc;
+			}
+			break;
+		}
+	}
+
+	*params = (void *) p;
+	return EOK;
+
+err_malloc:
+	free(p);
+	*params = NULL;
+	return rc;
+}
+
+/** Print test parameters.
+ * @param[in] params Test parameters to print.
+ */
+static void print_params(const usbdiag_test_params_t *params)
+{
+	printf("Endpoint type: %s\n", usb_str_transfer_type(params->transfer_type));
+	char *str_min_duration = tmon_format_duration(params->min_duration, "%.3f %s");
+	printf("Min. duration: %s\n", str_min_duration);
+	free(str_min_duration);
+
+	if (params->transfer_size) {
+		char *str_size = tmon_format_size(params->transfer_size, "%.3f %s");
+		printf("Transfer size: %s\n", str_size);
+		free(str_size);
+	} else {
+		printf("Transfer size: (max. transfer size)\n");
+	}
+
+	printf("Validate data: %s\n", params->validate_data ? "yes" : "no");
+}
+
+/** Print test results.
+ * @param[in] params Test parameters.
+ * @param[in] results Test results.
+ */
+static void print_results(const usbdiag_test_params_t *params, const usbdiag_test_results_t *results)
+{
+	printf("Transfers performed: %u\n", results->transfer_count);
+
+	char *str_total_duration = tmon_format_duration(results->act_duration, "%.3f %s");
+	printf("Total duration: %s\n", str_total_duration);
+	free(str_total_duration);
+
+	char *str_size_per_transfer = tmon_format_size(results->transfer_size, "%.3f %s");
+	printf("Transfer size: %s\n", str_size_per_transfer);
+	free(str_size_per_transfer);
+
+	const size_t total_size = results->transfer_size * results->transfer_count;
+	char *str_total_size = tmon_format_size(total_size, "%.3f %s");
+	printf("Total size: %s\n", str_total_size);
+	free(str_total_size);
+
+	const double dur_per_transfer = (double) results->act_duration / (double) results->transfer_count;
+	char *str_dur_per_transfer = tmon_format_duration(dur_per_transfer, "%.3f %s");
+	printf("Avg. transfer duration: %s\n", str_dur_per_transfer);
+	free(str_dur_per_transfer);
+
+	const double speed = 1000.0 * (double) total_size / (double) results->act_duration;
+	char *str_speed = tmon_format_size(speed, "%.3f %s/s");
+	printf("Avg. speed: %s\n", str_speed);
+	free(str_speed);
+}
+
+/** Run test on "in" endpoint.
+ * @param[in] exch Open async exchange with the diagnostic device.
+ * @param[in] generic_params Test parameters. Must point to 'usbdiag_test_params_t'.
+ *
+ * @return Exit code
+ */
+static int test_in(async_exch_t *exch, const void *generic_params)
+{
+	const usbdiag_test_params_t *params = (usbdiag_test_params_t *) generic_params;
+	print_params(params);
+	puts("\nTesting... ");
+
+	usbdiag_test_results_t results;
+	int rc = usbdiag_test_in(exch, params, &results);
+	if (rc != EOK) {
+		puts("failed\n");
+		printf(NAME ": %s\n", str_error(rc));
+		return 1;
+	}
+
+	puts("succeeded\n\n");
+	print_results(params, &results);
+	return 0;
+}
+
+/** Run test on "out" endpoint.
+ * @param[in] exch Open async exchange with the diagnostic device.
+ * @param[in] generic_params Test parameters. Must point to 'usbdiag_test_params_t'.
+ *
+ * @return Exit code
+ */
+static int test_out(async_exch_t *exch, const void *generic_params)
+{
+	const usbdiag_test_params_t *params = (usbdiag_test_params_t *) generic_params;
+	print_params(params);
+	puts("\nTesting... ");
+
+	usbdiag_test_results_t results;
+	int rc = usbdiag_test_out(exch, params, &results);
+	if (rc) {
+		puts("failed\n");
+		printf(NAME ": %s\n", str_error(rc));
+		return 1;
+	}
+
+	puts("succeeded\n\n");
+	print_results(params, &results);
+	return 0;
+}
+
+#define GEN_PRE_RUN(FN, TYPE) \
+	static int pre_run_##FN(void *generic_params) \
+	{ \
+		usbdiag_test_params_t *params = (usbdiag_test_params_t *) generic_params; \
+		params->transfer_type = USB_TRANSFER_##TYPE; \
+		return EOK; \
+	}
+
+GEN_PRE_RUN(intr, INTERRUPT)
+GEN_PRE_RUN(bulk, BULK)
+GEN_PRE_RUN(isoch, ISOCHRONOUS)
+
+#undef GEN_PRE_RUN
+
+/** Interrupt in test command handler.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Argument values. Must point to exactly `argc` strings.
+ *
+ * @return Exit code
+ */
+int tmon_test_intr_in(int argc, char *argv[])
+{
+	static const tmon_test_ops_t ops = {
+		.pre_run = pre_run_intr,
+		.run = test_in,
+		.read_params = read_params
+	};
+
+	return tmon_test_main(argc, argv, &ops);
+}
+
+/** Interrupt out test command handler.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Argument values. Must point to exactly `argc` strings.
+ *
+ * @return Exit code
+ */
+int tmon_test_intr_out(int argc, char *argv[])
+{
+	static const tmon_test_ops_t ops = {
+		.pre_run = pre_run_intr,
+		.run = test_out,
+		.read_params = read_params
+	};
+
+	return tmon_test_main(argc, argv, &ops);
+}
+
+/** Interrupt bulk test command handler.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Argument values. Must point to exactly `argc` strings.
+ *
+ * @return Exit code
+ */
+int tmon_test_bulk_in(int argc, char *argv[])
+{
+	static const tmon_test_ops_t ops = {
+		.pre_run = pre_run_bulk,
+		.run = test_in,
+		.read_params = read_params
+	};
+
+	return tmon_test_main(argc, argv, &ops);
+}
+
+/** Bulk out test command handler.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Argument values. Must point to exactly `argc` strings.
+ *
+ * @return Exit code
+ */
+int tmon_test_bulk_out(int argc, char *argv[])
+{
+	static const tmon_test_ops_t ops = {
+		.pre_run = pre_run_bulk,
+		.run = test_out,
+		.read_params = read_params
+	};
+
+	return tmon_test_main(argc, argv, &ops);
+}
+
+/** Isochronous in test command handler.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Argument values. Must point to exactly `argc` strings.
+ *
+ * @return Exit code
+ */
+int tmon_test_isoch_in(int argc, char *argv[])
+{
+	static const tmon_test_ops_t ops = {
+		.pre_run = pre_run_isoch,
+		.run = test_in,
+		.read_params = read_params
+	};
+
+	return tmon_test_main(argc, argv, &ops);
+}
+
+/** Isochronous out test command handler.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Argument values. Must point to exactly `argc` strings.
+ *
+ * @return Exit code
+ */
+int tmon_test_isoch_out(int argc, char *argv[])
+{
+	static const tmon_test_ops_t ops = {
+		.pre_run = pre_run_isoch,
+		.run = test_out,
+		.read_params = read_params
+	};
+
+	return tmon_test_main(argc, argv, &ops);
+}
+
+/** @}
+ */
Index: uspace/app/tmon/tf.c
===================================================================
--- uspace/app/tmon/tf.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/app/tmon/tf.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -36,4 +36,5 @@
 
 #include <stdio.h>
+#include <macros.h>
 #include <devman.h>
 #include <str_error.h>
@@ -74,10 +75,15 @@
 	}
 
-	printf("Using device: %s\n", path);
+	printf("Device: %s\n", path);
 
 	// Read test parameters from options.
-	tmon_test_params_t *params = NULL;
+	void *params = NULL;
 	if ((rc = ops->read_params(argc, argv, &params))) {
 		printf(NAME ": Reading test parameters failed. %s\n", str_error(rc));
+		return 1;
+	}
+
+	if ((rc = ops->pre_run(params))) {
+		printf(NAME ": Pre-run hook failed. %s\n", str_error(rc));
 		return 1;
 	}
@@ -106,4 +112,73 @@
 }
 
+/** Unit of quantity used for pretty formatting. */
+typedef struct tmon_unit {
+	/** Prefix letter, which is printed before the actual unit. */
+	const char *unit;
+	/** Factor of the unit. */
+	double factor;
+} tmon_unit_t;
+
+/** Format a value for human reading.
+ * @param[in] val The value to format.
+ * @param[in] fmt Format string. Must include one double and char.
+ *
+ * @return Heap-allocated string if successful (caller becomes its owner), NULL otherwise.
+ */
+static char *format_unit(double val, const char *fmt, const tmon_unit_t *units, size_t len)
+{
+	// Figure out the "tightest" unit.
+	unsigned i;
+	for (i = 0; i < len; ++i) {
+		if (units[i].factor <= val)
+			break;
+	}
+
+	if (i == len) --i;
+	const char *unit = units[i].unit;
+	double factor = units[i].factor;
+
+	// Format the size.
+	const double div_size = val / factor;
+
+	char *out = NULL;
+	asprintf(&out, fmt, div_size, unit);
+
+	return out;
+}
+
+/** Static array of size units with decreasing factors. */
+static const tmon_unit_t size_units[] = {
+	{ .unit = "EB", .factor = 1ULL << 60 },
+	{ .unit = "PB", .factor = 1ULL << 50 },
+	{ .unit = "TB", .factor = 1ULL << 40 },
+	{ .unit = "GB", .factor = 1ULL << 30 },
+	{ .unit = "MB", .factor = 1ULL << 20 },
+	{ .unit = "kB", .factor = 1ULL << 10 },
+	{ .unit = "B",  .factor = 1ULL },
+};
+
+char *tmon_format_size(double val, const char *fmt)
+{
+	return format_unit(val, fmt, size_units, ARRAY_SIZE(size_units));
+}
+
+/** Static array of duration units with decreasing factors. */
+static const tmon_unit_t dur_units[] = {
+	{ .unit = "d",   .factor = 60 * 60 * 24 },
+	{ .unit = "h",   .factor = 60 * 60 },
+	{ .unit = "min", .factor = 60 },
+	{ .unit = "s",   .factor = 1 },
+	{ .unit = "ms",  .factor = 1e-3 },
+	{ .unit = "us",  .factor = 1e-6 },
+	{ .unit = "ns",  .factor = 1e-9 },
+	{ .unit = "ps",  .factor = 1e-12 },
+};
+
+char *tmon_format_duration(usbdiag_dur_t val, const char *fmt)
+{
+	return format_unit(val / 1000.0, fmt, dur_units, ARRAY_SIZE(dur_units));
+}
+
 /** @}
  */
Index: uspace/app/tmon/tf.h
===================================================================
--- uspace/app/tmon/tf.h	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/app/tmon/tf.h	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -38,17 +38,17 @@
 
 #include <async.h>
-
-/** Parameters common for all tests. */
-typedef struct tmon_test_params {
-	/* Nothing here. */
-} tmon_test_params_t;
+#include <usbdiag_iface.h>
 
 /** Operations to implement by all tests. */
 typedef struct tmon_test_ops {
-	int (*run)(async_exch_t *, const tmon_test_params_t *);
-	int (*read_params)(int, char **, tmon_test_params_t **);
+	int (*pre_run)(void *);
+	int (*run)(async_exch_t *, const void *);
+	int (*read_params)(int, char **, void **);
 } tmon_test_ops_t;
 
 int tmon_test_main(int, char **, const tmon_test_ops_t *);
+
+char *tmon_format_size(double, const char *);
+char *tmon_format_duration(usbdiag_dur_t, const char *);
 
 #endif /* TMON_TF_H_ */
Index: uspace/drv/bus/usb/usbdiag/device.c
===================================================================
--- uspace/drv/bus/usb/usbdiag/device.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/drv/bus/usb/usbdiag/device.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -46,16 +46,6 @@
 
 static usbdiag_iface_t diag_interface = {
-	.burst_intr_in = usbdiag_burst_test_intr_in,
-	.burst_intr_out = usbdiag_burst_test_intr_out,
-	.burst_bulk_in = usbdiag_burst_test_bulk_in,
-	.burst_bulk_out = usbdiag_burst_test_bulk_out,
-	.burst_isoch_in = usbdiag_burst_test_isoch_in,
-	.burst_isoch_out = usbdiag_burst_test_isoch_out,
-	.data_intr_in = usbdiag_data_test_intr_in,
-	.data_intr_out = usbdiag_data_test_intr_out,
-	.data_bulk_in = usbdiag_data_test_bulk_in,
-	.data_bulk_out = usbdiag_data_test_bulk_out,
-	.data_isoch_in = usbdiag_data_test_isoch_in,
-	.data_isoch_out = usbdiag_data_test_isoch_out
+	.test_in = usbdiag_dev_test_in,
+	.test_out = usbdiag_dev_test_out,
 };
 
@@ -86,10 +76,16 @@
 	} while (0);
 
-	_MAP_EP(dev->intr_in, INTR_IN);
-	_MAP_EP(dev->intr_out, INTR_OUT);
-	_MAP_EP(dev->bulk_in, BULK_IN);
-	_MAP_EP(dev->bulk_out, BULK_OUT);
-	_MAP_EP(dev->isoch_in, ISOCH_IN);
-	_MAP_EP(dev->isoch_out, ISOCH_OUT);
+	_MAP_EP(dev->burst_intr_in, BURST_INTR_IN);
+	_MAP_EP(dev->burst_intr_out, BURST_INTR_OUT);
+	_MAP_EP(dev->burst_bulk_in, BURST_BULK_IN);
+	_MAP_EP(dev->burst_bulk_out, BURST_BULK_OUT);
+	_MAP_EP(dev->burst_isoch_in, BURST_ISOCH_IN);
+	_MAP_EP(dev->burst_isoch_out, BURST_ISOCH_OUT);
+	_MAP_EP(dev->data_intr_in, DATA_INTR_IN);
+	_MAP_EP(dev->data_intr_out, DATA_INTR_OUT);
+	_MAP_EP(dev->data_bulk_in, DATA_BULK_IN);
+	_MAP_EP(dev->data_bulk_out, DATA_BULK_OUT);
+	_MAP_EP(dev->data_isoch_in, DATA_ISOCH_IN);
+	_MAP_EP(dev->data_isoch_out, DATA_ISOCH_OUT);
 
 #undef _MAP_EP
Index: uspace/drv/bus/usb/usbdiag/device.h
===================================================================
--- uspace/drv/bus/usb/usbdiag/device.h	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/drv/bus/usb/usbdiag/device.h	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -40,10 +40,17 @@
 #include <usb/dev/driver.h>
 
-#define USBDIAG_EP_INTR_IN    1
-#define USBDIAG_EP_INTR_OUT   2
-#define USBDIAG_EP_BULK_IN    3
-#define USBDIAG_EP_BULK_OUT   4
-#define USBDIAG_EP_ISOCH_IN   5
-#define USBDIAG_EP_ISOCH_OUT  6
+#define USBDIAG_EP_BURST_INTR_IN    1
+#define USBDIAG_EP_BURST_INTR_OUT   2
+#define USBDIAG_EP_BURST_BULK_IN    3
+#define USBDIAG_EP_BURST_BULK_OUT   4
+#define USBDIAG_EP_BURST_ISOCH_IN   5
+#define USBDIAG_EP_BURST_ISOCH_OUT  6
+
+#define USBDIAG_EP_DATA_INTR_IN     7
+#define USBDIAG_EP_DATA_INTR_OUT    8
+#define USBDIAG_EP_DATA_BULK_IN     9
+#define USBDIAG_EP_DATA_BULK_OUT   10
+#define USBDIAG_EP_DATA_ISOCH_IN   11
+#define USBDIAG_EP_DATA_ISOCH_OUT  12
 
 /**
@@ -53,10 +60,19 @@
 	usb_device_t *usb_dev;
 	ddf_fun_t *fun;
-	usb_pipe_t *intr_in;
-	usb_pipe_t *intr_out;
-	usb_pipe_t *bulk_in;
-	usb_pipe_t *bulk_out;
-	usb_pipe_t *isoch_in;
-	usb_pipe_t *isoch_out;
+
+	usb_pipe_t *burst_intr_in;
+	usb_pipe_t *burst_intr_out;
+	usb_pipe_t *burst_bulk_in;
+	usb_pipe_t *burst_bulk_out;
+	usb_pipe_t *burst_isoch_in;
+	usb_pipe_t *burst_isoch_out;
+
+	usb_pipe_t *data_intr_in;
+	usb_pipe_t *data_intr_out;
+	usb_pipe_t *data_bulk_in;
+	usb_pipe_t *data_bulk_out;
+	usb_pipe_t *data_isoch_in;
+	usb_pipe_t *data_isoch_out;
+
 } usbdiag_dev_t;
 
Index: uspace/drv/bus/usb/usbdiag/main.c
===================================================================
--- uspace/drv/bus/usb/usbdiag/main.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/drv/bus/usb/usbdiag/main.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -139,45 +139,93 @@
 }
 
-static const usb_endpoint_description_t intr_in_ep = {
-	.transfer_type = USB_TRANSFER_INTERRUPT,
-	.direction = USB_DIRECTION_IN,
-	.interface_class = USB_CLASS_DIAGNOSTIC,
-	.interface_subclass = 0x00,
-	.interface_protocol = 0x01,
-	.flags = 0
-};
-static const usb_endpoint_description_t intr_out_ep = {
-	.transfer_type = USB_TRANSFER_INTERRUPT,
-	.direction = USB_DIRECTION_OUT,
-	.interface_class = USB_CLASS_DIAGNOSTIC,
-	.interface_subclass = 0x00,
-	.interface_protocol = 0x01,
-	.flags = 0
-};
-static const usb_endpoint_description_t bulk_in_ep = {
-	.transfer_type = USB_TRANSFER_BULK,
-	.direction = USB_DIRECTION_IN,
-	.interface_class = USB_CLASS_DIAGNOSTIC,
-	.interface_subclass = 0x00,
-	.interface_protocol = 0x01,
-	.flags = 0
-};
-static const usb_endpoint_description_t bulk_out_ep = {
-	.transfer_type = USB_TRANSFER_BULK,
-	.direction = USB_DIRECTION_OUT,
-	.interface_class = USB_CLASS_DIAGNOSTIC,
-	.interface_subclass = 0x00,
-	.interface_protocol = 0x01,
-	.flags = 0
-};
-static const usb_endpoint_description_t isoch_in_ep = {
-	.transfer_type = USB_TRANSFER_ISOCHRONOUS,
-	.direction = USB_DIRECTION_IN,
-	.interface_class = USB_CLASS_DIAGNOSTIC,
-	.interface_subclass = 0x00,
-	.interface_protocol = 0x01,
-	.flags = 0
-};
-static const usb_endpoint_description_t isoch_out_ep = {
+static const usb_endpoint_description_t burst_intr_in_ep = {
+	.transfer_type = USB_TRANSFER_INTERRUPT,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t burst_intr_out_ep = {
+	.transfer_type = USB_TRANSFER_INTERRUPT,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t burst_bulk_in_ep = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t burst_bulk_out_ep = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t burst_isoch_in_ep = {
+	.transfer_type = USB_TRANSFER_ISOCHRONOUS,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t burst_isoch_out_ep = {
+	.transfer_type = USB_TRANSFER_ISOCHRONOUS,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t data_intr_in_ep = {
+	.transfer_type = USB_TRANSFER_INTERRUPT,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t data_intr_out_ep = {
+	.transfer_type = USB_TRANSFER_INTERRUPT,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t data_bulk_in_ep = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t data_bulk_out_ep = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t data_isoch_in_ep = {
+	.transfer_type = USB_TRANSFER_ISOCHRONOUS,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_DIAGNOSTIC,
+	.interface_subclass = 0x00,
+	.interface_protocol = 0x01,
+	.flags = 0
+};
+static const usb_endpoint_description_t data_isoch_out_ep = {
 	.transfer_type = USB_TRANSFER_ISOCHRONOUS,
 	.direction = USB_DIRECTION_OUT,
@@ -189,10 +237,16 @@
 
 static const usb_endpoint_description_t *diag_endpoints[] = {
-	[USBDIAG_EP_INTR_IN] = &intr_in_ep,
-	[USBDIAG_EP_INTR_OUT] = &intr_out_ep,
-	[USBDIAG_EP_BULK_IN] = &bulk_in_ep,
-	[USBDIAG_EP_BULK_OUT] = &bulk_out_ep,
-	[USBDIAG_EP_ISOCH_IN] = &isoch_in_ep,
-	[USBDIAG_EP_ISOCH_OUT] = &isoch_out_ep,
+	[USBDIAG_EP_BURST_INTR_IN] = &burst_intr_in_ep,
+	[USBDIAG_EP_BURST_INTR_OUT] = &burst_intr_out_ep,
+	[USBDIAG_EP_BURST_BULK_IN] = &burst_bulk_in_ep,
+	[USBDIAG_EP_BURST_BULK_OUT] = &burst_bulk_out_ep,
+	[USBDIAG_EP_BURST_ISOCH_IN] = &burst_isoch_in_ep,
+	[USBDIAG_EP_BURST_ISOCH_OUT] = &burst_isoch_out_ep,
+	[USBDIAG_EP_DATA_INTR_IN] = &data_intr_in_ep,
+	[USBDIAG_EP_DATA_INTR_OUT] = &data_intr_out_ep,
+	[USBDIAG_EP_DATA_BULK_IN] = &data_bulk_in_ep,
+	[USBDIAG_EP_DATA_BULK_OUT] = &data_bulk_out_ep,
+	[USBDIAG_EP_DATA_ISOCH_IN] = &data_isoch_in_ep,
+	[USBDIAG_EP_DATA_ISOCH_OUT] = &data_isoch_out_ep,
 	NULL
 };
Index: uspace/drv/bus/usb/usbdiag/tests.c
===================================================================
--- uspace/drv/bus/usb/usbdiag/tests.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/drv/bus/usb/usbdiag/tests.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -44,10 +44,22 @@
 #define NAME "usbdiag"
 
-static int burst_in_test(usb_pipe_t *pipe, int cycles, size_t size, usbdiag_dur_t *duration)
+static const uint32_t test_data_src = 0xDEADBEEF;
+
+static int test_in(usb_pipe_t *pipe, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
 {
 	if (!pipe)
 		return EBADMEM;
 
-	char *buffer = usb_pipe_alloc_buffer(pipe, size);
+	bool validate = params->validate_data;
+	size_t size = params->transfer_size;
+	if (!size)
+		size = pipe->desc.max_transfer_size;
+
+	const uint32_t test_data = uint32_host2usb(test_data_src);
+	if (validate && size % sizeof(test_data))
+		return EINVAL;
+
+	size_t test_data_size = size / sizeof(test_data);
+	char *buffer = (char *) malloc(size);
 	if (!buffer)
 		return ENOMEM;
@@ -55,11 +67,19 @@
 	// TODO: Are we sure that no other test is running on this endpoint?
 
-	usb_log_info("Performing %s IN burst test.", usb_str_transfer_type(pipe->desc.transfer_type));
+	usb_log_info("Performing %s IN test with duration %ld ms.", usb_str_transfer_type(pipe->desc.transfer_type), params->min_duration);
 
 	int rc = EOK;
-	struct timeval start_time;
+	uint32_t transfer_count = 0;
+
+	struct timeval start_time, final_time, stop_time;
 	gettimeofday(&start_time, NULL);
-
-	for (int i = 0; i < cycles; ++i) {
+	gettimeofday(&stop_time, NULL);
+
+	tv_add_diff(&stop_time, params->min_duration * 1000);
+	gettimeofday(&final_time, NULL);
+
+	while (!tv_gt(&final_time, &stop_time)) {
+		++transfer_count;
+
 		// Read device's response.
 		size_t remaining = size;
@@ -83,24 +103,53 @@
 		if (rc)
 			break;
-	}
-
-	struct timeval final_time;
-	gettimeofday(&final_time, NULL);
+
+		if (validate) {
+			uint32_t *beef_buffer = (uint32_t *) buffer;
+
+			/* Check if the beef is really dead. */
+			for (size_t i = 0; i < test_data_size; ++i) {
+				if (beef_buffer[i] != test_data) {
+					usb_log_error("Read of %s IN endpoint returned "
+						"invalid data at address %zu. [ 0x%X != 0x%X ]",
+						usb_str_transfer_type(pipe->desc.transfer_type), i * sizeof(test_data), beef_buffer[i], test_data);
+					rc = EINVAL;
+				}
+			}
+
+			if (rc)
+				break;
+		}
+
+		gettimeofday(&final_time, NULL);
+	}
+
 	usbdiag_dur_t in_duration = ((final_time.tv_usec - start_time.tv_usec) / 1000) +
 	    ((final_time.tv_sec - start_time.tv_sec) * 1000);
 
-	usb_log_info("Burst test on %s IN endpoint completed in %lu ms.", usb_str_transfer_type(pipe->desc.transfer_type), in_duration);
-
-	usb_pipe_free_buffer(pipe, buffer);
-	if (duration)
-		*duration = in_duration;
+	usb_log_info("Test on %s IN endpoint completed in %lu ms.", usb_str_transfer_type(pipe->desc.transfer_type), in_duration);
+
+	results->act_duration = in_duration;
+	results->transfer_count = transfer_count;
+	results->transfer_size = size;
+
+	free(buffer);
 
 	return rc;
 }
 
-static int burst_out_test(usb_pipe_t *pipe, int cycles, size_t size, usbdiag_dur_t *duration)
+static int test_out(usb_pipe_t *pipe, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
 {
 	if (!pipe)
 		return EBADMEM;
+
+	bool validate = params->validate_data;
+	size_t size = params->transfer_size;
+	if (!size)
+		size = pipe->desc.max_transfer_size;
+
+	const uint32_t test_data = uint32_host2usb(test_data_src);
+
+	if (validate && size % sizeof(test_data))
+		return EINVAL;
 
 	char *buffer = usb_pipe_alloc_buffer(pipe, size);
@@ -108,15 +157,27 @@
 		return ENOMEM;
 
-	memset(buffer, 42, size);
+	if (validate) {
+		for (size_t i = 0; i < size; i += sizeof(test_data)) {
+			memcpy(buffer + i, &test_data, sizeof(test_data));
+		}
+	}
 
 	// TODO: Are we sure that no other test is running on this endpoint?
 
-	usb_log_info("Performing %s OUT burst test.", usb_str_transfer_type(pipe->desc.transfer_type));
+	usb_log_info("Performing %s OUT test.", usb_str_transfer_type(pipe->desc.transfer_type));
 
 	int rc = EOK;
-	struct timeval start_time;
+	uint32_t transfer_count = 0;
+
+	struct timeval start_time, final_time, stop_time;
 	gettimeofday(&start_time, NULL);
-
-	for (int i = 0; i < cycles; ++i) {
+	gettimeofday(&stop_time, NULL);
+
+	tv_add_diff(&stop_time, params->min_duration * 1000);
+	gettimeofday(&final_time, NULL);
+
+	while (!tv_gt(&final_time, &stop_time)) {
+		++transfer_count;
+
 		// Write buffer to device.
 		if ((rc = usb_pipe_write_dma(pipe, buffer, size))) {
@@ -124,144 +185,23 @@
 			break;
 		}
-	}
-
-	struct timeval final_time;
-	gettimeofday(&final_time, NULL);
+
+		gettimeofday(&final_time, NULL);
+	}
+
 	usbdiag_dur_t in_duration = ((final_time.tv_usec - start_time.tv_usec) / 1000) +
 	    ((final_time.tv_sec - start_time.tv_sec) * 1000);
 
-	usb_log_info("Burst test on %s OUT endpoint completed in %ld ms.", usb_str_transfer_type(pipe->desc.transfer_type), in_duration);
-
-	usb_pipe_free_buffer(pipe, buffer);
-	if (duration)
-		*duration = in_duration;
+	usb_log_info("Test on %s OUT endpoint completed in %ld ms.", usb_str_transfer_type(pipe->desc.transfer_type), in_duration);
+
+	results->act_duration = in_duration;
+	results->transfer_count = transfer_count;
+	results->transfer_size = size;
+
+	free(buffer);
 
 	return rc;
 }
 
-static const uint32_t test_data_src = 0xDEADBEEF;
-
-static int data_in_test(usb_pipe_t *pipe, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!pipe)
-		return EBADMEM;
-
-	const uint32_t test_data = uint32_host2usb(test_data_src);
-
-	if (size % sizeof(test_data))
-		return EINVAL;
-
-	char *buffer = usb_pipe_alloc_buffer(pipe, size);
-	if (!buffer)
-		return ENOMEM;
-
-	// TODO: Are we sure that no other test is running on this endpoint?
-
-	usb_log_info("Performing %s IN data test.", usb_str_transfer_type(pipe->desc.transfer_type));
-
-	int rc = EOK;
-	struct timeval start_time;
-	gettimeofday(&start_time, NULL);
-
-	for (int i = 0; i < cycles; ++i) {
-		// Read device's response.
-		size_t remaining = size;
-		size_t transferred;
-
-		while (remaining > 0) {
-			if ((rc = usb_pipe_read_dma(pipe, buffer + size - remaining, remaining, &transferred))) {
-				usb_log_error("Read of %s IN endpoint failed with error: %s", usb_str_transfer_type(pipe->desc.transfer_type), str_error(rc));
-				break;
-			}
-
-			if (transferred > remaining) {
-				usb_log_error("Read of %s IN endpoint returned more data than expected.", usb_str_transfer_type(pipe->desc.transfer_type));
-				rc = EINVAL;
-				break;
-			}
-
-			remaining -= transferred;
-		}
-
-		if (rc)
-			break;
-
-		for (size_t i = 0; i < size; i += sizeof(test_data)) {
-			if (*(uint32_t *)(buffer + i) != test_data) {
-				usb_log_error("Read of %s IN endpoint returned "
-				    "invald data at address %zu.",
-				    usb_str_transfer_type(pipe->desc.transfer_type), i);
-				rc = EINVAL;
-				break;
-			}
-		}
-
-		if (rc)
-			break;
-	}
-
-	struct timeval final_time;
-	gettimeofday(&final_time, NULL);
-	usbdiag_dur_t in_duration = ((final_time.tv_usec - start_time.tv_usec) / 1000) +
-	    ((final_time.tv_sec - start_time.tv_sec) * 1000);
-
-	usb_log_info("Data test on %s IN endpoint completed in %lu ms.", usb_str_transfer_type(pipe->desc.transfer_type), in_duration);
-
-	usb_pipe_free_buffer(pipe, buffer);
-	if (duration)
-		*duration = in_duration;
-
-	return rc;
-}
-
-static int data_out_test(usb_pipe_t *pipe, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!pipe)
-		return EBADMEM;
-
-	const uint32_t test_data = uint32_host2usb(test_data_src);
-
-	if (size % sizeof(test_data))
-		return EINVAL;
-
-	char *buffer = usb_pipe_alloc_buffer(pipe, size);
-	if (!buffer)
-		return ENOMEM;
-
-	for (size_t i = 0; i < size; i += sizeof(test_data)) {
-		memcpy(buffer + i, &test_data, sizeof(test_data));
-	}
-
-	// TODO: Are we sure that no other test is running on this endpoint?
-
-	usb_log_info("Performing %s OUT data test.", usb_str_transfer_type(pipe->desc.transfer_type));
-
-	int rc = EOK;
-	struct timeval start_time;
-	gettimeofday(&start_time, NULL);
-
-	for (int i = 0; i < cycles; ++i) {
-		// Write buffer to device.
-		if ((rc = usb_pipe_write_dma(pipe, buffer, size))) {
-			usb_log_error("Write to %s OUT endpoint failed with error: %s", usb_str_transfer_type(pipe->desc.transfer_type), str_error(rc));
-			break;
-		}
-	}
-
-	struct timeval final_time;
-	gettimeofday(&final_time, NULL);
-	usbdiag_dur_t in_duration = ((final_time.tv_usec - start_time.tv_usec) / 1000) +
-	    ((final_time.tv_sec - start_time.tv_sec) * 1000);
-
-	usb_log_info("Data test on %s OUT endpoint completed in %ld ms.", usb_str_transfer_type(pipe->desc.transfer_type), in_duration);
-
-	usb_pipe_free_buffer(pipe, buffer);
-	if (duration)
-		*duration = in_duration;
-
-	return rc;
-}
-
-int usbdiag_burst_test_intr_in(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
+int usbdiag_dev_test_in(ddf_fun_t *fun, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
 {
 	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
@@ -269,8 +209,24 @@
 		return EBADMEM;
 
-	return burst_in_test(dev->intr_in, cycles, size, duration);
-}
-
-int usbdiag_burst_test_intr_out(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
+	usb_pipe_t *pipe;
+
+	switch (params->transfer_type) {
+	case USB_TRANSFER_INTERRUPT:
+		pipe = params->validate_data ? dev->data_intr_in : dev->burst_intr_in;
+		break;
+	case USB_TRANSFER_BULK:
+		pipe = params->validate_data ? dev->data_bulk_in : dev->burst_bulk_in;
+		break;
+	case USB_TRANSFER_ISOCHRONOUS:
+		pipe = params->validate_data ? dev->data_isoch_in : dev->burst_isoch_in;
+		break;
+	default:
+		return ENOTSUP;
+	}
+
+	return test_in(pipe, params, results);
+}
+
+int usbdiag_dev_test_out(ddf_fun_t *fun, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
 {
 	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
@@ -278,95 +234,21 @@
 		return EBADMEM;
 
-	return burst_out_test(dev->intr_out, cycles, size, duration);
-}
-
-int usbdiag_burst_test_bulk_in(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return burst_in_test(dev->bulk_in, cycles, size, duration);
-}
-
-int usbdiag_burst_test_bulk_out(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return burst_out_test(dev->bulk_out, cycles, size, duration);
-}
-
-int usbdiag_burst_test_isoch_in(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return burst_in_test(dev->isoch_in, cycles, size, duration);
-}
-
-int usbdiag_burst_test_isoch_out(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return burst_out_test(dev->isoch_out, cycles, size, duration);
-}
-
-int usbdiag_data_test_intr_in(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return data_in_test(dev->intr_in, cycles, size, duration);
-}
-
-int usbdiag_data_test_intr_out(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return data_out_test(dev->intr_out, cycles, size, duration);
-}
-
-int usbdiag_data_test_bulk_in(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return data_in_test(dev->bulk_in, cycles, size, duration);
-}
-
-int usbdiag_data_test_bulk_out(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return data_out_test(dev->bulk_out, cycles, size, duration);
-}
-
-int usbdiag_data_test_isoch_in(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return data_in_test(dev->isoch_in, cycles, size, duration);
-}
-
-int usbdiag_data_test_isoch_out(ddf_fun_t *fun, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	usbdiag_dev_t *dev = ddf_fun_to_usbdiag_dev(fun);
-	if (!dev)
-		return EBADMEM;
-
-	return data_out_test(dev->isoch_out, cycles, size, duration);
+	usb_pipe_t *pipe;
+
+	switch (params->transfer_type) {
+	case USB_TRANSFER_INTERRUPT:
+		pipe = params->validate_data ? dev->data_intr_out : dev->burst_intr_out;
+		break;
+	case USB_TRANSFER_BULK:
+		pipe = params->validate_data ? dev->data_bulk_out : dev->burst_bulk_out;
+		break;
+	case USB_TRANSFER_ISOCHRONOUS:
+		pipe = params->validate_data ? dev->data_isoch_out : dev->burst_isoch_out;
+		break;
+	default:
+		return ENOTSUP;
+	}
+
+	return test_out(pipe, params, results);
 }
 
Index: uspace/drv/bus/usb/usbdiag/tests.h
===================================================================
--- uspace/drv/bus/usb/usbdiag/tests.h	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/drv/bus/usb/usbdiag/tests.h	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -39,17 +39,6 @@
 #include <ddf/driver.h>
 
-int usbdiag_burst_test_intr_in(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_burst_test_intr_out(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_burst_test_bulk_in(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_burst_test_bulk_out(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_burst_test_isoch_in(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_burst_test_isoch_out(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-
-int usbdiag_data_test_intr_in(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_data_test_intr_out(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_data_test_bulk_in(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_data_test_bulk_out(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_data_test_isoch_in(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
-int usbdiag_data_test_isoch_out(ddf_fun_t *, int, size_t, usbdiag_dur_t *);
+int usbdiag_dev_test_in(ddf_fun_t *, const usbdiag_test_params_t *, usbdiag_test_results_t *);
+int usbdiag_dev_test_out(ddf_fun_t *, const usbdiag_test_params_t *, usbdiag_test_results_t *);
 
 #endif /* USBDIAG_TESTS_H_ */
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -88,5 +88,11 @@
 	if (dev->speed >= USB_SPEED_SUPER) {
 		ep->packets_per_uframe = xhci_ep->max_burst * xhci_ep->mult;
-		ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
+		if (ep->transfer_type == USB_TRANSFER_ISOCHRONOUS
+			|| ep->transfer_type == USB_TRANSFER_INTERRUPT) {
+			ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
+		}
+		else {
+			ep->max_transfer_size = 200 * PAGE_SIZE;
+		}
 	}
 
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -126,4 +126,28 @@
 }
 
+static int calculate_trb_count(xhci_transfer_t *transfer)
+{
+	const size_t size = transfer->batch.buffer_size;
+	return (size + PAGE_SIZE - 1 )/ PAGE_SIZE;
+}
+
+static void trb_set_buffer(xhci_transfer_t *transfer, xhci_trb_t *trb,
+	size_t i, size_t total, size_t *remaining)
+{
+	const uintptr_t ptr = dma_buffer_phys(&transfer->hc_buffer,
+		transfer->hc_buffer.virt + i * PAGE_SIZE);
+
+	trb->parameter = host2xhci(64, ptr);
+	TRB_CTRL_SET_TD_SIZE(*trb, max(31, total - i - 1));
+	if (*remaining > PAGE_SIZE) {
+		TRB_CTRL_SET_XFER_LEN(*trb, PAGE_SIZE);
+		*remaining -= PAGE_SIZE;
+	}
+	else {
+		TRB_CTRL_SET_XFER_LEN(*trb, *remaining);
+		*remaining = 0;
+	}
+}
+
 static int schedule_control(xhci_hc_t* hc, xhci_transfer_t* transfer)
 {
@@ -133,8 +157,12 @@
 	usb_device_request_setup_packet_t* setup = &batch->setup.packet;
 
-	xhci_trb_t trbs[3];
-	int trbs_used = 0;
-
-	xhci_trb_t *trb_setup = trbs + trbs_used++;
+	size_t buffer_count = 0;
+	if (setup->length > 0) {
+		buffer_count = calculate_trb_count(transfer);
+	}
+
+	xhci_trb_t trbs[buffer_count + 2];
+
+	xhci_trb_t *trb_setup = trbs;
 	xhci_trb_clean(trb_setup);
 
@@ -155,26 +183,25 @@
 
 	/* Data stage */
-	xhci_trb_t *trb_data = NULL;
 	if (setup->length > 0) {
-		trb_data = trbs + trbs_used++;
-		xhci_trb_clean(trb_data);
-
-		trb_data->parameter = host2xhci(64, transfer->hc_buffer.phys);
-
-		// data size (sent for OUT, or buffer size)
-		TRB_CTRL_SET_XFER_LEN(*trb_data, batch->buffer_size);
-		// FIXME: TD size 4.11.2.4
-		TRB_CTRL_SET_TD_SIZE(*trb_data, 1);
-
-		// Some more fields here, no idea what they mean
-		TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
-
 		int stage_dir = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
 					? STAGE_IN : STAGE_OUT;
-		TRB_CTRL_SET_DIR(*trb_data, stage_dir);
+		size_t remaining = transfer->batch.buffer_size;
+
+		for (size_t i = 0; i < buffer_count; ++i) {
+			xhci_trb_clean(&trbs[i + 1]);
+			trb_set_buffer(transfer, &trbs[i + 1], i, buffer_count, &remaining);
+
+			TRB_CTRL_SET_DIR(trbs[i + 1], stage_dir);
+			TRB_CTRL_SET_TRB_TYPE(trbs[i + 1], XHCI_TRB_TYPE_DATA_STAGE);
+
+			if (i == buffer_count - 1) break;
+
+			/* Set the chain bit as this is not the last TRB */
+			TRB_CTRL_SET_CHAIN(trbs[i], 1);
+		}
 	}
 
 	/* Status stage */
-	xhci_trb_t *trb_status = trbs + trbs_used++;
+	xhci_trb_t *trb_status = trbs + buffer_count + 1;
 	xhci_trb_clean(trb_status);
 
@@ -192,35 +219,34 @@
 
 	return xhci_trb_ring_enqueue_multiple(get_ring(transfer), trbs,
-	    trbs_used, &transfer->interrupt_trb_phys);
+	    buffer_count + 2, &transfer->interrupt_trb_phys);
 }
 
 static int schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
 {
-	xhci_trb_t trb;
-	xhci_trb_clean(&trb);
-	trb.parameter = host2xhci(64, transfer->hc_buffer.phys);
-
-	// data size (sent for OUT, or buffer size)
-	TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
-
 	/* The stream-enabled endpoints need to chain ED trb */
 	xhci_endpoint_t *ep = xhci_endpoint_get(transfer->batch.ep);
 	if (!ep->primary_stream_data_size) {
-		// FIXME: TD size 4.11.2.4
-		TRB_CTRL_SET_TD_SIZE(trb, 1);
-
-		// we want an interrupt after this td is done
-		TRB_CTRL_SET_IOC(trb, 1);
-		TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
+		const size_t buffer_count = calculate_trb_count(transfer);
+		xhci_trb_t trbs[buffer_count];
+		size_t remaining = transfer->batch.buffer_size;
+
+		for (size_t i = 0; i < buffer_count; ++i) {
+			xhci_trb_clean(&trbs[i]);
+			trb_set_buffer(transfer, &trbs[i], i, buffer_count, &remaining);
+			TRB_CTRL_SET_TRB_TYPE(trbs[i], XHCI_TRB_TYPE_NORMAL);
+
+			if (i == buffer_count - 1) break;
+
+			/* Set the chain bit as this is not the last TRB */
+			TRB_CTRL_SET_CHAIN(trbs[i], 1);
+		}
+		/* Set the interrupt bit for last TRB */
+		TRB_CTRL_SET_IOC(trbs[buffer_count - 1], 1);
 
 		xhci_trb_ring_t* ring = get_ring(transfer);
-		return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+		return xhci_trb_ring_enqueue_multiple(ring, &trbs[0], buffer_count,
+			&transfer->interrupt_trb_phys);
 	}
 	else {
-		TRB_CTRL_SET_TD_SIZE(trb, 2);
-		TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
-		TRB_CTRL_SET_CHAIN(trb, 1);
-		TRB_CTRL_SET_ENT(trb, 1);
-
 		xhci_trb_ring_t* ring = get_ring(transfer);
 		if (!ring) {
@@ -228,16 +254,23 @@
 		}
 
-		int err = xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
-
-		if (err) {
-			return err;
-		}
-
-		xhci_trb_clean(&trb);
-		trb.parameter = host2xhci(64, (uintptr_t) transfer);
-		TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_EVENT_DATA);
-		TRB_CTRL_SET_IOC(trb, 1);
-
-		return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+		const size_t buffer_count = calculate_trb_count(transfer);
+		xhci_trb_t trbs[buffer_count + 1];
+		size_t remaining = transfer->batch.buffer_size;
+
+		for (size_t i = 0; i < buffer_count; ++i) {
+			xhci_trb_clean(&trbs[i]);
+			trb_set_buffer(transfer, &trbs[i], i, buffer_count + 1, &remaining);
+			TRB_CTRL_SET_TRB_TYPE(trbs[i], XHCI_TRB_TYPE_NORMAL);
+			TRB_CTRL_SET_CHAIN(trbs[i], 1);
+		}
+		TRB_CTRL_SET_ENT(trbs[buffer_count - 1], 1);
+
+		xhci_trb_clean(&trbs[buffer_count]);
+		trbs[buffer_count].parameter = host2xhci(64, (uintptr_t) transfer);
+		TRB_CTRL_SET_TRB_TYPE(trbs[buffer_count], XHCI_TRB_TYPE_EVENT_DATA);
+		TRB_CTRL_SET_IOC(trbs[buffer_count], 1);
+
+		return xhci_trb_ring_enqueue_multiple(ring, &trbs[0], buffer_count + 1,
+			&transfer->interrupt_trb_phys);
 	}
 }
@@ -245,21 +278,24 @@
 static int schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
 {
-	xhci_trb_t trb;
-	xhci_trb_clean(&trb);
-	trb.parameter = host2xhci(64, transfer->hc_buffer.phys);
-
-	// data size (sent for OUT, or buffer size)
-	TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
-	// FIXME: TD size 4.11.2.4
-	TRB_CTRL_SET_TD_SIZE(trb, 1);
-
-	// we want an interrupt after this td is done
-	TRB_CTRL_SET_IOC(trb, 1);
-
-	TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
+	const size_t buffer_count = calculate_trb_count(transfer);
+	xhci_trb_t trbs[buffer_count];
+	size_t remaining = transfer->batch.buffer_size;
+
+	for (size_t i = 0; i < buffer_count; ++i) {
+		xhci_trb_clean(&trbs[i]);
+		trb_set_buffer(transfer, &trbs[i], i, buffer_count, &remaining);
+		TRB_CTRL_SET_TRB_TYPE(trbs[i], XHCI_TRB_TYPE_NORMAL);
+
+		if (i == buffer_count - 1) break;
+
+		/* Set the chain bit as this is not the last TRB */
+		TRB_CTRL_SET_CHAIN(trbs[i], 1);
+	}
+	/* Set the interrupt bit for last TRB */
+	TRB_CTRL_SET_IOC(trbs[buffer_count - 1], 1);
 
 	xhci_trb_ring_t* ring = get_ring(transfer);
-
-	return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+	return xhci_trb_ring_enqueue_multiple(ring, &trbs[0], buffer_count,
+		&transfer->interrupt_trb_phys);
 }
 
Index: uspace/lib/drv/generic/remote_usbdiag.c
===================================================================
--- uspace/lib/drv/generic/remote_usbdiag.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/lib/drv/generic/remote_usbdiag.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -43,16 +43,6 @@
 
 typedef enum {
-	IPC_M_USBDIAG_BURST_INTR_IN,
-	IPC_M_USBDIAG_BURST_INTR_OUT,
-	IPC_M_USBDIAG_BURST_BULK_IN,
-	IPC_M_USBDIAG_BURST_BULK_OUT,
-	IPC_M_USBDIAG_BURST_ISOCH_IN,
-	IPC_M_USBDIAG_BURST_ISOCH_OUT,
-	IPC_M_USBDIAG_DATA_INTR_IN,
-	IPC_M_USBDIAG_DATA_INTR_OUT,
-	IPC_M_USBDIAG_DATA_BULK_IN,
-	IPC_M_USBDIAG_DATA_BULK_OUT,
-	IPC_M_USBDIAG_DATA_ISOCH_IN,
-	IPC_M_USBDIAG_DATA_ISOCH_OUT
+	IPC_M_USBDIAG_TEST_IN,
+	IPC_M_USBDIAG_TEST_OUT,
 } usb_iface_funcs_t;
 
@@ -68,200 +58,71 @@
 }
 
-int usbdiag_burst_intr_in(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
+int usbdiag_test_in(async_exch_t *exch, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
 {
 	if (!exch)
 		return EBADMEM;
 
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_BURST_INTR_IN, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_burst_intr_out(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_TEST_IN, &answer);
+
+	int 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 (int) retval;
+}
+
+int usbdiag_test_out(async_exch_t *exch, const usbdiag_test_params_t *params, usbdiag_test_results_t *results)
 {
 	if (!exch)
 		return EBADMEM;
 
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_BURST_INTR_OUT, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_burst_bulk_in(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_BURST_BULK_IN, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_burst_bulk_out(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_BURST_BULK_OUT, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_burst_isoch_in(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_BURST_ISOCH_IN, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_burst_isoch_out(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_BURST_ISOCH_OUT, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_data_intr_in(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_DATA_INTR_IN, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_data_intr_out(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_DATA_INTR_OUT, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_data_bulk_in(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_DATA_BULK_IN, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_data_bulk_out(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_DATA_BULK_OUT, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_data_isoch_in(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_DATA_ISOCH_IN, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-int usbdiag_data_isoch_out(async_exch_t *exch, int cycles, size_t size, usbdiag_dur_t *duration)
-{
-	if (!exch)
-		return EBADMEM;
-
-	sysarg_t duration_;
-	const int rc = async_req_3_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_DATA_ISOCH_OUT, cycles, size, &duration_);
-
-	if (rc == EOK && duration)
-		*duration = duration_;
-
-	return rc;
-}
-
-static void remote_usbdiag_burst_intr_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_burst_intr_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_burst_bulk_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_burst_bulk_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_burst_isoch_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_burst_isoch_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-
-static void remote_usbdiag_data_intr_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_data_intr_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_data_bulk_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_data_bulk_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_data_isoch_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbdiag_data_isoch_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, DEV_IFACE_ID(USBDIAG_DEV_IFACE), IPC_M_USBDIAG_TEST_OUT, &answer);
+
+	int 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 (int) 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_BURST_INTR_IN] = remote_usbdiag_burst_intr_in,
-	[IPC_M_USBDIAG_BURST_INTR_OUT] = remote_usbdiag_burst_intr_out,
-	[IPC_M_USBDIAG_BURST_BULK_IN] = remote_usbdiag_burst_bulk_in,
-	[IPC_M_USBDIAG_BURST_BULK_OUT] = remote_usbdiag_burst_bulk_out,
-	[IPC_M_USBDIAG_BURST_ISOCH_IN] = remote_usbdiag_burst_isoch_in,
-	[IPC_M_USBDIAG_BURST_ISOCH_OUT] = remote_usbdiag_burst_isoch_out,
-	[IPC_M_USBDIAG_DATA_INTR_IN] = remote_usbdiag_data_intr_in,
-	[IPC_M_USBDIAG_DATA_INTR_OUT] = remote_usbdiag_data_intr_out,
-	[IPC_M_USBDIAG_DATA_BULK_IN] = remote_usbdiag_data_bulk_in,
-	[IPC_M_USBDIAG_DATA_BULK_OUT] = remote_usbdiag_data_bulk_out,
-	[IPC_M_USBDIAG_DATA_ISOCH_IN] = remote_usbdiag_data_isoch_in,
-	[IPC_M_USBDIAG_DATA_ISOCH_OUT] = remote_usbdiag_data_isoch_out
+	[IPC_M_USBDIAG_TEST_IN] = remote_usbdiag_test_in,
+	[IPC_M_USBDIAG_TEST_OUT] = remote_usbdiag_test_out
 };
 
@@ -272,254 +133,106 @@
 };
 
-void remote_usbdiag_burst_intr_in(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+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;
 
-	if (diag_iface->burst_bulk_in == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->burst_intr_in(fun, cycles, size, &duration);
+	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 int ret = !diag_iface->test_in ? ENOTSUP : diag_iface->test_in(fun, &params, &results);
 
 	if (ret != EOK) {
 		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_burst_intr_out(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+		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;
 
-	if (diag_iface->burst_bulk_out == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->burst_intr_out(fun, cycles, size, &duration);
+	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 int ret = !diag_iface->test_out ? ENOTSUP : diag_iface->test_out(fun, &params, &results);
 
 	if (ret != EOK) {
 		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_burst_bulk_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;
-
-	if (diag_iface->burst_bulk_in == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->burst_bulk_in(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_burst_bulk_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;
-
-	if (diag_iface->burst_bulk_out == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->burst_bulk_out(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_burst_isoch_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;
-
-	if (diag_iface->burst_isoch_in == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->burst_isoch_in(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_burst_isoch_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;
-
-	if (diag_iface->burst_isoch_out == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->burst_isoch_out(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_data_intr_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;
-
-	if (diag_iface->data_bulk_in == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->data_intr_in(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_data_intr_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;
-
-	if (diag_iface->data_bulk_out == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->data_intr_out(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_data_bulk_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;
-
-	if (diag_iface->data_bulk_in == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->data_bulk_in(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_data_bulk_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;
-
-	if (diag_iface->data_bulk_out == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->data_bulk_out(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_data_isoch_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;
-
-	if (diag_iface->data_isoch_in == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->data_isoch_in(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
-}
-
-void remote_usbdiag_data_isoch_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;
-
-	if (diag_iface->data_isoch_out == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	int cycles = DEV_IPC_GET_ARG1(*call);
-	size_t size = DEV_IPC_GET_ARG2(*call);
-	usbdiag_dur_t duration;
-	const int ret = diag_iface->data_isoch_out(fun, cycles, size, &duration);
-
-	if (ret != EOK) {
-		async_answer_0(callid, ret);
-	} else {
-		async_answer_1(callid, EOK, duration);
-	}
+		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/include/usbdiag_iface.h
===================================================================
--- uspace/lib/drv/include/usbdiag_iface.h	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/lib/drv/include/usbdiag_iface.h	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -39,4 +39,5 @@
 
 #include <async.h>
+#include <usbhc_iface.h>
 #include "ddf/driver.h"
 
@@ -46,35 +47,29 @@
 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*);
 
-int usbdiag_burst_intr_in(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_burst_intr_out(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_burst_bulk_in(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_burst_bulk_out(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_burst_isoch_in(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_burst_isoch_out(async_exch_t*, int, size_t, usbdiag_dur_t*);
-
-int usbdiag_data_intr_in(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_data_intr_out(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_data_bulk_in(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_data_bulk_out(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_data_isoch_in(async_exch_t*, int, size_t, usbdiag_dur_t*);
-int usbdiag_data_isoch_out(async_exch_t*, int, size_t, usbdiag_dur_t*);
+int usbdiag_test_in(async_exch_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
+int usbdiag_test_out(async_exch_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
 
 /** USB diagnostic device communication interface. */
 typedef struct {
-	int (*burst_intr_in)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*burst_intr_out)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*burst_bulk_in)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*burst_bulk_out)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*burst_isoch_in)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*burst_isoch_out)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*data_intr_in)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*data_intr_out)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*data_bulk_in)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*data_bulk_out)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*data_isoch_in)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
-	int (*data_isoch_out)(ddf_fun_t*, int, size_t, usbdiag_dur_t*);
+	int (*test_in)(ddf_fun_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
+	int (*test_out)(ddf_fun_t*, const usbdiag_test_params_t *, usbdiag_test_results_t *);
 } usbdiag_iface_t;
 
Index: uspace/lib/usbdev/src/pipesinit.c
===================================================================
--- uspace/lib/usbdev/src/pipesinit.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/lib/usbdev/src/pipesinit.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -146,5 +146,6 @@
 		if (interface_number_fits
 		    && interface_setting_fits
-		    && endpoint_descriptions_fits) {
+		    && endpoint_descriptions_fits
+		    && !mapping->present) {
 			return mapping;
 		}
@@ -153,4 +154,5 @@
 		mapping_count--;
 	}
+
 	return NULL;
 }
Index: uspace/lib/usbhost/src/endpoint.c
===================================================================
--- uspace/lib/usbhost/src/endpoint.c	(revision e67c50ac9b5c850d056f789863dea7d4da95af3a)
+++ uspace/lib/usbhost/src/endpoint.c	(revision 64ce0c1435496748632f7682b2657fdc79aad634)
@@ -244,7 +244,5 @@
 
 	/** Limit transfers with reserved bandwidth to the amount reserved */
-	if ((ep->transfer_type == USB_TRANSFER_INTERRUPT
-	    || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)
-	    && size > ep->max_transfer_size)
+	if (ep->direction == USB_DIRECTION_OUT && size > ep->max_transfer_size)
 		return ENOSPC;
 
