Index: uspace/srv/hw/bus/usb/hcd/virtual/Makefile
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -33,4 +33,6 @@
 
 SOURCES = \
+	conndev.c \
+	connhost.c \
 	devices.c \
 	hc.c \
Index: uspace/srv/hw/bus/usb/hcd/virtual/conn.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/conn.h	(revision b3718443703dd0cd113ce025bb4884a81207993a)
+++ uspace/srv/hw/bus/usb/hcd/virtual/conn.h	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 Vojtech Horky
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Connection handling of incoming calls.
+ */
+#ifndef VHCD_CONN_H_
+#define VHCD_CONN_H_
+
+#include <usb/hcd.h>
+#include "vhcd.h"
+#include "devices.h"
+
+void connection_handler_host(ipcarg_t, int);
+void connection_handler_device(ipcarg_t, virtdev_connection_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/conndev.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision b3718443703dd0cd113ce025bb4884a81207993a)
+++ uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2010 Vojtech Horky
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Connection handling of calls from virtual device (implementation).
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <usb/virtdev.h>
+
+#include "conn.h"
+#include "hc.h"
+
+/** Handle data from device to function.
+ */
+static void handle_data_from_device(ipc_callid_t iid, ipc_call_t icall,
+    virtdev_connection_t *dev)
+{
+	usb_endpoint_t endpoint = IPC_GET_ARG1(icall);
+	usb_target_t target = {
+		.address = dev->address,
+		.endpoint = endpoint
+	};
+	
+	dprintf("data from device %d [%d.%d]", dev->id,
+	    target.address, target.endpoint);
+	
+	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;
+	}
+	
+	rc = hc_fillin_transaction_from_device(USB_TRANSFER_INTERRUPT, target, buffer, len);
+	
+	ipc_answer_0(iid, rc);
+}
+
+/** Connection handler for communcation with virtual device.
+ *
+ * @param phone_hash Incoming phone hash.
+ * @param dev Virtual device handle.
+ */
+void connection_handler_device(ipcarg_t phone_hash, virtdev_connection_t *dev)
+{
+	assert(dev != NULL);
+	
+	dprintf("phone%#x: virtual device %d connected [%d]",
+	    phone_hash, dev->id, dev->address);
+	
+	while (true) {
+		ipc_callid_t callid; 
+		ipc_call_t call; 
+		
+		callid = async_get_call(&call);
+		
+		switch (IPC_GET_METHOD(call)) {
+			case IPC_M_PHONE_HUNGUP:
+				ipc_hangup(dev->phone);
+				ipc_answer_0(callid, EOK);
+				dprintf("phone%#x: device %d [%d] hang-up",
+				    phone_hash, dev->id, dev->address);
+				return;
+			
+			case IPC_M_CONNECT_TO_ME:
+				ipc_answer_0(callid, ELIMIT);
+				break;
+			
+			case IPC_M_USB_VIRTDEV_DATA_FROM_DEVICE:
+				handle_data_from_device(callid, call, dev);
+				break;
+			
+			default:
+				ipc_answer_0(callid, EINVAL);
+				break;
+		}
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/connhost.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/connhost.c	(revision b3718443703dd0cd113ce025bb4884a81207993a)
+++ uspace/srv/hw/bus/usb/hcd/virtual/connhost.c	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2010 Vojtech Horky
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Connection handling of calls from host (implementation).
+ */
+#include <assert.h>
+#include <errno.h>
+#include <usb/hcd.h>
+
+#include "vhcd.h"
+#include "conn.h"
+#include "hc.h"
+
+static usb_transaction_handle_t g_handle_seed = 1;
+static usb_transaction_handle_t create_transaction_handle(int phone)
+{
+	return g_handle_seed++;
+}
+
+typedef struct {
+	int phone;
+	usb_transaction_handle_t handle;
+} transaction_details_t;
+
+/** Callback for outgoing transaction.
+ */
+static void out_callback(void * buffer, size_t len, usb_transaction_outcome_t outcome, void * arg)
+{
+	dprintf("out_callback(buffer, %u, %d, %p)", len, outcome, arg);
+	(void)len;
+	transaction_details_t * trans = (transaction_details_t *)arg;
+	
+	async_msg_2(trans->phone, IPC_M_USB_HCD_DATA_SENT, trans->handle, outcome);
+	
+	free(trans);
+	free(buffer);
+}
+
+/** Callback for incoming transaction.
+ */
+static void in_callback(void * buffer, size_t len, usb_transaction_outcome_t outcome, void * arg)
+{
+	dprintf("in_callback(buffer, %u, %d, %p)", len, outcome, arg);
+	transaction_details_t * trans = (transaction_details_t *)arg;
+	
+	ipc_call_t answer_data;
+	ipcarg_t answer_rc;
+	aid_t req;
+	int rc;
+	
+	req = async_send_3(trans->phone,
+	    IPC_M_USB_HCD_DATA_RECEIVED,
+	    trans->handle, outcome,
+	    len,
+	    &answer_data);
+	
+	rc = async_data_write_start(trans->phone, buffer, len);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		goto leave;
+	}
+	
+	async_wait_for(req, &answer_rc);
+	rc = (int)answer_rc;
+	if (rc != EOK) {
+		goto leave;
+	}
+	
+leave:
+	free(trans);
+	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);
+	usb_target_t target = {
+		.address = IPC_GET_ARG1(icall),
+		.endpoint = IPC_GET_ARG2(icall)
+	};
+	
+	dprintf("pretending transfer to function (dev=%d:%d, type=%s)",
+	    target.address, target.endpoint,
+	    usb_str_transfer_type(transf_type));
+	
+	if (callback_phone == -1) {
+		ipc_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	usb_transaction_handle_t handle
+	    = 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;
+	}
+	
+	transaction_details_t * trans = malloc(sizeof(transaction_details_t));
+	trans->phone = callback_phone;
+	trans->handle = handle;
+	
+	dprintf("adding transaction to HC", NAME);
+	hc_add_transaction_to_device(transf_type, target,
+	    buffer, len,
+	    out_callback, trans);
+	
+	ipc_answer_1(iid, EOK, handle);
+	dprintf("transfer to function scheduled (handle %d)", handle);
+}
+
+/** Handle data from function to host.
+ */
+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));
+	
+	if (callback_phone == -1) {
+		ipc_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	usb_transaction_handle_t handle
+	    = create_transaction_handle(callback_phone);
+	
+	void * buffer = malloc(len);
+	
+	transaction_details_t * trans = malloc(sizeof(transaction_details_t));
+	trans->phone = callback_phone;
+	trans->handle = handle;
+	
+	dprintf("adding transaction to HC", NAME);
+	hc_add_transaction_from_device(transf_type, target,
+	    buffer, len,
+	    in_callback, trans);
+	
+	ipc_answer_1(iid, EOK, handle);
+	dprintf("transfer from function scheduled (handle %d)", handle);
+}
+
+
+
+/** Connection handler for communcation with host.
+ * By host is typically meant top-level USB driver.
+ *
+ * @param phone_hash Incoming phone hash.
+ * @param host_phone Callback phone to host.
+ */
+void connection_handler_host(ipcarg_t phone_hash, int host_phone)
+{
+	assert(host_phone > 0);
+	
+	dprintf("phone%#x: host connected", phone_hash);
+	
+	
+	while (true) {
+		ipc_callid_t callid;
+		ipc_call_t call;
+		
+		callid = async_get_call(&call);
+		
+		switch (IPC_GET_METHOD(call)) {
+			case IPC_M_PHONE_HUNGUP:
+				ipc_hangup(host_phone);
+				ipc_answer_0(callid, EOK);
+				dprintf("phone%#x: host hang-up", phone_hash);
+				return;
+			
+			case IPC_M_CONNECT_TO_ME:
+				ipc_answer_0(callid, ELIMIT);
+				break;
+
+			
+			case IPC_M_USB_HCD_SEND_DATA:
+				handle_data_to_function(callid, call, host_phone);
+				break;
+			
+			case IPC_M_USB_HCD_RECEIVE_DATA:
+				handle_data_from_function(callid, call, host_phone);
+				break;
+			
+			case IPC_M_USB_HCD_TRANSACTION_SIZE:
+				ipc_answer_1(callid, EOK, USB_MAX_PAYLOAD_SIZE);
+				break;
+			
+			default:
+				ipc_answer_0(callid, EINVAL);
+				break;
+		}
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/devices.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/devices.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/srv/hw/bus/usb/hcd/virtual/devices.c	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -31,5 +31,5 @@
  */
 /** @file
- * @brief
+ * @brief Virtual device management (implementation).
  */
 
@@ -53,4 +53,39 @@
 LIST_INITIALIZE(devices);
 
+/** Recognise device by id.
+ *
+ * @param id Device id.
+ * @param phone Callback phone.
+ */
+virtdev_connection_t *virtdev_recognise(int id, int phone)
+{
+	virtdev_connection_t * dev = NULL;
+	switch (id) {
+		case USB_VIRTDEV_KEYBOARD_ID:
+			dev = virtdev_add_device(
+			    USB_VIRTDEV_KEYBOARD_ADDRESS, phone);
+			break;
+		default:
+			break;
+	}
+	
+	/*
+	 * We do not want to mess-up the virtdev_add_device() as
+	 * the id is needed only before device probing/detection
+	 * is implemented.
+	 *
+	 * However, that does not mean that this will happen soon.
+	 */
+	if (dev) {
+		dev->id = id;
+	}
+	
+	return dev;
+}
+
+/** Find virtual device by its USB address.
+ *
+ * @retval NULL No virtual device at given address.
+ */
 virtdev_connection_t *virtdev_find_by_address(usb_address_t address)
 {
@@ -67,6 +102,22 @@
 }
 
+/** Create virtual device.
+ *
+ * @param address USB address.
+ * @param phone Callback phone.
+ * @return New device.
+ * @retval NULL Out of memory or address already occupied.
+ */
 virtdev_connection_t *virtdev_add_device(usb_address_t address, int phone)
 {
+	link_t *pos;
+	list_foreach(pos, &devices) {
+		virtdev_connection_t *dev
+		    = list_get_instance(pos, virtdev_connection_t, link);
+		if (dev->address == address) {
+			return NULL;
+		}
+	}
+	
 	virtdev_connection_t *dev = (virtdev_connection_t *)
 	    malloc(sizeof(virtdev_connection_t));
@@ -78,5 +129,7 @@
 }
 
- void virtdev_destroy_device(virtdev_connection_t *dev)
+/** Destroy virtual device.
+ */
+void virtdev_destroy_device(virtdev_connection_t *dev)
 {
 	list_remove(&dev->link);
Index: uspace/srv/hw/bus/usb/hcd/virtual/devices.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/devices.h	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/srv/hw/bus/usb/hcd/virtual/devices.h	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -31,5 +31,5 @@
  */ 
 /** @file
- * @brief
+ * @brief Virtual device management.
  */
 #ifndef VHCD_DEVICES_H_
@@ -39,4 +39,5 @@
 #include <usb/hcd.h>
 
+/** Connected virtual device. */
 typedef struct {
 	/** Assigned USB address. */
@@ -44,8 +45,11 @@
 	/** Phone used when sending data to device. */
 	int phone;
+	/** Device id. */
+	int id;
 	/** Linked-list handle. */
 	link_t link;
 } virtdev_connection_t;
 
+virtdev_connection_t *virtdev_recognise(int, int);
 virtdev_connection_t *virtdev_find_by_address(usb_address_t);
 virtdev_connection_t *virtdev_add_device(usb_address_t, int);
Index: uspace/srv/hw/bus/usb/hcd/virtual/hc.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -68,13 +68,21 @@
 static link_t transaction_from_device_list;
 
-
+/** Pending transaction details. */
 typedef struct {
+	/** Linked-list link. */
 	link_t link;
+	/** 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;
+	/** Transaction data length. */
 	size_t len;
+	/** Callback after transaction is done. */
 	hc_transaction_done_callback_t callback;
+	/** Argument to the callback. */
 	void * callback_arg;
 } transaction_t;
@@ -96,4 +104,7 @@
 }
 
+/** Call transaction callback.
+ * Calling this callback informs the backend that transaction was processed.
+ */
 static void process_transaction_with_outcome(transaction_t * transaction,
     usb_transaction_outcome_t outcome)
@@ -103,28 +114,10 @@
 	    usb_str_transaction_outcome(outcome));
 	
-	dprintf("  callback %p (%p, %u, %d, %p)", transaction->callback,
-	    transaction->buffer, transaction->len, outcome,
-	    transaction->callback_arg);
-	
 	transaction->callback(transaction->buffer, transaction->len, outcome,
 	    transaction->callback_arg);
-	dprintf("callback processed");
-}
-
-#if 0
-static void process_transaction(transaction_t * transaction)
-{
-	static unsigned int seed = 4089;
-	
-	unsigned int roulette = pseudo_random(&seed);
-	usb_transaction_outcome_t outcome = USB_OUTCOME_OK;
-	
-	PROB_TEST(outcome, USB_OUTCOME_BABBLE, PROB_OUTCOME_BABBLE, roulette);
-	PROB_TEST(outcome, USB_OUTCOME_CRCERROR, PROB_OUTCOME_CRCERROR, roulette);
-	
-	process_transaction_with_outcome(transaction, outcome);
-}
-#endif
-
+}
+
+/** Host controller manager main function.
+ */
 void hc_manager(void)
 {
@@ -150,6 +143,8 @@
 		virtdev_connection_t *dev = virtdev_find_by_address(
 		    transaction->target.address);
+		usb_transaction_outcome_t outcome = USB_OUTCOME_OK;
+		
 		if (dev != NULL) {
-			dprintf("sending data to device at %d.%d (phone %d)\n",
+			dprintf("sending data to device at %d.%d (phone %d)",
 			    dev->address, transaction->target.endpoint,
 			    dev->phone);
@@ -173,13 +168,20 @@
 				rc = (int)answer_rc;
 			}
+			
+			if (rc != EOK) {
+				outcome = USB_OUTCOME_BABBLE;
+			}
 		} else {
-			process_transaction_with_outcome(transaction,
-			    USB_OUTCOME_OK);
+			outcome = USB_OUTCOME_CRCERROR;
 		}
 		
+		process_transaction_with_outcome(transaction, outcome);
+		
 		free(transaction);
 	}
 }
 
