Index: uspace/app/usbinfo/Makefile
===================================================================
--- uspace/app/usbinfo/Makefile	(revision 81ca2044fc8ece8a458baa4b4bb7520a019fd9ea)
+++ uspace/app/usbinfo/Makefile	(revision e160da4d15699337ef066c7b2aa146e9cae810fc)
@@ -34,4 +34,5 @@
 
 SOURCES = \
+	desctree.c \
 	dev.c \
 	dump.c \
Index: uspace/app/usbinfo/desctree.c
===================================================================
--- uspace/app/usbinfo/desctree.c	(revision e160da4d15699337ef066c7b2aa146e9cae810fc)
+++ uspace/app/usbinfo/desctree.c	(revision e160da4d15699337ef066c7b2aa146e9cae810fc)
@@ -0,0 +1,87 @@
+/*
+ * 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 usbinfo
+ * @{
+ */
+/**
+ * @file
+ * Descriptor tree dump.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+
+#include <usb/usb.h>
+#include <usb/descriptor.h>
+#include <usb/debug.h>
+#include <usb/classes/classes.h>
+
+#include "usbinfo.h"
+#include <usb/dp.h>
+
+static void browse_descriptor_tree_internal(usb_dp_parser_t *parser,
+    usb_dp_parser_data_t *data, uint8_t *root, size_t depth,
+    dump_descriptor_in_tree_t callback, void *arg)
+{
+	if (root == NULL) {
+		return;
+	}
+	callback(root, depth, arg);
+	uint8_t *child = usb_dp_get_nested_descriptor(parser, data, root);
+	do {
+		browse_descriptor_tree_internal(parser, data, child, depth + 1,
+		    callback, arg);
+		child = usb_dp_get_sibling_descriptor(parser, data,
+		    root, child);
+	} while (child != NULL);
+}
+
+void browse_descriptor_tree(uint8_t *descriptors, size_t descriptors_size,
+    usb_dp_descriptor_nesting_t *descriptor_nesting,
+    dump_descriptor_in_tree_t callback, size_t initial_depth, void *arg)
+{
+	usb_dp_parser_data_t data = {
+		.data = descriptors,
+		.size = descriptors_size,
+		.arg = NULL
+	};
+
+	usb_dp_parser_t parser = {
+		.nesting = descriptor_nesting
+	};
+
+	browse_descriptor_tree_internal(&parser, &data, descriptors,
+	    initial_depth, callback, arg);
+}
+
+/** @}
+ */
Index: uspace/app/usbinfo/dev.c
===================================================================
--- uspace/app/usbinfo/dev.c	(revision 81ca2044fc8ece8a458baa4b4bb7520a019fd9ea)
+++ uspace/app/usbinfo/dev.c	(revision e160da4d15699337ef066c7b2aa146e9cae810fc)
@@ -93,4 +93,14 @@
 	}
 
+	rc = usb_request_get_full_configuration_descriptor_alloc(
+	    &dev->ctrl_pipe, 0, (void **)&dev->full_configuration_descriptor,
+	    &dev->full_configuration_descriptor_size);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to retrieve configuration descriptor: %s.\n",
+		    str_error(rc));
+		goto leave;
+	}
+
 	return dev;
 
Index: uspace/app/usbinfo/info.c
===================================================================
--- uspace/app/usbinfo/info.c	(revision 81ca2044fc8ece8a458baa4b4bb7520a019fd9ea)
+++ uspace/app/usbinfo/info.c	(revision e160da4d15699337ef066c7b2aa146e9cae810fc)
@@ -40,4 +40,5 @@
 #include <usb/recognise.h>
 #include <usb/request.h>
+#include <usb/classes/classes.h>
 #include "usbinfo.h"
 
@@ -56,4 +57,100 @@
 	    &dev->device_descriptor, &matches);
 	dump_match_ids(&matches, get_indent(0));
