Index: .bzrignore
===================================================================
--- .bzrignore	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ .bzrignore	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -53,4 +53,5 @@
 ./uspace/app/trace/trace
 ./uspace/app/usb/usb
+./uspace/app/virtusbkbd/vuk
 ./uspace/dist/app/bdsh
 ./uspace/dist/app/edit
@@ -72,4 +73,5 @@
 ./uspace/dist/app/trace
 ./uspace/dist/app/usb
+./uspace/dist/app/vuk
 ./uspace/dist/cfg/net/general
 ./uspace/dist/cfg/net/lo
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ boot/Makefile.common	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -134,4 +134,5 @@
 	$(USPACE_PATH)/app/stats/stats \
 	$(USPACE_PATH)/app/usb/usb \
+	$(USPACE_PATH)/app/virtusbkbd/vuk \
 	$(USPACE_PATH)/app/tasks/tasks \
 	$(USPACE_PATH)/app/top/top
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/Makefile	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -49,4 +49,5 @@
 	app/trace \
 	app/top \
+	app/virtusbkbd \
 	srv/clip \
 	srv/devmap \
Index: uspace/app/usb/example.c
===================================================================
--- uspace/app/usb/example.c	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/app/usb/example.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -129,5 +129,5 @@
 		usb_transaction_handle_t handle;
 
-		usb_target_t target = { 11 + i, 3 };
+		usb_target_t target = { i, 0 };
 		int rc = usb_hcd_send_data_to_function(hcd_phone,
 		    target, USB_TRANSFER_ISOCHRONOUS,
Index: uspace/app/virtusbkbd/Makefile
===================================================================
--- uspace/app/virtusbkbd/Makefile	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/app/virtusbkbd/Makefile	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+# acronym for virtual usb keyboard
+# (it is really annoying to write long names)
+BINARY = vuk
+LIBS = $(LIBUSB_PREFIX)/libusb.a
+EXTRA_CFLAGS = -I$(LIB_PREFIX)
+SOURCES = \
+	virtusbkbd.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/virtusbkbd/virtusbkbd.c
===================================================================
--- uspace/app/virtusbkbd/virtusbkbd.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/app/virtusbkbd/virtusbkbd.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -0,0 +1,110 @@
+/*
+ * 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 Virtual USB keyboard.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vfs/vfs.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+#include <async.h>
+
+#include <usb/hcd.h>
+#include <usb/virtdev.h>
+
+#define LOOPS 5
+#define NAME "virt-usb-kbd"
+
+#define DEV_HCD_NAME "hcd-virt"
+
+#define __QUOTEME(x) #x
+#define _QUOTEME(x) __QUOTEME(x)
+
+#define VERBOSE_EXEC(cmd, fmt, ...) \
+	(printf("%s: %s" fmt "\n", NAME, _QUOTEME(cmd), __VA_ARGS__), cmd(__VA_ARGS__))
+
+static void fibril_sleep(size_t sec)
+{
+	while (sec-- > 0) {
+		async_usleep(1000*1000);
+	}
+}
+
+static void on_data_from_host(usb_endpoint_t endpoint, void *buffer, size_t len)
+{
+	printf("%s: ignoring incomming data to endpoint %d\n", NAME, endpoint);
+}
+
+int main(int argc, char * argv[])
+{
+	int vhcd_phone = usb_virtdev_connect(DEV_HCD_NAME,
+	    USB_VIRTDEV_KEYBOARD_ID, on_data_from_host);
+	
+	if (vhcd_phone < 0) {
+		printf("%s: Unable to start comunication with VHCD at usb://%s (%s).\n",
+		    NAME, DEV_HCD_NAME, str_error(vhcd_phone));
+		return vhcd_phone;
+	}
+	
+	
+	
+	size_t i;
+	for (i = 0; i < LOOPS; i++) {
+		size_t size = 5;
+		char *data = (char *) "Hullo, World!";
+		
+		if (i > 0) {
+			fibril_sleep(2);
+		}
+		
+		printf("%s: Will send data to VHCD...\n", NAME);
+		int rc = usb_virtdev_data_to_host(vhcd_phone, 0, data, size);
+		printf("%s:   ...data sent (%s).\n", NAME, str_error(rc));
+	}
+	
+	fibril_sleep(1);
+	printf("%s: Terminating...\n", NAME);
+	
+	ipc_hangup(vhcd_phone);
+	
+	return 0;
+}
+
+
+/** @}
+ */
Index: uspace/lib/usb/Makefile
===================================================================
--- uspace/lib/usb/Makefile	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/lib/usb/Makefile	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -31,5 +31,6 @@
 
 SOURCES = \
-	hcd.c
+	hcd.c \
+	virtdev.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/usb/hcd.h
===================================================================
--- uspace/lib/usb/hcd.h	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/lib/usb/hcd.h	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -75,4 +75,10 @@
 	usb_endpoint_t endpoint;
 } usb_target_t;
