Index: uspace/app/usb/example.c
===================================================================
--- uspace/app/usb/example.c	(revision 954ea70e3458816f21ae5183cb92e63c3a8875e8)
+++ uspace/app/usb/example.c	(revision b8a3cda7a0bb2e2ea3eeb8a5d2368d8d10f4963b)
@@ -46,4 +46,5 @@
 
 #include <usb/hcd.h>
+#include <usb/devreq.h>
 
 #define LOOPS 5
@@ -87,4 +88,5 @@
 				ipc_answer_0(callid, EOK);
 				break;
+				
 			case IPC_M_USB_HCD_DATA_RECEIVED:
 				printf("%s: << Data received over USB (handle %d, outcome %s).\n",
@@ -95,10 +97,14 @@
 					break;
 				}
-				rc = async_data_write_accept(&buffer, false,
-				    1, MAX_SIZE_RECEIVE,
-				    0, &len);
-				if (rc != EOK) {
-					ipc_answer_0(callid, rc);
-					break;
+				len = IPC_GET_ARG3(call);
+				if (len > 0) {
+					rc = async_data_write_accept(&buffer, false,
+					    1, MAX_SIZE_RECEIVE,
+					    0, &len);
+					if (rc != EOK) {
+						ipc_answer_0(callid, rc);
+						break;
+					}
+					free(buffer);
 				}
 				printf("%s: << Received %uB long buffer (handle %d).\n",
@@ -106,6 +112,12 @@
 				ipc_answer_0(callid, EOK);
 				break;
+				
+			case IPC_M_PHONE_HUNGUP:
+				printf("%s: hang-up.\n", NAME);
+				return;
+				
 			default:
-				ipc_answer_0(callid, EINVAL);
+				printf("%s: method %d called.\n", NAME, IPC_GET_METHOD(call));
+				ipc_answer_0(callid, EOK);
 				break;
 		}
@@ -122,43 +134,31 @@
 	}
 	
-	char data[] = "Hullo, World!";
-	int data_len = sizeof(data)/sizeof(data[0]);
+	usb_target_t target = {0, 0};
+	usb_device_request_setup_packet_t setup_packet = {
+		.request_type = 0,
+		.request = USB_DEVREQ_SET_ADDRESS,
+		.index = 0,
+		.length = 0,
+	};
+	setup_packet.value = 5;
+	int rc;
 	
-	size_t i;
-	for (i = 0; i < LOOPS; i++) {
-		usb_transaction_handle_t handle;
-
-		usb_target_t target = { i, 0 };
-		int rc = usb_hcd_send_data_to_function(hcd_phone,
-		    target, USB_TRANSFER_ISOCHRONOUS,
-		    data, data_len,
-		    &handle);
-		if (rc != EOK) {
-			printf("%s: >> Failed to send data to function over HCD (%d: %s).\n",
-				NAME, rc, str_error(rc));
-			continue;
-		}
-
-		printf("%s: >> Transaction to function dispatched (handle %d).\n", NAME, handle);
-		
-		fibril_sleep(1);
-		
-		rc = usb_hcd_prepare_data_reception(hcd_phone,
-		    target, USB_TRANSFER_INTERRUPT,
-		    MAX_SIZE_RECEIVE,
-		    &handle);
-		if (rc != EOK) {
-			printf("%s: << Failed to start transaction for data receivement over HCD (%d: %s).\n",
-				NAME, rc, str_error(rc));
-			continue;
-		}
-		
-		printf("%s: << Transaction from function started (handle %d).\n", NAME, handle);
-		
-		fibril_sleep(2);
+	printf("%s: usb_hcd_transfer_control_write_setup(...)\n", NAME);
+	rc = usb_hcd_transfer_control_write_setup(hcd_phone, target,
+	    &setup_packet, sizeof(setup_packet), NULL);
+	if (rc != EOK) {
+		printf("%s: failed setting address (%d).\n", NAME, rc);
+		return rc;
 	}
 	
-	printf("%s: Waiting for transactions to be finished...\n", NAME);
-	fibril_sleep(10);
+	printf("%s: usb_hcd_transfer_control_write_status(...)\n", NAME);
+	rc = usb_hcd_transfer_control_write_status(hcd_phone, target, NULL);
+	if (rc != EOK) {
+		printf("%s: failed completing control transfer (%d).\n", NAME, rc);
+		return rc;
+	}
+	
+	printf("%s: sleeping for a while...\n", NAME);
+	fibril_sleep(5);
 	
 	printf("%s: exiting.\n", NAME);
Index: uspace/srv/hw/bus/usb/hcd/virtual/conndev.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision 954ea70e3458816f21ae5183cb92e63c3a8875e8)
+++ uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision b8a3cda7a0bb2e2ea3eeb8a5d2368d8d10f4963b)
@@ -51,4 +51,5 @@
 		.endpoint = IPC_GET_ARG2(icall)
 	};