+}
+
+static void dump_descriptor_tree_brief_device(const char *prefix,
+    usb_standard_device_descriptor_t *descriptor)
+{
+	printf("%sDevice (0x%04x by 0x%04x, %s)\n", prefix,
+	    (int) descriptor->product_id,
+	    (int) descriptor->vendor_id,
+	    usb_str_class(descriptor->device_class));
+}
+
+static void dump_descriptor_tree_brief_configuration(const char *prefix,
+    usb_standard_configuration_descriptor_t *descriptor)
+{
+	printf("%sConfiguration #%d\n", prefix,
+	    (int) descriptor->configuration_number);
+}
+
+static void dump_descriptor_tree_brief_interface(const char *prefix,
+    usb_standard_interface_descriptor_t *descriptor)
+{
+	printf("%sInterface #%d (%s, 0x%02x, 0x%02x)\n", prefix,
+	    (int) descriptor->interface_number,
+	    usb_str_class(descriptor->interface_class),
+	    (int) descriptor->interface_subclass,
+	    (int) descriptor->interface_protocol);
+}
+
+static void dump_descriptor_tree_brief_endpoint(const char *prefix,
+    usb_standard_endpoint_descriptor_t *descriptor)
+{
+	usb_endpoint_t endpoint_no = descriptor->endpoint_address & 0xF;
+	usb_transfer_type_t transfer = descriptor->attributes & 0x3;
+	usb_direction_t direction = descriptor->endpoint_address & 0x80
+	    ? USB_DIRECTION_IN : USB_DIRECTION_OUT;
+	printf("%sEndpoint #%d (%s %s, %zu)\n", prefix,
+	    endpoint_no, usb_str_transfer_type(transfer),
+	    direction == USB_DIRECTION_IN ? "in" : "out",
+	    (size_t) descriptor->max_packet_size);
+}
+
+
+static void dump_descriptor_tree_brief_callback(uint8_t *descriptor,
+    size_t depth, void *arg)
+{
+	const char *indent = get_indent(depth);
+
+	int descr_type = -1;
+	size_t descr_size = descriptor[0];
+	if (descr_size > 0) {
+		descr_type = descriptor[1];
+	}
+
+	switch (descr_type) {
+
+#define _BRANCH(type_enum, descriptor_type, callback) \
+	case type_enum: \
+		if (descr_size >= sizeof(descriptor_type)) { \
+			callback(indent, (descriptor_type *) descriptor); \
+		} else { \
+			descr_type = -1; \
+		} \
+		break;
+
+		_BRANCH(USB_DESCTYPE_DEVICE,
+		    usb_standard_device_descriptor_t,
+		    dump_descriptor_tree_brief_device);
+		_BRANCH(USB_DESCTYPE_CONFIGURATION,
+		    usb_standard_configuration_descriptor_t,
+		    dump_descriptor_tree_brief_configuration);
+		_BRANCH(USB_DESCTYPE_INTERFACE,
+		    usb_standard_interface_descriptor_t,
+		    dump_descriptor_tree_brief_interface);
+		_BRANCH(USB_DESCTYPE_ENDPOINT,
+		    usb_standard_endpoint_descriptor_t,
+		    dump_descriptor_tree_brief_endpoint);
+
+		default:
+			break;
+	}
+
+	if (descr_type == -1) {
+		printf("%sInvalid descriptor.\n", indent);
+	}
+}
+
+void dump_descriptor_tree_brief(usbinfo_device_t *dev)
+{
+	dump_descriptor_tree_brief_callback((uint8_t *)&dev->device_descriptor,
+	    0, NULL);
+	browse_descriptor_tree(dev->full_configuration_descriptor,
+	    dev->full_configuration_descriptor_size,
+	    usb_dp_standard_descriptor_nesting,
+	    dump_descriptor_tree_brief_callback,
+	    1,
+	    NULL);
 }
 
Index: uspace/app/usbinfo/main.c
===================================================================
--- uspace/app/usbinfo/main.c	(revision 81ca2044fc8ece8a458baa4b4bb7520a019fd9ea)
+++ uspace/app/usbinfo/main.c	(revision e160da4d15699337ef066c7b2aa146e9cae810fc)
@@ -47,15 +47,4 @@
 #include "usbinfo.h"
 