+
+static inline int usb_target_same(usb_target_t a, usb_target_t b)
+{
+	return (a.address == b.address)
+	    && (a.endpoint == b.endpoint);
+}
 
 /** Opaque handle of active USB transaction.
Index: uspace/lib/usb/virtdev.c
===================================================================
--- uspace/lib/usb/virtdev.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/lib/usb/virtdev.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -0,0 +1,178 @@
+/*
+ * 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 libusb usb
+ * @{
+ */
+/** @file
+ * @brief Virtual USB device (implementation).
+ */
+#include "virtdev.h"
+#include <devmap.h>
+#include <fcntl.h>
+#include <vfs/vfs.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#define NAMESPACE "usb"
+
+static usb_virtdev_on_data_from_host_t on_data_from_host = NULL;
+
+static void handle_data_to_device(ipc_callid_t iid, ipc_call_t icall)
+{
+	usb_endpoint_t endpoint = IPC_GET_ARG1(icall);
+	
+	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;
+	}
+	
+	on_data_from_host(endpoint, buffer, len);
+	
+	free(buffer);
+	
+	ipc_answer_0(iid, EOK);
+}
+
+static void callback_connection(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_answer_0(iid, EOK);
+	
+	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_answer_0(callid, EOK);
+				return;
+			
+			case IPC_M_USB_VIRTDEV_DATA_TO_DEVICE:
+				handle_data_to_device(callid, call);
+				break;
+			
+			default:
+				ipc_answer_0(callid, EINVAL);
+				break;
+		}
+	}
+}
+
+/** Create necessary phones for comunication with virtual HCD.
+ * This function wraps following calls:
+ * -# open <code>/dev/usb/<i>hcd_path</i></code> for reading
+ * -# access phone of file opened in previous step
+ * -# create callback through just opened phone
+ * -# create handler for calling on data from host to function
+ * -# return the (outgoing) phone
+ *
+ * @warning This function is wrapper for several actions and therefore
+ * it is not possible - in case of error - to determine at which point
+ * error occured.
+ *
+ * @param hcd_path HCD identification under devfs
+ *     (without <code>/dev/usb/</code>).
+ * @param device_id Internal device identification (used by HCD).
+ * @param callback Handler for callbacks from HCD.
+ * @return Phone for comunicating with HCD or error code from errno.h.
+ */
+int usb_virtdev_connect(const char *hcd_path, int device_id,
+    usb_virtdev_on_data_from_host_t callback)
+{
+	char dev_path[DEVMAP_NAME_MAXLEN + 1];
+	snprintf(dev_path, DEVMAP_NAME_MAXLEN,
+	    "/dev/%s/%s", NAMESPACE, hcd_path);
+	
+	int fd = open(dev_path, O_RDONLY);
+	if (fd < 0) {
+		return fd;
+	}
+	
+	int hcd_phone = fd_phone(fd);
+	
+	if (hcd_phone < 0) {
+		return hcd_phone;
+	}
+	
+	ipcarg_t phonehash;
+	int rc = ipc_connect_to_me(hcd_phone, 1, device_id, 0, &phonehash);
+	if (rc != EOK) {
+		return rc;
+	}
+	on_data_from_host = callback;
+	async_new_connection(phonehash, 0, NULL, callback_connection);
+	
+	return hcd_phone;
+}
+
+int usb_virtdev_data_to_host(int phone,
+    usb_endpoint_t endpoint,
+    void * buffer, size_t size)
+{
+	if (phone < 0) {
+		return EINVAL;
+	}
+	if ((buffer == NULL) || (size == 0)) {
+		return EINVAL;
+	}
+
+	ipc_call_t answer_data;
+	ipcarg_t answer_rc;
+	aid_t req;
+	int rc;
+	
+	req = async_send_1(phone,
+	    IPC_M_USB_VIRTDEV_DATA_FROM_DEVICE,
+	    endpoint,
+	    &answer_data);
+	
+	rc = async_data_write_start(phone, buffer, size);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	async_wait_for(req, &answer_rc);
+	rc = (int)answer_rc;
+	if (rc != EOK) {
+		return rc;
+	}
+	
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/virtdev.h
===================================================================
--- uspace/lib/usb/virtdev.h	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
+++ uspace/lib/usb/virtdev.h	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -0,0 +1,59 @@
+/*
+ * 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 libusb usb
+ * @{
+ */
+/** @file
+ * @brief Virtual USB device.
+ */
+#ifndef LIBUSB_VIRTDEV_H_
+#define LIBUSB_VIRTDEV_H_
+
+#include <ipc/ipc.h>
+#include <async.h>
+#include "hcd.h"
+
+#define USB_VIRTDEV_KEYBOARD_ID 1
+#define USB_VIRTDEV_KEYBOARD_ADDRESS 1
+
+typedef void (*usb_virtdev_on_data_from_host_t)(usb_endpoint_t, void *, size_t);
+
+int usb_virtdev_connect(const char *, int, usb_virtdev_on_data_from_host_t);
+int usb_virtdev_data_to_host(int, usb_endpoint_t,
+    void *, size_t);
+
+typedef enum {
+	IPC_M_USB_VIRTDEV_DATA_TO_DEVICE = IPC_FIRST_USER_METHOD,
+	IPC_M_USB_VIRTDEV_DATA_FROM_DEVICE
+} usb_virtdev_method_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/Makefile
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -33,4 +33,5 @@
 
 SOURCES = \
+	devices.c \
 	hc.c \
 	hcd.c
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 bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -0,0 +1,88 @@
+/*
+ * 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
+ */
+
+#include <ipc/ipc.h>
+#include <adt/list.h>
+#include <bool.h>
+#include <async.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include <usb/virtdev.h>
+
+#include "devices.h"
+
+#define list_foreach(pos, head) \
+	for (pos = (head)->next; pos != (head); \
+        	pos = pos->next)
+
+LIST_INITIALIZE(devices);
+
+virtdev_connection_t *virtdev_find_by_address(usb_address_t address)
+{
+	link_t *pos;
+	list_foreach(pos, &devices) {
+		virtdev_connection_t *dev
+		    = list_get_instance(pos, virtdev_connection_t, link);
+		if (dev->address == address) {
+			return dev;
+		}
+	}
+	
+	return NULL;
+}
+
+virtdev_connection_t *virtdev_add_device(usb_address_t address, int phone)
+{
+	virtdev_connection_t *dev = (virtdev_connection_t *)
+	    malloc(sizeof(virtdev_connection_t));
+	dev->phone = phone;
+	dev->address = address;
+	list_append(&dev->link, &devices);
+	
+	return dev;
+}
+
+ void virtdev_destroy_device(virtdev_connection_t *dev)
+{
+	list_remove(&dev->link);
+	free(dev);
+}
+
+/**
+ * @}
+ */
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 bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -0,0 +1,57 @@
+/*
+ * 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
+ */
+#ifndef VHCD_DEVICES_H_
+#define VHCD_DEVICES_H_
+
+#include <adt/list.h>
+#include <usb/hcd.h>
+
+typedef struct {
+	/** Assigned USB address. */
+	usb_address_t address;
+	/** Phone used when sending data to device. */
+	int phone;
+	/** Linked-list handle. */
+	link_t link;
+} virtdev_connection_t;
+
+virtdev_connection_t *virtdev_find_by_address(usb_address_t);
+virtdev_connection_t *virtdev_add_device(usb_address_t, int);
+void virtdev_destroy_device(virtdev_connection_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/hc.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -40,7 +40,12 @@
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include <usb/virtdev.h>
 
 #include "vhcd.h"
 #include "hc.h"
+#include "devices.h"
 
 #define USLEEP_BASE (500 * 1000)
@@ -60,5 +65,6 @@
 	} while (0)
 
-static link_t transaction_list;
+static link_t transaction_to_device_list;
+static link_t transaction_from_device_list;
 
 
@@ -97,8 +103,14 @@
 	    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)
 {
@@ -113,8 +125,10 @@
 	process_transaction_with_outcome(transaction, outcome);
 }