+	size_t len = IPC_GET_ARG3(icall);
 	
 	if (!hub_can_device_signal(dev)) {
@@ -60,16 +61,19 @@
 	    target.address, target.endpoint);
 	
-	size_t len;
-	void * buffer;
-	int rc = async_data_write_accept(&buffer, false,
-	    1, USB_MAX_PAYLOAD_SIZE,
-	    0, &len);
+	int rc;
 	
-	if (rc != EOK) {
-		ipc_answer_0(iid, rc);
-		return;
+	void * buffer = NULL;
+	if (len > 0) {
+		rc = async_data_write_accept(&buffer, false,
+		    1, USB_MAX_PAYLOAD_SIZE,
+		    0, &len);
+		
+		if (rc != EOK) {
+			ipc_answer_0(iid, rc);
+			return;
+		}
 	}
 	
-	rc = hc_fillin_transaction_from_device(USB_TRANSFER_INTERRUPT, target, buffer, len);
+	rc = hc_fillin_transaction_from_device(target, buffer, len);
 	
 	ipc_answer_0(iid, rc);
Index: uspace/srv/hw/bus/usb/hcd/virtual/connhost.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/connhost.c	(revision 954ea70e3458816f21ae5183cb92e63c3a8875e8)
+++ uspace/srv/hw/bus/usb/hcd/virtual/connhost.c	(revision b8a3cda7a0bb2e2ea3eeb8a5d2368d8d10f4963b)
@@ -84,8 +84,10 @@
 	    &answer_data);
 	
-	rc = async_data_write_start(trans->phone, buffer, len);
-	if (rc != EOK) {
-		async_wait_for(req, NULL);
-		goto leave;
+	if (len > 0) {
+		rc = async_data_write_start(trans->phone, buffer, len);
+		if (rc != EOK) {
+			async_wait_for(req, NULL);
+			goto leave;
+		}
 	}
 	
@@ -98,12 +100,15 @@
 leave:
 	free(trans);
-	free(buffer);
+	if (buffer != NULL) {
+		free(buffer);
+	}
 }
 
 /** Handle data from host to function.
  */
