--- qemu-0.10.5/block-vvfat.c.orig	2009-05-20 22:46:58.000000000 +0200
+++ qemu-0.10.5/block-vvfat.c	2009-05-28 11:16:51.531545800 +0200
@@ -813,7 +813,13 @@
 
 static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
 {
-    return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
+    uint32_t ret;
+
+    if (sector_num < s->faked_sectors)
+        ret = 0;
+    else
+        ret = (sector_num-s->faked_sectors)/s->sectors_per_cluster;
+    return ret;
 }
 
 static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
--- qemu-0.10.5/configure.orig	2009-05-20 22:46:58.000000000 +0200
+++ qemu-0.10.5/configure	2009-05-28 13:28:12.282918400 +0200
@@ -201,17 +201,21 @@
 CYGWIN*)
 mingw32="yes"
 OS_CFLAGS="-mno-cygwin"
+VL_OS_LDFLAGS="-mno-cygwin"
+usb="generic"
 if [ "$cpu" = "i386" ] ; then
     kqemu="yes"
 fi
-audio_possible_drivers="sdl"
+audio_possible_drivers="dsound sdl fmod"
 ;;
 MINGW32*)
 mingw32="yes"
+usb="generic"
 if [ "$cpu" = "i386" ] ; then
     kqemu="yes"
 fi
 audio_possible_drivers="dsound sdl fmod"
+audio_drv_list="dsound sdl fmod"
 ;;
 GNU/kFreeBSD)
 audio_drv_list="oss"
@@ -1464,19 +1468,6 @@
 
 echo "#define CONFIG_UNAME_RELEASE \"$uname_release\"" >> $config_h
 
-# USB host support
-case "$usb" in
-linux)
-  echo "HOST_USB=linux" >> $config_mak
-;;
-bsd)
-  echo "HOST_USB=bsd" >> $config_mak
-;;
-*)
-  echo "HOST_USB=stub" >> $config_mak
-;;
-esac
-
 tools=
 if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then
   tools="qemu-img\$(EXESUF) $tools"
@@ -1488,6 +1479,38 @@
 
 test -f ${config_h}~ && cmp -s $config_h ${config_h}~ && mv ${config_h}~ $config_h
 
+# USB host support
+if [ "$usb" = "generic" ]; then
+  if `libusb-config --libs 1>&2> /dev/null`; then
+    LIBUSB_LIBS=`libusb-config --libs 2> /dev/null`
+    LIBUSB_CFLAGS=`libusb-config --cflags 2> /dev/null`
+  else
+    LIBUSB_LIBS=-lusb
+  fi
+
+  # libusb probe
+  cat > $TMPC << EOF
+#include <usb.h>
+int main( void ) { usb_set_debug(0); return 0; }
+EOF
+
+  if ! $cc -o $TMPE $TMPC $LIBUSB_LIBS $LIBUSB_CFLAGS $LDFLAGS 2> /dev/null ; then
+    usb="stub"
+  fi
+fi
+case "$usb" in
+linux)
+  echo "HOST_USB=linux" >> $config_mak
+;;
+generic)
+  echo "HOST_USB=libusb" >> $config_mak
+  echo "LIBUSB_LIBS=$LIBUSB_LIBS" >> $config_mak
+;;
+*)
+  echo "HOST_USB=stub" >> $config_mak
+;;
+esac
+
 for target in $target_list; do
 target_dir="$target"
 config_mak=$target_dir/config.mak
--- qemu-0.10.5/Makefile.target.orig	2009-05-20 22:46:58.000000000 +0200
+++ qemu-0.10.5/Makefile.target	2009-05-28 11:16:51.609668800 +0200
@@ -564,6 +564,10 @@
 # USB layer
 OBJS+= usb-ohci.o
 
+ifeq ($(HOST_USB), libusb)
+LIBS+=$(LIBUSB_LIBS)
+endif
+
 # EEPROM emulation
 OBJS += eeprom93xx.o
 