+/** Create new transaction
+ */
 static transaction_t *transaction_create(usb_transfer_type_t type, usb_target_t target,
     usb_direction_t direction,
@@ -201,5 +203,6 @@
 }
 
-
+/** Add transaction directioned towards the device.
+ */
 void hc_add_transaction_to_device(usb_transfer_type_t type, usb_target_t target,
     void * buffer, size_t len,
@@ -211,4 +214,6 @@
 }
 
+/** Add transaction directioned from the device.
+ */
 void hc_add_transaction_from_device(usb_transfer_type_t type, usb_target_t target,
     void * buffer, size_t len,
@@ -220,13 +225,18 @@
 }
 
+/** Fill data to existing transaction from device.
+ */
 int hc_fillin_transaction_from_device(usb_transfer_type_t type, usb_target_t target,
     void * buffer, size_t len)
 {
 	dprintf("finding transaction to fill data in...");
+	int rc;
+	
 	/*
 	 * Find correct transaction envelope in the list.
 	 */
 	if (list_empty(&transaction_from_device_list)) {
-		return ENOENT;
+		rc = ENOENT;
+		goto leave;
 	}
 	
@@ -243,5 +253,6 @@
 	}
 	if (transaction == NULL) {
-		return ENOENT;
+		rc = ENOENT;
+		goto leave;
 	}
 	
