Index: uspace/lib/usbvirt/ids.h
===================================================================
--- uspace/lib/usbvirt/ids.h	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/lib/usbvirt/ids.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -50,4 +50,5 @@
 } usbvirt_transaction_type_t;
 
+const char *usbvirt_str_transaction_type(usbvirt_transaction_type_t type);
 
 #endif
Index: uspace/lib/usbvirt/transaction.c
===================================================================
--- uspace/lib/usbvirt/transaction.c	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/lib/usbvirt/transaction.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -38,8 +38,25 @@
 #include <mem.h>
 
+#include "ids.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 *);
+
+/** Convert virtual USB transaction type to string.
+ */
+const char *usbvirt_str_transaction_type(usbvirt_transaction_type_t type)
+{
+	switch (type) {
+		case USBVIRT_TRANSACTION_SETUP:
+			return "setup";
+		case USBVIRT_TRANSACTION_IN:
+			return "in";
+		case USBVIRT_TRANSACTION_OUT:
+			return "out";
+		default:
+			return "unknown";
+	}
+}
 
 int transaction_setup(usbvirt_device_t *device, usb_endpoint_t endpoint,
Index: uspace/srv/hw/bus/usb/hcd/virtual/Makefile
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -35,4 +35,5 @@
 	conndev.c \
 	connhost.c \
+	debug.c \
 	devices.c \
 	hc.c \
Index: uspace/srv/hw/bus/usb/hcd/virtual/conndev.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -44,4 +44,6 @@
 /** Connection handler for communcation with virtual device.
  *
+ * This function also takes care of proper phone hung-up.
+ *
  * @param phone_hash Incoming phone hash.
  * @param dev Virtual device handle.
@@ -51,6 +53,5 @@
 	assert(dev != NULL);
 	
-	dprintf("phone%#x: virtual device %d connected",
-	    phone_hash, dev->id);
+	dprintf(0, "virtual device connected through phone %#x", phone_hash);
 	
 	while (true) {
@@ -64,6 +65,6 @@
 				ipc_hangup(dev->phone);
 				ipc_answer_0(callid, EOK);
-				dprintf("phone%#x: device %d hang-up",
-				    phone_hash, dev->id);
+				dprintf(0, "phone%#x: device hung-up",
+				    phone_hash);
 				return;
 			
@@ -73,4 +74,5 @@
 			
 			default:
+				dprintf_inval_call(2, call, phone_hash);
 				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 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/connhost.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -56,6 +56,6 @@
 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;
+	dprintf(2, "out_callback(buffer, %u, %d, %p)", len, outcome, arg);
+	
 	transaction_details_t * trans = (transaction_details_t *)arg;
 	
@@ -63,5 +63,7 @@
 	
 	free(trans);
-	free(buffer);
+	if (buffer) {
+		free(buffer);
+	}
 }
 
@@ -70,5 +72,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);
+	dprintf(2, "in_callback(buffer, %u, %d, %p)", len, outcome, arg);
 	transaction_details_t * trans = (transaction_details_t *)arg;
 	
@@ -116,5 +118,5 @@
 	};
 	
-	dprintf("pretending transfer to function (dev=%d:%d)",
+	dprintf(1, "pretending transfer to function (dev=%d:%d)",
 	    target.address, target.endpoint);
 	
@@ -144,5 +146,4 @@
 	trans->handle = handle;
 	
-	dprintf("adding transaction to HC", NAME);
 	hc_add_transaction_to_device(setup_transaction, target,
 	    buffer, len,
@@ -150,5 +151,5 @@
 	
 	ipc_answer_1(iid, EOK, handle);
-	dprintf("transfer to function scheduled (handle %d)", handle);
+	dprintf(2, "transfer to function scheduled (handle %d)", handle);
 }
 
@@ -163,5 +164,5 @@
 	size_t len = IPC_GET_ARG3(icall);
 	
-	dprintf("pretending transfer from function (dev=%d:%d)",
+	dprintf(1, "pretending transfer from function (dev=%d:%d)",
 	    target.address, target.endpoint);
 	
@@ -183,5 +184,4 @@
 	trans->handle = handle;
 	
-	dprintf("adding transaction to HC", NAME);
 	hc_add_transaction_from_device(target,
 	    buffer, len,
@@ -189,5 +189,5 @@
 	
 	ipc_answer_1(iid, EOK, handle);
-	dprintf("transfer from function scheduled (handle %d)", handle);
+	dprintf(2, "transfer from function scheduled (handle %d)", handle);
 }
 
@@ -197,4 +197,6 @@
  * By host is typically meant top-level USB driver.
  *
+ * This function also takes care of proper phone hung-up.
+ *
  * @param phone_hash Incoming phone hash.
  * @param host_phone Callback phone to host.
@@ -204,5 +206,5 @@
 	assert(host_phone > 0);
 	
-	dprintf("phone%#x: host connected", phone_hash);
+	dprintf(0, "host connected through phone %#x", phone_hash);
 	
 	
@@ -217,5 +219,6 @@
 				ipc_hangup(host_phone);
 				ipc_answer_0(callid, EOK);
-				dprintf("phone%#x: host hang-up", phone_hash);
+				dprintf(0, "phone%#x: host hung-up",
+				    phone_hash);
 				return;
 			
@@ -223,14 +226,4 @@
 				ipc_answer_0(callid, ELIMIT);
 				break;
-
-			
-			case IPC_M_USB_HCD_SEND_DATA:
-				handle_data_to_function(callid, call,
-				    false, 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:
@@ -271,4 +264,5 @@
 			
 			default:
+				dprintf_inval_call(2, call, phone_hash);
 				ipc_answer_0(callid, EINVAL);
 				break;
Index: uspace/srv/hw/bus/usb/hcd/virtual/debug.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/debug.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
+++ uspace/srv/hw/bus/usb/hcd/virtual/debug.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -0,0 +1,78 @@
+/*
+ * 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 Debugging support.
+ */
+#include <stdio.h>
+#include <ipc/ipc.h>
+
+#include "vhcd.h"
+
+/** Current debug level. */
+int debug_level = 0;
+
+/** Debugging printf.
+ * This function is intended for single-line messages as it
+ * automatically prints debugging prefix at the beginning of the
+ * line.
+ *
+ * @see printf
+ * @param level Debugging level.
+ */
+void dprintf(int level, const char *format, ...)
+{
+	if (level > debug_level) {
+		return;
+	}
+	
+	printf("%s(%d): ", NAME, level);
+	va_list args;
+	va_start(args, format);
+	vprintf(format, args);
+	va_end(args);
+	printf("\n");
+}
+
+/** Debug print informing of invalid call.
+ */
+void dprintf_inval_call(int level, ipc_call_t call, ipcarg_t phone_hash)
+{
+	dprintf(level, "phone%#x: invalid call [%u (%u, %u, %u, %u, %u)]",
+	    phone_hash,
+	    IPC_GET_METHOD(call),
+	    IPC_GET_ARG1(call), IPC_GET_ARG2(call), IPC_GET_ARG3(call),
+	    IPC_GET_ARG4(call), IPC_GET_ARG5(call));
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/hc.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hc.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -68,8 +68,9 @@
 static link_t transaction_list;
 
-#define TRANSACTION_FORMAT "T[%d:%d (%d) %d]"
+#define TRANSACTION_FORMAT "T[%d:%d %s (%d)]"
 #define TRANSACTION_PRINTF(t) \
 	(t).target.address, (t).target.endpoint, \
-	(int)(t).len, (int)(t).type
+	usbvirt_str_transaction_type((t).type), \
+	(int)(t).len
 
 #define transaction_get_instance(lnk) \
@@ -88,5 +89,5 @@
     usb_transaction_outcome_t outcome)
 {
-	dprintf("processing transaction " TRANSACTION_FORMAT ", outcome: %s",
+	dprintf(3, "processing transaction " TRANSACTION_FORMAT ", outcome: %s",
 	    TRANSACTION_PRINTF(*transaction),
 	    usb_str_transaction_outcome(outcome));
@@ -113,5 +114,8 @@
 		}
 		
-		dprintf("virtual hub has address %d:*.", virthub_dev.address);
+		char ports[HUB_PORT_COUNT + 2];
+		hub_get_port_statuses(ports, HUB_PORT_COUNT + 1);
+		dprintf(3, "virtual hub: addr=%d ports=%s",
+		    virthub_dev.address, ports);
 		
 		link_t *first_transaction_link = transaction_list.next;
@@ -120,5 +124,5 @@
 		list_remove(first_transaction_link);
 		
-		dprintf("processing transaction " TRANSACTION_FORMAT "",
+		dprintf(3, "processing transaction " TRANSACTION_FORMAT "",
 		    TRANSACTION_PRINTF(*transaction));
 		
@@ -148,4 +152,7 @@
 	transaction->callback = callback;
 	transaction->callback_arg = arg;
+	
+	dprintf(1, "creating transaction " TRANSACTION_FORMAT,
+	    TRANSACTION_PRINTF(*transaction));
 	
 	return transaction;
Index: uspace/srv/hw/bus/usb/hcd/virtual/hcd.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -57,5 +57,4 @@
 	
 	ipc_answer_0(iid, EOK);
-	printf("%s: new client connected (phone %#x).\n", NAME, phone_hash);
 	
 	while (true) {
@@ -112,4 +111,5 @@
 		 * No other methods could be served now.
 		 */
+		dprintf_inval_call(1, call, phone_hash);
 		ipc_answer_0(callid, ENOTSUP);
 	}
@@ -117,6 +117,13 @@
 
 int main(int argc, char * argv[])
-{
-	printf("%s: Virtual USB host controller driver.\n", NAME);
+{	
+	printf("%s: virtual USB host controller driver.\n", NAME);
+	
+	int i;
+	for (i = 1; i < argc; i++) {
+		if (str_cmp(argv[i], "-d") == 0) {
+			debug_level++;
+		}
+	}
 	
 	int rc;
@@ -138,5 +145,6 @@
 	hub_init();
 	
-	printf("%s: accepting connections.\n", NAME);
+	printf("%s: accepting connections [devmap=%s, debug=%d].\n", NAME,
+	    DEVMAP_PATH, debug_level);
 	hc_manager();
 	
Index: uspace/srv/hw/bus/usb/hcd/virtual/hub.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -43,5 +43,4 @@
 #include "hubintern.h"
 
-hub_port_t hub_ports[HUB_PORT_COUNT];
 
 /** Standard device descriptor. */
@@ -108,4 +107,5 @@
 };
 
+/** All hub configuration descriptors. */
 static usbvirt_device_configuration_extras_t extra_descriptors[] = {
 	{
@@ -137,11 +137,14 @@
 };
 
+/** Hub as a virtual device. */
 usbvirt_device_t virthub_dev = {
 	.ops = &hub_ops,
 	.descriptors = &descriptors,
 };
- 
+
+/** Hub device. */
 hub_device_t hub_dev;
 
+/** Initialize virtual hub. */
 void hub_init(void)
 {
@@ -156,9 +159,13 @@
 	
 	usbvirt_connect_local(&virthub_dev);
-	//virthub_dev.send_data = send_data;
-	
-	printf("%s: virtual hub (%d ports) created.\n", NAME, HUB_PORT_COUNT);
-}
-
+	
+	dprintf(1, "virtual hub (%d ports) created", HUB_PORT_COUNT);
+}
+
+/** Connect device to the hub.
+ *
+ * @param device Device to be connected.
+ * @return Port where the device was connected to.
+ */
 size_t hub_add_device(virtdev_connection_t *device)
 {
@@ -191,5 +198,5 @@
 }
 
-
+/** Disconnect device from the hub. */
 void hub_remove_device(virtdev_connection_t *device)
 {
@@ -209,4 +216,8 @@
 }
 
+/** Tell whether device port is open.
+ *
+ * @return Whether communication to and from the device can go through the hub.
+ */
 bool hub_can_device_signal(virtdev_connection_t * device)
 {
@@ -221,4 +232,21 @@
 }
 
+/** Format hub port status.
+ *
+ * @param result Buffer where to store status string.
+ * @param len Number of characters that is possible to store in @p result
+ * 	(excluding trailing zero).
+ */
+void hub_get_port_statuses(char *result, size_t len)
+{
+	if (len > HUB_PORT_COUNT) {
+		len = HUB_PORT_COUNT;
+	}
+	size_t i;
+	for (i = 0; i < len; i++) {
+		result[i] = hub_port_state_as_char(hub_dev.ports[i].state);
+	}
+	result[len] = 0;
+}
 
 /**
Index: uspace/srv/hw/bus/usb/hcd/virtual/hub.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hub.h	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hub.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -51,4 +51,5 @@
 void hub_remove_device(virtdev_connection_t *);
 bool hub_can_device_signal(virtdev_connection_t *);
+void hub_get_port_statuses(char *result, size_t len);
 
 #endif
Index: uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -38,5 +38,7 @@
 #include "hub.h"
 
+/** Endpoint number for status change pipe. */
 #define HUB_STATUS_CHANGE_PIPE 1
+/** Configuration value for hub configuration. */
 #define HUB_CONFIGURATION_ID 1
 
@@ -66,4 +68,8 @@
 } __attribute__ ((packed)) hub_descriptor_t;
 
+/** Hub port internal state.
+ * Some states (e.g. port over current) are not covered as they are not
+ * simulated at all.
+ */
 typedef enum {
 	HUB_PORT_STATE_NOT_CONFIGURED,
@@ -78,4 +84,29 @@
 } hub_port_state_t;
 
+/** Convert hub port state to a char. */
+static inline char hub_port_state_as_char(hub_port_state_t state) {
+	switch (state) {
+		case HUB_PORT_STATE_NOT_CONFIGURED:
+			return '-';
+		case HUB_PORT_STATE_POWERED_OFF:
+			return 'O';
+		case HUB_PORT_STATE_DISCONNECTED:
+			return 'X';
+		case HUB_PORT_STATE_DISABLED:
+			return 'D';
+		case HUB_PORT_STATE_RESETTING:
+			return 'R';
+		case HUB_PORT_STATE_ENABLED:
+			return 'E';
+		case HUB_PORT_STATE_SUSPENDED:
+			return 'S';
+		case HUB_PORT_STATE_RESUMING:
+			return 'F';
+		default:
+			return '?';
+	}
+}
+
+/** Hub status change mask bits. */
 typedef enum {
 	HUB_STATUS_C_PORT_CONNECTION = (1 << 0),
@@ -87,4 +118,5 @@
 } hub_status_change_t;
 
+/** Hub port information. */
 typedef struct {
 	virtdev_connection_t *device;
@@ -93,4 +125,5 @@
 } hub_port_t;
 
+/** Hub device type. */
 typedef struct {
 	hub_port_t ports[HUB_PORT_COUNT];
Index: uspace/srv/hw/bus/usb/hcd/virtual/hubops.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hubops.c	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hubops.c	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -43,4 +43,6 @@
 #include "hubintern.h"
 
+/** Produce a byte from bit values.
+ */
 #define MAKE_BYTE(b0, b1, b2, b3, b4, b5, b6, b7) \
 	(( \
@@ -63,5 +65,5 @@
     void *buffer, size_t size, size_t *actual_size);
 
-
+/** Standard USB requests. */
 static usbvirt_standard_device_request_ops_t standard_request_ops = {
 	.on_get_status = NULL,
@@ -78,5 +80,5 @@
 };
 
-
+/** Hub operations. */
 usbvirt_device_ops_t hub_ops = {
 	.standard_request_ops = &standard_request_ops,
@@ -86,4 +88,5 @@
 };
 
+/** Callback for GET_DESCRIPTOR request. */
 static int on_get_descriptor(struct usbvirt_device *dev,
     usb_device_request_setup_packet_t *request, uint8_t *data)
@@ -124,4 +127,5 @@
 }
 
+/** Get access to a port or return with EINVAL. */
 #define _GET_PORT(portvar, index) \
 	do { \
@@ -274,10 +278,9 @@
 
 
-
-
+/** Callback for class request. */
 static int on_class_request(struct usbvirt_device *dev,
     usb_device_request_setup_packet_t *request, uint8_t *data)
 {	
-	printf("%s: hub class request (%d)\n", NAME, (int) request->request);
+	dprintf(2, "hub class request (%d)\n", (int) request->request);
 	
 	uint8_t recipient = request->request_type & 31;
@@ -287,5 +290,5 @@
 	do { \
 		if (!(cond)) { \
-			printf("%s: WARN: invalid class request (%s not met).\n", \
+			dprintf(0, "WARN: invalid class request (%s not met).\n", \
 			    NAME, #cond); \
 			return EINVAL; \
@@ -347,8 +350,13 @@
 }
 
+/** Callback for data request. */
 static int on_data_request(struct usbvirt_device *dev,
     usb_endpoint_t endpoint,
     void *buffer, size_t size, size_t *actual_size)
 {
+	if (endpoint != HUB_STATUS_CHANGE_PIPE) {
+		return EINVAL;
+	}
+	
 	uint8_t change_map = 0;
 	
Index: uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h	(revision 7a7bfeb30fdd348b34bb92383e98a63c4fff1346)
+++ uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h	(revision 355f7c27fa96297800cdc913cb419e414d5da68d)
@@ -36,6 +36,4 @@
 #define VHCD_VHCD_H_
 
-#include <stdio.h>
-
 #define NAME "hcd-virt"
 #define NAMESPACE "usb"
@@ -43,16 +41,7 @@
 #define DEVMAP_PATH NAMESPACE "/" NAME
 
-/** Debugging printf.
- * @see printf
- */
-static inline void dprintf(const char * format, ...)
-{
-	printf("%s:   ", NAME);
-	va_list args;
-	va_start(args, format);
-	vprintf(format, args);
-	va_end(args);
-	printf("\n");
-}
+extern int debug_level;
+void dprintf(int, const char *, ...);
+void dprintf_inval_call(int, ipc_call_t, ipcarg_t);
 
 #endif
