Index: uspace/lib/usbvirt/Makefile
===================================================================
--- uspace/lib/usbvirt/Makefile	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/Makefile	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -34,4 +34,5 @@
 
 SOURCES = \
+	callback.c \
 	ctrlpipe.c \
 	main.c \
Index: uspace/lib/usbvirt/callback.c
===================================================================
--- uspace/lib/usbvirt/callback.c	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
+++ uspace/lib/usbvirt/callback.c	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -0,0 +1,235 @@
+/*
+ * 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 libusbvirt usb
+ * @{
+ */
+/** @file
+ * @brief Callback connection handling.
+ */
+#include <devmap.h>
+#include <fcntl.h>
+#include <vfs/vfs.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <mem.h>
+
+#include "hub.h"
+#include "device.h"
+#include "private.h"
+
+#define NAMESPACE "usb"
+
+/** Wrapper for SETUP transaction over telephone. */
+static void handle_setup_transaction(usbvirt_device_t *device,
+    ipc_callid_t iid, ipc_call_t icall)
+{
+	usb_address_t address = IPC_GET_ARG1(icall);
+	usb_endpoint_t endpoint = IPC_GET_ARG2(icall);
+	size_t expected_len = IPC_GET_ARG3(icall);
+	
+	if (address != device->address) {
+		ipc_answer_0(iid, EADDRNOTAVAIL);
+		return;
+	}
+	
+	if ((endpoint < 0) || (endpoint >= USB11_ENDPOINT_MAX)) {
+		ipc_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	if (expected_len == 0) {
+		ipc_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	size_t len = 0;
+	void * buffer = NULL;
+	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 = device->transaction_setup(device, endpoint, buffer, len);
+	
+	ipc_answer_0(iid, rc);
+}
+
+/** Wrapper for OUT transaction over telephone. */
+static void handle_out_transaction(usbvirt_device_t *device,
+    ipc_callid_t iid, ipc_call_t icall)
+{
+	usb_address_t address = IPC_GET_ARG1(icall);
+	usb_endpoint_t endpoint = IPC_GET_ARG2(icall);
+	size_t expected_len = IPC_GET_ARG3(icall);
+	
+	if (address != device->address) {
+		ipc_answer_0(iid, EADDRNOTAVAIL);
+		return;
+	}
+	
+	if ((endpoint < 0) || (endpoint >= USB11_ENDPOINT_MAX)) {
+		ipc_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	int rc = EOK;
+	
+	size_t len = 0;
+	void *buffer = NULL;
+	
+	if (expected_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 = device->transaction_out(device, endpoint, buffer, len);
+	
+	if (buffer != NULL) {
+		free(buffer);
+	}
+	
+	ipc_answer_0(iid, rc);
+}
+
+
+/** Wrapper for IN transaction over telephone. */
+static void handle_in_transaction(usbvirt_device_t *device,
+    ipc_callid_t iid, ipc_call_t icall)
+{
+	usb_address_t address = IPC_GET_ARG1(icall);
+	usb_endpoint_t endpoint = IPC_GET_ARG2(icall);
+	size_t expected_len = IPC_GET_ARG3(icall);
+	
+	if (address != device->address) {
+		ipc_answer_0(iid, EADDRNOTAVAIL);
+		return;
+	}
+	
+	if ((endpoint < 0) || (endpoint >= USB11_ENDPOINT_MAX)) {
+		ipc_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	int rc = EOK;
+	
+	void *buffer = expected_len > 0 ? malloc(expected_len) : NULL;
+	size_t len;
+	
+	rc = device->transaction_in(device, endpoint, buffer, expected_len, &len);
+	/*
+	 * If the request was processed, we will send data back.
+	 */
+	if (rc == EOK) {
+		size_t receive_len;
+		ipc_callid_t callid;
+		if (!async_data_read_receive(&callid, &receive_len)) {
+			ipc_answer_0(iid, EINVAL);
+			return;
+		}
+		async_data_read_finalize(callid, buffer, receive_len);
+	}
+	
+	ipc_answer_0(iid, rc);
+}
+
+/** Wrapper for getting device name. */
+static void handle_get_name(usbvirt_device_t *device,
+    ipc_callid_t iid, ipc_call_t icall)
+{
+	if (device->name == NULL) {
+		ipc_answer_0(iid, ENOENT);
+	}
+	
+	size_t size = str_size(device->name);
+	
+	ipc_callid_t callid;
+	size_t accepted_size;
+	if (!async_data_read_receive(&callid, &accepted_size)) {
+		ipc_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	if (accepted_size > size) {
+		accepted_size = size;
+	}
+	async_data_read_finalize(callid, device->name, accepted_size);
+	
+	ipc_answer_1(iid, EOK, accepted_size);
+}
+
+/** Callback connection for a given device. */
+void device_callback_connection(usbvirt_device_t *device, 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_USBVIRT_GET_NAME:
+				handle_get_name(device, callid, call);
+				break;
+			
+			case IPC_M_USBVIRT_TRANSACTION_SETUP:
+				handle_setup_transaction(device, callid, call);
+				break;
+			
+			case IPC_M_USBVIRT_TRANSACTION_OUT:
+				handle_out_transaction(device, callid, call);
+				break;
+				
+			case IPC_M_USBVIRT_TRANSACTION_IN:
+				handle_in_transaction(device, callid, call);
+				break;
+			
+			default:
+				ipc_answer_0(callid, EINVAL);
+				break;
+		}
+	}
+}
+
+
+/**
+ * @}
+ */
Index: uspace/lib/usbvirt/ctrlpipe.c
===================================================================
--- uspace/lib/usbvirt/ctrlpipe.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/ctrlpipe.c	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -47,4 +47,7 @@
 usb_address_t dev_new_address = -1;
 
+/** Tell request type.
+ * By type is meant either standard, class, vendor or other.
+ */
 static int request_get_type(uint8_t request_type)
 {
@@ -52,6 +55,7 @@
 }
 
-
-int control_pipe(usbvirt_control_transfer_t *transfer)
+/** Handle communication over control pipe zero.
+ */
+int control_pipe(usbvirt_device_t *device, usbvirt_control_transfer_t *transfer)
 {
 	if (transfer->request_size < sizeof(usb_device_request_setup_packet_t)) {
@@ -68,5 +72,5 @@
 	switch (type) {
 		case REQUEST_TYPE_STANDARD:
-			rc = handle_std_request(request, remaining_data);
+			rc = handle_std_request(device, request, remaining_data);
 			break;
 		case REQUEST_TYPE_CLASS:
@@ -80,17 +84,17 @@
 	}
 	
-	if (dev_new_address != -1) {
+	if (device->new_address != -1) {
 		/*
 		 * TODO: handle when this request is invalid (e.g.
 		 * setting address when in configured state).
 		 */
-		if (dev_new_address == 0) {
+		if (device->new_address == 0) {
 			device->state = USBVIRT_STATE_DEFAULT;
 		} else {
 			device->state = USBVIRT_STATE_ADDRESS;
 		}
-		device->address = dev_new_address;
+		device->address = device->new_address;
 		
-		dev_new_address = -1;
+		device->new_address = -1;
 	}
 	
Index: uspace/lib/usbvirt/device.h
===================================================================
--- uspace/lib/usbvirt/device.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/device.h	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -40,8 +40,8 @@
 #include <usb/devreq.h>
 
-struct usbvirt_device;
+typedef struct usbvirt_device usbvirt_device_t;
 struct usbvirt_control_transfer;
 
-typedef int (*usbvirt_on_device_request_t)(struct usbvirt_device *dev,
+typedef int (*usbvirt_on_device_request_t)(usbvirt_device_t *dev,
 	usb_device_request_setup_packet_t *request,
 	uint8_t *data);
@@ -72,13 +72,13 @@
 	usbvirt_on_device_request_t on_class_device_request;
 	
-	int (*on_control_transfer)(struct usbvirt_device *dev,
+	int (*on_control_transfer)(usbvirt_device_t *dev,
 	    usb_endpoint_t endpoint, struct usbvirt_control_transfer *transfer);
 	
 	/** Callback for all other incoming data. */
-	int (*on_data)(struct usbvirt_device *dev,
+	int (*on_data)(usbvirt_device_t *dev,
 	    usb_endpoint_t endpoint, void *buffer, size_t size);
 	
 	/** Callback for host request for data. */
-	int (*on_data_request)(struct usbvirt_device *dev,
+	int (*on_data_request)(usbvirt_device_t *dev,
 	    usb_endpoint_t endpoint, void *buffer, size_t size, size_t *actual_size);
 	
@@ -133,23 +133,30 @@
  */
 typedef struct usbvirt_control_transfer {
+	/** Transfer direction (read/write control transfer). */
 	usb_direction_t direction;
+	/** Request data. */
 	void *request;
+	/** Size of request data. */
 	size_t request_size;
+	/** Payload. */
 	void *data;
+	/** Size of payload. */
 	size_t data_size;
 } usbvirt_control_transfer_t;
 
 /** Virtual USB device. */
-typedef struct usbvirt_device {
+struct usbvirt_device {
 	/** Callback device operations. */
 	usbvirt_device_ops_t *ops;
 	
-	
 	/** Reply onto control transfer.
 	 */
-	int (*control_transfer_reply)(struct usbvirt_device *dev,
+	int (*control_transfer_reply)(usbvirt_device_t *dev,
 	    usb_endpoint_t endpoint, void *buffer, size_t size);
 	
-	/* Device attributes. */
+	/** Device name.
+	 * Used in debug prints and sent to virtual host controller.
+	 */
+	const char *name;
 	
 	/** Standard descriptors. */
@@ -158,29 +165,26 @@
 	/** Current device state. */
 	usbvirt_device_state_t state;
+	
 	/** Device address. */
 	usb_address_t address;
+	/** New device address.
+	 * This field is used during SET_ADDRESS request.
+	 * On all other occasions, it holds invalid address (e.g. -1).
+	 */
+	usb_address_t new_address;
 	
-	/* Private attributes. */
-	
-	/** Phone to HC.
-	 * @warning Do not change, this is private variable.
-	 */
-	int vhcd_phone_;
-	
-	/** Device id.
-	 * This item will be removed when device enumeration and
-	 * recognition is implemented.
-	 */
-	int device_id_;
-	
-	int (*transaction_out)(struct usbvirt_device *dev,
+	/** Process OUT transaction. */
+	int (*transaction_out)(usbvirt_device_t *dev,
 	    usb_endpoint_t endpoint, void *buffer, size_t size);
-	int (*transaction_setup)(struct usbvirt_device *dev,
+	/** Process SETUP transaction. */
+	int (*transaction_setup)(usbvirt_device_t *dev,
 	    usb_endpoint_t endpoint, void *buffer, size_t size);
-	int (*transaction_in)(struct usbvirt_device *dev,
+	/** Process IN transaction. */
+	int (*transaction_in)(usbvirt_device_t *dev,
 	    usb_endpoint_t endpoint, void *buffer, size_t size, size_t *data_size);
 	
+	/** State information on control-transfer endpoints. */
 	usbvirt_control_transfer_t current_control_transfers[USB11_ENDPOINT_MAX];
-} usbvirt_device_t;
+};
 
 #endif
Index: uspace/lib/usbvirt/hub.h
===================================================================
--- uspace/lib/usbvirt/hub.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/hub.h	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -38,8 +38,20 @@
 #include "device.h"
 
+/** USB transaction type.
+ * This types does not correspond directly to types in USB specification,
+ * as actually DATA transactions are marked with these types to identify
+ * their direction (and tag).
+ */
+typedef enum {
+	USBVIRT_TRANSACTION_SETUP,
+	USBVIRT_TRANSACTION_IN,
+	USBVIRT_TRANSACTION_OUT
+} usbvirt_transaction_type_t;
 
+const char *usbvirt_str_transaction_type(usbvirt_transaction_type_t type);
+
+/** Telephony methods of virtual devices. */
 typedef enum {
-	IPC_M_USBVIRT_DATA_TO_DEVICE = IPC_FIRST_USER_METHOD,
-	IPC_M_USBVIRT_DATA_FROM_DEVICE,
+	IPC_M_USBVIRT_GET_NAME = IPC_FIRST_USER_METHOD,
 	IPC_M_USBVIRT_TRANSACTION_SETUP,
 	IPC_M_USBVIRT_TRANSACTION_OUT,
@@ -48,6 +60,6 @@
 
 int usbvirt_connect(usbvirt_device_t *, const char *);
-int usbvirt_disconnect(void);
 int usbvirt_connect_local(usbvirt_device_t *);
+int usbvirt_disconnect(usbvirt_device_t *dev);
 
 #endif
Index: uspace/lib/usbvirt/ids.h
===================================================================
--- uspace/lib/usbvirt/ids.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ 	(revision )
@@ -1,57 +1,0 @@
-/*
- * 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 libusbvirt usb
- * @{
- */
-/** @file
- * @brief Virtual USB device.
- */
-#ifndef LIBUSBVIRT_IDS_H_
-#define LIBUSBVIRT_IDS_H_
-
-#include "device.h"
-
-#define USBVIRT_DEV_KEYBOARD_ID 1
-#define USBVIRT_DEV_KEYBOARD_ADDRESS 1
-
-int usbvirt_connect(usbvirt_device_t *, const char *);
-int usbvirt_disconnect(void);
-
-typedef enum {
-	USBVIRT_TRANSACTION_SETUP,
-	USBVIRT_TRANSACTION_IN,
-	USBVIRT_TRANSACTION_OUT
-} usbvirt_transaction_type_t;
-
-const char *usbvirt_str_transaction_type(usbvirt_transaction_type_t type);
-
-#endif
-/**
- * @}
- */
Index: uspace/lib/usbvirt/main.c
===================================================================
--- uspace/lib/usbvirt/main.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/main.c	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -31,5 +31,5 @@
  */
 /** @file
- * @brief Main handler for virtual USB device.
+ * @brief Device registration with virtual USB framework.
  */
 #include <devmap.h>
@@ -39,4 +39,5 @@
 #include <stdlib.h>
 #include <mem.h>
+#include <assert.h>
 
 #include "hub.h"
@@ -46,158 +47,59 @@
 #define NAMESPACE "usb"
 
-usbvirt_device_t *device = NULL;
-
-static void handle_setup_transaction(ipc_callid_t iid, ipc_call_t icall)
-{
-	usb_address_t address = IPC_GET_ARG1(icall);
-	usb_endpoint_t endpoint = IPC_GET_ARG2(icall);
-	size_t expected_len = IPC_GET_ARG3(icall);
-	
-	if (address != device->address) {
-		ipc_answer_0(iid, EADDRNOTAVAIL);
-		return;
-	}
-	
-	if ((endpoint < 0) || (endpoint >= USB11_ENDPOINT_MAX)) {
-		ipc_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	if (expected_len == 0) {
-		ipc_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	size_t len = 0;
-	void * buffer = NULL;
-	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 = device->transaction_setup(device, endpoint, buffer, len);
-	
-	ipc_answer_0(iid, rc);
-}
-
-
-static void handle_out_transaction(ipc_callid_t iid, ipc_call_t icall)
-{
-	usb_address_t address = IPC_GET_ARG1(icall);
-	usb_endpoint_t endpoint = IPC_GET_ARG2(icall);
-	size_t expected_len = IPC_GET_ARG3(icall);
-	
-	if (address != device->address) {
-		ipc_answer_0(iid, EADDRNOTAVAIL);
-		return;
-	}
-	
-	if ((endpoint < 0) || (endpoint >= USB11_ENDPOINT_MAX)) {
-		ipc_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	int rc = EOK;
-	
-	size_t len = 0;
-	void *buffer = NULL;
-	
-	if (expected_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;
+/** Virtual device wrapper. */
+typedef struct {
+	/** Actual device. */
+	usbvirt_device_t *device;
+	/** Phone to host controller. */
+	int vhcd_phone;
+	/** Device id. */
+	ipcarg_t id;
+	/** Linked-list member. */
+	link_t link;
+} virtual_device_t;
+
+/*** List of known device. */
+static LIST_INITIALIZE(device_list);
+
+/** Find virtual device wrapper based on the contents. */
+static virtual_device_t *find_device(usbvirt_device_t *device)
+{
+	if (list_empty(&device_list)) {
+		return NULL;
+	}
+	
+	link_t *pos;
+	for (pos = device_list.next; pos != &device_list; pos = pos->next) {
+		virtual_device_t *dev
+		    = list_get_instance(pos, virtual_device_t, link);
+		if (dev->device == device) {
+			return dev;
 		}
 	}
 	
-	rc = device->transaction_out(device, endpoint, buffer, len);
-	
-	if (buffer != NULL) {
-		free(buffer);
-	}
-	
-	ipc_answer_0(iid, rc);
-}
-
-
-
-static void handle_in_transaction(ipc_callid_t iid, ipc_call_t icall)
-{
-	usb_address_t address = IPC_GET_ARG1(icall);
-	usb_endpoint_t endpoint = IPC_GET_ARG2(icall);
-	size_t expected_len = IPC_GET_ARG3(icall);
-	
-	if (address != device->address) {
-		ipc_answer_0(iid, EADDRNOTAVAIL);
-		return;
-	}
-	
-	if ((endpoint < 0) || (endpoint >= USB11_ENDPOINT_MAX)) {
-		ipc_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	int rc = EOK;
-	
-	void *buffer = expected_len > 0 ? malloc(expected_len) : NULL;
-	size_t len;
-	
-	rc = device->transaction_in(device, endpoint, buffer, expected_len, &len);
-	/*
-	 * If the request was processed, we will send data back.
-	 */
-	if (rc == EOK) {
-		size_t receive_len;
-		if (!async_data_read_receive(&iid, &receive_len)) {
-			ipc_answer_0(iid, EINVAL);
-			return;
+	return NULL;
+}
+
+/** Find virtual device wrapper by its id. */
+static virtual_device_t *find_device_by_id(ipcarg_t id)
+{
+	if (list_empty(&device_list)) {
+		return NULL;
+	}
+	
+	link_t *pos;
+	for (pos = device_list.next; pos != &device_list; pos = pos->next) {
+		virtual_device_t *dev
+		    = list_get_instance(pos, virtual_device_t, link);
+		if (dev->id == id) {
+			return dev;
 		}
-		async_data_read_finalize(iid, buffer, receive_len);
-	}
-	
-	ipc_answer_0(iid, rc);
-}
-
-
-
-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_USBVIRT_TRANSACTION_SETUP:
-				handle_setup_transaction(callid, call);
-				break;
-			
-			case IPC_M_USBVIRT_TRANSACTION_OUT:
-				handle_out_transaction(callid, call);
-				break;
-				
-			case IPC_M_USBVIRT_TRANSACTION_IN:
-				handle_in_transaction(callid, call);
-				break;
-			
-			default:
-				ipc_answer_0(callid, EINVAL);
-				break;
-		}
-	}
-}
-
-static int control_transfer_reply(struct usbvirt_device *device,
+	}
+	
+	return NULL;
+}
+
+/** Reply to a control transfer. */
+static int control_transfer_reply(usbvirt_device_t *device,
 	    usb_endpoint_t endpoint, void *buffer, size_t size)
 {
@@ -213,4 +115,5 @@
 }
 
+/** Initialize virtual device. */
 static void device_init(usbvirt_device_t *dev)
 {
@@ -223,4 +126,5 @@
 	dev->state = USBVIRT_STATE_DEFAULT;
 	dev->address = 0;
+	dev->new_address = -1;
 	
 	size_t i;
@@ -235,4 +139,47 @@
 }
 
+/** Add a virtual device.
+ * The returned device (if not NULL) shall be destroy via destroy_device().
+ */
+static virtual_device_t *add_device(usbvirt_device_t *dev)
+{
+	assert(find_device(dev) == NULL);
+	virtual_device_t *new_device
+	    = (virtual_device_t *) malloc(sizeof(virtual_device_t));
+	
+	new_device->device = dev;
+	link_initialize(&new_device->link);
+	
+	list_append(&new_device->link, &device_list);
+	
+	return new_device;
+}
+
+/** Destroy virtual device. */
+static void destroy_device(virtual_device_t *dev)
+{
+	if (dev->vhcd_phone > 0) {
+		ipc_hangup(dev->vhcd_phone);
+	}
+	
+	list_remove(&dev->link);
+	
+	free(dev);
+}
+
+/** Callback connection handler. */
+static void callback_connection(ipc_callid_t iid, ipc_call_t *icall)
+{
+	// FIXME - determine which device just called back
+	virtual_device_t *dev = find_device_by_id(0);
+	if (dev == NULL) {
+		ipc_answer_0(iid, EINVAL);
+		printf("Ooops\n");
+		return;
+	}
+	
+	device_callback_connection(dev->device, iid, icall);
+}
+
 /** Create necessary phones for comunication with virtual HCD.
  * This function wraps following calls:
@@ -254,4 +201,9 @@
 int usbvirt_connect(usbvirt_device_t *dev, const char *hcd_path)
 {
+	virtual_device_t *virtual_device = find_device(dev);
+	if (virtual_device != NULL) {
+		return EEXISTS;
+	}
+	
 	char dev_path[DEVMAP_NAME_MAXLEN + 1];
 	snprintf(dev_path, DEVMAP_NAME_MAXLEN,
@@ -270,13 +222,14 @@
 	
 	ipcarg_t phonehash;
-	int rc = ipc_connect_to_me(hcd_phone, 1, dev->device_id_, 0, &phonehash);
+	int rc = ipc_connect_to_me(hcd_phone, 1, 0, 0, &phonehash);
 	if (rc != EOK) {
 		return rc;
 	}
 	
-	dev->vhcd_phone_ = hcd_phone;
 	device_init(dev);
 	
-	device = dev;
+	virtual_device = add_device(dev);
+	virtual_device->vhcd_phone = hcd_phone;
+	virtual_device->id = 0;
 	
 	async_new_connection(phonehash, 0, NULL, callback_connection);
@@ -290,12 +243,20 @@
  *
  * @param dev Device to connect.
- * @return Always EOK.
+ * @return Error code.
+ * @retval EOK Device connected.
+ * @retval EEXISTS This device is already connected.
  */
 int usbvirt_connect_local(usbvirt_device_t *dev)
 {
-	dev->vhcd_phone_ = -1;
+	virtual_device_t *virtual_device = find_device(dev);
+	if (virtual_device != NULL) {
+		return EEXISTS;
+	}
+	
 	device_init(dev);
 	
-	device = dev;
+	virtual_device = add_device(dev);
+	virtual_device->vhcd_phone = -1;
+	virtual_device->id = 0;
 	
 	return EOK;
@@ -304,11 +265,17 @@
 /** Disconnects device from HCD.
  *
- * @return Always EOK.
- */
-int usbvirt_disconnect(void)
-{
-	ipc_hangup(device->vhcd_phone_);
-	
-	device = NULL;
+ * @param dev Device to be disconnected.
+ * @return Error code.
+ * @retval EOK Device connected.
+ * @retval ENOENT This device is not connected.
+ */
+int usbvirt_disconnect(usbvirt_device_t *dev)
+{
+	virtual_device_t *virtual_device = find_device(dev);
+	if (virtual_device == NULL) {
+		return ENOENT;
+	}
+	
+	destroy_device(virtual_device);
 	
 	return EOK;
Index: uspace/lib/usbvirt/private.h
===================================================================
--- uspace/lib/usbvirt/private.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/private.h	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -39,5 +39,4 @@
 #include "hub.h"
 
-extern usbvirt_device_t *device;
 
 #define DEVICE_HAS_OP(dev, op) \
@@ -54,9 +53,9 @@
     usb_endpoint_t endpoint, void *buffer, size_t size);
 
-int control_pipe(usbvirt_control_transfer_t *transfer);
+int control_pipe(usbvirt_device_t *device, usbvirt_control_transfer_t *transfer);
 
-int handle_std_request(usb_device_request_setup_packet_t *request, uint8_t *data);
+int handle_std_request(usbvirt_device_t *device, usb_device_request_setup_packet_t *request, uint8_t *data);
 
-extern usb_address_t dev_new_address;
+void device_callback_connection(usbvirt_device_t *device, ipc_callid_t iid, ipc_call_t *icall);
 
 int transaction_setup(usbvirt_device_t *device, usb_endpoint_t endpoint,
Index: uspace/lib/usbvirt/stdreq.c
===================================================================
--- uspace/lib/usbvirt/stdreq.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/stdreq.c	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -49,5 +49,7 @@
  */
  
-static int handle_get_descriptor(uint8_t type, uint8_t index, uint16_t language,
+/** GET_DESCRIPTOR handler. */
+static int handle_get_descriptor(usbvirt_device_t *device,
+    uint8_t type, uint8_t index, uint16_t language,
     uint16_t length)
 {
@@ -106,5 +108,7 @@
 }
 
-static int handle_set_address(uint16_t new_address,
+/** SET_ADDRESS handler. */
+static int handle_set_address(usbvirt_device_t *device,
+    uint16_t new_address,
     uint16_t zero1, uint16_t zero2)
 {
@@ -117,10 +121,12 @@
 	}
 	
-	dev_new_address = new_address;
+	device->new_address = new_address;
 	
 	return EOK;
 }
 
-static int handle_set_configuration(uint16_t configuration_value,
+/** SET_CONFIGURATION handler. */
+static int handle_set_configuration(usbvirt_device_t *device,
+    uint16_t configuration_value,
     uint16_t zero1, uint16_t zero2)
 {
@@ -177,20 +183,21 @@
 	} while (false)
 
-
-int handle_std_request(usb_device_request_setup_packet_t *request, uint8_t *data)
+/** Handle standard device request. */
+int handle_std_request(usbvirt_device_t *device,
+    usb_device_request_setup_packet_t *request, uint8_t *data)
 {
 	HANDLE_REQUEST(request, data, USB_DEVREQ_GET_DESCRIPTOR,
 	    device, on_get_descriptor,
-	    handle_get_descriptor(request->value_low, request->value_high,
+	    handle_get_descriptor(device, request->value_low, request->value_high,
 	        request->index, request->length));
 	
 	HANDLE_REQUEST(request, data, USB_DEVREQ_SET_ADDRESS,
 	    device, on_set_address,
-	    handle_set_address(request->value,
+	    handle_set_address(device, request->value,
 	        request->index, request->length));
 	
 	HANDLE_REQUEST(request, data, USB_DEVREQ_SET_CONFIGURATION,
 	    device, on_set_configuration,
-	    handle_set_configuration(request->value,
+	    handle_set_configuration(device, request->value,
 	        request->index, request->length));
 	
Index: uspace/lib/usbvirt/transaction.c
===================================================================
--- uspace/lib/usbvirt/transaction.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/lib/usbvirt/transaction.c	(revision ca07cd38cc4c8d1bf28bd26cf1e06a9df555ce24)
@@ -38,9 +38,11 @@
 #include <mem.h>
 
-#include "ids.h"
+#include "hub.h"
 #include "private.h"
 
-static usb_direction_t setup_transaction_direction(usb_endpoint_t, void *, size_t);
-static void process_control_transfer(usb_endpoint_t, usbvirt_control_transfer_t *);
+static usb_direction_t setup_transaction_direction(usbvirt_device_t *,
+    usb_endpoint_t, void *, size_t);
+static void process_control_transfer(usbvirt_device_t *,
+    usb_endpoint_t, usbvirt_control_transfer_t *);
 
 /** Convert virtual USB transaction type to string.
@@ -60,4 +62,7 @@
 }
 
+/** SETUP transaction handling.
+ * The setup transaction only prepares control transfer on given endpoint.
+ */
 int transaction_setup(usbvirt_device_t *device, usb_endpoint_t endpoint,
     void *buffer, size_t size)
@@ -72,5 +77,5 @@
 	}
 	
-	transfer->direction = setup_transaction_direction(endpoint,
+	transfer->direction = setup_transaction_direction(device, endpoint,
 	    buffer, size);
 	transfer->request = buffer;
@@ -79,7 +84,14 @@
 	transfer->data_size = 0;
 	
+	if (transfer->direction == USB_DIRECTION_IN) {
+		process_control_transfer(device, endpoint, transfer);
+	}
+	
 	return EOK;
 }
 
+/** OUT transaction handling.
+ * The OUT transaction can trigger processing of a control transfer.
+ */
 int transaction_out(usbvirt_device_t *device, usb_endpoint_t endpoint,
     void *buffer, size_t size)
@@ -133,4 +145,7 @@
 }
 
+/** IN transaction handling.
+ * The IN transaction can trigger processing of a control transfer.
+ */
 int transaction_in(usbvirt_device_t *device, usb_endpoint_t endpoint,
     void *buffer, size_t size, size_t *data_size)
@@ -145,5 +160,5 @@
 			 * This means end of input data.
 			 */
-			process_control_transfer(endpoint, transfer);
+			process_control_transfer(device, endpoint, transfer);
 		} else {
 			/*
@@ -151,5 +166,19 @@
 			 * of the buffer.
 			 */
-			// TODO: implement
+			// FIXME: handle when the HC wants the data back
+			// in more chunks
+			size_t actual_size = 0;
+			if (transfer->data) {
+				actual_size = transfer->data_size;
+			}
+			if (actual_size > size) {
+				actual_size = size;
+			}
+			if (actual_size > 0) {
+				memcpy(buffer, transfer->data, actual_size);
+				if (data_size) {
+					*data_size = actual_size;
+				}
+			}
 		}
 		
@@ -170,6 +199,10 @@
 }
 
-
-static usb_direction_t setup_transaction_direction(usb_endpoint_t endpoint,
+/** Determine direction of control transfer.
+ * First, try the user provided callback, otherwise guess, believing that
+ * it uses the same format as control pipe 0.
+ */
+static usb_direction_t setup_transaction_direction(usbvirt_device_t *device,
+    usb_endpoint_t endpoint,
     void *data, size_t size)
 {
@@ -202,5 +235,8 @@
 }
 
-static void process_control_transfer(usb_endpoint_t endpoint,
+/** Process control transfer.
+ */
+static void process_control_transfer(usbvirt_device_t *device,
+    usb_endpoint_t endpoint,
     usbvirt_control_transfer_t *transfer)
 {
@@ -213,5 +249,5 @@
 	if (rc == EFORWARD) {
 		if (endpoint == 0) {
-			rc = control_pipe(transfer);
+			rc = control_pipe(device, transfer);
 		}
 	}