+#endif
 
 void hc_manager(void)
 {
-	list_initialize(&transaction_list);
+	list_initialize(&transaction_to_device_list);
+	list_initialize(&transaction_from_device_list);
 	
 	static unsigned int seed = 4573;
@@ -125,20 +139,48 @@
 		async_usleep(USLEEP_BASE + (pseudo_random(&seed) % USLEEP_VAR));
 		
-		if (list_empty(&transaction_list)) {
+		if (list_empty(&transaction_to_device_list)) {
 			continue;
 		}
 		
-		link_t * first_transaction_link = transaction_list.next;
-		transaction_t * first_transaction
+		link_t * first_transaction_link = transaction_to_device_list.next;
+		transaction_t * transaction
 		    = transaction_get_instance(first_transaction_link);
 		list_remove(first_transaction_link);
 		
-		process_transaction(first_transaction);
-		
-		free(first_transaction);
-	}
-}
-
-static void hc_add_transaction(usb_transfer_type_t type, usb_target_t target,
+		virtdev_connection_t *dev = virtdev_find_by_address(
+		    transaction->target.address);
+		if (dev != NULL) {
+			dprintf("sending data to device at %d.%d (phone %d)\n",
+			    dev->address, transaction->target.endpoint,
+			    dev->phone);
+			ipc_call_t answer_data;
+			ipcarg_t answer_rc;
+			aid_t req;
+			int rc;
+			
+			req = async_send_2(dev->phone,
+			    IPC_M_USB_VIRTDEV_DATA_TO_DEVICE,
+			    transaction->target.endpoint,
+			    transaction->type,
+			    &answer_data);
+			
+			rc = async_data_write_start(dev->phone,
+			    transaction->buffer, transaction->len);
+			if (rc != EOK) {
+				async_wait_for(req, NULL);
+			} else {
+				async_wait_for(req, &answer_rc);
+				rc = (int)answer_rc;
+			}
+		} else {
+			process_transaction_with_outcome(transaction,
+			    USB_OUTCOME_OK);
+		}
+		
+		free(transaction);
+	}
+}
+
+static transaction_t *transaction_create(usb_transfer_type_t type, usb_target_t target,
     usb_direction_t direction,
     void * buffer, size_t len,
@@ -156,37 +198,77 @@
 	transaction->callback_arg = arg;
 	
-	dprintf("adding transaction " TRANSACTION_FORMAT,
-	    TRANSACTION_PRINTF(*transaction));
-
-	list_append(&transaction->link, &transaction_list);
-}
-
-void hc_add_out_transaction(usb_transfer_type_t type, usb_target_t target,
+	return transaction;
+}
+
+
+void hc_add_transaction_to_device(usb_transfer_type_t type, usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg)
 {
-	hc_add_transaction(type, target, USB_DIRECTION_OUT,
-	    buffer, len, callback, arg);
-}
-
-void hc_add_in_transaction(usb_transfer_type_t type, usb_target_t target,
+	transaction_t *transaction = transaction_create(type, target,
+	    USB_DIRECTION_OUT, buffer, len, callback, arg);
+	list_append(&transaction->link, &transaction_to_device_list);
+}
+
+void hc_add_transaction_from_device(usb_transfer_type_t type, usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg)
 {
-	static unsigned int seed = 216;
-	
-	size_t i;
-	char * data = (char *)buffer;
-	for (i = 0; i < len; i++, data++) {
-		*data = 'A' + (pseudo_random(&seed) % ('Z' - 'A'));
-	}
-	
-	unsigned int shortening = pseudo_random(&seed) % SHORTENING_VAR;
-	if (len > shortening) {
-		len -= shortening;
-	}
-	
-	hc_add_transaction(type, target, USB_DIRECTION_IN,
-	    buffer, len, callback, arg);
+	transaction_t *transaction = transaction_create(type, target,
+	    USB_DIRECTION_IN, buffer, len, callback, arg);
+	list_append(&transaction->link, &transaction_from_device_list);
+}
+
+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...");
+	/*
+	 * Find correct transaction envelope in the list.
+	 */
+	if (list_empty(&transaction_from_device_list)) {
+		return ENOENT;
+	}
+	
+	transaction_t *transaction = NULL;
+	link_t *pos = transaction_from_device_list.next;
+	
+	while (pos != &transaction_from_device_list) {
+		transaction_t *t = transaction_get_instance(pos);
+		if (usb_target_same(t->target, target)) {
+			transaction = t;
+			break;
+		}
+		pos = pos->next;
+	}
+	if (transaction == NULL) {
+		return ENOENT;
+	}
+	
+	/*
+	 * Remove the transaction from the list as it will be processed now.
+	 */
+	list_remove(&transaction->link);
+	
+	if (transaction->len < len) {
+		process_transaction_with_outcome(transaction, USB_OUTCOME_BABBLE);
+		return ENOMEM;
+	}
+	
+	/*
+	 * Copy the data and finish processing the transaction.
+	 */
+	transaction->len = len;
+	memcpy(transaction->buffer, buffer, len);
+	
+	process_transaction_with_outcome(transaction, USB_OUTCOME_OK);
+	
+	dprintf(" ...transaction " TRANSACTION_FORMAT " sent back",
+	    TRANSACTION_PRINTF(*transaction));
+	
+	
+	free(transaction);
+	
+	return EOK;
 }
 