-static void handle_data_to_function(ipc_callid_t iid, ipc_call_t icall, int callback_phone)
-{
-	usb_transfer_type_t transf_type = IPC_GET_ARG3(icall);
+static void handle_data_to_function(ipc_callid_t iid, ipc_call_t icall,
+    bool setup_transaction, int callback_phone)
+{
+	size_t expected_len = IPC_GET_ARG3(icall);
 	usb_target_t target = {
 		.address = IPC_GET_ARG1(icall),
@@ -111,7 +116,6 @@
 	};
 	
-	dprintf("pretending transfer to function (dev=%d:%d, type=%s)",
-	    target.address, target.endpoint,
-	    usb_str_transfer_type(transf_type));
+	dprintf("pretending transfer to function (dev=%d:%d)",
+	    target.address, target.endpoint);
 	
 	if (callback_phone == -1) {
@@ -123,13 +127,15 @@
 	    = create_transaction_handle(callback_phone);
 	
-	size_t len;
-	void * buffer;
-	int rc = async_data_write_accept(&buffer, false,
-	    1, USB_MAX_PAYLOAD_SIZE,
-	    0, &len);
-	
-	if (rc != EOK) {
-		ipc_answer_0(iid, rc);
-		return;
+	size_t len = 0;
+	void * buffer = NULL;
+	if (expected_len > 0) {
+		int rc = async_data_write_accept(&buffer, false,
+		    1, USB_MAX_PAYLOAD_SIZE,
+		    0, &len);
+		
+		if (rc != EOK) {
+			ipc_answer_0(iid, rc);
+			return;
+		}
 	}
 	
@@ -139,5 +145,5 @@
 	
 	dprintf("adding transaction to HC", NAME);
-	hc_add_transaction_to_device(transf_type, target,
+	hc_add_transaction_to_device(setup_transaction, target,
 	    buffer, len,
 	    out_callback, trans);
@@ -151,14 +157,12 @@
 static void handle_data_from_function(ipc_callid_t iid, ipc_call_t icall, int callback_phone)
 {
-	usb_transfer_type_t transf_type = IPC_GET_ARG3(icall);
 	usb_target_t target = {
 		.address = IPC_GET_ARG1(icall),
 		.endpoint = IPC_GET_ARG2(icall)
 	};
-	size_t len = IPC_GET_ARG4(icall);
-	
-	dprintf("pretending transfer from function (dev=%d:%d, type=%s)",
-	    target.address, target.endpoint,
-	    usb_str_transfer_type(transf_type));
+	size_t len = IPC_GET_ARG3(icall);
+	
+	dprintf("pretending transfer from function (dev=%d:%d)",
+	    target.address, target.endpoint);
 	
 	if (callback_phone == -1) {
@@ -170,5 +174,8 @@
 	    = create_transaction_handle(callback_phone);
 	
-	void * buffer = malloc(len);
+	void * buffer = NULL;
+	if (len > 0) {
+		buffer = malloc(len);
+	}
 	
 	transaction_details_t * trans = malloc(sizeof(transaction_details_t));
@@ -177,5 +184,5 @@
 	
 	dprintf("adding transaction to HC", NAME);
-	hc_add_transaction_from_device(transf_type, target,
+	hc_add_transaction_from_device(target,
 	    buffer, len,
 	    in_callback, trans);
@@ -219,5 +226,6 @@
 			
 			case IPC_M_USB_HCD_SEND_DATA:
-				handle_data_to_function(callid, call, host_phone);
+				handle_data_to_function(callid, call,
+				    false, host_phone);
 				break;
 			
@@ -230,4 +238,36 @@
 				break;
 			
+			
+			case IPC_M_USB_HCD_INTERRUPT_OUT:
+				handle_data_to_function(callid, call,
+				    false, host_phone);
+				break;
+				
+			case IPC_M_USB_HCD_INTERRUPT_IN:
+				handle_data_from_function(callid, call, host_phone);
+				break;
+			
+			case IPC_M_USB_HCD_CONTROL_WRITE_SETUP:
+				handle_data_to_function(callid, call,
+				    true, host_phone);
+				break;
+				
+			case IPC_M_USB_HCD_CONTROL_WRITE_DATA:
+				handle_data_to_function(callid, call,
+				    false, host_phone);
+				break;
+				
+			case IPC_M_USB_HCD_CONTROL_WRITE_STATUS:
+				handle_data_from_function(callid, call, host_phone);
+				break;
+			
+			case IPC_M_USB_HCD_CONTROL_READ_SETUP:
+				handle_data_to_function(callid, call,
+				    true, host_phone);
+				break;
+				
+			case IPC_M_USB_HCD_CONTROL_READ_DATA:
+			case IPC_M_USB_HCD_CONTROL_READ_STATUS:
+			
 			default:
 				ipc_answer_0(callid, EINVAL);
Index: uspace/srv/hw/bus/usb/hcd/virtual/devices.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/devices.c	(revision 954ea70e3458816f21ae5183cb92e63c3a8875e8)
+++ uspace/srv/hw/bus/usb/hcd/virtual/devices.c	(revision b8a3cda7a0bb2e2ea3eeb8a5d2368d8d10f4963b)
@@ -48,4 +48,5 @@
 #include "devices.h"
 #include "hub.h"
+#include "vhcd.h"
 
 #define list_foreach(pos, head) \
@@ -124,15 +125,18 @@
 		ipcarg_t answer_rc;
 		aid_t req;
-		int rc;
+		int rc = EOK;
 		
-		req = async_send_3(dev->phone,
+		req = async_send_4(dev->phone,
 		    IPC_M_USBVIRT_DATA_TO_DEVICE,
 		    transaction->target.address,
 		    transaction->target.endpoint,
 		    transaction->type,
+		    transaction->len,
 		    &answer_data);
 		
-		rc = async_data_write_start(dev->phone,
-		    transaction->buffer, transaction->len);
+		if (transaction->len > 0) {
+			rc = async_data_write_start(dev->phone,
+			    transaction->buffer, transaction->len);
+		}
 		if (rc != EOK) {
 			async_wait_for(req, NULL);
Index: uspace/srv/hw/bus/usb/hcd/virtual/hc.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision 954ea70e3458816f21ae5183cb92e63c3a8875e8)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision b8a3cda7a0bb2e2ea3eeb8a5d2368d8d10f4963b)
@@ -51,5 +51,5 @@
 #define USLEEP_BASE (500 * 1000)
 
-#define USLEEP_VAR 10000
+#define USLEEP_VAR 5000
 
 #define SHORTENING_VAR 15
@@ -68,9 +68,7 @@
 static link_t transaction_from_device_list;
 
-#define TRANSACTION_FORMAT "T[%d:%d %s %s (%d)]"
+#define TRANSACTION_FORMAT "T[%d:%d (%d)]"
 #define TRANSACTION_PRINTF(t) \
 	(t).target.address, (t).target.endpoint, \
-	usb_str_transfer_type((t).type), \
-	((t).direction == USB_DIRECTION_IN ? "in" : "out"), \
 	(int)(t).len
 
@@ -132,6 +130,6 @@
 /** Create new transaction
  */
-static transaction_t *transaction_create(usb_transfer_type_t type, usb_target_t target,
-    usb_direction_t direction,
+static transaction_t *transaction_create(usbvirt_transaction_type_t type,
+    usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg)
@@ -142,5 +140,4 @@
 	transaction->type = type;
 	transaction->target = target;
-	transaction->direction = direction;
 	transaction->buffer = buffer;
 	transaction->len = len;
@@ -153,10 +150,11 @@
 /** Add transaction directioned towards the device.
  */
-void hc_add_transaction_to_device(usb_transfer_type_t type, usb_target_t target,
+void hc_add_transaction_to_device(bool setup, usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg)
 {
-	transaction_t *transaction = transaction_create(type, target,
-	    USB_DIRECTION_OUT, buffer, len, callback, arg);
+	transaction_t *transaction = transaction_create(
+	    setup ? USBVIRT_TRANSACTION_SETUP : USBVIRT_TRANSACTION_OUT, target,
+	    buffer, len, callback, arg);
 	list_append(&transaction->link, &transaction_to_device_list);
 }
@@ -164,10 +162,10 @@
 /** Add transaction directioned from the device.
  */
-void hc_add_transaction_from_device(usb_transfer_type_t type, usb_target_t target,
+void hc_add_transaction_from_device(usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg)
 {
-	transaction_t *transaction = transaction_create(type, target,
-	    USB_DIRECTION_IN, buffer, len, callback, arg);
+	transaction_t *transaction = transaction_create(USBVIRT_TRANSACTION_IN,
+	    target, buffer, len, callback, arg);
 	list_append(&transaction->link, &transaction_from_device_list);
 }
@@ -175,8 +173,9 @@
 /** Fill data to existing transaction from device.
  */
-int hc_fillin_transaction_from_device(usb_transfer_type_t type, usb_target_t target,
+int hc_fillin_transaction_from_device(usb_target_t target,
     void * buffer, size_t len)
 {
-	dprintf("finding transaction to fill data in...");
+	dprintf("finding transaction to fill data in (%d:%d)...",
+	    target.address, target.endpoint);
 	int rc;
 	
Index: uspace/srv/hw/bus/usb/hcd/virtual/hc.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hc.h	(revision 954ea70e3458816f21ae5183cb92e63c3a8875e8)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hc.h	(revision b8a3cda7a0bb2e2ea3eeb8a5d2368d8d10f4963b)
@@ -37,4 +37,5 @@
 
 #include <usb/hcd.h>
+#include <usbvirt/ids.h>
 
 /** Callback after transaction is sent to USB.
@@ -52,10 +53,10 @@
 	/** Linked-list link. */
 	link_t link;
+	/** Transaction type. */
+	usbvirt_transaction_type_t type;
 	/** Device address. */
 	usb_target_t target;
 	/** Direction of the transaction. */
 	usb_direction_t direction;
-	/** Transfer type. */
-	usb_transfer_type_t type;
 	/** Transaction data buffer. */
 	void * buffer;
@@ -70,13 +71,13 @@
 void hc_manager(void);
 
-void hc_add_transaction_to_device(usb_transfer_type_t type, usb_target_t target,
+void hc_add_transaction_to_device(bool setup, usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg);
 
-void hc_add_transaction_from_device(usb_transfer_type_t type, usb_target_t target,
+void hc_add_transaction_from_device(usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg);
 
-int hc_fillin_transaction_from_device(usb_transfer_type_t type, usb_target_t target,
+int hc_fillin_transaction_from_device(usb_target_t target,
     void * buffer, size_t len);
 
Index: uspace/srv/hw/bus/usb/hcd/virtual/hub.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision 954ea70e3458816f21ae5183cb92e63c3a8875e8)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision b8a3cda7a0bb2e2ea3eeb8a5d2368d8d10f4963b)
@@ -37,4 +37,5 @@
 #include <usbvirt/device.h>
 #include <errno.h>
+#include <stdlib.h>
 
 #include "vhcd.h"
@@ -143,4 +144,18 @@
 hub_device_t hub_dev;
 
+static int send_data(struct usbvirt_device *dev,
+	    usb_endpoint_t endpoint, void *buffer, size_t size)
+{
+	usb_target_t target = { dev->address, endpoint };
+	void *my_buffer = NULL;
+	if (size > 0) {
+		my_buffer = malloc(size);
+		memcpy(my_buffer, buffer, size);
+	}
+	hc_fillin_transaction_from_device(target, my_buffer, size);
+	
+	return EOK;
+}
+
 void hub_init(void)
 {
@@ -155,4 +170,5 @@
 	
 	usbvirt_connect_local(&virthub_dev);
+	virthub_dev.send_data = send_data;
 	
 	printf("%s: virtual hub (%d ports) created.\n", NAME, HUB_PORT_COUNT);