-static void print_usage(char *app_name)
-{
-#define INDENT "      "
-	printf(NAME ": query USB devices for descriptors\n\n");
-	printf("Usage: %s [options] device [device [device [ ... ]]]\n",
-	    app_name);
-	printf(INDENT "The device is a devman path to the device.\n");
-	printf("\n");
-#undef INDENT
-}
-
 static bool resolve_hc_handle_and_dev_addr(const char *devpath,
     devman_handle_t *out_hc_handle, usb_address_t *out_device_address)
@@ -130,11 +119,36 @@
 }
 
+static void print_usage(char *app_name)
+{
+#define _INDENT "      "
+#define _OPTION(opt, description) \
+	printf(_INDENT opt "\n" _INDENT _INDENT description "\n")
+
+	printf(NAME ": query USB devices for descriptors\n\n");
+	printf("Usage: %s [options] device [device [device [ ... ]]]\n",
+	    app_name);
+	printf(_INDENT "The device is a devman path to the device.\n");
+
+	_OPTION("-h --help", "Print this help and exit.");
+	_OPTION("-i --identification", "Brief device identification.");
+	_OPTION("-m --match-ids", "Print match ids generated for the device.");
+	_OPTION("-t --descriptor-tree", "Print descriptor tree.");
+
+	printf("\n");
+	printf("If no option is specified, `-i' is considered default.\n");
+	printf("\n");
+
+#undef _OPTION
+#undef _INDENT
+}
+
 static struct option long_options[] = {
 	{"help", no_argument, NULL, 'h'},
 	{"identification", no_argument, NULL, 'i'},
 	{"match-ids", no_argument, NULL, 'm'},
+	{"descriptor-tree", no_argument, NULL, 't'},
 	{0, 0, NULL, 0}
 };
-static const char *short_options = "him";
+static const char *short_options = "himt";
 
 int main(int argc, char *argv[])
@@ -147,4 +161,5 @@
 	bool action_print_short_identification = false;
 	bool action_print_match_ids = false;
+	bool action_print_descriptor_tree = false;
 
 	/*
@@ -171,5 +186,9 @@
 				action_print_match_ids = true;
 				break;
+			case 't':
+				action_print_descriptor_tree = true;
+				break;
 			default:
+				assert(false && "unreachable code");
 				break;
 		}
@@ -177,5 +196,7 @@
 
 	/* Set the default action. */
-	if (!action_print_match_ids && !action_print_short_identification) {
+	if (!action_print_match_ids
+	    && !action_print_short_identification
+	    && !action_print_descriptor_tree) {
 		action_print_short_identification = true;
 	}
@@ -215,4 +236,7 @@
 			dump_device_match_ids(dev);
 		}
+		if (action_print_descriptor_tree) {
+			dump_descriptor_tree_brief(dev);
+		}
 
 		/* Destroy the control pipe (close the session etc.). */
Index: uspace/app/usbinfo/usbinfo.h
===================================================================
--- uspace/app/usbinfo/usbinfo.h	(revision 81ca2044fc8ece8a458baa4b4bb7520a019fd9ea)
+++ uspace/app/usbinfo/usbinfo.h	(revision e160da4d15699337ef066c7b2aa146e9cae810fc)
@@ -40,4 +40,5 @@
 #include <usb/pipes.h>
 #include <usb/debug.h>
+#include <usb/dp.h>
 #include <ipc/devman.h>
 
@@ -46,4 +47,6 @@
 	usb_device_connection_t wire;
 	usb_standard_device_descriptor_t device_descriptor;
+	uint8_t *full_configuration_descriptor;
+	size_t full_configuration_descriptor_size;
 } usbinfo_device_t;
 
@@ -65,7 +68,13 @@
 void destroy_device(usbinfo_device_t *);
 
+typedef void (*dump_descriptor_in_tree_t)(uint8_t *, size_t, void *);
+void browse_descriptor_tree(uint8_t *, size_t, usb_dp_descriptor_nesting_t *,
+    dump_descriptor_in_tree_t, size_t, void *);
+
 
 void dump_short_device_identification(usbinfo_device_t *);
 void dump_device_match_ids(usbinfo_device_t *);
+void dump_descriptor_tree_brief(usbinfo_device_t *);
+
 
 #endif