@@ -253,5 +264,6 @@
 	if (transaction->len < len) {
 		process_transaction_with_outcome(transaction, USB_OUTCOME_BABBLE);
-		return ENOMEM;
+		rc = ENOMEM;
+		goto leave;
 	}
 	
@@ -264,11 +276,15 @@
 	process_transaction_with_outcome(transaction, USB_OUTCOME_OK);
 	
-	dprintf(" ...transaction " TRANSACTION_FORMAT " sent back",
+	dprintf("  ...transaction " TRANSACTION_FORMAT " sent back",
 	    TRANSACTION_PRINTF(*transaction));
 	
 	
 	free(transaction);
-	
-	return EOK;
+	rc = EOK;
+	
+leave:
+	dprintf("  ...fill-in transaction: %s", str_error(rc));
+	
+	return rc;
 }
 
Index: uspace/srv/hw/bus/usb/hcd/virtual/hc.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hc.h	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hc.h	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -38,5 +38,13 @@
 #include <usb/hcd.h>
 
-typedef void (*hc_transaction_done_callback_t)(void *, size_t, usb_transaction_outcome_t, void *);
+/** Callback after transaction is sent to USB.
+ *
+ * @param buffer Transaction data buffer.
+ * @param size Transaction data size.
+ * @param outcome Transaction outcome.
+ * @param arg Custom argument.
+ */
+typedef void (*hc_transaction_done_callback_t)(void *buffer, size_t size,
+    usb_transaction_outcome_t outcome, void *arg);
 
 void hc_manager(void);