--- qemu-0.10.5/osdep.c.orig	2009-05-20 22:46:59.000000000 +0200
+++ qemu-0.10.5/osdep.c	2009-05-28 11:16:51.640918000 +0200
@@ -224,9 +224,10 @@
     fd = open(filename, O_RDWR | O_CREAT, 0600);
     if (fd == -1)
         return -1;
-
+#ifndef __CYGWIN__
     if (lockf(fd, F_TLOCK, 0) == -1)
         return -1;
+#endif
 
     len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid());
     if (write(fd, buffer, len) != len)
--- qemu-0.10.5/qemu_socket.h.orig	2009-05-20 22:47:00.000000000 +0200
+++ qemu-0.10.5/qemu_socket.h	2009-05-28 11:16:51.672167200 +0200
@@ -14,6 +14,7 @@
 #define EWOULDBLOCK WSAEWOULDBLOCK
 #define EINTR       WSAEINTR
 #define EINPROGRESS WSAEINPROGRESS
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
 
 int inet_aton(const char *cp, struct in_addr *ia);
 
--- qemu-0.10.5/slirp/bootp.c.orig	2009-05-20 22:47:00.000000000 +0200
+++ qemu-0.10.5/slirp/bootp.c	2009-05-28 11:16:51.719041000 +0200
@@ -66,6 +66,18 @@
     return bc;
 }
 
+/* From VirtualBox */
+static void release_addr(struct in_addr *paddr)
+{
+    int i;
+
+    i = ntohl(paddr->s_addr) - START_ADDR - ntohl(special_addr.s_addr);
+    if (i >= NB_ADDR)
+        return;
+    memset(bootp_clients[i].macaddr, '\0', 6);
+    bootp_clients[i].allocated = 0;
+}
+
 static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
 {
     BOOTPClient *bc;
@@ -133,6 +145,7 @@
     struct in_addr dns_addr;
     int dhcp_msg_type, val;
     uint8_t *q;
+    int freply_nack = 0;
 
     /* extract exact DHCP msg type */
     dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type);
@@ -141,6 +154,13 @@
     if (dhcp_msg_type == 0)
         dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
 
+    if (dhcp_msg_type == DHCPRELEASE) {
+        release_addr(&bp->bp_ciaddr);
+        dprintf("released addr=%08lx\n", ntohl(bp->bp_ciaddr.s_addr));
+        /* This message is not to be answered in any way. */
+        return;
+    }
+
     if (dhcp_msg_type != DHCPDISCOVER &&
         dhcp_msg_type != DHCPREQUEST)
         return;