Index: uspace/srv/hw/bus/usb/hcd/virtual/hc.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hc.h	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hc.h	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -42,12 +42,14 @@
 void hc_manager(void);
 
-void hc_add_out_transaction(usb_transfer_type_t type, usb_target_t target,
+void hc_add_transaction_to_device(usb_transfer_type_t type, usb_target_t target,
     void * buffer, size_t len,
     hc_transaction_done_callback_t callback, void * arg);
 
-void hc_add_in_transaction(usb_transfer_type_t type, usb_target_t target,
+void hc_add_transaction_from_device(usb_transfer_type_t type, 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,
+    void * buffer, size_t len);
 
 #endif
Index: uspace/srv/hw/bus/usb/hcd/virtual/hcd.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision 4bc309bf9ecb5c52d870148e834fec37fd241140)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision bc9a629fec035ed4a62c13a00781814283dc523f)
@@ -45,6 +45,8 @@
 
 #include <usb/hcd.h>
+#include <usb/virtdev.h>
 #include "vhcd.h"
 #include "hc.h"
+#include "devices.h"
 
 static usb_transaction_handle_t g_handle_seed = 1;
@@ -61,4 +63,5 @@
 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;
@@ -72,4 +75,5 @@
 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;
 	
@@ -140,5 +144,5 @@
 	
 	dprintf("adding transaction to HC", NAME);
-	hc_add_out_transaction(transf_type, target,
+	hc_add_transaction_to_device(transf_type, target,
 	    buffer, len,
 	    out_callback, trans);
@@ -176,5 +180,5 @@
 	
 	dprintf("adding transaction to HC", NAME);
-	hc_add_in_transaction(transf_type, target,
+	hc_add_transaction_from_device(transf_type, target,
 	    buffer, len,
 	    in_callback, trans);
@@ -184,4 +188,76 @@
 }
 
+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)
@@ -216,4 +292,12 @@
 					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;
 			