Index: uspace/srv/hw/bus/usb/hcd/virtual/hcd.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -49,215 +49,6 @@
 #include "hc.h"
 #include "devices.h"
+#include "conn.h"
 
-static usb_transaction_handle_t g_handle_seed = 1;
-static usb_transaction_handle_t create_transaction_handle(int phone)
-{
-	return g_handle_seed++;
-}
-
-typedef struct {
-	int phone;
-	usb_transaction_handle_t handle;
-} transaction_details_t;
-
-static void out_callback(void * buffer, size_t len, usb_transaction_outcome_t outcome, void * arg)
-{
-	dprintf("out_callback(buffer, %u, %d, %p)", len, outcome, arg);
-	(void)len;
-	transaction_details_t * trans = (transaction_details_t *)arg;
-	
-	async_msg_2(trans->phone, IPC_M_USB_HCD_DATA_SENT, trans->handle, outcome);
-	
-	free(trans);
-	free(buffer);
-}
-
-static void in_callback(void * buffer, size_t len, usb_transaction_outcome_t outcome, void * arg)
-{
-	dprintf("in_callback(buffer, %u, %d, %p)", len, outcome, arg);
-	transaction_details_t * trans = (transaction_details_t *)arg;
-	
-	ipc_call_t answer_data;
-	ipcarg_t answer_rc;
-	aid_t req;
-	int rc;
-	
-	req = async_send_3(trans->phone,
-	    IPC_M_USB_HCD_DATA_RECEIVED,
-	    trans->handle, outcome,
-	    len,
-	    &answer_data);
-	
-	rc = async_data_write_start(trans->phone, buffer, len);
-	if (rc != EOK) {
-		async_wait_for(req, NULL);
-		goto leave;
-	}
-	
-	async_wait_for(req, &answer_rc);
-	rc = (int)answer_rc;
-	if (rc != EOK) {
-		goto leave;
-	}
-	
-leave:
-	free(trans);
-	free(buffer);
-}
-
-
-
-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);
-	usb_target_t target = {
-		.address = IPC_GET_ARG1(icall),
-		.endpoint = IPC_GET_ARG2(icall)
-	};
-	
-	dprintf("pretending transfer to function (dev=%d:%d, type=%s)",
-	    target.address, target.endpoint,
-	    usb_str_transfer_type(transf_type));
-	
-	if (callback_phone == -1) {
-		ipc_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	usb_transaction_handle_t handle
-	    = 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;
-	}
-	
-	transaction_details_t * trans = malloc(sizeof(transaction_details_t));
-	trans->phone = callback_phone;
-	trans->handle = handle;
-	
-	dprintf("adding transaction to HC", NAME);
-	hc_add_transaction_to_device(transf_type, target,
-	    buffer, len,
-	    out_callback, trans);
-	
-	ipc_answer_1(iid, EOK, handle);
-	dprintf("transfer to function scheduled (handle %d)", handle);
-}
-
-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));
-	
-	if (callback_phone == -1) {
-		ipc_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	usb_transaction_handle_t handle
-	    = create_transaction_handle(callback_phone);
-	
-	void * buffer = malloc(len);
-	
-	transaction_details_t * trans = malloc(sizeof(transaction_details_t));
-	trans->phone = callback_phone;
-	trans->handle = handle;
-	
-	dprintf("adding transaction to HC", NAME);
-	hc_add_transaction_from_device(transf_type, target,
-	    buffer, len,
-	    in_callback, trans);
-	
-	ipc_answer_1(iid, EOK, handle);
-	dprintf("transfer from function scheduled (handle %d)", handle);
-}
-
-static void handle_data_from_device(ipc_callid_t iid, ipc_call_t icall, virtdev_connection_t *dev)
-{
-	usb_endpoint_t endpoint = IPC_GET_ARG1(icall);
-	usb_target_t target = {
-		.address = dev->address,
-		.endpoint = endpoint
-	};
-	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;
-	}
-	
-	rc = hc_fillin_transaction_from_device(USB_TRANSFER_INTERRUPT, target, buffer, len);
-	
-	ipc_answer_0(iid, rc);
-}
-
-static virtdev_connection_t *recognise_device(int id, int callback_phone)
-{
-	switch (id) {
-		case USB_VIRTDEV_KEYBOARD_ID:
-			return virtdev_add_device(
-			    USB_VIRTDEV_KEYBOARD_ADDRESS, callback_phone);
-		default:
-			return NULL;
-	}		
-}
-
-static void device_client_connection(int callback_phone, int device_id)
-{
-	virtdev_connection_t *dev = recognise_device(device_id, callback_phone);
-	if (!dev) {
-		if (callback_phone != -1) {
-			ipc_hangup(callback_phone);
-		}
-		return;
-	}
-	dprintf("device %d connected", device_id);
-	while (true) {
-		ipc_callid_t callid; 
-		ipc_call_t call; 
-		
-		callid = async_get_call(&call);
-		
-		switch (IPC_GET_METHOD(call)) {
-			case IPC_M_PHONE_HUNGUP:
-				if (callback_phone != -1) {
-					ipc_hangup(callback_phone);
-				}
-				ipc_answer_0(callid, EOK);
-				dprintf("hung-up on device %d", device_id);
-				virtdev_destroy_device(dev);
-				return;
-			case IPC_M_CONNECT_TO_ME:
-				ipc_answer_0(callid, ELIMIT);
-				break;
-			case IPC_M_USB_VIRTDEV_DATA_FROM_DEVICE:
-				dprintf("data from device %d", device_id);
-				handle_data_from_device(callid, call, dev);
-				break;
-			default:
-				ipc_answer_0(callid, EINVAL);
-				break;
-		}
-	}
-}
 
 static void client_connection(ipc_callid_t iid, ipc_call_t *icall)