@@ -155,7 +175,6 @@
     memset(rbp, 0, sizeof(struct bootp_t));
 
     if (dhcp_msg_type == DHCPDISCOVER) {
-    new_addr:
         bc = get_new_addr(&daddr.sin_addr);
         if (!bc) {
             dprintf("no address left\n");
@@ -165,9 +184,11 @@
     } else {
         bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
         if (!bc) {
-            /* if never assigned, behaves as if it was already
-               assigned (windows fix because it remembers its address) */
-            goto new_addr;
+            /* if never assigned, reply DHCPNACK to BROADCAST.
+               (windows fix because it remembers its address). */
+            daddr.sin_addr.s_addr = htonl(0xffffffff);
+            freply_nack = 1;
+            dprintf("reply NACK\n");
         }
     }
 
@@ -188,6 +209,9 @@
     rbp->bp_hlen = 6;
     memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
 
+    if (freply_nack)
+        rbp->bp_yiaddr.s_addr = htonl(0); /* When NACK, IP address is 0. */
+    else
     rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
     rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
 
@@ -204,11 +228,14 @@
     } else if (dhcp_msg_type == DHCPREQUEST) {
         *q++ = RFC2132_MSG_TYPE;
         *q++ = 1;
+        if (freply_nack)
+            *q++ = DHCPNACK;
+        else
         *q++ = DHCPACK;
     }
 
     if (dhcp_msg_type == DHCPDISCOVER ||
-        dhcp_msg_type == DHCPREQUEST) {
+        ((dhcp_msg_type == DHCPREQUEST) && !freply_nack)) {
         *q++ = RFC2132_SRV_ID;
         *q++ = 4;
         memcpy(q, &saddr.sin_addr, 4);
--- qemu-0.10.5/slirp/bootp.h.orig	2009-05-20 22:47:00.000000000 +0200
+++ qemu-0.10.5/slirp/bootp.h	2009-05-28 11:16:51.734665600 +0200
@@ -71,6 +71,8 @@
 #define DHCPOFFER		2
 #define DHCPREQUEST		3
 #define DHCPACK			5
+#define DHCPNACK		6
+#define DHCPRELEASE		7
 
 #define RFC1533_VENDOR_MAJOR	0
 #define RFC1533_VENDOR_MINOR	0
--- qemu-0.10.5/usb-libusb.c.orig	2009-05-28 11:16:51.734665600 +0200
+++ qemu-0.10.5/usb-libusb.c	2009-05-28 11:16:51.734665600 +0200
@@ -0,0 +1,536 @@
+/*
+ * Generic libusb host USB redirector
+ *
+ * Copyright (c) 2006 Lonnie Mendez
+ *      Code adapted from and modified from usb-linux.c
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "hw/usb.h"
+#include "console.h"
+
+#include <usb.h>
+#if !defined(ETIMEDOUT) && defined(_WIN32)
+#define ETIMEDOUT 116
+#endif
+
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
+                        int vendor_id, int product_id, 
+                        const char *product_name, int speed);
+static int usb_host_find_device(int *pbus_num, int *paddr, 
+                                const char *devname, struct usb_device **device);
+
+
+//#define DEBUG
+/* should be adjustable for interrupt endpoints */
+#define TRANS_TIMEOUT 50
+
+typedef struct USBHostDevice {
+    USBDevice dev;
+    int num_interfaces;
+    struct usb_dev_handle *udev;
+} USBHostDevice;
+static USBHostDevice *hostdev_find(int bus_num, int addr);
+static void usb_host_handle_reset(USBDevice *dev)
+{   
+#ifdef DEBUG
+    printf("usb_host_handle_reset called\n");
+#endif
+
+#if 0
+    USBHostDevice *s = (USBHostDevice *)dev;
+    /* USBDEVFS_RESET, but not the first time as it has already be
+       * done by the host OS - usb_reset() - must reopen handle
+       */
+    usb_reset(s->udev);
+#endif
+} 
+
+static int usb_host_handle_control(USBDevice *dev,
+                                   int request,
+                                   int value,
+                                   int index,
+                                   int length,
+                                   uint8_t *data)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+    int ret;
+
+    if (request == (DeviceOutRequest | USB_REQ_SET_ADDRESS)) {
+        /* specific SET_ADDRESS support */
+        dev->addr = value;
+        return 0;
+    } else {
+        ret = usb_control_msg(s->udev, request >> 8, request & 0xff, value, index,
+                              data, length, TRANS_TIMEOUT);
+        if (ret < 0) {
+#ifdef DEBUG
+            printf("handle_control: ret is %d - %s\n", ret, usb_strerror());
+#endif
+            // TODO: figure out how usb_clear_halt()/etc fit in for different host OS
+            switch(ret) {
+            case -ETIMEDOUT:
+                return USB_RET_NAK;
+            case -EIO:
+            default:
+                return USB_RET_STALL;
+            }
+        } else {
+            return ret;
+        }
+   }
+}
+
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+    int ret = 0;
+#ifdef DEBUG
+    int i;
+#endif
+    uint8_t devep = p->devep;
+    int pid = p->pid;
+    uint8_t *data = p->data;
+    int len = p->len;
+
+    /* XXX: optimize and handle all data types by looking at the
+       config descriptor */
+    if (pid == USB_TOKEN_IN)
+        devep |= 0x80;
+
+#ifdef DEBUG
+    if (pid != USB_TOKEN_IN) {
+        printf("writing(%d bytes - to ep %x):\n", len, devep);
+        for (i = 0; i < len; i++)
+            printf("%x ", data[i]);
+        printf("\n");
+    }
+#endif
+
+    /* most libusb backends channel interrupt packets to a bulk transfer
+      * handler, with the exception of bsd.c */
+    switch(pid) {
+        case USB_TOKEN_IN:
+            ret = usb_bulk_read(s->udev, devep, data, len, TRANS_TIMEOUT);
+            break;
+        case USB_TOKEN_OUT:
+            ret = usb_bulk_write(s->udev, devep, data, len, TRANS_TIMEOUT);
+            break;
+    }
+    
+#ifdef DEBUG
+    if (pid == USB_TOKEN_IN) {
+        printf("reading(%d bytes - from ep %x):\n", ret, devep);
+        for (i = 0; i < ret; i++)
+            printf("%x ", data[i]);
+        printf("\n");
+    }
+#endif
+
+    if (ret < 0) {
+#ifdef DEBUG
+        printf("handle_data: ret is %d - %s\n", ret, usb_strerror());
+#endif
+        // TODO: figure out how usb_clear_halt()/etc fit in for different host OS
+        switch(ret) {
+        case -ETIMEDOUT:
+            return USB_RET_NAK;
+        case -EIO: /* stall condition on win32 - same for mac os x? */
+            usb_clear_halt(s->udev, devep);
+        case -EPIPE:
+        default:
+            return USB_RET_STALL;
+        }
+    } else {
+        return ret;
+    }
+}
+
+static void usb_host_handle_destroy(USBDevice *dev)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+    int i;
+
+    if (!s)
+        return;
+
+    for (i = 0; i < s->num_interfaces; i++)
+        usb_release_interface(s->udev, i);
+    usb_close(s->udev);
+    s->udev = NULL;
+    qemu_free(s);
+}
+
+/* XXX: exclude high speed devices or implement EHCI */
+USBDevice *usb_host_device_open(const char *devname)
+{
+    USBHostDevice *dev;
+    struct usb_dev_handle *udev = NULL;
+    struct usb_device *device = NULL;
+    struct usb_config_descriptor config_desc;
+    int config_descr_len, nb_interfaces;
+    int bus_num, addr, interface, ret;
+    char product_name[32] ;
+    
+    if ((ret = usb_host_find_device(&bus_num, &addr, devname, &device)) < 0) 
+        return NULL;
+
+    if (!device)
+        return NULL;
+    
+    udev = usb_open(device);
+
+    if (!udev) {
+#ifdef DEBUG
+        printf("couldn't open usb device at %d.%d\n", bus_num, addr);
+#endif
+        return NULL;
+    }
+    
+    /* read the 1st config descriptor */
+    config_descr_len = usb_get_descriptor(udev, 2, 0, &config_desc, sizeof(config_desc));
+    if (config_descr_len <= 0) {
+        printf("read config_desc: %s", usb_strerror());
+        goto fail;
+    }
+    
+    if (config_descr_len > sizeof(config_desc)) {
+        printf("config_desc size mismatch\n");
+        goto fail;
+    }
+        
+    nb_interfaces = config_desc.bNumInterfaces;
+    usb_set_configuration(udev, config_desc.bConfigurationValue);
+    
+    for (interface = 0; interface < nb_interfaces; interface++) {
+        /* might as well */
+#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
+        usb_detach_kernel_driver_np(udev, interface);
+#endif
+    
+        ret = usb_claim_interface(udev, interface);
+    
+        if (ret < 0) {
+            printf("usb_claim_interface: %s", usb_strerror());
+        fail:
+            if (udev)
+                usb_close(udev);
+            return NULL;
+        }
+    }
+
+#ifdef DEBUG
+    printf("host USB device %d.%d grabbed\n", bus_num, addr);
+#endif
+
+    dev = qemu_mallocz(sizeof(USBHostDevice));
+    if (!dev)
+        goto fail;
+    
+    /* not accurate - might be better off reporting only low/full */
+    switch (device->descriptor.bcdUSB) {
+        case 0x0110:
+            dev->dev.speed = USB_SPEED_FULL;
+            break;
+        case 0x0200:
+            dev->dev.speed = USB_SPEED_HIGH;
+            break;
+        case 0x0100:
+            dev->dev.speed = USB_SPEED_LOW;
+            break;
+        default:
+            dev->dev.speed = USB_SPEED_FULL;
+    }
+    
+    dev->udev = udev;
+    dev->num_interfaces = nb_interfaces;
+    dev->dev.handle_packet = usb_generic_handle_packet;
+
+    dev->dev.handle_reset = usb_host_handle_reset;
+    dev->dev.handle_control = usb_host_handle_control;
+    dev->dev.handle_data = usb_host_handle_data;
+    dev->dev.handle_destroy = usb_host_handle_destroy;
+    
+    /* get product string if available */
+    usb_get_string_simple(udev, device->descriptor.iProduct,
+                                   product_name, sizeof(product_name));
+    
+    if (product_name[0] == '\0')
+        snprintf(dev->dev.devname, sizeof(dev->dev.devname),
+                   "host:%s", devname);
+    else
+        pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
+                   product_name);
+    
+    return (USBDevice *)dev;
+}
+
+int usb_host_device_close(const char *devname)
+{
+    USBHostDevice *dev;
+    struct usb_device *device = NULL;
+    int bus_num, addr, ret;
+    
+    if ((ret = usb_host_find_device(&bus_num, &addr, devname, &device)) < 0) 
+        return -1;
+
+    if (!device)
+        return -1;
+	dev = hostdev_find(bus_num, addr);	
+    if (dev) {
+            usb_device_del_addr(0, dev->dev.addr);
+            return 0;
+    }
+
+    return -1;
+}
+static USBHostDevice *hostdev_find(int bus_num, int addr)
+{
+    return NULL;
+}
+
+static int usb_host_scan(void *opaque, struct usb_device **device,
+                         USBScanFunc *func)
+{
+    struct usb_bus *busses, *bus;
+    struct usb_dev_handle *udev;
+    struct usb_device *dev;
+    uint16_t vendor_id, product_id, class_id, speed;
+    char product_name[256];
+    int ret = 0;
+
+#ifdef DEBUG
+    usb_set_debug(255);
+#else
+    usb_set_debug(0);
+#endif
+    
+    usb_init();
+    usb_find_busses();
+    usb_find_devices();
+
+    busses = usb_get_busses();
+
+    for (bus = busses; bus; bus = bus->next) {
+        for (dev = bus->devices; dev; dev = dev->next) {
+            vendor_id = dev->descriptor.idVendor;
+            product_id = dev->descriptor.idProduct;
+
+            /* only find devices - not root hubs */
+            if (vendor_id == 0x0000 && product_id == 0x0000)
+                continue;
+
+            udev = usb_open(dev);
+            if (!udev)
+                continue;
+
+            class_id = dev->descriptor.bDeviceClass;
+            
+            /* not accurate - might be better off reporting only low/full */
+            switch (dev->descriptor.bcdUSB) {
+                case 0x0110:
+                    speed = USB_SPEED_FULL;
+                    break;
+                case 0x0200:
+                    speed = USB_SPEED_HIGH;
+                    break;
+                case 0x0100:
+                    speed = USB_SPEED_LOW;
+                    break;
+                default:
+                    speed = USB_SPEED_FULL;
+            }
+
+            ret = usb_get_string_simple(udev, dev->descriptor.iProduct,
+                                        product_name, sizeof(product_name));
+            if (ret < 0)
+                product_name[0] = '\0';
+
+            ret = func(opaque, bus->location, dev->devnum, class_id, vendor_id,
+                       product_id, product_name, speed);
+
+            usb_close(udev);
+
+            if (ret) {
+                *device = dev;
+                goto the_end;
+            }
+        }
+    }
+
+the_end:
+    return ret;
+
+}
+
+typedef struct FindDeviceState {
+    int vendor_id;
+    int product_id;
+    int bus_num;
+    int addr;
+} FindDeviceState;
+
+static int usb_host_find_device_scan(void *opaque, int bus_num, int addr, 
+                                     int class_id,
+                                     int vendor_id, int product_id, 
+                                     const char *product_name, int speed)
+{
+    FindDeviceState *s = opaque;
+    if (s->vendor_id == -1 &&
+        s->product_id == -1 &&
+        bus_num == s->bus_num &&
+        addr == s->addr) {
+        s->vendor_id = vendor_id;
+        s->product_id = product_id;
+        return 1;
+    } else if (vendor_id == s->vendor_id &&
+        product_id == s->product_id) {
+        s->bus_num = bus_num;
+        s->addr = addr;
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+/* the syntax is : 
+   'bus.addr' (decimal numbers) or 
+   'vendor_id:product_id' (hexa numbers) */
+static int usb_host_find_device(int *pbus_num, int *paddr, 
+                                const char *devname, struct usb_device **device)
+{
+    const char *p;
+    int ret;
+    FindDeviceState fs;
+
+    p = strchr(devname, '.');
+    if (p) {
+        fs.vendor_id = -1;
+        fs.product_id = -1;
+        fs.bus_num = strtoul(devname, NULL, 0);
+        fs.addr = strtoul(p + 1, NULL, 0);
+        ret = usb_host_scan(&fs, device, usb_host_find_device_scan);
+        if (ret) {
+            *pbus_num = fs.bus_num;
+            *paddr = fs.addr;
+            return 0;
+        }
+    }
+    p = strchr(devname, ':');
+    if (p) {
+        fs.vendor_id = strtoul(devname, NULL, 16);
+        fs.product_id = strtoul(p + 1, NULL, 16);
+        ret = usb_host_scan(&fs, device, usb_host_find_device_scan);
+        if (ret) {
+            *pbus_num = fs.bus_num;
+            *paddr = fs.addr;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+    int class;
+    const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+    { USB_CLASS_AUDIO, "Audio"},
+    { USB_CLASS_COMM, "Communication"},
+    { USB_CLASS_HID, "HID"},
+    { USB_CLASS_HUB, "Hub" },
+    { USB_CLASS_PHYSICAL, "Physical" },
+    { USB_CLASS_PRINTER, "Printer" },
+    { USB_CLASS_MASS_STORAGE, "Storage" },
+    { USB_CLASS_CDC_DATA, "Data" },
+    { USB_CLASS_APP_SPEC, "Application Specific" },
+    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+    { USB_CLASS_STILL_IMAGE, "Still Image" },
+    { USB_CLASS_CSCID, 	"Smart Card" },
+    { USB_CLASS_CONTENT_SEC, "Content Security" },
+    { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+    const struct usb_class_info *p;
+    for(p = usb_class_info; p->class != -1; p++) {
+        if (p->class == class)
+            break;
+    }
+    return p->class_name;
+}
+
+void usb_info_device(int bus_num, int addr, int class_id,
+                     int vendor_id, int product_id, 
+                     const char *product_name,
+                     int speed)
+{
+    const char *class_str, *speed_str;
+
+    switch(speed) {
+    case USB_SPEED_LOW: 
+        speed_str = "1.5"; 
+        break;
+    case USB_SPEED_FULL: 
+        speed_str = "12"; 
+        break;
+    case USB_SPEED_HIGH: 
+        speed_str = "480"; 
+        break;
+    default:
+        speed_str = "?"; 
+        break;
+    }
+
+    term_printf("  Device %d.%d, speed %s Mb/s\n", 
+                bus_num, addr, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str) 
+        term_printf("    %s:", class_str);
+    else
+        term_printf("    Class %02x:", class_id);
+    term_printf(" USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0')
+        term_printf(", %s", product_name);
+    term_printf("\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr, 
+                                int class_id,
+                                int vendor_id, int product_id, 
+                                const char *product_name,
+                                int speed)
+{
+    usb_info_device(bus_num, addr, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+void usb_host_info(void)
+{
+    usb_host_scan(NULL, NULL, usb_host_info_device);
+}