@@ -268,6 +59,4 @@
 	printf("%s: new client connected (phone %#x).\n", NAME, phone_hash);
 	
-	int callback_phone = -1;
-	
 	while (true) {
 		ipc_callid_t callid; 
@@ -275,47 +64,53 @@
 		
 		callid = async_get_call(&call);
-		dprintf("%#x calls method %d", phone_hash, IPC_GET_METHOD(call));
-		switch (IPC_GET_METHOD(call)) {
-			case IPC_M_PHONE_HUNGUP:
-				if (callback_phone != -1) {
-					ipc_hangup(callback_phone);
+		
+		/*
+		 * We can do nothing until we have the callback phone.
+		 * Thus, we will wait for the callback and start processing
+		 * after that.
+		 */
+		int method = (int) IPC_GET_METHOD(call);
+		
+		if (method == IPC_M_PHONE_HUNGUP) {
+			ipc_answer_0(callid, EOK);
+			return;
+		}
+		
+		if (method == IPC_M_CONNECT_TO_ME) {
+			int kind = IPC_GET_ARG1(call);
+			int callback = IPC_GET_ARG5(call);
+			
+			/*
+			 * Determine whether host connected to us
+			 * or a device.
+			 */
+			if (kind == 0) {
+				ipc_answer_0(callid, EOK);
+				connection_handler_host(phone_hash, callback);
+				return;
+			} else if (kind == 1) {
+				int device_id = IPC_GET_ARG2(call);
+				virtdev_connection_t *dev
+				    = virtdev_recognise(device_id, callback);
+				if (!dev) {
+					ipc_answer_0(callid, EEXISTS);
+					ipc_hangup(callback);
+					return;
 				}
 				ipc_answer_0(callid, EOK);
-				printf("%s: hung-up on %#x.\n", NAME, phone_hash);
+				connection_handler_device(phone_hash, dev);
+				virtdev_destroy_device(dev);
 				return;
-			
-			case IPC_M_CONNECT_TO_ME:
-				if (callback_phone != -1) {
-					ipc_answer_0(callid, ELIMIT);
-				} else {
-					callback_phone = IPC_GET_ARG5(call);
-					ipc_answer_0(callid, EOK);
-				}
-				if (IPC_GET_ARG1(call) == 1) {
-					/* Virtual device was just connected
-					 * to us. This is handled elsewhere.
-					 */
-					device_client_connection(callback_phone,
-					    IPC_GET_ARG2(call));
-					return;
-				}
-				break;
-			
-			case IPC_M_USB_HCD_SEND_DATA:
-				handle_data_to_function(callid, call, callback_phone);
-				break;
-			
-			case IPC_M_USB_HCD_RECEIVE_DATA:
-				handle_data_from_function(callid, call, callback_phone);
-				break;
-			
-			case IPC_M_USB_HCD_TRANSACTION_SIZE:
-				ipc_answer_1(callid, EOK, USB_MAX_PAYLOAD_SIZE);
-				break;
-			
-			default:
+			} else {
 				ipc_answer_0(callid, EINVAL);
-				break;
+				ipc_hangup(callback);
+				return;
+			}
 		}
+		
+		/*
+		 * No other methods could be served now.
+		 */
+		ipc_answer_0(callid, ENOTSUP);
 	}
 }
Index: uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h	(revision b3718443703dd0cd113ce025bb4884a81207993a)
@@ -41,4 +41,7 @@
 #define DEVMAP_PATH NAMESPACE "/" NAME
 
+/** Debugging printf.
+ * @see printf
+ */
 static inline void dprintf(const char * format, ...)
 {
