usb: the big rename
authorGerd Hoffmann <kraxel@redhat.com>
Wed, 7 Mar 2012 13:55:18 +0000 (14:55 +0100)
committerGerd Hoffmann <kraxel@redhat.com>
Tue, 13 Mar 2012 09:15:32 +0000 (10:15 +0100)
Reorganize usb source files.  Create a new hw/usb/ directory and move
all usb source code to that place.  Also make filenames a bit more
descriptive.  Host adapters are prefixed with "hch-" now, usb device
emulations are prefixed with "dev-".  Fixup paths Makefile and include
paths to make it compile.  No code changes.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
50 files changed:
Makefile.objs
Makefile.target
configure
hw/usb-audio.c [deleted file]
hw/usb-bt.c [deleted file]
hw/usb-bus.c [deleted file]
hw/usb-ccid.c [deleted file]
hw/usb-desc.c [deleted file]
hw/usb-desc.h [deleted file]
hw/usb-ehci.c [deleted file]
hw/usb-hid.c [deleted file]
hw/usb-hub.c [deleted file]
hw/usb-libhw.c [deleted file]
hw/usb-msd.c [deleted file]
hw/usb-musb.c [deleted file]
hw/usb-net.c [deleted file]
hw/usb-ohci.c [deleted file]
hw/usb-serial.c [deleted file]
hw/usb-uhci.c [deleted file]
hw/usb-wacom.c [deleted file]
hw/usb-xhci.c [deleted file]
hw/usb.c [deleted file]
hw/usb/bus.c [new file with mode: 0644]
hw/usb/core.c [new file with mode: 0644]
hw/usb/desc.c [new file with mode: 0644]
hw/usb/desc.h [new file with mode: 0644]
hw/usb/dev-audio.c [new file with mode: 0644]
hw/usb/dev-bluetooth.c [new file with mode: 0644]
hw/usb/dev-hid.c [new file with mode: 0644]
hw/usb/dev-hub.c [new file with mode: 0644]
hw/usb/dev-network.c [new file with mode: 0644]
hw/usb/dev-serial.c [new file with mode: 0644]
hw/usb/dev-smartcard-reader.c [new file with mode: 0644]
hw/usb/dev-storage.c [new file with mode: 0644]
hw/usb/dev-wacom.c [new file with mode: 0644]
hw/usb/hcd-ehci.c [new file with mode: 0644]
hw/usb/hcd-musb.c [new file with mode: 0644]
hw/usb/hcd-ohci.c [new file with mode: 0644]
hw/usb/hcd-uhci.c [new file with mode: 0644]
hw/usb/hcd-xhci.c [new file with mode: 0644]
hw/usb/host-bsd.c [new file with mode: 0644]
hw/usb/host-linux.c [new file with mode: 0644]
hw/usb/host-stub.c [new file with mode: 0644]
hw/usb/libhw.c [new file with mode: 0644]
hw/usb/redirect.c [new file with mode: 0644]
trace-events
usb-bsd.c [deleted file]
usb-linux.c [deleted file]
usb-redir.c [deleted file]
usb-stub.c [deleted file]

index 5f0b3f71366a0b96dcf9d1c996fb47f521d96985..1eca4a86239a1ac523aab03dcaa5943bd8cbe28c 100644 (file)
@@ -107,13 +107,15 @@ common-obj-y += eeprom93xx.o
 common-obj-y += scsi-disk.o cdrom.o
 common-obj-y += scsi-generic.o scsi-bus.o
 common-obj-y += hid.o
-common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
-common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o usb-audio.o
+common-obj-y += usb/core.o usb/bus.o usb/desc.o usb/dev-hub.o
+common-obj-y += usb/host-$(HOST_USB).o
+common-obj-y += usb/dev-hid.o usb/dev-storage.o usb/dev-wacom.o
+common-obj-y += usb/dev-serial.o usb/dev-network.o usb/dev-audio.o
 common-obj-$(CONFIG_SSI) += ssi.o
 common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
 common-obj-$(CONFIG_SD) += sd.o
-common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
-common-obj-y += bt-hci-csr.o
+common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
+common-obj-y += bt-hci-csr.o usb/dev-bluetooth.o
 common-obj-y += buffered_file.o migration.o migration-tcp.o
 common-obj-y += qemu-char.o #aio.o
 common-obj-y += msmouse.o ps2.o
@@ -194,7 +196,7 @@ user-obj-y += $(trace-obj-y)
 hw-obj-y =
 hw-obj-y += vl.o loader.o
 hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
-hw-obj-y += usb-libhw.o
+hw-obj-y += usb/libhw.o
 hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
 hw-obj-y += fw_cfg.o
 hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
@@ -217,10 +219,10 @@ hw-obj-$(CONFIG_PARALLEL) += parallel.o
 hw-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
 hw-obj-$(CONFIG_PCSPK) += pcspk.o
 hw-obj-$(CONFIG_PCKBD) += pckbd.o
-hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
-hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
-hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
-hw-obj-$(CONFIG_USB_XHCI) += usb-xhci.o
+hw-obj-$(CONFIG_USB_UHCI) += usb/hcd-uhci.o
+hw-obj-$(CONFIG_USB_OHCI) += usb/hcd-ohci.o
+hw-obj-$(CONFIG_USB_EHCI) += usb/hcd-ehci.o
+hw-obj-$(CONFIG_USB_XHCI) += usb/hcd-xhci.o
 hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
@@ -228,9 +230,9 @@ hw-obj-$(CONFIG_DMA) += dma.o
 hw-obj-$(CONFIG_I82374) += i82374.o
 hw-obj-$(CONFIG_HPET) += hpet.o
 hw-obj-$(CONFIG_APPLESMC) += applesmc.o
-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
+hw-obj-$(CONFIG_SMARTCARD) += usb/dev-smartcard-reader.o ccid-card-passthru.o
 hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
-hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o
+hw-obj-$(CONFIG_USB_REDIR) += usb/redirect.o
 hw-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
 
 # PPC devices
index 1bd25a89ad32d5033e2ede3ef3c3cc86da6abba6..ed769527f72bfbe4ad20174d69d762a72f1b4324 100644 (file)
@@ -370,7 +370,7 @@ obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
 obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
                omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o
 obj-arm-y += omap_sx1.o palm.o tsc210x.o
-obj-arm-y += nseries.o blizzard.o onenand.o cbus.o tusb6010.o usb-musb.o
+obj-arm-y += nseries.o blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o
 obj-arm-y += mst_fpga.o mainstone.o
 obj-arm-y += z2.o
 obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o
index d7631ed24724de95aeb9c3e0a52ff9a637c3de26..fe4fc4fb967a19e13dd7726b285c0142f8858be5 100755 (executable)
--- a/configure
+++ b/configure
@@ -3447,6 +3447,7 @@ mkdir -p $target_dir
 mkdir -p $target_dir/fpu
 mkdir -p $target_dir/tcg
 mkdir -p $target_dir/ide
+mkdir -p $target_dir/usb
 mkdir -p $target_dir/9pfs
 mkdir -p $target_dir/kvm
 if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" -o "$target" = "arm-bsd-user" -o "$target" = "armeb-bsd-user" ; then
@@ -3863,7 +3864,7 @@ done # for target in $targets
 DIRS="tests tests/tcg tests/tcg/cris slirp audio block net pc-bios/optionrom"
 DIRS="$DIRS pc-bios/spapr-rtas"
 DIRS="$DIRS roms/seabios roms/vgabios"
-DIRS="$DIRS fsdev ui"
+DIRS="$DIRS fsdev ui usb"
 DIRS="$DIRS qapi qapi-generated"
 DIRS="$DIRS qga trace qom"
 FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
@@ -3904,6 +3905,7 @@ for hwlib in 32 64; do
   d=libhw$hwlib
   mkdir -p $d
   mkdir -p $d/ide
+  mkdir -p $d/usb
   symlink $source_path/Makefile.hw $d/Makefile
   mkdir -p $d/9pfs
   echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak
diff --git a/hw/usb-audio.c b/hw/usb-audio.c
deleted file mode 100644 (file)
index fed1361..0000000
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * QEMU USB audio device
- *
- * written by:
- *  H. Peter Anvin <hpa@linux.intel.com>
- *  Gerd Hoffmann <kraxel@redhat.com>
- *
- * lousely based on usb net device code which is:
- *
- * Copyright (c) 2006 Thomas Sailer
- * Copyright (c) 2008 Andrzej Zaborowski
- *
- * 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 "usb.h"
-#include "usb-desc.h"
-#include "hw.h"
-#include "audiodev.h"
-#include "audio/audio.h"
-
-#define USBAUDIO_VENDOR_NUM     0x46f4 /* CRC16() of "QEMU" */
-#define USBAUDIO_PRODUCT_NUM    0x0002
-
-#define DEV_CONFIG_VALUE        1 /* The one and only */
-
-/* Descriptor subtypes for AC interfaces */
-#define DST_AC_HEADER           1
-#define DST_AC_INPUT_TERMINAL   2
-#define DST_AC_OUTPUT_TERMINAL  3
-#define DST_AC_FEATURE_UNIT     6
-/* Descriptor subtypes for AS interfaces */
-#define DST_AS_GENERAL          1
-#define DST_AS_FORMAT_TYPE      2
-/* Descriptor subtypes for endpoints */
-#define DST_EP_GENERAL          1
-
-enum usb_audio_strings {
-    STRING_NULL,
-    STRING_MANUFACTURER,
-    STRING_PRODUCT,
-    STRING_SERIALNUMBER,
-    STRING_CONFIG,
-    STRING_USBAUDIO_CONTROL,
-    STRING_INPUT_TERMINAL,
-    STRING_FEATURE_UNIT,
-    STRING_OUTPUT_TERMINAL,
-    STRING_NULL_STREAM,
-    STRING_REAL_STREAM,
-};
-
-static const USBDescStrings usb_audio_stringtable = {
-    [STRING_MANUFACTURER]       = "QEMU",
-    [STRING_PRODUCT]            = "QEMU USB Audio",
-    [STRING_SERIALNUMBER]       = "1",
-    [STRING_CONFIG]             = "Audio Configuration",
-    [STRING_USBAUDIO_CONTROL]   = "Audio Device",
-    [STRING_INPUT_TERMINAL]     = "Audio Output Pipe",
-    [STRING_FEATURE_UNIT]       = "Audio Output Volume Control",
-    [STRING_OUTPUT_TERMINAL]    = "Audio Output Terminal",
-    [STRING_NULL_STREAM]        = "Audio Output - Disabled",
-    [STRING_REAL_STREAM]        = "Audio Output - 48 kHz Stereo",
-};
-
-#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff)
-#define U24(x) U16(x), (((x) >> 16) & 0xff)
-#define U32(x) U24(x), (((x) >> 24) & 0xff)
-
-/*
- * A Basic Audio Device uses these specific values
- */
-#define USBAUDIO_PACKET_SIZE     192
-#define USBAUDIO_SAMPLE_RATE     48000
-#define USBAUDIO_PACKET_INTERVAL 1
-
-static const USBDescIface desc_iface[] = {
-    {
-        .bInterfaceNumber              = 0,
-        .bNumEndpoints                 = 0,
-        .bInterfaceClass               = USB_CLASS_AUDIO,
-        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_CONTROL,
-        .bInterfaceProtocol            = 0x04,
-        .iInterface                    = STRING_USBAUDIO_CONTROL,
-        .ndesc                         = 4,
-        .descs = (USBDescOther[]) {
-            {
-                /* Headphone Class-Specific AC Interface Header Descriptor */
-                .data = (uint8_t[]) {
-                    0x09,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_HEADER,              /*  u8  bDescriptorSubtype */
-                    U16(0x0100),                /* u16  bcdADC */
-                    U16(0x2b),                  /* u16  wTotalLength */
-                    0x01,                       /*  u8  bInCollection */
-                    0x01,                       /*  u8  baInterfaceNr */
-                }
-            },{
-                /* Generic Stereo Input Terminal ID1 Descriptor */
-                .data = (uint8_t[]) {
-                    0x0c,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_INPUT_TERMINAL,      /*  u8  bDescriptorSubtype */
-                    0x01,                       /*  u8  bTerminalID */
-                    U16(0x0101),                /* u16  wTerminalType */
-                    0x00,                       /*  u8  bAssocTerminal */
-                    0x02,                       /* u16  bNrChannels */
-                    U16(0x0003),                /* u16  wChannelConfig */
-                    0x00,                       /*  u8  iChannelNames */
-                    STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
-                }
-            },{
-                /* Generic Stereo Feature Unit ID2 Descriptor */
-                .data = (uint8_t[]) {
-                    0x0d,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_FEATURE_UNIT,        /*  u8  bDescriptorSubtype */
-                    0x02,                       /*  u8  bUnitID */
-                    0x01,                       /*  u8  bSourceID */
-                    0x02,                       /*  u8  bControlSize */
-                    U16(0x0001),                /* u16  bmaControls(0) */
-                    U16(0x0002),                /* u16  bmaControls(1) */
-                    U16(0x0002),                /* u16  bmaControls(2) */
-                    STRING_FEATURE_UNIT,        /*  u8  iFeature */
-                }
-            },{
-                /* Headphone Ouptut Terminal ID3 Descriptor */
-                .data = (uint8_t[]) {
-                    0x09,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_OUTPUT_TERMINAL,     /*  u8  bDescriptorSubtype */
-                    0x03,                       /*  u8  bUnitID */
-                    U16(0x0301),                /* u16  wTerminalType (SPK) */
-                    0x00,                       /*  u8  bAssocTerminal */
-                    0x02,                       /*  u8  bSourceID */
-                    STRING_OUTPUT_TERMINAL,     /*  u8  iTerminal */
-                }
-            }
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 0,
-        .bNumEndpoints                 = 0,
-        .bInterfaceClass               = USB_CLASS_AUDIO,
-        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
-        .iInterface                    = STRING_NULL_STREAM,
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 1,
-        .bNumEndpoints                 = 1,
-        .bInterfaceClass               = USB_CLASS_AUDIO,
-        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
-        .iInterface                    = STRING_REAL_STREAM,
-        .ndesc                         = 2,
-        .descs = (USBDescOther[]) {
-            {
-                /* Headphone Class-specific AS General Interface Descriptor */
-                .data = (uint8_t[]) {
-                    0x07,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
-                    0x01,                       /*  u8  bTerminalLink */
-                    0x00,                       /*  u8  bDelay */
-                    0x01, 0x00,                 /* u16  wFormatTag */
-                }
-            },{
-                /* Headphone Type I Format Type Descriptor */
-                .data = (uint8_t[]) {
-                    0x0b,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
-                    0x01,                       /*  u8  bFormatType */
-                    0x02,                       /*  u8  bNrChannels */
-                    0x02,                       /*  u8  bSubFrameSize */
-                    0x10,                       /*  u8  bBitResolution */
-                    0x01,                       /*  u8  bSamFreqType */
-                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
-                }
-            }
-        },
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | 0x01,
-                .bmAttributes          = 0x0d,
-                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE,
-                .bInterval             = 1,
-                .is_audio              = 1,
-                /* Stereo Headphone Class-specific
-                   AS Audio Data Endpoint Descriptor */
-                .extra = (uint8_t[]) {
-                    0x07,                       /*  u8  bLength */
-                    USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
-                    DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
-                    0x00,                       /*  u8  bmAttributes */
-                    0x00,                       /*  u8  bLockDelayUnits */
-                    U16(0x0000),                /* u16  wLockDelay */
-                },
-            },
-        }
-    }
-};
-
-static const USBDescDevice desc_device = {
-    .bcdUSB                        = 0x0200,
-    .bMaxPacketSize0               = 64,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 2,
-            .bConfigurationValue   = DEV_CONFIG_VALUE,
-            .iConfiguration        = STRING_CONFIG,
-            .bmAttributes          = 0xc0,
-            .bMaxPower             = 0x32,
-            .nif = ARRAY_SIZE(desc_iface),
-            .ifs = desc_iface,
-        },
-    },
-};
-
-static const USBDesc desc_audio = {
-    .id = {
-        .idVendor          = USBAUDIO_VENDOR_NUM,
-        .idProduct         = USBAUDIO_PRODUCT_NUM,
-        .bcdDevice         = 0,
-        .iManufacturer     = STRING_MANUFACTURER,
-        .iProduct          = STRING_PRODUCT,
-        .iSerialNumber     = STRING_SERIALNUMBER,
-    },
-    .full = &desc_device,
-    .str  = usb_audio_stringtable,
-};
-
-/*
- * A USB audio device supports an arbitrary number of alternate
- * interface settings for each interface.  Each corresponds to a block
- * diagram of parameterized blocks.  This can thus refer to things like
- * number of channels, data rates, or in fact completely different
- * block diagrams.  Alternative setting 0 is always the null block diagram,
- * which is used by a disabled device.
- */
-enum usb_audio_altset {
-    ALTSET_OFF  = 0x00,         /* No endpoint */
-    ALTSET_ON   = 0x01,         /* Single endpoint */
-};
-
-/*
- * Class-specific control requests
- */
-#define CR_SET_CUR      0x01
-#define CR_GET_CUR      0x81
-#define CR_SET_MIN      0x02
-#define CR_GET_MIN      0x82
-#define CR_SET_MAX      0x03
-#define CR_GET_MAX      0x83
-#define CR_SET_RES      0x04
-#define CR_GET_RES      0x84
-#define CR_SET_MEM      0x05
-#define CR_GET_MEM      0x85
-#define CR_GET_STAT     0xff
-
-/*
- * Feature Unit Control Selectors
- */
-#define MUTE_CONTROL                    0x01
-#define VOLUME_CONTROL                  0x02
-#define BASS_CONTROL                    0x03
-#define MID_CONTROL                     0x04
-#define TREBLE_CONTROL                  0x05
-#define GRAPHIC_EQUALIZER_CONTROL       0x06
-#define AUTOMATIC_GAIN_CONTROL          0x07
-#define DELAY_CONTROL                   0x08
-#define BASS_BOOST_CONTROL              0x09
-#define LOUDNESS_CONTROL                0x0a
-
-/*
- * buffering
- */
-
-struct streambuf {
-    uint8_t *data;
-    uint32_t size;
-    uint32_t prod;
-    uint32_t cons;
-};
-
-static void streambuf_init(struct streambuf *buf, uint32_t size)
-{
-    g_free(buf->data);
-    buf->size = size - (size % USBAUDIO_PACKET_SIZE);
-    buf->data = g_malloc(buf->size);
-    buf->prod = 0;
-    buf->cons = 0;
-}
-
-static void streambuf_fini(struct streambuf *buf)
-{
-    g_free(buf->data);
-    buf->data = NULL;
-}
-
-static int streambuf_put(struct streambuf *buf, USBPacket *p)
-{
-    uint32_t free = buf->size - (buf->prod - buf->cons);
-
-    if (!free) {
-        return 0;
-    }
-    assert(free >= USBAUDIO_PACKET_SIZE);
-    usb_packet_copy(p, buf->data + (buf->prod % buf->size),
-                    USBAUDIO_PACKET_SIZE);
-    buf->prod += USBAUDIO_PACKET_SIZE;
-    return USBAUDIO_PACKET_SIZE;
-}
-
-static uint8_t *streambuf_get(struct streambuf *buf)
-{
-    uint32_t used = buf->prod - buf->cons;
-    uint8_t *data;
-
-    if (!used) {
-        return NULL;
-    }
-    assert(used >= USBAUDIO_PACKET_SIZE);
-    data = buf->data + (buf->cons % buf->size);
-    buf->cons += USBAUDIO_PACKET_SIZE;
-    return data;
-}
-
-typedef struct USBAudioState {
-    /* qemu interfaces */
-    USBDevice dev;
-    QEMUSoundCard card;
-
-    /* state */
-    struct {
-        enum usb_audio_altset altset;
-        struct audsettings as;
-        SWVoiceOut *voice;
-        bool mute;
-        uint8_t vol[2];
-        struct streambuf buf;
-    } out;
-
-    /* properties */
-    uint32_t debug;
-    uint32_t buffer;
-} USBAudioState;
-
-static void output_callback(void *opaque, int avail)
-{
-    USBAudioState *s = opaque;
-    uint8_t *data;
-
-    for (;;) {
-        if (avail < USBAUDIO_PACKET_SIZE) {
-            return;
-        }
-        data = streambuf_get(&s->out.buf);
-        if (NULL == data) {
-            return;
-        }
-        AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE);
-        avail -= USBAUDIO_PACKET_SIZE;
-    }
-}
-
-static int usb_audio_set_output_altset(USBAudioState *s, int altset)
-{
-    switch (altset) {
-    case ALTSET_OFF:
-        streambuf_init(&s->out.buf, s->buffer);
-        AUD_set_active_out(s->out.voice, false);
-        break;
-    case ALTSET_ON:
-        AUD_set_active_out(s->out.voice, true);
-        break;
-    default:
-        return -1;
-    }
-
-    if (s->debug) {
-        fprintf(stderr, "usb-audio: set interface %d\n", altset);
-    }
-    s->out.altset = altset;
-    return 0;
-}
-
-/*
- * Note: we arbitrarily map the volume control range onto -inf..+8 dB
- */
-#define ATTRIB_ID(cs, attrib, idif)     \
-    (((cs) << 24) | ((attrib) << 16) | (idif))
-
-static int usb_audio_get_control(USBAudioState *s, uint8_t attrib,
-                                 uint16_t cscn, uint16_t idif,
-                                 int length, uint8_t *data)
-{
-    uint8_t cs = cscn >> 8;
-    uint8_t cn = cscn - 1;      /* -1 for the non-present master control */
-    uint32_t aid = ATTRIB_ID(cs, attrib, idif);
-    int ret = USB_RET_STALL;
-
-    switch (aid) {
-    case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200):
-        data[0] = s->out.mute;
-        ret = 1;
-        break;
-    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
-        if (cn < 2) {
-            uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
-            data[0] = vol;
-            data[1] = vol >> 8;
-            ret = 2;
-        }
-        break;
-    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200):
-        if (cn < 2) {
-            data[0] = 0x01;
-            data[1] = 0x80;
-            ret = 2;
-        }
-        break;
-    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200):
-        if (cn < 2) {
-            data[0] = 0x00;
-            data[1] = 0x08;
-            ret = 2;
-        }
-        break;
-    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200):
-        if (cn < 2) {
-            data[0] = 0x88;
-            data[1] = 0x00;
-            ret = 2;
-        }
-        break;
-    }
-
-    return ret;
-}
-static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
-                                 uint16_t cscn, uint16_t idif,
-                                 int length, uint8_t *data)
-{
-    uint8_t cs = cscn >> 8;
-    uint8_t cn = cscn - 1;      /* -1 for the non-present master control */
-    uint32_t aid = ATTRIB_ID(cs, attrib, idif);
-    int ret = USB_RET_STALL;
-    bool set_vol = false;
-
-    switch (aid) {
-    case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200):
-        s->out.mute = data[0] & 1;
-        set_vol = true;
-        ret = 0;
-        break;
-    case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200):
-        if (cn < 2) {
-            uint16_t vol = data[0] + (data[1] << 8);
-
-            if (s->debug) {
-                fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
-            }
-
-            vol -= 0x8000;
-            vol = (vol * 255 + 0x4400) / 0x8800;
-            if (vol > 255) {
-                vol = 255;
-            }
-
-            s->out.vol[cn] = vol;
-            set_vol = true;
-            ret = 0;
-        }
-        break;
-    }
-
-    if (set_vol) {
-        if (s->debug) {
-            fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
-                    s->out.mute, s->out.vol[0], s->out.vol[1]);
-        }
-        AUD_set_volume_out(s->out.voice, s->out.mute,
-                           s->out.vol[0], s->out.vol[1]);
-    }
-
-    return ret;
-}
-
-static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
-                                    int request, int value, int index,
-                                    int length, uint8_t *data)
-{
-    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
-    int ret = 0;
-
-    if (s->debug) {
-        fprintf(stderr, "usb-audio: control transaction: "
-                "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
-                request, value, index, length);
-    }
-
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    switch (request) {
-    case ClassInterfaceRequest | CR_GET_CUR:
-    case ClassInterfaceRequest | CR_GET_MIN:
-    case ClassInterfaceRequest | CR_GET_MAX:
-    case ClassInterfaceRequest | CR_GET_RES:
-        ret = usb_audio_get_control(s, request & 0xff, value, index,
-                                    length, data);
-        if (ret < 0) {
-            if (s->debug) {
-                fprintf(stderr, "usb-audio: fail: get control\n");
-            }
-            goto fail;
-        }
-        break;
-
-    case ClassInterfaceOutRequest | CR_SET_CUR:
-    case ClassInterfaceOutRequest | CR_SET_MIN:
-    case ClassInterfaceOutRequest | CR_SET_MAX:
-    case ClassInterfaceOutRequest | CR_SET_RES:
-        ret = usb_audio_set_control(s, request & 0xff, value, index,
-                                    length, data);
-        if (ret < 0) {
-            if (s->debug) {
-                fprintf(stderr, "usb-audio: fail: set control\n");
-            }
-            goto fail;
-        }
-        break;
-
-    default:
-fail:
-        if (s->debug) {
-            fprintf(stderr, "usb-audio: failed control transaction: "
-                    "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
-                    request, value, index, length);
-        }
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static void usb_audio_set_interface(USBDevice *dev, int iface,
-                                    int old, int value)
-{
-    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
-
-    if (iface == 1) {
-        usb_audio_set_output_altset(s, value);
-    }
-}
-
-static void usb_audio_handle_reset(USBDevice *dev)
-{
-    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
-
-    if (s->debug) {
-        fprintf(stderr, "usb-audio: reset\n");
-    }
-    usb_audio_set_output_altset(s, ALTSET_OFF);
-}
-
-static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
-{
-    int rc;
-
-    if (s->out.altset == ALTSET_OFF) {
-        return USB_RET_STALL;
-    }
-
-    rc = streambuf_put(&s->out.buf, p);
-    if (rc < p->iov.size && s->debug > 1) {
-        fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
-                p->iov.size - rc);
-    }
-
-    return 0;
-}
-
-static int usb_audio_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBAudioState *s = (USBAudioState *) dev;
-    int ret = 0;
-
-    switch (p->pid) {
-    case USB_TOKEN_OUT:
-        switch (p->ep->nr) {
-        case 1:
-            ret = usb_audio_handle_dataout(s, p);
-            break;
-        default:
-            goto fail;
-        }
-        break;
-
-    default:
-fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-    if (ret == USB_RET_STALL && s->debug) {
-        fprintf(stderr, "usb-audio: failed data transaction: "
-                        "pid 0x%x ep 0x%x len 0x%zx\n",
-                        p->pid, p->ep->nr, p->iov.size);
-    }
-    return ret;
-}
-
-static void usb_audio_handle_destroy(USBDevice *dev)
-{
-    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
-
-    if (s->debug) {
-        fprintf(stderr, "usb-audio: destroy\n");
-    }
-
-    usb_audio_set_output_altset(s, ALTSET_OFF);
-    AUD_close_out(&s->card, s->out.voice);
-    AUD_remove_card(&s->card);
-
-    streambuf_fini(&s->out.buf);
-}
-
-static int usb_audio_initfn(USBDevice *dev)
-{
-    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
-
-    usb_desc_init(dev);
-    s->dev.opaque = s;
-    AUD_register_card("usb-audio", &s->card);
-
-    s->out.altset        = ALTSET_OFF;
-    s->out.mute          = false;
-    s->out.vol[0]        = 240; /* 0 dB */
-    s->out.vol[1]        = 240; /* 0 dB */
-    s->out.as.freq       = USBAUDIO_SAMPLE_RATE;
-    s->out.as.nchannels  = 2;
-    s->out.as.fmt        = AUD_FMT_S16;
-    s->out.as.endianness = 0;
-    streambuf_init(&s->out.buf, s->buffer);
-
-    s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio",
-                                s, output_callback, &s->out.as);
-    AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
-    AUD_set_active_out(s->out.voice, 0);
-    return 0;
-}
-
-static const VMStateDescription vmstate_usb_audio = {
-    .name = "usb-audio",
-    .unmigratable = 1,
-};
-
-static Property usb_audio_properties[] = {
-    DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
-    DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
-                       8 * USBAUDIO_PACKET_SIZE),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usb_audio_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *k = USB_DEVICE_CLASS(klass);
-
-    dc->vmsd          = &vmstate_usb_audio;
-    dc->props         = usb_audio_properties;
-    k->product_desc   = "QEMU USB Audio Interface";
-    k->usb_desc       = &desc_audio;
-    k->init           = usb_audio_initfn;
-    k->handle_reset   = usb_audio_handle_reset;
-    k->handle_control = usb_audio_handle_control;
-    k->handle_data    = usb_audio_handle_data;
-    k->handle_destroy = usb_audio_handle_destroy;
-    k->set_interface  = usb_audio_set_interface;
-}
-
-static TypeInfo usb_audio_info = {
-    .name          = "usb-audio",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBAudioState),
-    .class_init    = usb_audio_class_init,
-};
-
-static void usb_audio_register_types(void)
-{
-    type_register_static(&usb_audio_info);
-    usb_legacy_register("usb-audio", "audio", NULL);
-}
-
-type_init(usb_audio_register_types)
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
deleted file mode 100644 (file)
index 23c39ec..0000000
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * QEMU Bluetooth HCI USB Transport Layer v1.0
- *
- * Copyright (C) 2007 OpenMoko, Inc.
- * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu-common.h"
-#include "usb.h"
-#include "usb-desc.h"
-#include "net.h"
-#include "bt.h"
-
-struct USBBtState {
-    USBDevice dev;
-    struct HCIInfo *hci;
-
-    int config;
-
-#define CFIFO_LEN_MASK 255
-#define DFIFO_LEN_MASK 4095
-    struct usb_hci_in_fifo_s {
-        uint8_t data[(DFIFO_LEN_MASK + 1) * 2];
-        struct {
-            uint8_t *data;
-            int len;
-        } fifo[CFIFO_LEN_MASK + 1];
-        int dstart, dlen, dsize, start, len;
-    } evt, acl, sco;
-
-    struct usb_hci_out_fifo_s {
-        uint8_t data[4096];
-       int len;
-    } outcmd, outacl, outsco;
-};
-
-#define USB_EVT_EP     1
-#define USB_ACL_EP     2
-#define USB_SCO_EP     3
-
-enum {
-    STR_MANUFACTURER = 1,
-    STR_SERIALNUMBER,
-};
-
-static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
-    [STR_SERIALNUMBER]     = "1",
-};
-
-static const USBDescIface desc_iface_bluetooth[] = {
-    {
-        .bInterfaceNumber              = 0,
-        .bNumEndpoints                 = 3,
-        .bInterfaceClass               = 0xe0, /* Wireless */
-        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
-        .bInterfaceProtocol            = 0x01, /* Bluetooth */
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_EVT_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_INT,
-                .wMaxPacketSize        = 0x10,
-                .bInterval             = 0x02,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_OUT | USB_ACL_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-                .wMaxPacketSize        = 0x40,
-                .bInterval             = 0x0a,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_ACL_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-                .wMaxPacketSize        = 0x40,
-                .bInterval             = 0x0a,
-            },
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 0,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = 0xe0, /* Wireless */
-        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
-        .bInterfaceProtocol            = 0x01, /* Bluetooth */
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0,
-                .bInterval             = 0x01,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0,
-                .bInterval             = 0x01,
-            },
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 1,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = 0xe0, /* Wireless */
-        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
-        .bInterfaceProtocol            = 0x01, /* Bluetooth */
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x09,
-                .bInterval             = 0x01,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x09,
-                .bInterval             = 0x01,
-            },
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 2,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = 0xe0, /* Wireless */
-        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
-        .bInterfaceProtocol            = 0x01, /* Bluetooth */
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x11,
-                .bInterval             = 0x01,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x11,
-                .bInterval             = 0x01,
-            },
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 3,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = 0xe0, /* Wireless */
-        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
-        .bInterfaceProtocol            = 0x01, /* Bluetooth */
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x19,
-                .bInterval             = 0x01,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x19,
-                .bInterval             = 0x01,
-            },
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 4,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = 0xe0, /* Wireless */
-        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
-        .bInterfaceProtocol            = 0x01, /* Bluetooth */
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x21,
-                .bInterval             = 0x01,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x21,
-                .bInterval             = 0x01,
-            },
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 5,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = 0xe0, /* Wireless */
-        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
-        .bInterfaceProtocol            = 0x01, /* Bluetooth */
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x31,
-                .bInterval             = 0x01,
-            },
-            {
-                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
-                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
-                .wMaxPacketSize        = 0x31,
-                .bInterval             = 0x01,
-            },
-        },
-    }
-};
-
-static const USBDescDevice desc_device_bluetooth = {
-    .bcdUSB                        = 0x0110,
-    .bDeviceClass                  = 0xe0, /* Wireless */
-    .bDeviceSubClass               = 0x01, /* Radio Frequency */
-    .bDeviceProtocol               = 0x01, /* Bluetooth */
-    .bMaxPacketSize0               = 64,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 2,
-            .bConfigurationValue   = 1,
-            .bmAttributes          = 0xc0,
-            .bMaxPower             = 0,
-            .nif = ARRAY_SIZE(desc_iface_bluetooth),
-            .ifs = desc_iface_bluetooth,
-        },
-    },
-};
-
-static const USBDesc desc_bluetooth = {
-    .id = {
-        .idVendor          = 0x0a12,
-        .idProduct         = 0x0001,
-        .bcdDevice         = 0x1958,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = 0,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device_bluetooth,
-    .str  = desc_strings,
-};
-
-static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo)
-{
-    fifo->dstart = 0;
-    fifo->dlen = 0;
-    fifo->dsize = DFIFO_LEN_MASK + 1;
-    fifo->start = 0;
-    fifo->len = 0;
-}
-
-static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo,
-                const uint8_t *data, int len)
-{
-    int off = fifo->dstart + fifo->dlen;
-    uint8_t *buf;
-
-    fifo->dlen += len;
-    if (off <= DFIFO_LEN_MASK) {
-        if (off + len > DFIFO_LEN_MASK + 1 &&
-                        (fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) {
-            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
-            exit(-1);
-        }
-        buf = fifo->data + off;
-    } else {
-        if (fifo->dlen > fifo->dsize) {
-            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
-            exit(-1);
-        }
-        buf = fifo->data + off - fifo->dsize;
-    }
-
-    off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK;
-    fifo->fifo[off].data = memcpy(buf, data, len);
-    fifo->fifo[off].len = len;
-}
-
-static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
-                USBPacket *p)
-{
-    int len;
-
-    if (likely(!fifo->len))
-        return USB_RET_STALL;
-
-    len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
-    usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
-    if (len == p->iov.size) {
-        fifo->fifo[fifo->start].len -= len;
-        fifo->fifo[fifo->start].data += len;
-    } else {
-        fifo->start ++;
-        fifo->start &= CFIFO_LEN_MASK;
-        fifo->len --;
-    }
-
-    fifo->dstart += len;
-    fifo->dlen -= len;
-    if (fifo->dstart >= fifo->dsize) {
-        fifo->dstart = 0;
-        fifo->dsize = DFIFO_LEN_MASK + 1;
-    }
-
-    return len;
-}
-
-static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
-                struct usb_hci_out_fifo_s *fifo,
-                void (*send)(struct HCIInfo *, const uint8_t *, int),
-                int (*complete)(const uint8_t *, int),
-                USBPacket *p)
-{
-    usb_packet_copy(p, fifo->data + fifo->len, p->iov.size);
-    fifo->len += p->iov.size;
-    if (complete(fifo->data, fifo->len)) {
-        send(s->hci, fifo->data, fifo->len);
-        fifo->len = 0;
-    }
-
-    /* TODO: do we need to loop? */
-}
-
-static int usb_bt_hci_cmd_complete(const uint8_t *data, int len)
-{
-    len -= HCI_COMMAND_HDR_SIZE;
-    return len >= 0 &&
-            len >= ((struct hci_command_hdr *) data)->plen;
-}
-
-static int usb_bt_hci_acl_complete(const uint8_t *data, int len)
-{
-    len -= HCI_ACL_HDR_SIZE;
-    return len >= 0 &&
-            len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen);
-}
-
-static int usb_bt_hci_sco_complete(const uint8_t *data, int len)
-{
-    len -= HCI_SCO_HDR_SIZE;
-    return len >= 0 &&
-            len >= ((struct hci_sco_hdr *) data)->dlen;
-}
-
-static void usb_bt_handle_reset(USBDevice *dev)
-{
-    struct USBBtState *s = (struct USBBtState *) dev->opaque;
-
-    usb_bt_fifo_reset(&s->evt);
-    usb_bt_fifo_reset(&s->acl);
-    usb_bt_fifo_reset(&s->sco);
-    s->outcmd.len = 0;
-    s->outacl.len = 0;
-    s->outsco.len = 0;
-}
-
-static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    struct USBBtState *s = (struct USBBtState *) dev->opaque;
-    int ret;
-
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        switch (request) {
-        case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-            s->config = 0;
-            break;
-        case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-            s->config = 1;
-            usb_bt_fifo_reset(&s->evt);
-            usb_bt_fifo_reset(&s->acl);
-            usb_bt_fifo_reset(&s->sco);
-            break;
-        }
-        return ret;
-    }
-
-    ret = 0;
-    switch (request) {
-    case InterfaceRequest | USB_REQ_GET_STATUS:
-    case EndpointRequest | USB_REQ_GET_STATUS:
-        data[0] = 0x00;
-        data[1] = 0x00;
-        ret = 2;
-        break;
-    case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
-    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
-        goto fail;
-    case InterfaceOutRequest | USB_REQ_SET_FEATURE:
-    case EndpointOutRequest | USB_REQ_SET_FEATURE:
-        goto fail;
-        break;
-    case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
-        if (s->config)
-            usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
-                            usb_bt_hci_cmd_complete, p);
-        break;
-    default:
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
-{
-    struct USBBtState *s = (struct USBBtState *) dev->opaque;
-    int ret = 0;
-
-    if (!s->config)
-        goto fail;
-
-    switch (p->pid) {
-    case USB_TOKEN_IN:
-        switch (p->ep->nr) {
-        case USB_EVT_EP:
-            ret = usb_bt_fifo_dequeue(&s->evt, p);
-            break;
-
-        case USB_ACL_EP:
-            ret = usb_bt_fifo_dequeue(&s->acl, p);
-            break;
-
-        case USB_SCO_EP:
-            ret = usb_bt_fifo_dequeue(&s->sco, p);
-            break;
-
-        default:
-            goto fail;
-        }
-        break;
-
-    case USB_TOKEN_OUT:
-        switch (p->ep->nr) {
-        case USB_ACL_EP:
-            usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
-                            usb_bt_hci_acl_complete, p);
-            break;
-
-        case USB_SCO_EP:
-            usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send,
-                            usb_bt_hci_sco_complete, p);
-            break;
-
-        default:
-            goto fail;
-        }
-        break;
-
-    default:
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-
-    return ret;
-}
-
-static void usb_bt_out_hci_packet_event(void *opaque,
-                const uint8_t *data, int len)
-{
-    struct USBBtState *s = (struct USBBtState *) opaque;
-
-    usb_bt_fifo_enqueue(&s->evt, data, len);
-}
-
-static void usb_bt_out_hci_packet_acl(void *opaque,
-                const uint8_t *data, int len)
-{
-    struct USBBtState *s = (struct USBBtState *) opaque;
-
-    usb_bt_fifo_enqueue(&s->acl, data, len);
-}
-
-static void usb_bt_handle_destroy(USBDevice *dev)
-{
-    struct USBBtState *s = (struct USBBtState *) dev->opaque;
-
-    s->hci->opaque = NULL;
-    s->hci->evt_recv = NULL;
-    s->hci->acl_recv = NULL;
-}
-
-static int usb_bt_initfn(USBDevice *dev)
-{
-    usb_desc_init(dev);
-    return 0;
-}
-
-USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci)
-{
-    USBDevice *dev;
-    struct USBBtState *s;
-
-    if (!hci)
-        return NULL;
-    dev = usb_create_simple(bus, "usb-bt-dongle");
-    if (!dev) {
-        return NULL;
-    }
-    s = DO_UPCAST(struct USBBtState, dev, dev);
-    s->dev.opaque = s;
-
-    s->hci = hci;
-    s->hci->opaque = s;
-    s->hci->evt_recv = usb_bt_out_hci_packet_event;
-    s->hci->acl_recv = usb_bt_out_hci_packet_acl;
-
-    usb_bt_handle_reset(&s->dev);
-
-    return dev;
-}
-
-static const VMStateDescription vmstate_usb_bt = {
-    .name = "usb-bt",
-    .unmigratable = 1,
-};
-
-static void usb_bt_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = usb_bt_initfn;
-    uc->product_desc   = "QEMU BT dongle";
-    uc->usb_desc       = &desc_bluetooth;
-    uc->handle_reset   = usb_bt_handle_reset;
-    uc->handle_control = usb_bt_handle_control;
-    uc->handle_data    = usb_bt_handle_data;
-    uc->handle_destroy = usb_bt_handle_destroy;
-    dc->vmsd = &vmstate_usb_bt;
-}
-
-static TypeInfo bt_info = {
-    .name          = "usb-bt-dongle",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(struct USBBtState),
-    .class_init    = usb_bt_class_initfn,
-};
-
-static void usb_bt_register_types(void)
-{
-    type_register_static(&bt_info);
-}
-
-type_init(usb_bt_register_types)
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
deleted file mode 100644 (file)
index 70b7ebc..0000000
+++ /dev/null
@@ -1,584 +0,0 @@
-#include "hw.h"
-#include "usb.h"
-#include "qdev.h"
-#include "sysemu.h"
-#include "monitor.h"
-#include "trace.h"
-
-static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
-
-static char *usb_get_dev_path(DeviceState *dev);
-static char *usb_get_fw_dev_path(DeviceState *qdev);
-static int usb_qdev_exit(DeviceState *qdev);
-
-static struct BusInfo usb_bus_info = {
-    .name      = "USB",
-    .size      = sizeof(USBBus),
-    .print_dev = usb_bus_dev_print,
-    .get_dev_path = usb_get_dev_path,
-    .get_fw_dev_path = usb_get_fw_dev_path,
-    .props      = (Property[]) {
-        DEFINE_PROP_STRING("port", USBDevice, port_path),
-        DEFINE_PROP_END_OF_LIST()
-    },
-};
-static int next_usb_bus = 0;
-static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
-
-const VMStateDescription vmstate_usb_device = {
-    .name = "USBDevice",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField []) {
-        VMSTATE_UINT8(addr, USBDevice),
-        VMSTATE_INT32(state, USBDevice),
-        VMSTATE_INT32(remote_wakeup, USBDevice),
-        VMSTATE_INT32(setup_state, USBDevice),
-        VMSTATE_INT32(setup_len, USBDevice),
-        VMSTATE_INT32(setup_index, USBDevice),
-        VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8),
-        VMSTATE_END_OF_LIST(),
-    }
-};
-
-void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host)
-{
-    qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
-    bus->ops = ops;
-    bus->busnr = next_usb_bus++;
-    bus->qbus.allow_hotplug = 1; /* Yes, we can */
-    QTAILQ_INIT(&bus->free);
-    QTAILQ_INIT(&bus->used);
-    QTAILQ_INSERT_TAIL(&busses, bus, next);
-}
-
-USBBus *usb_bus_find(int busnr)
-{
-    USBBus *bus;
-
-    if (-1 == busnr)
-        return QTAILQ_FIRST(&busses);
-    QTAILQ_FOREACH(bus, &busses, next) {
-        if (bus->busnr == busnr)
-            return bus;
-    }
-    return NULL;
-}
-
-static int usb_device_init(USBDevice *dev)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->init) {
-        return klass->init(dev);
-    }
-    return 0;
-}
-
-USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->find_device) {
-        return klass->find_device(dev, addr);
-    }
-    return NULL;
-}
-
-static void usb_device_handle_destroy(USBDevice *dev)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->handle_destroy) {
-        klass->handle_destroy(dev);
-    }
-}
-
-void usb_device_cancel_packet(USBDevice *dev, USBPacket *p)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->cancel_packet) {
-        klass->cancel_packet(dev, p);
-    }
-}
-
-void usb_device_handle_attach(USBDevice *dev)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->handle_attach) {
-        klass->handle_attach(dev);
-    }
-}
-
-void usb_device_handle_reset(USBDevice *dev)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->handle_reset) {
-        klass->handle_reset(dev);
-    }
-}
-
-int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
-                              int value, int index, int length, uint8_t *data)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->handle_control) {
-        return klass->handle_control(dev, p, request, value, index, length,
-                                         data);
-    }
-    return -ENOSYS;
-}
-
-int usb_device_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->handle_data) {
-        return klass->handle_data(dev, p);
-    }
-    return -ENOSYS;
-}
-
-const char *usb_device_get_product_desc(USBDevice *dev)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    return klass->product_desc;
-}
-
-const USBDesc *usb_device_get_usb_desc(USBDevice *dev)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    return klass->usb_desc;
-}
-
-void usb_device_set_interface(USBDevice *dev, int interface,
-                              int alt_old, int alt_new)
-{
-    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
-    if (klass->set_interface) {
-        klass->set_interface(dev, interface, alt_old, alt_new);
-    }
-}
-
-static int usb_qdev_init(DeviceState *qdev)
-{
-    USBDevice *dev = USB_DEVICE(qdev);
-    int rc;
-
-    pstrcpy(dev->product_desc, sizeof(dev->product_desc),
-            usb_device_get_product_desc(dev));
-    dev->auto_attach = 1;
-    QLIST_INIT(&dev->strings);
-    usb_ep_init(dev);
-    rc = usb_claim_port(dev);
-    if (rc != 0) {
-        return rc;
-    }
-    rc = usb_device_init(dev);
-    if (rc != 0) {
-        usb_release_port(dev);
-        return rc;
-    }
-    if (dev->auto_attach) {
-        rc = usb_device_attach(dev);
-        if (rc != 0) {
-            usb_qdev_exit(qdev);
-            return rc;
-        }
-    }
-    return 0;
-}
-
-static int usb_qdev_exit(DeviceState *qdev)
-{
-    USBDevice *dev = USB_DEVICE(qdev);
-
-    if (dev->attached) {
-        usb_device_detach(dev);
-    }
-    usb_device_handle_destroy(dev);
-    if (dev->port) {
-        usb_release_port(dev);
-    }
-    return 0;
-}
-
-typedef struct LegacyUSBFactory
-{
-    const char *name;
-    const char *usbdevice_name;
-    USBDevice *(*usbdevice_init)(USBBus *bus, const char *params);
-} LegacyUSBFactory;
-
-static GSList *legacy_usb_factory;
-
-void usb_legacy_register(const char *typename, const char *usbdevice_name,
-                         USBDevice *(*usbdevice_init)(USBBus *bus,
-                                                      const char *params))
-{
-    if (usbdevice_name) {
-        LegacyUSBFactory *f = g_malloc0(sizeof(*f));
-        f->name = typename;
-        f->usbdevice_name = usbdevice_name;
-        f->usbdevice_init = usbdevice_init;
-        legacy_usb_factory = g_slist_append(legacy_usb_factory, f);
-    }
-}
-
-USBDevice *usb_create(USBBus *bus, const char *name)
-{
-    DeviceState *dev;
-
-    dev = qdev_create(&bus->qbus, name);
-    return USB_DEVICE(dev);
-}
-
-USBDevice *usb_create_simple(USBBus *bus, const char *name)
-{
-    USBDevice *dev = usb_create(bus, name);
-    int rc;
-
-    if (!dev) {
-        error_report("Failed to create USB device '%s'", name);
-        return NULL;
-    }
-    rc = qdev_init(&dev->qdev);
-    if (rc < 0) {
-        error_report("Failed to initialize USB device '%s'", name);
-        return NULL;
-    }
-    return dev;
-}
-
-static void usb_fill_port(USBPort *port, void *opaque, int index,
-                          USBPortOps *ops, int speedmask)
-{
-    port->opaque = opaque;
-    port->index = index;
-    port->ops = ops;
-    port->speedmask = speedmask;
-    usb_port_location(port, NULL, index + 1);
-}
-
-void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
-                       USBPortOps *ops, int speedmask)
-{
-    usb_fill_port(port, opaque, index, ops, speedmask);
-    QTAILQ_INSERT_TAIL(&bus->free, port, next);
-    bus->nfree++;
-}
-
-int usb_register_companion(const char *masterbus, USBPort *ports[],
-                           uint32_t portcount, uint32_t firstport,
-                           void *opaque, USBPortOps *ops, int speedmask)
-{
-    USBBus *bus;
-    int i;
-
-    QTAILQ_FOREACH(bus, &busses, next) {
-        if (strcmp(bus->qbus.name, masterbus) == 0) {
-            break;
-        }
-    }
-
-    if (!bus || !bus->ops->register_companion) {
-        qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
-                      "an USB masterbus");
-        if (bus) {
-            error_printf_unless_qmp(
-                "USB bus '%s' does not allow companion controllers\n",
-                masterbus);
-        }
-        return -1;
-    }
-
-    for (i = 0; i < portcount; i++) {
-        usb_fill_port(ports[i], opaque, i, ops, speedmask);
-    }
-
-    return bus->ops->register_companion(bus, ports, portcount, firstport);
-}
-
-void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
-{
-    if (upstream) {
-        snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
-                 upstream->path, portnr);
-    } else {
-        snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
-    }
-}
-
-void usb_unregister_port(USBBus *bus, USBPort *port)
-{
-    if (port->dev)
-        qdev_free(&port->dev->qdev);
-    QTAILQ_REMOVE(&bus->free, port, next);
-    bus->nfree--;
-}
-
-int usb_claim_port(USBDevice *dev)
-{
-    USBBus *bus = usb_bus_from_device(dev);
-    USBPort *port;
-
-    assert(dev->port == NULL);
-
-    if (dev->port_path) {
-        QTAILQ_FOREACH(port, &bus->free, next) {
-            if (strcmp(port->path, dev->port_path) == 0) {
-                break;
-            }
-        }
-        if (port == NULL) {
-            error_report("Error: usb port %s (bus %s) not found (in use?)",
-                         dev->port_path, bus->qbus.name);
-            return -1;
-        }
-    } else {
-        if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) {
-            /* Create a new hub and chain it on */
-            usb_create_simple(bus, "usb-hub");
-        }
-        if (bus->nfree == 0) {
-            error_report("Error: tried to attach usb device %s to a bus "
-                         "with no free ports", dev->product_desc);
-            return -1;
-        }
-        port = QTAILQ_FIRST(&bus->free);
-    }
-    trace_usb_port_claim(bus->busnr, port->path);
-
-    QTAILQ_REMOVE(&bus->free, port, next);
-    bus->nfree--;
-
-    dev->port = port;
-    port->dev = dev;
-
-    QTAILQ_INSERT_TAIL(&bus->used, port, next);
-    bus->nused++;
-    return 0;
-}
-
-void usb_release_port(USBDevice *dev)
-{
-    USBBus *bus = usb_bus_from_device(dev);
-    USBPort *port = dev->port;
-
-    assert(port != NULL);
-    trace_usb_port_release(bus->busnr, port->path);
-
-    QTAILQ_REMOVE(&bus->used, port, next);
-    bus->nused--;
-
-    dev->port = NULL;
-    port->dev = NULL;
-
-    QTAILQ_INSERT_TAIL(&bus->free, port, next);
-    bus->nfree++;
-}
-
-int usb_device_attach(USBDevice *dev)
-{
-    USBBus *bus = usb_bus_from_device(dev);
-    USBPort *port = dev->port;
-
-    assert(port != NULL);
-    assert(!dev->attached);
-    trace_usb_port_attach(bus->busnr, port->path);
-
-    if (!(port->speedmask & dev->speedmask)) {
-        error_report("Warning: speed mismatch trying to attach "
-                     "usb device %s to bus %s",
-                     dev->product_desc, bus->qbus.name);
-        return -1;
-    }
-
-    dev->attached++;
-    usb_attach(port);
-
-    return 0;
-}
-
-int usb_device_detach(USBDevice *dev)
-{
-    USBBus *bus = usb_bus_from_device(dev);
-    USBPort *port = dev->port;
-
-    assert(port != NULL);
-    assert(dev->attached);
-    trace_usb_port_detach(bus->busnr, port->path);
-
-    usb_detach(port);
-    dev->attached--;
-    return 0;
-}
-
-int usb_device_delete_addr(int busnr, int addr)
-{
-    USBBus *bus;
-    USBPort *port;
-    USBDevice *dev;
-
-    bus = usb_bus_find(busnr);
-    if (!bus)
-        return -1;
-
-    QTAILQ_FOREACH(port, &bus->used, next) {
-        if (port->dev->addr == addr)
-            break;
-    }
-    if (!port)
-        return -1;
-    dev = port->dev;
-
-    qdev_free(&dev->qdev);
-    return 0;
-}
-
-static const char *usb_speed(unsigned int speed)
-{
-    static const char *txt[] = {
-        [ USB_SPEED_LOW  ] = "1.5",
-        [ USB_SPEED_FULL ] = "12",
-        [ USB_SPEED_HIGH ] = "480",
-        [ USB_SPEED_SUPER ] = "5000",
-    };
-    if (speed >= ARRAY_SIZE(txt))
-        return "?";
-    return txt[speed];
-}
-
-static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
-{
-    USBDevice *dev = USB_DEVICE(qdev);
-    USBBus *bus = usb_bus_from_device(dev);
-
-    monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
-                   indent, "", bus->busnr, dev->addr,
-                   dev->port ? dev->port->path : "-",
-                   usb_speed(dev->speed), dev->product_desc,
-                   dev->attached ? ", attached" : "");
-}
-
-static char *usb_get_dev_path(DeviceState *qdev)
-{
-    USBDevice *dev = USB_DEVICE(qdev);
-    return g_strdup(dev->port->path);
-}
-
-static char *usb_get_fw_dev_path(DeviceState *qdev)
-{
-    USBDevice *dev = USB_DEVICE(qdev);
-    char *fw_path, *in;
-    ssize_t pos = 0, fw_len;
-    long nr;
-
-    fw_len = 32 + strlen(dev->port->path) * 6;
-    fw_path = g_malloc(fw_len);
-    in = dev->port->path;
-    while (fw_len - pos > 0) {
-        nr = strtol(in, &in, 10);
-        if (in[0] == '.') {
-            /* some hub between root port and device */
-            pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
-            in++;
-        } else {
-            /* the device itself */
-            pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
-                            qdev_fw_name(qdev), nr);
-            break;
-        }
-    }
-    return fw_path;
-}
-
-void usb_info(Monitor *mon)
-{
-    USBBus *bus;
-    USBDevice *dev;
-    USBPort *port;
-
-    if (QTAILQ_EMPTY(&busses)) {
-        monitor_printf(mon, "USB support not enabled\n");
-        return;
-    }
-
-    QTAILQ_FOREACH(bus, &busses, next) {
-        QTAILQ_FOREACH(port, &bus->used, next) {
-            dev = port->dev;
-            if (!dev)
-                continue;
-            monitor_printf(mon, "  Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
-                           bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
-                           dev->product_desc);
-        }
-    }
-}
-
-/* handle legacy -usbdevice cmd line option */
-USBDevice *usbdevice_create(const char *cmdline)
-{
-    USBBus *bus = usb_bus_find(-1 /* any */);
-    LegacyUSBFactory *f = NULL;
-    GSList *i;
-    char driver[32];
-    const char *params;
-    int len;
-
-    params = strchr(cmdline,':');
-    if (params) {
-        params++;
-        len = params - cmdline;
-        if (len > sizeof(driver))
-            len = sizeof(driver);
-        pstrcpy(driver, len, cmdline);
-    } else {
-        params = "";
-        pstrcpy(driver, sizeof(driver), cmdline);
-    }
-
-    for (i = legacy_usb_factory; i; i = i->next) {
-        f = i->data;
-        if (strcmp(f->usbdevice_name, driver) == 0) {
-            break;
-        }
-    }
-    if (i == NULL) {
-#if 0
-        /* no error because some drivers are not converted (yet) */
-        error_report("usbdevice %s not found", driver);
-#endif
-        return NULL;
-    }
-
-    if (!f->usbdevice_init) {
-        if (*params) {
-            error_report("usbdevice %s accepts no params", driver);
-            return NULL;
-        }
-        return usb_create_simple(bus, f->name);
-    }
-    return f->usbdevice_init(bus, params);
-}
-
-static void usb_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->bus_info = &usb_bus_info;
-    k->init     = usb_qdev_init;
-    k->unplug   = qdev_simple_unplug_cb;
-    k->exit     = usb_qdev_exit;
-}
-
-static TypeInfo usb_device_type_info = {
-    .name = TYPE_USB_DEVICE,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(USBDevice),
-    .abstract = true,
-    .class_size = sizeof(USBDeviceClass),
-    .class_init = usb_device_class_init,
-};
-
-static void usb_register_types(void)
-{
-    type_register_static(&usb_device_type_info);
-}
-
-type_init(usb_register_types)
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
deleted file mode 100644 (file)
index ced687f..0000000
+++ /dev/null
@@ -1,1365 +0,0 @@
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * CCID Device emulation
- *
- * Written by Alon Levy, with contributions from Robert Relyea.
- *
- * Based on usb-serial.c, see its copyright and attributions below.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- * ------- (original copyright & attribution for usb-serial.c below) --------
- * Copyright (c) 2006 CodeSourcery.
- * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
- * Written by Paul Brook, reused for FTDI by Samuel Thibault,
- */
-
-/*
- * References:
- *
- * CCID Specification Revision 1.1 April 22nd 2005
- *  "Universal Serial Bus, Device Class: Smart Card"
- *  Specification for Integrated Circuit(s) Cards Interface Devices
- *
- * Endianness note: from the spec (1.3)
- *  "Fields that are larger than a byte are stored in little endian"
- *
- * KNOWN BUGS
- * 1. remove/insert can sometimes result in removed state instead of inserted.
- * This is a result of the following:
- *  symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This can happen
- *  when a short packet is sent, as seen in uhci-usb.c, resulting from a urb
- *  from the guest requesting SPD and us returning a smaller packet.
- *  Not sure which messages trigger this.
- */
-
-#include "qemu-common.h"
-#include "qemu-error.h"
-#include "usb.h"
-#include "usb-desc.h"
-#include "monitor.h"
-
-#include "hw/ccid.h"
-
-#define DPRINTF(s, lvl, fmt, ...) \
-do { \
-    if (lvl <= s->debug) { \
-        printf("usb-ccid: " fmt , ## __VA_ARGS__); \
-    } \
-} while (0)
-
-#define D_WARN 1
-#define D_INFO 2
-#define D_MORE_INFO 3
-#define D_VERBOSE 4
-
-#define CCID_DEV_NAME "usb-ccid"
-
-/*
- * The two options for variable sized buffers:
- * make them constant size, for large enough constant,
- * or handle the migration complexity - VMState doesn't handle this case.
- * sizes are expected never to be exceeded, unless guest misbehaves.
- */
-#define BULK_OUT_DATA_SIZE 65536
-#define PENDING_ANSWERS_NUM 128
-
-#define BULK_IN_BUF_SIZE 384
-#define BULK_IN_PENDING_NUM 8
-
-#define InterfaceOutClass \
-    ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8)
-
-#define InterfaceInClass  \
-    ((USB_DIR_IN  | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8)
-
-#define CCID_MAX_PACKET_SIZE                64
-
-#define CCID_CONTROL_ABORT                  0x1
-#define CCID_CONTROL_GET_CLOCK_FREQUENCIES  0x2
-#define CCID_CONTROL_GET_DATA_RATES         0x3
-
-#define CCID_PRODUCT_DESCRIPTION        "QEMU USB CCID"
-#define CCID_VENDOR_DESCRIPTION         "QEMU " QEMU_VERSION
-#define CCID_INTERFACE_NAME             "CCID Interface"
-#define CCID_SERIAL_NUMBER_STRING       "1"
-/*
- * Using Gemplus Vendor and Product id
- * Effect on various drivers:
- *  usbccid.sys (winxp, others untested) is a class driver so it doesn't care.
- *  linux has a number of class drivers, but openct filters based on
- *   vendor/product (/etc/openct.conf under fedora), hence Gemplus.
- */
-#define CCID_VENDOR_ID                  0x08e6
-#define CCID_PRODUCT_ID                 0x4433
-#define CCID_DEVICE_VERSION             0x0000
-
-/*
- * BULK_OUT messages from PC to Reader
- * Defined in CCID Rev 1.1 6.1 (page 26)
- */
-#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn              0x62
-#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff             0x63
-#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus           0x65
-#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock                0x6f
-#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters           0x6c
-#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters         0x6d
-#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters           0x61
-#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape                  0x6b
-#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock                0x6e
-#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU                  0x6a
-#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure                  0x69
-#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical              0x71
-#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort                   0x72
-#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73
-
-/*
- * BULK_IN messages from Reader to PC
- * Defined in CCID Rev 1.1 6.2 (page 48)
- */
-#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock               0x80
-#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus              0x81
-#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters              0x82
-#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape                  0x83
-#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84
-
-/*
- * INTERRUPT_IN messages from Reader to PC
- * Defined in CCID Rev 1.1 6.3 (page 56)
- */
-#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange        0x50
-#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError           0x51
-
-/*
- * Endpoints for CCID - addresses are up to us to decide.
- * To support slot insertion and removal we must have an interrupt in ep
- * in addition we need a bulk in and bulk out ep
- * 5.2, page 20
- */
-#define CCID_INT_IN_EP       1
-#define CCID_BULK_IN_EP      2
-#define CCID_BULK_OUT_EP     3
-
-/* bmSlotICCState masks */
-#define SLOT_0_STATE_MASK    1
-#define SLOT_0_CHANGED_MASK  2
-
-/* Status codes that go in bStatus (see 6.2.6) */
-enum {
-    ICC_STATUS_PRESENT_ACTIVE = 0,
-    ICC_STATUS_PRESENT_INACTIVE,
-    ICC_STATUS_NOT_PRESENT
-};
-
-enum {
-    COMMAND_STATUS_NO_ERROR = 0,
-    COMMAND_STATUS_FAILED,
-    COMMAND_STATUS_TIME_EXTENSION_REQUIRED
-};
-
-/* Error codes that go in bError (see 6.2.6) */
-enum {
-    ERROR_CMD_NOT_SUPPORTED = 0,
-    ERROR_CMD_ABORTED       = -1,
-    ERROR_ICC_MUTE          = -2,
-    ERROR_XFR_PARITY_ERROR  = -3,
-    ERROR_XFR_OVERRUN       = -4,
-    ERROR_HW_ERROR          = -5,
-};
-
-/* 6.2.6 RDR_to_PC_SlotStatus definitions */
-enum {
-    CLOCK_STATUS_RUNNING = 0,
-    /*
-     * 0 - Clock Running, 1 - Clock stopped in State L, 2 - H,
-     * 3 - unknown state. rest are RFU
-     */
-};
-
-typedef struct QEMU_PACKED CCID_Header {
-    uint8_t     bMessageType;
-    uint32_t    dwLength;
-    uint8_t     bSlot;
-    uint8_t     bSeq;
-} CCID_Header;
-
-typedef struct QEMU_PACKED CCID_BULK_IN {
-    CCID_Header hdr;
-    uint8_t     bStatus;        /* Only used in BULK_IN */
-    uint8_t     bError;         /* Only used in BULK_IN */
-} CCID_BULK_IN;
-
-typedef struct QEMU_PACKED CCID_SlotStatus {
-    CCID_BULK_IN b;
-    uint8_t     bClockStatus;
-} CCID_SlotStatus;
-
-typedef struct QEMU_PACKED CCID_Parameter {
-    CCID_BULK_IN b;
-    uint8_t     bProtocolNum;
-    uint8_t     abProtocolDataStructure[0];
-} CCID_Parameter;
-
-typedef struct QEMU_PACKED CCID_DataBlock {
-    CCID_BULK_IN b;
-    uint8_t      bChainParameter;
-    uint8_t      abData[0];
-} CCID_DataBlock;
-
-/* 6.1.4 PC_to_RDR_XfrBlock */
-typedef struct QEMU_PACKED CCID_XferBlock {
-    CCID_Header  hdr;
-    uint8_t      bBWI; /* Block Waiting Timeout */
-    uint16_t     wLevelParameter; /* XXX currently unused */
-    uint8_t      abData[0];
-} CCID_XferBlock;
-
-typedef struct QEMU_PACKED CCID_IccPowerOn {
-    CCID_Header hdr;
-    uint8_t     bPowerSelect;
-    uint16_t    abRFU;
-} CCID_IccPowerOn;
-
-typedef struct QEMU_PACKED CCID_IccPowerOff {
-    CCID_Header hdr;
-    uint16_t    abRFU;
-} CCID_IccPowerOff;
-
-typedef struct QEMU_PACKED CCID_SetParameters {
-    CCID_Header hdr;
-    uint8_t     bProtocolNum;
-    uint16_t   abRFU;
-    uint8_t    abProtocolDataStructure[0];
-} CCID_SetParameters;
-
-typedef struct CCID_Notify_Slot_Change {
-    uint8_t     bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */
-    uint8_t     bmSlotICCState;
-} CCID_Notify_Slot_Change;
-
-/* used for DataBlock response to XferBlock */
-typedef struct Answer {
-    uint8_t slot;
-    uint8_t seq;
-} Answer;
-
-/* pending BULK_IN messages */
-typedef struct BulkIn {
-    uint8_t  data[BULK_IN_BUF_SIZE];
-    uint32_t len;
-    uint32_t pos;
-} BulkIn;
-
-enum {
-    MIGRATION_NONE,
-    MIGRATION_MIGRATED,
-};
-
-typedef struct CCIDBus {
-    BusState qbus;
-} CCIDBus;
-
-#define MAX_PROTOCOL_SIZE   7
-
-/*
- * powered - defaults to true, changed by PowerOn/PowerOff messages
- */
-typedef struct USBCCIDState {
-    USBDevice dev;
-    USBEndpoint *intr;
-    CCIDBus bus;
-    CCIDCardState *card;
-    BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */
-    uint32_t bulk_in_pending_start;
-    uint32_t bulk_in_pending_end; /* first free */
-    uint32_t bulk_in_pending_num;
-    BulkIn *current_bulk_in;
-    uint8_t  bulk_out_data[BULK_OUT_DATA_SIZE];
-    uint32_t bulk_out_pos;
-    uint64_t last_answer_error;
-    Answer pending_answers[PENDING_ANSWERS_NUM];
-    uint32_t pending_answers_start;
-    uint32_t pending_answers_end;
-    uint32_t pending_answers_num;
-    uint8_t  bError;
-    uint8_t  bmCommandStatus;
-    uint8_t  bProtocolNum;
-    uint8_t  abProtocolDataStructure[MAX_PROTOCOL_SIZE];
-    uint32_t ulProtocolDataStructureSize;
-    uint32_t state_vmstate;
-    uint32_t migration_target_ip;
-    uint16_t migration_target_port;
-    uint8_t  migration_state;
-    uint8_t  bmSlotICCState;
-    uint8_t  powered;
-    uint8_t  notify_slot_change;
-    uint8_t  debug;
-} USBCCIDState;
-
-/*
- * CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9,
- * "USB Device Framework", section 9.6.1, in the Universal Serial Bus
- * Specification.
- *
- * This device implemented based on the spec and with an Athena Smart Card
- * Reader as reference:
- *   0dc3:1004 Athena Smartcard Solutions, Inc.
- */
-
-static const uint8_t qemu_ccid_descriptor[] = {
-        /* Smart Card Device Class Descriptor */
-        0x36,       /* u8  bLength; */
-        0x21,       /* u8  bDescriptorType; Functional */
-        0x10, 0x01, /* u16 bcdCCID; CCID Specification Release Number. */
-        0x00,       /*
-                     * u8  bMaxSlotIndex; The index of the highest available
-                     * slot on this device. All slots are consecutive starting
-                     * at 00h.
-                     */
-        0x07,       /* u8  bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */
-
-        0x03, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
-        0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
-                    /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */
-        0xa0, 0x0f, 0x00, 0x00,
-                    /* u32 dwMaximumClock; */
-        0x00, 0x00, 0x01, 0x00,
-        0x00,       /* u8 bNumClockSupported;                 *
-                     *    0 means just the default and max.   */
-                    /* u32 dwDataRate ;bps. 9600 == 00002580h */
-        0x80, 0x25, 0x00, 0x00,
-                    /* u32 dwMaxDataRate ; 11520 bps == 0001C200h */
-        0x00, 0xC2, 0x01, 0x00,
-        0x00,       /* u8  bNumDataRatesSupported; 00 means all rates between
-                     *     default and max */
-                    /* u32 dwMaxIFSD;                                  *
-                     *     maximum IFSD supported by CCID for protocol *
-                     *     T=1 (Maximum seen from various cards)       */
-        0xfe, 0x00, 0x00, 0x00,
-                    /* u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */
-        0x00, 0x00, 0x00, 0x00,
-                    /* u32 dwMechanical;  0 - no special characteristics. */
-        0x00, 0x00, 0x00, 0x00,
-                    /*
-                     * u32 dwFeatures;
-                     * 0 - No special characteristics
-                     * + 2 Automatic parameter configuration based on ATR data
-                     * + 4 Automatic activation of ICC on inserting
-                     * + 8 Automatic ICC voltage selection
-                     * + 10 Automatic ICC clock frequency change
-                     * + 20 Automatic baud rate change
-                     * + 40 Automatic parameters negotiation made by the CCID
-                     * + 80 automatic PPS made by the CCID
-                     * 100 CCID can set ICC in clock stop mode
-                     * 200 NAD value other then 00 accepted (T=1 protocol)
-                     * + 400 Automatic IFSD exchange as first exchange (T=1)
-                     * One of the following only:
-                     * + 10000 TPDU level exchanges with CCID
-                     * 20000 Short APDU level exchange with CCID
-                     * 40000 Short and Extended APDU level exchange with CCID
-                     *
-                     * + 100000 USB Wake up signaling supported on card
-                     * insertion and removal. Must set bit 5 in bmAttributes
-                     * in Configuration descriptor if 100000 is set.
-                     */
-        0xfe, 0x04, 0x11, 0x00,
-                    /*
-                     * u32 dwMaxCCIDMessageLength; For extended APDU in
-                     * [261 + 10 , 65544 + 10]. Otherwise the minimum is
-                     * wMaxPacketSize of the Bulk-OUT endpoint
-                     */
-        0x12, 0x00, 0x01, 0x00,
-        0xFF,       /*
-                     * u8  bClassGetResponse; Significant only for CCID that
-                     * offers an APDU level for exchanges. Indicates the
-                     * default class value used by the CCID when it sends a
-                     * Get Response command to perform the transportation of
-                     * an APDU by T=0 protocol
-                     * FFh indicates that the CCID echos the class of the APDU.
-                     */
-        0xFF,       /*
-                     * u8  bClassEnvelope; EAPDU only. Envelope command for
-                     * T=0
-                     */
-        0x00, 0x00, /*
-                     * u16 wLcdLayout; XXYY Number of lines (XX) and chars per
-                     * line for LCD display used for PIN entry. 0000 - no LCD
-                     */
-        0x01,       /*
-                     * u8  bPINSupport; 01h PIN Verification,
-                     *                  02h PIN Modification
-                     */
-        0x01,       /* u8  bMaxCCIDBusySlots; */
-};
-
-enum {
-    STR_MANUFACTURER = 1,
-    STR_PRODUCT,
-    STR_SERIALNUMBER,
-    STR_INTERFACE,
-};
-
-static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]  = "QEMU " QEMU_VERSION,
-    [STR_PRODUCT]       = "QEMU USB CCID",
-    [STR_SERIALNUMBER]  = "1",
-    [STR_INTERFACE]     = "CCID Interface",
-};
-
-static const USBDescIface desc_iface0 = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 3,
-    .bInterfaceClass               = 0x0b,
-    .bInterfaceSubClass            = 0x00,
-    .bInterfaceProtocol            = 0x00,
-    .iInterface                    = STR_INTERFACE,
-    .ndesc                         = 1,
-    .descs = (USBDescOther[]) {
-        {
-            /* smartcard descriptor */
-            .data = qemu_ccid_descriptor,
-        },
-    },
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | CCID_INT_IN_EP,
-            .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .bInterval             = 255,
-            .wMaxPacketSize        = 64,
-        },{
-            .bEndpointAddress      = USB_DIR_IN | CCID_BULK_IN_EP,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 64,
-        },{
-            .bEndpointAddress      = USB_DIR_OUT | CCID_BULK_OUT_EP,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 64,
-        },
-    }
-};
-
-static const USBDescDevice desc_device = {
-    .bcdUSB                        = 0x0110,
-    .bMaxPacketSize0               = 64,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .bmAttributes          = 0xe0,
-            .bMaxPower             = 50,
-            .nif = 1,
-            .ifs = &desc_iface0,
-        },
-    },
-};
-
-static const USBDesc desc_ccid = {
-    .id = {
-        .idVendor          = CCID_VENDOR_ID,
-        .idProduct         = CCID_PRODUCT_ID,
-        .bcdDevice         = CCID_DEVICE_VERSION,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device,
-    .str  = desc_strings,
-};
-
-static const uint8_t *ccid_card_get_atr(CCIDCardState *card, uint32_t *len)
-{
-    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
-    if (cc->get_atr) {
-        return cc->get_atr(card, len);
-    }
-    return NULL;
-}
-
-static void ccid_card_apdu_from_guest(CCIDCardState *card,
-                                      const uint8_t *apdu,
-                                      uint32_t len)
-{
-    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
-    if (cc->apdu_from_guest) {
-        cc->apdu_from_guest(card, apdu, len);
-    }
-}
-
-static int ccid_card_exitfn(CCIDCardState *card)
-{
-    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
-    if (cc->exitfn) {
-        return cc->exitfn(card);
-    }
-    return 0;
-}
-
-static int ccid_card_initfn(CCIDCardState *card)
-{
-    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
-    if (cc->initfn) {
-        return cc->initfn(card);
-    }
-    return 0;
-}
-
-static bool ccid_has_pending_answers(USBCCIDState *s)
-{
-    return s->pending_answers_num > 0;
-}
-
-static void ccid_clear_pending_answers(USBCCIDState *s)
-{
-    s->pending_answers_num = 0;
-    s->pending_answers_start = 0;
-    s->pending_answers_end = 0;
-}
-
-static void ccid_print_pending_answers(USBCCIDState *s)
-{
-    Answer *answer;
-    int i, count;
-
-    DPRINTF(s, D_VERBOSE, "usb-ccid: pending answers:");
-    if (!ccid_has_pending_answers(s)) {
-        DPRINTF(s, D_VERBOSE, " empty\n");
-        return;
-    }
-    for (i = s->pending_answers_start, count = s->pending_answers_num ;
-         count > 0; count--, i++) {
-        answer = &s->pending_answers[i % PENDING_ANSWERS_NUM];
-        if (count == 1) {
-            DPRINTF(s, D_VERBOSE, "%d:%d\n", answer->slot, answer->seq);
-        } else {
-            DPRINTF(s, D_VERBOSE, "%d:%d,", answer->slot, answer->seq);
-        }
-    }
-}
-
-static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr)
-{
-    Answer *answer;
-
-    assert(s->pending_answers_num < PENDING_ANSWERS_NUM);
-    s->pending_answers_num++;
-    answer =
-        &s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM];
-    answer->slot = hdr->bSlot;
-    answer->seq = hdr->bSeq;
-    ccid_print_pending_answers(s);
-}
-
-static void ccid_remove_pending_answer(USBCCIDState *s,
-    uint8_t *slot, uint8_t *seq)
-{
-    Answer *answer;
-
-    assert(s->pending_answers_num > 0);
-    s->pending_answers_num--;
-    answer =
-        &s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM];
-    *slot = answer->slot;
-    *seq = answer->seq;
-    ccid_print_pending_answers(s);
-}
-
-static void ccid_bulk_in_clear(USBCCIDState *s)
-{
-    s->bulk_in_pending_start = 0;
-    s->bulk_in_pending_end = 0;
-    s->bulk_in_pending_num = 0;
-}
-
-static void ccid_bulk_in_release(USBCCIDState *s)
-{
-    assert(s->current_bulk_in != NULL);
-    s->current_bulk_in->pos = 0;
-    s->current_bulk_in = NULL;
-}
-
-static void ccid_bulk_in_get(USBCCIDState *s)
-{
-    if (s->current_bulk_in != NULL || s->bulk_in_pending_num == 0) {
-        return;
-    }
-    assert(s->bulk_in_pending_num > 0);
-    s->bulk_in_pending_num--;
-    s->current_bulk_in =
-        &s->bulk_in_pending[(s->bulk_in_pending_start++) % BULK_IN_PENDING_NUM];
-}
-
-static void *ccid_reserve_recv_buf(USBCCIDState *s, uint16_t len)
-{
-    BulkIn *bulk_in;
-
-    DPRINTF(s, D_VERBOSE, "%s: QUEUE: reserve %d bytes\n", __func__, len);
-
-    /* look for an existing element */
-    if (len > BULK_IN_BUF_SIZE) {
-        DPRINTF(s, D_WARN, "usb-ccid.c: %s: len larger then max (%d>%d). "
-                           "discarding message.\n",
-                           __func__, len, BULK_IN_BUF_SIZE);
-        return NULL;
-    }
-    if (s->bulk_in_pending_num >= BULK_IN_PENDING_NUM) {
-        DPRINTF(s, D_WARN, "usb-ccid.c: %s: No free bulk_in buffers. "
-                           "discarding message.\n", __func__);
-        return NULL;
-    }
-    bulk_in =
-        &s->bulk_in_pending[(s->bulk_in_pending_end++) % BULK_IN_PENDING_NUM];
-    s->bulk_in_pending_num++;
-    bulk_in->len = len;
-    return bulk_in->data;
-}
-
-static void ccid_reset(USBCCIDState *s)
-{
-    ccid_bulk_in_clear(s);
-    ccid_clear_pending_answers(s);
-}
-
-static void ccid_detach(USBCCIDState *s)
-{
-    ccid_reset(s);
-}
-
-static void ccid_handle_reset(USBDevice *dev)
-{
-    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
-
-    DPRINTF(s, 1, "Reset\n");
-
-    ccid_reset(s);
-}
-
-static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
-                               int value, int index, int length, uint8_t *data)
-{
-    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
-    int ret = 0;
-
-    DPRINTF(s, 1, "got control %x, value %x\n", request, value);
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    switch (request) {
-        /* Class specific requests.  */
-    case InterfaceOutClass | CCID_CONTROL_ABORT:
-        DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
-        ret = USB_RET_STALL;
-        break;
-    case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
-        DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n");
-        ret = USB_RET_STALL;
-        break;
-    case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES:
-        DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n");
-        ret = USB_RET_STALL;
-        break;
-    default:
-        DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",
-                request, value);
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static bool ccid_card_inserted(USBCCIDState *s)
-{
-    return s->bmSlotICCState & SLOT_0_STATE_MASK;
-}
-
-static uint8_t ccid_card_status(USBCCIDState *s)
-{
-    return ccid_card_inserted(s)
-            ? (s->powered ?
-                ICC_STATUS_PRESENT_ACTIVE
-              : ICC_STATUS_PRESENT_INACTIVE
-              )
-            : ICC_STATUS_NOT_PRESENT;
-}
-
-static uint8_t ccid_calc_status(USBCCIDState *s)
-{
-    /*
-     * page 55, 6.2.6, calculation of bStatus from bmICCStatus and
-     * bmCommandStatus
-     */
-    uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6);
-    DPRINTF(s, D_VERBOSE, "status = %d\n", ret);
-    return ret;
-}
-
-static void ccid_reset_error_status(USBCCIDState *s)
-{
-    s->bError = ERROR_CMD_NOT_SUPPORTED;
-    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
-}
-
-static void ccid_write_slot_status(USBCCIDState *s, CCID_Header *recv)
-{
-    CCID_SlotStatus *h = ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus));
-    if (h == NULL) {
-        return;
-    }
-    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus;
-    h->b.hdr.dwLength = 0;
-    h->b.hdr.bSlot = recv->bSlot;
-    h->b.hdr.bSeq = recv->bSeq;
-    h->b.bStatus = ccid_calc_status(s);
-    h->b.bError = s->bError;
-    h->bClockStatus = CLOCK_STATUS_RUNNING;
-    ccid_reset_error_status(s);
-}
-
-static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv)
-{
-    CCID_Parameter *h;
-    uint32_t len = s->ulProtocolDataStructureSize;
-
-    h = ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len);
-    if (h == NULL) {
-        return;
-    }
-    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters;
-    h->b.hdr.dwLength = 0;
-    h->b.hdr.bSlot = recv->bSlot;
-    h->b.hdr.bSeq = recv->bSeq;
-    h->b.bStatus = ccid_calc_status(s);
-    h->b.bError = s->bError;
-    h->bProtocolNum = s->bProtocolNum;
-    memcpy(h->abProtocolDataStructure, s->abProtocolDataStructure, len);
-    ccid_reset_error_status(s);
-}
-
-static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq,
-                                  const uint8_t *data, uint32_t len)
-{
-    CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len);
-
-    if (p == NULL) {
-        return;
-    }
-    p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock;
-    p->b.hdr.dwLength = cpu_to_le32(len);
-    p->b.hdr.bSlot = slot;
-    p->b.hdr.bSeq = seq;
-    p->b.bStatus = ccid_calc_status(s);
-    p->b.bError = s->bError;
-    if (p->b.bError) {
-        DPRINTF(s, D_VERBOSE, "error %d", p->b.bError);
-    }
-    memcpy(p->abData, data, len);
-    ccid_reset_error_status(s);
-}
-
-static void ccid_write_data_block_answer(USBCCIDState *s,
-    const uint8_t *data, uint32_t len)
-{
-    uint8_t seq;
-    uint8_t slot;
-
-    if (!ccid_has_pending_answers(s)) {
-        abort();
-    }
-    ccid_remove_pending_answer(s, &slot, &seq);
-    ccid_write_data_block(s, slot, seq, data, len);
-}
-
-static void ccid_write_data_block_atr(USBCCIDState *s, CCID_Header *recv)
-{
-    const uint8_t *atr = NULL;
-    uint32_t len = 0;
-
-    if (s->card) {
-        atr = ccid_card_get_atr(s->card, &len);
-    }
-    ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len);
-}
-
-static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv)
-{
-    CCID_SetParameters *ph = (CCID_SetParameters *) recv;
-    uint32_t len = 0;
-    if ((ph->bProtocolNum & 3) == 0) {
-        len = 5;
-    }
-    if ((ph->bProtocolNum & 3) == 1) {
-        len = 7;
-    }
-    if (len == 0) {
-        s->bmCommandStatus = COMMAND_STATUS_FAILED;
-        s->bError = 7; /* Protocol invalid or not supported */
-        return;
-    }
-    s->bProtocolNum = ph->bProtocolNum;
-    memcpy(s->abProtocolDataStructure, ph->abProtocolDataStructure, len);
-    s->ulProtocolDataStructureSize = len;
-    DPRINTF(s, 1, "%s: using len %d\n", __func__, len);
-}
-
-/*
- * must be 5 bytes for T=0, 7 bytes for T=1
- * See page 52
- */
-static const uint8_t abDefaultProtocolDataStructure[7] = {
-    0x77, 0x00, 0x00, 0x00, 0x00, 0xfe /*IFSC*/, 0x00 /*NAD*/ };
-
-static void ccid_reset_parameters(USBCCIDState *s)
-{
-   uint32_t len = sizeof(abDefaultProtocolDataStructure);
-
-   s->bProtocolNum = 1; /* T=1 */
-   s->ulProtocolDataStructureSize = len;
-   memcpy(s->abProtocolDataStructure, abDefaultProtocolDataStructure, len);
-}
-
-static void ccid_report_error_failed(USBCCIDState *s, uint8_t error)
-{
-    s->bmCommandStatus = COMMAND_STATUS_FAILED;
-    s->bError = error;
-}
-
-/* NOTE: only a single slot is supported (SLOT_0) */
-static void ccid_on_slot_change(USBCCIDState *s, bool full)
-{
-    /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56 */
-    uint8_t current = s->bmSlotICCState;
-    if (full) {
-        s->bmSlotICCState |= SLOT_0_STATE_MASK;
-    } else {
-        s->bmSlotICCState &= ~SLOT_0_STATE_MASK;
-    }
-    if (current != s->bmSlotICCState) {
-        s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
-    }
-    s->notify_slot_change = true;
-    usb_wakeup(s->intr);
-}
-
-static void ccid_write_data_block_error(
-    USBCCIDState *s, uint8_t slot, uint8_t seq)
-{
-    ccid_write_data_block(s, slot, seq, NULL, 0);
-}
-
-static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
-{
-    uint32_t len;
-
-    if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) {
-        DPRINTF(s, 1,
-                "usb-ccid: not sending apdu to client, no card connected\n");
-        ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq);
-        return;
-    }
-    len = le32_to_cpu(recv->hdr.dwLength);
-    DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__,
-                recv->hdr.bSeq, len);
-    ccid_add_pending_answer(s, (CCID_Header *)recv);
-    if (s->card) {
-        ccid_card_apdu_from_guest(s->card, recv->abData, len);
-    } else {
-        DPRINTF(s, D_WARN, "warning: discarded apdu\n");
-    }
-}
-
-/*
- * Handle a single USB_TOKEN_OUT, return value returned to guest.
- * Return value:
- *  0             - all ok
- *  USB_RET_STALL - failed to handle packet
- */
-static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
-{
-    CCID_Header *ccid_header;
-
-    if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
-        return USB_RET_STALL;
-    }
-    ccid_header = (CCID_Header *)s->bulk_out_data;
-    usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
-    s->bulk_out_pos += p->iov.size;
-    if (p->iov.size == CCID_MAX_PACKET_SIZE) {
-        DPRINTF(s, D_VERBOSE,
-            "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
-            p->iov.size, ccid_header->dwLength);
-        return 0;
-    }
-    if (s->bulk_out_pos < 10) {
-        DPRINTF(s, 1,
-                "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n",
-                __func__);
-    } else {
-        DPRINTF(s, D_MORE_INFO, "%s %x\n", __func__, ccid_header->bMessageType);
-        switch (ccid_header->bMessageType) {
-        case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
-            ccid_write_slot_status(s, ccid_header);
-            break;
-        case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
-            DPRINTF(s, 1, "PowerOn: %d\n",
-                ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect);
-            s->powered = true;
-            if (!ccid_card_inserted(s)) {
-                ccid_report_error_failed(s, ERROR_ICC_MUTE);
-            }
-            /* atr is written regardless of error. */
-            ccid_write_data_block_atr(s, ccid_header);
-            break;
-        case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
-            DPRINTF(s, 1, "PowerOff\n");
-            ccid_reset_error_status(s);
-            s->powered = false;
-            ccid_write_slot_status(s, ccid_header);
-            break;
-        case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
-            ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data);
-            break;
-        case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
-            ccid_reset_error_status(s);
-            ccid_set_parameters(s, ccid_header);
-            ccid_write_parameters(s, ccid_header);
-            break;
-        case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
-            ccid_reset_error_status(s);
-            ccid_reset_parameters(s);
-            ccid_write_parameters(s, ccid_header);
-            break;
-        case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
-            ccid_reset_error_status(s);
-            ccid_write_parameters(s, ccid_header);
-            break;
-        default:
-            DPRINTF(s, 1,
-                "handle_data: ERROR: unhandled message type %Xh\n",
-                ccid_header->bMessageType);
-            /*
-             * The caller is expecting the device to respond, tell it we
-             * don't support the operation.
-             */
-            ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
-            ccid_write_slot_status(s, ccid_header);
-            break;
-        }
-    }
-    s->bulk_out_pos = 0;
-    return 0;
-}
-
-static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
-{
-    int ret = 0;
-
-    assert(p->iov.size > 0);
-    ccid_bulk_in_get(s);
-    if (s->current_bulk_in != NULL) {
-        ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
-                  p->iov.size);
-        usb_packet_copy(p, s->current_bulk_in->data +
-                        s->current_bulk_in->pos, ret);
-        s->current_bulk_in->pos += ret;
-        if (s->current_bulk_in->pos == s->current_bulk_in->len) {
-            ccid_bulk_in_release(s);
-        }
-    } else {
-        /* return when device has no data - usb 2.0 spec Table 8-4 */
-        ret = USB_RET_NAK;
-    }
-    if (ret > 0) {
-        DPRINTF(s, D_MORE_INFO,
-                "%s: %zd/%d req/act to guest (BULK_IN)\n",
-                __func__, p->iov.size, ret);
-    }
-    if (ret != USB_RET_NAK && ret < p->iov.size) {
-        DPRINTF(s, 1,
-                "%s: returning short (EREMOTEIO) %d < %zd\n",
-                __func__, ret, p->iov.size);
-    }
-    return ret;
-}
-
-static int ccid_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
-    int ret = 0;
-    uint8_t buf[2];
-
-    switch (p->pid) {
-    case USB_TOKEN_OUT:
-        ret = ccid_handle_bulk_out(s, p);
-        break;
-
-    case USB_TOKEN_IN:
-        switch (p->ep->nr) {
-        case CCID_BULK_IN_EP:
-            if (!p->iov.size) {
-                ret = USB_RET_NAK;
-            } else {
-                ret = ccid_bulk_in_copy_to_guest(s, p);
-            }
-            break;
-        case CCID_INT_IN_EP:
-            if (s->notify_slot_change) {
-                /* page 56, RDR_to_PC_NotifySlotChange */
-                buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
-                buf[1] = s->bmSlotICCState;
-                usb_packet_copy(p, buf, 2);
-                ret = 2;
-                s->notify_slot_change = false;
-                s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
-                DPRINTF(s, D_INFO,
-                        "handle_data: int_in: notify_slot_change %X, "
-                        "requested len %zd\n",
-                        s->bmSlotICCState, p->iov.size);
-            }
-            break;
-        default:
-            DPRINTF(s, 1, "Bad endpoint\n");
-            ret = USB_RET_STALL;
-            break;
-        }
-        break;
-    default:
-        DPRINTF(s, 1, "Bad token\n");
-        ret = USB_RET_STALL;
-        break;
-    }
-
-    return ret;
-}
-
-static void ccid_handle_destroy(USBDevice *dev)
-{
-    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
-
-    ccid_bulk_in_clear(s);
-}
-
-static void ccid_flush_pending_answers(USBCCIDState *s)
-{
-    while (ccid_has_pending_answers(s)) {
-        ccid_write_data_block_answer(s, NULL, 0);
-    }
-}
-
-static Answer *ccid_peek_next_answer(USBCCIDState *s)
-{
-    return s->pending_answers_num == 0
-        ? NULL
-        : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM];
-}
-
-static struct BusInfo ccid_bus_info = {
-    .name = "ccid-bus",
-    .size = sizeof(CCIDBus),
-    .props = (Property[]) {
-        DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0),
-        DEFINE_PROP_END_OF_LIST(),
-    }
-};
-
-void ccid_card_send_apdu_to_guest(CCIDCardState *card,
-                                  uint8_t *apdu, uint32_t len)
-{
-    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev,
-                                card->qdev.parent_bus->parent);
-    Answer *answer;
-
-    if (!ccid_has_pending_answers(s)) {
-        DPRINTF(s, 1, "CCID ERROR: got an APDU without pending answers\n");
-        return;
-    }
-    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
-    answer = ccid_peek_next_answer(s);
-    if (answer == NULL) {
-        abort();
-    }
-    DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n",
-        len, answer->seq, answer->slot);
-    ccid_write_data_block_answer(s, apdu, len);
-}
-
-void ccid_card_card_removed(CCIDCardState *card)
-{
-    USBCCIDState *s =
-        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
-
-    ccid_on_slot_change(s, false);
-    ccid_flush_pending_answers(s);
-    ccid_reset(s);
-}
-
-int ccid_card_ccid_attach(CCIDCardState *card)
-{
-    USBCCIDState *s =
-        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
-
-    DPRINTF(s, 1, "CCID Attach\n");
-    if (s->migration_state == MIGRATION_MIGRATED) {
-        s->migration_state = MIGRATION_NONE;
-    }
-    return 0;
-}
-
-void ccid_card_ccid_detach(CCIDCardState *card)
-{
-    USBCCIDState *s =
-        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
-
-    DPRINTF(s, 1, "CCID Detach\n");
-    if (ccid_card_inserted(s)) {
-        ccid_on_slot_change(s, false);
-    }
-    ccid_detach(s);
-}
-
-void ccid_card_card_error(CCIDCardState *card, uint64_t error)
-{
-    USBCCIDState *s =
-        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
-
-    s->bmCommandStatus = COMMAND_STATUS_FAILED;
-    s->last_answer_error = error;
-    DPRINTF(s, 1, "VSC_Error: %" PRIX64 "\n", s->last_answer_error);
-    /* TODO: these errors should be more verbose and propagated to the guest.*/
-    /*
-     * We flush all pending answers on CardRemove message in ccid-card-passthru,
-     * so check that first to not trigger abort
-     */
-    if (ccid_has_pending_answers(s)) {
-        ccid_write_data_block_answer(s, NULL, 0);
-    }
-}
-
-void ccid_card_card_inserted(CCIDCardState *card)
-{
-    USBCCIDState *s =
-        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
-
-    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
-    ccid_flush_pending_answers(s);
-    ccid_on_slot_change(s, true);
-}
-
-static int ccid_card_exit(DeviceState *qdev)
-{
-    int ret = 0;
-    CCIDCardState *card = CCID_CARD(qdev);
-    USBCCIDState *s =
-        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
-
-    if (ccid_card_inserted(s)) {
-        ccid_card_card_removed(card);
-    }
-    ret = ccid_card_exitfn(card);
-    s->card = NULL;
-    return ret;
-}
-
-static int ccid_card_init(DeviceState *qdev)
-{
-    CCIDCardState *card = CCID_CARD(qdev);
-    USBCCIDState *s =
-        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
-    int ret = 0;
-
-    if (card->slot != 0) {
-        error_report("Warning: usb-ccid supports one slot, can't add %d",
-                card->slot);
-        return -1;
-    }
-    if (s->card != NULL) {
-        error_report("Warning: usb-ccid card already full, not adding");
-        return -1;
-    }
-    ret = ccid_card_initfn(card);
-    if (ret == 0) {
-        s->card = card;
-    }
-    return ret;
-}
-
-static int ccid_initfn(USBDevice *dev)
-{
-    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
-
-    usb_desc_init(dev);
-    qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL);
-    s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP);
-    s->bus.qbus.allow_hotplug = 1;
-    s->card = NULL;
-    s->migration_state = MIGRATION_NONE;
-    s->migration_target_ip = 0;
-    s->migration_target_port = 0;
-    s->dev.speed = USB_SPEED_FULL;
-    s->dev.speedmask = USB_SPEED_MASK_FULL;
-    s->notify_slot_change = false;
-    s->powered = true;
-    s->pending_answers_num = 0;
-    s->last_answer_error = 0;
-    s->bulk_in_pending_start = 0;
-    s->bulk_in_pending_end = 0;
-    s->current_bulk_in = NULL;
-    ccid_reset_error_status(s);
-    s->bulk_out_pos = 0;
-    ccid_reset_parameters(s);
-    ccid_reset(s);
-    return 0;
-}
-
-static int ccid_post_load(void *opaque, int version_id)
-{
-    USBCCIDState *s = opaque;
-
-    /*
-     * This must be done after usb_device_attach, which sets state to ATTACHED,
-     * while it must be DEFAULT in order to accept packets (like it is after
-     * reset, but reset will reset our addr and call our reset handler which
-     * may change state, and we don't want to do that when migrating).
-     */
-    s->dev.state = s->state_vmstate;
-    return 0;
-}
-
-static void ccid_pre_save(void *opaque)
-{
-    USBCCIDState *s = opaque;
-
-    s->state_vmstate = s->dev.state;
-    if (s->dev.attached) {
-        /*
-         * Migrating an open device, ignore reconnection CHR_EVENT to avoid an
-         * erroneous detach.
-         */
-        s->migration_state = MIGRATION_MIGRATED;
-    }
-}
-
-static VMStateDescription bulk_in_vmstate = {
-    .name = "CCID BulkIn state",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_BUFFER(data, BulkIn),
-        VMSTATE_UINT32(len, BulkIn),
-        VMSTATE_UINT32(pos, BulkIn),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static VMStateDescription answer_vmstate = {
-    .name = "CCID Answer state",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(slot, Answer),
-        VMSTATE_UINT8(seq, Answer),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static VMStateDescription usb_device_vmstate = {
-    .name = "usb_device",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(addr, USBDevice),
-        VMSTATE_BUFFER(setup_buf, USBDevice),
-        VMSTATE_BUFFER(data_buf, USBDevice),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static VMStateDescription ccid_vmstate = {
-    .name = CCID_DEV_NAME,
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .post_load = ccid_post_load,
-    .pre_save = ccid_pre_save,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice),
-        VMSTATE_UINT8(debug, USBCCIDState),
-        VMSTATE_BUFFER(bulk_out_data, USBCCIDState),
-        VMSTATE_UINT32(bulk_out_pos, USBCCIDState),
-        VMSTATE_UINT8(bmSlotICCState, USBCCIDState),
-        VMSTATE_UINT8(powered, USBCCIDState),
-        VMSTATE_UINT8(notify_slot_change, USBCCIDState),
-        VMSTATE_UINT64(last_answer_error, USBCCIDState),
-        VMSTATE_UINT8(bError, USBCCIDState),
-        VMSTATE_UINT8(bmCommandStatus, USBCCIDState),
-        VMSTATE_UINT8(bProtocolNum, USBCCIDState),
-        VMSTATE_BUFFER(abProtocolDataStructure, USBCCIDState),
-        VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState),
-        VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState,
-                       BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn),
-        VMSTATE_UINT32(bulk_in_pending_start, USBCCIDState),
-        VMSTATE_UINT32(bulk_in_pending_end, USBCCIDState),
-        VMSTATE_STRUCT_ARRAY(pending_answers, USBCCIDState,
-                        PENDING_ANSWERS_NUM, 1, answer_vmstate, Answer),
-        VMSTATE_UINT32(pending_answers_num, USBCCIDState),
-        VMSTATE_UINT8(migration_state, USBCCIDState),
-        VMSTATE_UINT32(state_vmstate, USBCCIDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property ccid_properties[] = {
-    DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void ccid_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = ccid_initfn;
-    uc->product_desc   = "QEMU USB CCID";
-    uc->usb_desc       = &desc_ccid;
-    uc->handle_reset   = ccid_handle_reset;
-    uc->handle_control = ccid_handle_control;
-    uc->handle_data    = ccid_handle_data;
-    uc->handle_destroy = ccid_handle_destroy;
-    dc->desc = "CCID Rev 1.1 smartcard reader";
-    dc->vmsd = &ccid_vmstate;
-    dc->props = ccid_properties;
-}
-
-static TypeInfo ccid_info = {
-    .name          = CCID_DEV_NAME,
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBCCIDState),
-    .class_init    = ccid_class_initfn,
-};
-
-static void ccid_card_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->bus_info = &ccid_bus_info;
-    k->init = ccid_card_init;
-    k->exit = ccid_card_exit;
-}
-
-static TypeInfo ccid_card_type_info = {
-    .name = TYPE_CCID_CARD,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(CCIDCardState),
-    .abstract = true,
-    .class_size = sizeof(CCIDCardClass),
-    .class_init = ccid_card_class_init,
-};
-
-static void ccid_register_types(void)
-{
-    type_register_static(&ccid_card_type_info);
-    type_register_static(&ccid_info);
-    usb_legacy_register(CCID_DEV_NAME, "ccid", NULL);
-}
-
-type_init(ccid_register_types)
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
deleted file mode 100644 (file)
index ccf85ad..0000000
+++ /dev/null
@@ -1,601 +0,0 @@
-#include "usb.h"
-#include "usb-desc.h"
-#include "trace.h"
-
-/* ------------------------------------------------------------------ */
-
-static uint8_t usb_lo(uint16_t val)
-{
-    return val & 0xff;
-}
-
-static uint8_t usb_hi(uint16_t val)
-{
-    return (val >> 8) & 0xff;
-}
-
-int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
-                    uint8_t *dest, size_t len)
-{
-    uint8_t bLength = 0x12;
-
-    if (len < bLength) {
-        return -1;
-    }
-
-    dest[0x00] = bLength;
-    dest[0x01] = USB_DT_DEVICE;
-
-    dest[0x02] = usb_lo(dev->bcdUSB);
-    dest[0x03] = usb_hi(dev->bcdUSB);
-    dest[0x04] = dev->bDeviceClass;
-    dest[0x05] = dev->bDeviceSubClass;
-    dest[0x06] = dev->bDeviceProtocol;
-    dest[0x07] = dev->bMaxPacketSize0;
-
-    dest[0x08] = usb_lo(id->idVendor);
-    dest[0x09] = usb_hi(id->idVendor);
-    dest[0x0a] = usb_lo(id->idProduct);
-    dest[0x0b] = usb_hi(id->idProduct);
-    dest[0x0c] = usb_lo(id->bcdDevice);
-    dest[0x0d] = usb_hi(id->bcdDevice);
-    dest[0x0e] = id->iManufacturer;
-    dest[0x0f] = id->iProduct;
-    dest[0x10] = id->iSerialNumber;
-
-    dest[0x11] = dev->bNumConfigurations;
-
-    return bLength;
-}
-
-int usb_desc_device_qualifier(const USBDescDevice *dev,
-                              uint8_t *dest, size_t len)
-{
-    uint8_t bLength = 0x0a;
-
-    if (len < bLength) {
-        return -1;
-    }
-
-    dest[0x00] = bLength;
-    dest[0x01] = USB_DT_DEVICE_QUALIFIER;
-
-    dest[0x02] = usb_lo(dev->bcdUSB);
-    dest[0x03] = usb_hi(dev->bcdUSB);
-    dest[0x04] = dev->bDeviceClass;
-    dest[0x05] = dev->bDeviceSubClass;
-    dest[0x06] = dev->bDeviceProtocol;
-    dest[0x07] = dev->bMaxPacketSize0;
-    dest[0x08] = dev->bNumConfigurations;
-    dest[0x09] = 0; /* reserved */
-
-    return bLength;
-}
-
-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
-{
-    uint8_t  bLength = 0x09;
-    uint16_t wTotalLength = 0;
-    int i, rc;
-
-    if (len < bLength) {
-        return -1;
-    }
-
-    dest[0x00] = bLength;
-    dest[0x01] = USB_DT_CONFIG;
-    dest[0x04] = conf->bNumInterfaces;
-    dest[0x05] = conf->bConfigurationValue;
-    dest[0x06] = conf->iConfiguration;
-    dest[0x07] = conf->bmAttributes;
-    dest[0x08] = conf->bMaxPower;
-    wTotalLength += bLength;
-
-    /* handle grouped interfaces if any*/
-    for (i = 0; i < conf->nif_groups; i++) {
-        rc = usb_desc_iface_group(&(conf->if_groups[i]),
-                                  dest + wTotalLength,
-                                  len - wTotalLength);
-        if (rc < 0) {
-            return rc;
-        }
-        wTotalLength += rc;
-    }
-
-    /* handle normal (ungrouped / no IAD) interfaces if any */
-    for (i = 0; i < conf->nif; i++) {
-        rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
-        if (rc < 0) {
-            return rc;
-        }
-        wTotalLength += rc;
-    }
-
-    dest[0x02] = usb_lo(wTotalLength);
-    dest[0x03] = usb_hi(wTotalLength);
-    return wTotalLength;
-}
-
-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
-                         size_t len)
-{
-    int pos = 0;
-    int i = 0;
-
-    /* handle interface association descriptor */
-    uint8_t bLength = 0x08;
-
-    if (len < bLength) {
-        return -1;
-    }
-
-    dest[0x00] = bLength;
-    dest[0x01] = USB_DT_INTERFACE_ASSOC;
-    dest[0x02] = iad->bFirstInterface;
-    dest[0x03] = iad->bInterfaceCount;
-    dest[0x04] = iad->bFunctionClass;
-    dest[0x05] = iad->bFunctionSubClass;
-    dest[0x06] = iad->bFunctionProtocol;
-    dest[0x07] = iad->iFunction;
-    pos += bLength;
-
-    /* handle associated interfaces in this group */
-    for (i = 0; i < iad->nif; i++) {
-        int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
-        if (rc < 0) {
-            return rc;
-        }
-        pos += rc;
-    }
-
-    return pos;
-}
-
-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
-{
-    uint8_t bLength = 0x09;
-    int i, rc, pos = 0;
-
-    if (len < bLength) {
-        return -1;
-    }
-
-    dest[0x00] = bLength;
-    dest[0x01] = USB_DT_INTERFACE;
-    dest[0x02] = iface->bInterfaceNumber;
-    dest[0x03] = iface->bAlternateSetting;
-    dest[0x04] = iface->bNumEndpoints;
-    dest[0x05] = iface->bInterfaceClass;
-    dest[0x06] = iface->bInterfaceSubClass;
-    dest[0x07] = iface->bInterfaceProtocol;
-    dest[0x08] = iface->iInterface;
-    pos += bLength;
-
-    for (i = 0; i < iface->ndesc; i++) {
-        rc = usb_desc_other(iface->descs + i, dest + pos, len - pos);
-        if (rc < 0) {
-            return rc;
-        }
-        pos += rc;
-    }
-
-    for (i = 0; i < iface->bNumEndpoints; i++) {
-        rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
-        if (rc < 0) {
-            return rc;
-        }
-        pos += rc;
-    }
-
-    return pos;
-}
-
-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
-{
-    uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
-    uint8_t extralen = ep->extra ? ep->extra[0] : 0;
-
-    if (len < bLength + extralen) {
-        return -1;
-    }
-
-    dest[0x00] = bLength;
-    dest[0x01] = USB_DT_ENDPOINT;
-    dest[0x02] = ep->bEndpointAddress;
-    dest[0x03] = ep->bmAttributes;
-    dest[0x04] = usb_lo(ep->wMaxPacketSize);
-    dest[0x05] = usb_hi(ep->wMaxPacketSize);
-    dest[0x06] = ep->bInterval;
-    if (ep->is_audio) {
-        dest[0x07] = ep->bRefresh;
-        dest[0x08] = ep->bSynchAddress;
-    }
-    if (ep->extra) {
-        memcpy(dest + bLength, ep->extra, extralen);
-    }
-
-    return bLength + extralen;
-}
-
-int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
-{
-    int bLength = desc->length ? desc->length : desc->data[0];
-
-    if (len < bLength) {
-        return -1;
-    }
-
-    memcpy(dest, desc->data, bLength);
-    return bLength;
-}
-
-/* ------------------------------------------------------------------ */
-
-static void usb_desc_ep_init(USBDevice *dev)
-{
-    const USBDescIface *iface;
-    int i, e, pid, ep;
-
-    usb_ep_init(dev);
-    for (i = 0; i < dev->ninterfaces; i++) {
-        iface = dev->ifaces[i];
-        if (iface == NULL) {
-            continue;
-        }
-        for (e = 0; e < iface->bNumEndpoints; e++) {
-            pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
-                USB_TOKEN_IN : USB_TOKEN_OUT;
-            ep = iface->eps[e].bEndpointAddress & 0x0f;
-            usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
-            usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
-            usb_ep_set_max_packet_size(dev, pid, ep,
-                                       iface->eps[e].wMaxPacketSize);
-        }
-    }
-}
-
-static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
-                                                   int nif, int alt)
-{
-    const USBDescIface *iface;
-    int g, i;
-
-    if (!dev->config) {
-        return NULL;
-    }
-    for (g = 0; g < dev->config->nif_groups; g++) {
-        for (i = 0; i < dev->config->if_groups[g].nif; i++) {
-            iface = &dev->config->if_groups[g].ifs[i];
-            if (iface->bInterfaceNumber == nif &&
-                iface->bAlternateSetting == alt) {
-                return iface;
-            }
-        }
-    }
-    for (i = 0; i < dev->config->nif; i++) {
-        iface = &dev->config->ifs[i];
-        if (iface->bInterfaceNumber == nif &&
-            iface->bAlternateSetting == alt) {
-            return iface;
-        }
-    }
-    return NULL;
-}
-
-static int usb_desc_set_interface(USBDevice *dev, int index, int value)
-{
-    const USBDescIface *iface;
-    int old;
-
-    iface = usb_desc_find_interface(dev, index, value);
-    if (iface == NULL) {
-        return -1;
-    }
-
-    old = dev->altsetting[index];
-    dev->altsetting[index] = value;
-    dev->ifaces[index] = iface;
-    usb_desc_ep_init(dev);
-
-    if (old != value) {
-        usb_device_set_interface(dev, index, old, value);
-    }
-    return 0;
-}
-
-static int usb_desc_set_config(USBDevice *dev, int value)
-{
-    int i;
-
-    if (value == 0) {
-        dev->configuration = 0;
-        dev->ninterfaces   = 0;
-        dev->config = NULL;
-    } else {
-        for (i = 0; i < dev->device->bNumConfigurations; i++) {
-            if (dev->device->confs[i].bConfigurationValue == value) {
-                dev->configuration = value;
-                dev->ninterfaces   = dev->device->confs[i].bNumInterfaces;
-                dev->config = dev->device->confs + i;
-                assert(dev->ninterfaces <= USB_MAX_INTERFACES);
-            }
-        }
-        if (i < dev->device->bNumConfigurations) {
-            return -1;
-        }
-    }
-
-    for (i = 0; i < dev->ninterfaces; i++) {
-        usb_desc_set_interface(dev, i, 0);
-    }
-    for (; i < USB_MAX_INTERFACES; i++) {
-        dev->altsetting[i] = 0;
-        dev->ifaces[i] = NULL;
-    }
-
-    return 0;
-}
-
-static void usb_desc_setdefaults(USBDevice *dev)
-{
-    const USBDesc *desc = usb_device_get_usb_desc(dev);
-
-    assert(desc != NULL);
-    switch (dev->speed) {
-    case USB_SPEED_LOW:
-    case USB_SPEED_FULL:
-        dev->device = desc->full;
-        break;
-    case USB_SPEED_HIGH:
-        dev->device = desc->high;
-        break;
-    }
-    usb_desc_set_config(dev, 0);
-}
-
-void usb_desc_init(USBDevice *dev)
-{
-    const USBDesc *desc = usb_device_get_usb_desc(dev);
-
-    assert(desc != NULL);
-    dev->speed = USB_SPEED_FULL;
-    dev->speedmask = 0;
-    if (desc->full) {
-        dev->speedmask |= USB_SPEED_MASK_FULL;
-    }
-    if (desc->high) {
-        dev->speedmask |= USB_SPEED_MASK_HIGH;
-    }
-    usb_desc_setdefaults(dev);
-}
-
-void usb_desc_attach(USBDevice *dev)
-{
-    const USBDesc *desc = usb_device_get_usb_desc(dev);
-
-    assert(desc != NULL);
-    if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
-        dev->speed = USB_SPEED_HIGH;
-    } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
-        dev->speed = USB_SPEED_FULL;
-    } else {
-        fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n",
-                usb_device_get_product_desc(dev));
-        return;
-    }
-    usb_desc_setdefaults(dev);
-}
-
-void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str)
-{
-    USBDescString *s;
-
-    QLIST_FOREACH(s, &dev->strings, next) {
-        if (s->index == index) {
-            break;
-        }
-    }
-    if (s == NULL) {
-        s = g_malloc0(sizeof(*s));
-        s->index = index;
-        QLIST_INSERT_HEAD(&dev->strings, s, next);
-    }
-    g_free(s->str);
-    s->str = g_strdup(str);
-}
-
-const char *usb_desc_get_string(USBDevice *dev, uint8_t index)
-{
-    USBDescString *s;
-
-    QLIST_FOREACH(s, &dev->strings, next) {
-        if (s->index == index) {
-            return s->str;
-        }
-    }
-    return NULL;
-}
-
-int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
-{
-    uint8_t bLength, pos, i;
-    const char *str;
-
-    if (len < 4) {
-        return -1;
-    }
-
-    if (index == 0) {
-        /* language ids */
-        dest[0] = 4;
-        dest[1] = USB_DT_STRING;
-        dest[2] = 0x09;
-        dest[3] = 0x04;
-        return 4;
-    }
-
-    str = usb_desc_get_string(dev, index);
-    if (str == NULL) {
-        str = usb_device_get_usb_desc(dev)->str[index];
-        if (str == NULL) {
-            return 0;
-        }
-    }
-
-    bLength = strlen(str) * 2 + 2;
-    dest[0] = bLength;
-    dest[1] = USB_DT_STRING;
-    i = 0; pos = 2;
-    while (pos+1 < bLength && pos+1 < len) {
-        dest[pos++] = str[i++];
-        dest[pos++] = 0;
-    }
-    return pos;
-}
-
-int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len)
-{
-    const USBDesc *desc = usb_device_get_usb_desc(dev);
-    const USBDescDevice *other_dev;
-    uint8_t buf[256];
-    uint8_t type = value >> 8;
-    uint8_t index = value & 0xff;
-    int ret = -1;
-
-    if (dev->speed == USB_SPEED_HIGH) {
-        other_dev = usb_device_get_usb_desc(dev)->full;
-    } else {
-        other_dev = usb_device_get_usb_desc(dev)->high;
-    }
-
-    switch(type) {
-    case USB_DT_DEVICE:
-        ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
-        trace_usb_desc_device(dev->addr, len, ret);
-        break;
-    case USB_DT_CONFIG:
-        if (index < dev->device->bNumConfigurations) {
-            ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
-        }
-        trace_usb_desc_config(dev->addr, index, len, ret);
-        break;
-    case USB_DT_STRING:
-        ret = usb_desc_string(dev, index, buf, sizeof(buf));
-        trace_usb_desc_string(dev->addr, index, len, ret);
-        break;
-
-    case USB_DT_DEVICE_QUALIFIER:
-        if (other_dev != NULL) {
-            ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
-        }
-        trace_usb_desc_device_qualifier(dev->addr, len, ret);
-        break;
-    case USB_DT_OTHER_SPEED_CONFIG:
-        if (other_dev != NULL && index < other_dev->bNumConfigurations) {
-            ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
-            buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
-        }
-        trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
-        break;
-
-    case USB_DT_DEBUG:
-        /* ignore silently */
-        break;
-
-    default:
-        fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
-                dev->addr, type, len);
-        break;
-    }
-
-    if (ret > 0) {
-        if (ret > len) {
-            ret = len;
-        }
-        memcpy(dest, buf, ret);
-    }
-    return ret;
-}
-
-int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
-        int request, int value, int index, int length, uint8_t *data)
-{
-    const USBDesc *desc = usb_device_get_usb_desc(dev);
-    int ret = -1;
-
-    assert(desc != NULL);
-    switch(request) {
-    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-        dev->addr = value;
-        trace_usb_set_addr(dev->addr);
-        ret = 0;
-        break;
-
-    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
-        ret = usb_desc_get_descriptor(dev, value, data, length);
-        break;
-
-    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-        /*
-         * 9.4.2: 0 should be returned if the device is unconfigured, otherwise
-         * the non zero value of bConfigurationValue.
-         */
-        data[0] = dev->config ? dev->config->bConfigurationValue : 0;
-        ret = 1;
-        break;
-    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        ret = usb_desc_set_config(dev, value);
-        trace_usb_set_config(dev->addr, value, ret);
-        break;
-
-    case DeviceRequest | USB_REQ_GET_STATUS: {
-        const USBDescConfig *config = dev->config ?
-            dev->config : &dev->device->confs[0];
-
-        data[0] = 0;
-        /*
-         * Default state: Device behavior when this request is received while
-         *                the device is in the Default state is not specified.
-         * We return the same value that a configured device would return if
-         * it used the first configuration.
-         */
-        if (config->bmAttributes & 0x40) {
-            data[0] |= 1 << USB_DEVICE_SELF_POWERED;
-        }
-        if (dev->remote_wakeup) {
-            data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
-        }
-        data[1] = 0x00;
-        ret = 2;
-        break;
-    }
-    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
-        if (value == USB_DEVICE_REMOTE_WAKEUP) {
-            dev->remote_wakeup = 0;
-            ret = 0;
-        }
-        trace_usb_clear_device_feature(dev->addr, value, ret);
-        break;
-    case DeviceOutRequest | USB_REQ_SET_FEATURE:
-        if (value == USB_DEVICE_REMOTE_WAKEUP) {
-            dev->remote_wakeup = 1;
-            ret = 0;
-        }
-        trace_usb_set_device_feature(dev->addr, value, ret);
-        break;
-
-    case InterfaceRequest | USB_REQ_GET_INTERFACE:
-        if (index < 0 || index >= dev->ninterfaces) {
-            break;
-        }
-        data[0] = dev->altsetting[index];
-        ret = 1;
-        break;
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = usb_desc_set_interface(dev, index, value);
-        trace_usb_set_interface(dev->addr, index, value, ret);
-        break;
-
-    }
-    return ret;
-}
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
deleted file mode 100644 (file)
index d6e07ea..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-#ifndef QEMU_HW_USB_DESC_H
-#define QEMU_HW_USB_DESC_H
-
-#include <inttypes.h>
-
-struct USBDescID {
-    uint16_t                  idVendor;
-    uint16_t                  idProduct;
-    uint16_t                  bcdDevice;
-    uint8_t                   iManufacturer;
-    uint8_t                   iProduct;
-    uint8_t                   iSerialNumber;
-};
-
-struct USBDescDevice {
-    uint16_t                  bcdUSB;
-    uint8_t                   bDeviceClass;
-    uint8_t                   bDeviceSubClass;
-    uint8_t                   bDeviceProtocol;
-    uint8_t                   bMaxPacketSize0;
-    uint8_t                   bNumConfigurations;
-
-    const USBDescConfig       *confs;
-};
-
-struct USBDescConfig {
-    uint8_t                   bNumInterfaces;
-    uint8_t                   bConfigurationValue;
-    uint8_t                   iConfiguration;
-    uint8_t                   bmAttributes;
-    uint8_t                   bMaxPower;
-
-    /* grouped interfaces */
-    uint8_t                   nif_groups;
-    const USBDescIfaceAssoc   *if_groups;
-
-    /* "normal" interfaces */
-    uint8_t                   nif;
-    const USBDescIface        *ifs;
-};
-
-/* conceptually an Interface Association Descriptor, and releated interfaces */
-struct USBDescIfaceAssoc {
-    uint8_t                   bFirstInterface;
-    uint8_t                   bInterfaceCount;
-    uint8_t                   bFunctionClass;
-    uint8_t                   bFunctionSubClass;
-    uint8_t                   bFunctionProtocol;
-    uint8_t                   iFunction;
-
-    uint8_t                   nif;
-    const USBDescIface        *ifs;
-};
-
-struct USBDescIface {
-    uint8_t                   bInterfaceNumber;
-    uint8_t                   bAlternateSetting;
-    uint8_t                   bNumEndpoints;
-    uint8_t                   bInterfaceClass;
-    uint8_t                   bInterfaceSubClass;
-    uint8_t                   bInterfaceProtocol;
-    uint8_t                   iInterface;
-
-    uint8_t                   ndesc;
-    USBDescOther              *descs;
-    USBDescEndpoint           *eps;
-};
-
-struct USBDescEndpoint {
-    uint8_t                   bEndpointAddress;
-    uint8_t                   bmAttributes;
-    uint16_t                  wMaxPacketSize;
-    uint8_t                   bInterval;
-    uint8_t                   bRefresh;
-    uint8_t                   bSynchAddress;
-
-    uint8_t                   is_audio; /* has bRefresh + bSynchAddress */
-    uint8_t                   *extra;
-};
-
-struct USBDescOther {
-    uint8_t                   length;
-    const uint8_t             *data;
-};
-
-typedef const char *USBDescStrings[256];
-
-struct USBDesc {
-    USBDescID                 id;
-    const USBDescDevice       *full;
-    const USBDescDevice       *high;
-    const char* const         *str;
-};
-
-/* generate usb packages from structs */
-int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
-                    uint8_t *dest, size_t len);
-int usb_desc_device_qualifier(const USBDescDevice *dev,
-                              uint8_t *dest, size_t len);
-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
-                         size_t len);
-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
-int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
-
-/* control message emulation helpers */
-void usb_desc_init(USBDevice *dev);
-void usb_desc_attach(USBDevice *dev);
-void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
-const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
-int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
-int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
-int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
-        int request, int value, int index, int length, uint8_t *data);
-
-#endif /* QEMU_HW_USB_DESC_H */
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
deleted file mode 100644 (file)
index df742f7..0000000
+++ /dev/null
@@ -1,2345 +0,0 @@
-/*
- * QEMU USB EHCI Emulation
- *
- * Copyright(c) 2008  Emutex Ltd. (address@hidden)
- *
- * EHCI project was started by Mark Burkley, with contributions by
- * Niels de Vos.  David S. Ahern continued working on it.  Kevin Wolf,
- * Jan Kiszka and Vincent Palatin contributed bugfixes.
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or(at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw.h"
-#include "qemu-timer.h"
-#include "usb.h"
-#include "pci.h"
-#include "monitor.h"
-#include "trace.h"
-#include "dma.h"
-
-#define EHCI_DEBUG   0
-
-#if EHCI_DEBUG
-#define DPRINTF printf
-#else
-#define DPRINTF(...)
-#endif
-
-/* internal processing - reset HC to try and recover */
-#define USB_RET_PROCERR   (-99)
-
-#define MMIO_SIZE        0x1000
-
-/* Capability Registers Base Address - section 2.2 */
-#define CAPREGBASE       0x0000
-#define CAPLENGTH        CAPREGBASE + 0x0000  // 1-byte, 0x0001 reserved
-#define HCIVERSION       CAPREGBASE + 0x0002  // 2-bytes, i/f version #
-#define HCSPARAMS        CAPREGBASE + 0x0004  // 4-bytes, structural params
-#define HCCPARAMS        CAPREGBASE + 0x0008  // 4-bytes, capability params
-#define EECP             HCCPARAMS + 1
-#define HCSPPORTROUTE1   CAPREGBASE + 0x000c
-#define HCSPPORTROUTE2   CAPREGBASE + 0x0010
-
-#define OPREGBASE        0x0020        // Operational Registers Base Address
-
-#define USBCMD           OPREGBASE + 0x0000
-#define USBCMD_RUNSTOP   (1 << 0)      // run / Stop
-#define USBCMD_HCRESET   (1 << 1)      // HC Reset
-#define USBCMD_FLS       (3 << 2)      // Frame List Size
-#define USBCMD_FLS_SH    2             // Frame List Size Shift
-#define USBCMD_PSE       (1 << 4)      // Periodic Schedule Enable
-#define USBCMD_ASE       (1 << 5)      // Asynch Schedule Enable
-#define USBCMD_IAAD      (1 << 6)      // Int Asynch Advance Doorbell
-#define USBCMD_LHCR      (1 << 7)      // Light Host Controller Reset
-#define USBCMD_ASPMC     (3 << 8)      // Async Sched Park Mode Count
-#define USBCMD_ASPME     (1 << 11)     // Async Sched Park Mode Enable
-#define USBCMD_ITC       (0x7f << 16)  // Int Threshold Control
-#define USBCMD_ITC_SH    16            // Int Threshold Control Shift
-
-#define USBSTS           OPREGBASE + 0x0004
-#define USBSTS_RO_MASK   0x0000003f
-#define USBSTS_INT       (1 << 0)      // USB Interrupt
-#define USBSTS_ERRINT    (1 << 1)      // Error Interrupt
-#define USBSTS_PCD       (1 << 2)      // Port Change Detect
-#define USBSTS_FLR       (1 << 3)      // Frame List Rollover
-#define USBSTS_HSE       (1 << 4)      // Host System Error
-#define USBSTS_IAA       (1 << 5)      // Interrupt on Async Advance
-#define USBSTS_HALT      (1 << 12)     // HC Halted
-#define USBSTS_REC       (1 << 13)     // Reclamation
-#define USBSTS_PSS       (1 << 14)     // Periodic Schedule Status
-#define USBSTS_ASS       (1 << 15)     // Asynchronous Schedule Status
-
-/*
- *  Interrupt enable bits correspond to the interrupt active bits in USBSTS
- *  so no need to redefine here.
- */
-#define USBINTR              OPREGBASE + 0x0008
-#define USBINTR_MASK         0x0000003f
-
-#define FRINDEX              OPREGBASE + 0x000c
-#define CTRLDSSEGMENT        OPREGBASE + 0x0010
-#define PERIODICLISTBASE     OPREGBASE + 0x0014
-#define ASYNCLISTADDR        OPREGBASE + 0x0018
-#define ASYNCLISTADDR_MASK   0xffffffe0
-
-#define CONFIGFLAG           OPREGBASE + 0x0040
-
-#define PORTSC               (OPREGBASE + 0x0044)
-#define PORTSC_BEGIN         PORTSC
-#define PORTSC_END           (PORTSC + 4 * NB_PORTS)
-/*
- * Bits that are reserved or are read-only are masked out of values
- * written to us by software
- */
-#define PORTSC_RO_MASK       0x007001c0
-#define PORTSC_RWC_MASK      0x0000002a
-#define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
-#define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
-#define PORTSC_WKCN_E        (1 << 20)    // Wake on Connect Enable
-#define PORTSC_PTC           (15 << 16)   // Port Test Control
-#define PORTSC_PTC_SH        16           // Port Test Control shift
-#define PORTSC_PIC           (3 << 14)    // Port Indicator Control
-#define PORTSC_PIC_SH        14           // Port Indicator Control Shift
-#define PORTSC_POWNER        (1 << 13)    // Port Owner
-#define PORTSC_PPOWER        (1 << 12)    // Port Power
-#define PORTSC_LINESTAT      (3 << 10)    // Port Line Status
-#define PORTSC_LINESTAT_SH   10           // Port Line Status Shift
-#define PORTSC_PRESET        (1 << 8)     // Port Reset
-#define PORTSC_SUSPEND       (1 << 7)     // Port Suspend
-#define PORTSC_FPRES         (1 << 6)     // Force Port Resume
-#define PORTSC_OCC           (1 << 5)     // Over Current Change
-#define PORTSC_OCA           (1 << 4)     // Over Current Active
-#define PORTSC_PEDC          (1 << 3)     // Port Enable/Disable Change
-#define PORTSC_PED           (1 << 2)     // Port Enable/Disable
-#define PORTSC_CSC           (1 << 1)     // Connect Status Change
-#define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
-
-#define FRAME_TIMER_FREQ 1000
-#define FRAME_TIMER_NS   (1000000000 / FRAME_TIMER_FREQ)
-
-#define NB_MAXINTRATE    8        // Max rate at which controller issues ints
-#define NB_PORTS         6        // Number of downstream ports
-#define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction
-#define MAX_ITERATIONS   20       // Max number of QH before we break the loop
-#define MAX_QH           100      // Max allowable queue heads in a chain
-
-/*  Internal periodic / asynchronous schedule state machine states
- */
-typedef enum {
-    EST_INACTIVE = 1000,
-    EST_ACTIVE,
-    EST_EXECUTING,
-    EST_SLEEPING,
-    /*  The following states are internal to the state machine function
-    */
-    EST_WAITLISTHEAD,
-    EST_FETCHENTRY,
-    EST_FETCHQH,
-    EST_FETCHITD,
-    EST_FETCHSITD,
-    EST_ADVANCEQUEUE,
-    EST_FETCHQTD,
-    EST_EXECUTE,
-    EST_WRITEBACK,
-    EST_HORIZONTALQH
-} EHCI_STATES;
-
-/* macros for accessing fields within next link pointer entry */
-#define NLPTR_GET(x)             ((x) & 0xffffffe0)
-#define NLPTR_TYPE_GET(x)        (((x) >> 1) & 3)
-#define NLPTR_TBIT(x)            ((x) & 1)  // 1=invalid, 0=valid
-
-/* link pointer types */
-#define NLPTR_TYPE_ITD           0     // isoc xfer descriptor
-#define NLPTR_TYPE_QH            1     // queue head
-#define NLPTR_TYPE_STITD         2     // split xaction, isoc xfer descriptor
-#define NLPTR_TYPE_FSTN          3     // frame span traversal node
-
-
-/*  EHCI spec version 1.0 Section 3.3
- */
-typedef struct EHCIitd {
-    uint32_t next;
-
-    uint32_t transact[8];
-#define ITD_XACT_ACTIVE          (1 << 31)
-#define ITD_XACT_DBERROR         (1 << 30)
-#define ITD_XACT_BABBLE          (1 << 29)
-#define ITD_XACT_XACTERR         (1 << 28)
-#define ITD_XACT_LENGTH_MASK     0x0fff0000
-#define ITD_XACT_LENGTH_SH       16
-#define ITD_XACT_IOC             (1 << 15)
-#define ITD_XACT_PGSEL_MASK      0x00007000
-#define ITD_XACT_PGSEL_SH        12
-#define ITD_XACT_OFFSET_MASK     0x00000fff
-
-    uint32_t bufptr[7];
-#define ITD_BUFPTR_MASK          0xfffff000
-#define ITD_BUFPTR_SH            12
-#define ITD_BUFPTR_EP_MASK       0x00000f00
-#define ITD_BUFPTR_EP_SH         8
-#define ITD_BUFPTR_DEVADDR_MASK  0x0000007f
-#define ITD_BUFPTR_DEVADDR_SH    0
-#define ITD_BUFPTR_DIRECTION     (1 << 11)
-#define ITD_BUFPTR_MAXPKT_MASK   0x000007ff
-#define ITD_BUFPTR_MAXPKT_SH     0
-#define ITD_BUFPTR_MULT_MASK     0x00000003
-#define ITD_BUFPTR_MULT_SH       0
-} EHCIitd;
-
-/*  EHCI spec version 1.0 Section 3.4
- */
-typedef struct EHCIsitd {
-    uint32_t next;                  // Standard next link pointer
-    uint32_t epchar;
-#define SITD_EPCHAR_IO              (1 << 31)
-#define SITD_EPCHAR_PORTNUM_MASK    0x7f000000
-#define SITD_EPCHAR_PORTNUM_SH      24
-#define SITD_EPCHAR_HUBADD_MASK     0x007f0000
-#define SITD_EPCHAR_HUBADDR_SH      16
-#define SITD_EPCHAR_EPNUM_MASK      0x00000f00
-#define SITD_EPCHAR_EPNUM_SH        8
-#define SITD_EPCHAR_DEVADDR_MASK    0x0000007f
-
-    uint32_t uframe;
-#define SITD_UFRAME_CMASK_MASK      0x0000ff00
-#define SITD_UFRAME_CMASK_SH        8
-#define SITD_UFRAME_SMASK_MASK      0x000000ff
-
-    uint32_t results;
-#define SITD_RESULTS_IOC              (1 << 31)
-#define SITD_RESULTS_PGSEL            (1 << 30)
-#define SITD_RESULTS_TBYTES_MASK      0x03ff0000
-#define SITD_RESULTS_TYBYTES_SH       16
-#define SITD_RESULTS_CPROGMASK_MASK   0x0000ff00
-#define SITD_RESULTS_CPROGMASK_SH     8
-#define SITD_RESULTS_ACTIVE           (1 << 7)
-#define SITD_RESULTS_ERR              (1 << 6)
-#define SITD_RESULTS_DBERR            (1 << 5)
-#define SITD_RESULTS_BABBLE           (1 << 4)
-#define SITD_RESULTS_XACTERR          (1 << 3)
-#define SITD_RESULTS_MISSEDUF         (1 << 2)
-#define SITD_RESULTS_SPLITXSTATE      (1 << 1)
-
-    uint32_t bufptr[2];
-#define SITD_BUFPTR_MASK              0xfffff000
-#define SITD_BUFPTR_CURROFF_MASK      0x00000fff
-#define SITD_BUFPTR_TPOS_MASK         0x00000018
-#define SITD_BUFPTR_TPOS_SH           3
-#define SITD_BUFPTR_TCNT_MASK         0x00000007
-
-    uint32_t backptr;                 // Standard next link pointer
-} EHCIsitd;
-
-/*  EHCI spec version 1.0 Section 3.5
- */
-typedef struct EHCIqtd {
-    uint32_t next;                    // Standard next link pointer
-    uint32_t altnext;                 // Standard next link pointer
-    uint32_t token;
-#define QTD_TOKEN_DTOGGLE             (1 << 31)
-#define QTD_TOKEN_TBYTES_MASK         0x7fff0000
-#define QTD_TOKEN_TBYTES_SH           16
-#define QTD_TOKEN_IOC                 (1 << 15)
-#define QTD_TOKEN_CPAGE_MASK          0x00007000
-#define QTD_TOKEN_CPAGE_SH            12
-#define QTD_TOKEN_CERR_MASK           0x00000c00
-#define QTD_TOKEN_CERR_SH             10
-#define QTD_TOKEN_PID_MASK            0x00000300
-#define QTD_TOKEN_PID_SH              8
-#define QTD_TOKEN_ACTIVE              (1 << 7)
-#define QTD_TOKEN_HALT                (1 << 6)
-#define QTD_TOKEN_DBERR               (1 << 5)
-#define QTD_TOKEN_BABBLE              (1 << 4)
-#define QTD_TOKEN_XACTERR             (1 << 3)
-#define QTD_TOKEN_MISSEDUF            (1 << 2)
-#define QTD_TOKEN_SPLITXSTATE         (1 << 1)
-#define QTD_TOKEN_PING                (1 << 0)
-
-    uint32_t bufptr[5];               // Standard buffer pointer
-#define QTD_BUFPTR_MASK               0xfffff000
-#define QTD_BUFPTR_SH                 12
-} EHCIqtd;
-
-/*  EHCI spec version 1.0 Section 3.6
- */
-typedef struct EHCIqh {
-    uint32_t next;                    // Standard next link pointer
-
-    /* endpoint characteristics */
-    uint32_t epchar;
-#define QH_EPCHAR_RL_MASK             0xf0000000
-#define QH_EPCHAR_RL_SH               28
-#define QH_EPCHAR_C                   (1 << 27)
-#define QH_EPCHAR_MPLEN_MASK          0x07FF0000
-#define QH_EPCHAR_MPLEN_SH            16
-#define QH_EPCHAR_H                   (1 << 15)
-#define QH_EPCHAR_DTC                 (1 << 14)
-#define QH_EPCHAR_EPS_MASK            0x00003000
-#define QH_EPCHAR_EPS_SH              12
-#define EHCI_QH_EPS_FULL              0
-#define EHCI_QH_EPS_LOW               1
-#define EHCI_QH_EPS_HIGH              2
-#define EHCI_QH_EPS_RESERVED          3
-
-#define QH_EPCHAR_EP_MASK             0x00000f00
-#define QH_EPCHAR_EP_SH               8
-#define QH_EPCHAR_I                   (1 << 7)
-#define QH_EPCHAR_DEVADDR_MASK        0x0000007f
-#define QH_EPCHAR_DEVADDR_SH          0
-
-    /* endpoint capabilities */
-    uint32_t epcap;
-#define QH_EPCAP_MULT_MASK            0xc0000000
-#define QH_EPCAP_MULT_SH              30
-#define QH_EPCAP_PORTNUM_MASK         0x3f800000
-#define QH_EPCAP_PORTNUM_SH           23
-#define QH_EPCAP_HUBADDR_MASK         0x007f0000
-#define QH_EPCAP_HUBADDR_SH           16
-#define QH_EPCAP_CMASK_MASK           0x0000ff00
-#define QH_EPCAP_CMASK_SH             8
-#define QH_EPCAP_SMASK_MASK           0x000000ff
-#define QH_EPCAP_SMASK_SH             0
-
-    uint32_t current_qtd;             // Standard next link pointer
-    uint32_t next_qtd;                // Standard next link pointer
-    uint32_t altnext_qtd;
-#define QH_ALTNEXT_NAKCNT_MASK        0x0000001e
-#define QH_ALTNEXT_NAKCNT_SH          1
-
-    uint32_t token;                   // Same as QTD token
-    uint32_t bufptr[5];               // Standard buffer pointer
-#define BUFPTR_CPROGMASK_MASK         0x000000ff
-#define BUFPTR_FRAMETAG_MASK          0x0000001f
-#define BUFPTR_SBYTES_MASK            0x00000fe0
-#define BUFPTR_SBYTES_SH              5
-} EHCIqh;
-
-/*  EHCI spec version 1.0 Section 3.7
- */
-typedef struct EHCIfstn {
-    uint32_t next;                    // Standard next link pointer
-    uint32_t backptr;                 // Standard next link pointer
-} EHCIfstn;
-
-typedef struct EHCIQueue EHCIQueue;
-typedef struct EHCIState EHCIState;
-
-enum async_state {
-    EHCI_ASYNC_NONE = 0,
-    EHCI_ASYNC_INFLIGHT,
-    EHCI_ASYNC_FINISHED,
-};
-
-struct EHCIQueue {
-    EHCIState *ehci;
-    QTAILQ_ENTRY(EHCIQueue) next;
-    uint32_t seen;
-    uint64_t ts;
-
-    /* cached data from guest - needs to be flushed
-     * when guest removes an entry (doorbell, handshake sequence)
-     */
-    EHCIqh qh;             // copy of current QH (being worked on)
-    uint32_t qhaddr;       // address QH read from
-    EHCIqtd qtd;           // copy of current QTD (being worked on)
-    uint32_t qtdaddr;      // address QTD read from
-
-    USBPacket packet;
-    QEMUSGList sgl;
-    int pid;
-    uint32_t tbytes;
-    enum async_state async;
-    int usb_status;
-};
-
-typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
-
-struct EHCIState {
-    PCIDevice dev;
-    USBBus bus;
-    qemu_irq irq;
-    MemoryRegion mem;
-    int companion_count;
-
-    /* properties */
-    uint32_t freq;
-    uint32_t maxframes;
-
-    /*
-     *  EHCI spec version 1.0 Section 2.3
-     *  Host Controller Operational Registers
-     */
-    union {
-        uint8_t mmio[MMIO_SIZE];
-        struct {
-            uint8_t cap[OPREGBASE];
-            uint32_t usbcmd;
-            uint32_t usbsts;
-            uint32_t usbintr;
-            uint32_t frindex;
-            uint32_t ctrldssegment;
-            uint32_t periodiclistbase;
-            uint32_t asynclistaddr;
-            uint32_t notused[9];
-            uint32_t configflag;
-            uint32_t portsc[NB_PORTS];
-        };
-    };
-
-    /*
-     *  Internal states, shadow registers, etc
-     */
-    uint32_t sofv;
-    QEMUTimer *frame_timer;
-    int attach_poll_counter;
-    int astate;                        // Current state in asynchronous schedule
-    int pstate;                        // Current state in periodic schedule
-    USBPort ports[NB_PORTS];
-    USBPort *companion_ports[NB_PORTS];
-    uint32_t usbsts_pending;
-    EHCIQueueHead aqueues;
-    EHCIQueueHead pqueues;
-
-    uint32_t a_fetch_addr;   // which address to look at next
-    uint32_t p_fetch_addr;   // which address to look at next
-
-    USBPacket ipacket;
-    QEMUSGList isgl;
-    int isoch_pause;
-
-    uint64_t last_run_ns;
-};
-
-#define SET_LAST_RUN_CLOCK(s) \
-    (s)->last_run_ns = qemu_get_clock_ns(vm_clock);
-
-/* nifty macros from Arnon's EHCI version  */
-#define get_field(data, field) \
-    (((data) & field##_MASK) >> field##_SH)
-
-#define set_field(data, newval, field) do { \
-    uint32_t val = *data; \
-    val &= ~ field##_MASK; \
-    val |= ((newval) << field##_SH) & field##_MASK; \
-    *data = val; \
-    } while(0)
-
-static const char *ehci_state_names[] = {
-    [EST_INACTIVE]     = "INACTIVE",
-    [EST_ACTIVE]       = "ACTIVE",
-    [EST_EXECUTING]    = "EXECUTING",
-    [EST_SLEEPING]     = "SLEEPING",
-    [EST_WAITLISTHEAD] = "WAITLISTHEAD",
-    [EST_FETCHENTRY]   = "FETCH ENTRY",
-    [EST_FETCHQH]      = "FETCH QH",
-    [EST_FETCHITD]     = "FETCH ITD",
-    [EST_ADVANCEQUEUE] = "ADVANCEQUEUE",
-    [EST_FETCHQTD]     = "FETCH QTD",
-    [EST_EXECUTE]      = "EXECUTE",
-    [EST_WRITEBACK]    = "WRITEBACK",
-    [EST_HORIZONTALQH] = "HORIZONTALQH",
-};
-
-static const char *ehci_mmio_names[] = {
-    [CAPLENGTH]         = "CAPLENGTH",
-    [HCIVERSION]        = "HCIVERSION",
-    [HCSPARAMS]         = "HCSPARAMS",
-    [HCCPARAMS]         = "HCCPARAMS",
-    [USBCMD]            = "USBCMD",
-    [USBSTS]            = "USBSTS",
-    [USBINTR]           = "USBINTR",
-    [FRINDEX]           = "FRINDEX",
-    [PERIODICLISTBASE]  = "P-LIST BASE",
-    [ASYNCLISTADDR]     = "A-LIST ADDR",
-    [PORTSC_BEGIN]      = "PORTSC #0",
-    [PORTSC_BEGIN + 4]  = "PORTSC #1",
-    [PORTSC_BEGIN + 8]  = "PORTSC #2",
-    [PORTSC_BEGIN + 12] = "PORTSC #3",
-    [PORTSC_BEGIN + 16] = "PORTSC #4",
-    [PORTSC_BEGIN + 20] = "PORTSC #5",
-    [CONFIGFLAG]        = "CONFIGFLAG",
-};
-
-static const char *nr2str(const char **n, size_t len, uint32_t nr)
-{
-    if (nr < len && n[nr] != NULL) {
-        return n[nr];
-    } else {
-        return "unknown";
-    }
-}
-
-static const char *state2str(uint32_t state)
-{
-    return nr2str(ehci_state_names, ARRAY_SIZE(ehci_state_names), state);
-}
-
-static const char *addr2str(target_phys_addr_t addr)
-{
-    return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
-}
-
-static void ehci_trace_usbsts(uint32_t mask, int state)
-{
-    /* interrupts */
-    if (mask & USBSTS_INT) {
-        trace_usb_ehci_usbsts("INT", state);
-    }
-    if (mask & USBSTS_ERRINT) {
-        trace_usb_ehci_usbsts("ERRINT", state);
-    }
-    if (mask & USBSTS_PCD) {
-        trace_usb_ehci_usbsts("PCD", state);
-    }
-    if (mask & USBSTS_FLR) {
-        trace_usb_ehci_usbsts("FLR", state);
-    }
-    if (mask & USBSTS_HSE) {
-        trace_usb_ehci_usbsts("HSE", state);
-    }
-    if (mask & USBSTS_IAA) {
-        trace_usb_ehci_usbsts("IAA", state);
-    }
-
-    /* status */
-    if (mask & USBSTS_HALT) {
-        trace_usb_ehci_usbsts("HALT", state);
-    }
-    if (mask & USBSTS_REC) {
-        trace_usb_ehci_usbsts("REC", state);
-    }
-    if (mask & USBSTS_PSS) {
-        trace_usb_ehci_usbsts("PSS", state);
-    }
-    if (mask & USBSTS_ASS) {
-        trace_usb_ehci_usbsts("ASS", state);
-    }
-}
-
-static inline void ehci_set_usbsts(EHCIState *s, int mask)
-{
-    if ((s->usbsts & mask) == mask) {
-        return;
-    }
-    ehci_trace_usbsts(mask, 1);
-    s->usbsts |= mask;
-}
-
-static inline void ehci_clear_usbsts(EHCIState *s, int mask)
-{
-    if ((s->usbsts & mask) == 0) {
-        return;
-    }
-    ehci_trace_usbsts(mask, 0);
-    s->usbsts &= ~mask;
-}
-
-static inline void ehci_set_interrupt(EHCIState *s, int intr)
-{
-    int level = 0;
-
-    // TODO honour interrupt threshold requests
-
-    ehci_set_usbsts(s, intr);
-
-    if ((s->usbsts & USBINTR_MASK) & s->usbintr) {
-        level = 1;
-    }
-
-    qemu_set_irq(s->irq, level);
-}
-
-static inline void ehci_record_interrupt(EHCIState *s, int intr)
-{
-    s->usbsts_pending |= intr;
-}
-
-static inline void ehci_commit_interrupt(EHCIState *s)
-{
-    if (!s->usbsts_pending) {
-        return;
-    }
-    ehci_set_interrupt(s, s->usbsts_pending);
-    s->usbsts_pending = 0;
-}
-
-static void ehci_set_state(EHCIState *s, int async, int state)
-{
-    if (async) {
-        trace_usb_ehci_state("async", state2str(state));
-        s->astate = state;
-    } else {
-        trace_usb_ehci_state("periodic", state2str(state));
-        s->pstate = state;
-    }
-}
-
-static int ehci_get_state(EHCIState *s, int async)
-{
-    return async ? s->astate : s->pstate;
-}
-
-static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr)
-{
-    if (async) {
-        s->a_fetch_addr = addr;
-    } else {
-        s->p_fetch_addr = addr;
-    }
-}
-
-static int ehci_get_fetch_addr(EHCIState *s, int async)
-{
-    return async ? s->a_fetch_addr : s->p_fetch_addr;
-}
-
-static void ehci_trace_qh(EHCIQueue *q, target_phys_addr_t addr, EHCIqh *qh)
-{
-    /* need three here due to argument count limits */
-    trace_usb_ehci_qh_ptrs(q, addr, qh->next,
-                           qh->current_qtd, qh->next_qtd, qh->altnext_qtd);
-    trace_usb_ehci_qh_fields(addr,
-                             get_field(qh->epchar, QH_EPCHAR_RL),
-                             get_field(qh->epchar, QH_EPCHAR_MPLEN),
-                             get_field(qh->epchar, QH_EPCHAR_EPS),
-                             get_field(qh->epchar, QH_EPCHAR_EP),
-                             get_field(qh->epchar, QH_EPCHAR_DEVADDR));
-    trace_usb_ehci_qh_bits(addr,
-                           (bool)(qh->epchar & QH_EPCHAR_C),
-                           (bool)(qh->epchar & QH_EPCHAR_H),
-                           (bool)(qh->epchar & QH_EPCHAR_DTC),
-                           (bool)(qh->epchar & QH_EPCHAR_I));
-}
-
-static void ehci_trace_qtd(EHCIQueue *q, target_phys_addr_t addr, EHCIqtd *qtd)
-{
-    /* need three here due to argument count limits */
-    trace_usb_ehci_qtd_ptrs(q, addr, qtd->next, qtd->altnext);
-    trace_usb_ehci_qtd_fields(addr,
-                              get_field(qtd->token, QTD_TOKEN_TBYTES),
-                              get_field(qtd->token, QTD_TOKEN_CPAGE),
-                              get_field(qtd->token, QTD_TOKEN_CERR),
-                              get_field(qtd->token, QTD_TOKEN_PID));
-    trace_usb_ehci_qtd_bits(addr,
-                            (bool)(qtd->token & QTD_TOKEN_IOC),
-                            (bool)(qtd->token & QTD_TOKEN_ACTIVE),
-                            (bool)(qtd->token & QTD_TOKEN_HALT),
-                            (bool)(qtd->token & QTD_TOKEN_BABBLE),
-                            (bool)(qtd->token & QTD_TOKEN_XACTERR));
-}
-
-static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd)
-{
-    trace_usb_ehci_itd(addr, itd->next,
-                       get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT),
-                       get_field(itd->bufptr[2], ITD_BUFPTR_MULT),
-                       get_field(itd->bufptr[0], ITD_BUFPTR_EP),
-                       get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR));
-}
-
-static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
-                            EHCIsitd *sitd)
-{
-    trace_usb_ehci_sitd(addr, sitd->next,
-                        (bool)(sitd->results & SITD_RESULTS_ACTIVE));
-}
-
-/* queue management */
-
-static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async)
-{
-    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
-    EHCIQueue *q;
-
-    q = g_malloc0(sizeof(*q));
-    q->ehci = ehci;
-    QTAILQ_INSERT_HEAD(head, q, next);
-    trace_usb_ehci_queue_action(q, "alloc");
-    return q;
-}
-
-static void ehci_free_queue(EHCIQueue *q, int async)
-{
-    EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues;
-    trace_usb_ehci_queue_action(q, "free");
-    if (q->async == EHCI_ASYNC_INFLIGHT) {
-        usb_cancel_packet(&q->packet);
-    }
-    QTAILQ_REMOVE(head, q, next);
-    g_free(q);
-}
-
-static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
-                                        int async)
-{
-    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
-    EHCIQueue *q;
-
-    QTAILQ_FOREACH(q, head, next) {
-        if (addr == q->qhaddr) {
-            return q;
-        }
-    }
-    return NULL;
-}
-
-static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
-{
-    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
-    EHCIQueue *q, *tmp;
-
-    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
-        if (q->seen) {
-            q->seen = 0;
-            q->ts = ehci->last_run_ns;
-            continue;
-        }
-        if (!flush && ehci->last_run_ns < q->ts + 250000000) {
-            /* allow 0.25 sec idle */
-            continue;
-        }
-        ehci_free_queue(q, async);
-    }
-}
-
-static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
-{
-    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
-    EHCIQueue *q, *tmp;
-
-    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
-        if (!usb_packet_is_inflight(&q->packet) ||
-            q->packet.ep->dev != dev) {
-            continue;
-        }
-        ehci_free_queue(q, async);
-    }
-}
-
-static void ehci_queues_rip_all(EHCIState *ehci, int async)
-{
-    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
-    EHCIQueue *q, *tmp;
-
-    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
-        ehci_free_queue(q, async);
-    }
-}
-
-/* Attach or detach a device on root hub */
-
-static void ehci_attach(USBPort *port)
-{
-    EHCIState *s = port->opaque;
-    uint32_t *portsc = &s->portsc[port->index];
-
-    trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
-
-    if (*portsc & PORTSC_POWNER) {
-        USBPort *companion = s->companion_ports[port->index];
-        companion->dev = port->dev;
-        companion->ops->attach(companion);
-        return;
-    }
-
-    *portsc |= PORTSC_CONNECT;
-    *portsc |= PORTSC_CSC;
-
-    ehci_set_interrupt(s, USBSTS_PCD);
-}
-
-static void ehci_detach(USBPort *port)
-{
-    EHCIState *s = port->opaque;
-    uint32_t *portsc = &s->portsc[port->index];
-
-    trace_usb_ehci_port_detach(port->index);
-
-    if (*portsc & PORTSC_POWNER) {
-        USBPort *companion = s->companion_ports[port->index];
-        companion->ops->detach(companion);
-        companion->dev = NULL;
-        /*
-         * EHCI spec 4.2.2: "When a disconnect occurs... On the event,
-         * the port ownership is returned immediately to the EHCI controller."
-         */
-        *portsc &= ~PORTSC_POWNER;
-        return;
-    }
-
-    ehci_queues_rip_device(s, port->dev, 0);
-    ehci_queues_rip_device(s, port->dev, 1);
-
-    *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
-    *portsc |= PORTSC_CSC;
-
-    ehci_set_interrupt(s, USBSTS_PCD);
-}
-
-static void ehci_child_detach(USBPort *port, USBDevice *child)
-{
-    EHCIState *s = port->opaque;
-    uint32_t portsc = s->portsc[port->index];
-
-    if (portsc & PORTSC_POWNER) {
-        USBPort *companion = s->companion_ports[port->index];
-        companion->ops->child_detach(companion, child);
-        companion->dev = NULL;
-        return;
-    }
-
-    ehci_queues_rip_device(s, child, 0);
-    ehci_queues_rip_device(s, child, 1);
-}
-
-static void ehci_wakeup(USBPort *port)
-{
-    EHCIState *s = port->opaque;
-    uint32_t portsc = s->portsc[port->index];
-
-    if (portsc & PORTSC_POWNER) {
-        USBPort *companion = s->companion_ports[port->index];
-        if (companion->ops->wakeup) {
-            companion->ops->wakeup(companion);
-        }
-    }
-}
-
-static int ehci_register_companion(USBBus *bus, USBPort *ports[],
-                                   uint32_t portcount, uint32_t firstport)
-{
-    EHCIState *s = container_of(bus, EHCIState, bus);
-    uint32_t i;
-
-    if (firstport + portcount > NB_PORTS) {
-        qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport",
-                      "firstport on masterbus");
-        error_printf_unless_qmp(
-            "firstport value of %u makes companion take ports %u - %u, which "
-            "is outside of the valid range of 0 - %u\n", firstport, firstport,
-            firstport + portcount - 1, NB_PORTS - 1);
-        return -1;
-    }
-
-    for (i = 0; i < portcount; i++) {
-        if (s->companion_ports[firstport + i]) {
-            qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
-                          "an USB masterbus");
-            error_printf_unless_qmp(
-                "port %u on masterbus %s already has a companion assigned\n",
-                firstport + i, bus->qbus.name);
-            return -1;
-        }
-    }
-
-    for (i = 0; i < portcount; i++) {
-        s->companion_ports[firstport + i] = ports[i];
-        s->ports[firstport + i].speedmask |=
-            USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL;
-        /* Ensure devs attached before the initial reset go to the companion */
-        s->portsc[firstport + i] = PORTSC_POWNER;
-    }
-
-    s->companion_count++;
-    s->mmio[0x05] = (s->companion_count << 4) | portcount;
-
-    return 0;
-}
-
-static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr)
-{
-    USBDevice *dev;
-    USBPort *port;
-    int i;
-
-    for (i = 0; i < NB_PORTS; i++) {
-        port = &ehci->ports[i];
-        if (!(ehci->portsc[i] & PORTSC_PED)) {
-            DPRINTF("Port %d not enabled\n", i);
-            continue;
-        }
-        dev = usb_find_device(port, addr);
-        if (dev != NULL) {
-            return dev;
-        }
-    }
-    return NULL;
-}
-
-/* 4.1 host controller initialization */
-static void ehci_reset(void *opaque)
-{
-    EHCIState *s = opaque;
-    int i;
-    USBDevice *devs[NB_PORTS];
-
-    trace_usb_ehci_reset();
-
-    /*
-     * Do the detach before touching portsc, so that it correctly gets send to
-     * us or to our companion based on PORTSC_POWNER before the reset.
-     */
-    for(i = 0; i < NB_PORTS; i++) {
-        devs[i] = s->ports[i].dev;
-        if (devs[i] && devs[i]->attached) {
-            usb_detach(&s->ports[i]);
-        }
-    }
-
-    memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
-
-    s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
-    s->usbsts = USBSTS_HALT;
-
-    s->astate = EST_INACTIVE;
-    s->pstate = EST_INACTIVE;
-    s->isoch_pause = -1;
-    s->attach_poll_counter = 0;
-
-    for(i = 0; i < NB_PORTS; i++) {
-        if (s->companion_ports[i]) {
-            s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
-        } else {
-            s->portsc[i] = PORTSC_PPOWER;
-        }
-        if (devs[i] && devs[i]->attached) {
-            usb_attach(&s->ports[i]);
-            usb_device_reset(devs[i]);
-        }
-    }
-    ehci_queues_rip_all(s, 0);
-    ehci_queues_rip_all(s, 1);
-    qemu_del_timer(s->frame_timer);
-}
-
-static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
-{
-    EHCIState *s = ptr;
-    uint32_t val;
-
-    val = s->mmio[addr];
-
-    return val;
-}
-
-static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
-{
-    EHCIState *s = ptr;
-    uint32_t val;
-
-    val = s->mmio[addr] | (s->mmio[addr+1] << 8);
-
-    return val;
-}
-
-static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
-{
-    EHCIState *s = ptr;
-    uint32_t val;
-
-    val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
-          (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
-
-    trace_usb_ehci_mmio_readl(addr, addr2str(addr), val);
-    return val;
-}
-
-static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
-{
-    fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
-    exit(1);
-}
-
-static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
-{
-    fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
-    exit(1);
-}
-
-static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
-{
-    USBDevice *dev = s->ports[port].dev;
-    uint32_t *portsc = &s->portsc[port];
-    uint32_t orig;
-
-    if (s->companion_ports[port] == NULL)
-        return;
-
-    owner = owner & PORTSC_POWNER;
-    orig  = *portsc & PORTSC_POWNER;
-
-    if (!(owner ^ orig)) {
-        return;
-    }
-
-    if (dev && dev->attached) {
-        usb_detach(&s->ports[port]);
-    }
-
-    *portsc &= ~PORTSC_POWNER;
-    *portsc |= owner;
-
-    if (dev && dev->attached) {
-        usb_attach(&s->ports[port]);
-    }
-}
-
-static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
-{
-    uint32_t *portsc = &s->portsc[port];
-    USBDevice *dev = s->ports[port].dev;
-
-    /* Clear rwc bits */
-    *portsc &= ~(val & PORTSC_RWC_MASK);
-    /* The guest may clear, but not set the PED bit */
-    *portsc &= val | ~PORTSC_PED;
-    /* POWNER is masked out by RO_MASK as it is RO when we've no companion */
-    handle_port_owner_write(s, port, val);
-    /* And finally apply RO_MASK */
-    val &= PORTSC_RO_MASK;
-
-    if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
-        trace_usb_ehci_port_reset(port, 1);
-    }
-
-    if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
-        trace_usb_ehci_port_reset(port, 0);
-        if (dev && dev->attached) {
-            usb_port_reset(&s->ports[port]);
-            *portsc &= ~PORTSC_CSC;
-        }
-
-        /*
-         *  Table 2.16 Set the enable bit(and enable bit change) to indicate
-         *  to SW that this port has a high speed device attached
-         */
-        if (dev && dev->attached && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
-            val |= PORTSC_PED;
-        }
-    }
-
-    *portsc &= ~PORTSC_RO_MASK;
-    *portsc |= val;
-}
-
-static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
-{
-    EHCIState *s = ptr;
-    uint32_t *mmio = (uint32_t *)(&s->mmio[addr]);
-    uint32_t old = *mmio;
-    int i;
-
-    trace_usb_ehci_mmio_writel(addr, addr2str(addr), val);
-
-    /* Only aligned reads are allowed on OHCI */
-    if (addr & 3) {
-        fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
-                TARGET_FMT_plx "\n", addr);
-        return;
-    }
-
-    if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
-        handle_port_status_write(s, (addr-PORTSC)/4, val);
-        trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
-        return;
-    }
-
-    if (addr < OPREGBASE) {
-        fprintf(stderr, "usb-ehci: write attempt to read-only register"
-                TARGET_FMT_plx "\n", addr);
-        return;
-    }
-
-
-    /* Do any register specific pre-write processing here.  */
-    switch(addr) {
-    case USBCMD:
-        if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
-            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
-            SET_LAST_RUN_CLOCK(s);
-            ehci_clear_usbsts(s, USBSTS_HALT);
-        }
-
-        if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
-            qemu_del_timer(s->frame_timer);
-            ehci_queues_rip_all(s, 0);
-            ehci_queues_rip_all(s, 1);
-            ehci_set_usbsts(s, USBSTS_HALT);
-        }
-
-        if (val & USBCMD_HCRESET) {
-            ehci_reset(s);
-            val = s->usbcmd;
-        }
-
-        /* not supporting dynamic frame list size at the moment */
-        if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
-            fprintf(stderr, "attempt to set frame list size -- value %d\n",
-                    val & USBCMD_FLS);
-            val &= ~USBCMD_FLS;
-        }
-        break;
-
-    case USBSTS:
-        val &= USBSTS_RO_MASK;              // bits 6 thru 31 are RO
-        ehci_clear_usbsts(s, val);          // bits 0 thru 5 are R/WC
-        val = s->usbsts;
-        ehci_set_interrupt(s, 0);
-        break;
-
-    case USBINTR:
-        val &= USBINTR_MASK;
-        break;
-
-    case FRINDEX:
-        s->sofv = val >> 3;
-        break;
-
-    case CONFIGFLAG:
-        val &= 0x1;
-        if (val) {
-            for(i = 0; i < NB_PORTS; i++)
-                handle_port_owner_write(s, i, 0);
-        }
-        break;
-
-    case PERIODICLISTBASE:
-        if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
-            fprintf(stderr,
-              "ehci: PERIODIC list base register set while periodic schedule\n"
-              "      is enabled and HC is enabled\n");
-        }
-        break;
-
-    case ASYNCLISTADDR:
-        if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
-            fprintf(stderr,
-              "ehci: ASYNC list address register set while async schedule\n"
-              "      is enabled and HC is enabled\n");
-        }
-        break;
-    }
-
-    *mmio = val;
-    trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
-}
-
-
-// TODO : Put in common header file, duplication from usb-ohci.c
-
-/* Get an array of dwords from main memory */
-static inline int get_dwords(EHCIState *ehci, uint32_t addr,
-                             uint32_t *buf, int num)
-{
-    int i;
-
-    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        pci_dma_read(&ehci->dev, addr, buf, sizeof(*buf));
-        *buf = le32_to_cpu(*buf);
-    }
-
-    return 1;
-}
-
-/* Put an array of dwords in to main memory */
-static inline int put_dwords(EHCIState *ehci, uint32_t addr,
-                             uint32_t *buf, int num)
-{
-    int i;
-
-    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        uint32_t tmp = cpu_to_le32(*buf);
-        pci_dma_write(&ehci->dev, addr, &tmp, sizeof(tmp));
-    }
-
-    return 1;
-}
-
-// 4.10.2
-
-static int ehci_qh_do_overlay(EHCIQueue *q)
-{
-    int i;
-    int dtoggle;
-    int ping;
-    int eps;
-    int reload;
-
-    // remember values in fields to preserve in qh after overlay
-
-    dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE;
-    ping    = q->qh.token & QTD_TOKEN_PING;
-
-    q->qh.current_qtd = q->qtdaddr;
-    q->qh.next_qtd    = q->qtd.next;
-    q->qh.altnext_qtd = q->qtd.altnext;
-    q->qh.token       = q->qtd.token;
-
-
-    eps = get_field(q->qh.epchar, QH_EPCHAR_EPS);
-    if (eps == EHCI_QH_EPS_HIGH) {
-        q->qh.token &= ~QTD_TOKEN_PING;
-        q->qh.token |= ping;
-    }
-
-    reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
-    set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
-
-    for (i = 0; i < 5; i++) {
-        q->qh.bufptr[i] = q->qtd.bufptr[i];
-    }
-
-    if (!(q->qh.epchar & QH_EPCHAR_DTC)) {
-        // preserve QH DT bit
-        q->qh.token &= ~QTD_TOKEN_DTOGGLE;
-        q->qh.token |= dtoggle;
-    }
-
-    q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
-    q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
-
-    put_dwords(q->ehci, NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh,
-               sizeof(EHCIqh) >> 2);
-
-    return 0;
-}
-
-static int ehci_init_transfer(EHCIQueue *q)
-{
-    uint32_t cpage, offset, bytes, plen;
-    dma_addr_t page;
-
-    cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
-    bytes  = get_field(q->qh.token, QTD_TOKEN_TBYTES);
-    offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
-    pci_dma_sglist_init(&q->sgl, &q->ehci->dev, 5);
-
-    while (bytes > 0) {
-        if (cpage > 4) {
-            fprintf(stderr, "cpage out of range (%d)\n", cpage);
-            return USB_RET_PROCERR;
-        }
-
-        page  = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
-        page += offset;
-        plen  = bytes;
-        if (plen > 4096 - offset) {
-            plen = 4096 - offset;
-            offset = 0;
-            cpage++;
-        }
-
-        qemu_sglist_add(&q->sgl, page, plen);
-        bytes -= plen;
-    }
-    return 0;
-}
-
-static void ehci_finish_transfer(EHCIQueue *q, int status)
-{
-    uint32_t cpage, offset;
-
-    qemu_sglist_destroy(&q->sgl);
-
-    if (status > 0) {
-        /* update cpage & offset */
-        cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
-        offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
-
-        offset += status;
-        cpage  += offset >> QTD_BUFPTR_SH;
-        offset &= ~QTD_BUFPTR_MASK;
-
-        set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
-        q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
-        q->qh.bufptr[0] |= offset;
-    }
-}
-
-static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
-{
-    EHCIQueue *q;
-    EHCIState *s = port->opaque;
-    uint32_t portsc = s->portsc[port->index];
-
-    if (portsc & PORTSC_POWNER) {
-        USBPort *companion = s->companion_ports[port->index];
-        companion->ops->complete(companion, packet);
-        return;
-    }
-
-    q = container_of(packet, EHCIQueue, packet);
-    trace_usb_ehci_queue_action(q, "wakeup");
-    assert(q->async == EHCI_ASYNC_INFLIGHT);
-    q->async = EHCI_ASYNC_FINISHED;
-    q->usb_status = packet->result;
-}
-
-static void ehci_execute_complete(EHCIQueue *q)
-{
-    assert(q->async != EHCI_ASYNC_INFLIGHT);
-    q->async = EHCI_ASYNC_NONE;
-
-    DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
-            q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
-
-    if (q->usb_status < 0) {
-        switch(q->usb_status) {
-        case USB_RET_IOERROR:
-        case USB_RET_NODEV:
-            q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
-            set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
-            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
-            break;
-        case USB_RET_STALL:
-            q->qh.token |= QTD_TOKEN_HALT;
-            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
-            break;
-        case USB_RET_NAK:
-            set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
-            return; /* We're not done yet with this transaction */
-        case USB_RET_BABBLE:
-            q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
-            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
-            break;
-        default:
-            /* should not be triggerable */
-            fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status);
-            assert(0);
-            break;
-        }
-    } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) {
-        q->usb_status = USB_RET_BABBLE;
-        q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
-        ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
-    } else {
-        // TODO check 4.12 for splits
-
-        if (q->tbytes && q->pid == USB_TOKEN_IN) {
-            q->tbytes -= q->usb_status;
-        } else {
-            q->tbytes = 0;
-        }
-
-        DPRINTF("updating tbytes to %d\n", q->tbytes);
-        set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
-    }
-    ehci_finish_transfer(q, q->usb_status);
-    usb_packet_unmap(&q->packet);
-
-    q->qh.token ^= QTD_TOKEN_DTOGGLE;
-    q->qh.token &= ~QTD_TOKEN_ACTIVE;
-
-    if (q->qh.token & QTD_TOKEN_IOC) {
-        ehci_record_interrupt(q->ehci, USBSTS_INT);
-    }
-}
-
-// 4.10.3
-
-static int ehci_execute(EHCIQueue *q)
-{
-    USBDevice *dev;
-    USBEndpoint *ep;
-    int ret;
-    int endp;
-    int devadr;
-
-    if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) {
-        fprintf(stderr, "Attempting to execute inactive QH\n");
-        return USB_RET_PROCERR;
-    }
-
-    q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
-    if (q->tbytes > BUFF_SIZE) {
-        fprintf(stderr, "Request for more bytes than allowed\n");
-        return USB_RET_PROCERR;
-    }
-
-    q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
-    switch(q->pid) {
-        case 0: q->pid = USB_TOKEN_OUT; break;
-        case 1: q->pid = USB_TOKEN_IN; break;
-        case 2: q->pid = USB_TOKEN_SETUP; break;
-        default: fprintf(stderr, "bad token\n"); break;
-    }
-
-    if (ehci_init_transfer(q) != 0) {
-        return USB_RET_PROCERR;
-    }
-
-    endp = get_field(q->qh.epchar, QH_EPCHAR_EP);
-    devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
-
-    /* TODO: associating device with ehci port */
-    dev = ehci_find_device(q->ehci, devadr);
-    ep = usb_ep_get(dev, q->pid, endp);
-
-    usb_packet_setup(&q->packet, q->pid, ep);
-    usb_packet_map(&q->packet, &q->sgl);
-
-    ret = usb_handle_packet(dev, &q->packet);
-    DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
-            "(total %d) endp %x ret %d\n",
-            q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
-            q->packet.iov.size, q->tbytes, endp, ret);
-
-    if (ret > BUFF_SIZE) {
-        fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
-        return USB_RET_PROCERR;
-    }
-
-    return ret;
-}
-
-/*  4.7.2
- */
-
-static int ehci_process_itd(EHCIState *ehci,
-                            EHCIitd *itd)
-{
-    USBDevice *dev;
-    USBEndpoint *ep;
-    int ret;
-    uint32_t i, len, pid, dir, devaddr, endp;
-    uint32_t pg, off, ptr1, ptr2, max, mult;
-
-    dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
-    devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
-    endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
-    max = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
-    mult = get_field(itd->bufptr[2], ITD_BUFPTR_MULT);
-
-    for(i = 0; i < 8; i++) {
-        if (itd->transact[i] & ITD_XACT_ACTIVE) {
-            pg   = get_field(itd->transact[i], ITD_XACT_PGSEL);
-            off  = itd->transact[i] & ITD_XACT_OFFSET_MASK;
-            ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
-            ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK);
-            len  = get_field(itd->transact[i], ITD_XACT_LENGTH);
-
-            if (len > max * mult) {
-                len = max * mult;
-            }
-
-            if (len > BUFF_SIZE) {
-                return USB_RET_PROCERR;
-            }
-
-            pci_dma_sglist_init(&ehci->isgl, &ehci->dev, 2);
-            if (off + len > 4096) {
-                /* transfer crosses page border */
-                uint32_t len2 = off + len - 4096;
-                uint32_t len1 = len - len2;
-                qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
-                qemu_sglist_add(&ehci->isgl, ptr2, len2);
-            } else {
-                qemu_sglist_add(&ehci->isgl, ptr1 + off, len);
-            }
-
-            pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT;
-
-            dev = ehci_find_device(ehci, devaddr);
-            ep = usb_ep_get(dev, pid, endp);
-            if (ep->type == USB_ENDPOINT_XFER_ISOC) {
-                usb_packet_setup(&ehci->ipacket, pid, ep);
-                usb_packet_map(&ehci->ipacket, &ehci->isgl);
-                ret = usb_handle_packet(dev, &ehci->ipacket);
-                assert(ret != USB_RET_ASYNC);
-                usb_packet_unmap(&ehci->ipacket);
-            } else {
-                DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
-                ret = USB_RET_NAK;
-            }
-            qemu_sglist_destroy(&ehci->isgl);
-
-            if (ret < 0) {
-                switch (ret) {
-                default:
-                    fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
-                    /* Fall through */
-                case USB_RET_IOERROR:
-                case USB_RET_NODEV:
-                    /* 3.3.2: XACTERR is only allowed on IN transactions */
-                    if (dir) {
-                        itd->transact[i] |= ITD_XACT_XACTERR;
-                        ehci_record_interrupt(ehci, USBSTS_ERRINT);
-                    }
-                    break;
-                case USB_RET_BABBLE:
-                    itd->transact[i] |= ITD_XACT_BABBLE;
-                    ehci_record_interrupt(ehci, USBSTS_ERRINT);
-                    break;
-                case USB_RET_NAK:
-                    /* no data for us, so do a zero-length transfer */
-                    ret = 0;
-                    break;
-                }
-            }
-            if (ret >= 0) {
-                if (!dir) {
-                    /* OUT */
-                    set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
-                } else {
-                    /* IN */
-                    set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
-                }
-            }
-            if (itd->transact[i] & ITD_XACT_IOC) {
-                ehci_record_interrupt(ehci, USBSTS_INT);
-            }
-            itd->transact[i] &= ~ITD_XACT_ACTIVE;
-        }
-    }
-    return 0;
-}
-
-/*  This state is the entry point for asynchronous schedule
- *  processing.  Entry here consitutes a EHCI start event state (4.8.5)
- */
-static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
-{
-    EHCIqh qh;
-    int i = 0;
-    int again = 0;
-    uint32_t entry = ehci->asynclistaddr;
-
-    /* set reclamation flag at start event (4.8.6) */
-    if (async) {
-        ehci_set_usbsts(ehci, USBSTS_REC);
-    }
-
-    ehci_queues_rip_unused(ehci, async, 0);
-
-    /*  Find the head of the list (4.9.1.1) */
-    for(i = 0; i < MAX_QH; i++) {
-        get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh,
-                   sizeof(EHCIqh) >> 2);
-        ehci_trace_qh(NULL, NLPTR_GET(entry), &qh);
-
-        if (qh.epchar & QH_EPCHAR_H) {
-            if (async) {
-                entry |= (NLPTR_TYPE_QH << 1);
-            }
-
-            ehci_set_fetch_addr(ehci, async, entry);
-            ehci_set_state(ehci, async, EST_FETCHENTRY);
-            again = 1;
-            goto out;
-        }
-
-        entry = qh.next;
-        if (entry == ehci->asynclistaddr) {
-            break;
-        }
-    }
-
-    /* no head found for list. */
-
-    ehci_set_state(ehci, async, EST_ACTIVE);
-
-out:
-    return again;
-}
-
-
-/*  This state is the entry point for periodic schedule processing as
- *  well as being a continuation state for async processing.
- */
-static int ehci_state_fetchentry(EHCIState *ehci, int async)
-{
-    int again = 0;
-    uint32_t entry = ehci_get_fetch_addr(ehci, async);
-
-    if (NLPTR_TBIT(entry)) {
-        ehci_set_state(ehci, async, EST_ACTIVE);
-        goto out;
-    }
-
-    /* section 4.8, only QH in async schedule */
-    if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) {
-        fprintf(stderr, "non queue head request in async schedule\n");
-        return -1;
-    }
-
-    switch (NLPTR_TYPE_GET(entry)) {
-    case NLPTR_TYPE_QH:
-        ehci_set_state(ehci, async, EST_FETCHQH);
-        again = 1;
-        break;
-
-    case NLPTR_TYPE_ITD:
-        ehci_set_state(ehci, async, EST_FETCHITD);
-        again = 1;
-        break;
-
-    case NLPTR_TYPE_STITD:
-        ehci_set_state(ehci, async, EST_FETCHSITD);
-        again = 1;
-        break;
-
-    default:
-        /* TODO: handle FSTN type */
-        fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
-                "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
-        return -1;
-    }
-
-out:
-    return again;
-}
-
-static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
-{
-    uint32_t entry;
-    EHCIQueue *q;
-
-    entry = ehci_get_fetch_addr(ehci, async);
-    q = ehci_find_queue_by_qh(ehci, entry, async);
-    if (NULL == q) {
-        q = ehci_alloc_queue(ehci, async);
-    }
-    q->qhaddr = entry;
-    q->seen++;
-
-    if (q->seen > 1) {
-        /* we are going in circles -- stop processing */
-        ehci_set_state(ehci, async, EST_ACTIVE);
-        q = NULL;
-        goto out;
-    }
-
-    get_dwords(ehci, NLPTR_GET(q->qhaddr),
-               (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
-    ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
-
-    if (q->async == EHCI_ASYNC_INFLIGHT) {
-        /* I/O still in progress -- skip queue */
-        ehci_set_state(ehci, async, EST_HORIZONTALQH);
-        goto out;
-    }
-    if (q->async == EHCI_ASYNC_FINISHED) {
-        /* I/O finished -- continue processing queue */
-        trace_usb_ehci_queue_action(q, "resume");
-        ehci_set_state(ehci, async, EST_EXECUTING);
-        goto out;
-    }
-
-    if (async && (q->qh.epchar & QH_EPCHAR_H)) {
-
-        /*  EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
-        if (ehci->usbsts & USBSTS_REC) {
-            ehci_clear_usbsts(ehci, USBSTS_REC);
-        } else {
-            DPRINTF("FETCHQH:  QH 0x%08x. H-bit set, reclamation status reset"
-                       " - done processing\n", q->qhaddr);
-            ehci_set_state(ehci, async, EST_ACTIVE);
-            q = NULL;
-            goto out;
-        }
-    }
-
-#if EHCI_DEBUG
-    if (q->qhaddr != q->qh.next) {
-    DPRINTF("FETCHQH:  QH 0x%08x (h %x halt %x active %x) next 0x%08x\n",
-               q->qhaddr,
-               q->qh.epchar & QH_EPCHAR_H,
-               q->qh.token & QTD_TOKEN_HALT,
-               q->qh.token & QTD_TOKEN_ACTIVE,
-               q->qh.next);
-    }
-#endif
-
-    if (q->qh.token & QTD_TOKEN_HALT) {
-        ehci_set_state(ehci, async, EST_HORIZONTALQH);
-
-    } else if ((q->qh.token & QTD_TOKEN_ACTIVE) &&
-               (NLPTR_TBIT(q->qh.current_qtd) == 0)) {
-        q->qtdaddr = q->qh.current_qtd;
-        ehci_set_state(ehci, async, EST_FETCHQTD);
-
-    } else {
-        /*  EHCI spec version 1.0 Section 4.10.2 */
-        ehci_set_state(ehci, async, EST_ADVANCEQUEUE);
-    }
-
-out:
-    return q;
-}
-
-static int ehci_state_fetchitd(EHCIState *ehci, int async)
-{
-    uint32_t entry;
-    EHCIitd itd;
-
-    assert(!async);
-    entry = ehci_get_fetch_addr(ehci, async);
-
-    get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
-               sizeof(EHCIitd) >> 2);
-    ehci_trace_itd(ehci, entry, &itd);
-
-    if (ehci_process_itd(ehci, &itd) != 0) {
-        return -1;
-    }
-
-    put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
-               sizeof(EHCIitd) >> 2);
-    ehci_set_fetch_addr(ehci, async, itd.next);
-    ehci_set_state(ehci, async, EST_FETCHENTRY);
-
-    return 1;
-}
-
-static int ehci_state_fetchsitd(EHCIState *ehci, int async)
-{
-    uint32_t entry;
-    EHCIsitd sitd;
-
-    assert(!async);
-    entry = ehci_get_fetch_addr(ehci, async);
-
-    get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd,
-               sizeof(EHCIsitd) >> 2);
-    ehci_trace_sitd(ehci, entry, &sitd);
-
-    if (!(sitd.results & SITD_RESULTS_ACTIVE)) {
-        /* siTD is not active, nothing to do */;
-    } else {
-        /* TODO: split transfers are not implemented */
-        fprintf(stderr, "WARNING: Skipping active siTD\n");
-    }
-
-    ehci_set_fetch_addr(ehci, async, sitd.next);
-    ehci_set_state(ehci, async, EST_FETCHENTRY);
-    return 1;
-}
-
-/* Section 4.10.2 - paragraph 3 */
-static int ehci_state_advqueue(EHCIQueue *q, int async)
-{
-#if 0
-    /* TO-DO: 4.10.2 - paragraph 2
-     * if I-bit is set to 1 and QH is not active
-     * go to horizontal QH
-     */
-    if (I-bit set) {
-        ehci_set_state(ehci, async, EST_HORIZONTALQH);
-        goto out;
-    }
-#endif
-
-    /*
-     * want data and alt-next qTD is valid
-     */
-    if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
-        (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
-        q->qtdaddr = q->qh.altnext_qtd;
-        ehci_set_state(q->ehci, async, EST_FETCHQTD);
-
-    /*
-     *  next qTD is valid
-     */
-    } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) {
-        q->qtdaddr = q->qh.next_qtd;
-        ehci_set_state(q->ehci, async, EST_FETCHQTD);
-
-    /*
-     *  no valid qTD, try next QH
-     */
-    } else {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
-    }
-
-    return 1;
-}
-
-/* Section 4.10.2 - paragraph 4 */
-static int ehci_state_fetchqtd(EHCIQueue *q, int async)
-{
-    int again = 0;
-
-    get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qtd,
-               sizeof(EHCIqtd) >> 2);
-    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd);
-
-    if (q->qtd.token & QTD_TOKEN_ACTIVE) {
-        ehci_set_state(q->ehci, async, EST_EXECUTE);
-        again = 1;
-    } else {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
-        again = 1;
-    }
-
-    return again;
-}
-
-static int ehci_state_horizqh(EHCIQueue *q, int async)
-{
-    int again = 0;
-
-    if (ehci_get_fetch_addr(q->ehci, async) != q->qh.next) {
-        ehci_set_fetch_addr(q->ehci, async, q->qh.next);
-        ehci_set_state(q->ehci, async, EST_FETCHENTRY);
-        again = 1;
-    } else {
-        ehci_set_state(q->ehci, async, EST_ACTIVE);
-    }
-
-    return again;
-}
-
-/*
- *  Write the qh back to guest physical memory.  This step isn't
- *  in the EHCI spec but we need to do it since we don't share
- *  physical memory with our guest VM.
- *
- *  The first three dwords are read-only for the EHCI, so skip them
- *  when writing back the qh.
- */
-static void ehci_flush_qh(EHCIQueue *q)
-{
-    uint32_t *qh = (uint32_t *) &q->qh;
-    uint32_t dwords = sizeof(EHCIqh) >> 2;
-    uint32_t addr = NLPTR_GET(q->qhaddr);
-
-    put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
-}
-
-static int ehci_state_execute(EHCIQueue *q, int async)
-{
-    int again = 0;
-
-    if (ehci_qh_do_overlay(q) != 0) {
-        return -1;
-    }
-
-    // TODO verify enough time remains in the uframe as in 4.4.1.1
-    // TODO write back ptr to async list when done or out of time
-    // TODO Windows does not seem to ever set the MULT field
-
-    if (!async) {
-        int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
-        if (!transactCtr) {
-            ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
-            again = 1;
-            goto out;
-        }
-    }
-
-    if (async) {
-        ehci_set_usbsts(q->ehci, USBSTS_REC);
-    }
-
-    q->usb_status = ehci_execute(q);
-    if (q->usb_status == USB_RET_PROCERR) {
-        again = -1;
-        goto out;
-    }
-    if (q->usb_status == USB_RET_ASYNC) {
-        ehci_flush_qh(q);
-        trace_usb_ehci_queue_action(q, "suspend");
-        q->async = EHCI_ASYNC_INFLIGHT;
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
-        again = 1;
-        goto out;
-    }
-
-    ehci_set_state(q->ehci, async, EST_EXECUTING);
-    again = 1;
-
-out:
-    return again;
-}
-
-static int ehci_state_executing(EHCIQueue *q, int async)
-{
-    int again = 0;
-
-    ehci_execute_complete(q);
-    if (q->usb_status == USB_RET_ASYNC) {
-        goto out;
-    }
-    if (q->usb_status == USB_RET_PROCERR) {
-        again = -1;
-        goto out;
-    }
-
-    // 4.10.3
-    if (!async) {
-        int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
-        transactCtr--;
-        set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT);
-        // 4.10.3, bottom of page 82, should exit this state when transaction
-        // counter decrements to 0
-    }
-
-    /* 4.10.5 */
-    if (q->usb_status == USB_RET_NAK) {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
-    } else {
-        ehci_set_state(q->ehci, async, EST_WRITEBACK);
-    }
-
-    again = 1;
-
-out:
-    ehci_flush_qh(q);
-    return again;
-}
-
-
-static int ehci_state_writeback(EHCIQueue *q, int async)
-{
-    int again = 0;
-
-    /*  Write back the QTD from the QH area */
-    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd);
-    put_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qh.next_qtd,
-               sizeof(EHCIqtd) >> 2);
-
-    /*
-     * EHCI specs say go horizontal here.
-     *
-     * We can also advance the queue here for performance reasons.  We
-     * need to take care to only take that shortcut in case we've
-     * processed the qtd just written back without errors, i.e. halt
-     * bit is clear.
-     */
-    if (q->qh.token & QTD_TOKEN_HALT) {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
-        again = 1;
-    } else {
-        ehci_set_state(q->ehci, async, EST_ADVANCEQUEUE);
-        again = 1;
-    }
-    return again;
-}
-
-/*
- * This is the state machine that is common to both async and periodic
- */
-
-static void ehci_advance_state(EHCIState *ehci,
-                               int async)
-{
-    EHCIQueue *q = NULL;
-    int again;
-    int iter = 0;
-
-    do {
-        if (ehci_get_state(ehci, async) == EST_FETCHQH) {
-            iter++;
-            /* if we are roaming a lot of QH without executing a qTD
-             * something is wrong with the linked list. TO-DO: why is
-             * this hack needed?
-             */
-            assert(iter < MAX_ITERATIONS);
-#if 0
-            if (iter > MAX_ITERATIONS) {
-                DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n");
-                ehci_set_state(ehci, async, EST_ACTIVE);
-                break;
-            }
-#endif
-        }
-        switch(ehci_get_state(ehci, async)) {
-        case EST_WAITLISTHEAD:
-            again = ehci_state_waitlisthead(ehci, async);
-            break;
-
-        case EST_FETCHENTRY:
-            again = ehci_state_fetchentry(ehci, async);
-            break;
-
-        case EST_FETCHQH:
-            q = ehci_state_fetchqh(ehci, async);
-            again = q ? 1 : 0;
-            break;
-
-        case EST_FETCHITD:
-            again = ehci_state_fetchitd(ehci, async);
-            break;
-
-        case EST_FETCHSITD:
-            again = ehci_state_fetchsitd(ehci, async);
-            break;
-
-        case EST_ADVANCEQUEUE:
-            again = ehci_state_advqueue(q, async);
-            break;
-
-        case EST_FETCHQTD:
-            again = ehci_state_fetchqtd(q, async);
-            break;
-
-        case EST_HORIZONTALQH:
-            again = ehci_state_horizqh(q, async);
-            break;
-
-        case EST_EXECUTE:
-            iter = 0;
-            again = ehci_state_execute(q, async);
-            break;
-
-        case EST_EXECUTING:
-            assert(q != NULL);
-            again = ehci_state_executing(q, async);
-            break;
-
-        case EST_WRITEBACK:
-            assert(q != NULL);
-            again = ehci_state_writeback(q, async);
-            break;
-
-        default:
-            fprintf(stderr, "Bad state!\n");
-            again = -1;
-            assert(0);
-            break;
-        }
-
-        if (again < 0) {
-            fprintf(stderr, "processing error - resetting ehci HC\n");
-            ehci_reset(ehci);
-            again = 0;
-            assert(0);
-        }
-    }
-    while (again);
-
-    ehci_commit_interrupt(ehci);
-}
-
-static void ehci_advance_async_state(EHCIState *ehci)
-{
-    const int async = 1;
-
-    switch(ehci_get_state(ehci, async)) {
-    case EST_INACTIVE:
-        if (!(ehci->usbcmd & USBCMD_ASE)) {
-            break;
-        }
-        ehci_set_usbsts(ehci, USBSTS_ASS);
-        ehci_set_state(ehci, async, EST_ACTIVE);
-        // No break, fall through to ACTIVE
-
-    case EST_ACTIVE:
-        if ( !(ehci->usbcmd & USBCMD_ASE)) {
-            ehci_queues_rip_all(ehci, async);
-            ehci_clear_usbsts(ehci, USBSTS_ASS);
-            ehci_set_state(ehci, async, EST_INACTIVE);
-            break;
-        }
-
-        /* make sure guest has acknowledged the doorbell interrupt */
-        /* TO-DO: is this really needed? */
-        if (ehci->usbsts & USBSTS_IAA) {
-            DPRINTF("IAA status bit still set.\n");
-            break;
-        }
-
-        /* check that address register has been set */
-        if (ehci->asynclistaddr == 0) {
-            break;
-        }
-
-        ehci_set_state(ehci, async, EST_WAITLISTHEAD);
-        ehci_advance_state(ehci, async);
-
-        /* If the doorbell is set, the guest wants to make a change to the
-         * schedule. The host controller needs to release cached data.
-         * (section 4.8.2)
-         */
-        if (ehci->usbcmd & USBCMD_IAAD) {
-            /* Remove all unseen qhs from the async qhs queue */
-            ehci_queues_rip_unused(ehci, async, 1);
-            DPRINTF("ASYNC: doorbell request acknowledged\n");
-            ehci->usbcmd &= ~USBCMD_IAAD;
-            ehci_set_interrupt(ehci, USBSTS_IAA);
-        }
-        break;
-
-    default:
-        /* this should only be due to a developer mistake */
-        fprintf(stderr, "ehci: Bad asynchronous state %d. "
-                "Resetting to active\n", ehci->astate);
-        assert(0);
-    }
-}
-
-static void ehci_advance_periodic_state(EHCIState *ehci)
-{
-    uint32_t entry;
-    uint32_t list;
-    const int async = 0;
-
-    // 4.6
-
-    switch(ehci_get_state(ehci, async)) {
-    case EST_INACTIVE:
-        if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
-            ehci_set_usbsts(ehci, USBSTS_PSS);
-            ehci_set_state(ehci, async, EST_ACTIVE);
-            // No break, fall through to ACTIVE
-        } else
-            break;
-
-    case EST_ACTIVE:
-        if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
-            ehci_queues_rip_all(ehci, async);
-            ehci_clear_usbsts(ehci, USBSTS_PSS);
-            ehci_set_state(ehci, async, EST_INACTIVE);
-            break;
-        }
-
-        list = ehci->periodiclistbase & 0xfffff000;
-        /* check that register has been set */
-        if (list == 0) {
-            break;
-        }
-        list |= ((ehci->frindex & 0x1ff8) >> 1);
-
-        pci_dma_read(&ehci->dev, list, &entry, sizeof entry);
-        entry = le32_to_cpu(entry);
-
-        DPRINTF("PERIODIC state adv fr=%d.  [%08X] -> %08X\n",
-                ehci->frindex / 8, list, entry);
-        ehci_set_fetch_addr(ehci, async,entry);
-        ehci_set_state(ehci, async, EST_FETCHENTRY);
-        ehci_advance_state(ehci, async);
-        ehci_queues_rip_unused(ehci, async, 0);
-        break;
-
-    default:
-        /* this should only be due to a developer mistake */
-        fprintf(stderr, "ehci: Bad periodic state %d. "
-                "Resetting to active\n", ehci->pstate);
-        assert(0);
-    }
-}
-
-static void ehci_frame_timer(void *opaque)
-{
-    EHCIState *ehci = opaque;
-    int64_t expire_time, t_now;
-    uint64_t ns_elapsed;
-    int frames;
-    int i;
-    int skipped_frames = 0;
-
-    t_now = qemu_get_clock_ns(vm_clock);
-    expire_time = t_now + (get_ticks_per_sec() / ehci->freq);
-
-    ns_elapsed = t_now - ehci->last_run_ns;
-    frames = ns_elapsed / FRAME_TIMER_NS;
-
-    for (i = 0; i < frames; i++) {
-        if ( !(ehci->usbsts & USBSTS_HALT)) {
-            if (ehci->isoch_pause <= 0) {
-                ehci->frindex += 8;
-            }
-
-            if (ehci->frindex > 0x00001fff) {
-                ehci->frindex = 0;
-                ehci_set_interrupt(ehci, USBSTS_FLR);
-            }
-
-            ehci->sofv = (ehci->frindex - 1) >> 3;
-            ehci->sofv &= 0x000003ff;
-        }
-
-        if (frames - i > ehci->maxframes) {
-            skipped_frames++;
-        } else {
-            ehci_advance_periodic_state(ehci);
-        }
-
-        ehci->last_run_ns += FRAME_TIMER_NS;
-    }
-
-#if 0
-    if (skipped_frames) {
-        DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
-    }
-#endif
-
-    /*  Async is not inside loop since it executes everything it can once
-     *  called
-     */
-    ehci_advance_async_state(ehci);
-
-    qemu_mod_timer(ehci->frame_timer, expire_time);
-}
-
-
-static const MemoryRegionOps ehci_mem_ops = {
-    .old_mmio = {
-        .read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl },
-        .write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel },
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int usb_ehci_initfn(PCIDevice *dev);
-
-static USBPortOps ehci_port_ops = {
-    .attach = ehci_attach,
-    .detach = ehci_detach,
-    .child_detach = ehci_child_detach,
-    .wakeup = ehci_wakeup,
-    .complete = ehci_async_complete_packet,
-};
-
-static USBBusOps ehci_bus_ops = {
-    .register_companion = ehci_register_companion,
-};
-
-static const VMStateDescription vmstate_ehci = {
-    .name = "ehci",
-    .unmigratable = 1,
-};
-
-static Property ehci_properties[] = {
-    DEFINE_PROP_UINT32("freq",      EHCIState, freq, FRAME_TIMER_FREQ),
-    DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void ehci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_ehci_initfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */
-    k->revision = 0x10;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_ehci;
-    dc->props = ehci_properties;
-}
-
-static TypeInfo ehci_info = {
-    .name          = "usb-ehci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(EHCIState),
-    .class_init    = ehci_class_init,
-};
-
-static void ich9_ehci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_ehci_initfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1;
-    k->revision = 0x03;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_ehci;
-    dc->props = ehci_properties;
-}
-
-static TypeInfo ich9_ehci_info = {
-    .name          = "ich9-usb-ehci1",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(EHCIState),
-    .class_init    = ich9_ehci_class_init,
-};
-
-static int usb_ehci_initfn(PCIDevice *dev)
-{
-    EHCIState *s = DO_UPCAST(EHCIState, dev, dev);
-    uint8_t *pci_conf = s->dev.config;
-    int i;
-
-    pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
-
-    /* capabilities pointer */
-    pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
-    //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
-
-    pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */
-    pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
-    pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
-
-    // pci_conf[0x50] = 0x01; // power management caps
-
-    pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); // release number (2.1.4)
-    pci_set_byte(&pci_conf[0x61], 0x20);  // frame length adjustment (2.1.5)
-    pci_set_word(&pci_conf[0x62], 0x00);  // port wake up capability (2.1.6)
-
-    pci_conf[0x64] = 0x00;
-    pci_conf[0x65] = 0x00;
-    pci_conf[0x66] = 0x00;
-    pci_conf[0x67] = 0x00;
-    pci_conf[0x68] = 0x01;
-    pci_conf[0x69] = 0x00;
-    pci_conf[0x6a] = 0x00;
-    pci_conf[0x6b] = 0x00;  // USBLEGSUP
-    pci_conf[0x6c] = 0x00;
-    pci_conf[0x6d] = 0x00;
-    pci_conf[0x6e] = 0x00;
-    pci_conf[0x6f] = 0xc0;  // USBLEFCTLSTS
-
-    // 2.2 host controller interface version
-    s->mmio[0x00] = (uint8_t) OPREGBASE;
-    s->mmio[0x01] = 0x00;
-    s->mmio[0x02] = 0x00;
-    s->mmio[0x03] = 0x01;        // HC version
-    s->mmio[0x04] = NB_PORTS;    // Number of downstream ports
-    s->mmio[0x05] = 0x00;        // No companion ports at present
-    s->mmio[0x06] = 0x00;
-    s->mmio[0x07] = 0x00;
-    s->mmio[0x08] = 0x80;        // We can cache whole frame, not 64-bit capable
-    s->mmio[0x09] = 0x68;        // EECP
-    s->mmio[0x0a] = 0x00;
-    s->mmio[0x0b] = 0x00;
-
-    s->irq = s->dev.irq[3];
-
-    usb_bus_new(&s->bus, &ehci_bus_ops, &s->dev.qdev);
-    for(i = 0; i < NB_PORTS; i++) {
-        usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
-                          USB_SPEED_MASK_HIGH);
-        s->ports[i].dev = 0;
-    }
-
-    s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
-    QTAILQ_INIT(&s->aqueues);
-    QTAILQ_INIT(&s->pqueues);
-
-    qemu_register_reset(ehci_reset, s);
-
-    memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
-    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
-
-    return 0;
-}
-
-static void ehci_register_types(void)
-{
-    type_register_static(&ehci_info);
-    type_register_static(&ich9_ehci_info);
-}
-
-type_init(ehci_register_types)
-
-/*
- * vim: expandtab ts=4
- */
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
deleted file mode 100644 (file)
index 37bca78..0000000
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
- * QEMU USB HID devices
- *
- * Copyright (c) 2005 Fabrice Bellard
- * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
- *
- * 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 "hw.h"
-#include "console.h"
-#include "usb.h"
-#include "usb-desc.h"
-#include "qemu-timer.h"
-#include "hid.h"
-
-/* HID interface requests */
-#define GET_REPORT   0xa101
-#define GET_IDLE     0xa102
-#define GET_PROTOCOL 0xa103
-#define SET_REPORT   0x2109
-#define SET_IDLE     0x210a
-#define SET_PROTOCOL 0x210b
-
-/* HID descriptor types */
-#define USB_DT_HID    0x21
-#define USB_DT_REPORT 0x22
-#define USB_DT_PHY    0x23
-
-typedef struct USBHIDState {
-    USBDevice dev;
-    USBEndpoint *intr;
-    HIDState hid;
-} USBHIDState;
-
-enum {
-    STR_MANUFACTURER = 1,
-    STR_PRODUCT_MOUSE,
-    STR_PRODUCT_TABLET,
-    STR_PRODUCT_KEYBOARD,
-    STR_SERIALNUMBER,
-    STR_CONFIG_MOUSE,
-    STR_CONFIG_TABLET,
-    STR_CONFIG_KEYBOARD,
-};
-
-static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
-    [STR_PRODUCT_MOUSE]    = "QEMU USB Mouse",
-    [STR_PRODUCT_TABLET]   = "QEMU USB Tablet",
-    [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard",
-    [STR_SERIALNUMBER]     = "42", /* == remote wakeup works */
-    [STR_CONFIG_MOUSE]     = "HID Mouse",
-    [STR_CONFIG_TABLET]    = "HID Tablet",
-    [STR_CONFIG_KEYBOARD]  = "HID Keyboard",
-};
-
-static const USBDescIface desc_iface_mouse = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 1,
-    .bInterfaceClass               = USB_CLASS_HID,
-    .bInterfaceSubClass            = 0x01, /* boot */
-    .bInterfaceProtocol            = 0x02,
-    .ndesc                         = 1,
-    .descs = (USBDescOther[]) {
-        {
-            /* HID descriptor */
-            .data = (uint8_t[]) {
-                0x09,          /*  u8  bLength */
-                USB_DT_HID,    /*  u8  bDescriptorType */
-                0x01, 0x00,    /*  u16 HID_class */
-                0x00,          /*  u8  country_code */
-                0x01,          /*  u8  num_descriptors */
-                USB_DT_REPORT, /*  u8  type: Report */
-                52, 0,         /*  u16 len */
-            },
-        },
-    },
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .wMaxPacketSize        = 4,
-            .bInterval             = 0x0a,
-        },
-    },
-};
-
-static const USBDescIface desc_iface_tablet = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 1,
-    .bInterfaceClass               = USB_CLASS_HID,
-    .bInterfaceProtocol            = 0x02,
-    .ndesc                         = 1,
-    .descs = (USBDescOther[]) {
-        {
-            /* HID descriptor */
-            .data = (uint8_t[]) {
-                0x09,          /*  u8  bLength */
-                USB_DT_HID,    /*  u8  bDescriptorType */
-                0x01, 0x00,    /*  u16 HID_class */
-                0x00,          /*  u8  country_code */
-                0x01,          /*  u8  num_descriptors */
-                USB_DT_REPORT, /*  u8  type: Report */
-                74, 0,         /*  u16 len */
-            },
-        },
-    },
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .wMaxPacketSize        = 8,
-            .bInterval             = 0x0a,
-        },
-    },
-};
-
-static const USBDescIface desc_iface_keyboard = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 1,
-    .bInterfaceClass               = USB_CLASS_HID,
-    .bInterfaceSubClass            = 0x01, /* boot */
-    .bInterfaceProtocol            = 0x01, /* keyboard */
-    .ndesc                         = 1,
-    .descs = (USBDescOther[]) {
-        {
-            /* HID descriptor */
-            .data = (uint8_t[]) {
-                0x09,          /*  u8  bLength */
-                USB_DT_HID,    /*  u8  bDescriptorType */
-                0x11, 0x01,    /*  u16 HID_class */
-                0x00,          /*  u8  country_code */
-                0x01,          /*  u8  num_descriptors */
-                USB_DT_REPORT, /*  u8  type: Report */
-                0x3f, 0,       /*  u16 len */
-            },
-        },
-    },
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .wMaxPacketSize        = 8,
-            .bInterval             = 0x0a,
-        },
-    },
-};
-
-static const USBDescDevice desc_device_mouse = {
-    .bcdUSB                        = 0x0100,
-    .bMaxPacketSize0               = 8,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .iConfiguration        = STR_CONFIG_MOUSE,
-            .bmAttributes          = 0xa0,
-            .bMaxPower             = 50,
-            .nif = 1,
-            .ifs = &desc_iface_mouse,
-        },
-    },
-};
-
-static const USBDescDevice desc_device_tablet = {
-    .bcdUSB                        = 0x0100,
-    .bMaxPacketSize0               = 8,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .iConfiguration        = STR_CONFIG_TABLET,
-            .bmAttributes          = 0xa0,
-            .bMaxPower             = 50,
-            .nif = 1,
-            .ifs = &desc_iface_tablet,
-        },
-    },
-};
-
-static const USBDescDevice desc_device_keyboard = {
-    .bcdUSB                        = 0x0100,
-    .bMaxPacketSize0               = 8,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .iConfiguration        = STR_CONFIG_KEYBOARD,
-            .bmAttributes          = 0xa0,
-            .bMaxPower             = 50,
-            .nif = 1,
-            .ifs = &desc_iface_keyboard,
-        },
-    },
-};
-
-static const USBDesc desc_mouse = {
-    .id = {
-        .idVendor          = 0x0627,
-        .idProduct         = 0x0001,
-        .bcdDevice         = 0,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT_MOUSE,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device_mouse,
-    .str  = desc_strings,
-};
-
-static const USBDesc desc_tablet = {
-    .id = {
-        .idVendor          = 0x0627,
-        .idProduct         = 0x0001,
-        .bcdDevice         = 0,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT_TABLET,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device_tablet,
-    .str  = desc_strings,
-};
-
-static const USBDesc desc_keyboard = {
-    .id = {
-        .idVendor          = 0x0627,
-        .idProduct         = 0x0001,
-        .bcdDevice         = 0,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT_KEYBOARD,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device_keyboard,
-    .str  = desc_strings,
-};
-
-static const uint8_t qemu_mouse_hid_report_descriptor[] = {
-    0x05, 0x01,                /* Usage Page (Generic Desktop) */
-    0x09, 0x02,                /* Usage (Mouse) */
-    0xa1, 0x01,                /* Collection (Application) */
-    0x09, 0x01,                /*   Usage (Pointer) */
-    0xa1, 0x00,                /*   Collection (Physical) */
-    0x05, 0x09,                /*     Usage Page (Button) */
-    0x19, 0x01,                /*     Usage Minimum (1) */
-    0x29, 0x03,                /*     Usage Maximum (3) */
-    0x15, 0x00,                /*     Logical Minimum (0) */
-    0x25, 0x01,                /*     Logical Maximum (1) */
-    0x95, 0x03,                /*     Report Count (3) */
-    0x75, 0x01,                /*     Report Size (1) */
-    0x81, 0x02,                /*     Input (Data, Variable, Absolute) */
-    0x95, 0x01,                /*     Report Count (1) */
-    0x75, 0x05,                /*     Report Size (5) */
-    0x81, 0x01,                /*     Input (Constant) */
-    0x05, 0x01,                /*     Usage Page (Generic Desktop) */
-    0x09, 0x30,                /*     Usage (X) */
-    0x09, 0x31,                /*     Usage (Y) */
-    0x09, 0x38,                /*     Usage (Wheel) */
-    0x15, 0x81,                /*     Logical Minimum (-0x7f) */
-    0x25, 0x7f,                /*     Logical Maximum (0x7f) */
-    0x75, 0x08,                /*     Report Size (8) */
-    0x95, 0x03,                /*     Report Count (3) */
-    0x81, 0x06,                /*     Input (Data, Variable, Relative) */
-    0xc0,              /*   End Collection */
-    0xc0,              /* End Collection */
-};
-
-static const uint8_t qemu_tablet_hid_report_descriptor[] = {
-    0x05, 0x01,                /* Usage Page (Generic Desktop) */
-    0x09, 0x01,                /* Usage (Pointer) */
-    0xa1, 0x01,                /* Collection (Application) */
-    0x09, 0x01,                /*   Usage (Pointer) */
-    0xa1, 0x00,                /*   Collection (Physical) */
-    0x05, 0x09,                /*     Usage Page (Button) */
-    0x19, 0x01,                /*     Usage Minimum (1) */
-    0x29, 0x03,                /*     Usage Maximum (3) */
-    0x15, 0x00,                /*     Logical Minimum (0) */
-    0x25, 0x01,                /*     Logical Maximum (1) */
-    0x95, 0x03,                /*     Report Count (3) */
-    0x75, 0x01,                /*     Report Size (1) */
-    0x81, 0x02,                /*     Input (Data, Variable, Absolute) */
-    0x95, 0x01,                /*     Report Count (1) */
-    0x75, 0x05,                /*     Report Size (5) */
-    0x81, 0x01,                /*     Input (Constant) */
-    0x05, 0x01,                /*     Usage Page (Generic Desktop) */
-    0x09, 0x30,                /*     Usage (X) */
-    0x09, 0x31,                /*     Usage (Y) */
-    0x15, 0x00,                /*     Logical Minimum (0) */
-    0x26, 0xff, 0x7f,  /*     Logical Maximum (0x7fff) */
-    0x35, 0x00,                /*     Physical Minimum (0) */
-    0x46, 0xff, 0x7f,  /*     Physical Maximum (0x7fff) */
-    0x75, 0x10,                /*     Report Size (16) */
-    0x95, 0x02,                /*     Report Count (2) */
-    0x81, 0x02,                /*     Input (Data, Variable, Absolute) */
-    0x05, 0x01,                /*     Usage Page (Generic Desktop) */
-    0x09, 0x38,                /*     Usage (Wheel) */
-    0x15, 0x81,                /*     Logical Minimum (-0x7f) */
-    0x25, 0x7f,                /*     Logical Maximum (0x7f) */
-    0x35, 0x00,                /*     Physical Minimum (same as logical) */
-    0x45, 0x00,                /*     Physical Maximum (same as logical) */
-    0x75, 0x08,                /*     Report Size (8) */
-    0x95, 0x01,                /*     Report Count (1) */
-    0x81, 0x06,                /*     Input (Data, Variable, Relative) */
-    0xc0,              /*   End Collection */
-    0xc0,              /* End Collection */
-};
-
-static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
-    0x05, 0x01,                /* Usage Page (Generic Desktop) */
-    0x09, 0x06,                /* Usage (Keyboard) */
-    0xa1, 0x01,                /* Collection (Application) */
-    0x75, 0x01,                /*   Report Size (1) */
-    0x95, 0x08,                /*   Report Count (8) */
-    0x05, 0x07,                /*   Usage Page (Key Codes) */
-    0x19, 0xe0,                /*   Usage Minimum (224) */
-    0x29, 0xe7,                /*   Usage Maximum (231) */
-    0x15, 0x00,                /*   Logical Minimum (0) */
-    0x25, 0x01,                /*   Logical Maximum (1) */
-    0x81, 0x02,                /*   Input (Data, Variable, Absolute) */
-    0x95, 0x01,                /*   Report Count (1) */
-    0x75, 0x08,                /*   Report Size (8) */
-    0x81, 0x01,                /*   Input (Constant) */
-    0x95, 0x05,                /*   Report Count (5) */
-    0x75, 0x01,                /*   Report Size (1) */
-    0x05, 0x08,                /*   Usage Page (LEDs) */
-    0x19, 0x01,                /*   Usage Minimum (1) */
-    0x29, 0x05,                /*   Usage Maximum (5) */
-    0x91, 0x02,                /*   Output (Data, Variable, Absolute) */
-    0x95, 0x01,                /*   Report Count (1) */
-    0x75, 0x03,                /*   Report Size (3) */
-    0x91, 0x01,                /*   Output (Constant) */
-    0x95, 0x06,                /*   Report Count (6) */
-    0x75, 0x08,                /*   Report Size (8) */
-    0x15, 0x00,                /*   Logical Minimum (0) */
-    0x25, 0xff,                /*   Logical Maximum (255) */
-    0x05, 0x07,                /*   Usage Page (Key Codes) */
-    0x19, 0x00,                /*   Usage Minimum (0) */
-    0x29, 0xff,                /*   Usage Maximum (255) */
-    0x81, 0x00,                /*   Input (Data, Array) */
-    0xc0,              /* End Collection */
-};
-
-static void usb_hid_changed(HIDState *hs)
-{
-    USBHIDState *us = container_of(hs, USBHIDState, hid);
-
-    usb_wakeup(us->intr);
-}
-
-static void usb_hid_handle_reset(USBDevice *dev)
-{
-    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
-
-    hid_reset(&us->hid);
-}
-
-static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
-    HIDState *hs = &us->hid;
-    int ret;
-
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    ret = 0;
-    switch (request) {
-        /* hid specific requests */
-    case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
-        switch (value >> 8) {
-        case 0x22:
-            if (hs->kind == HID_MOUSE) {
-               memcpy(data, qemu_mouse_hid_report_descriptor,
-                      sizeof(qemu_mouse_hid_report_descriptor));
-               ret = sizeof(qemu_mouse_hid_report_descriptor);
-            } else if (hs->kind == HID_TABLET) {
-                memcpy(data, qemu_tablet_hid_report_descriptor,
-                      sizeof(qemu_tablet_hid_report_descriptor));
-               ret = sizeof(qemu_tablet_hid_report_descriptor);
-            } else if (hs->kind == HID_KEYBOARD) {
-                memcpy(data, qemu_keyboard_hid_report_descriptor,
-                       sizeof(qemu_keyboard_hid_report_descriptor));
-                ret = sizeof(qemu_keyboard_hid_report_descriptor);
-            }
-            break;
-        default:
-            goto fail;
-        }
-        break;
-    case GET_REPORT:
-        if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
-            ret = hid_pointer_poll(hs, data, length);
-        } else if (hs->kind == HID_KEYBOARD) {
-            ret = hid_keyboard_poll(hs, data, length);
-        }
-        break;
-    case SET_REPORT:
-        if (hs->kind == HID_KEYBOARD) {
-            ret = hid_keyboard_write(hs, data, length);
-        } else {
-            goto fail;
-        }
-        break;
-    case GET_PROTOCOL:
-        if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
-            goto fail;
-        }
-        ret = 1;
-        data[0] = hs->protocol;
-        break;
-    case SET_PROTOCOL:
-        if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
-            goto fail;
-        }
-        ret = 0;
-        hs->protocol = value;
-        break;
-    case GET_IDLE:
-        ret = 1;
-        data[0] = hs->idle;
-        break;
-    case SET_IDLE:
-        hs->idle = (uint8_t) (value >> 8);
-        hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock));
-        if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
-            hid_pointer_activate(hs);
-        }
-        ret = 0;
-        break;
-    default:
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
-    HIDState *hs = &us->hid;
-    uint8_t buf[p->iov.size];
-    int ret = 0;
-
-    switch (p->pid) {
-    case USB_TOKEN_IN:
-        if (p->ep->nr == 1) {
-            int64_t curtime = qemu_get_clock_ns(vm_clock);
-            if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
-                hid_pointer_activate(hs);
-            }
-            if (!hid_has_events(hs) &&
-                (!hs->idle || hs->next_idle_clock - curtime > 0)) {
-                return USB_RET_NAK;
-            }
-            hid_set_next_idle(hs, curtime);
-            if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
-                ret = hid_pointer_poll(hs, buf, p->iov.size);
-            } else if (hs->kind == HID_KEYBOARD) {
-                ret = hid_keyboard_poll(hs, buf, p->iov.size);
-            }
-            usb_packet_copy(p, buf, ret);
-        } else {
-            goto fail;
-        }
-        break;
-    case USB_TOKEN_OUT:
-    default:
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static void usb_hid_handle_destroy(USBDevice *dev)
-{
-    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
-
-    hid_free(&us->hid);
-}
-
-static int usb_hid_initfn(USBDevice *dev, int kind)
-{
-    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
-
-    usb_desc_init(dev);
-    us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
-    hid_init(&us->hid, kind, usb_hid_changed);
-    return 0;
-}
-
-static int usb_tablet_initfn(USBDevice *dev)
-{
-    return usb_hid_initfn(dev, HID_TABLET);
-}
-
-static int usb_mouse_initfn(USBDevice *dev)
-{
-    return usb_hid_initfn(dev, HID_MOUSE);
-}
-
-static int usb_keyboard_initfn(USBDevice *dev)
-{
-    return usb_hid_initfn(dev, HID_KEYBOARD);
-}
-
-static int usb_ptr_post_load(void *opaque, int version_id)
-{
-    USBHIDState *s = opaque;
-
-    if (s->dev.remote_wakeup) {
-        hid_pointer_activate(&s->hid);
-    }
-    return 0;
-}
-
-static const VMStateDescription vmstate_usb_ptr = {
-    .name = "usb-ptr",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .post_load = usb_ptr_post_load,
-    .fields = (VMStateField []) {
-        VMSTATE_USB_DEVICE(dev, USBHIDState),
-        VMSTATE_HID_POINTER_DEVICE(hid, USBHIDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_usb_kbd = {
-    .name = "usb-kbd",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField []) {
-        VMSTATE_USB_DEVICE(dev, USBHIDState),
-        VMSTATE_HID_KEYBOARD_DEVICE(hid, USBHIDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void usb_hid_class_initfn(ObjectClass *klass, void *data)
-{
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->handle_reset   = usb_hid_handle_reset;
-    uc->handle_control = usb_hid_handle_control;
-    uc->handle_data    = usb_hid_handle_data;
-    uc->handle_destroy = usb_hid_handle_destroy;
-}
-
-static void usb_tablet_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    usb_hid_class_initfn(klass, data);
-    uc->init           = usb_tablet_initfn;
-    uc->product_desc   = "QEMU USB Tablet";
-    uc->usb_desc       = &desc_tablet;
-    dc->vmsd = &vmstate_usb_ptr;
-}
-
-static TypeInfo usb_tablet_info = {
-    .name          = "usb-tablet",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHIDState),
-    .class_init    = usb_tablet_class_initfn,
-};
-
-static void usb_mouse_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    usb_hid_class_initfn(klass, data);
-    uc->init           = usb_mouse_initfn;
-    uc->product_desc   = "QEMU USB Mouse";
-    uc->usb_desc       = &desc_mouse;
-    dc->vmsd = &vmstate_usb_ptr;
-}
-
-static TypeInfo usb_mouse_info = {
-    .name          = "usb-mouse",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHIDState),
-    .class_init    = usb_mouse_class_initfn,
-};
-
-static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    usb_hid_class_initfn(klass, data);
-    uc->init           = usb_keyboard_initfn;
-    uc->product_desc   = "QEMU USB Keyboard";
-    uc->usb_desc       = &desc_keyboard;
-    dc->vmsd = &vmstate_usb_kbd;
-}
-
-static TypeInfo usb_keyboard_info = {
-    .name          = "usb-kbd",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHIDState),
-    .class_init    = usb_keyboard_class_initfn,
-};
-
-static void usb_hid_register_types(void)
-{
-    type_register_static(&usb_tablet_info);
-    usb_legacy_register("usb-tablet", "tablet", NULL);
-    type_register_static(&usb_mouse_info);
-    usb_legacy_register("usb-mouse", "mouse", NULL);
-    type_register_static(&usb_keyboard_info);
-    usb_legacy_register("usb-kbd", "keyboard", NULL);
-}
-
-type_init(usb_hid_register_types)
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
deleted file mode 100644 (file)
index a12856e..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * QEMU USB HUB emulation
- *
- * Copyright (c) 2005 Fabrice Bellard
- *
- * 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 "usb.h"
-#include "usb-desc.h"
-
-//#define DEBUG
-
-#define NUM_PORTS 8
-
-typedef struct USBHubPort {
-    USBPort port;
-    uint16_t wPortStatus;
-    uint16_t wPortChange;
-} USBHubPort;
-
-typedef struct USBHubState {
-    USBDevice dev;
-    USBEndpoint *intr;
-    USBHubPort ports[NUM_PORTS];
-} USBHubState;
-
-#define ClearHubFeature                (0x2000 | USB_REQ_CLEAR_FEATURE)
-#define ClearPortFeature       (0x2300 | USB_REQ_CLEAR_FEATURE)
-#define GetHubDescriptor       (0xa000 | USB_REQ_GET_DESCRIPTOR)
-#define GetHubStatus           (0xa000 | USB_REQ_GET_STATUS)
-#define GetPortStatus          (0xa300 | USB_REQ_GET_STATUS)
-#define SetHubFeature          (0x2000 | USB_REQ_SET_FEATURE)
-#define SetPortFeature         (0x2300 | USB_REQ_SET_FEATURE)
-
-#define PORT_STAT_CONNECTION   0x0001
-#define PORT_STAT_ENABLE       0x0002
-#define PORT_STAT_SUSPEND      0x0004
-#define PORT_STAT_OVERCURRENT  0x0008
-#define PORT_STAT_RESET                0x0010
-#define PORT_STAT_POWER                0x0100
-#define PORT_STAT_LOW_SPEED    0x0200
-#define PORT_STAT_HIGH_SPEED    0x0400
-#define PORT_STAT_TEST          0x0800
-#define PORT_STAT_INDICATOR     0x1000
-
-#define PORT_STAT_C_CONNECTION 0x0001
-#define PORT_STAT_C_ENABLE     0x0002
-#define PORT_STAT_C_SUSPEND    0x0004
-#define PORT_STAT_C_OVERCURRENT        0x0008
-#define PORT_STAT_C_RESET      0x0010
-
-#define PORT_CONNECTION                0
-#define PORT_ENABLE            1
-#define PORT_SUSPEND           2
-#define PORT_OVERCURRENT       3
-#define PORT_RESET             4
-#define PORT_POWER             8
-#define PORT_LOWSPEED          9
-#define PORT_HIGHSPEED         10
-#define PORT_C_CONNECTION      16
-#define PORT_C_ENABLE          17
-#define PORT_C_SUSPEND         18
-#define PORT_C_OVERCURRENT     19
-#define PORT_C_RESET           20
-#define PORT_TEST               21
-#define PORT_INDICATOR          22
-
-/* same as Linux kernel root hubs */
-
-enum {
-    STR_MANUFACTURER = 1,
-    STR_PRODUCT,
-    STR_SERIALNUMBER,
-};
-
-static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
-    [STR_PRODUCT]      = "QEMU USB Hub",
-    [STR_SERIALNUMBER] = "314159",
-};
-
-static const USBDescIface desc_iface_hub = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 1,
-    .bInterfaceClass               = USB_CLASS_HUB,
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .wMaxPacketSize        = 1 + (NUM_PORTS + 7) / 8,
-            .bInterval             = 0xff,
-        },
-    }
-};
-
-static const USBDescDevice desc_device_hub = {
-    .bcdUSB                        = 0x0110,
-    .bDeviceClass                  = USB_CLASS_HUB,
-    .bMaxPacketSize0               = 8,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .bmAttributes          = 0xe0,
-            .nif = 1,
-            .ifs = &desc_iface_hub,
-        },
-    },
-};
-
-static const USBDesc desc_hub = {
-    .id = {
-        .idVendor          = 0x0409,
-        .idProduct         = 0x55aa,
-        .bcdDevice         = 0x0101,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device_hub,
-    .str  = desc_strings,
-};
-
-static const uint8_t qemu_hub_hub_descriptor[] =
-{
-       0x00,                   /*  u8  bLength; patched in later */
-       0x29,                   /*  u8  bDescriptorType; Hub-descriptor */
-       0x00,                   /*  u8  bNbrPorts; (patched later) */
-       0x0a,                   /* u16  wHubCharacteristics; */
-       0x00,                   /*   (per-port OC, no power switching) */
-       0x01,                   /*  u8  bPwrOn2pwrGood; 2ms */
-       0x00                    /*  u8  bHubContrCurrent; 0 mA */
-
-        /* DeviceRemovable and PortPwrCtrlMask patched in later */
-};
-
-static void usb_hub_attach(USBPort *port1)
-{
-    USBHubState *s = port1->opaque;
-    USBHubPort *port = &s->ports[port1->index];
-
-    port->wPortStatus |= PORT_STAT_CONNECTION;
-    port->wPortChange |= PORT_STAT_C_CONNECTION;
-    if (port->port.dev->speed == USB_SPEED_LOW) {
-        port->wPortStatus |= PORT_STAT_LOW_SPEED;
-    } else {
-        port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
-    }
-    usb_wakeup(s->intr);
-}
-
-static void usb_hub_detach(USBPort *port1)
-{
-    USBHubState *s = port1->opaque;
-    USBHubPort *port = &s->ports[port1->index];
-
-    usb_wakeup(s->intr);
-
-    /* Let upstream know the device on this port is gone */
-    s->dev.port->ops->child_detach(s->dev.port, port1->dev);
-
-    port->wPortStatus &= ~PORT_STAT_CONNECTION;
-    port->wPortChange |= PORT_STAT_C_CONNECTION;
-    if (port->wPortStatus & PORT_STAT_ENABLE) {
-        port->wPortStatus &= ~PORT_STAT_ENABLE;
-        port->wPortChange |= PORT_STAT_C_ENABLE;
-    }
-}
-
-static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
-{
-    USBHubState *s = port1->opaque;
-
-    /* Pass along upstream */
-    s->dev.port->ops->child_detach(s->dev.port, child);
-}
-
-static void usb_hub_wakeup(USBPort *port1)
-{
-    USBHubState *s = port1->opaque;
-    USBHubPort *port = &s->ports[port1->index];
-
-    if (port->wPortStatus & PORT_STAT_SUSPEND) {
-        port->wPortChange |= PORT_STAT_C_SUSPEND;
-        usb_wakeup(s->intr);
-    }
-}
-
-static void usb_hub_complete(USBPort *port, USBPacket *packet)
-{
-    USBHubState *s = port->opaque;
-
-    /*
-     * Just pass it along upstream for now.
-     *
-     * If we ever implement usb 2.0 split transactions this will
-     * become a little more complicated ...
-     *
-     * Can't use usb_packet_complete() here because packet->owner is
-     * cleared already, go call the ->complete() callback directly
-     * instead.
-     */
-    s->dev.port->ops->complete(s->dev.port, packet);
-}
-
-static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
-{
-    USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
-    USBHubPort *port;
-    USBDevice *downstream;
-    int i;
-
-    for (i = 0; i < NUM_PORTS; i++) {
-        port = &s->ports[i];
-        if (!(port->wPortStatus & PORT_STAT_ENABLE)) {
-            continue;
-        }
-        downstream = usb_find_device(&port->port, addr);
-        if (downstream != NULL) {
-            return downstream;
-        }
-    }
-    return NULL;
-}
-
-static void usb_hub_handle_reset(USBDevice *dev)
-{
-    USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
-    USBHubPort *port;
-    int i;
-
-    for (i = 0; i < NUM_PORTS; i++) {
-        port = s->ports + i;
-        port->wPortStatus = PORT_STAT_POWER;
-        port->wPortChange = 0;
-        if (port->port.dev && port->port.dev->attached) {
-            port->wPortStatus |= PORT_STAT_CONNECTION;
-            port->wPortChange |= PORT_STAT_C_CONNECTION;
-            if (port->port.dev->speed == USB_SPEED_LOW) {
-                port->wPortStatus |= PORT_STAT_LOW_SPEED;
-            }
-        }
-    }
-}
-
-static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    USBHubState *s = (USBHubState *)dev;
-    int ret;
-
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    switch(request) {
-    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
-        if (value == 0 && index != 0x81) { /* clear ep halt */
-            goto fail;
-        }
-        ret = 0;
-        break;
-        /* usb specific requests */
-    case GetHubStatus:
-        data[0] = 0;
-        data[1] = 0;
-        data[2] = 0;
-        data[3] = 0;
-        ret = 4;
-        break;
-    case GetPortStatus:
-        {
-            unsigned int n = index - 1;
-            USBHubPort *port;
-            if (n >= NUM_PORTS) {
-                goto fail;
-            }
-            port = &s->ports[n];
-            data[0] = port->wPortStatus;
-            data[1] = port->wPortStatus >> 8;
-            data[2] = port->wPortChange;
-            data[3] = port->wPortChange >> 8;
-            ret = 4;
-        }
-        break;
-    case SetHubFeature:
-    case ClearHubFeature:
-        if (value == 0 || value == 1) {
-        } else {
-            goto fail;
-        }
-        ret = 0;
-        break;
-    case SetPortFeature:
-        {
-            unsigned int n = index - 1;
-            USBHubPort *port;
-            USBDevice *dev;
-            if (n >= NUM_PORTS) {
-                goto fail;
-            }
-            port = &s->ports[n];
-            dev = port->port.dev;
-            switch(value) {
-            case PORT_SUSPEND:
-                port->wPortStatus |= PORT_STAT_SUSPEND;
-                break;
-            case PORT_RESET:
-                if (dev && dev->attached) {
-                    usb_device_reset(dev);
-                    port->wPortChange |= PORT_STAT_C_RESET;
-                    /* set enable bit */
-                    port->wPortStatus |= PORT_STAT_ENABLE;
-                }
-                break;
-            case PORT_POWER:
-                break;
-            default:
-                goto fail;
-            }
-            ret = 0;
-        }
-        break;
-    case ClearPortFeature:
-        {
-            unsigned int n = index - 1;
-            USBHubPort *port;
-
-            if (n >= NUM_PORTS) {
-                goto fail;
-            }
-            port = &s->ports[n];
-            switch(value) {
-            case PORT_ENABLE:
-                port->wPortStatus &= ~PORT_STAT_ENABLE;
-                break;
-            case PORT_C_ENABLE:
-                port->wPortChange &= ~PORT_STAT_C_ENABLE;
-                break;
-            case PORT_SUSPEND:
-                port->wPortStatus &= ~PORT_STAT_SUSPEND;
-                break;
-            case PORT_C_SUSPEND:
-                port->wPortChange &= ~PORT_STAT_C_SUSPEND;
-                break;
-            case PORT_C_CONNECTION:
-                port->wPortChange &= ~PORT_STAT_C_CONNECTION;
-                break;
-            case PORT_C_OVERCURRENT:
-                port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
-                break;
-            case PORT_C_RESET:
-                port->wPortChange &= ~PORT_STAT_C_RESET;
-                break;
-            default:
-                goto fail;
-            }
-            ret = 0;
-        }
-        break;
-    case GetHubDescriptor:
-        {
-            unsigned int n, limit, var_hub_size = 0;
-            memcpy(data, qemu_hub_hub_descriptor,
-                   sizeof(qemu_hub_hub_descriptor));
-            data[2] = NUM_PORTS;
-
-            /* fill DeviceRemovable bits */
-            limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
-            for (n = 7; n < limit; n++) {
-                data[n] = 0x00;
-                var_hub_size++;
-            }
-
-            /* fill PortPwrCtrlMask bits */
-            limit = limit + ((NUM_PORTS + 7) / 8);
-            for (;n < limit; n++) {
-                data[n] = 0xff;
-                var_hub_size++;
-            }
-
-            ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
-            data[0] = ret;
-            break;
-        }
-    default:
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBHubState *s = (USBHubState *)dev;
-    int ret;
-
-    switch(p->pid) {
-    case USB_TOKEN_IN:
-        if (p->ep->nr == 1) {
-            USBHubPort *port;
-            unsigned int status;
-            uint8_t buf[4];
-            int i, n;
-            n = (NUM_PORTS + 1 + 7) / 8;
-            if (p->iov.size == 1) { /* FreeBSD workaround */
-                n = 1;
-            } else if (n > p->iov.size) {
-                return USB_RET_BABBLE;
-            }
-            status = 0;
-            for(i = 0; i < NUM_PORTS; i++) {
-                port = &s->ports[i];
-                if (port->wPortChange)
-                    status |= (1 << (i + 1));
-            }
-            if (status != 0) {
-                for(i = 0; i < n; i++) {
-                    buf[i] = status >> (8 * i);
-                }
-                usb_packet_copy(p, buf, n);
-                ret = n;
-            } else {
-                ret = USB_RET_NAK; /* usb11 11.13.1 */
-            }
-        } else {
-            goto fail;
-        }
-        break;
-    case USB_TOKEN_OUT:
-    default:
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static void usb_hub_handle_destroy(USBDevice *dev)
-{
-    USBHubState *s = (USBHubState *)dev;
-    int i;
-
-    for (i = 0; i < NUM_PORTS; i++) {
-        usb_unregister_port(usb_bus_from_device(dev),
-                            &s->ports[i].port);
-    }
-}
-
-static USBPortOps usb_hub_port_ops = {
-    .attach = usb_hub_attach,
-    .detach = usb_hub_detach,
-    .child_detach = usb_hub_child_detach,
-    .wakeup = usb_hub_wakeup,
-    .complete = usb_hub_complete,
-};
-
-static int usb_hub_initfn(USBDevice *dev)
-{
-    USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
-    USBHubPort *port;
-    int i;
-
-    usb_desc_init(dev);
-    s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
-    for (i = 0; i < NUM_PORTS; i++) {
-        port = &s->ports[i];
-        usb_register_port(usb_bus_from_device(dev),
-                          &port->port, s, i, &usb_hub_port_ops,
-                          USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
-        usb_port_location(&port->port, dev->port, i+1);
-    }
-    usb_hub_handle_reset(dev);
-    return 0;
-}
-
-static const VMStateDescription vmstate_usb_hub_port = {
-    .name = "usb-hub-port",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField []) {
-        VMSTATE_UINT16(wPortStatus, USBHubPort),
-        VMSTATE_UINT16(wPortChange, USBHubPort),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_usb_hub = {
-    .name = "usb-hub",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField []) {
-        VMSTATE_USB_DEVICE(dev, USBHubState),
-        VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0,
-                             vmstate_usb_hub_port, USBHubPort),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void usb_hub_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = usb_hub_initfn;
-    uc->product_desc   = "QEMU USB Hub";
-    uc->usb_desc       = &desc_hub;
-    uc->find_device    = usb_hub_find_device;
-    uc->handle_reset   = usb_hub_handle_reset;
-    uc->handle_control = usb_hub_handle_control;
-    uc->handle_data    = usb_hub_handle_data;
-    uc->handle_destroy = usb_hub_handle_destroy;
-    dc->fw_name = "hub";
-    dc->vmsd = &vmstate_usb_hub;
-}
-
-static TypeInfo hub_info = {
-    .name          = "usb-hub",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHubState),
-    .class_init    = usb_hub_class_initfn,
-};
-
-static void usb_hub_register_types(void)
-{
-    type_register_static(&hub_info);
-}
-
-type_init(usb_hub_register_types)
diff --git a/hw/usb-libhw.c b/hw/usb-libhw.c
deleted file mode 100644 (file)
index 162b42b..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * QEMU USB emulation, libhw bits.
- *
- * 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 "cpu-common.h"
-#include "usb.h"
-#include "dma.h"
-
-int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
-{
-    int is_write = (p->pid == USB_TOKEN_IN);
-    target_phys_addr_t len;
-    void *mem;
-    int i;
-
-    for (i = 0; i < sgl->nsg; i++) {
-        len = sgl->sg[i].len;
-        mem = cpu_physical_memory_map(sgl->sg[i].base, &len,
-                                      is_write);
-        if (!mem) {
-            goto err;
-        }
-        qemu_iovec_add(&p->iov, mem, len);
-        if (len != sgl->sg[i].len) {
-            goto err;
-        }
-    }
-    return 0;
-
-err:
-    usb_packet_unmap(p);
-    return -1;
-}
-
-void usb_packet_unmap(USBPacket *p)
-{
-    int is_write = (p->pid == USB_TOKEN_IN);
-    int i;
-
-    for (i = 0; i < p->iov.niov; i++) {
-        cpu_physical_memory_unmap(p->iov.iov[i].iov_base,
-                                  p->iov.iov[i].iov_len, is_write,
-                                  p->iov.iov[i].iov_len);
-    }
-}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
deleted file mode 100644 (file)
index c6f08a0..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * USB Mass Storage Device emulation
- *
- * Copyright (c) 2006 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the LGPL.
- */
-
-#include "qemu-common.h"
-#include "qemu-option.h"
-#include "qemu-config.h"
-#include "usb.h"
-#include "usb-desc.h"
-#include "scsi.h"
-#include "console.h"
-#include "monitor.h"
-#include "sysemu.h"
-#include "blockdev.h"
-
-//#define DEBUG_MSD
-
-#ifdef DEBUG_MSD
-#define DPRINTF(fmt, ...) \
-do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-/* USB requests.  */
-#define MassStorageReset  0xff
-#define GetMaxLun         0xfe
-
-enum USBMSDMode {
-    USB_MSDM_CBW, /* Command Block.  */
-    USB_MSDM_DATAOUT, /* Transfer data to device.  */
-    USB_MSDM_DATAIN, /* Transfer data from device.  */
-    USB_MSDM_CSW /* Command Status.  */
-};
-
-struct usb_msd_csw {
-    uint32_t sig;
-    uint32_t tag;
-    uint32_t residue;
-    uint8_t status;
-};
-
-typedef struct {
-    USBDevice dev;
-    enum USBMSDMode mode;
-    uint32_t scsi_len;
-    uint8_t *scsi_buf;
-    uint32_t data_len;
-    uint32_t residue;
-    struct usb_msd_csw csw;
-    SCSIRequest *req;
-    SCSIBus bus;
-    BlockConf conf;
-    char *serial;
-    SCSIDevice *scsi_dev;
-    uint32_t removable;
-    /* For async completion.  */
-    USBPacket *packet;
-} MSDState;
-
-struct usb_msd_cbw {
-    uint32_t sig;
-    uint32_t tag;
-    uint32_t data_len;
-    uint8_t flags;
-    uint8_t lun;
-    uint8_t cmd_len;
-    uint8_t cmd[16];
-};
-
-enum {
-    STR_MANUFACTURER = 1,
-    STR_PRODUCT,
-    STR_SERIALNUMBER,
-    STR_CONFIG_FULL,
-    STR_CONFIG_HIGH,
-};
-
-static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
-    [STR_PRODUCT]      = "QEMU USB HARDDRIVE",
-    [STR_SERIALNUMBER] = "1",
-    [STR_CONFIG_FULL]  = "Full speed config (usb 1.1)",
-    [STR_CONFIG_HIGH]  = "High speed config (usb 2.0)",
-};
-
-static const USBDescIface desc_iface_full = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 2,
-    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
-    .bInterfaceSubClass            = 0x06, /* SCSI */
-    .bInterfaceProtocol            = 0x50, /* Bulk */
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 64,
-        },{
-            .bEndpointAddress      = USB_DIR_OUT | 0x02,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 64,
-        },
-    }
-};
-
-static const USBDescDevice desc_device_full = {
-    .bcdUSB                        = 0x0200,
-    .bMaxPacketSize0               = 8,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .iConfiguration        = STR_CONFIG_FULL,
-            .bmAttributes          = 0xc0,
-            .nif = 1,
-            .ifs = &desc_iface_full,
-        },
-    },
-};
-
-static const USBDescIface desc_iface_high = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 2,
-    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
-    .bInterfaceSubClass            = 0x06, /* SCSI */
-    .bInterfaceProtocol            = 0x50, /* Bulk */
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 512,
-        },{
-            .bEndpointAddress      = USB_DIR_OUT | 0x02,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 512,
-        },
-    }
-};
-
-static const USBDescDevice desc_device_high = {
-    .bcdUSB                        = 0x0200,
-    .bMaxPacketSize0               = 64,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .iConfiguration        = STR_CONFIG_HIGH,
-            .bmAttributes          = 0xc0,
-            .nif = 1,
-            .ifs = &desc_iface_high,
-        },
-    },
-};
-
-static const USBDesc desc = {
-    .id = {
-        .idVendor          = 0x46f4, /* CRC16() of "QEMU" */
-        .idProduct         = 0x0001,
-        .bcdDevice         = 0,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device_full,
-    .high = &desc_device_high,
-    .str  = desc_strings,
-};
-
-static void usb_msd_copy_data(MSDState *s, USBPacket *p)
-{
-    uint32_t len;
-    len = p->iov.size - p->result;
-    if (len > s->scsi_len)
-        len = s->scsi_len;
-    usb_packet_copy(p, s->scsi_buf, len);
-    s->scsi_len -= len;
-    s->scsi_buf += len;
-    s->data_len -= len;
-    if (s->scsi_len == 0 || s->data_len == 0) {
-        scsi_req_continue(s->req);
-    }
-}
-
-static void usb_msd_send_status(MSDState *s, USBPacket *p)
-{
-    int len;
-
-    DPRINTF("Command status %d tag 0x%x, len %zd\n",
-            s->csw.status, s->csw.tag, p->iov.size);
-
-    assert(s->csw.sig == 0x53425355);
-    len = MIN(sizeof(s->csw), p->iov.size);
-    usb_packet_copy(p, &s->csw, len);
-    memset(&s->csw, 0, sizeof(s->csw));
-}
-
-static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
-{
-    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
-    USBPacket *p = s->packet;
-
-    assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
-    s->scsi_len = len;
-    s->scsi_buf = scsi_req_get_buf(req);
-    if (p) {
-        usb_msd_copy_data(s, p);
-        p = s->packet;
-        if (p && p->result == p->iov.size) {
-            /* Set s->packet to NULL before calling usb_packet_complete
-               because another request may be issued before
-               usb_packet_complete returns.  */
-            DPRINTF("Packet complete %p\n", p);
-            s->packet = NULL;
-            usb_packet_complete(&s->dev, p);
-        }
-    }
-}
-
-static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
-{
-    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
-    USBPacket *p = s->packet;
-
-    DPRINTF("Command complete %d tag 0x%x\n", status, req->tag);
-    s->residue = s->data_len;
-
-    s->csw.sig = cpu_to_le32(0x53425355);
-    s->csw.tag = cpu_to_le32(req->tag);
-    s->csw.residue = s->residue;
-    s->csw.status = status != 0;
-
-    if (s->packet) {
-        if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
-            /* A deferred packet with no write data remaining must be
-               the status read packet.  */
-            usb_msd_send_status(s, p);
-            s->mode = USB_MSDM_CBW;
-        } else {
-            if (s->data_len) {
-                int len = (p->iov.size - p->result);
-                usb_packet_skip(p, len);
-                s->data_len -= len;
-            }
-            if (s->data_len == 0) {
-                s->mode = USB_MSDM_CSW;
-            }
-        }
-        s->packet = NULL;
-        usb_packet_complete(&s->dev, p);
-    } else if (s->data_len == 0) {
-        s->mode = USB_MSDM_CSW;
-    }
-    scsi_req_unref(req);
-    s->req = NULL;
-}
-
-static void usb_msd_request_cancelled(SCSIRequest *req)
-{
-    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
-
-    if (req == s->req) {
-        scsi_req_unref(s->req);
-        s->req = NULL;
-        s->packet = NULL;
-        s->scsi_len = 0;
-    }
-}
-
-static void usb_msd_handle_reset(USBDevice *dev)
-{
-    MSDState *s = (MSDState *)dev;
-
-    DPRINTF("Reset\n");
-    if (s->req) {
-        scsi_req_cancel(s->req);
-    }
-    assert(s->req == NULL);
-
-    if (s->packet) {
-        USBPacket *p = s->packet;
-        s->packet = NULL;
-        p->result = USB_RET_STALL;
-        usb_packet_complete(dev, p);
-    }
-
-    s->mode = USB_MSDM_CBW;
-}
-
-static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    MSDState *s = (MSDState *)dev;
-    int ret;
-
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    ret = 0;
-    switch (request) {
-    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
-        ret = 0;
-        break;
-        /* Class specific requests.  */
-    case ClassInterfaceOutRequest | MassStorageReset:
-        /* Reset state ready for the next CBW.  */
-        s->mode = USB_MSDM_CBW;
-        ret = 0;
-        break;
-    case ClassInterfaceRequest | GetMaxLun:
-        data[0] = 0;
-        ret = 1;
-        break;
-    default:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
-{
-    MSDState *s = DO_UPCAST(MSDState, dev, dev);
-
-    if (s->req) {
-        scsi_req_cancel(s->req);
-    }
-}
-
-static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
-{
-    MSDState *s = (MSDState *)dev;
-    uint32_t tag;
-    int ret = 0;
-    struct usb_msd_cbw cbw;
-    uint8_t devep = p->ep->nr;
-
-    switch (p->pid) {
-    case USB_TOKEN_OUT:
-        if (devep != 2)
-            goto fail;
-
-        switch (s->mode) {
-        case USB_MSDM_CBW:
-            if (p->iov.size != 31) {
-                fprintf(stderr, "usb-msd: Bad CBW size");
-                goto fail;
-            }
-            usb_packet_copy(p, &cbw, 31);
-            if (le32_to_cpu(cbw.sig) != 0x43425355) {
-                fprintf(stderr, "usb-msd: Bad signature %08x\n",
-                        le32_to_cpu(cbw.sig));
-                goto fail;
-            }
-            DPRINTF("Command on LUN %d\n", cbw.lun);
-            if (cbw.lun != 0) {
-                fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
-                goto fail;
-            }
-            tag = le32_to_cpu(cbw.tag);
-            s->data_len = le32_to_cpu(cbw.data_len);
-            if (s->data_len == 0) {
-                s->mode = USB_MSDM_CSW;
-            } else if (cbw.flags & 0x80) {
-                s->mode = USB_MSDM_DATAIN;
-            } else {
-                s->mode = USB_MSDM_DATAOUT;
-            }
-            DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
-                    tag, cbw.flags, cbw.cmd_len, s->data_len);
-            s->residue = 0;
-            s->scsi_len = 0;
-            s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL);
-            scsi_req_enqueue(s->req);
-            if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
-                scsi_req_continue(s->req);
-            }
-            ret = p->result;
-            break;
-
-        case USB_MSDM_DATAOUT:
-            DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len);
-            if (p->iov.size > s->data_len) {
-                goto fail;
-            }
-
-            if (s->scsi_len) {
-                usb_msd_copy_data(s, p);
-            }
-            if (s->residue) {
-                int len = p->iov.size - p->result;
-                if (len) {
-                    usb_packet_skip(p, len);
-                    s->data_len -= len;
-                    if (s->data_len == 0) {
-                        s->mode = USB_MSDM_CSW;
-                    }
-                }
-            }
-            if (p->result < p->iov.size) {
-                DPRINTF("Deferring packet %p\n", p);
-                s->packet = p;
-                ret = USB_RET_ASYNC;
-            } else {
-                ret = p->result;
-            }
-            break;
-
-        default:
-            DPRINTF("Unexpected write (len %zd)\n", p->iov.size);
-            goto fail;
-        }
-        break;
-
-    case USB_TOKEN_IN:
-        if (devep != 1)
-            goto fail;
-
-        switch (s->mode) {
-        case USB_MSDM_DATAOUT:
-            if (s->data_len != 0 || p->iov.size < 13) {
-                goto fail;
-            }
-            /* Waiting for SCSI write to complete.  */
-            s->packet = p;
-            ret = USB_RET_ASYNC;
-            break;
-
-        case USB_MSDM_CSW:
-            if (p->iov.size < 13) {
-                goto fail;
-            }
-
-            if (s->req) {
-                /* still in flight */
-                s->packet = p;
-                ret = USB_RET_ASYNC;
-            } else {
-                usb_msd_send_status(s, p);
-                s->mode = USB_MSDM_CBW;
-                ret = 13;
-            }
-            break;
-
-        case USB_MSDM_DATAIN:
-            DPRINTF("Data in %zd/%d, scsi_len %d\n",
-                    p->iov.size, s->data_len, s->scsi_len);
-            if (s->scsi_len) {
-                usb_msd_copy_data(s, p);
-            }
-            if (s->residue) {
-                int len = p->iov.size - p->result;
-                if (len) {
-                    usb_packet_skip(p, len);
-                    s->data_len -= len;
-                    if (s->data_len == 0) {
-                        s->mode = USB_MSDM_CSW;
-                    }
-                }
-            }
-            if (p->result < p->iov.size) {
-                DPRINTF("Deferring packet %p\n", p);
-                s->packet = p;
-                ret = USB_RET_ASYNC;
-            } else {
-                ret = p->result;
-            }
-            break;
-
-        default:
-            DPRINTF("Unexpected read (len %zd)\n", p->iov.size);
-            goto fail;
-        }
-        break;
-
-    default:
-        DPRINTF("Bad token\n");
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-
-    return ret;
-}
-
-static void usb_msd_password_cb(void *opaque, int err)
-{
-    MSDState *s = opaque;
-
-    if (!err)
-        err = usb_device_attach(&s->dev);
-
-    if (err)
-        qdev_unplug(&s->dev.qdev);
-}
-
-static const struct SCSIBusInfo usb_msd_scsi_info = {
-    .tcq = false,
-    .max_target = 0,
-    .max_lun = 0,
-
-    .transfer_data = usb_msd_transfer_data,
-    .complete = usb_msd_command_complete,
-    .cancel = usb_msd_request_cancelled
-};
-
-static int usb_msd_initfn(USBDevice *dev)
-{
-    MSDState *s = DO_UPCAST(MSDState, dev, dev);
-    BlockDriverState *bs = s->conf.bs;
-    DriveInfo *dinfo;
-
-    if (!bs) {
-        error_report("drive property not set");
-        return -1;
-    }
-
-    /*
-     * Hack alert: this pretends to be a block device, but it's really
-     * a SCSI bus that can serve only a single device, which it
-     * creates automatically.  But first it needs to detach from its
-     * blockdev, or else scsi_bus_legacy_add_drive() dies when it
-     * attaches again.
-     *
-     * The hack is probably a bad idea.
-     */
-    bdrv_detach_dev(bs, &s->dev.qdev);
-    s->conf.bs = NULL;
-
-    if (!s->serial) {
-        /* try to fall back to value set with legacy -drive serial=... */
-        dinfo = drive_get_by_blockdev(bs);
-        if (*dinfo->serial) {
-            s->serial = strdup(dinfo->serial);
-        }
-    }
-    if (s->serial) {
-        usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial);
-    }
-
-    usb_desc_init(dev);
-    scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info);
-    s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable,
-                                            s->conf.bootindex);
-    if (!s->scsi_dev) {
-        return -1;
-    }
-    s->bus.qbus.allow_hotplug = 0;
-    usb_msd_handle_reset(dev);
-
-    if (bdrv_key_required(bs)) {
-        if (cur_mon) {
-            monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s);
-            s->dev.auto_attach = 0;
-        } else {
-            autostart = 0;
-        }
-    }
-
-    return 0;
-}
-
-static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
-{
-    static int nr=0;
-    char id[8];
-    QemuOpts *opts;
-    DriveInfo *dinfo;
-    USBDevice *dev;
-    const char *p1;
-    char fmt[32];
-
-    /* parse -usbdevice disk: syntax into drive opts */
-    snprintf(id, sizeof(id), "usb%d", nr++);
-    opts = qemu_opts_create(qemu_find_opts("drive"), id, 0);
-
-    p1 = strchr(filename, ':');
-    if (p1++) {
-        const char *p2;
-
-        if (strstart(filename, "format=", &p2)) {
-            int len = MIN(p1 - p2, sizeof(fmt));
-            pstrcpy(fmt, len, p2);
-            qemu_opt_set(opts, "format", fmt);
-        } else if (*filename != ':') {
-            printf("unrecognized USB mass-storage option %s\n", filename);
-            return NULL;
-        }
-        filename = p1;
-    }
-    if (!*filename) {
-        printf("block device specification needed\n");
-        return NULL;
-    }
-    qemu_opt_set(opts, "file", filename);
-    qemu_opt_set(opts, "if", "none");
-
-    /* create host drive */
-    dinfo = drive_init(opts, 0);
-    if (!dinfo) {
-        qemu_opts_del(opts);
-        return NULL;
-    }
-
-    /* create guest device */
-    dev = usb_create(bus, "usb-storage");
-    if (!dev) {
-        return NULL;
-    }
-    if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
-        qdev_free(&dev->qdev);
-        return NULL;
-    }
-    if (qdev_init(&dev->qdev) < 0)
-        return NULL;
-
-    return dev;
-}
-
-static const VMStateDescription vmstate_usb_msd = {
-    .name = "usb-storage",
-    .unmigratable = 1, /* FIXME: handle transactions which are in flight */
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField []) {
-        VMSTATE_USB_DEVICE(dev, MSDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property msd_properties[] = {
-    DEFINE_BLOCK_PROPERTIES(MSDState, conf),
-    DEFINE_PROP_STRING("serial", MSDState, serial),
-    DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usb_msd_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = usb_msd_initfn;
-    uc->product_desc   = "QEMU USB MSD";
-    uc->usb_desc       = &desc;
-    uc->cancel_packet  = usb_msd_cancel_io;
-    uc->handle_attach  = usb_desc_attach;
-    uc->handle_reset   = usb_msd_handle_reset;
-    uc->handle_control = usb_msd_handle_control;
-    uc->handle_data    = usb_msd_handle_data;
-    dc->fw_name = "storage";
-    dc->vmsd = &vmstate_usb_msd;
-    dc->props = msd_properties;
-}
-
-static TypeInfo msd_info = {
-    .name          = "usb-storage",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(MSDState),
-    .class_init    = usb_msd_class_initfn,
-};
-
-static void usb_msd_register_types(void)
-{
-    type_register_static(&msd_info);
-    usb_legacy_register("usb-storage", "disk", usb_msd_init);
-}
-
-type_init(usb_msd_register_types)
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
deleted file mode 100644 (file)
index 820907a..0000000
+++ /dev/null
@@ -1,1544 +0,0 @@
-/*
- * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics,
- * USB2.0 OTG compliant core used in various chips.
- *
- * Copyright (C) 2008 Nokia Corporation
- * Written by Andrzej Zaborowski <andrew@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * Only host-mode and non-DMA accesses are currently supported.
- */
-#include "qemu-common.h"
-#include "qemu-timer.h"
-#include "usb.h"
-#include "irq.h"
-#include "hw.h"
-
-/* Common USB registers */
-#define MUSB_HDRC_FADDR                0x00    /* 8-bit */
-#define MUSB_HDRC_POWER                0x01    /* 8-bit */
-
-#define MUSB_HDRC_INTRTX       0x02    /* 16-bit */
-#define MUSB_HDRC_INTRRX       0x04
-#define MUSB_HDRC_INTRTXE      0x06  
-#define MUSB_HDRC_INTRRXE      0x08  
-#define MUSB_HDRC_INTRUSB      0x0a    /* 8 bit */
-#define MUSB_HDRC_INTRUSBE     0x0b    /* 8 bit */
-#define MUSB_HDRC_FRAME                0x0c    /* 16-bit */
-#define MUSB_HDRC_INDEX                0x0e    /* 8 bit */
-#define MUSB_HDRC_TESTMODE     0x0f    /* 8 bit */
-
-/* Per-EP registers in indexed mode */
-#define MUSB_HDRC_EP_IDX       0x10    /* 8-bit */
-
-/* EP FIFOs */
-#define MUSB_HDRC_FIFO         0x20
-
-/* Additional Control Registers */
-#define        MUSB_HDRC_DEVCTL        0x60    /* 8 bit */
-
-/* These are indexed */
-#define MUSB_HDRC_TXFIFOSZ     0x62    /* 8 bit (see masks) */
-#define MUSB_HDRC_RXFIFOSZ     0x63    /* 8 bit (see masks) */
-#define MUSB_HDRC_TXFIFOADDR   0x64    /* 16 bit offset shifted right 3 */
-#define MUSB_HDRC_RXFIFOADDR   0x66    /* 16 bit offset shifted right 3 */
-
-/* Some more registers */
-#define MUSB_HDRC_VCTRL                0x68    /* 8 bit */
-#define MUSB_HDRC_HWVERS       0x6c    /* 8 bit */
-
-/* Added in HDRC 1.9(?) & MHDRC 1.4 */
-/* ULPI pass-through */
-#define MUSB_HDRC_ULPI_VBUSCTL 0x70
-#define MUSB_HDRC_ULPI_REGDATA 0x74
-#define MUSB_HDRC_ULPI_REGADDR 0x75
-#define MUSB_HDRC_ULPI_REGCTL  0x76
-
-/* Extended config & PHY control */
-#define MUSB_HDRC_ENDCOUNT     0x78    /* 8 bit */
-#define MUSB_HDRC_DMARAMCFG    0x79    /* 8 bit */
-#define MUSB_HDRC_PHYWAIT      0x7a    /* 8 bit */
-#define MUSB_HDRC_PHYVPLEN     0x7b    /* 8 bit */
-#define MUSB_HDRC_HS_EOF1      0x7c    /* 8 bit, units of 546.1 us */
-#define MUSB_HDRC_FS_EOF1      0x7d    /* 8 bit, units of 533.3 ns */
-#define MUSB_HDRC_LS_EOF1      0x7e    /* 8 bit, units of 1.067 us */
-
-/* Per-EP BUSCTL registers */
-#define MUSB_HDRC_BUSCTL       0x80
-
-/* Per-EP registers in flat mode */
-#define MUSB_HDRC_EP           0x100
-
-/* offsets to registers in flat model */
-#define MUSB_HDRC_TXMAXP       0x00    /* 16 bit apparently */
-#define MUSB_HDRC_TXCSR                0x02    /* 16 bit apparently */
-#define MUSB_HDRC_CSR0         MUSB_HDRC_TXCSR         /* re-used for EP0 */
-#define MUSB_HDRC_RXMAXP       0x04    /* 16 bit apparently */
-#define MUSB_HDRC_RXCSR                0x06    /* 16 bit apparently */
-#define MUSB_HDRC_RXCOUNT      0x08    /* 16 bit apparently */
-#define MUSB_HDRC_COUNT0       MUSB_HDRC_RXCOUNT       /* re-used for EP0 */
-#define MUSB_HDRC_TXTYPE       0x0a    /* 8 bit apparently */
-#define MUSB_HDRC_TYPE0                MUSB_HDRC_TXTYPE        /* re-used for EP0 */
-#define MUSB_HDRC_TXINTERVAL   0x0b    /* 8 bit apparently */
-#define MUSB_HDRC_NAKLIMIT0    MUSB_HDRC_TXINTERVAL    /* re-used for EP0 */
-#define MUSB_HDRC_RXTYPE       0x0c    /* 8 bit apparently */
-#define MUSB_HDRC_RXINTERVAL   0x0d    /* 8 bit apparently */
-#define MUSB_HDRC_FIFOSIZE     0x0f    /* 8 bit apparently */
-#define MUSB_HDRC_CONFIGDATA   MGC_O_HDRC_FIFOSIZE     /* re-used for EP0 */
-
-/* "Bus control" registers */
-#define MUSB_HDRC_TXFUNCADDR   0x00
-#define MUSB_HDRC_TXHUBADDR    0x02
-#define MUSB_HDRC_TXHUBPORT    0x03
-
-#define MUSB_HDRC_RXFUNCADDR   0x04
-#define MUSB_HDRC_RXHUBADDR    0x06
-#define MUSB_HDRC_RXHUBPORT    0x07
-
-/*
- * MUSBHDRC Register bit masks
- */
-
-/* POWER */
-#define MGC_M_POWER_ISOUPDATE          0x80 
-#define        MGC_M_POWER_SOFTCONN            0x40
-#define        MGC_M_POWER_HSENAB              0x20
-#define        MGC_M_POWER_HSMODE              0x10
-#define MGC_M_POWER_RESET              0x08
-#define MGC_M_POWER_RESUME             0x04
-#define MGC_M_POWER_SUSPENDM           0x02
-#define MGC_M_POWER_ENSUSPEND          0x01
-
-/* INTRUSB */
-#define MGC_M_INTR_SUSPEND             0x01
-#define MGC_M_INTR_RESUME              0x02
-#define MGC_M_INTR_RESET               0x04
-#define MGC_M_INTR_BABBLE              0x04
-#define MGC_M_INTR_SOF                 0x08 
-#define MGC_M_INTR_CONNECT             0x10
-#define MGC_M_INTR_DISCONNECT          0x20
-#define MGC_M_INTR_SESSREQ             0x40
-#define MGC_M_INTR_VBUSERROR           0x80    /* FOR SESSION END */
-#define MGC_M_INTR_EP0                 0x01    /* FOR EP0 INTERRUPT */
-
-/* DEVCTL */
-#define MGC_M_DEVCTL_BDEVICE           0x80   
-#define MGC_M_DEVCTL_FSDEV             0x40
-#define MGC_M_DEVCTL_LSDEV             0x20
-#define MGC_M_DEVCTL_VBUS              0x18
-#define MGC_S_DEVCTL_VBUS              3
-#define MGC_M_DEVCTL_HM                        0x04
-#define MGC_M_DEVCTL_HR                        0x02
-#define MGC_M_DEVCTL_SESSION           0x01
-
-/* TESTMODE */
-#define MGC_M_TEST_FORCE_HOST          0x80
-#define MGC_M_TEST_FIFO_ACCESS         0x40
-#define MGC_M_TEST_FORCE_FS            0x20
-#define MGC_M_TEST_FORCE_HS            0x10
-#define MGC_M_TEST_PACKET              0x08
-#define MGC_M_TEST_K                   0x04
-#define MGC_M_TEST_J                   0x02
-#define MGC_M_TEST_SE0_NAK             0x01
-
-/* CSR0 */
-#define        MGC_M_CSR0_FLUSHFIFO            0x0100
-#define MGC_M_CSR0_TXPKTRDY            0x0002
-#define MGC_M_CSR0_RXPKTRDY            0x0001
-
-/* CSR0 in Peripheral mode */
-#define MGC_M_CSR0_P_SVDSETUPEND       0x0080
-#define MGC_M_CSR0_P_SVDRXPKTRDY       0x0040
-#define MGC_M_CSR0_P_SENDSTALL         0x0020
-#define MGC_M_CSR0_P_SETUPEND          0x0010
-#define MGC_M_CSR0_P_DATAEND           0x0008
-#define MGC_M_CSR0_P_SENTSTALL         0x0004
-
-/* CSR0 in Host mode */
-#define MGC_M_CSR0_H_NO_PING           0x0800
-#define MGC_M_CSR0_H_WR_DATATOGGLE     0x0400  /* set to allow setting: */
-#define MGC_M_CSR0_H_DATATOGGLE                0x0200  /* data toggle control */
-#define        MGC_M_CSR0_H_NAKTIMEOUT         0x0080
-#define MGC_M_CSR0_H_STATUSPKT         0x0040
-#define MGC_M_CSR0_H_REQPKT            0x0020
-#define MGC_M_CSR0_H_ERROR             0x0010
-#define MGC_M_CSR0_H_SETUPPKT          0x0008
-#define MGC_M_CSR0_H_RXSTALL           0x0004
-
-/* CONFIGDATA */
-#define MGC_M_CONFIGDATA_MPRXE         0x80    /* auto bulk pkt combining */
-#define MGC_M_CONFIGDATA_MPTXE         0x40    /* auto bulk pkt splitting */
-#define MGC_M_CONFIGDATA_BIGENDIAN     0x20
-#define MGC_M_CONFIGDATA_HBRXE         0x10    /* HB-ISO for RX */
-#define MGC_M_CONFIGDATA_HBTXE         0x08    /* HB-ISO for TX */
-#define MGC_M_CONFIGDATA_DYNFIFO       0x04    /* dynamic FIFO sizing */
-#define MGC_M_CONFIGDATA_SOFTCONE      0x02    /* SoftConnect */
-#define MGC_M_CONFIGDATA_UTMIDW                0x01    /* Width, 0 => 8b, 1 => 16b */
-
-/* TXCSR in Peripheral and Host mode */
-#define MGC_M_TXCSR_AUTOSET            0x8000
-#define MGC_M_TXCSR_ISO                        0x4000
-#define MGC_M_TXCSR_MODE               0x2000
-#define MGC_M_TXCSR_DMAENAB            0x1000
-#define MGC_M_TXCSR_FRCDATATOG         0x0800
-#define MGC_M_TXCSR_DMAMODE            0x0400
-#define MGC_M_TXCSR_CLRDATATOG         0x0040
-#define MGC_M_TXCSR_FLUSHFIFO          0x0008
-#define MGC_M_TXCSR_FIFONOTEMPTY       0x0002
-#define MGC_M_TXCSR_TXPKTRDY           0x0001
-
-/* TXCSR in Peripheral mode */
-#define MGC_M_TXCSR_P_INCOMPTX         0x0080
-#define MGC_M_TXCSR_P_SENTSTALL                0x0020
-#define MGC_M_TXCSR_P_SENDSTALL                0x0010
-#define MGC_M_TXCSR_P_UNDERRUN         0x0004
-
-/* TXCSR in Host mode */
-#define MGC_M_TXCSR_H_WR_DATATOGGLE    0x0200
-#define MGC_M_TXCSR_H_DATATOGGLE       0x0100
-#define MGC_M_TXCSR_H_NAKTIMEOUT       0x0080
-#define MGC_M_TXCSR_H_RXSTALL          0x0020
-#define MGC_M_TXCSR_H_ERROR            0x0004
-
-/* RXCSR in Peripheral and Host mode */
-#define MGC_M_RXCSR_AUTOCLEAR          0x8000
-#define MGC_M_RXCSR_DMAENAB            0x2000
-#define MGC_M_RXCSR_DISNYET            0x1000
-#define MGC_M_RXCSR_DMAMODE            0x0800
-#define MGC_M_RXCSR_INCOMPRX           0x0100
-#define MGC_M_RXCSR_CLRDATATOG         0x0080
-#define MGC_M_RXCSR_FLUSHFIFO          0x0010
-#define MGC_M_RXCSR_DATAERROR          0x0008
-#define MGC_M_RXCSR_FIFOFULL           0x0002
-#define MGC_M_RXCSR_RXPKTRDY           0x0001
-
-/* RXCSR in Peripheral mode */
-#define MGC_M_RXCSR_P_ISO              0x4000
-#define MGC_M_RXCSR_P_SENTSTALL                0x0040
-#define MGC_M_RXCSR_P_SENDSTALL                0x0020
-#define MGC_M_RXCSR_P_OVERRUN          0x0004
-
-/* RXCSR in Host mode */
-#define MGC_M_RXCSR_H_AUTOREQ          0x4000
-#define MGC_M_RXCSR_H_WR_DATATOGGLE    0x0400
-#define MGC_M_RXCSR_H_DATATOGGLE       0x0200
-#define MGC_M_RXCSR_H_RXSTALL          0x0040
-#define MGC_M_RXCSR_H_REQPKT           0x0020
-#define MGC_M_RXCSR_H_ERROR            0x0004
-
-/* HUBADDR */
-#define MGC_M_HUBADDR_MULTI_TT         0x80
-
-/* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */
-#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02
-#define MGC_M_ULPI_VBCTL_USEEXTVBUS    0x01
-#define MGC_M_ULPI_REGCTL_INT_ENABLE   0x08
-#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04
-#define MGC_M_ULPI_REGCTL_COMPLETE     0x02
-#define MGC_M_ULPI_REGCTL_REG          0x01
-
-/* #define MUSB_DEBUG */
-
-#ifdef MUSB_DEBUG
-#define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \
-                               __LINE__, ##__VA_ARGS__)
-#else
-#define TRACE(...)
-#endif
-
-
-static void musb_attach(USBPort *port);
-static void musb_detach(USBPort *port);
-static void musb_child_detach(USBPort *port, USBDevice *child);
-static void musb_schedule_cb(USBPort *port, USBPacket *p);
-static void musb_async_cancel_device(MUSBState *s, USBDevice *dev);
-
-static USBPortOps musb_port_ops = {
-    .attach = musb_attach,
-    .detach = musb_detach,
-    .child_detach = musb_child_detach,
-    .complete = musb_schedule_cb,
-};
-
-static USBBusOps musb_bus_ops = {
-};
-
-typedef struct MUSBPacket MUSBPacket;
-typedef struct MUSBEndPoint MUSBEndPoint;
-
-struct MUSBPacket {
-    USBPacket p;
-    MUSBEndPoint *ep;
-    int dir;
-};
-
-struct MUSBEndPoint {
-    uint16_t faddr[2];
-    uint8_t haddr[2];
-    uint8_t hport[2];
-    uint16_t csr[2];
-    uint16_t maxp[2];
-    uint16_t rxcount;
-    uint8_t type[2];
-    uint8_t interval[2];
-    uint8_t config;
-    uint8_t fifosize;
-    int timeout[2];    /* Always in microframes */
-
-    uint8_t *buf[2];
-    int fifolen[2];
-    int fifostart[2];
-    int fifoaddr[2];
-    MUSBPacket packey[2];
-    int status[2];
-    int ext_size[2];
-
-    /* For callbacks' use */
-    int epnum;
-    int interrupt[2];
-    MUSBState *musb;
-    USBCallback *delayed_cb[2];
-    QEMUTimer *intv_timer[2];
-};
-
-struct MUSBState {
-    qemu_irq irqs[musb_irq_max];
-    USBBus bus;
-    USBPort port;
-
-    int idx;
-    uint8_t devctl;
-    uint8_t power;
-    uint8_t faddr;
-
-    uint8_t intr;
-    uint8_t mask;
-    uint16_t tx_intr;
-    uint16_t tx_mask;
-    uint16_t rx_intr;
-    uint16_t rx_mask;
-
-    int setup_len;
-    int session;
-
-    uint8_t buf[0x8000];
-
-        /* Duplicating the world since 2008!...  probably we should have 32
-         * logical, single endpoints instead.  */
-    MUSBEndPoint ep[16];
-};
-
-void musb_reset(MUSBState *s)
-{
-    int i;
-
-    s->faddr = 0x00;
-    s->devctl = 0;
-    s->power = MGC_M_POWER_HSENAB;
-    s->tx_intr = 0x0000;
-    s->rx_intr = 0x0000;
-    s->tx_mask = 0xffff;
-    s->rx_mask = 0xffff;
-    s->intr = 0x00;
-    s->mask = 0x06;
-    s->idx = 0;
-
-    s->setup_len = 0;
-    s->session = 0;
-    memset(s->buf, 0, sizeof(s->buf));
-
-    /* TODO: _DW */
-    s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO;
-    for (i = 0; i < 16; i ++) {
-        s->ep[i].fifosize = 64;
-        s->ep[i].maxp[0] = 0x40;
-        s->ep[i].maxp[1] = 0x40;
-        s->ep[i].musb = s;
-        s->ep[i].epnum = i;
-        usb_packet_init(&s->ep[i].packey[0].p);
-        usb_packet_init(&s->ep[i].packey[1].p);
-    }
-}
-
-struct MUSBState *musb_init(DeviceState *parent_device, int gpio_base)
-{
-    MUSBState *s = g_malloc0(sizeof(*s));
-    int i;
-
-    for (i = 0; i < musb_irq_max; i++) {
-        s->irqs[i] = qdev_get_gpio_in(parent_device, gpio_base + i);
-    }
-
-    musb_reset(s);
-
-    usb_bus_new(&s->bus, &musb_bus_ops, parent_device);
-    usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
-                      USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
-
-    return s;
-}
-
-static void musb_vbus_set(MUSBState *s, int level)
-{
-    if (level)
-        s->devctl |= 3 << MGC_S_DEVCTL_VBUS;
-    else
-        s->devctl &= ~MGC_M_DEVCTL_VBUS;
-
-    qemu_set_irq(s->irqs[musb_set_vbus], level);
-}
-
-static void musb_intr_set(MUSBState *s, int line, int level)
-{
-    if (!level) {
-        s->intr &= ~(1 << line);
-        qemu_irq_lower(s->irqs[line]);
-    } else if (s->mask & (1 << line)) {
-        s->intr |= 1 << line;
-        qemu_irq_raise(s->irqs[line]);
-    }
-}
-
-static void musb_tx_intr_set(MUSBState *s, int line, int level)
-{
-    if (!level) {
-        s->tx_intr &= ~(1 << line);
-        if (!s->tx_intr)
-            qemu_irq_lower(s->irqs[musb_irq_tx]);
-    } else if (s->tx_mask & (1 << line)) {
-        s->tx_intr |= 1 << line;
-        qemu_irq_raise(s->irqs[musb_irq_tx]);
-    }
-}
-
-static void musb_rx_intr_set(MUSBState *s, int line, int level)
-{
-    if (line) {
-        if (!level) {
-            s->rx_intr &= ~(1 << line);
-            if (!s->rx_intr)
-                qemu_irq_lower(s->irqs[musb_irq_rx]);
-        } else if (s->rx_mask & (1 << line)) {
-            s->rx_intr |= 1 << line;
-            qemu_irq_raise(s->irqs[musb_irq_rx]);
-        }
-    } else
-        musb_tx_intr_set(s, line, level);
-}
-
-uint32_t musb_core_intr_get(MUSBState *s)
-{
-    return (s->rx_intr << 15) | s->tx_intr;
-}
-
-void musb_core_intr_clear(MUSBState *s, uint32_t mask)
-{
-    if (s->rx_intr) {
-        s->rx_intr &= mask >> 15;
-        if (!s->rx_intr)
-            qemu_irq_lower(s->irqs[musb_irq_rx]);
-    }
-
-    if (s->tx_intr) {
-        s->tx_intr &= mask & 0xffff;
-        if (!s->tx_intr)
-            qemu_irq_lower(s->irqs[musb_irq_tx]);
-    }
-}
-
-void musb_set_size(MUSBState *s, int epnum, int size, int is_tx)
-{
-    s->ep[epnum].ext_size[!is_tx] = size;
-    s->ep[epnum].fifostart[0] = 0;
-    s->ep[epnum].fifostart[1] = 0;
-    s->ep[epnum].fifolen[0] = 0;
-    s->ep[epnum].fifolen[1] = 0;
-}
-
-static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess)
-{
-    int detect_prev = prev_dev && prev_sess;
-    int detect = !!s->port.dev && s->session;
-
-    if (detect && !detect_prev) {
-        /* Let's skip the ID pin sense and VBUS sense formalities and
-         * and signal a successful SRP directly.  This should work at least
-         * for the Linux driver stack.  */
-        musb_intr_set(s, musb_irq_connect, 1);
-
-        if (s->port.dev->speed == USB_SPEED_LOW) {
-            s->devctl &= ~MGC_M_DEVCTL_FSDEV;
-            s->devctl |= MGC_M_DEVCTL_LSDEV;
-        } else {
-            s->devctl |= MGC_M_DEVCTL_FSDEV;
-            s->devctl &= ~MGC_M_DEVCTL_LSDEV;
-        }
-
-        /* A-mode?  */
-        s->devctl &= ~MGC_M_DEVCTL_BDEVICE;
-
-        /* Host-mode bit?  */
-        s->devctl |= MGC_M_DEVCTL_HM;
-#if 1
-        musb_vbus_set(s, 1);
-#endif
-    } else if (!detect && detect_prev) {
-#if 1
-        musb_vbus_set(s, 0);
-#endif
-    }
-}
-
-/* Attach or detach a device on our only port.  */
-static void musb_attach(USBPort *port)
-{
-    MUSBState *s = (MUSBState *) port->opaque;
-
-    musb_intr_set(s, musb_irq_vbus_request, 1);
-    musb_session_update(s, 0, s->session);
-}
-
-static void musb_detach(USBPort *port)
-{
-    MUSBState *s = (MUSBState *) port->opaque;
-
-    musb_async_cancel_device(s, port->dev);
-
-    musb_intr_set(s, musb_irq_disconnect, 1);
-    musb_session_update(s, 1, s->session);
-}
-
-static void musb_child_detach(USBPort *port, USBDevice *child)
-{
-    MUSBState *s = (MUSBState *) port->opaque;
-
-    musb_async_cancel_device(s, child);
-}
-
-static void musb_cb_tick0(void *opaque)
-{
-    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
-
-    ep->delayed_cb[0](&ep->packey[0].p, opaque);
-}
-
-static void musb_cb_tick1(void *opaque)
-{
-    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
-
-    ep->delayed_cb[1](&ep->packey[1].p, opaque);
-}
-
-#define musb_cb_tick   (dir ? musb_cb_tick1 : musb_cb_tick0)
-
-static void musb_schedule_cb(USBPort *port, USBPacket *packey)
-{
-    MUSBPacket *p = container_of(packey, MUSBPacket, p);
-    MUSBEndPoint *ep = p->ep;
-    int dir = p->dir;
-    int timeout = 0;
-
-    if (ep->status[dir] == USB_RET_NAK)
-        timeout = ep->timeout[dir];
-    else if (ep->interrupt[dir])
-        timeout = 8;
-    else
-        return musb_cb_tick(ep);
-
-    if (!ep->intv_timer[dir])
-        ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, ep);
-
-    qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock_ns(vm_clock) +
-                   muldiv64(timeout, get_ticks_per_sec(), 8000));
-}
-
-static int musb_timeout(int ttype, int speed, int val)
-{
-#if 1
-    return val << 3;
-#endif
-
-    switch (ttype) {
-    case USB_ENDPOINT_XFER_CONTROL:
-        if (val < 2)
-            return 0;
-        else if (speed == USB_SPEED_HIGH)
-            return 1 << (val - 1);
-        else
-            return 8 << (val - 1);
-
-    case USB_ENDPOINT_XFER_INT:
-        if (speed == USB_SPEED_HIGH)
-            if (val < 2)
-                return 0;
-            else
-                return 1 << (val - 1);
-        else
-            return val << 3;
-
-    case USB_ENDPOINT_XFER_BULK:
-    case USB_ENDPOINT_XFER_ISOC:
-        if (val < 2)
-            return 0;
-        else if (speed == USB_SPEED_HIGH)
-            return 1 << (val - 1);
-        else
-            return 8 << (val - 1);
-        /* TODO: what with low-speed Bulk and Isochronous?  */
-    }
-
-    hw_error("bad interval\n");
-}
-
-static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
-                int epnum, int pid, int len, USBCallback cb, int dir)
-{
-    USBDevice *dev;
-    USBEndpoint *uep;
-    int ret;
-    int idx = epnum && dir;
-    int ttype;
-
-    /* ep->type[0,1] contains:
-     * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow)
-     * in bits 5:4 the transfer type (BULK / INT)
-     * in bits 3:0 the EP num
-     */
-    ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0;
-
-    ep->timeout[dir] = musb_timeout(ttype,
-                    ep->type[idx] >> 6, ep->interval[idx]);
-    ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT;
-    ep->delayed_cb[dir] = cb;
-
-    /* A wild guess on the FADDR semantics... */
-    dev = usb_find_device(&s->port, ep->faddr[idx]);
-    uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
-    usb_packet_setup(&ep->packey[dir].p, pid, uep);
-    usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
-    ep->packey[dir].ep = ep;
-    ep->packey[dir].dir = dir;
-
-    ret = usb_handle_packet(dev, &ep->packey[dir].p);
-
-    if (ret == USB_RET_ASYNC) {
-        ep->status[dir] = len;
-        return;
-    }
-
-    ep->status[dir] = ret;
-    musb_schedule_cb(&s->port, &ep->packey[dir].p);
-}
-
-static void musb_tx_packet_complete(USBPacket *packey, void *opaque)
-{
-    /* Unfortunately we can't use packey->devep because that's the remote
-     * endpoint number and may be different than our local.  */
-    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
-    int epnum = ep->epnum;
-    MUSBState *s = ep->musb;
-
-    ep->fifostart[0] = 0;
-    ep->fifolen[0] = 0;
-#ifdef CLEAR_NAK
-    if (ep->status[0] != USB_RET_NAK) {
-#endif
-        if (epnum)
-            ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY);
-        else
-            ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY;
-#ifdef CLEAR_NAK
-    }
-#endif
-
-    /* Clear all of the error bits first */
-    if (epnum)
-        ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL |
-                        MGC_M_TXCSR_H_NAKTIMEOUT);
-    else
-        ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
-                        MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
-
-    if (ep->status[0] == USB_RET_STALL) {
-        /* Command not supported by target! */
-        ep->status[0] = 0;
-
-        if (epnum)
-            ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL;
-        else
-            ep->csr[0] |= MGC_M_CSR0_H_RXSTALL;
-    }
-
-    if (ep->status[0] == USB_RET_NAK) {
-        ep->status[0] = 0;
-
-        /* NAK timeouts are only generated in Bulk transfers and
-         * Data-errors in Isochronous.  */
-        if (ep->interrupt[0]) {
-            return;
-        }
-
-        if (epnum)
-            ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT;
-        else
-            ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT;
-    }
-
-    if (ep->status[0] < 0) {
-        if (ep->status[0] == USB_RET_BABBLE)
-            musb_intr_set(s, musb_irq_rst_babble, 1);
-
-        /* Pretend we've tried three times already and failed (in
-         * case of USB_TOKEN_SETUP).  */
-        if (epnum)
-            ep->csr[0] |= MGC_M_TXCSR_H_ERROR;
-        else
-            ep->csr[0] |= MGC_M_CSR0_H_ERROR;
-
-        musb_tx_intr_set(s, epnum, 1);
-        return;
-    }
-    /* TODO: check len for over/underruns of an OUT packet?  */
-
-#ifdef SETUPLEN_HACK
-    if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP)
-        s->setup_len = ep->packey[0].data[6];
-#endif
-
-    /* In DMA mode: if no error, assert DMA request for this EP,
-     * and skip the interrupt.  */
-    musb_tx_intr_set(s, epnum, 1);
-}
-
-static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
-{
-    /* Unfortunately we can't use packey->devep because that's the remote
-     * endpoint number and may be different than our local.  */
-    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
-    int epnum = ep->epnum;
-    MUSBState *s = ep->musb;
-
-    ep->fifostart[1] = 0;
-    ep->fifolen[1] = 0;
-
-#ifdef CLEAR_NAK
-    if (ep->status[1] != USB_RET_NAK) {
-#endif
-        ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
-        if (!epnum)
-            ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT;
-#ifdef CLEAR_NAK
-    }
-#endif
-
-    /* Clear all of the imaginable error bits first */
-    ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL |
-                    MGC_M_RXCSR_DATAERROR);
-    if (!epnum)
-        ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
-                        MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
-
-    if (ep->status[1] == USB_RET_STALL) {
-        ep->status[1] = 0;
-        packey->result = 0;
-
-        ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
-        if (!epnum)
-            ep->csr[0] |= MGC_M_CSR0_H_RXSTALL;
-    }
-
-    if (ep->status[1] == USB_RET_NAK) {
-        ep->status[1] = 0;
-
-        /* NAK timeouts are only generated in Bulk transfers and
-         * Data-errors in Isochronous.  */
-        if (ep->interrupt[1])
-            return musb_packet(s, ep, epnum, USB_TOKEN_IN,
-                            packey->iov.size, musb_rx_packet_complete, 1);
-
-        ep->csr[1] |= MGC_M_RXCSR_DATAERROR;
-        if (!epnum)
-            ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT;
-    }
-
-    if (ep->status[1] < 0) {
-        if (ep->status[1] == USB_RET_BABBLE) {
-            musb_intr_set(s, musb_irq_rst_babble, 1);
-            return;
-        }
-
-        /* Pretend we've tried three times already and failed (in
-         * case of a control transfer).  */
-        ep->csr[1] |= MGC_M_RXCSR_H_ERROR;
-        if (!epnum)
-            ep->csr[0] |= MGC_M_CSR0_H_ERROR;
-
-        musb_rx_intr_set(s, epnum, 1);
-        return;
-    }
-    /* TODO: check len for over/underruns of an OUT packet?  */
-    /* TODO: perhaps make use of e->ext_size[1] here.  */
-
-    packey->result = ep->status[1];
-
-    if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
-        ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
-        if (!epnum)
-            ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
-
-        ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */
-        /* In DMA mode: assert DMA request for this EP */
-    }
-
-    /* Only if DMA has not been asserted */
-    musb_rx_intr_set(s, epnum, 1);
-}
-
-static void musb_async_cancel_device(MUSBState *s, USBDevice *dev)
-{
-    int ep, dir;
-
-    for (ep = 0; ep < 16; ep++) {
-        for (dir = 0; dir < 2; dir++) {
-            if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) ||
-                s->ep[ep].packey[dir].p.ep->dev != dev) {
-                continue;
-            }
-            usb_cancel_packet(&s->ep[ep].packey[dir].p);
-            /* status updates needed here? */
-        }
-    }
-}
-
-static void musb_tx_rdy(MUSBState *s, int epnum)
-{
-    MUSBEndPoint *ep = s->ep + epnum;
-    int pid;
-    int total, valid = 0;
-    TRACE("start %d, len %d",  ep->fifostart[0], ep->fifolen[0] );
-    ep->fifostart[0] += ep->fifolen[0];
-    ep->fifolen[0] = 0;
-
-    /* XXX: how's the total size of the packet retrieved exactly in
-     * the generic case?  */
-    total = ep->maxp[0] & 0x3ff;
-
-    if (ep->ext_size[0]) {
-        total = ep->ext_size[0];
-        ep->ext_size[0] = 0;
-        valid = 1;
-    }
-
-    /* If the packet is not fully ready yet, wait for a next segment.  */
-    if (epnum && (ep->fifostart[0]) < total)
-        return;
-
-    if (!valid)
-        total = ep->fifostart[0];
-
-    pid = USB_TOKEN_OUT;
-    if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) {
-        pid = USB_TOKEN_SETUP;
-        if (total != 8) {
-            TRACE("illegal SETUPPKT length of %i bytes", total);
-        }
-        /* Controller should retry SETUP packets three times on errors
-         * but it doesn't make sense for us to do that.  */
-    }
-
-    return musb_packet(s, ep, epnum, pid,
-                    total, musb_tx_packet_complete, 0);
-}
-
-static void musb_rx_req(MUSBState *s, int epnum)
-{
-    MUSBEndPoint *ep = s->ep + epnum;
-    int total;
-
-    /* If we already have a packet, which didn't fit into the
-     * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */
-    if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
-                    (ep->fifostart[1]) + ep->rxcount <
-                    ep->packey[1].p.iov.size) {
-        TRACE("0x%08x, %d",  ep->fifostart[1], ep->rxcount );
-        ep->fifostart[1] += ep->rxcount;
-        ep->fifolen[1] = 0;
-
-        ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]),
-                        ep->maxp[1]);
-
-        ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
-        if (!epnum)
-            ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT;
-
-        /* Clear all of the error bits first */
-        ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL |
-                        MGC_M_RXCSR_DATAERROR);
-        if (!epnum)
-            ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
-                            MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
-
-        ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
-        if (!epnum)
-            ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
-        musb_rx_intr_set(s, epnum, 1);
-        return;
-    }
-
-    /* The driver sets maxp[1] to 64 or less because it knows the hardware
-     * FIFO is this deep.  Bigger packets get split in
-     * usb_generic_handle_packet but we can also do the splitting locally
-     * for performance.  It turns out we can also have a bigger FIFO and
-     * ignore the limit set in ep->maxp[1].  The Linux MUSB driver deals
-     * OK with single packets of even 32KB and we avoid splitting, however
-     * usb_msd.c sometimes sends a packet bigger than what Linux expects
-     * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN.  Splitting
-     * hides this overrun from Linux.  Up to 4096 everything is fine
-     * though.  Currently this is disabled.
-     *
-     * XXX: mind ep->fifosize.  */
-    total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf));
-
-#ifdef SETUPLEN_HACK
-    /* Why should *we* do that instead of Linux?  */
-    if (!epnum) {
-        if (ep->packey[0].p.devaddr == 2) {
-            total = MIN(s->setup_len, 8);
-        } else {
-            total = MIN(s->setup_len, 64);
-        }
-        s->setup_len -= total;
-    }
-#endif
-
-    return musb_packet(s, ep, epnum, USB_TOKEN_IN,
-                    total, musb_rx_packet_complete, 1);
-}
-
-static uint8_t musb_read_fifo(MUSBEndPoint *ep)
-{
-    uint8_t value;
-    if (ep->fifolen[1] >= 64) {
-        /* We have a FIFO underrun */
-        TRACE("EP%d FIFO is now empty, stop reading", ep->epnum);
-        return 0x00000000;
-    }
-    /* In DMA mode clear RXPKTRDY and set REQPKT automatically
-     * (if AUTOREQ is set) */
-
-    ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL;
-    value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++];
-    TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] );
-    return value;
-}
-
-static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value)
-{
-    TRACE("EP%d = %02x", ep->epnum, value);
-    if (ep->fifolen[0] >= 64) {
-        /* We have a FIFO overrun */
-        TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum);
-        return;
-     }
-
-     ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value;
-     ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY;
-}
-
-static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir)
-{
-    if (ep->intv_timer[dir])
-        qemu_del_timer(ep->intv_timer[dir]);
-}
-
-/* Bus control */
-static uint8_t musb_busctl_readb(void *opaque, int ep, int addr)
-{
-    MUSBState *s = (MUSBState *) opaque;
-
-    switch (addr) {
-    /* For USB2.0 HS hubs only */
-    case MUSB_HDRC_TXHUBADDR:
-        return s->ep[ep].haddr[0];
-    case MUSB_HDRC_TXHUBPORT:
-        return s->ep[ep].hport[0];
-    case MUSB_HDRC_RXHUBADDR:
-        return s->ep[ep].haddr[1];
-    case MUSB_HDRC_RXHUBPORT:
-        return s->ep[ep].hport[1];
-
-    default:
-        TRACE("unknown register 0x%02x", addr);
-        return 0x00;
-    };
-}
-
-static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value)
-{
-    MUSBState *s = (MUSBState *) opaque;
-
-    switch (addr) {
-    case MUSB_HDRC_TXFUNCADDR:
-        s->ep[ep].faddr[0] = value;
-        break;
-    case MUSB_HDRC_RXFUNCADDR:
-        s->ep[ep].faddr[1] = value;
-        break;
-    case MUSB_HDRC_TXHUBADDR:
-        s->ep[ep].haddr[0] = value;
-        break;
-    case MUSB_HDRC_TXHUBPORT:
-        s->ep[ep].hport[0] = value;
-        break;
-    case MUSB_HDRC_RXHUBADDR:
-        s->ep[ep].haddr[1] = value;
-        break;
-    case MUSB_HDRC_RXHUBPORT:
-        s->ep[ep].hport[1] = value;
-        break;
-
-    default:
-        TRACE("unknown register 0x%02x", addr);
-        break;
-    };
-}
-
-static uint16_t musb_busctl_readh(void *opaque, int ep, int addr)
-{
-    MUSBState *s = (MUSBState *) opaque;
-
-    switch (addr) {
-    case MUSB_HDRC_TXFUNCADDR:
-        return s->ep[ep].faddr[0];
-    case MUSB_HDRC_RXFUNCADDR:
-        return s->ep[ep].faddr[1];
-
-    default:
-        return musb_busctl_readb(s, ep, addr) |
-                (musb_busctl_readb(s, ep, addr | 1) << 8);
-    };
-}
-
-static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value)
-{
-    MUSBState *s = (MUSBState *) opaque;
-
-    switch (addr) {
-    case MUSB_HDRC_TXFUNCADDR:
-        s->ep[ep].faddr[0] = value;
-        break;
-    case MUSB_HDRC_RXFUNCADDR:
-        s->ep[ep].faddr[1] = value;
-        break;
-
-    default:
-        musb_busctl_writeb(s, ep, addr, value & 0xff);
-        musb_busctl_writeb(s, ep, addr | 1, value >> 8);
-    };
-}
-
-/* Endpoint control */
-static uint8_t musb_ep_readb(void *opaque, int ep, int addr)
-{
-    MUSBState *s = (MUSBState *) opaque;
-
-    switch (addr) {
-    case MUSB_HDRC_TXTYPE:
-        return s->ep[ep].type[0];
-    case MUSB_HDRC_TXINTERVAL:
-        return s->ep[ep].interval[0];
-    case MUSB_HDRC_RXTYPE:
-        return s->ep[ep].type[1];
-    case MUSB_HDRC_RXINTERVAL:
-        return s->ep[ep].interval[1];
-    case (MUSB_HDRC_FIFOSIZE & ~1):
-        return 0x00;
-    case MUSB_HDRC_FIFOSIZE:
-        return ep ? s->ep[ep].fifosize : s->ep[ep].config;
-    case MUSB_HDRC_RXCOUNT:
-        return s->ep[ep].rxcount;
-
-    default:
-        TRACE("unknown register 0x%02x", addr);
-        return 0x00;
-    };
-}
-
-static void musb_ep_writeb(void *opaque, int ep, int addr, uint8_t value)
-{
-    MUSBState *s = (MUSBState *) opaque;
-
-    switch (addr) {
-    case MUSB_HDRC_TXTYPE:
-        s->ep[ep].type[0] = value;
-        break;
-    case MUSB_HDRC_TXINTERVAL:
-        s->ep[ep].interval[0] = value;
-        musb_ep_frame_cancel(&s->ep[ep], 0);
-        break;
-    case MUSB_HDRC_RXTYPE:
-        s->ep[ep].type[1] = value;
-        break;
-    case MUSB_HDRC_RXINTERVAL:
-        s->ep[ep].interval[1] = value;
-        musb_ep_frame_cancel(&s->ep[ep], 1);
-        break;
-    case (MUSB_HDRC_FIFOSIZE & ~1):
-        break;
-    case MUSB_HDRC_FIFOSIZE:
-        TRACE("somebody messes with fifosize (now %i bytes)", value);
-        s->ep[ep].fifosize = value;
-        break;
-    default:
-        TRACE("unknown register 0x%02x", addr);
-        break;
-    };
-}
-
-static uint16_t musb_ep_readh(void *opaque, int ep, int addr)
-{
-    MUSBState *s = (MUSBState *) opaque;
-    uint16_t ret;
-
-    switch (addr) {
-    case MUSB_HDRC_TXMAXP:
-        return s->ep[ep].maxp[0];
-    case MUSB_HDRC_TXCSR:
-        return s->ep[ep].csr[0];
-    case MUSB_HDRC_RXMAXP:
-        return s->ep[ep].maxp[1];
-    case MUSB_HDRC_RXCSR:
-        ret = s->ep[ep].csr[1];
-
-        /* TODO: This and other bits probably depend on
-         * ep->csr[1] & MGC_M_RXCSR_AUTOCLEAR.  */
-        if (s->ep[ep].csr[1] & MGC_M_RXCSR_AUTOCLEAR)
-            s->ep[ep].csr[1] &= ~MGC_M_RXCSR_RXPKTRDY;
-
-        return ret;
-    case MUSB_HDRC_RXCOUNT:
-        return s->ep[ep].rxcount;
-
-    default:
-        return musb_ep_readb(s, ep, addr) |
-                (musb_ep_readb(s, ep, addr | 1) << 8);
-    };
-}
-
-static void musb_ep_writeh(void *opaque, int ep, int addr, uint16_t value)
-{
-    MUSBState *s = (MUSBState *) opaque;
-
-    switch (addr) {
-    case MUSB_HDRC_TXMAXP:
-        s->ep[ep].maxp[0] = value;
-        break;
-    case MUSB_HDRC_TXCSR:
-        if (ep) {
-            s->ep[ep].csr[0] &= value & 0xa6;
-            s->ep[ep].csr[0] |= value & 0xff59;
-        } else {
-            s->ep[ep].csr[0] &= value & 0x85;
-            s->ep[ep].csr[0] |= value & 0xf7a;
-        }
-
-        musb_ep_frame_cancel(&s->ep[ep], 0);
-
-        if ((ep && (value & MGC_M_TXCSR_FLUSHFIFO)) ||
-                        (!ep && (value & MGC_M_CSR0_FLUSHFIFO))) {
-            s->ep[ep].fifolen[0] = 0;
-            s->ep[ep].fifostart[0] = 0;
-            if (ep)
-                s->ep[ep].csr[0] &=
-                        ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY);
-            else
-                s->ep[ep].csr[0] &=
-                        ~(MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_RXPKTRDY);
-        }
-        if (
-                        (ep &&
-#ifdef CLEAR_NAK
-                         (value & MGC_M_TXCSR_TXPKTRDY) &&
-                         !(value & MGC_M_TXCSR_H_NAKTIMEOUT)) ||
-#else
-                         (value & MGC_M_TXCSR_TXPKTRDY)) ||
-#endif
-                        (!ep &&
-#ifdef CLEAR_NAK
-                         (value & MGC_M_CSR0_TXPKTRDY) &&
-                         !(value & MGC_M_CSR0_H_NAKTIMEOUT)))
-#else
-                         (value & MGC_M_CSR0_TXPKTRDY)))
-#endif
-            musb_tx_rdy(s, ep);
-        if (!ep &&
-                        (value & MGC_M_CSR0_H_REQPKT) &&
-#ifdef CLEAR_NAK
-                        !(value & (MGC_M_CSR0_H_NAKTIMEOUT |
-                                        MGC_M_CSR0_RXPKTRDY)))
-#else
-                        !(value & MGC_M_CSR0_RXPKTRDY))
-#endif
-            musb_rx_req(s, ep);
-        break;
-
-    case MUSB_HDRC_RXMAXP:
-        s->ep[ep].maxp[1] = value;
-        break;
-    case MUSB_HDRC_RXCSR:
-        /* (DMA mode only) */
-        if (
-                (value & MGC_M_RXCSR_H_AUTOREQ) &&
-                !(value & MGC_M_RXCSR_RXPKTRDY) &&
-                (s->ep[ep].csr[1] & MGC_M_RXCSR_RXPKTRDY))
-            value |= MGC_M_RXCSR_H_REQPKT;
-
-        s->ep[ep].csr[1] &= 0x102 | (value & 0x4d);
-        s->ep[ep].csr[1] |= value & 0xfeb0;
-
-        musb_ep_frame_cancel(&s->ep[ep], 1);
-
-        if (value & MGC_M_RXCSR_FLUSHFIFO) {
-            s->ep[ep].fifolen[1] = 0;
-            s->ep[ep].fifostart[1] = 0;
-            s->ep[ep].csr[1] &= ~(MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY);
-            /* If double buffering and we have two packets ready, flush
-             * only the first one and set up the fifo at the second packet.  */
-        }
-#ifdef CLEAR_NAK
-        if ((value & MGC_M_RXCSR_H_REQPKT) && !(value & MGC_M_RXCSR_DATAERROR))
-#else
-        if (value & MGC_M_RXCSR_H_REQPKT)
-#endif
-            musb_rx_req(s, ep);
-        break;
-    case MUSB_HDRC_RXCOUNT:
-        s->ep[ep].rxcount = value;
-        break;
-
-    default:
-        musb_ep_writeb(s, ep, addr, value & 0xff);
-        musb_ep_writeb(s, ep, addr | 1, value >> 8);
-    };
-}
-
-/* Generic control */
-static uint32_t musb_readb(void *opaque, target_phys_addr_t addr)
-{
-    MUSBState *s = (MUSBState *) opaque;
-    int ep, i;
-    uint8_t ret;
-
-    switch (addr) {
-    case MUSB_HDRC_FADDR:
-        return s->faddr;
-    case MUSB_HDRC_POWER:
-        return s->power;
-    case MUSB_HDRC_INTRUSB:
-        ret = s->intr;
-        for (i = 0; i < sizeof(ret) * 8; i ++)
-            if (ret & (1 << i))
-                musb_intr_set(s, i, 0);
-        return ret;
-    case MUSB_HDRC_INTRUSBE:
-        return s->mask;
-    case MUSB_HDRC_INDEX:
-        return s->idx;
-    case MUSB_HDRC_TESTMODE:
-        return 0x00;
-
-    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
-        return musb_ep_readb(s, s->idx, addr & 0xf);
-
-    case MUSB_HDRC_DEVCTL:
-        return s->devctl;
-
-    case MUSB_HDRC_TXFIFOSZ:
-    case MUSB_HDRC_RXFIFOSZ:
-    case MUSB_HDRC_VCTRL:
-        /* TODO */
-        return 0x00;
-
-    case MUSB_HDRC_HWVERS:
-        return (1 << 10) | 400;
-
-    case (MUSB_HDRC_VCTRL | 1):
-    case (MUSB_HDRC_HWVERS | 1):
-    case (MUSB_HDRC_DEVCTL | 1):
-        return 0x00;
-
-    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
-        ep = (addr >> 3) & 0xf;
-        return musb_busctl_readb(s, ep, addr & 0x7);
-
-    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
-        ep = (addr >> 4) & 0xf;
-        return musb_ep_readb(s, ep, addr & 0xf);
-
-    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
-        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
-        return musb_read_fifo(s->ep + ep);
-
-    default:
-        TRACE("unknown register 0x%02x", (int) addr);
-        return 0x00;
-    };
-}
-
-static void musb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
-{
-    MUSBState *s = (MUSBState *) opaque;
-    int ep;
-
-    switch (addr) {
-    case MUSB_HDRC_FADDR:
-        s->faddr = value & 0x7f;
-        break;
-    case MUSB_HDRC_POWER:
-        s->power = (value & 0xef) | (s->power & 0x10);
-        /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */
-        if ((value & MGC_M_POWER_RESET) && s->port.dev) {
-            usb_device_reset(s->port.dev);
-            /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set.  */
-            if ((value & MGC_M_POWER_HSENAB) &&
-                            s->port.dev->speed == USB_SPEED_HIGH)
-                s->power |= MGC_M_POWER_HSMODE;        /* Success */
-            /* Restart frame counting.  */
-        }
-        if (value & MGC_M_POWER_SUSPENDM) {
-            /* When all transfers finish, suspend and if MGC_M_POWER_ENSUSPEND
-             * is set, also go into low power mode.  Frame counting stops.  */
-            /* XXX: Cleared when the interrupt register is read */
-        }
-        if (value & MGC_M_POWER_RESUME) {
-            /* Wait 20ms and signal resuming on the bus.  Frame counting
-             * restarts.  */
-        }
-        break;
-    case MUSB_HDRC_INTRUSB:
-        break;
-    case MUSB_HDRC_INTRUSBE:
-        s->mask = value & 0xff;
-        break;
-    case MUSB_HDRC_INDEX:
-        s->idx = value & 0xf;
-        break;
-    case MUSB_HDRC_TESTMODE:
-        break;
-
-    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
-        musb_ep_writeb(s, s->idx, addr & 0xf, value);
-        break;
-
-    case MUSB_HDRC_DEVCTL:
-        s->session = !!(value & MGC_M_DEVCTL_SESSION);
-        musb_session_update(s,
-                        !!s->port.dev,
-                        !!(s->devctl & MGC_M_DEVCTL_SESSION));
-
-        /* It seems this is the only R/W bit in this register?  */
-        s->devctl &= ~MGC_M_DEVCTL_SESSION;
-        s->devctl |= value & MGC_M_DEVCTL_SESSION;
-        break;
-
-    case MUSB_HDRC_TXFIFOSZ:
-    case MUSB_HDRC_RXFIFOSZ:
-    case MUSB_HDRC_VCTRL:
-        /* TODO */
-        break;
-
-    case (MUSB_HDRC_VCTRL | 1):
-    case (MUSB_HDRC_DEVCTL | 1):
-        break;
-
-    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
-        ep = (addr >> 3) & 0xf;
-        musb_busctl_writeb(s, ep, addr & 0x7, value);
-        break;
-
-    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
-        ep = (addr >> 4) & 0xf;
-        musb_ep_writeb(s, ep, addr & 0xf, value);
-        break;
-
-    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
-        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
-        musb_write_fifo(s->ep + ep, value & 0xff);
-        break;
-
-    default:
-        TRACE("unknown register 0x%02x", (int) addr);
-        break;
-    };
-}
-
-static uint32_t musb_readh(void *opaque, target_phys_addr_t addr)
-{
-    MUSBState *s = (MUSBState *) opaque;
-    int ep, i;
-    uint16_t ret;
-
-    switch (addr) {
-    case MUSB_HDRC_INTRTX:
-        ret = s->tx_intr;
-        /* Auto clear */
-        for (i = 0; i < sizeof(ret) * 8; i ++)
-            if (ret & (1 << i))
-                musb_tx_intr_set(s, i, 0);
-        return ret;
-    case MUSB_HDRC_INTRRX:
-        ret = s->rx_intr;
-        /* Auto clear */
-        for (i = 0; i < sizeof(ret) * 8; i ++)
-            if (ret & (1 << i))
-                musb_rx_intr_set(s, i, 0);
-        return ret;
-    case MUSB_HDRC_INTRTXE:
-        return s->tx_mask;
-    case MUSB_HDRC_INTRRXE:
-        return s->rx_mask;
-
-    case MUSB_HDRC_FRAME:
-        /* TODO */
-        return 0x0000;
-    case MUSB_HDRC_TXFIFOADDR:
-        return s->ep[s->idx].fifoaddr[0];
-    case MUSB_HDRC_RXFIFOADDR:
-        return s->ep[s->idx].fifoaddr[1];
-
-    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
-        return musb_ep_readh(s, s->idx, addr & 0xf);
-
-    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
-        ep = (addr >> 3) & 0xf;
-        return musb_busctl_readh(s, ep, addr & 0x7);
-
-    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
-        ep = (addr >> 4) & 0xf;
-        return musb_ep_readh(s, ep, addr & 0xf);
-
-    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
-        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
-        return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8);
-
-    default:
-        return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8);
-    };
-}
-
-static void musb_writeh(void *opaque, target_phys_addr_t addr, uint32_t value)
-{
-    MUSBState *s = (MUSBState *) opaque;
-    int ep;
-
-    switch (addr) {
-    case MUSB_HDRC_INTRTXE:
-        s->tx_mask = value;
-        /* XXX: the masks seem to apply on the raising edge like with
-         * edge-triggered interrupts, thus no need to update.  I may be
-         * wrong though.  */
-        break;
-    case MUSB_HDRC_INTRRXE:
-        s->rx_mask = value;
-        break;
-
-    case MUSB_HDRC_FRAME:
-        /* TODO */
-        break;
-    case MUSB_HDRC_TXFIFOADDR:
-        s->ep[s->idx].fifoaddr[0] = value;
-        s->ep[s->idx].buf[0] =
-                s->buf + ((value << 3) & 0x7ff );
-        break;
-    case MUSB_HDRC_RXFIFOADDR:
-        s->ep[s->idx].fifoaddr[1] = value;
-        s->ep[s->idx].buf[1] =
-                s->buf + ((value << 3) & 0x7ff);
-        break;
-
-    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
-        musb_ep_writeh(s, s->idx, addr & 0xf, value);
-        break;
-
-    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
-        ep = (addr >> 3) & 0xf;
-        musb_busctl_writeh(s, ep, addr & 0x7, value);
-        break;
-
-    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
-        ep = (addr >> 4) & 0xf;
-        musb_ep_writeh(s, ep, addr & 0xf, value);
-        break;
-
-    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
-        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
-        musb_write_fifo(s->ep + ep, value & 0xff);
-        musb_write_fifo(s->ep + ep, (value >> 8) & 0xff);
-        break;
-
-    default:
-        musb_writeb(s, addr, value & 0xff);
-        musb_writeb(s, addr | 1, value >> 8);
-    };
-}
-
-static uint32_t musb_readw(void *opaque, target_phys_addr_t addr)
-{
-    MUSBState *s = (MUSBState *) opaque;
-    int ep;
-
-    switch (addr) {
-    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
-        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
-        return ( musb_read_fifo(s->ep + ep)       |
-                 musb_read_fifo(s->ep + ep) << 8  |
-                 musb_read_fifo(s->ep + ep) << 16 |
-                 musb_read_fifo(s->ep + ep) << 24 );
-    default:
-        TRACE("unknown register 0x%02x", (int) addr);
-        return 0x00000000;
-    };
-}
-
-static void musb_writew(void *opaque, target_phys_addr_t addr, uint32_t value)
-{
-    MUSBState *s = (MUSBState *) opaque;
-    int ep;
-
-    switch (addr) {
-    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
-        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
-        musb_write_fifo(s->ep + ep, value & 0xff);
-        musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff);
-        musb_write_fifo(s->ep + ep, (value >> 16) & 0xff);
-        musb_write_fifo(s->ep + ep, (value >> 24) & 0xff);
-            break;
-    default:
-        TRACE("unknown register 0x%02x", (int) addr);
-        break;
-    };
-}
-
-CPUReadMemoryFunc * const musb_read[] = {
-    musb_readb,
-    musb_readh,
-    musb_readw,
-};
-
-CPUWriteMemoryFunc * const musb_write[] = {
-    musb_writeb,
-    musb_writeh,
-    musb_writew,
-};
diff --git a/hw/usb-net.c b/hw/usb-net.c
deleted file mode 100644 (file)
index 22b8201..0000000
+++ /dev/null
@@ -1,1423 +0,0 @@
-/*
- * QEMU USB Net devices
- *
- * Copyright (c) 2006 Thomas Sailer
- * Copyright (c) 2008 Andrzej Zaborowski
- *
- * 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 "usb.h"
-#include "usb-desc.h"
-#include "net.h"
-#include "qemu-queue.h"
-#include "sysemu.h"
-#include "iov.h"
-
-/*#define TRAFFIC_DEBUG*/
-/* Thanks to NetChip Technologies for donating this product ID.
- * It's for devices with only CDC Ethernet configurations.
- */
-#define CDC_VENDOR_NUM          0x0525  /* NetChip */
-#define CDC_PRODUCT_NUM         0xa4a1  /* Linux-USB Ethernet Gadget */
-/* For hardware that can talk RNDIS and either of the above protocols,
- * use this ID ... the windows INF files will know it.
- */
-#define RNDIS_VENDOR_NUM        0x0525  /* NetChip */
-#define RNDIS_PRODUCT_NUM       0xa4a2  /* Ethernet/RNDIS Gadget */
-
-enum usbstring_idx {
-    STRING_MANUFACTURER                = 1,
-    STRING_PRODUCT,
-    STRING_ETHADDR,
-    STRING_DATA,
-    STRING_CONTROL,
-    STRING_RNDIS_CONTROL,
-    STRING_CDC,
-    STRING_SUBSET,
-    STRING_RNDIS,
-    STRING_SERIALNUMBER,
-};
-
-#define DEV_CONFIG_VALUE               1       /* CDC or a subset */
-#define DEV_RNDIS_CONFIG_VALUE         2       /* RNDIS; optional */
-
-#define USB_CDC_SUBCLASS_ACM           0x02
-#define USB_CDC_SUBCLASS_ETHERNET      0x06
-
-#define USB_CDC_PROTO_NONE             0
-#define USB_CDC_ACM_PROTO_VENDOR       0xff
-
-#define USB_CDC_HEADER_TYPE            0x00    /* header_desc */
-#define USB_CDC_CALL_MANAGEMENT_TYPE   0x01    /* call_mgmt_descriptor */
-#define USB_CDC_ACM_TYPE               0x02    /* acm_descriptor */
-#define USB_CDC_UNION_TYPE             0x06    /* union_desc */
-#define USB_CDC_ETHERNET_TYPE          0x0f    /* ether_desc */
-
-#define USB_CDC_SEND_ENCAPSULATED_COMMAND      0x00
-#define USB_CDC_GET_ENCAPSULATED_RESPONSE      0x01
-#define USB_CDC_REQ_SET_LINE_CODING            0x20
-#define USB_CDC_REQ_GET_LINE_CODING            0x21
-#define USB_CDC_REQ_SET_CONTROL_LINE_STATE     0x22
-#define USB_CDC_REQ_SEND_BREAK                 0x23
-#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40
-#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41
-#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42
-#define USB_CDC_SET_ETHERNET_PACKET_FILTER     0x43
-#define USB_CDC_GET_ETHERNET_STATISTIC         0x44
-
-#define LOG2_STATUS_INTERVAL_MSEC      5    /* 1 << 5 == 32 msec */
-#define STATUS_BYTECOUNT               16   /* 8 byte header + data */
-
-#define ETH_FRAME_LEN                  1514 /* Max. octets in frame sans FCS */
-
-static const USBDescStrings usb_net_stringtable = {
-    [STRING_MANUFACTURER]       = "QEMU",
-    [STRING_PRODUCT]            = "RNDIS/QEMU USB Network Device",
-    [STRING_ETHADDR]            = "400102030405",
-    [STRING_DATA]               = "QEMU USB Net Data Interface",
-    [STRING_CONTROL]            = "QEMU USB Net Control Interface",
-    [STRING_RNDIS_CONTROL]      = "QEMU USB Net RNDIS Control Interface",
-    [STRING_CDC]                = "QEMU USB Net CDC",
-    [STRING_SUBSET]             = "QEMU USB Net Subset",
-    [STRING_RNDIS]              = "QEMU USB Net RNDIS",
-    [STRING_SERIALNUMBER]       = "1",
-};
-
-static const USBDescIface desc_iface_rndis[] = {
-    {
-        /* RNDIS Control Interface */
-        .bInterfaceNumber              = 0,
-        .bNumEndpoints                 = 1,
-        .bInterfaceClass               = USB_CLASS_COMM,
-        .bInterfaceSubClass            = USB_CDC_SUBCLASS_ACM,
-        .bInterfaceProtocol            = USB_CDC_ACM_PROTO_VENDOR,
-        .iInterface                    = STRING_RNDIS_CONTROL,
-        .ndesc                         = 4,
-        .descs = (USBDescOther[]) {
-            {
-                /* Header Descriptor */
-                .data = (uint8_t[]) {
-                    0x05,                       /*  u8    bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
-                    USB_CDC_HEADER_TYPE,        /*  u8    bDescriptorSubType */
-                    0x10, 0x01,                 /*  le16  bcdCDC */
-                },
-            },{
-                /* Call Management Descriptor */
-                .data = (uint8_t[]) {
-                    0x05,                       /*  u8    bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
-                    USB_CDC_CALL_MANAGEMENT_TYPE, /*  u8    bDescriptorSubType */
-                    0x00,                       /*  u8    bmCapabilities */
-                    0x01,                       /*  u8    bDataInterface */
-                },
-            },{
-                /* ACM Descriptor */
-                .data = (uint8_t[]) {
-                    0x04,                       /*  u8    bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
-                    USB_CDC_ACM_TYPE,           /*  u8    bDescriptorSubType */
-                    0x00,                       /*  u8    bmCapabilities */
-                },
-            },{
-                /* Union Descriptor */
-                .data = (uint8_t[]) {
-                    0x05,                       /*  u8    bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
-                    USB_CDC_UNION_TYPE,         /*  u8    bDescriptorSubType */
-                    0x00,                       /*  u8    bMasterInterface0 */
-                    0x01,                       /*  u8    bSlaveInterface0 */
-                },
-            },
-        },
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_IN | 0x01,
-                .bmAttributes          = USB_ENDPOINT_XFER_INT,
-                .wMaxPacketSize        = STATUS_BYTECOUNT,
-                .bInterval             = 1 << LOG2_STATUS_INTERVAL_MSEC,
-            },
-        }
-    },{
-        /* RNDIS Data Interface */
-        .bInterfaceNumber              = 1,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = USB_CLASS_CDC_DATA,
-        .iInterface                    = STRING_DATA,
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_IN | 0x02,
-                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-                .wMaxPacketSize        = 0x40,
-            },{
-                .bEndpointAddress      = USB_DIR_OUT | 0x02,
-                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-                .wMaxPacketSize        = 0x40,
-            }
-        }
-    }
-};
-
-static const USBDescIface desc_iface_cdc[] = {
-    {
-        /* CDC Control Interface */
-        .bInterfaceNumber              = 0,
-        .bNumEndpoints                 = 1,
-        .bInterfaceClass               = USB_CLASS_COMM,
-        .bInterfaceSubClass            = USB_CDC_SUBCLASS_ETHERNET,
-        .bInterfaceProtocol            = USB_CDC_PROTO_NONE,
-        .iInterface                    = STRING_CONTROL,
-        .ndesc                         = 3,
-        .descs = (USBDescOther[]) {
-            {
-                /* Header Descriptor */
-                .data = (uint8_t[]) {
-                    0x05,                       /*  u8    bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
-                    USB_CDC_HEADER_TYPE,        /*  u8    bDescriptorSubType */
-                    0x10, 0x01,                 /*  le16  bcdCDC */
-                },
-            },{
-                /* Union Descriptor */
-                .data = (uint8_t[]) {
-                    0x05,                       /*  u8    bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
-                    USB_CDC_UNION_TYPE,         /*  u8    bDescriptorSubType */
-                    0x00,                       /*  u8    bMasterInterface0 */
-                    0x01,                       /*  u8    bSlaveInterface0 */
-                },
-            },{
-                /* Ethernet Descriptor */
-                .data = (uint8_t[]) {
-                    0x0d,                       /*  u8    bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
-                    USB_CDC_ETHERNET_TYPE,      /*  u8    bDescriptorSubType */
-                    STRING_ETHADDR,             /*  u8    iMACAddress */
-                    0x00, 0x00, 0x00, 0x00,     /*  le32  bmEthernetStatistics */
-                    ETH_FRAME_LEN & 0xff,
-                    ETH_FRAME_LEN >> 8,         /*  le16  wMaxSegmentSize */
-                    0x00, 0x00,                 /*  le16  wNumberMCFilters */
-                    0x00,                       /*  u8    bNumberPowerFilters */
-                },
-            },
-        },
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_IN | 0x01,
-                .bmAttributes          = USB_ENDPOINT_XFER_INT,
-                .wMaxPacketSize        = STATUS_BYTECOUNT,
-                .bInterval             = 1 << LOG2_STATUS_INTERVAL_MSEC,
-            },
-        }
-    },{
-        /* CDC Data Interface (off) */
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 0,
-        .bNumEndpoints                 = 0,
-        .bInterfaceClass               = USB_CLASS_CDC_DATA,
-    },{
-        /* CDC Data Interface */
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 1,
-        .bNumEndpoints                 = 2,
-        .bInterfaceClass               = USB_CLASS_CDC_DATA,
-        .iInterface                    = STRING_DATA,
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_IN | 0x02,
-                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-                .wMaxPacketSize        = 0x40,
-            },{
-                .bEndpointAddress      = USB_DIR_OUT | 0x02,
-                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-                .wMaxPacketSize        = 0x40,
-            }
-        }
-    }
-};
-
-static const USBDescDevice desc_device_net = {
-    .bcdUSB                        = 0x0200,
-    .bDeviceClass                  = USB_CLASS_COMM,
-    .bMaxPacketSize0               = 0x40,
-    .bNumConfigurations            = 2,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 2,
-            .bConfigurationValue   = DEV_RNDIS_CONFIG_VALUE,
-            .iConfiguration        = STRING_RNDIS,
-            .bmAttributes          = 0xc0,
-            .bMaxPower             = 0x32,
-            .nif = ARRAY_SIZE(desc_iface_rndis),
-            .ifs = desc_iface_rndis,
-        },{
-            .bNumInterfaces        = 2,
-            .bConfigurationValue   = DEV_CONFIG_VALUE,
-            .iConfiguration        = STRING_CDC,
-            .bmAttributes          = 0xc0,
-            .bMaxPower             = 0x32,
-            .nif = ARRAY_SIZE(desc_iface_cdc),
-            .ifs = desc_iface_cdc,
-        }
-    },
-};
-
-static const USBDesc desc_net = {
-    .id = {
-        .idVendor          = RNDIS_VENDOR_NUM,
-        .idProduct         = RNDIS_PRODUCT_NUM,
-        .bcdDevice         = 0,
-        .iManufacturer     = STRING_MANUFACTURER,
-        .iProduct          = STRING_PRODUCT,
-        .iSerialNumber     = STRING_SERIALNUMBER,
-    },
-    .full = &desc_device_net,
-    .str  = usb_net_stringtable,
-};
-
-/*
- * RNDIS Definitions - in theory not specific to USB.
- */
-#define RNDIS_MAXIMUM_FRAME_SIZE       1518
-#define RNDIS_MAX_TOTAL_SIZE           1558
-
-/* Remote NDIS Versions */
-#define RNDIS_MAJOR_VERSION            1
-#define RNDIS_MINOR_VERSION            0
-
-/* Status Values */
-#define RNDIS_STATUS_SUCCESS           0x00000000U /* Success */
-#define RNDIS_STATUS_FAILURE           0xc0000001U /* Unspecified error */
-#define RNDIS_STATUS_INVALID_DATA      0xc0010015U /* Invalid data */
-#define RNDIS_STATUS_NOT_SUPPORTED     0xc00000bbU /* Unsupported request */
-#define RNDIS_STATUS_MEDIA_CONNECT     0x4001000bU /* Device connected */
-#define RNDIS_STATUS_MEDIA_DISCONNECT  0x4001000cU /* Device disconnected */
-
-/* Message Set for Connectionless (802.3) Devices */
-enum {
-    RNDIS_PACKET_MSG           = 1,
-    RNDIS_INITIALIZE_MSG       = 2,    /* Initialize device */
-    RNDIS_HALT_MSG             = 3,
-    RNDIS_QUERY_MSG            = 4,
-    RNDIS_SET_MSG              = 5,
-    RNDIS_RESET_MSG            = 6,
-    RNDIS_INDICATE_STATUS_MSG  = 7,
-    RNDIS_KEEPALIVE_MSG                = 8,
-};
-
-/* Message completion */
-enum {
-    RNDIS_INITIALIZE_CMPLT     = 0x80000002U,
-    RNDIS_QUERY_CMPLT          = 0x80000004U,
-    RNDIS_SET_CMPLT            = 0x80000005U,
-    RNDIS_RESET_CMPLT          = 0x80000006U,
-    RNDIS_KEEPALIVE_CMPLT      = 0x80000008U,
-};
-
-/* Device Flags */
-enum {
-    RNDIS_DF_CONNECTIONLESS    = 1,
-    RNDIS_DF_CONNECTIONORIENTED        = 2,
-};
-
-#define RNDIS_MEDIUM_802_3             0x00000000U
-
-/* from drivers/net/sk98lin/h/skgepnmi.h */
-#define OID_PNP_CAPABILITIES           0xfd010100
-#define OID_PNP_SET_POWER              0xfd010101
-#define OID_PNP_QUERY_POWER            0xfd010102
-#define OID_PNP_ADD_WAKE_UP_PATTERN    0xfd010103
-#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xfd010104
-#define OID_PNP_ENABLE_WAKE_UP         0xfd010106
-
-typedef uint32_t le32;
-
-typedef struct rndis_init_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-    le32 MajorVersion;
-    le32 MinorVersion;
-    le32 MaxTransferSize;
-} rndis_init_msg_type;
-
-typedef struct rndis_init_cmplt_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-    le32 Status;
-    le32 MajorVersion;
-    le32 MinorVersion;
-    le32 DeviceFlags;
-    le32 Medium;
-    le32 MaxPacketsPerTransfer;
-    le32 MaxTransferSize;
-    le32 PacketAlignmentFactor;
-    le32 AFListOffset;
-    le32 AFListSize;
-} rndis_init_cmplt_type;
-
-typedef struct rndis_halt_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-} rndis_halt_msg_type;
-
-typedef struct rndis_query_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-    le32 OID;
-    le32 InformationBufferLength;
-    le32 InformationBufferOffset;
-    le32 DeviceVcHandle;
-} rndis_query_msg_type;
-
-typedef struct rndis_query_cmplt_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-    le32 Status;
-    le32 InformationBufferLength;
-    le32 InformationBufferOffset;
-} rndis_query_cmplt_type;
-
-typedef struct rndis_set_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-    le32 OID;
-    le32 InformationBufferLength;
-    le32 InformationBufferOffset;
-    le32 DeviceVcHandle;
-} rndis_set_msg_type;
-
-typedef struct rndis_set_cmplt_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-    le32 Status;
-} rndis_set_cmplt_type;
-
-typedef struct rndis_reset_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 Reserved;
-} rndis_reset_msg_type;
-
-typedef struct rndis_reset_cmplt_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 Status;
-    le32 AddressingReset;
-} rndis_reset_cmplt_type;
-
-typedef struct rndis_indicate_status_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 Status;
-    le32 StatusBufferLength;
-    le32 StatusBufferOffset;
-} rndis_indicate_status_msg_type;
-
-typedef struct rndis_keepalive_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-} rndis_keepalive_msg_type;
-
-typedef struct rndis_keepalive_cmplt_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 RequestID;
-    le32 Status;
-} rndis_keepalive_cmplt_type;
-
-struct rndis_packet_msg_type {
-    le32 MessageType;
-    le32 MessageLength;
-    le32 DataOffset;
-    le32 DataLength;
-    le32 OOBDataOffset;
-    le32 OOBDataLength;
-    le32 NumOOBDataElements;
-    le32 PerPacketInfoOffset;
-    le32 PerPacketInfoLength;
-    le32 VcHandle;
-    le32 Reserved;
-};
-
-struct rndis_config_parameter {
-    le32 ParameterNameOffset;
-    le32 ParameterNameLength;
-    le32 ParameterType;
-    le32 ParameterValueOffset;
-    le32 ParameterValueLength;
-};
-
-/* implementation specific */
-enum rndis_state
-{
-    RNDIS_UNINITIALIZED,
-    RNDIS_INITIALIZED,
-    RNDIS_DATA_INITIALIZED,
-};
-
-/* from ndis.h */
-enum ndis_oid {
-    /* Required Object IDs (OIDs) */
-    OID_GEN_SUPPORTED_LIST             = 0x00010101,
-    OID_GEN_HARDWARE_STATUS            = 0x00010102,
-    OID_GEN_MEDIA_SUPPORTED            = 0x00010103,
-    OID_GEN_MEDIA_IN_USE               = 0x00010104,
-    OID_GEN_MAXIMUM_LOOKAHEAD          = 0x00010105,
-    OID_GEN_MAXIMUM_FRAME_SIZE         = 0x00010106,
-    OID_GEN_LINK_SPEED                 = 0x00010107,
-    OID_GEN_TRANSMIT_BUFFER_SPACE      = 0x00010108,
-    OID_GEN_RECEIVE_BUFFER_SPACE       = 0x00010109,
-    OID_GEN_TRANSMIT_BLOCK_SIZE                = 0x0001010a,
-    OID_GEN_RECEIVE_BLOCK_SIZE         = 0x0001010b,
-    OID_GEN_VENDOR_ID                  = 0x0001010c,
-    OID_GEN_VENDOR_DESCRIPTION         = 0x0001010d,
-    OID_GEN_CURRENT_PACKET_FILTER      = 0x0001010e,
-    OID_GEN_CURRENT_LOOKAHEAD          = 0x0001010f,
-    OID_GEN_DRIVER_VERSION             = 0x00010110,
-    OID_GEN_MAXIMUM_TOTAL_SIZE         = 0x00010111,
-    OID_GEN_PROTOCOL_OPTIONS           = 0x00010112,
-    OID_GEN_MAC_OPTIONS                        = 0x00010113,
-    OID_GEN_MEDIA_CONNECT_STATUS       = 0x00010114,
-    OID_GEN_MAXIMUM_SEND_PACKETS       = 0x00010115,
-    OID_GEN_VENDOR_DRIVER_VERSION      = 0x00010116,
-    OID_GEN_SUPPORTED_GUIDS            = 0x00010117,
-    OID_GEN_NETWORK_LAYER_ADDRESSES    = 0x00010118,
-    OID_GEN_TRANSPORT_HEADER_OFFSET    = 0x00010119,
-    OID_GEN_MACHINE_NAME               = 0x0001021a,
-    OID_GEN_RNDIS_CONFIG_PARAMETER     = 0x0001021b,
-    OID_GEN_VLAN_ID                    = 0x0001021c,
-
-    /* Optional OIDs */
-    OID_GEN_MEDIA_CAPABILITIES         = 0x00010201,
-    OID_GEN_PHYSICAL_MEDIUM            = 0x00010202,
-
-    /* Required statistics OIDs */
-    OID_GEN_XMIT_OK                    = 0x00020101,
-    OID_GEN_RCV_OK                     = 0x00020102,
-    OID_GEN_XMIT_ERROR                 = 0x00020103,
-    OID_GEN_RCV_ERROR                  = 0x00020104,
-    OID_GEN_RCV_NO_BUFFER              = 0x00020105,
-
-    /* Optional statistics OIDs */
-    OID_GEN_DIRECTED_BYTES_XMIT                = 0x00020201,
-    OID_GEN_DIRECTED_FRAMES_XMIT       = 0x00020202,
-    OID_GEN_MULTICAST_BYTES_XMIT       = 0x00020203,
-    OID_GEN_MULTICAST_FRAMES_XMIT      = 0x00020204,
-    OID_GEN_BROADCAST_BYTES_XMIT       = 0x00020205,
-    OID_GEN_BROADCAST_FRAMES_XMIT      = 0x00020206,
-    OID_GEN_DIRECTED_BYTES_RCV         = 0x00020207,
-    OID_GEN_DIRECTED_FRAMES_RCV                = 0x00020208,
-    OID_GEN_MULTICAST_BYTES_RCV                = 0x00020209,
-    OID_GEN_MULTICAST_FRAMES_RCV       = 0x0002020a,
-    OID_GEN_BROADCAST_BYTES_RCV                = 0x0002020b,
-    OID_GEN_BROADCAST_FRAMES_RCV       = 0x0002020c,
-    OID_GEN_RCV_CRC_ERROR              = 0x0002020d,
-    OID_GEN_TRANSMIT_QUEUE_LENGTH      = 0x0002020e,
-    OID_GEN_GET_TIME_CAPS              = 0x0002020f,
-    OID_GEN_GET_NETCARD_TIME           = 0x00020210,
-    OID_GEN_NETCARD_LOAD               = 0x00020211,
-    OID_GEN_DEVICE_PROFILE             = 0x00020212,
-    OID_GEN_INIT_TIME_MS               = 0x00020213,
-    OID_GEN_RESET_COUNTS               = 0x00020214,
-    OID_GEN_MEDIA_SENSE_COUNTS         = 0x00020215,
-    OID_GEN_FRIENDLY_NAME              = 0x00020216,
-    OID_GEN_MINIPORT_INFO              = 0x00020217,
-    OID_GEN_RESET_VERIFY_PARAMETERS    = 0x00020218,
-
-    /* IEEE 802.3 (Ethernet) OIDs */
-    OID_802_3_PERMANENT_ADDRESS                = 0x01010101,
-    OID_802_3_CURRENT_ADDRESS          = 0x01010102,
-    OID_802_3_MULTICAST_LIST           = 0x01010103,
-    OID_802_3_MAXIMUM_LIST_SIZE                = 0x01010104,
-    OID_802_3_MAC_OPTIONS              = 0x01010105,
-    OID_802_3_RCV_ERROR_ALIGNMENT      = 0x01020101,
-    OID_802_3_XMIT_ONE_COLLISION       = 0x01020102,
-    OID_802_3_XMIT_MORE_COLLISIONS     = 0x01020103,
-    OID_802_3_XMIT_DEFERRED            = 0x01020201,
-    OID_802_3_XMIT_MAX_COLLISIONS      = 0x01020202,
-    OID_802_3_RCV_OVERRUN              = 0x01020203,
-    OID_802_3_XMIT_UNDERRUN            = 0x01020204,
-    OID_802_3_XMIT_HEARTBEAT_FAILURE   = 0x01020205,
-    OID_802_3_XMIT_TIMES_CRS_LOST      = 0x01020206,
-    OID_802_3_XMIT_LATE_COLLISIONS     = 0x01020207,
-};
-
-static const uint32_t oid_supported_list[] =
-{
-    /* the general stuff */
-    OID_GEN_SUPPORTED_LIST,
-    OID_GEN_HARDWARE_STATUS,
-    OID_GEN_MEDIA_SUPPORTED,
-    OID_GEN_MEDIA_IN_USE,
-    OID_GEN_MAXIMUM_FRAME_SIZE,
-    OID_GEN_LINK_SPEED,
-    OID_GEN_TRANSMIT_BLOCK_SIZE,
-    OID_GEN_RECEIVE_BLOCK_SIZE,
-    OID_GEN_VENDOR_ID,
-    OID_GEN_VENDOR_DESCRIPTION,
-    OID_GEN_VENDOR_DRIVER_VERSION,
-    OID_GEN_CURRENT_PACKET_FILTER,
-    OID_GEN_MAXIMUM_TOTAL_SIZE,
-    OID_GEN_MEDIA_CONNECT_STATUS,
-    OID_GEN_PHYSICAL_MEDIUM,
-
-    /* the statistical stuff */
-    OID_GEN_XMIT_OK,
-    OID_GEN_RCV_OK,
-    OID_GEN_XMIT_ERROR,
-    OID_GEN_RCV_ERROR,
-    OID_GEN_RCV_NO_BUFFER,
-
-    /* IEEE 802.3 */
-    /* the general stuff */
-    OID_802_3_PERMANENT_ADDRESS,
-    OID_802_3_CURRENT_ADDRESS,
-    OID_802_3_MULTICAST_LIST,
-    OID_802_3_MAC_OPTIONS,
-    OID_802_3_MAXIMUM_LIST_SIZE,
-
-    /* the statistical stuff */
-    OID_802_3_RCV_ERROR_ALIGNMENT,
-    OID_802_3_XMIT_ONE_COLLISION,
-    OID_802_3_XMIT_MORE_COLLISIONS,
-};
-
-#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA    (1 << 0)
-#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED     (1 << 1)
-#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND     (1 << 2)
-#define NDIS_MAC_OPTION_NO_LOOPBACK            (1 << 3)
-#define NDIS_MAC_OPTION_FULL_DUPLEX            (1 << 4)
-#define NDIS_MAC_OPTION_EOTX_INDICATION                (1 << 5)
-#define NDIS_MAC_OPTION_8021P_PRIORITY         (1 << 6)
-
-struct rndis_response {
-    QTAILQ_ENTRY(rndis_response) entries;
-    uint32_t length;
-    uint8_t buf[0];
-};
-
-typedef struct USBNetState {
-    USBDevice dev;
-
-    enum rndis_state rndis_state;
-    uint32_t medium;
-    uint32_t speed;
-    uint32_t media_state;
-    uint16_t filter;
-    uint32_t vendorid;
-
-    unsigned int out_ptr;
-    uint8_t out_buf[2048];
-
-    USBPacket *inpkt;
-    unsigned int in_ptr, in_len;
-    uint8_t in_buf[2048];
-
-    char usbstring_mac[13];
-    NICState *nic;
-    NICConf conf;
-    QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp;
-} USBNetState;
-
-static int is_rndis(USBNetState *s)
-{
-    return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE;
-}
-
-static int ndis_query(USBNetState *s, uint32_t oid,
-                      uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf,
-                      size_t outlen)
-{
-    unsigned int i;
-
-    switch (oid) {
-    /* general oids (table 4-1) */
-    /* mandatory */
-    case OID_GEN_SUPPORTED_LIST:
-        for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++)
-            ((le32 *) outbuf)[i] = cpu_to_le32(oid_supported_list[i]);
-        return sizeof(oid_supported_list);
-
-    /* mandatory */
-    case OID_GEN_HARDWARE_STATUS:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_MEDIA_SUPPORTED:
-        *((le32 *) outbuf) = cpu_to_le32(s->medium);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_MEDIA_IN_USE:
-        *((le32 *) outbuf) = cpu_to_le32(s->medium);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_MAXIMUM_FRAME_SIZE:
-        *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_LINK_SPEED:
-        *((le32 *) outbuf) = cpu_to_le32(s->speed);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_TRANSMIT_BLOCK_SIZE:
-        *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_RECEIVE_BLOCK_SIZE:
-        *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_VENDOR_ID:
-        *((le32 *) outbuf) = cpu_to_le32(s->vendorid);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_VENDOR_DESCRIPTION:
-        pstrcpy((char *)outbuf, outlen, "QEMU USB RNDIS Net");
-        return strlen((char *)outbuf) + 1;
-
-    case OID_GEN_VENDOR_DRIVER_VERSION:
-        *((le32 *) outbuf) = cpu_to_le32(1);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_CURRENT_PACKET_FILTER:
-        *((le32 *) outbuf) = cpu_to_le32(s->filter);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_MAXIMUM_TOTAL_SIZE:
-        *((le32 *) outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_MEDIA_CONNECT_STATUS:
-        *((le32 *) outbuf) = cpu_to_le32(s->media_state);
-        return sizeof(le32);
-
-    case OID_GEN_PHYSICAL_MEDIUM:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    case OID_GEN_MAC_OPTIONS:
-        *((le32 *) outbuf) = cpu_to_le32(
-                        NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
-                        NDIS_MAC_OPTION_FULL_DUPLEX);
-        return sizeof(le32);
-
-    /* statistics OIDs (table 4-2) */
-    /* mandatory */
-    case OID_GEN_XMIT_OK:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_RCV_OK:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_XMIT_ERROR:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_RCV_ERROR:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_GEN_RCV_NO_BUFFER:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* ieee802.3 OIDs (table 4-3) */
-    /* mandatory */
-    case OID_802_3_PERMANENT_ADDRESS:
-        memcpy(outbuf, s->conf.macaddr.a, 6);
-        return 6;
-
-    /* mandatory */
-    case OID_802_3_CURRENT_ADDRESS:
-        memcpy(outbuf, s->conf.macaddr.a, 6);
-        return 6;
-
-    /* mandatory */
-    case OID_802_3_MULTICAST_LIST:
-        *((le32 *) outbuf) = cpu_to_le32(0xe0000000);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_802_3_MAXIMUM_LIST_SIZE:
-        *((le32 *) outbuf) = cpu_to_le32(1);
-        return sizeof(le32);
-
-    case OID_802_3_MAC_OPTIONS:
-        return 0;
-
-    /* ieee802.3 statistics OIDs (table 4-4) */
-    /* mandatory */
-    case OID_802_3_RCV_ERROR_ALIGNMENT:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_802_3_XMIT_ONE_COLLISION:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    /* mandatory */
-    case OID_802_3_XMIT_MORE_COLLISIONS:
-        *((le32 *) outbuf) = cpu_to_le32(0);
-        return sizeof(le32);
-
-    default:
-        fprintf(stderr, "usbnet: unknown OID 0x%08x\n", oid);
-        return 0;
-    }
-    return -1;
-}
-
-static int ndis_set(USBNetState *s, uint32_t oid,
-                uint8_t *inbuf, unsigned int inlen)
-{
-    switch (oid) {
-    case OID_GEN_CURRENT_PACKET_FILTER:
-        s->filter = le32_to_cpup((le32 *) inbuf);
-        if (s->filter) {
-            s->rndis_state = RNDIS_DATA_INITIALIZED;
-        } else {
-            s->rndis_state = RNDIS_INITIALIZED;
-        }
-        return 0;
-
-    case OID_802_3_MULTICAST_LIST:
-        return 0;
-    }
-    return -1;
-}
-
-static int rndis_get_response(USBNetState *s, uint8_t *buf)
-{
-    int ret = 0;
-    struct rndis_response *r = s->rndis_resp.tqh_first;
-
-    if (!r)
-        return ret;
-
-    QTAILQ_REMOVE(&s->rndis_resp, r, entries);
-    ret = r->length;
-    memcpy(buf, r->buf, r->length);
-    g_free(r);
-
-    return ret;
-}
-
-static void *rndis_queue_response(USBNetState *s, unsigned int length)
-{
-    struct rndis_response *r =
-            g_malloc0(sizeof(struct rndis_response) + length);
-
-    QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries);
-    r->length = length;
-
-    return &r->buf[0];
-}
-
-static void rndis_clear_responsequeue(USBNetState *s)
-{
-    struct rndis_response *r;
-
-    while ((r = s->rndis_resp.tqh_first)) {
-        QTAILQ_REMOVE(&s->rndis_resp, r, entries);
-        g_free(r);
-    }
-}
-
-static int rndis_init_response(USBNetState *s, rndis_init_msg_type *buf)
-{
-    rndis_init_cmplt_type *resp =
-            rndis_queue_response(s, sizeof(rndis_init_cmplt_type));
-
-    if (!resp)
-        return USB_RET_STALL;
-
-    resp->MessageType = cpu_to_le32(RNDIS_INITIALIZE_CMPLT);
-    resp->MessageLength = cpu_to_le32(sizeof(rndis_init_cmplt_type));
-    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-    resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
-    resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
-    resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
-    resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
-    resp->MaxPacketsPerTransfer = cpu_to_le32(1);
-    resp->MaxTransferSize = cpu_to_le32(ETH_FRAME_LEN +
-                    sizeof(struct rndis_packet_msg_type) + 22);
-    resp->PacketAlignmentFactor = cpu_to_le32(0);
-    resp->AFListOffset = cpu_to_le32(0);
-    resp->AFListSize = cpu_to_le32(0);
-    return 0;
-}
-
-static int rndis_query_response(USBNetState *s,
-                rndis_query_msg_type *buf, unsigned int length)
-{
-    rndis_query_cmplt_type *resp;
-    /* oid_supported_list is the largest data reply */
-    uint8_t infobuf[sizeof(oid_supported_list)];
-    uint32_t bufoffs, buflen;
-    int infobuflen;
-    unsigned int resplen;
-
-    bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
-    buflen = le32_to_cpu(buf->InformationBufferLength);
-    if (bufoffs + buflen > length)
-        return USB_RET_STALL;
-
-    infobuflen = ndis_query(s, le32_to_cpu(buf->OID),
-                            bufoffs + (uint8_t *) buf, buflen, infobuf,
-                            sizeof(infobuf));
-    resplen = sizeof(rndis_query_cmplt_type) +
-            ((infobuflen < 0) ? 0 : infobuflen);
-    resp = rndis_queue_response(s, resplen);
-    if (!resp)
-        return USB_RET_STALL;
-
-    resp->MessageType = cpu_to_le32(RNDIS_QUERY_CMPLT);
-    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-    resp->MessageLength = cpu_to_le32(resplen);
-
-    if (infobuflen < 0) {
-        /* OID not supported */
-        resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
-        resp->InformationBufferLength = cpu_to_le32(0);
-        resp->InformationBufferOffset = cpu_to_le32(0);
-        return 0;
-    }
-
-    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-    resp->InformationBufferOffset =
-            cpu_to_le32(infobuflen ? sizeof(rndis_query_cmplt_type) - 8 : 0);
-    resp->InformationBufferLength = cpu_to_le32(infobuflen);
-    memcpy(resp + 1, infobuf, infobuflen);
-
-    return 0;
-}
-
-static int rndis_set_response(USBNetState *s,
-                rndis_set_msg_type *buf, unsigned int length)
-{
-    rndis_set_cmplt_type *resp =
-            rndis_queue_response(s, sizeof(rndis_set_cmplt_type));
-    uint32_t bufoffs, buflen;
-    int ret;
-
-    if (!resp)
-        return USB_RET_STALL;
-
-    bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
-    buflen = le32_to_cpu(buf->InformationBufferLength);
-    if (bufoffs + buflen > length)
-        return USB_RET_STALL;
-
-    ret = ndis_set(s, le32_to_cpu(buf->OID),
-                    bufoffs + (uint8_t *) buf, buflen);
-    resp->MessageType = cpu_to_le32(RNDIS_SET_CMPLT);
-    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-    resp->MessageLength = cpu_to_le32(sizeof(rndis_set_cmplt_type));
-    if (ret < 0) {
-        /* OID not supported */
-        resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
-        return 0;
-    }
-    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-
-    return 0;
-}
-
-static int rndis_reset_response(USBNetState *s, rndis_reset_msg_type *buf)
-{
-    rndis_reset_cmplt_type *resp =
-            rndis_queue_response(s, sizeof(rndis_reset_cmplt_type));
-
-    if (!resp)
-        return USB_RET_STALL;
-
-    resp->MessageType = cpu_to_le32(RNDIS_RESET_CMPLT);
-    resp->MessageLength = cpu_to_le32(sizeof(rndis_reset_cmplt_type));
-    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-    resp->AddressingReset = cpu_to_le32(1); /* reset information */
-
-    return 0;
-}
-
-static int rndis_keepalive_response(USBNetState *s,
-                rndis_keepalive_msg_type *buf)
-{
-    rndis_keepalive_cmplt_type *resp =
-            rndis_queue_response(s, sizeof(rndis_keepalive_cmplt_type));
-
-    if (!resp)
-        return USB_RET_STALL;
-
-    resp->MessageType = cpu_to_le32(RNDIS_KEEPALIVE_CMPLT);
-    resp->MessageLength = cpu_to_le32(sizeof(rndis_keepalive_cmplt_type));
-    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-
-    return 0;
-}
-
-static int rndis_parse(USBNetState *s, uint8_t *data, int length)
-{
-    uint32_t msg_type;
-    le32 *tmp = (le32 *) data;
-
-    msg_type = le32_to_cpup(tmp);
-
-    switch (msg_type) {
-    case RNDIS_INITIALIZE_MSG:
-        s->rndis_state = RNDIS_INITIALIZED;
-        return rndis_init_response(s, (rndis_init_msg_type *) data);
-
-    case RNDIS_HALT_MSG:
-        s->rndis_state = RNDIS_UNINITIALIZED;
-        return 0;
-
-    case RNDIS_QUERY_MSG:
-        return rndis_query_response(s, (rndis_query_msg_type *) data, length);
-
-    case RNDIS_SET_MSG:
-        return rndis_set_response(s, (rndis_set_msg_type *) data, length);
-
-    case RNDIS_RESET_MSG:
-        rndis_clear_responsequeue(s);
-        s->out_ptr = s->in_ptr = s->in_len = 0;
-        return rndis_reset_response(s, (rndis_reset_msg_type *) data);
-
-    case RNDIS_KEEPALIVE_MSG:
-        /* For USB: host does this every 5 seconds */
-        return rndis_keepalive_response(s, (rndis_keepalive_msg_type *) data);
-    }
-
-    return USB_RET_STALL;
-}
-
-static void usb_net_handle_reset(USBDevice *dev)
-{
-}
-
-static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    USBNetState *s = (USBNetState *) dev;
-    int ret;
-
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    ret = 0;
-    switch(request) {
-    case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
-        if (!is_rndis(s) || value || index != 0) {
-            goto fail;
-        }
-#ifdef TRAFFIC_DEBUG
-        {
-            unsigned int i;
-            fprintf(stderr, "SEND_ENCAPSULATED_COMMAND:");
-            for (i = 0; i < length; i++) {
-                if (!(i & 15))
-                    fprintf(stderr, "\n%04x:", i);
-                fprintf(stderr, " %02x", data[i]);
-            }
-            fprintf(stderr, "\n\n");
-        }
-#endif
-        ret = rndis_parse(s, data, length);
-        break;
-
-    case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
-        if (!is_rndis(s) || value || index != 0) {
-            goto fail;
-        }
-        ret = rndis_get_response(s, data);
-        if (!ret) {
-            data[0] = 0;
-            ret = 1;
-        }
-#ifdef TRAFFIC_DEBUG
-        {
-            unsigned int i;
-            fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:");
-            for (i = 0; i < ret; i++) {
-                if (!(i & 15))
-                    fprintf(stderr, "\n%04x:", i);
-                fprintf(stderr, " %02x", data[i]);
-            }
-            fprintf(stderr, "\n\n");
-        }
-#endif
-        break;
-
-    default:
-    fail:
-        fprintf(stderr, "usbnet: failed control transaction: "
-                        "request 0x%x value 0x%x index 0x%x length 0x%x\n",
-                        request, value, index, length);
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static int usb_net_handle_statusin(USBNetState *s, USBPacket *p)
-{
-    le32 buf[2];
-    int ret = 8;
-
-    if (p->iov.size < 8) {
-        return USB_RET_STALL;
-    }
-
-    buf[0] = cpu_to_le32(1);
-    buf[1] = cpu_to_le32(0);
-    usb_packet_copy(p, buf, 8);
-    if (!s->rndis_resp.tqh_first)
-        ret = USB_RET_NAK;
-
-#ifdef TRAFFIC_DEBUG
-    fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
-            p->iov.size, ret);
-    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
-#endif
-
-    return ret;
-}
-
-static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
-{
-    int ret = USB_RET_NAK;
-
-    if (s->in_ptr > s->in_len) {
-        s->in_ptr = s->in_len = 0;
-        ret = USB_RET_NAK;
-        return ret;
-    }
-    if (!s->in_len) {
-        ret = USB_RET_NAK;
-        return ret;
-    }
-    ret = s->in_len - s->in_ptr;
-    if (ret > p->iov.size) {
-        ret = p->iov.size;
-    }
-    usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
-    s->in_ptr += ret;
-    if (s->in_ptr >= s->in_len &&
-                    (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
-        /* no short packet necessary */
-        s->in_ptr = s->in_len = 0;
-    }
-
-#ifdef TRAFFIC_DEBUG
-    fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret);
-    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
-#endif
-
-    return ret;
-}
-
-static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
-{
-    int ret = p->iov.size;
-    int sz = sizeof(s->out_buf) - s->out_ptr;
-    struct rndis_packet_msg_type *msg =
-            (struct rndis_packet_msg_type *) s->out_buf;
-    uint32_t len;
-
-#ifdef TRAFFIC_DEBUG
-    fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size);
-    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size);
-#endif
-
-    if (sz > ret)
-        sz = ret;
-    usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
-    s->out_ptr += sz;
-
-    if (!is_rndis(s)) {
-        if (ret < 64) {
-            qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
-            s->out_ptr = 0;
-        }
-        return ret;
-    }
-    len = le32_to_cpu(msg->MessageLength);
-    if (s->out_ptr < 8 || s->out_ptr < len)
-        return ret;
-    if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
-        uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
-        uint32_t size = le32_to_cpu(msg->DataLength);
-        if (offs + size <= len)
-            qemu_send_packet(&s->nic->nc, s->out_buf + offs, size);
-    }
-    s->out_ptr -= len;
-    memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
-
-    return ret;
-}
-
-static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBNetState *s = (USBNetState *) dev;
-    int ret = 0;
-
-    switch(p->pid) {
-    case USB_TOKEN_IN:
-        switch (p->ep->nr) {
-        case 1:
-            ret = usb_net_handle_statusin(s, p);
-            break;
-
-        case 2:
-            ret = usb_net_handle_datain(s, p);
-            break;
-
-        default:
-            goto fail;
-        }
-        break;
-
-    case USB_TOKEN_OUT:
-        switch (p->ep->nr) {
-        case 2:
-            ret = usb_net_handle_dataout(s, p);
-            break;
-
-        default:
-            goto fail;
-        }
-        break;
-
-    default:
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-    if (ret == USB_RET_STALL)
-        fprintf(stderr, "usbnet: failed data transaction: "
-                        "pid 0x%x ep 0x%x len 0x%zx\n",
-                        p->pid, p->ep->nr, p->iov.size);
-    return ret;
-}
-
-static ssize_t usbnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
-{
-    USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
-    struct rndis_packet_msg_type *msg;
-
-    if (is_rndis(s)) {
-        msg = (struct rndis_packet_msg_type *) s->in_buf;
-        if (s->rndis_state != RNDIS_DATA_INITIALIZED) {
-            return -1;
-        }
-        if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf))
-            return -1;
-
-        memset(msg, 0, sizeof(struct rndis_packet_msg_type));
-        msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG);
-        msg->MessageLength = cpu_to_le32(size + sizeof(struct rndis_packet_msg_type));
-        msg->DataOffset = cpu_to_le32(sizeof(struct rndis_packet_msg_type) - 8);
-        msg->DataLength = cpu_to_le32(size);
-        /* msg->OOBDataOffset;
-         * msg->OOBDataLength;
-         * msg->NumOOBDataElements;
-         * msg->PerPacketInfoOffset;
-         * msg->PerPacketInfoLength;
-         * msg->VcHandle;
-         * msg->Reserved;
-         */
-        memcpy(msg + 1, buf, size);
-        s->in_len = size + sizeof(struct rndis_packet_msg_type);
-    } else {
-        if (size > sizeof(s->in_buf))
-            return -1;
-        memcpy(s->in_buf, buf, size);
-        s->in_len = size;
-    }
-    s->in_ptr = 0;
-    return size;
-}
-
-static int usbnet_can_receive(VLANClientState *nc)
-{
-    USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) {
-        return 1;
-    }
-
-    return !s->in_len;
-}
-
-static void usbnet_cleanup(VLANClientState *nc)
-{
-    USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    s->nic = NULL;
-}
-
-static void usb_net_handle_destroy(USBDevice *dev)
-{
-    USBNetState *s = (USBNetState *) dev;
-
-    /* TODO: remove the nd_table[] entry */
-    rndis_clear_responsequeue(s);
-    qemu_del_vlan_client(&s->nic->nc);
-}
-
-static NetClientInfo net_usbnet_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = usbnet_can_receive,
-    .receive = usbnet_receive,
-    .cleanup = usbnet_cleanup,
-};
-
-static int usb_net_initfn(USBDevice *dev)
-{
-    USBNetState *s = DO_UPCAST(USBNetState, dev, dev);
-
-    usb_desc_init(dev);
-
-    s->rndis_state = RNDIS_UNINITIALIZED;
-    QTAILQ_INIT(&s->rndis_resp);
-
-    s->medium = 0;     /* NDIS_MEDIUM_802_3 */
-    s->speed = 1000000; /* 100MBps, in 100Bps units */
-    s->media_state = 0;        /* NDIS_MEDIA_STATE_CONNECTED */;
-    s->filter = 0;
-    s->vendorid = 0x1234;
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
-                          object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
-    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
-    snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
-             "%02x%02x%02x%02x%02x%02x",
-             0x40,
-             s->conf.macaddr.a[1],
-             s->conf.macaddr.a[2],
-             s->conf.macaddr.a[3],
-             s->conf.macaddr.a[4],
-             s->conf.macaddr.a[5]);
-    usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac);
-
-    add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0");
-    return 0;
-}
-
-static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
-{
-    USBDevice *dev;
-    QemuOpts *opts;
-    int idx;
-
-    opts = qemu_opts_parse(qemu_find_opts("net"), cmdline, 0);
-    if (!opts) {
-        return NULL;
-    }
-    qemu_opt_set(opts, "type", "nic");
-    qemu_opt_set(opts, "model", "usb");
-
-    idx = net_client_init(NULL, opts, 0);
-    if (idx == -1) {
-        return NULL;
-    }
-
-    dev = usb_create(bus, "usb-net");
-    if (!dev) {
-        return NULL;
-    }
-    qdev_set_nic_properties(&dev->qdev, &nd_table[idx]);
-    qdev_init_nofail(&dev->qdev);
-    return dev;
-}
-
-static const VMStateDescription vmstate_usb_net = {
-    .name = "usb-net",
-    .unmigratable = 1,
-};
-
-static Property net_properties[] = {
-    DEFINE_NIC_PROPERTIES(USBNetState, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usb_net_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = usb_net_initfn;
-    uc->product_desc   = "QEMU USB Network Interface";
-    uc->usb_desc       = &desc_net;
-    uc->handle_reset   = usb_net_handle_reset;
-    uc->handle_control = usb_net_handle_control;
-    uc->handle_data    = usb_net_handle_data;
-    uc->handle_destroy = usb_net_handle_destroy;
-    dc->fw_name = "network";
-    dc->vmsd = &vmstate_usb_net;
-    dc->props = net_properties;
-}
-
-static TypeInfo net_info = {
-    .name          = "usb-net",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBNetState),
-    .class_init    = usb_net_class_initfn,
-};
-
-static void usb_net_register_types(void)
-{
-    type_register_static(&net_info);
-    usb_legacy_register("usb-net", "net", usb_net_init);
-}
-
-type_init(usb_net_register_types)
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
deleted file mode 100644 (file)
index 20aaa74..0000000
+++ /dev/null
@@ -1,1898 +0,0 @@
-/*
- * QEMU USB OHCI Emulation
- * Copyright (c) 2004 Gianni Tedesco
- * Copyright (c) 2006 CodeSourcery
- * Copyright (c) 2006 Openedhand Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * TODO:
- *  o Isochronous transfers
- *  o Allocate bandwidth in frames properly
- *  o Disable timers when nothing needs to be done, or remove timer usage
- *    all together.
- *  o Handle unrecoverable errors properly
- *  o BIOS work to boot from USB storage
-*/
-
-#include "hw.h"
-#include "qemu-timer.h"
-#include "usb.h"
-#include "pci.h"
-#include "usb-ohci.h"
-#include "sysbus.h"
-#include "qdev-addr.h"
-
-//#define DEBUG_OHCI
-/* Dump packet contents.  */
-//#define DEBUG_PACKET
-//#define DEBUG_ISOCH
-/* This causes frames to occur 1000x slower */
-//#define OHCI_TIME_WARP 1
-
-#ifdef DEBUG_OHCI
-#define DPRINTF printf
-#else
-#define DPRINTF(...)
-#endif
-
-/* Number of Downstream Ports on the root hub.  */
-
-#define OHCI_MAX_PORTS 15
-
-static int64_t usb_frame_time;
-static int64_t usb_bit_time;
-
-typedef struct OHCIPort {
-    USBPort port;
-    uint32_t ctrl;
-} OHCIPort;
-
-typedef struct {
-    USBBus bus;
-    qemu_irq irq;
-    MemoryRegion mem;
-    int num_ports;
-    const char *name;
-
-    QEMUTimer *eof_timer;
-    int64_t sof_time;
-
-    /* OHCI state */
-    /* Control partition */
-    uint32_t ctl, status;
-    uint32_t intr_status;
-    uint32_t intr;
-
-    /* memory pointer partition */
-    uint32_t hcca;
-    uint32_t ctrl_head, ctrl_cur;
-    uint32_t bulk_head, bulk_cur;
-    uint32_t per_cur;
-    uint32_t done;
-    int done_count;
-
-    /* Frame counter partition */
-    uint32_t fsmps:15;
-    uint32_t fit:1;
-    uint32_t fi:14;
-    uint32_t frt:1;
-    uint16_t frame_number;
-    uint16_t padding;
-    uint32_t pstart;
-    uint32_t lst;
-
-    /* Root Hub partition */
-    uint32_t rhdesc_a, rhdesc_b;
-    uint32_t rhstatus;
-    OHCIPort rhport[OHCI_MAX_PORTS];
-
-    /* PXA27x Non-OHCI events */
-    uint32_t hstatus;
-    uint32_t hmask;
-    uint32_t hreset;
-    uint32_t htest;
-
-    /* SM501 local memory offset */
-    target_phys_addr_t localmem_base;
-
-    /* Active packets.  */
-    uint32_t old_ctl;
-    USBPacket usb_packet;
-    uint8_t usb_buf[8192];
-    uint32_t async_td;
-    int async_complete;
-
-} OHCIState;
-
-/* Host Controller Communications Area */
-struct ohci_hcca {
-    uint32_t intr[32];
-    uint16_t frame, pad;
-    uint32_t done;
-};
-
-static void ohci_bus_stop(OHCIState *ohci);
-static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
-
-/* Bitfields for the first word of an Endpoint Desciptor.  */
-#define OHCI_ED_FA_SHIFT  0
-#define OHCI_ED_FA_MASK   (0x7f<<OHCI_ED_FA_SHIFT)
-#define OHCI_ED_EN_SHIFT  7
-#define OHCI_ED_EN_MASK   (0xf<<OHCI_ED_EN_SHIFT)
-#define OHCI_ED_D_SHIFT   11
-#define OHCI_ED_D_MASK    (3<<OHCI_ED_D_SHIFT)
-#define OHCI_ED_S         (1<<13)
-#define OHCI_ED_K         (1<<14)
-#define OHCI_ED_F         (1<<15)
-#define OHCI_ED_MPS_SHIFT 16
-#define OHCI_ED_MPS_MASK  (0x7ff<<OHCI_ED_MPS_SHIFT)
-
-/* Flags in the head field of an Endpoint Desciptor.  */
-#define OHCI_ED_H         1
-#define OHCI_ED_C         2
-
-/* Bitfields for the first word of a Transfer Desciptor.  */
-#define OHCI_TD_R         (1<<18)
-#define OHCI_TD_DP_SHIFT  19
-#define OHCI_TD_DP_MASK   (3<<OHCI_TD_DP_SHIFT)
-#define OHCI_TD_DI_SHIFT  21
-#define OHCI_TD_DI_MASK   (7<<OHCI_TD_DI_SHIFT)
-#define OHCI_TD_T0        (1<<24)
-#define OHCI_TD_T1        (1<<25)
-#define OHCI_TD_EC_SHIFT  26
-#define OHCI_TD_EC_MASK   (3<<OHCI_TD_EC_SHIFT)
-#define OHCI_TD_CC_SHIFT  28
-#define OHCI_TD_CC_MASK   (0xf<<OHCI_TD_CC_SHIFT)
-
-/* Bitfields for the first word of an Isochronous Transfer Desciptor.  */
-/* CC & DI - same as in the General Transfer Desciptor */
-#define OHCI_TD_SF_SHIFT  0
-#define OHCI_TD_SF_MASK   (0xffff<<OHCI_TD_SF_SHIFT)
-#define OHCI_TD_FC_SHIFT  24
-#define OHCI_TD_FC_MASK   (7<<OHCI_TD_FC_SHIFT)
-
-/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */
-#define OHCI_TD_PSW_CC_SHIFT 12
-#define OHCI_TD_PSW_CC_MASK  (0xf<<OHCI_TD_PSW_CC_SHIFT)
-#define OHCI_TD_PSW_SIZE_SHIFT 0
-#define OHCI_TD_PSW_SIZE_MASK  (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
-
-#define OHCI_PAGE_MASK    0xfffff000
-#define OHCI_OFFSET_MASK  0xfff
-
-#define OHCI_DPTR_MASK    0xfffffff0
-
-#define OHCI_BM(val, field) \
-  (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT)
-
-#define OHCI_SET_BM(val, field, newval) do { \
-    val &= ~OHCI_##field##_MASK; \
-    val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \
-    } while(0)
-
-/* endpoint descriptor */
-struct ohci_ed {
-    uint32_t flags;
-    uint32_t tail;
-    uint32_t head;
-    uint32_t next;
-};
-
-/* General transfer descriptor */
-struct ohci_td {
-    uint32_t flags;
-    uint32_t cbp;
-    uint32_t next;
-    uint32_t be;
-};
-
-/* Isochronous transfer descriptor */
-struct ohci_iso_td {
-    uint32_t flags;
-    uint32_t bp;
-    uint32_t next;
-    uint32_t be;
-    uint16_t offset[8];
-};
-
-#define USB_HZ                      12000000
-
-/* OHCI Local stuff */
-#define OHCI_CTL_CBSR         ((1<<0)|(1<<1))
-#define OHCI_CTL_PLE          (1<<2)
-#define OHCI_CTL_IE           (1<<3)
-#define OHCI_CTL_CLE          (1<<4)
-#define OHCI_CTL_BLE          (1<<5)
-#define OHCI_CTL_HCFS         ((1<<6)|(1<<7))
-#define  OHCI_USB_RESET       0x00
-#define  OHCI_USB_RESUME      0x40
-#define  OHCI_USB_OPERATIONAL 0x80
-#define  OHCI_USB_SUSPEND     0xc0
-#define OHCI_CTL_IR           (1<<8)
-#define OHCI_CTL_RWC          (1<<9)
-#define OHCI_CTL_RWE          (1<<10)
-
-#define OHCI_STATUS_HCR       (1<<0)
-#define OHCI_STATUS_CLF       (1<<1)
-#define OHCI_STATUS_BLF       (1<<2)
-#define OHCI_STATUS_OCR       (1<<3)
-#define OHCI_STATUS_SOC       ((1<<6)|(1<<7))
-
-#define OHCI_INTR_SO          (1<<0) /* Scheduling overrun */
-#define OHCI_INTR_WD          (1<<1) /* HcDoneHead writeback */
-#define OHCI_INTR_SF          (1<<2) /* Start of frame */
-#define OHCI_INTR_RD          (1<<3) /* Resume detect */
-#define OHCI_INTR_UE          (1<<4) /* Unrecoverable error */
-#define OHCI_INTR_FNO         (1<<5) /* Frame number overflow */
-#define OHCI_INTR_RHSC        (1<<6) /* Root hub status change */
-#define OHCI_INTR_OC          (1<<30) /* Ownership change */
-#define OHCI_INTR_MIE         (1<<31) /* Master Interrupt Enable */
-
-#define OHCI_HCCA_SIZE        0x100
-#define OHCI_HCCA_MASK        0xffffff00
-
-#define OHCI_EDPTR_MASK       0xfffffff0
-
-#define OHCI_FMI_FI           0x00003fff
-#define OHCI_FMI_FSMPS        0xffff0000
-#define OHCI_FMI_FIT          0x80000000
-
-#define OHCI_FR_RT            (1<<31)
-
-#define OHCI_LS_THRESH        0x628
-
-#define OHCI_RHA_RW_MASK      0x00000000 /* Mask of supported features.  */
-#define OHCI_RHA_PSM          (1<<8)
-#define OHCI_RHA_NPS          (1<<9)
-#define OHCI_RHA_DT           (1<<10)
-#define OHCI_RHA_OCPM         (1<<11)
-#define OHCI_RHA_NOCP         (1<<12)
-#define OHCI_RHA_POTPGT_MASK  0xff000000
-
-#define OHCI_RHS_LPS          (1<<0)
-#define OHCI_RHS_OCI          (1<<1)
-#define OHCI_RHS_DRWE         (1<<15)
-#define OHCI_RHS_LPSC         (1<<16)
-#define OHCI_RHS_OCIC         (1<<17)
-#define OHCI_RHS_CRWE         (1<<31)
-
-#define OHCI_PORT_CCS         (1<<0)
-#define OHCI_PORT_PES         (1<<1)
-#define OHCI_PORT_PSS         (1<<2)
-#define OHCI_PORT_POCI        (1<<3)
-#define OHCI_PORT_PRS         (1<<4)
-#define OHCI_PORT_PPS         (1<<8)
-#define OHCI_PORT_LSDA        (1<<9)
-#define OHCI_PORT_CSC         (1<<16)
-#define OHCI_PORT_PESC        (1<<17)
-#define OHCI_PORT_PSSC        (1<<18)
-#define OHCI_PORT_OCIC        (1<<19)
-#define OHCI_PORT_PRSC        (1<<20)
-#define OHCI_PORT_WTC         (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \
-                               |OHCI_PORT_OCIC|OHCI_PORT_PRSC)
-
-#define OHCI_TD_DIR_SETUP     0x0
-#define OHCI_TD_DIR_OUT       0x1
-#define OHCI_TD_DIR_IN        0x2
-#define OHCI_TD_DIR_RESERVED  0x3
-
-#define OHCI_CC_NOERROR             0x0
-#define OHCI_CC_CRC                 0x1
-#define OHCI_CC_BITSTUFFING         0x2
-#define OHCI_CC_DATATOGGLEMISMATCH  0x3
-#define OHCI_CC_STALL               0x4
-#define OHCI_CC_DEVICENOTRESPONDING 0x5
-#define OHCI_CC_PIDCHECKFAILURE     0x6
-#define OHCI_CC_UNDEXPETEDPID       0x7
-#define OHCI_CC_DATAOVERRUN         0x8
-#define OHCI_CC_DATAUNDERRUN        0x9
-#define OHCI_CC_BUFFEROVERRUN       0xc
-#define OHCI_CC_BUFFERUNDERRUN      0xd
-
-#define OHCI_HRESET_FSBIR       (1 << 0)
-
-/* Update IRQ levels */
-static inline void ohci_intr_update(OHCIState *ohci)
-{
-    int level = 0;
-
-    if ((ohci->intr & OHCI_INTR_MIE) &&
-        (ohci->intr_status & ohci->intr))
-        level = 1;
-
-    qemu_set_irq(ohci->irq, level);
-}
-
-/* Set an interrupt */
-static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
-{
-    ohci->intr_status |= intr;
-    ohci_intr_update(ohci);
-}
-
-/* Attach or detach a device on a root hub port.  */
-static void ohci_attach(USBPort *port1)
-{
-    OHCIState *s = port1->opaque;
-    OHCIPort *port = &s->rhport[port1->index];
-    uint32_t old_state = port->ctrl;
-
-    /* set connect status */
-    port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
-
-    /* update speed */
-    if (port->port.dev->speed == USB_SPEED_LOW) {
-        port->ctrl |= OHCI_PORT_LSDA;
-    } else {
-        port->ctrl &= ~OHCI_PORT_LSDA;
-    }
-
-    /* notify of remote-wakeup */
-    if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
-        ohci_set_interrupt(s, OHCI_INTR_RD);
-    }
-
-    DPRINTF("usb-ohci: Attached port %d\n", port1->index);
-
-    if (old_state != port->ctrl) {
-        ohci_set_interrupt(s, OHCI_INTR_RHSC);
-    }
-}
-
-static void ohci_detach(USBPort *port1)
-{
-    OHCIState *s = port1->opaque;
-    OHCIPort *port = &s->rhport[port1->index];
-    uint32_t old_state = port->ctrl;
-
-    ohci_async_cancel_device(s, port1->dev);
-
-    /* set connect status */
-    if (port->ctrl & OHCI_PORT_CCS) {
-        port->ctrl &= ~OHCI_PORT_CCS;
-        port->ctrl |= OHCI_PORT_CSC;
-    }
-    /* disable port */
-    if (port->ctrl & OHCI_PORT_PES) {
-        port->ctrl &= ~OHCI_PORT_PES;
-        port->ctrl |= OHCI_PORT_PESC;
-    }
-    DPRINTF("usb-ohci: Detached port %d\n", port1->index);
-
-    if (old_state != port->ctrl) {
-        ohci_set_interrupt(s, OHCI_INTR_RHSC);
-    }
-}
-
-static void ohci_wakeup(USBPort *port1)
-{
-    OHCIState *s = port1->opaque;
-    OHCIPort *port = &s->rhport[port1->index];
-    uint32_t intr = 0;
-    if (port->ctrl & OHCI_PORT_PSS) {
-        DPRINTF("usb-ohci: port %d: wakeup\n", port1->index);
-        port->ctrl |= OHCI_PORT_PSSC;
-        port->ctrl &= ~OHCI_PORT_PSS;
-        intr = OHCI_INTR_RHSC;
-    }
-    /* Note that the controller can be suspended even if this port is not */
-    if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
-        DPRINTF("usb-ohci: remote-wakeup: SUSPEND->RESUME\n");
-        /* This is the one state transition the controller can do by itself */
-        s->ctl &= ~OHCI_CTL_HCFS;
-        s->ctl |= OHCI_USB_RESUME;
-        /* In suspend mode only ResumeDetected is possible, not RHSC:
-         * see the OHCI spec 5.1.2.3.
-         */
-        intr = OHCI_INTR_RD;
-    }
-    ohci_set_interrupt(s, intr);
-}
-
-static void ohci_child_detach(USBPort *port1, USBDevice *child)
-{
-    OHCIState *s = port1->opaque;
-
-    ohci_async_cancel_device(s, child);
-}
-
-static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr)
-{
-    USBDevice *dev;
-    int i;
-
-    for (i = 0; i < ohci->num_ports; i++) {
-        if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) {
-            continue;
-        }
-        dev = usb_find_device(&ohci->rhport[i].port, addr);
-        if (dev != NULL) {
-            return dev;
-        }
-    }
-    return NULL;
-}
-
-/* Reset the controller */
-static void ohci_reset(void *opaque)
-{
-    OHCIState *ohci = opaque;
-    OHCIPort *port;
-    int i;
-
-    ohci_bus_stop(ohci);
-    ohci->ctl = 0;
-    ohci->old_ctl = 0;
-    ohci->status = 0;
-    ohci->intr_status = 0;
-    ohci->intr = OHCI_INTR_MIE;
-
-    ohci->hcca = 0;
-    ohci->ctrl_head = ohci->ctrl_cur = 0;
-    ohci->bulk_head = ohci->bulk_cur = 0;
-    ohci->per_cur = 0;
-    ohci->done = 0;
-    ohci->done_count = 7;
-
-    /* FSMPS is marked TBD in OCHI 1.0, what gives ffs?
-     * I took the value linux sets ...
-     */
-    ohci->fsmps = 0x2778;
-    ohci->fi = 0x2edf;
-    ohci->fit = 0;
-    ohci->frt = 0;
-    ohci->frame_number = 0;
-    ohci->pstart = 0;
-    ohci->lst = OHCI_LS_THRESH;
-
-    ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
-    ohci->rhdesc_b = 0x0; /* Impl. specific */
-    ohci->rhstatus = 0;
-
-    for (i = 0; i < ohci->num_ports; i++)
-      {
-        port = &ohci->rhport[i];
-        port->ctrl = 0;
-        if (port->port.dev && port->port.dev->attached) {
-            usb_port_reset(&port->port);
-        }
-      }
-    if (ohci->async_td) {
-        usb_cancel_packet(&ohci->usb_packet);
-        ohci->async_td = 0;
-    }
-    DPRINTF("usb-ohci: Reset %s\n", ohci->name);
-}
-
-/* Get an array of dwords from main memory */
-static inline int get_dwords(OHCIState *ohci,
-                             uint32_t addr, uint32_t *buf, int num)
-{
-    int i;
-
-    addr += ohci->localmem_base;
-
-    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        cpu_physical_memory_read(addr, buf, sizeof(*buf));
-        *buf = le32_to_cpu(*buf);
-    }
-
-    return 1;
-}
-
-/* Put an array of dwords in to main memory */
-static inline int put_dwords(OHCIState *ohci,
-                             uint32_t addr, uint32_t *buf, int num)
-{
-    int i;
-
-    addr += ohci->localmem_base;
-
-    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        uint32_t tmp = cpu_to_le32(*buf);
-        cpu_physical_memory_write(addr, &tmp, sizeof(tmp));
-    }
-
-    return 1;
-}
-
-/* Get an array of words from main memory */
-static inline int get_words(OHCIState *ohci,
-                            uint32_t addr, uint16_t *buf, int num)
-{
-    int i;
-
-    addr += ohci->localmem_base;
-
-    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        cpu_physical_memory_read(addr, buf, sizeof(*buf));
-        *buf = le16_to_cpu(*buf);
-    }
-
-    return 1;
-}
-
-/* Put an array of words in to main memory */
-static inline int put_words(OHCIState *ohci,
-                            uint32_t addr, uint16_t *buf, int num)
-{
-    int i;
-
-    addr += ohci->localmem_base;
-
-    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        uint16_t tmp = cpu_to_le16(*buf);
-        cpu_physical_memory_write(addr, &tmp, sizeof(tmp));
-    }
-
-    return 1;
-}
-
-static inline int ohci_read_ed(OHCIState *ohci,
-                               uint32_t addr, struct ohci_ed *ed)
-{
-    return get_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
-}
-
-static inline int ohci_read_td(OHCIState *ohci,
-                               uint32_t addr, struct ohci_td *td)
-{
-    return get_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
-}
-
-static inline int ohci_read_iso_td(OHCIState *ohci,
-                                   uint32_t addr, struct ohci_iso_td *td)
-{
-    return (get_dwords(ohci, addr, (uint32_t *)td, 4) &&
-            get_words(ohci, addr + 16, td->offset, 8));
-}
-
-static inline int ohci_read_hcca(OHCIState *ohci,
-                                 uint32_t addr, struct ohci_hcca *hcca)
-{
-    cpu_physical_memory_read(addr + ohci->localmem_base, hcca, sizeof(*hcca));
-    return 1;
-}
-
-static inline int ohci_put_ed(OHCIState *ohci,
-                              uint32_t addr, struct ohci_ed *ed)
-{
-    return put_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
-}
-
-static inline int ohci_put_td(OHCIState *ohci,
-                              uint32_t addr, struct ohci_td *td)
-{
-    return put_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
-}
-
-static inline int ohci_put_iso_td(OHCIState *ohci,
-                                  uint32_t addr, struct ohci_iso_td *td)
-{
-    return (put_dwords(ohci, addr, (uint32_t *)td, 4) &&
-            put_words(ohci, addr + 16, td->offset, 8));
-}
-
-static inline int ohci_put_hcca(OHCIState *ohci,
-                                uint32_t addr, struct ohci_hcca *hcca)
-{
-    cpu_physical_memory_write(addr + ohci->localmem_base, hcca, sizeof(*hcca));
-    return 1;
-}
-
-/* Read/Write the contents of a TD from/to main memory.  */
-static void ohci_copy_td(OHCIState *ohci, struct ohci_td *td,
-                         uint8_t *buf, int len, int write)
-{
-    uint32_t ptr;
-    uint32_t n;
-
-    ptr = td->cbp;
-    n = 0x1000 - (ptr & 0xfff);
-    if (n > len)
-        n = len;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
-    if (n == len)
-        return;
-    ptr = td->be & ~0xfffu;
-    buf += n;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
-}
-
-/* Read/Write the contents of an ISO TD from/to main memory.  */
-static void ohci_copy_iso_td(OHCIState *ohci,
-                             uint32_t start_addr, uint32_t end_addr,
-                             uint8_t *buf, int len, int write)
-{
-    uint32_t ptr;
-    uint32_t n;
-
-    ptr = start_addr;
-    n = 0x1000 - (ptr & 0xfff);
-    if (n > len)
-        n = len;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
-    if (n == len)
-        return;
-    ptr = end_addr & ~0xfffu;
-    buf += n;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
-}
-
-static void ohci_process_lists(OHCIState *ohci, int completion);
-
-static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
-{
-    OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
-#ifdef DEBUG_PACKET
-    DPRINTF("Async packet complete\n");
-#endif
-    ohci->async_complete = 1;
-    ohci_process_lists(ohci, 1);
-}
-
-#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
-
-static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
-                               int completion)
-{
-    int dir;
-    size_t len = 0;
-#ifdef DEBUG_ISOCH
-    const char *str = NULL;
-#endif
-    int pid;
-    int ret;
-    int i;
-    USBDevice *dev;
-    USBEndpoint *ep;
-    struct ohci_iso_td iso_td;
-    uint32_t addr;
-    uint16_t starting_frame;
-    int16_t relative_frame_number;
-    int frame_count;
-    uint32_t start_offset, next_offset, end_offset = 0;
-    uint32_t start_addr, end_addr;
-
-    addr = ed->head & OHCI_DPTR_MASK;
-
-    if (!ohci_read_iso_td(ohci, addr, &iso_td)) {
-        printf("usb-ohci: ISO_TD read error at %x\n", addr);
-        return 0;
-    }
-
-    starting_frame = OHCI_BM(iso_td.flags, TD_SF);
-    frame_count = OHCI_BM(iso_td.flags, TD_FC);
-    relative_frame_number = USUB(ohci->frame_number, starting_frame); 
-
-#ifdef DEBUG_ISOCH
-    printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
-           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
-           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
-           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
-           "frame_number 0x%.8x starting_frame 0x%.8x\n"
-           "frame_count  0x%.8x relative %d\n"
-           "di 0x%.8x cc 0x%.8x\n",
-           ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK,
-           iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
-           iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
-           iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
-           ohci->frame_number, starting_frame, 
-           frame_count, relative_frame_number,         
-           OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
-#endif
-
-    if (relative_frame_number < 0) {
-        DPRINTF("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
-        return 1;
-    } else if (relative_frame_number > frame_count) {
-        /* ISO TD expired - retire the TD to the Done Queue and continue with
-           the next ISO TD of the same ED */
-        DPRINTF("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, 
-               frame_count);
-        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
-        ed->head &= ~OHCI_DPTR_MASK;
-        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
-        iso_td.next = ohci->done;
-        ohci->done = addr;
-        i = OHCI_BM(iso_td.flags, TD_DI);
-        if (i < ohci->done_count)
-            ohci->done_count = i;
-        ohci_put_iso_td(ohci, addr, &iso_td);
-        return 0;
-    }
-
-    dir = OHCI_BM(ed->flags, ED_D);
-    switch (dir) {
-    case OHCI_TD_DIR_IN:
-#ifdef DEBUG_ISOCH
-        str = "in";
-#endif
-        pid = USB_TOKEN_IN;
-        break;
-    case OHCI_TD_DIR_OUT:
-#ifdef DEBUG_ISOCH
-        str = "out";
-#endif
-        pid = USB_TOKEN_OUT;
-        break;
-    case OHCI_TD_DIR_SETUP:
-#ifdef DEBUG_ISOCH
-        str = "setup";
-#endif
-        pid = USB_TOKEN_SETUP;
-        break;
-    default:
-        printf("usb-ohci: Bad direction %d\n", dir);
-        return 1;
-    }
-
-    if (!iso_td.bp || !iso_td.be) {
-        printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be);
-        return 1;
-    }
-
-    start_offset = iso_td.offset[relative_frame_number];
-    next_offset = iso_td.offset[relative_frame_number + 1];
-
-    if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || 
-        ((relative_frame_number < frame_count) && 
-         !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
-        printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
-               start_offset, next_offset);
-        return 1;
-    }
-
-    if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
-        printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n",
-                start_offset, next_offset);
-        return 1;
-    }
-
-    if ((start_offset & 0x1000) == 0) {
-        start_addr = (iso_td.bp & OHCI_PAGE_MASK) |
-            (start_offset & OHCI_OFFSET_MASK);
-    } else {
-        start_addr = (iso_td.be & OHCI_PAGE_MASK) |
-            (start_offset & OHCI_OFFSET_MASK);
-    }
-
-    if (relative_frame_number < frame_count) {
-        end_offset = next_offset - 1;
-        if ((end_offset & 0x1000) == 0) {
-            end_addr = (iso_td.bp & OHCI_PAGE_MASK) |
-                (end_offset & OHCI_OFFSET_MASK);
-        } else {
-            end_addr = (iso_td.be & OHCI_PAGE_MASK) |
-                (end_offset & OHCI_OFFSET_MASK);
-        }
-    } else {
-        /* Last packet in the ISO TD */
-        end_addr = iso_td.be;
-    }
-
-    if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
-        len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
-            - (start_addr & OHCI_OFFSET_MASK);
-    } else {
-        len = end_addr - start_addr + 1;
-    }
-
-    if (len && dir != OHCI_TD_DIR_IN) {
-        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, 0);
-    }
-
-    if (completion) {
-        ret = ohci->usb_packet.result;
-    } else {
-        dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
-        ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
-        usb_packet_setup(&ohci->usb_packet, pid, ep);
-        usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
-        ret = usb_handle_packet(dev, &ohci->usb_packet);
-        if (ret == USB_RET_ASYNC) {
-            return 1;
-        }
-    }
-
-#ifdef DEBUG_ISOCH
-    printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
-           start_offset, end_offset, start_addr, end_addr, str, len, ret);
-#endif
-
-    /* Writeback */
-    if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
-        /* IN transfer succeeded */
-        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, 1);
-        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
-                    OHCI_CC_NOERROR);
-        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
-    } else if (dir == OHCI_TD_DIR_OUT && ret == len) {
-        /* OUT transfer succeeded */
-        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
-                    OHCI_CC_NOERROR);
-        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
-    } else {
-        if (ret > (ssize_t) len) {
-            printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
-            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
-                        OHCI_CC_DATAOVERRUN);
-            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
-                        len);
-        } else if (ret >= 0) {
-            printf("usb-ohci: DataUnderrun %d\n", ret);
-            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
-                        OHCI_CC_DATAUNDERRUN);
-        } else {
-            switch (ret) {
-            case USB_RET_IOERROR:
-            case USB_RET_NODEV:
-                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
-                            OHCI_CC_DEVICENOTRESPONDING);
-                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
-                            0);
-                break;
-            case USB_RET_NAK:
-            case USB_RET_STALL:
-                printf("usb-ohci: got NAK/STALL %d\n", ret);
-                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
-                            OHCI_CC_STALL);
-                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
-                            0);
-                break;
-            default:
-                printf("usb-ohci: Bad device response %d\n", ret);
-                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
-                            OHCI_CC_UNDEXPETEDPID);
-                break;
-            }
-        }
-    }
-
-    if (relative_frame_number == frame_count) {
-        /* Last data packet of ISO TD - retire the TD to the Done Queue */
-        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR);
-        ed->head &= ~OHCI_DPTR_MASK;
-        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
-        iso_td.next = ohci->done;
-        ohci->done = addr;
-        i = OHCI_BM(iso_td.flags, TD_DI);
-        if (i < ohci->done_count)
-            ohci->done_count = i;
-    }
-    ohci_put_iso_td(ohci, addr, &iso_td);
-    return 1;
-}
-
-/* Service a transport descriptor.
-   Returns nonzero to terminate processing of this endpoint.  */
-
-static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
-{
-    int dir;
-    size_t len = 0, pktlen = 0;
-#ifdef DEBUG_PACKET
-    const char *str = NULL;
-#endif
-    int pid;
-    int ret;
-    int i;
-    USBDevice *dev;
-    USBEndpoint *ep;
-    struct ohci_td td;
-    uint32_t addr;
-    int flag_r;
-    int completion;
-
-    addr = ed->head & OHCI_DPTR_MASK;
-    /* See if this TD has already been submitted to the device.  */
-    completion = (addr == ohci->async_td);
-    if (completion && !ohci->async_complete) {
-#ifdef DEBUG_PACKET
-        DPRINTF("Skipping async TD\n");
-#endif
-        return 1;
-    }
-    if (!ohci_read_td(ohci, addr, &td)) {
-        fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
-        return 0;
-    }
-
-    dir = OHCI_BM(ed->flags, ED_D);
-    switch (dir) {
-    case OHCI_TD_DIR_OUT:
-    case OHCI_TD_DIR_IN:
-        /* Same value.  */
-        break;
-    default:
-        dir = OHCI_BM(td.flags, TD_DP);
-        break;
-    }
-
-    switch (dir) {
-    case OHCI_TD_DIR_IN:
-#ifdef DEBUG_PACKET
-        str = "in";
-#endif
-        pid = USB_TOKEN_IN;
-        break;
-    case OHCI_TD_DIR_OUT:
-#ifdef DEBUG_PACKET
-        str = "out";
-#endif
-        pid = USB_TOKEN_OUT;
-        break;
-    case OHCI_TD_DIR_SETUP:
-#ifdef DEBUG_PACKET
-        str = "setup";
-#endif
-        pid = USB_TOKEN_SETUP;
-        break;
-    default:
-        fprintf(stderr, "usb-ohci: Bad direction\n");
-        return 1;
-    }
-    if (td.cbp && td.be) {
-        if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) {
-            len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff);
-        } else {
-            len = (td.be - td.cbp) + 1;
-        }
-
-        pktlen = len;
-        if (len && dir != OHCI_TD_DIR_IN) {
-            /* The endpoint may not allow us to transfer it all now */
-            pktlen = (ed->flags & OHCI_ED_MPS_MASK) >> OHCI_ED_MPS_SHIFT;
-            if (pktlen > len) {
-                pktlen = len;
-            }
-            if (!completion) {
-                ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen, 0);
-            }
-        }
-    }
-
-    flag_r = (td.flags & OHCI_TD_R) != 0;
-#ifdef DEBUG_PACKET
-    DPRINTF(" TD @ 0x%.8x %" PRId64 " of %" PRId64
-            " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
-            addr, (int64_t)pktlen, (int64_t)len, str, flag_r, td.cbp, td.be);
-
-    if (pktlen > 0 && dir != OHCI_TD_DIR_IN) {
-        DPRINTF("  data:");
-        for (i = 0; i < pktlen; i++) {
-            printf(" %.2x", ohci->usb_buf[i]);
-        }
-        DPRINTF("\n");
-    }
-#endif
-    if (completion) {
-        ret = ohci->usb_packet.result;
-        ohci->async_td = 0;
-        ohci->async_complete = 0;
-    } else {
-        if (ohci->async_td) {
-            /* ??? The hardware should allow one active packet per
-               endpoint.  We only allow one active packet per controller.
-               This should be sufficient as long as devices respond in a
-               timely manner.
-            */
-#ifdef DEBUG_PACKET
-            DPRINTF("Too many pending packets\n");
-#endif
-            return 1;
-        }
-        dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
-        ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
-        usb_packet_setup(&ohci->usb_packet, pid, ep);
-        usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
-        ret = usb_handle_packet(dev, &ohci->usb_packet);
-#ifdef DEBUG_PACKET
-        DPRINTF("ret=%d\n", ret);
-#endif
-        if (ret == USB_RET_ASYNC) {
-            ohci->async_td = addr;
-            return 1;
-        }
-    }
-    if (ret >= 0) {
-        if (dir == OHCI_TD_DIR_IN) {
-            ohci_copy_td(ohci, &td, ohci->usb_buf, ret, 1);
-#ifdef DEBUG_PACKET
-            DPRINTF("  data:");
-            for (i = 0; i < ret; i++)
-                printf(" %.2x", ohci->usb_buf[i]);
-            DPRINTF("\n");
-#endif
-        } else {
-            ret = pktlen;
-        }
-    }
-
-    /* Writeback */
-    if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
-        /* Transmission succeeded.  */
-        if (ret == len) {
-            td.cbp = 0;
-        } else {
-            if ((td.cbp & 0xfff) + ret > 0xfff) {
-                td.cbp = (td.be & ~0xfff) + ((td.cbp + ret) & 0xfff);
-            } else {
-                td.cbp += ret;
-            }
-        }
-        td.flags |= OHCI_TD_T1;
-        td.flags ^= OHCI_TD_T0;
-        OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
-        OHCI_SET_BM(td.flags, TD_EC, 0);
-
-        if ((dir != OHCI_TD_DIR_IN) && (ret != len)) {
-            /* Partial packet transfer: TD not ready to retire yet */
-            goto exit_no_retire;
-        }
-
-        /* Setting ED_C is part of the TD retirement process */
-        ed->head &= ~OHCI_ED_C;
-        if (td.flags & OHCI_TD_T0)
-            ed->head |= OHCI_ED_C;
-    } else {
-        if (ret >= 0) {
-            DPRINTF("usb-ohci: Underrun\n");
-            OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
-        } else {
-            switch (ret) {
-            case USB_RET_IOERROR:
-            case USB_RET_NODEV:
-                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
-            case USB_RET_NAK:
-                DPRINTF("usb-ohci: got NAK\n");
-                return 1;
-            case USB_RET_STALL:
-                DPRINTF("usb-ohci: got STALL\n");
-                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL);
-                break;
-            case USB_RET_BABBLE:
-                DPRINTF("usb-ohci: got BABBLE\n");
-                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
-                break;
-            default:
-                fprintf(stderr, "usb-ohci: Bad device response %d\n", ret);
-                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID);
-                OHCI_SET_BM(td.flags, TD_EC, 3);
-                break;
-            }
-        }
-        ed->head |= OHCI_ED_H;
-    }
-
-    /* Retire this TD */
-    ed->head &= ~OHCI_DPTR_MASK;
-    ed->head |= td.next & OHCI_DPTR_MASK;
-    td.next = ohci->done;
-    ohci->done = addr;
-    i = OHCI_BM(td.flags, TD_DI);
-    if (i < ohci->done_count)
-        ohci->done_count = i;
-exit_no_retire:
-    ohci_put_td(ohci, addr, &td);
-    return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
-}
-
-/* Service an endpoint list.  Returns nonzero if active TD were found.  */
-static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
-{
-    struct ohci_ed ed;
-    uint32_t next_ed;
-    uint32_t cur;
-    int active;
-
-    active = 0;
-
-    if (head == 0)
-        return 0;
-
-    for (cur = head; cur; cur = next_ed) {
-        if (!ohci_read_ed(ohci, cur, &ed)) {
-            fprintf(stderr, "usb-ohci: ED read error at %x\n", cur);
-            return 0;
-        }
-
-        next_ed = ed.next & OHCI_DPTR_MASK;
-
-        if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
-            uint32_t addr;
-            /* Cancel pending packets for ED that have been paused.  */
-            addr = ed.head & OHCI_DPTR_MASK;
-            if (ohci->async_td && addr == ohci->async_td) {
-                usb_cancel_packet(&ohci->usb_packet);
-                ohci->async_td = 0;
-            }
-            continue;
-        }
-
-        while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
-#ifdef DEBUG_PACKET
-            DPRINTF("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
-                    "h=%u c=%u\n  head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur,
-                    OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN),
-                    OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0,
-                    (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0,
-                    OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0,
-                    (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
-                    ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
-#endif
-            active = 1;
-
-            if ((ed.flags & OHCI_ED_F) == 0) {
-                if (ohci_service_td(ohci, &ed))
-                    break;
-            } else {
-                /* Handle isochronous endpoints */
-                if (ohci_service_iso_td(ohci, &ed, completion))
-                    break;
-            }
-        }
-
-        ohci_put_ed(ohci, cur, &ed);
-    }
-
-    return active;
-}
-
-/* Generate a SOF event, and set a timer for EOF */
-static void ohci_sof(OHCIState *ohci)
-{
-    ohci->sof_time = qemu_get_clock_ns(vm_clock);
-    qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
-    ohci_set_interrupt(ohci, OHCI_INTR_SF);
-}
-
-/* Process Control and Bulk lists.  */
-static void ohci_process_lists(OHCIState *ohci, int completion)
-{
-    if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
-        if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
-            DPRINTF("usb-ohci: head %x, cur %x\n",
-                    ohci->ctrl_head, ohci->ctrl_cur);
-        }
-        if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
-            ohci->ctrl_cur = 0;
-            ohci->status &= ~OHCI_STATUS_CLF;
-        }
-    }
-
-    if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
-        if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
-            ohci->bulk_cur = 0;
-            ohci->status &= ~OHCI_STATUS_BLF;
-        }
-    }
-}
-
-/* Do frame processing on frame boundary */
-static void ohci_frame_boundary(void *opaque)
-{
-    OHCIState *ohci = opaque;
-    struct ohci_hcca hcca;
-
-    ohci_read_hcca(ohci, ohci->hcca, &hcca);
-
-    /* Process all the lists at the end of the frame */
-    if (ohci->ctl & OHCI_CTL_PLE) {
-        int n;
-
-        n = ohci->frame_number & 0x1f;
-        ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
-    }
-
-    /* Cancel all pending packets if either of the lists has been disabled.  */
-    if (ohci->async_td &&
-        ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
-        usb_cancel_packet(&ohci->usb_packet);
-        ohci->async_td = 0;
-    }
-    ohci->old_ctl = ohci->ctl;
-    ohci_process_lists(ohci, 0);
-
-    /* Frame boundary, so do EOF stuf here */
-    ohci->frt = ohci->fit;
-
-    /* Increment frame number and take care of endianness. */
-    ohci->frame_number = (ohci->frame_number + 1) & 0xffff;
-    hcca.frame = cpu_to_le16(ohci->frame_number);
-
-    if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) {
-        if (!ohci->done)
-            abort();
-        if (ohci->intr & ohci->intr_status)
-            ohci->done |= 1;
-        hcca.done = cpu_to_le32(ohci->done);
-        ohci->done = 0;
-        ohci->done_count = 7;
-        ohci_set_interrupt(ohci, OHCI_INTR_WD);
-    }
-
-    if (ohci->done_count != 7 && ohci->done_count != 0)
-        ohci->done_count--;
-
-    /* Do SOF stuff here */
-    ohci_sof(ohci);
-
-    /* Writeback HCCA */
-    ohci_put_hcca(ohci, ohci->hcca, &hcca);
-}
-
-/* Start sending SOF tokens across the USB bus, lists are processed in
- * next frame
- */
-static int ohci_bus_start(OHCIState *ohci)
-{
-    ohci->eof_timer = qemu_new_timer_ns(vm_clock,
-                    ohci_frame_boundary,
-                    ohci);
-
-    if (ohci->eof_timer == NULL) {
-        fprintf(stderr, "usb-ohci: %s: qemu_new_timer_ns failed\n", ohci->name);
-        /* TODO: Signal unrecoverable error */
-        return 0;
-    }
-
-    DPRINTF("usb-ohci: %s: USB Operational\n", ohci->name);
-
-    ohci_sof(ohci);
-
-    return 1;
-}
-
-/* Stop sending SOF tokens on the bus */
-static void ohci_bus_stop(OHCIState *ohci)
-{
-    if (ohci->eof_timer)
-        qemu_del_timer(ohci->eof_timer);
-    ohci->eof_timer = NULL;
-}
-
-/* Sets a flag in a port status register but only set it if the port is
- * connected, if not set ConnectStatusChange flag. If flag is enabled
- * return 1.
- */
-static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val)
-{
-    int ret = 1;
-
-    /* writing a 0 has no effect */
-    if (val == 0)
-        return 0;
-
-    /* If CurrentConnectStatus is cleared we set
-     * ConnectStatusChange
-     */
-    if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) {
-        ohci->rhport[i].ctrl |= OHCI_PORT_CSC;
-        if (ohci->rhstatus & OHCI_RHS_DRWE) {
-            /* TODO: CSC is a wakeup event */
-        }
-        return 0;
-    }
-
-    if (ohci->rhport[i].ctrl & val)
-        ret = 0;
-
-    /* set the bit */
-    ohci->rhport[i].ctrl |= val;
-
-    return ret;
-}
-
-/* Set the frame interval - frame interval toggle is manipulated by the hcd only */
-static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
-{
-    val &= OHCI_FMI_FI;
-
-    if (val != ohci->fi) {
-        DPRINTF("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
-            ohci->name, ohci->fi, ohci->fi);
-    }
-
-    ohci->fi = val;
-}
-
-static void ohci_port_power(OHCIState *ohci, int i, int p)
-{
-    if (p) {
-        ohci->rhport[i].ctrl |= OHCI_PORT_PPS;
-    } else {
-        ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS|
-                    OHCI_PORT_CCS|
-                    OHCI_PORT_PSS|
-                    OHCI_PORT_PRS);
-    }
-}
-
-/* Set HcControlRegister */
-static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
-{
-    uint32_t old_state;
-    uint32_t new_state;
-
-    old_state = ohci->ctl & OHCI_CTL_HCFS;
-    ohci->ctl = val;
-    new_state = ohci->ctl & OHCI_CTL_HCFS;
-
-    /* no state change */
-    if (old_state == new_state)
-        return;
-
-    switch (new_state) {
-    case OHCI_USB_OPERATIONAL:
-        ohci_bus_start(ohci);
-        break;
-    case OHCI_USB_SUSPEND:
-        ohci_bus_stop(ohci);
-        DPRINTF("usb-ohci: %s: USB Suspended\n", ohci->name);
-        break;
-    case OHCI_USB_RESUME:
-        DPRINTF("usb-ohci: %s: USB Resume\n", ohci->name);
-        break;
-    case OHCI_USB_RESET:
-        ohci_reset(ohci);
-        DPRINTF("usb-ohci: %s: USB Reset\n", ohci->name);
-        break;
-    }
-}
-
-static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
-{
-    uint16_t fr;
-    int64_t tks;
-
-    if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL)
-        return (ohci->frt << 31);
-
-    /* Being in USB operational state guarnatees sof_time was
-     * set already.
-     */
-    tks = qemu_get_clock_ns(vm_clock) - ohci->sof_time;
-
-    /* avoid muldiv if possible */
-    if (tks >= usb_frame_time)
-        return (ohci->frt << 31);
-
-    tks = muldiv64(1, tks, usb_bit_time);
-    fr = (uint16_t)(ohci->fi - tks);
-
-    return (ohci->frt << 31) | fr;
-}
-
-
-/* Set root hub status */
-static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
-{
-    uint32_t old_state;
-
-    old_state = ohci->rhstatus;
-
-    /* write 1 to clear OCIC */
-    if (val & OHCI_RHS_OCIC)
-        ohci->rhstatus &= ~OHCI_RHS_OCIC;
-
-    if (val & OHCI_RHS_LPS) {
-        int i;
-
-        for (i = 0; i < ohci->num_ports; i++)
-            ohci_port_power(ohci, i, 0);
-        DPRINTF("usb-ohci: powered down all ports\n");
-    }
-
-    if (val & OHCI_RHS_LPSC) {
-        int i;
-
-        for (i = 0; i < ohci->num_ports; i++)
-            ohci_port_power(ohci, i, 1);
-        DPRINTF("usb-ohci: powered up all ports\n");
-    }
-
-    if (val & OHCI_RHS_DRWE)
-        ohci->rhstatus |= OHCI_RHS_DRWE;
-
-    if (val & OHCI_RHS_CRWE)
-        ohci->rhstatus &= ~OHCI_RHS_DRWE;
-
-    if (old_state != ohci->rhstatus)
-        ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
-}
-
-/* Set root hub port status */
-static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
-{
-    uint32_t old_state;
-    OHCIPort *port;
-
-    port = &ohci->rhport[portnum];
-    old_state = port->ctrl;
-
-    /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */
-    if (val & OHCI_PORT_WTC)
-        port->ctrl &= ~(val & OHCI_PORT_WTC);
-
-    if (val & OHCI_PORT_CCS)
-        port->ctrl &= ~OHCI_PORT_PES;
-
-    ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
-
-    if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) {
-        DPRINTF("usb-ohci: port %d: SUSPEND\n", portnum);
-    }
-
-    if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
-        DPRINTF("usb-ohci: port %d: RESET\n", portnum);
-        usb_device_reset(port->port.dev);
-        port->ctrl &= ~OHCI_PORT_PRS;
-        /* ??? Should this also set OHCI_PORT_PESC.  */
-        port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
-    }
-
-    /* Invert order here to ensure in ambiguous case, device is
-     * powered up...
-     */
-    if (val & OHCI_PORT_LSDA)
-        ohci_port_power(ohci, portnum, 0);
-    if (val & OHCI_PORT_PPS)
-        ohci_port_power(ohci, portnum, 1);
-
-    if (old_state != port->ctrl)
-        ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
-
-    return;
-}
-
-static uint64_t ohci_mem_read(void *opaque,
-                              target_phys_addr_t addr,
-                              unsigned size)
-{
-    OHCIState *ohci = opaque;
-    uint32_t retval;
-
-    /* Only aligned reads are allowed on OHCI */
-    if (addr & 3) {
-        fprintf(stderr, "usb-ohci: Mis-aligned read\n");
-        return 0xffffffff;
-    } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
-        /* HcRhPortStatus */
-        retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS;
-    } else {
-        switch (addr >> 2) {
-        case 0: /* HcRevision */
-            retval = 0x10;
-            break;
-
-        case 1: /* HcControl */
-            retval = ohci->ctl;
-            break;
-
-        case 2: /* HcCommandStatus */
-            retval = ohci->status;
-            break;
-
-        case 3: /* HcInterruptStatus */
-            retval = ohci->intr_status;
-            break;
-
-        case 4: /* HcInterruptEnable */
-        case 5: /* HcInterruptDisable */
-            retval = ohci->intr;
-            break;
-
-        case 6: /* HcHCCA */
-            retval = ohci->hcca;
-            break;
-
-        case 7: /* HcPeriodCurrentED */
-            retval = ohci->per_cur;
-            break;
-
-        case 8: /* HcControlHeadED */
-            retval = ohci->ctrl_head;
-            break;
-
-        case 9: /* HcControlCurrentED */
-            retval = ohci->ctrl_cur;
-            break;
-
-        case 10: /* HcBulkHeadED */
-            retval = ohci->bulk_head;
-            break;
-
-        case 11: /* HcBulkCurrentED */
-            retval = ohci->bulk_cur;
-            break;
-
-        case 12: /* HcDoneHead */
-            retval = ohci->done;
-            break;
-
-        case 13: /* HcFmInterretval */
-            retval = (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi);
-            break;
-
-        case 14: /* HcFmRemaining */
-            retval = ohci_get_frame_remaining(ohci);
-            break;
-
-        case 15: /* HcFmNumber */
-            retval = ohci->frame_number;
-            break;
-
-        case 16: /* HcPeriodicStart */
-            retval = ohci->pstart;
-            break;
-
-        case 17: /* HcLSThreshold */
-            retval = ohci->lst;
-            break;
-
-        case 18: /* HcRhDescriptorA */
-            retval = ohci->rhdesc_a;
-            break;
-
-        case 19: /* HcRhDescriptorB */
-            retval = ohci->rhdesc_b;
-            break;
-
-        case 20: /* HcRhStatus */
-            retval = ohci->rhstatus;
-            break;
-
-        /* PXA27x specific registers */
-        case 24: /* HcStatus */
-            retval = ohci->hstatus & ohci->hmask;
-            break;
-
-        case 25: /* HcHReset */
-            retval = ohci->hreset;
-            break;
-
-        case 26: /* HcHInterruptEnable */
-            retval = ohci->hmask;
-            break;
-
-        case 27: /* HcHInterruptTest */
-            retval = ohci->htest;
-            break;
-
-        default:
-            fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr);
-            retval = 0xffffffff;
-        }
-    }
-
-    return retval;
-}
-
-static void ohci_mem_write(void *opaque,
-                           target_phys_addr_t addr,
-                           uint64_t val,
-                           unsigned size)
-{
-    OHCIState *ohci = opaque;
-
-    /* Only aligned reads are allowed on OHCI */
-    if (addr & 3) {
-        fprintf(stderr, "usb-ohci: Mis-aligned write\n");
-        return;
-    }
-
-    if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
-        /* HcRhPortStatus */
-        ohci_port_set_status(ohci, (addr - 0x54) >> 2, val);
-        return;
-    }
-
-    switch (addr >> 2) {
-    case 1: /* HcControl */
-        ohci_set_ctl(ohci, val);
-        break;
-
-    case 2: /* HcCommandStatus */
-        /* SOC is read-only */
-        val = (val & ~OHCI_STATUS_SOC);
-
-        /* Bits written as '0' remain unchanged in the register */
-        ohci->status |= val;
-
-        if (ohci->status & OHCI_STATUS_HCR)
-            ohci_reset(ohci);
-        break;
-
-    case 3: /* HcInterruptStatus */
-        ohci->intr_status &= ~val;
-        ohci_intr_update(ohci);
-        break;
-
-    case 4: /* HcInterruptEnable */
-        ohci->intr |= val;
-        ohci_intr_update(ohci);
-        break;
-
-    case 5: /* HcInterruptDisable */
-        ohci->intr &= ~val;
-        ohci_intr_update(ohci);
-        break;
-
-    case 6: /* HcHCCA */
-        ohci->hcca = val & OHCI_HCCA_MASK;
-        break;
-
-    case 7: /* HcPeriodCurrentED */
-        /* Ignore writes to this read-only register, Linux does them */
-        break;
-
-    case 8: /* HcControlHeadED */
-        ohci->ctrl_head = val & OHCI_EDPTR_MASK;
-        break;
-
-    case 9: /* HcControlCurrentED */
-        ohci->ctrl_cur = val & OHCI_EDPTR_MASK;
-        break;
-
-    case 10: /* HcBulkHeadED */
-        ohci->bulk_head = val & OHCI_EDPTR_MASK;
-        break;
-
-    case 11: /* HcBulkCurrentED */
-        ohci->bulk_cur = val & OHCI_EDPTR_MASK;
-        break;
-
-    case 13: /* HcFmInterval */
-        ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16;
-        ohci->fit = (val & OHCI_FMI_FIT) >> 31;
-        ohci_set_frame_interval(ohci, val);
-        break;
-
-    case 15: /* HcFmNumber */
-        break;
-
-    case 16: /* HcPeriodicStart */
-        ohci->pstart = val & 0xffff;
-        break;
-
-    case 17: /* HcLSThreshold */
-        ohci->lst = val & 0xffff;
-        break;
-
-    case 18: /* HcRhDescriptorA */
-        ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK;
-        ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK;
-        break;
-
-    case 19: /* HcRhDescriptorB */
-        break;
-
-    case 20: /* HcRhStatus */
-        ohci_set_hub_status(ohci, val);
-        break;
-
-    /* PXA27x specific registers */
-    case 24: /* HcStatus */
-        ohci->hstatus &= ~(val & ohci->hmask);
-
-    case 25: /* HcHReset */
-        ohci->hreset = val & ~OHCI_HRESET_FSBIR;
-        if (val & OHCI_HRESET_FSBIR)
-            ohci_reset(ohci);
-        break;
-
-    case 26: /* HcHInterruptEnable */
-        ohci->hmask = val;
-        break;
-
-    case 27: /* HcHInterruptTest */
-        ohci->htest = val;
-        break;
-
-    default:
-        fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr);
-        break;
-    }
-}
-
-static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
-{
-    if (ohci->async_td &&
-        usb_packet_is_inflight(&ohci->usb_packet) &&
-        ohci->usb_packet.ep->dev == dev) {
-        usb_cancel_packet(&ohci->usb_packet);
-        ohci->async_td = 0;
-    }
-}
-
-static const MemoryRegionOps ohci_mem_ops = {
-    .read = ohci_mem_read,
-    .write = ohci_mem_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static USBPortOps ohci_port_ops = {
-    .attach = ohci_attach,
-    .detach = ohci_detach,
-    .child_detach = ohci_child_detach,
-    .wakeup = ohci_wakeup,
-    .complete = ohci_async_complete_packet,
-};
-
-static USBBusOps ohci_bus_ops = {
-};
-
-static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
-                         int num_ports, uint32_t localmem_base,
-                         char *masterbus, uint32_t firstport)
-{
-    int i;
-
-    if (usb_frame_time == 0) {
-#ifdef OHCI_TIME_WARP
-        usb_frame_time = get_ticks_per_sec();
-        usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ/1000);
-#else
-        usb_frame_time = muldiv64(1, get_ticks_per_sec(), 1000);
-        if (get_ticks_per_sec() >= USB_HZ) {
-            usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ);
-        } else {
-            usb_bit_time = 1;
-        }
-#endif
-        DPRINTF("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n",
-                usb_frame_time, usb_bit_time);
-    }
-
-    ohci->num_ports = num_ports;
-    if (masterbus) {
-        USBPort *ports[OHCI_MAX_PORTS];
-        for(i = 0; i < num_ports; i++) {
-            ports[i] = &ohci->rhport[i].port;
-        }
-        if (usb_register_companion(masterbus, ports, num_ports,
-                firstport, ohci, &ohci_port_ops,
-                USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
-            return -1;
-        }
-    } else {
-        usb_bus_new(&ohci->bus, &ohci_bus_ops, dev);
-        for (i = 0; i < num_ports; i++) {
-            usb_register_port(&ohci->bus, &ohci->rhport[i].port,
-                              ohci, i, &ohci_port_ops,
-                              USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
-        }
-    }
-
-    memory_region_init_io(&ohci->mem, &ohci_mem_ops, ohci, "ohci", 256);
-    ohci->localmem_base = localmem_base;
-
-    ohci->name = object_get_typename(OBJECT(dev));
-    usb_packet_init(&ohci->usb_packet);
-
-    ohci->async_td = 0;
-    qemu_register_reset(ohci_reset, ohci);
-
-    return 0;
-}
-
-typedef struct {
-    PCIDevice pci_dev;
-    OHCIState state;
-    char *masterbus;
-    uint32_t num_ports;
-    uint32_t firstport;
-} OHCIPCIState;
-
-static int usb_ohci_initfn_pci(struct PCIDevice *dev)
-{
-    OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev);
-
-    ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */
-    ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
-
-    if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0,
-                      ohci->masterbus, ohci->firstport) != 0) {
-        return -1;
-    }
-    ohci->state.irq = ohci->pci_dev.irq[0];
-
-    /* TODO: avoid cast below by using dev */
-    pci_register_bar(&ohci->pci_dev, 0, 0, &ohci->state.mem);
-    return 0;
-}
-
-void usb_ohci_init_pci(struct PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "pci-ohci");
-}
-
-typedef struct {
-    SysBusDevice busdev;
-    OHCIState ohci;
-    uint32_t num_ports;
-    target_phys_addr_t dma_offset;
-} OHCISysBusState;
-
-static int ohci_init_pxa(SysBusDevice *dev)
-{
-    OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev);
-
-    /* Cannot fail as we pass NULL for masterbus */
-    usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0);
-    sysbus_init_irq(dev, &s->ohci.irq);
-    sysbus_init_mmio(dev, &s->ohci.mem);
-
-    return 0;
-}
-
-static Property ohci_pci_properties[] = {
-    DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus),
-    DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3),
-    DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void ohci_pci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_ohci_initfn_pci;
-    k->vendor_id = PCI_VENDOR_ID_APPLE;
-    k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->desc = "Apple USB Controller";
-    dc->props = ohci_pci_properties;
-}
-
-static TypeInfo ohci_pci_info = {
-    .name          = "pci-ohci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(OHCIPCIState),
-    .class_init    = ohci_pci_class_init,
-};
-
-static Property ohci_sysbus_properties[] = {
-    DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3),
-    DEFINE_PROP_TADDR("dma-offset", OHCISysBusState, dma_offset, 3),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void ohci_sysbus_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sbc->init = ohci_init_pxa;
-    dc->desc = "OHCI USB Controller";
-    dc->props = ohci_sysbus_properties;
-}
-
-static TypeInfo ohci_sysbus_info = {
-    .name          = "sysbus-ohci",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(OHCISysBusState),
-    .class_init    = ohci_sysbus_class_init,
-};
-
-static void ohci_register_types(void)
-{
-    type_register_static(&ohci_pci_info);
-    type_register_static(&ohci_sysbus_info);
-}
-
-type_init(ohci_register_types)
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
deleted file mode 100644 (file)
index 0aae379..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-/*
- * FTDI FT232BM Device emulation
- *
- * Copyright (c) 2006 CodeSourcery.
- * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
- * Written by Paul Brook, reused for FTDI by Samuel Thibault
- *
- * This code is licensed under the LGPL.
- */
-
-#include "qemu-common.h"
-#include "qemu-error.h"
-#include "usb.h"
-#include "usb-desc.h"
-#include "qemu-char.h"
-
-//#define DEBUG_Serial
-
-#ifdef DEBUG_Serial
-#define DPRINTF(fmt, ...) \
-do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-#define RECV_BUF 384
-
-/* Commands */
-#define FTDI_RESET             0
-#define FTDI_SET_MDM_CTRL      1
-#define FTDI_SET_FLOW_CTRL     2
-#define FTDI_SET_BAUD          3
-#define FTDI_SET_DATA          4
-#define FTDI_GET_MDM_ST                5
-#define FTDI_SET_EVENT_CHR     6
-#define FTDI_SET_ERROR_CHR     7
-#define FTDI_SET_LATENCY       9
-#define FTDI_GET_LATENCY       10
-
-#define DeviceOutVendor        ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
-#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
-
-/* RESET */
-
-#define FTDI_RESET_SIO 0
-#define FTDI_RESET_RX  1
-#define FTDI_RESET_TX  2
-
-/* SET_MDM_CTRL */
-
-#define FTDI_DTR       1
-#define FTDI_SET_DTR   (FTDI_DTR << 8)
-#define FTDI_RTS       2
-#define FTDI_SET_RTS   (FTDI_RTS << 8)
-
-/* SET_FLOW_CTRL */
-
-#define FTDI_RTS_CTS_HS                1
-#define FTDI_DTR_DSR_HS                2
-#define FTDI_XON_XOFF_HS       4
-
-/* SET_DATA */
-
-#define FTDI_PARITY    (0x7 << 8)
-#define FTDI_ODD       (0x1 << 8)
-#define FTDI_EVEN      (0x2 << 8)
-#define FTDI_MARK      (0x3 << 8)
-#define FTDI_SPACE     (0x4 << 8)
-
-#define FTDI_STOP      (0x3 << 11)
-#define FTDI_STOP1     (0x0 << 11)
-#define FTDI_STOP15    (0x1 << 11)
-#define FTDI_STOP2     (0x2 << 11)
-
-/* GET_MDM_ST */
-/* TODO: should be sent every 40ms */
-#define FTDI_CTS  (1<<4)        // CTS line status
-#define FTDI_DSR  (1<<5)        // DSR line status
-#define FTDI_RI   (1<<6)        // RI line status
-#define FTDI_RLSD (1<<7)        // Receive Line Signal Detect
-
-/* Status */
-
-#define FTDI_DR   (1<<0)        // Data Ready
-#define FTDI_OE   (1<<1)        // Overrun Err
-#define FTDI_PE   (1<<2)        // Parity Err
-#define FTDI_FE   (1<<3)        // Framing Err
-#define FTDI_BI   (1<<4)        // Break Interrupt
-#define FTDI_THRE (1<<5)        // Transmitter Holding Register
-#define FTDI_TEMT (1<<6)        // Transmitter Empty
-#define FTDI_FIFO (1<<7)        // Error in FIFO
-
-typedef struct {
-    USBDevice dev;
-    uint8_t recv_buf[RECV_BUF];
-    uint16_t recv_ptr;
-    uint16_t recv_used;
-    uint8_t event_chr;
-    uint8_t error_chr;
-    uint8_t event_trigger;
-    QEMUSerialSetParams params;
-    int latency;        /* ms */
-    CharDriverState *cs;
-} USBSerialState;
-
-enum {
-    STR_MANUFACTURER = 1,
-    STR_PRODUCT_SERIAL,
-    STR_PRODUCT_BRAILLE,
-    STR_SERIALNUMBER,
-};
-
-static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]    = "QEMU " QEMU_VERSION,
-    [STR_PRODUCT_SERIAL]  = "QEMU USB SERIAL",
-    [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
-    [STR_SERIALNUMBER]    = "1",
-};
-
-static const USBDescIface desc_iface0 = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 2,
-    .bInterfaceClass               = 0xff,
-    .bInterfaceSubClass            = 0xff,
-    .bInterfaceProtocol            = 0xff,
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 64,
-        },{
-            .bEndpointAddress      = USB_DIR_OUT | 0x02,
-            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
-            .wMaxPacketSize        = 64,
-        },
-    }
-};
-
-static const USBDescDevice desc_device = {
-    .bcdUSB                        = 0x0200,
-    .bMaxPacketSize0               = 8,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .bmAttributes          = 0x80,
-            .bMaxPower             = 50,
-            .nif = 1,
-            .ifs = &desc_iface0,
-        },
-    },
-};
-
-static const USBDesc desc_serial = {
-    .id = {
-        .idVendor          = 0x0403,
-        .idProduct         = 0x6001,
-        .bcdDevice         = 0x0400,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT_SERIAL,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device,
-    .str  = desc_strings,
-};
-
-static const USBDesc desc_braille = {
-    .id = {
-        .idVendor          = 0x0403,
-        .idProduct         = 0xfe72,
-        .bcdDevice         = 0x0400,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT_BRAILLE,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device,
-    .str  = desc_strings,
-};
-
-static void usb_serial_reset(USBSerialState *s)
-{
-    /* TODO: Set flow control to none */
-    s->event_chr = 0x0d;
-    s->event_trigger = 0;
-    s->recv_ptr = 0;
-    s->recv_used = 0;
-    /* TODO: purge in char driver */
-}
-
-static void usb_serial_handle_reset(USBDevice *dev)
-{
-    USBSerialState *s = (USBSerialState *)dev;
-
-    DPRINTF("Reset\n");
-
-    usb_serial_reset(s);
-    /* TODO: Reset char device, send BREAK? */
-}
-
-static uint8_t usb_get_modem_lines(USBSerialState *s)
-{
-    int flags;
-    uint8_t ret;
-
-    if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP)
-        return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
-
-    ret = 0;
-    if (flags & CHR_TIOCM_CTS)
-        ret |= FTDI_CTS;
-    if (flags & CHR_TIOCM_DSR)
-        ret |= FTDI_DSR;
-    if (flags & CHR_TIOCM_RI)
-        ret |= FTDI_RI;
-    if (flags & CHR_TIOCM_CAR)
-        ret |= FTDI_RLSD;
-
-    return ret;
-}
-
-static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    USBSerialState *s = (USBSerialState *)dev;
-    int ret;
-
-    DPRINTF("got control %x, value %x\n",request, value);
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    ret = 0;
-    switch (request) {
-    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
-        ret = 0;
-        break;
-
-        /* Class specific requests.  */
-    case DeviceOutVendor | FTDI_RESET:
-        switch (value) {
-        case FTDI_RESET_SIO:
-            usb_serial_reset(s);
-            break;
-        case FTDI_RESET_RX:
-            s->recv_ptr = 0;
-            s->recv_used = 0;
-            /* TODO: purge from char device */
-            break;
-        case FTDI_RESET_TX:
-            /* TODO: purge from char device */
-            break;
-        }
-        break;
-    case DeviceOutVendor | FTDI_SET_MDM_CTRL:
-    {
-        static int flags;
-        qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
-        if (value & FTDI_SET_RTS) {
-            if (value & FTDI_RTS)
-                flags |= CHR_TIOCM_RTS;
-            else
-                flags &= ~CHR_TIOCM_RTS;
-        }
-        if (value & FTDI_SET_DTR) {
-            if (value & FTDI_DTR)
-                flags |= CHR_TIOCM_DTR;
-            else
-                flags &= ~CHR_TIOCM_DTR;
-        }
-        qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
-        break;
-    }
-    case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
-        /* TODO: ioctl */
-        break;
-    case DeviceOutVendor | FTDI_SET_BAUD: {
-        static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
-        int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
-                                     | ((index & 1) << 2)];
-        int divisor = value & 0x3fff;
-
-        /* chip special cases */
-        if (divisor == 1 && subdivisor8 == 0)
-            subdivisor8 = 4;
-        if (divisor == 0 && subdivisor8 == 0)
-            divisor = 1;
-
-        s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
-        qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
-        break;
-    }
-    case DeviceOutVendor | FTDI_SET_DATA:
-        switch (value & FTDI_PARITY) {
-            case 0:
-                s->params.parity = 'N';
-                break;
-            case FTDI_ODD:
-                s->params.parity = 'O';
-                break;
-            case FTDI_EVEN:
-                s->params.parity = 'E';
-                break;
-            default:
-                DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
-                goto fail;
-        }
-        switch (value & FTDI_STOP) {
-            case FTDI_STOP1:
-                s->params.stop_bits = 1;
-                break;
-            case FTDI_STOP2:
-                s->params.stop_bits = 2;
-                break;
-            default:
-                DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
-                goto fail;
-        }
-        qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
-        /* TODO: TX ON/OFF */
-        break;
-    case DeviceInVendor | FTDI_GET_MDM_ST:
-        data[0] = usb_get_modem_lines(s) | 1;
-        data[1] = 0;
-        ret = 2;
-        break;
-    case DeviceOutVendor | FTDI_SET_EVENT_CHR:
-        /* TODO: handle it */
-        s->event_chr = value;
-        break;
-    case DeviceOutVendor | FTDI_SET_ERROR_CHR:
-        /* TODO: handle it */
-        s->error_chr = value;
-        break;
-    case DeviceOutVendor | FTDI_SET_LATENCY:
-        s->latency = value;
-        break;
-    case DeviceInVendor | FTDI_GET_LATENCY:
-        data[0] = s->latency;
-        ret = 1;
-        break;
-    default:
-    fail:
-        DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBSerialState *s = (USBSerialState *)dev;
-    int i, ret = 0;
-    uint8_t devep = p->ep->nr;
-    struct iovec *iov;
-    uint8_t header[2];
-    int first_len, len;
-
-    switch (p->pid) {
-    case USB_TOKEN_OUT:
-        if (devep != 2)
-            goto fail;
-        for (i = 0; i < p->iov.niov; i++) {
-            iov = p->iov.iov + i;
-            qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len);
-        }
-        break;
-
-    case USB_TOKEN_IN:
-        if (devep != 1)
-            goto fail;
-        first_len = RECV_BUF - s->recv_ptr;
-        len = p->iov.size;
-        if (len <= 2) {
-            ret = USB_RET_NAK;
-            break;
-        }
-        header[0] = usb_get_modem_lines(s) | 1;
-        /* We do not have the uart details */
-        /* handle serial break */
-        if (s->event_trigger && s->event_trigger & FTDI_BI) {
-            s->event_trigger &= ~FTDI_BI;
-            header[1] = FTDI_BI;
-            usb_packet_copy(p, header, 2);
-            ret = 2;
-            break;
-        } else {
-            header[1] = 0;
-        }
-        len -= 2;
-        if (len > s->recv_used)
-            len = s->recv_used;
-        if (!len) {
-            ret = USB_RET_NAK;
-            break;
-        }
-        if (first_len > len)
-            first_len = len;
-        usb_packet_copy(p, header, 2);
-        usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len);
-        if (len > first_len)
-            usb_packet_copy(p, s->recv_buf, len - first_len);
-        s->recv_used -= len;
-        s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
-        ret = len + 2;
-        break;
-
-    default:
-        DPRINTF("Bad token\n");
-    fail:
-        ret = USB_RET_STALL;
-        break;
-    }
-
-    return ret;
-}
-
-static void usb_serial_handle_destroy(USBDevice *dev)
-{
-    USBSerialState *s = (USBSerialState *)dev;
-
-    qemu_chr_delete(s->cs);
-}
-
-static int usb_serial_can_read(void *opaque)
-{
-    USBSerialState *s = opaque;
-    return RECV_BUF - s->recv_used;
-}
-
-static void usb_serial_read(void *opaque, const uint8_t *buf, int size)
-{
-    USBSerialState *s = opaque;
-    int first_size, start;
-
-    /* room in the buffer? */
-    if (size > (RECV_BUF - s->recv_used))
-        size = RECV_BUF - s->recv_used;
-
-    start = s->recv_ptr + s->recv_used;
-    if (start < RECV_BUF) {
-        /* copy data to end of buffer */
-        first_size = RECV_BUF - start;
-        if (first_size > size)
-            first_size = size;
-
-        memcpy(s->recv_buf + start, buf, first_size);
-
-        /* wrap around to front if needed */
-        if (size > first_size)
-            memcpy(s->recv_buf, buf + first_size, size - first_size);
-    } else {
-        start -= RECV_BUF;
-        memcpy(s->recv_buf + start, buf, size);
-    }
-    s->recv_used += size;
-}
-
-static void usb_serial_event(void *opaque, int event)
-{
-    USBSerialState *s = opaque;
-
-    switch (event) {
-        case CHR_EVENT_BREAK:
-            s->event_trigger |= FTDI_BI;
-            break;
-        case CHR_EVENT_FOCUS:
-            break;
-        case CHR_EVENT_OPENED:
-            usb_serial_reset(s);
-            /* TODO: Reset USB port */
-            break;
-    }
-}
-
-static int usb_serial_initfn(USBDevice *dev)
-{
-    USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev);
-
-    usb_desc_init(dev);
-
-    if (!s->cs) {
-        error_report("Property chardev is required");
-        return -1;
-    }
-
-    qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read,
-                          usb_serial_event, s);
-    usb_serial_handle_reset(dev);
-    return 0;
-}
-
-static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
-{
-    USBDevice *dev;
-    CharDriverState *cdrv;
-    uint32_t vendorid = 0, productid = 0;
-    char label[32];
-    static int index;
-
-    while (*filename && *filename != ':') {
-        const char *p;
-        char *e;
-        if (strstart(filename, "vendorid=", &p)) {
-            vendorid = strtol(p, &e, 16);
-            if (e == p || (*e && *e != ',' && *e != ':')) {
-                error_report("bogus vendor ID %s", p);
-                return NULL;
-            }
-            filename = e;
-        } else if (strstart(filename, "productid=", &p)) {
-            productid = strtol(p, &e, 16);
-            if (e == p || (*e && *e != ',' && *e != ':')) {
-                error_report("bogus product ID %s", p);
-                return NULL;
-            }
-            filename = e;
-        } else {
-            error_report("unrecognized serial USB option %s", filename);
-            return NULL;
-        }
-        while(*filename == ',')
-            filename++;
-    }
-    if (!*filename) {
-        error_report("character device specification needed");
-        return NULL;
-    }
-    filename++;
-
-    snprintf(label, sizeof(label), "usbserial%d", index++);
-    cdrv = qemu_chr_new(label, filename, NULL);
-    if (!cdrv)
-        return NULL;
-
-    dev = usb_create(bus, "usb-serial");
-    if (!dev) {
-        return NULL;
-    }
-    qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
-    if (vendorid)
-        qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid);
-    if (productid)
-        qdev_prop_set_uint16(&dev->qdev, "productid", productid);
-    qdev_init_nofail(&dev->qdev);
-
-    return dev;
-}
-
-static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
-{
-    USBDevice *dev;
-    CharDriverState *cdrv;
-
-    cdrv = qemu_chr_new("braille", "braille", NULL);
-    if (!cdrv)
-        return NULL;
-
-    dev = usb_create(bus, "usb-braille");
-    qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
-    qdev_init_nofail(&dev->qdev);
-
-    return dev;
-}
-
-static const VMStateDescription vmstate_usb_serial = {
-    .name = "usb-serial",
-    .unmigratable = 1,
-};
-
-static Property serial_properties[] = {
-    DEFINE_PROP_CHR("chardev", USBSerialState, cs),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usb_serial_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init = usb_serial_initfn;
-    uc->product_desc   = "QEMU USB Serial";
-    uc->usb_desc       = &desc_serial;
-    uc->handle_reset   = usb_serial_handle_reset;
-    uc->handle_control = usb_serial_handle_control;
-    uc->handle_data    = usb_serial_handle_data;
-    uc->handle_destroy = usb_serial_handle_destroy;
-    dc->vmsd = &vmstate_usb_serial;
-    dc->props = serial_properties;
-}
-
-static TypeInfo serial_info = {
-    .name          = "usb-serial",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBSerialState),
-    .class_init    = usb_serial_class_initfn,
-};
-
-static Property braille_properties[] = {
-    DEFINE_PROP_CHR("chardev", USBSerialState, cs),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usb_braille_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = usb_serial_initfn;
-    uc->product_desc   = "QEMU USB Braille";
-    uc->usb_desc       = &desc_braille;
-    uc->handle_reset   = usb_serial_handle_reset;
-    uc->handle_control = usb_serial_handle_control;
-    uc->handle_data    = usb_serial_handle_data;
-    uc->handle_destroy = usb_serial_handle_destroy;
-    dc->vmsd = &vmstate_usb_serial;
-    dc->props = braille_properties;
-}
-
-static TypeInfo braille_info = {
-    .name          = "usb-braille",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBSerialState),
-    .class_init    = usb_braille_class_initfn,
-};
-
-static void usb_serial_register_types(void)
-{
-    type_register_static(&serial_info);
-    usb_legacy_register("usb-serial", "serial", usb_serial_init);
-    type_register_static(&braille_info);
-    usb_legacy_register("usb-braille", "braille", usb_braille_init);
-}
-
-type_init(usb_serial_register_types)
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
deleted file mode 100644 (file)
index 304b84b..0000000
+++ /dev/null
@@ -1,1408 +0,0 @@
-/*
- * USB UHCI controller emulation
- *
- * Copyright (c) 2005 Fabrice Bellard
- *
- * Copyright (c) 2008 Max Krasnyansky
- *     Magor rewrite of the UHCI data structures parser and frame processor
- *     Support for fully async operation and multiple outstanding transactions
- *
- * 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 "hw.h"
-#include "usb.h"
-#include "pci.h"
-#include "qemu-timer.h"
-#include "usb-uhci.h"
-#include "iov.h"
-#include "dma.h"
-
-//#define DEBUG
-//#define DEBUG_DUMP_DATA
-
-#define UHCI_CMD_FGR      (1 << 4)
-#define UHCI_CMD_EGSM     (1 << 3)
-#define UHCI_CMD_GRESET   (1 << 2)
-#define UHCI_CMD_HCRESET  (1 << 1)
-#define UHCI_CMD_RS       (1 << 0)
-
-#define UHCI_STS_HCHALTED (1 << 5)
-#define UHCI_STS_HCPERR   (1 << 4)
-#define UHCI_STS_HSERR    (1 << 3)
-#define UHCI_STS_RD       (1 << 2)
-#define UHCI_STS_USBERR   (1 << 1)
-#define UHCI_STS_USBINT   (1 << 0)
-
-#define TD_CTRL_SPD     (1 << 29)
-#define TD_CTRL_ERROR_SHIFT  27
-#define TD_CTRL_IOS     (1 << 25)
-#define TD_CTRL_IOC     (1 << 24)
-#define TD_CTRL_ACTIVE  (1 << 23)
-#define TD_CTRL_STALL   (1 << 22)
-#define TD_CTRL_BABBLE  (1 << 20)
-#define TD_CTRL_NAK     (1 << 19)
-#define TD_CTRL_TIMEOUT (1 << 18)
-
-#define UHCI_PORT_SUSPEND (1 << 12)
-#define UHCI_PORT_RESET (1 << 9)
-#define UHCI_PORT_LSDA  (1 << 8)
-#define UHCI_PORT_RD    (1 << 6)
-#define UHCI_PORT_ENC   (1 << 3)
-#define UHCI_PORT_EN    (1 << 2)
-#define UHCI_PORT_CSC   (1 << 1)
-#define UHCI_PORT_CCS   (1 << 0)
-
-#define UHCI_PORT_READ_ONLY    (0x1bb)
-#define UHCI_PORT_WRITE_CLEAR  (UHCI_PORT_CSC | UHCI_PORT_ENC)
-
-#define FRAME_TIMER_FREQ 1000
-
-#define FRAME_MAX_LOOPS  256
-
-#define NB_PORTS 2
-
-#ifdef DEBUG
-#define DPRINTF printf
-
-static const char *pid2str(int pid)
-{
-    switch (pid) {
-    case USB_TOKEN_SETUP: return "SETUP";
-    case USB_TOKEN_IN:    return "IN";
-    case USB_TOKEN_OUT:   return "OUT";
-    }
-    return "?";
-}
-
-#else
-#define DPRINTF(...)
-#endif
-
-typedef struct UHCIState UHCIState;
-typedef struct UHCIAsync UHCIAsync;
-typedef struct UHCIQueue UHCIQueue;
-
-/* 
- * Pending async transaction.
- * 'packet' must be the first field because completion
- * handler does "(UHCIAsync *) pkt" cast.
- */
-
-struct UHCIAsync {
-    USBPacket packet;
-    QEMUSGList sgl;
-    UHCIQueue *queue;
-    QTAILQ_ENTRY(UHCIAsync) next;
-    uint32_t  td;
-    uint8_t   isoc;
-    uint8_t   done;
-};
-
-struct UHCIQueue {
-    uint32_t  token;
-    UHCIState *uhci;
-    QTAILQ_ENTRY(UHCIQueue) next;
-    QTAILQ_HEAD(, UHCIAsync) asyncs;
-    int8_t    valid;
-};
-
-typedef struct UHCIPort {
-    USBPort port;
-    uint16_t ctrl;
-} UHCIPort;
-
-struct UHCIState {
-    PCIDevice dev;
-    MemoryRegion io_bar;
-    USBBus bus; /* Note unused when we're a companion controller */
-    uint16_t cmd; /* cmd register */
-    uint16_t status;
-    uint16_t intr; /* interrupt enable register */
-    uint16_t frnum; /* frame number */
-    uint32_t fl_base_addr; /* frame list base address */
-    uint8_t sof_timing;
-    uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
-    int64_t expire_time;
-    QEMUTimer *frame_timer;
-    UHCIPort ports[NB_PORTS];
-
-    /* Interrupts that should be raised at the end of the current frame.  */
-    uint32_t pending_int_mask;
-
-    /* Active packets */
-    QTAILQ_HEAD(, UHCIQueue) queues;
-    uint8_t num_ports_vmstate;
-
-    /* Properties */
-    char *masterbus;
-    uint32_t firstport;
-};
-
-typedef struct UHCI_TD {
-    uint32_t link;
-    uint32_t ctrl; /* see TD_CTRL_xxx */
-    uint32_t token;
-    uint32_t buffer;
-} UHCI_TD;
-
-typedef struct UHCI_QH {
-    uint32_t link;
-    uint32_t el_link;
-} UHCI_QH;
-
-static inline int32_t uhci_queue_token(UHCI_TD *td)
-{
-    /* covers ep, dev, pid -> identifies the endpoint */
-    return td->token & 0x7ffff;
-}
-
-static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
-{
-    uint32_t token = uhci_queue_token(td);
-    UHCIQueue *queue;
-
-    QTAILQ_FOREACH(queue, &s->queues, next) {
-        if (queue->token == token) {
-            return queue;
-        }
-    }
-
-    queue = g_new0(UHCIQueue, 1);
-    queue->uhci = s;
-    queue->token = token;
-    QTAILQ_INIT(&queue->asyncs);
-    QTAILQ_INSERT_HEAD(&s->queues, queue, next);
-    return queue;
-}
-
-static void uhci_queue_free(UHCIQueue *queue)
-{
-    UHCIState *s = queue->uhci;
-
-    QTAILQ_REMOVE(&s->queues, queue, next);
-    g_free(queue);
-}
-
-static UHCIAsync *uhci_async_alloc(UHCIQueue *queue)
-{
-    UHCIAsync *async = g_new0(UHCIAsync, 1);
-
-    async->queue = queue;
-    usb_packet_init(&async->packet);
-    pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
-
-    return async;
-}
-
-static void uhci_async_free(UHCIAsync *async)
-{
-    usb_packet_cleanup(&async->packet);
-    qemu_sglist_destroy(&async->sgl);
-    g_free(async);
-}
-
-static void uhci_async_link(UHCIAsync *async)
-{
-    UHCIQueue *queue = async->queue;
-    QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
-}
-
-static void uhci_async_unlink(UHCIAsync *async)
-{
-    UHCIQueue *queue = async->queue;
-    QTAILQ_REMOVE(&queue->asyncs, async, next);
-}
-
-static void uhci_async_cancel(UHCIAsync *async)
-{
-    DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
-           async->td, async->token, async->done);
-
-    if (!async->done)
-        usb_cancel_packet(&async->packet);
-    uhci_async_free(async);
-}
-
-/*
- * Mark all outstanding async packets as invalid.
- * This is used for canceling them when TDs are removed by the HCD.
- */
-static void uhci_async_validate_begin(UHCIState *s)
-{
-    UHCIQueue *queue;
-
-    QTAILQ_FOREACH(queue, &s->queues, next) {
-        queue->valid--;
-    }
-}
-
-/*
- * Cancel async packets that are no longer valid
- */
-static void uhci_async_validate_end(UHCIState *s)
-{
-    UHCIQueue *queue, *n;
-    UHCIAsync *async;
-
-    QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
-        if (queue->valid > 0) {
-            continue;
-        }
-        while (!QTAILQ_EMPTY(&queue->asyncs)) {
-            async = QTAILQ_FIRST(&queue->asyncs);
-            uhci_async_unlink(async);
-            uhci_async_cancel(async);
-        }
-        uhci_queue_free(queue);
-    }
-}
-
-static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
-{
-    UHCIQueue *queue;
-    UHCIAsync *curr, *n;
-
-    QTAILQ_FOREACH(queue, &s->queues, next) {
-        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
-            if (!usb_packet_is_inflight(&curr->packet) ||
-                curr->packet.ep->dev != dev) {
-                continue;
-            }
-            uhci_async_unlink(curr);
-            uhci_async_cancel(curr);
-        }
-    }
-}
-
-static void uhci_async_cancel_all(UHCIState *s)
-{
-    UHCIQueue *queue;
-    UHCIAsync *curr, *n;
-
-    QTAILQ_FOREACH(queue, &s->queues, next) {
-        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
-            uhci_async_unlink(curr);
-            uhci_async_cancel(curr);
-        }
-    }
-}
-
-static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
-{
-    uint32_t token = uhci_queue_token(td);
-    UHCIQueue *queue;
-    UHCIAsync *async;
-
-    QTAILQ_FOREACH(queue, &s->queues, next) {
-        if (queue->token == token) {
-            break;
-        }
-    }
-    if (queue == NULL) {
-        return NULL;
-    }
-
-    QTAILQ_FOREACH(async, &queue->asyncs, next) {
-        if (async->td == addr) {
-            return async;
-        }
-    }
-
-    return NULL;
-}
-
-static void uhci_update_irq(UHCIState *s)
-{
-    int level;
-    if (((s->status2 & 1) && (s->intr & (1 << 2))) ||
-        ((s->status2 & 2) && (s->intr & (1 << 3))) ||
-        ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) ||
-        ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) ||
-        (s->status & UHCI_STS_HSERR) ||
-        (s->status & UHCI_STS_HCPERR)) {
-        level = 1;
-    } else {
-        level = 0;
-    }
-    qemu_set_irq(s->dev.irq[3], level);
-}
-
-static void uhci_reset(void *opaque)
-{
-    UHCIState *s = opaque;
-    uint8_t *pci_conf;
-    int i;
-    UHCIPort *port;
-
-    DPRINTF("uhci: full reset\n");
-
-    pci_conf = s->dev.config;
-
-    pci_conf[0x6a] = 0x01; /* usb clock */
-    pci_conf[0x6b] = 0x00;
-    s->cmd = 0;
-    s->status = 0;
-    s->status2 = 0;
-    s->intr = 0;
-    s->fl_base_addr = 0;
-    s->sof_timing = 64;
-
-    for(i = 0; i < NB_PORTS; i++) {
-        port = &s->ports[i];
-        port->ctrl = 0x0080;
-        if (port->port.dev && port->port.dev->attached) {
-            usb_port_reset(&port->port);
-        }
-    }
-
-    uhci_async_cancel_all(s);
-}
-
-static void uhci_pre_save(void *opaque)
-{
-    UHCIState *s = opaque;
-
-    uhci_async_cancel_all(s);
-}
-
-static const VMStateDescription vmstate_uhci_port = {
-    .name = "uhci port",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT16(ctrl, UHCIPort),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_uhci = {
-    .name = "uhci",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = uhci_pre_save,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, UHCIState),
-        VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
-        VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1,
-                             vmstate_uhci_port, UHCIPort),
-        VMSTATE_UINT16(cmd, UHCIState),
-        VMSTATE_UINT16(status, UHCIState),
-        VMSTATE_UINT16(intr, UHCIState),
-        VMSTATE_UINT16(frnum, UHCIState),
-        VMSTATE_UINT32(fl_base_addr, UHCIState),
-        VMSTATE_UINT8(sof_timing, UHCIState),
-        VMSTATE_UINT8(status2, UHCIState),
-        VMSTATE_TIMER(frame_timer, UHCIState),
-        VMSTATE_INT64_V(expire_time, UHCIState, 2),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    UHCIState *s = opaque;
-
-    addr &= 0x1f;
-    switch(addr) {
-    case 0x0c:
-        s->sof_timing = val;
-        break;
-    }
-}
-
-static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr)
-{
-    UHCIState *s = opaque;
-    uint32_t val;
-
-    addr &= 0x1f;
-    switch(addr) {
-    case 0x0c:
-        val = s->sof_timing;
-        break;
-    default:
-        val = 0xff;
-        break;
-    }
-    return val;
-}
-
-static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
-{
-    UHCIState *s = opaque;
-
-    addr &= 0x1f;
-    DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val);
-
-    switch(addr) {
-    case 0x00:
-        if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
-            /* start frame processing */
-            s->expire_time = qemu_get_clock_ns(vm_clock) +
-                (get_ticks_per_sec() / FRAME_TIMER_FREQ);
-            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
-            s->status &= ~UHCI_STS_HCHALTED;
-        } else if (!(val & UHCI_CMD_RS)) {
-            s->status |= UHCI_STS_HCHALTED;
-        }
-        if (val & UHCI_CMD_GRESET) {
-            UHCIPort *port;
-            int i;
-
-            /* send reset on the USB bus */
-            for(i = 0; i < NB_PORTS; i++) {
-                port = &s->ports[i];
-                usb_device_reset(port->port.dev);
-            }
-            uhci_reset(s);
-            return;
-        }
-        if (val & UHCI_CMD_HCRESET) {
-            uhci_reset(s);
-            return;
-        }
-        s->cmd = val;
-        break;
-    case 0x02:
-        s->status &= ~val;
-        /* XXX: the chip spec is not coherent, so we add a hidden
-           register to distinguish between IOC and SPD */
-        if (val & UHCI_STS_USBINT)
-            s->status2 = 0;
-        uhci_update_irq(s);
-        break;
-    case 0x04:
-        s->intr = val;
-        uhci_update_irq(s);
-        break;
-    case 0x06:
-        if (s->status & UHCI_STS_HCHALTED)
-            s->frnum = val & 0x7ff;
-        break;
-    case 0x10 ... 0x1f:
-        {
-            UHCIPort *port;
-            USBDevice *dev;
-            int n;
-
-            n = (addr >> 1) & 7;
-            if (n >= NB_PORTS)
-                return;
-            port = &s->ports[n];
-            dev = port->port.dev;
-            if (dev && dev->attached) {
-                /* port reset */
-                if ( (val & UHCI_PORT_RESET) &&
-                     !(port->ctrl & UHCI_PORT_RESET) ) {
-                    usb_device_reset(dev);
-                }
-            }
-            port->ctrl &= UHCI_PORT_READ_ONLY;
-            port->ctrl |= (val & ~UHCI_PORT_READ_ONLY);
-            /* some bits are reset when a '1' is written to them */
-            port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR);
-        }
-        break;
-    }
-}
-
-static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr)
-{
-    UHCIState *s = opaque;
-    uint32_t val;
-
-    addr &= 0x1f;
-    switch(addr) {
-    case 0x00:
-        val = s->cmd;
-        break;
-    case 0x02:
-        val = s->status;
-        break;
-    case 0x04:
-        val = s->intr;
-        break;
-    case 0x06:
-        val = s->frnum;
-        break;
-    case 0x10 ... 0x1f:
-        {
-            UHCIPort *port;
-            int n;
-            n = (addr >> 1) & 7;
-            if (n >= NB_PORTS)
-                goto read_default;
-            port = &s->ports[n];
-            val = port->ctrl;
-        }
-        break;
-    default:
-    read_default:
-        val = 0xff7f; /* disabled port */
-        break;
-    }
-
-    DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val);
-
-    return val;
-}
-
-static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
-{
-    UHCIState *s = opaque;
-
-    addr &= 0x1f;
-    DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val);
-
-    switch(addr) {
-    case 0x08:
-        s->fl_base_addr = val & ~0xfff;
-        break;
-    }
-}
-
-static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr)
-{
-    UHCIState *s = opaque;
-    uint32_t val;
-
-    addr &= 0x1f;
-    switch(addr) {
-    case 0x08:
-        val = s->fl_base_addr;
-        break;
-    default:
-        val = 0xffffffff;
-        break;
-    }
-    return val;
-}
-
-/* signal resume if controller suspended */
-static void uhci_resume (void *opaque)
-{
-    UHCIState *s = (UHCIState *)opaque;
-
-    if (!s)
-        return;
-
-    if (s->cmd & UHCI_CMD_EGSM) {
-        s->cmd |= UHCI_CMD_FGR;
-        s->status |= UHCI_STS_RD;
-        uhci_update_irq(s);
-    }
-}
-
-static void uhci_attach(USBPort *port1)
-{
-    UHCIState *s = port1->opaque;
-    UHCIPort *port = &s->ports[port1->index];
-
-    /* set connect status */
-    port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
-
-    /* update speed */
-    if (port->port.dev->speed == USB_SPEED_LOW) {
-        port->ctrl |= UHCI_PORT_LSDA;
-    } else {
-        port->ctrl &= ~UHCI_PORT_LSDA;
-    }
-
-    uhci_resume(s);
-}
-
-static void uhci_detach(USBPort *port1)
-{
-    UHCIState *s = port1->opaque;
-    UHCIPort *port = &s->ports[port1->index];
-
-    uhci_async_cancel_device(s, port1->dev);
-
-    /* set connect status */
-    if (port->ctrl & UHCI_PORT_CCS) {
-        port->ctrl &= ~UHCI_PORT_CCS;
-        port->ctrl |= UHCI_PORT_CSC;
-    }
-    /* disable port */
-    if (port->ctrl & UHCI_PORT_EN) {
-        port->ctrl &= ~UHCI_PORT_EN;
-        port->ctrl |= UHCI_PORT_ENC;
-    }
-
-    uhci_resume(s);
-}
-
-static void uhci_child_detach(USBPort *port1, USBDevice *child)
-{
-    UHCIState *s = port1->opaque;
-
-    uhci_async_cancel_device(s, child);
-}
-
-static void uhci_wakeup(USBPort *port1)
-{
-    UHCIState *s = port1->opaque;
-    UHCIPort *port = &s->ports[port1->index];
-
-    if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) {
-        port->ctrl |= UHCI_PORT_RD;
-        uhci_resume(s);
-    }
-}
-
-static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr)
-{
-    USBDevice *dev;
-    int i;
-
-    for (i = 0; i < NB_PORTS; i++) {
-        UHCIPort *port = &s->ports[i];
-        if (!(port->ctrl & UHCI_PORT_EN)) {
-            continue;
-        }
-        dev = usb_find_device(&port->port, addr);
-        if (dev != NULL) {
-            return dev;
-        }
-    }
-    return NULL;
-}
-
-static void uhci_async_complete(USBPort *port, USBPacket *packet);
-static void uhci_process_frame(UHCIState *s);
-
-/* return -1 if fatal error (frame must be stopped)
-          0 if TD successful
-          1 if TD unsuccessful or inactive
-*/
-static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
-{
-    int len = 0, max_len, err, ret;
-    uint8_t pid;
-
-    max_len = ((td->token >> 21) + 1) & 0x7ff;
-    pid = td->token & 0xff;
-
-    ret = async->packet.result;
-
-    if (td->ctrl & TD_CTRL_IOS)
-        td->ctrl &= ~TD_CTRL_ACTIVE;
-
-    if (ret < 0)
-        goto out;
-
-    len = async->packet.result;
-    td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
-
-    /* The NAK bit may have been set by a previous frame, so clear it
-       here.  The docs are somewhat unclear, but win2k relies on this
-       behavior.  */
-    td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK);
-    if (td->ctrl & TD_CTRL_IOC)
-        *int_mask |= 0x01;
-
-    if (pid == USB_TOKEN_IN) {
-        if (len > max_len) {
-            ret = USB_RET_BABBLE;
-            goto out;
-        }
-
-        if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
-            *int_mask |= 0x02;
-            /* short packet: do not update QH */
-            DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token);
-            return 1;
-        }
-    }
-
-    /* success */
-    return 0;
-
-out:
-    switch(ret) {
-    case USB_RET_STALL:
-        td->ctrl |= TD_CTRL_STALL;
-        td->ctrl &= ~TD_CTRL_ACTIVE;
-        s->status |= UHCI_STS_USBERR;
-        if (td->ctrl & TD_CTRL_IOC) {
-            *int_mask |= 0x01;
-        }
-        uhci_update_irq(s);
-        return 1;
-
-    case USB_RET_BABBLE:
-        td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
-        td->ctrl &= ~TD_CTRL_ACTIVE;
-        s->status |= UHCI_STS_USBERR;
-        if (td->ctrl & TD_CTRL_IOC) {
-            *int_mask |= 0x01;
-        }
-        uhci_update_irq(s);
-        /* frame interrupted */
-        return -1;
-
-    case USB_RET_NAK:
-        td->ctrl |= TD_CTRL_NAK;
-        if (pid == USB_TOKEN_SETUP)
-            break;
-       return 1;
-
-    case USB_RET_IOERROR:
-    case USB_RET_NODEV:
-    default:
-       break;
-    }
-
-    /* Retry the TD if error count is not zero */
-
-    td->ctrl |= TD_CTRL_TIMEOUT;
-    err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3;
-    if (err != 0) {
-        err--;
-        if (err == 0) {
-            td->ctrl &= ~TD_CTRL_ACTIVE;
-            s->status |= UHCI_STS_USBERR;
-            if (td->ctrl & TD_CTRL_IOC)
-                *int_mask |= 0x01;
-            uhci_update_irq(s);
-        }
-    }
-    td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) |
-        (err << TD_CTRL_ERROR_SHIFT);
-    return 1;
-}
-
-static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
-{
-    UHCIAsync *async;
-    int len = 0, max_len;
-    uint8_t pid;
-    USBDevice *dev;
-    USBEndpoint *ep;
-
-    /* Is active ? */
-    if (!(td->ctrl & TD_CTRL_ACTIVE))
-        return 1;
-
-    async = uhci_async_find_td(s, addr, td);
-    if (async) {
-        /* Already submitted */
-        async->queue->valid = 32;
-
-        if (!async->done)
-            return 1;
-
-        uhci_async_unlink(async);
-        goto done;
-    }
-
-    /* Allocate new packet */
-    async = uhci_async_alloc(uhci_queue_get(s, td));
-    if (!async)
-        return 1;
-
-    /* valid needs to be large enough to handle 10 frame delay
-     * for initial isochronous requests
-     */
-    async->queue->valid = 32;
-    async->td    = addr;
-    async->isoc  = td->ctrl & TD_CTRL_IOS;
-
-    max_len = ((td->token >> 21) + 1) & 0x7ff;
-    pid = td->token & 0xff;
-
-    dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
-    ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
-    usb_packet_setup(&async->packet, pid, ep);
-    qemu_sglist_add(&async->sgl, td->buffer, max_len);
-    usb_packet_map(&async->packet, &async->sgl);
-
-    switch(pid) {
-    case USB_TOKEN_OUT:
-    case USB_TOKEN_SETUP:
-        len = usb_handle_packet(dev, &async->packet);
-        if (len >= 0)
-            len = max_len;
-        break;
-
-    case USB_TOKEN_IN:
-        len = usb_handle_packet(dev, &async->packet);
-        break;
-
-    default:
-        /* invalid pid : frame interrupted */
-        uhci_async_free(async);
-        s->status |= UHCI_STS_HCPERR;
-        uhci_update_irq(s);
-        return -1;
-    }
-    if (len == USB_RET_ASYNC) {
-        uhci_async_link(async);
-        return 2;
-    }
-
-    async->packet.result = len;
-
-done:
-    len = uhci_complete_td(s, td, async, int_mask);
-    usb_packet_unmap(&async->packet);
-    uhci_async_free(async);
-    return len;
-}
-
-static void uhci_async_complete(USBPort *port, USBPacket *packet)
-{
-    UHCIAsync *async = container_of(packet, UHCIAsync, packet);
-    UHCIState *s = async->queue->uhci;
-
-    DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
-
-    if (async->isoc) {
-        UHCI_TD td;
-        uint32_t link = async->td;
-        uint32_t int_mask = 0, val;
-
-        pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td));
-        le32_to_cpus(&td.link);
-        le32_to_cpus(&td.ctrl);
-        le32_to_cpus(&td.token);
-        le32_to_cpus(&td.buffer);
-
-        uhci_async_unlink(async);
-        uhci_complete_td(s, &td, async, &int_mask);
-        s->pending_int_mask |= int_mask;
-
-        /* update the status bits of the TD */
-        val = cpu_to_le32(td.ctrl);
-        pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
-        uhci_async_free(async);
-    } else {
-        async->done = 1;
-        uhci_process_frame(s);
-    }
-}
-
-static int is_valid(uint32_t link)
-{
-    return (link & 1) == 0;
-}
-
-static int is_qh(uint32_t link)
-{
-    return (link & 2) != 0;
-}
-
-static int depth_first(uint32_t link)
-{
-    return (link & 4) != 0;
-}
-
-/* QH DB used for detecting QH loops */
-#define UHCI_MAX_QUEUES 128
-typedef struct {
-    uint32_t addr[UHCI_MAX_QUEUES];
-    int      count;
-} QhDb;
-
-static void qhdb_reset(QhDb *db)
-{
-    db->count = 0;
-}
-
-/* Add QH to DB. Returns 1 if already present or DB is full. */
-static int qhdb_insert(QhDb *db, uint32_t addr)
-{
-    int i;
-    for (i = 0; i < db->count; i++)
-        if (db->addr[i] == addr)
-            return 1;
-
-    if (db->count >= UHCI_MAX_QUEUES)
-        return 1;
-
-    db->addr[db->count++] = addr;
-    return 0;
-}
-
-static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
-{
-    uint32_t int_mask = 0;
-    uint32_t plink = td->link;
-    uint32_t token = uhci_queue_token(td);
-    UHCI_TD ptd;
-    int ret;
-
-    while (is_valid(plink)) {
-        pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
-        le32_to_cpus(&ptd.link);
-        le32_to_cpus(&ptd.ctrl);
-        le32_to_cpus(&ptd.token);
-        le32_to_cpus(&ptd.buffer);
-        if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
-            break;
-        }
-        if (uhci_queue_token(&ptd) != token) {
-            break;
-        }
-        ret = uhci_handle_td(s, plink, &ptd, &int_mask);
-        assert(ret == 2); /* got USB_RET_ASYNC */
-        assert(int_mask == 0);
-        plink = ptd.link;
-    }
-}
-
-static void uhci_process_frame(UHCIState *s)
-{
-    uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
-    uint32_t curr_qh, td_count = 0, bytes_count = 0;
-    int cnt, ret;
-    UHCI_TD td;
-    UHCI_QH qh;
-    QhDb qhdb;
-
-    frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
-
-    DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr);
-
-    pci_dma_read(&s->dev, frame_addr, &link, 4);
-    le32_to_cpus(&link);
-
-    int_mask = 0;
-    curr_qh  = 0;
-
-    qhdb_reset(&qhdb);
-
-    for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
-        if (is_qh(link)) {
-            /* QH */
-
-            if (qhdb_insert(&qhdb, link)) {
-                /*
-                 * We're going in circles. Which is not a bug because
-                 * HCD is allowed to do that as part of the BW management.
-                 *
-                 * Stop processing here if
-                 *  (a) no transaction has been done since we've been
-                 *      here last time, or
-                 *  (b) we've reached the usb 1.1 bandwidth, which is
-                 *      1280 bytes/frame.
-                 */
-                DPRINTF("uhci: detected loop. qh 0x%x\n", link);
-                if (td_count == 0) {
-                    DPRINTF("uhci: no transaction last round, stop\n");
-                    break;
-                } else if (bytes_count >= 1280) {
-                    DPRINTF("uhci: bandwidth limit reached, stop\n");
-                    break;
-                } else {
-                    td_count = 0;
-                    qhdb_reset(&qhdb);
-                    qhdb_insert(&qhdb, link);
-                }
-            }
-
-            pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh));
-            le32_to_cpus(&qh.link);
-            le32_to_cpus(&qh.el_link);
-
-            DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n",
-                    link, qh.link, qh.el_link);
-
-            if (!is_valid(qh.el_link)) {
-                /* QH w/o elements */
-                curr_qh = 0;
-                link = qh.link;
-            } else {
-                /* QH with elements */
-               curr_qh = link;
-               link = qh.el_link;
-            }
-            continue;
-        }
-
-        /* TD */
-        pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td));
-        le32_to_cpus(&td.link);
-        le32_to_cpus(&td.ctrl);
-        le32_to_cpus(&td.token);
-        le32_to_cpus(&td.buffer);
-
-        DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
-                link, td.link, td.ctrl, td.token, curr_qh);
-
-        old_td_ctrl = td.ctrl;
-        ret = uhci_handle_td(s, link, &td, &int_mask);
-        if (old_td_ctrl != td.ctrl) {
-            /* update the status bits of the TD */
-            val = cpu_to_le32(td.ctrl);
-            pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
-        }
-
-        switch (ret) {
-        case -1: /* interrupted frame */
-            goto out;
-
-        case 1: /* goto next queue */
-            DPRINTF("uhci: TD 0x%x skip. "
-                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
-                    link, td.link, td.ctrl, td.token, curr_qh);
-            link = curr_qh ? qh.link : td.link;
-            continue;
-
-        case 2: /* got USB_RET_ASYNC */
-            DPRINTF("uhci: TD 0x%x async. "
-                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
-                    link, td.link, td.ctrl, td.token, curr_qh);
-            if (is_valid(td.link)) {
-                uhci_fill_queue(s, &td);
-            }
-            link = curr_qh ? qh.link : td.link;
-            continue;
-
-        case 0: /* completed TD */
-            DPRINTF("uhci: TD 0x%x done. "
-                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
-                    link, td.link, td.ctrl, td.token, curr_qh);
-
-            link = td.link;
-            td_count++;
-            bytes_count += (td.ctrl & 0x7ff) + 1;
-
-            if (curr_qh) {
-                /* update QH element link */
-                qh.el_link = link;
-                val = cpu_to_le32(qh.el_link);
-                pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
-
-                if (!depth_first(link)) {
-                    /* done with this QH */
-
-                    DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
-                            curr_qh, qh.link, qh.el_link);
-
-                    curr_qh = 0;
-                    link    = qh.link;
-                }
-            }
-            break;
-
-        default:
-            assert(!"unknown return code");
-        }
-
-        /* go to the next entry */
-    }
-
-out:
-    s->pending_int_mask |= int_mask;
-}
-
-static void uhci_frame_timer(void *opaque)
-{
-    UHCIState *s = opaque;
-
-    /* prepare the timer for the next frame */
-    s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ);
-
-    if (!(s->cmd & UHCI_CMD_RS)) {
-        /* Full stop */
-        qemu_del_timer(s->frame_timer);
-        /* set hchalted bit in status - UHCI11D 2.1.2 */
-        s->status |= UHCI_STS_HCHALTED;
-
-        DPRINTF("uhci: halted\n");
-        return;
-    }
-
-    /* Complete the previous frame */
-    if (s->pending_int_mask) {
-        s->status2 |= s->pending_int_mask;
-        s->status  |= UHCI_STS_USBINT;
-        uhci_update_irq(s);
-    }
-    s->pending_int_mask = 0;
-
-    /* Start new frame */
-    s->frnum = (s->frnum + 1) & 0x7ff;
-
-    DPRINTF("uhci: new frame #%u\n" , s->frnum);
-
-    uhci_async_validate_begin(s);
-
-    uhci_process_frame(s);
-
-    uhci_async_validate_end(s);
-
-    qemu_mod_timer(s->frame_timer, s->expire_time);
-}
-
-static const MemoryRegionPortio uhci_portio[] = {
-    { 0, 32, 2, .write = uhci_ioport_writew, },
-    { 0, 32, 2, .read = uhci_ioport_readw, },
-    { 0, 32, 4, .write = uhci_ioport_writel, },
-    { 0, 32, 4, .read = uhci_ioport_readl, },
-    { 0, 32, 1, .write = uhci_ioport_writeb, },
-    { 0, 32, 1, .read = uhci_ioport_readb, },
-    PORTIO_END_OF_LIST()
-};
-
-static const MemoryRegionOps uhci_ioport_ops = {
-    .old_portio = uhci_portio,
-};
-
-static USBPortOps uhci_port_ops = {
-    .attach = uhci_attach,
-    .detach = uhci_detach,
-    .child_detach = uhci_child_detach,
-    .wakeup = uhci_wakeup,
-    .complete = uhci_async_complete,
-};
-
-static USBBusOps uhci_bus_ops = {
-};
-
-static int usb_uhci_common_initfn(PCIDevice *dev)
-{
-    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
-    uint8_t *pci_conf = s->dev.config;
-    int i;
-
-    pci_conf[PCI_CLASS_PROG] = 0x00;
-    /* TODO: reset value should be 0. */
-    pci_conf[PCI_INTERRUPT_PIN] = 4; /* interrupt pin D */
-    pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
-
-    if (s->masterbus) {
-        USBPort *ports[NB_PORTS];
-        for(i = 0; i < NB_PORTS; i++) {
-            ports[i] = &s->ports[i].port;
-        }
-        if (usb_register_companion(s->masterbus, ports, NB_PORTS,
-                s->firstport, s, &uhci_port_ops,
-                USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
-            return -1;
-        }
-    } else {
-        usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
-        for (i = 0; i < NB_PORTS; i++) {
-            usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
-                              USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
-        }
-    }
-    s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
-    s->num_ports_vmstate = NB_PORTS;
-    QTAILQ_INIT(&s->queues);
-
-    qemu_register_reset(uhci_reset, s);
-
-    memory_region_init_io(&s->io_bar, &uhci_ioport_ops, s, "uhci", 0x20);
-    /* Use region 4 for consistency with real hardware.  BSD guests seem
-       to rely on this.  */
-    pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
-
-    return 0;
-}
-
-static int usb_uhci_vt82c686b_initfn(PCIDevice *dev)
-{
-    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
-    uint8_t *pci_conf = s->dev.config;
-
-    /* USB misc control 1/2 */
-    pci_set_long(pci_conf + 0x40,0x00001000);
-    /* PM capability */
-    pci_set_long(pci_conf + 0x80,0x00020001);
-    /* USB legacy support  */
-    pci_set_long(pci_conf + 0xc0,0x00002000);
-
-    return usb_uhci_common_initfn(dev);
-}
-
-static int usb_uhci_exit(PCIDevice *dev)
-{
-    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
-
-    memory_region_destroy(&s->io_bar);
-    return 0;
-}
-
-static Property uhci_properties[] = {
-    DEFINE_PROP_STRING("masterbus", UHCIState, masterbus),
-    DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void piix3_uhci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_uhci_common_initfn;
-    k->exit = usb_uhci_exit;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82371SB_2;
-    k->revision = 0x01;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_uhci;
-    dc->props = uhci_properties;
-}
-
-static TypeInfo piix3_uhci_info = {
-    .name          = "piix3-usb-uhci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(UHCIState),
-    .class_init    = piix3_uhci_class_init,
-};
-
-static void piix4_uhci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_uhci_common_initfn;
-    k->exit = usb_uhci_exit;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82371AB_2;
-    k->revision = 0x01;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_uhci;
-    dc->props = uhci_properties;
-}
-
-static TypeInfo piix4_uhci_info = {
-    .name          = "piix4-usb-uhci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(UHCIState),
-    .class_init    = piix4_uhci_class_init,
-};
-
-static void vt82c686b_uhci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_uhci_vt82c686b_initfn;
-    k->exit = usb_uhci_exit;
-    k->vendor_id = PCI_VENDOR_ID_VIA;
-    k->device_id = PCI_DEVICE_ID_VIA_UHCI;
-    k->revision = 0x01;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_uhci;
-    dc->props = uhci_properties;
-}
-
-static TypeInfo vt82c686b_uhci_info = {
-    .name          = "vt82c686b-usb-uhci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(UHCIState),
-    .class_init    = vt82c686b_uhci_class_init,
-};
-
-static void ich9_uhci1_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_uhci_common_initfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1;
-    k->revision = 0x03;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_uhci;
-    dc->props = uhci_properties;
-}
-
-static TypeInfo ich9_uhci1_info = {
-    .name          = "ich9-usb-uhci1",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(UHCIState),
-    .class_init    = ich9_uhci1_class_init,
-};
-
-static void ich9_uhci2_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_uhci_common_initfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2;
-    k->revision = 0x03;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_uhci;
-    dc->props = uhci_properties;
-}
-
-static TypeInfo ich9_uhci2_info = {
-    .name          = "ich9-usb-uhci2",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(UHCIState),
-    .class_init    = ich9_uhci2_class_init,
-};
-
-static void ich9_uhci3_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = usb_uhci_common_initfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3;
-    k->revision = 0x03;
-    k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_uhci;
-    dc->props = uhci_properties;
-}
-
-static TypeInfo ich9_uhci3_info = {
-    .name          = "ich9-usb-uhci3",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(UHCIState),
-    .class_init    = ich9_uhci3_class_init,
-};
-
-static void uhci_register_types(void)
-{
-    type_register_static(&piix3_uhci_info);
-    type_register_static(&piix4_uhci_info);
-    type_register_static(&vt82c686b_uhci_info);
-    type_register_static(&ich9_uhci1_info);
-    type_register_static(&ich9_uhci2_info);
-    type_register_static(&ich9_uhci3_info);
-}
-
-type_init(uhci_register_types)
-
-void usb_uhci_piix3_init(PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "piix3-usb-uhci");
-}
-
-void usb_uhci_piix4_init(PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "piix4-usb-uhci");
-}
-
-void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "vt82c686b-usb-uhci");
-}
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
deleted file mode 100644 (file)
index 197e2dc..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Wacom PenPartner USB tablet emulation.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Author: Andrzej Zaborowski <balrog@zabor.org>
- *
- * Based on hw/usb-hid.c:
- * Copyright (c) 2005 Fabrice Bellard
- *
- * 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 "hw.h"
-#include "console.h"
-#include "usb.h"
-#include "usb-desc.h"
-
-/* Interface requests */
-#define WACOM_GET_REPORT       0x2101
-#define WACOM_SET_REPORT       0x2109
-
-/* HID interface requests */
-#define HID_GET_REPORT         0xa101
-#define HID_GET_IDLE           0xa102
-#define HID_GET_PROTOCOL       0xa103
-#define HID_SET_IDLE           0x210a
-#define HID_SET_PROTOCOL       0x210b
-
-typedef struct USBWacomState {
-    USBDevice dev;
-    QEMUPutMouseEntry *eh_entry;
-    int dx, dy, dz, buttons_state;
-    int x, y;
-    int mouse_grabbed;
-    enum {
-        WACOM_MODE_HID = 1,
-        WACOM_MODE_WACOM = 2,
-    } mode;
-    uint8_t idle;
-    int changed;
-} USBWacomState;
-
-enum {
-    STR_MANUFACTURER = 1,
-    STR_PRODUCT,
-    STR_SERIALNUMBER,
-};
-
-static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
-    [STR_PRODUCT]          = "Wacom PenPartner",
-    [STR_SERIALNUMBER]     = "1",
-};
-
-static const USBDescIface desc_iface_wacom = {
-    .bInterfaceNumber              = 0,
-    .bNumEndpoints                 = 1,
-    .bInterfaceClass               = USB_CLASS_HID,
-    .bInterfaceSubClass            = 0x01, /* boot */
-    .bInterfaceProtocol            = 0x02,
-    .ndesc                         = 1,
-    .descs = (USBDescOther[]) {
-        {
-            /* HID descriptor */
-            .data = (uint8_t[]) {
-                0x09,          /*  u8  bLength */
-                0x21,          /*  u8  bDescriptorType */
-                0x01, 0x10,    /*  u16 HID_class */
-                0x00,          /*  u8  country_code */
-                0x01,          /*  u8  num_descriptors */
-                0x22,          /*  u8  type: Report */
-                0x6e, 0,       /*  u16 len */
-            },
-        },
-    },
-    .eps = (USBDescEndpoint[]) {
-        {
-            .bEndpointAddress      = USB_DIR_IN | 0x01,
-            .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .wMaxPacketSize        = 8,
-            .bInterval             = 0x0a,
-        },
-    },
-};
-
-static const USBDescDevice desc_device_wacom = {
-    .bcdUSB                        = 0x0110,
-    .bMaxPacketSize0               = 8,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 1,
-            .bConfigurationValue   = 1,
-            .bmAttributes          = 0x80,
-            .bMaxPower             = 40,
-            .nif = 1,
-            .ifs = &desc_iface_wacom,
-        },
-    },
-};
-
-static const USBDesc desc_wacom = {
-    .id = {
-        .idVendor          = 0x056a,
-        .idProduct         = 0x0000,
-        .bcdDevice         = 0x4210,
-        .iManufacturer     = STR_MANUFACTURER,
-        .iProduct          = STR_PRODUCT,
-        .iSerialNumber     = STR_SERIALNUMBER,
-    },
-    .full = &desc_device_wacom,
-    .str  = desc_strings,
-};
-
-static void usb_mouse_event(void *opaque,
-                            int dx1, int dy1, int dz1, int buttons_state)
-{
-    USBWacomState *s = opaque;
-
-    s->dx += dx1;
-    s->dy += dy1;
-    s->dz += dz1;
-    s->buttons_state = buttons_state;
-    s->changed = 1;
-}
-
-static void usb_wacom_event(void *opaque,
-                            int x, int y, int dz, int buttons_state)
-{
-    USBWacomState *s = opaque;
-
-    /* scale to Penpartner resolution */
-    s->x = (x * 5040 / 0x7FFF);
-    s->y = (y * 3780 / 0x7FFF);
-    s->dz += dz;
-    s->buttons_state = buttons_state;
-    s->changed = 1;
-}
-
-static inline int int_clamp(int val, int vmin, int vmax)
-{
-    if (val < vmin)
-        return vmin;
-    else if (val > vmax)
-        return vmax;
-    else
-        return val;
-}
-
-static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
-{
-    int dx, dy, dz, b, l;
-
-    if (!s->mouse_grabbed) {
-        s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
-                        "QEMU PenPartner tablet");
-        qemu_activate_mouse_event_handler(s->eh_entry);
-        s->mouse_grabbed = 1;
-    }
-
-    dx = int_clamp(s->dx, -128, 127);
-    dy = int_clamp(s->dy, -128, 127);
-    dz = int_clamp(s->dz, -128, 127);
-
-    s->dx -= dx;
-    s->dy -= dy;
-    s->dz -= dz;
-
-    b = 0;
-    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
-        b |= 0x01;
-    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
-        b |= 0x02;
-    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
-        b |= 0x04;
-
-    buf[0] = b;
-    buf[1] = dx;
-    buf[2] = dy;
-    l = 3;
-    if (len >= 4) {
-        buf[3] = dz;
-        l = 4;
-    }
-    return l;
-}
-
-static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
-{
-    int b;
-
-    if (!s->mouse_grabbed) {
-        s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
-                        "QEMU PenPartner tablet");
-        qemu_activate_mouse_event_handler(s->eh_entry);
-        s->mouse_grabbed = 1;
-    }
-
-    b = 0;
-    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
-        b |= 0x01;
-    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
-        b |= 0x40;
-    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
-        b |= 0x20; /* eraser */
-
-    if (len < 7)
-        return 0;
-
-    buf[0] = s->mode;
-    buf[5] = 0x00 | (b & 0xf0);
-    buf[1] = s->x & 0xff;
-    buf[2] = s->x >> 8;
-    buf[3] = s->y & 0xff;
-    buf[4] = s->y >> 8;
-    if (b & 0x3f) {
-        buf[6] = 0;
-    } else {
-        buf[6] = (unsigned char) -127;
-    }
-
-    return 7;
-}
-
-static void usb_wacom_handle_reset(USBDevice *dev)
-{
-    USBWacomState *s = (USBWacomState *) dev;
-
-    s->dx = 0;
-    s->dy = 0;
-    s->dz = 0;
-    s->x = 0;
-    s->y = 0;
-    s->buttons_state = 0;
-    s->mode = WACOM_MODE_HID;
-}
-
-static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    USBWacomState *s = (USBWacomState *) dev;
-    int ret;
-
-    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
-    if (ret >= 0) {
-        return ret;
-    }
-
-    ret = 0;
-    switch (request) {
-    case WACOM_SET_REPORT:
-        if (s->mouse_grabbed) {
-            qemu_remove_mouse_event_handler(s->eh_entry);
-            s->mouse_grabbed = 0;
-        }
-        s->mode = data[0];
-        ret = 0;
-        break;
-    case WACOM_GET_REPORT:
-        data[0] = 0;
-        data[1] = s->mode;
-        ret = 2;
-        break;
-    /* USB HID requests */
-    case HID_GET_REPORT:
-        if (s->mode == WACOM_MODE_HID)
-            ret = usb_mouse_poll(s, data, length);
-        else if (s->mode == WACOM_MODE_WACOM)
-            ret = usb_wacom_poll(s, data, length);
-        break;
-    case HID_GET_IDLE:
-        ret = 1;
-        data[0] = s->idle;
-        break;
-    case HID_SET_IDLE:
-        s->idle = (uint8_t) (value >> 8);
-        ret = 0;
-        break;
-    default:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBWacomState *s = (USBWacomState *) dev;
-    uint8_t buf[p->iov.size];
-    int ret = 0;
-
-    switch (p->pid) {
-    case USB_TOKEN_IN:
-        if (p->ep->nr == 1) {
-            if (!(s->changed || s->idle))
-                return USB_RET_NAK;
-            s->changed = 0;
-            if (s->mode == WACOM_MODE_HID)
-                ret = usb_mouse_poll(s, buf, p->iov.size);
-            else if (s->mode == WACOM_MODE_WACOM)
-                ret = usb_wacom_poll(s, buf, p->iov.size);
-            usb_packet_copy(p, buf, ret);
-            break;
-        }
-        /* Fall through.  */
-    case USB_TOKEN_OUT:
-    default:
-        ret = USB_RET_STALL;
-        break;
-    }
-    return ret;
-}
-
-static void usb_wacom_handle_destroy(USBDevice *dev)
-{
-    USBWacomState *s = (USBWacomState *) dev;
-
-    if (s->mouse_grabbed) {
-        qemu_remove_mouse_event_handler(s->eh_entry);
-        s->mouse_grabbed = 0;
-    }
-}
-
-static int usb_wacom_initfn(USBDevice *dev)
-{
-    USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
-    usb_desc_init(dev);
-    s->changed = 1;
-    return 0;
-}
-
-static const VMStateDescription vmstate_usb_wacom = {
-    .name = "usb-wacom",
-    .unmigratable = 1,
-};
-
-static void usb_wacom_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->product_desc   = "QEMU PenPartner Tablet";
-    uc->usb_desc       = &desc_wacom;
-    uc->init           = usb_wacom_initfn;
-    uc->handle_reset   = usb_wacom_handle_reset;
-    uc->handle_control = usb_wacom_handle_control;
-    uc->handle_data    = usb_wacom_handle_data;
-    uc->handle_destroy = usb_wacom_handle_destroy;
-    dc->desc = "QEMU PenPartner Tablet";
-    dc->vmsd = &vmstate_usb_wacom;
-}
-
-static TypeInfo wacom_info = {
-    .name          = "usb-wacom-tablet",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBWacomState),
-    .class_init    = usb_wacom_class_init,
-};
-
-static void usb_wacom_register_types(void)
-{
-    type_register_static(&wacom_info);
-    usb_legacy_register("usb-wacom-tablet", "wacom-tablet", NULL);
-}
-
-type_init(usb_wacom_register_types)
diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c
deleted file mode 100644 (file)
index e8f1b6e..0000000
+++ /dev/null
@@ -1,2925 +0,0 @@
-/*
- * USB xHCI controller emulation
- *
- * Copyright (c) 2011 Securiforest
- * Date: 2011-05-11 ;  Author: Hector Martin <hector@marcansoft.com>
- * Based on usb-ohci.c, emulates Renesas NEC USB 3.0
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "hw.h"
-#include "qemu-timer.h"
-#include "usb.h"
-#include "pci.h"
-#include "qdev-addr.h"
-#include "msi.h"
-
-//#define DEBUG_XHCI
-//#define DEBUG_DATA
-
-#ifdef DEBUG_XHCI
-#define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
-#else
-#define DPRINTF(...) do {} while (0)
-#endif
-#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
-                             __func__, __LINE__); abort(); } while (0)
-
-#define MAXSLOTS 8
-#define MAXINTRS 1
-
-#define USB2_PORTS 4
-#define USB3_PORTS 4
-
-#define MAXPORTS (USB2_PORTS+USB3_PORTS)
-
-#define TD_QUEUE 24
-#define BG_XFERS 8
-#define BG_PKTS 8
-
-/* Very pessimistic, let's hope it's enough for all cases */
-#define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS)
-/* Do not deliver ER Full events. NEC's driver does some things not bound
- * to the specs when it gets them */
-#define ER_FULL_HACK
-
-#define LEN_CAP         0x40
-#define OFF_OPER        LEN_CAP
-#define LEN_OPER        (0x400 + 0x10 * MAXPORTS)
-#define OFF_RUNTIME     ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f)
-#define LEN_RUNTIME     (0x20 + MAXINTRS * 0x20)
-#define OFF_DOORBELL    (OFF_RUNTIME + LEN_RUNTIME)
-#define LEN_DOORBELL    ((MAXSLOTS + 1) * 0x20)
-
-/* must be power of 2 */
-#define LEN_REGS        0x2000
-
-#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS
-# error Increase LEN_REGS
-#endif
-
-#if MAXINTRS > 1
-# error TODO: only one interrupter supported
-#endif
-
-/* bit definitions */
-#define USBCMD_RS       (1<<0)
-#define USBCMD_HCRST    (1<<1)
-#define USBCMD_INTE     (1<<2)
-#define USBCMD_HSEE     (1<<3)
-#define USBCMD_LHCRST   (1<<7)
-#define USBCMD_CSS      (1<<8)
-#define USBCMD_CRS      (1<<9)
-#define USBCMD_EWE      (1<<10)
-#define USBCMD_EU3S     (1<<11)
-
-#define USBSTS_HCH      (1<<0)
-#define USBSTS_HSE      (1<<2)
-#define USBSTS_EINT     (1<<3)
-#define USBSTS_PCD      (1<<4)
-#define USBSTS_SSS      (1<<8)
-#define USBSTS_RSS      (1<<9)
-#define USBSTS_SRE      (1<<10)
-#define USBSTS_CNR      (1<<11)
-#define USBSTS_HCE      (1<<12)
-
-
-#define PORTSC_CCS          (1<<0)
-#define PORTSC_PED          (1<<1)
-#define PORTSC_OCA          (1<<3)
-#define PORTSC_PR           (1<<4)
-#define PORTSC_PLS_SHIFT        5
-#define PORTSC_PLS_MASK     0xf
-#define PORTSC_PP           (1<<9)
-#define PORTSC_SPEED_SHIFT      10
-#define PORTSC_SPEED_MASK   0xf
-#define PORTSC_SPEED_FULL   (1<<10)
-#define PORTSC_SPEED_LOW    (2<<10)
-#define PORTSC_SPEED_HIGH   (3<<10)
-#define PORTSC_SPEED_SUPER  (4<<10)
-#define PORTSC_PIC_SHIFT        14
-#define PORTSC_PIC_MASK     0x3
-#define PORTSC_LWS          (1<<16)
-#define PORTSC_CSC          (1<<17)
-#define PORTSC_PEC          (1<<18)
-#define PORTSC_WRC          (1<<19)
-#define PORTSC_OCC          (1<<20)
-#define PORTSC_PRC          (1<<21)
-#define PORTSC_PLC          (1<<22)
-#define PORTSC_CEC          (1<<23)
-#define PORTSC_CAS          (1<<24)
-#define PORTSC_WCE          (1<<25)
-#define PORTSC_WDE          (1<<26)
-#define PORTSC_WOE          (1<<27)
-#define PORTSC_DR           (1<<30)
-#define PORTSC_WPR          (1<<31)
-
-#define CRCR_RCS        (1<<0)
-#define CRCR_CS         (1<<1)
-#define CRCR_CA         (1<<2)
-#define CRCR_CRR        (1<<3)
-
-#define IMAN_IP         (1<<0)
-#define IMAN_IE         (1<<1)
-
-#define ERDP_EHB        (1<<3)
-
-#define TRB_SIZE 16
-typedef struct XHCITRB {
-    uint64_t parameter;
-    uint32_t status;
-    uint32_t control;
-    target_phys_addr_t addr;
-    bool ccs;
-} XHCITRB;
-
-
-typedef enum TRBType {
-    TRB_RESERVED = 0,
-    TR_NORMAL,
-    TR_SETUP,
-    TR_DATA,
-    TR_STATUS,
-    TR_ISOCH,
-    TR_LINK,
-    TR_EVDATA,
-    TR_NOOP,
-    CR_ENABLE_SLOT,
-    CR_DISABLE_SLOT,
-    CR_ADDRESS_DEVICE,
-    CR_CONFIGURE_ENDPOINT,
-    CR_EVALUATE_CONTEXT,
-    CR_RESET_ENDPOINT,
-    CR_STOP_ENDPOINT,
-    CR_SET_TR_DEQUEUE,
-    CR_RESET_DEVICE,
-    CR_FORCE_EVENT,
-    CR_NEGOTIATE_BW,
-    CR_SET_LATENCY_TOLERANCE,
-    CR_GET_PORT_BANDWIDTH,
-    CR_FORCE_HEADER,
-    CR_NOOP,
-    ER_TRANSFER = 32,
-    ER_COMMAND_COMPLETE,
-    ER_PORT_STATUS_CHANGE,
-    ER_BANDWIDTH_REQUEST,
-    ER_DOORBELL,
-    ER_HOST_CONTROLLER,
-    ER_DEVICE_NOTIFICATION,
-    ER_MFINDEX_WRAP,
-    /* vendor specific bits */
-    CR_VENDOR_VIA_CHALLENGE_RESPONSE = 48,
-    CR_VENDOR_NEC_FIRMWARE_REVISION  = 49,
-    CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50,
-} TRBType;
-
-#define CR_LINK TR_LINK
-
-typedef enum TRBCCode {
-    CC_INVALID = 0,
-    CC_SUCCESS,
-    CC_DATA_BUFFER_ERROR,
-    CC_BABBLE_DETECTED,
-    CC_USB_TRANSACTION_ERROR,
-    CC_TRB_ERROR,
-    CC_STALL_ERROR,
-    CC_RESOURCE_ERROR,
-    CC_BANDWIDTH_ERROR,
-    CC_NO_SLOTS_ERROR,
-    CC_INVALID_STREAM_TYPE_ERROR,
-    CC_SLOT_NOT_ENABLED_ERROR,
-    CC_EP_NOT_ENABLED_ERROR,
-    CC_SHORT_PACKET,
-    CC_RING_UNDERRUN,
-    CC_RING_OVERRUN,
-    CC_VF_ER_FULL,
-    CC_PARAMETER_ERROR,
-    CC_BANDWIDTH_OVERRUN,
-    CC_CONTEXT_STATE_ERROR,
-    CC_NO_PING_RESPONSE_ERROR,
-    CC_EVENT_RING_FULL_ERROR,
-    CC_INCOMPATIBLE_DEVICE_ERROR,
-    CC_MISSED_SERVICE_ERROR,
-    CC_COMMAND_RING_STOPPED,
-    CC_COMMAND_ABORTED,
-    CC_STOPPED,
-    CC_STOPPED_LENGTH_INVALID,
-    CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29,
-    CC_ISOCH_BUFFER_OVERRUN = 31,
-    CC_EVENT_LOST_ERROR,
-    CC_UNDEFINED_ERROR,
-    CC_INVALID_STREAM_ID_ERROR,
-    CC_SECONDARY_BANDWIDTH_ERROR,
-    CC_SPLIT_TRANSACTION_ERROR
-} TRBCCode;
-
-#define TRB_C               (1<<0)
-#define TRB_TYPE_SHIFT          10
-#define TRB_TYPE_MASK       0x3f
-#define TRB_TYPE(t)         (((t).control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
-
-#define TRB_EV_ED           (1<<2)
-
-#define TRB_TR_ENT          (1<<1)
-#define TRB_TR_ISP          (1<<2)
-#define TRB_TR_NS           (1<<3)
-#define TRB_TR_CH           (1<<4)
-#define TRB_TR_IOC          (1<<5)
-#define TRB_TR_IDT          (1<<6)
-#define TRB_TR_TBC_SHIFT        7
-#define TRB_TR_TBC_MASK     0x3
-#define TRB_TR_BEI          (1<<9)
-#define TRB_TR_TLBPC_SHIFT      16
-#define TRB_TR_TLBPC_MASK   0xf
-#define TRB_TR_FRAMEID_SHIFT    20
-#define TRB_TR_FRAMEID_MASK 0x7ff
-#define TRB_TR_SIA          (1<<31)
-
-#define TRB_TR_DIR          (1<<16)
-
-#define TRB_CR_SLOTID_SHIFT     24
-#define TRB_CR_SLOTID_MASK  0xff
-#define TRB_CR_EPID_SHIFT       16
-#define TRB_CR_EPID_MASK    0x1f
-
-#define TRB_CR_BSR          (1<<9)
-#define TRB_CR_DC           (1<<9)
-
-#define TRB_LK_TC           (1<<1)
-
-#define EP_TYPE_MASK        0x7
-#define EP_TYPE_SHIFT           3
-
-#define EP_STATE_MASK       0x7
-#define EP_DISABLED         (0<<0)
-#define EP_RUNNING          (1<<0)
-#define EP_HALTED           (2<<0)
-#define EP_STOPPED          (3<<0)
-#define EP_ERROR            (4<<0)
-
-#define SLOT_STATE_MASK     0x1f
-#define SLOT_STATE_SHIFT        27
-#define SLOT_STATE(s)       (((s)>>SLOT_STATE_SHIFT)&SLOT_STATE_MASK)
-#define SLOT_ENABLED        0
-#define SLOT_DEFAULT        1
-#define SLOT_ADDRESSED      2
-#define SLOT_CONFIGURED     3
-
-#define SLOT_CONTEXT_ENTRIES_MASK 0x1f
-#define SLOT_CONTEXT_ENTRIES_SHIFT 27
-
-typedef enum EPType {
-    ET_INVALID = 0,
-    ET_ISO_OUT,
-    ET_BULK_OUT,
-    ET_INTR_OUT,
-    ET_CONTROL,
-    ET_ISO_IN,
-    ET_BULK_IN,
-    ET_INTR_IN,
-} EPType;
-
-typedef struct XHCIRing {
-    target_phys_addr_t base;
-    target_phys_addr_t dequeue;
-    bool ccs;
-} XHCIRing;
-
-typedef struct XHCIPort {
-    USBPort port;
-    uint32_t portsc;
-} XHCIPort;
-
-struct XHCIState;
-typedef struct XHCIState XHCIState;
-
-typedef struct XHCITransfer {
-    XHCIState *xhci;
-    USBPacket packet;
-    bool running_async;
-    bool running_retry;
-    bool cancelled;
-    bool complete;
-    bool backgrounded;
-    unsigned int iso_pkts;
-    unsigned int slotid;
-    unsigned int epid;
-    bool in_xfer;
-    bool iso_xfer;
-    bool bg_xfer;
-
-    unsigned int trb_count;
-    unsigned int trb_alloced;
-    XHCITRB *trbs;
-
-    unsigned int data_length;
-    unsigned int data_alloced;
-    uint8_t *data;
-
-    TRBCCode status;
-
-    unsigned int pkts;
-    unsigned int pktsize;
-    unsigned int cur_pkt;
-} XHCITransfer;
-
-typedef struct XHCIEPContext {
-    XHCIRing ring;
-    unsigned int next_xfer;
-    unsigned int comp_xfer;
-    XHCITransfer transfers[TD_QUEUE];
-    XHCITransfer *retry;
-    bool bg_running;
-    bool bg_updating;
-    unsigned int next_bg;
-    XHCITransfer bg_transfers[BG_XFERS];
-    EPType type;
-    target_phys_addr_t pctx;
-    unsigned int max_psize;
-    bool has_bg;
-    uint32_t state;
-} XHCIEPContext;
-
-typedef struct XHCISlot {
-    bool enabled;
-    target_phys_addr_t ctx;
-    unsigned int port;
-    unsigned int devaddr;
-    XHCIEPContext * eps[31];
-} XHCISlot;
-
-typedef struct XHCIEvent {
-    TRBType type;
-    TRBCCode ccode;
-    uint64_t ptr;
-    uint32_t length;
-    uint32_t flags;
-    uint8_t slotid;
-    uint8_t epid;
-} XHCIEvent;
-
-struct XHCIState {
-    PCIDevice pci_dev;
-    USBBus bus;
-    qemu_irq irq;
-    MemoryRegion mem;
-    const char *name;
-    uint32_t msi;
-    unsigned int devaddr;
-
-    /* Operational Registers */
-    uint32_t usbcmd;
-    uint32_t usbsts;
-    uint32_t dnctrl;
-    uint32_t crcr_low;
-    uint32_t crcr_high;
-    uint32_t dcbaap_low;
-    uint32_t dcbaap_high;
-    uint32_t config;
-
-    XHCIPort ports[MAXPORTS];
-    XHCISlot slots[MAXSLOTS];
-
-    /* Runtime Registers */
-    uint32_t mfindex;
-    /* note: we only support one interrupter */
-    uint32_t iman;
-    uint32_t imod;
-    uint32_t erstsz;
-    uint32_t erstba_low;
-    uint32_t erstba_high;
-    uint32_t erdp_low;
-    uint32_t erdp_high;
-
-    target_phys_addr_t er_start;
-    uint32_t er_size;
-    bool er_pcs;
-    unsigned int er_ep_idx;
-    bool er_full;
-
-    XHCIEvent ev_buffer[EV_QUEUE];
-    unsigned int ev_buffer_put;
-    unsigned int ev_buffer_get;
-
-    XHCIRing cmd_ring;
-};
-
-typedef struct XHCIEvRingSeg {
-    uint32_t addr_low;
-    uint32_t addr_high;
-    uint32_t size;
-    uint32_t rsvd;
-} XHCIEvRingSeg;
-
-#ifdef DEBUG_XHCI
-static const char *TRBType_names[] = {
-    [TRB_RESERVED]                     = "TRB_RESERVED",
-    [TR_NORMAL]                        = "TR_NORMAL",
-    [TR_SETUP]                         = "TR_SETUP",
-    [TR_DATA]                          = "TR_DATA",
-    [TR_STATUS]                        = "TR_STATUS",
-    [TR_ISOCH]                         = "TR_ISOCH",
-    [TR_LINK]                          = "TR_LINK",
-    [TR_EVDATA]                        = "TR_EVDATA",
-    [TR_NOOP]                          = "TR_NOOP",
-    [CR_ENABLE_SLOT]                   = "CR_ENABLE_SLOT",
-    [CR_DISABLE_SLOT]                  = "CR_DISABLE_SLOT",
-    [CR_ADDRESS_DEVICE]                = "CR_ADDRESS_DEVICE",
-    [CR_CONFIGURE_ENDPOINT]            = "CR_CONFIGURE_ENDPOINT",
-    [CR_EVALUATE_CONTEXT]              = "CR_EVALUATE_CONTEXT",
-    [CR_RESET_ENDPOINT]                = "CR_RESET_ENDPOINT",
-    [CR_STOP_ENDPOINT]                 = "CR_STOP_ENDPOINT",
-    [CR_SET_TR_DEQUEUE]                = "CR_SET_TR_DEQUEUE",
-    [CR_RESET_DEVICE]                  = "CR_RESET_DEVICE",
-    [CR_FORCE_EVENT]                   = "CR_FORCE_EVENT",
-    [CR_NEGOTIATE_BW]                  = "CR_NEGOTIATE_BW",
-    [CR_SET_LATENCY_TOLERANCE]         = "CR_SET_LATENCY_TOLERANCE",
-    [CR_GET_PORT_BANDWIDTH]            = "CR_GET_PORT_BANDWIDTH",
-    [CR_FORCE_HEADER]                  = "CR_FORCE_HEADER",
-    [CR_NOOP]                          = "CR_NOOP",
-    [ER_TRANSFER]                      = "ER_TRANSFER",
-    [ER_COMMAND_COMPLETE]              = "ER_COMMAND_COMPLETE",
-    [ER_PORT_STATUS_CHANGE]            = "ER_PORT_STATUS_CHANGE",
-    [ER_BANDWIDTH_REQUEST]             = "ER_BANDWIDTH_REQUEST",
-    [ER_DOORBELL]                      = "ER_DOORBELL",
-    [ER_HOST_CONTROLLER]               = "ER_HOST_CONTROLLER",
-    [ER_DEVICE_NOTIFICATION]           = "ER_DEVICE_NOTIFICATION",
-    [ER_MFINDEX_WRAP]                  = "ER_MFINDEX_WRAP",
-    [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE",
-    [CR_VENDOR_NEC_FIRMWARE_REVISION]  = "CR_VENDOR_NEC_FIRMWARE_REVISION",
-    [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
-};
-
-static const char *lookup_name(uint32_t index, const char **list, uint32_t llen)
-{
-    if (index >= llen || list[index] == NULL) {
-        return "???";
-    }
-    return list[index];
-}
-
-static const char *trb_name(XHCITRB *trb)
-{
-    return lookup_name(TRB_TYPE(*trb), TRBType_names,
-                       ARRAY_SIZE(TRBType_names));
-}
-#endif
-
-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
-                         unsigned int epid);
-
-static inline target_phys_addr_t xhci_addr64(uint32_t low, uint32_t high)
-{
-#if TARGET_PHYS_ADDR_BITS > 32
-    return low | ((target_phys_addr_t)high << 32);
-#else
-    return low;
-#endif
-}
-
-static inline target_phys_addr_t xhci_mask64(uint64_t addr)
-{
-#if TARGET_PHYS_ADDR_BITS > 32
-    return addr;
-#else
-    return addr & 0xffffffff;
-#endif
-}
-
-static void xhci_irq_update(XHCIState *xhci)
-{
-    int level = 0;
-
-    if (xhci->iman & IMAN_IP && xhci->iman & IMAN_IE &&
-        xhci->usbcmd && USBCMD_INTE) {
-        level = 1;
-    }
-
-    DPRINTF("xhci_irq_update(): %d\n", level);
-
-    if (xhci->msi && msi_enabled(&xhci->pci_dev)) {
-        if (level) {
-            DPRINTF("xhci_irq_update(): MSI signal\n");
-            msi_notify(&xhci->pci_dev, 0);
-        }
-    } else {
-        qemu_set_irq(xhci->irq, level);
-    }
-}
-
-static inline int xhci_running(XHCIState *xhci)
-{
-    return !(xhci->usbsts & USBSTS_HCH) && !xhci->er_full;
-}
-
-static void xhci_die(XHCIState *xhci)
-{
-    xhci->usbsts |= USBSTS_HCE;
-    fprintf(stderr, "xhci: asserted controller error\n");
-}
-
-static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
-{
-    XHCITRB ev_trb;
-    target_phys_addr_t addr;
-
-    ev_trb.parameter = cpu_to_le64(event->ptr);
-    ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24));
-    ev_trb.control = (event->slotid << 24) | (event->epid << 16) |
-                     event->flags | (event->type << TRB_TYPE_SHIFT);
-    if (xhci->er_pcs) {
-        ev_trb.control |= TRB_C;
-    }
-    ev_trb.control = cpu_to_le32(ev_trb.control);
-
-    DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n",
-            xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control,
-            trb_name(&ev_trb));
-
-    addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
-    cpu_physical_memory_write(addr, (uint8_t *) &ev_trb, TRB_SIZE);
-
-    xhci->er_ep_idx++;
-    if (xhci->er_ep_idx >= xhci->er_size) {
-        xhci->er_ep_idx = 0;
-        xhci->er_pcs = !xhci->er_pcs;
-    }
-}
-
-static void xhci_events_update(XHCIState *xhci)
-{
-    target_phys_addr_t erdp;
-    unsigned int dp_idx;
-    bool do_irq = 0;
-
-    if (xhci->usbsts & USBSTS_HCH) {
-        return;
-    }
-
-    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
-    if (erdp < xhci->er_start ||
-        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
-        fprintf(stderr, "xhci: ERDP out of bounds: "TARGET_FMT_plx"\n", erdp);
-        fprintf(stderr, "xhci: ER at "TARGET_FMT_plx" len %d\n",
-                xhci->er_start, xhci->er_size);
-        xhci_die(xhci);
-        return;
-    }
-    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
-    assert(dp_idx < xhci->er_size);
-
-    /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus
-     * deadlocks when the ER is full. Hack it by holding off events until
-     * the driver decides to free at least half of the ring */
-    if (xhci->er_full) {
-        int er_free = dp_idx - xhci->er_ep_idx;
-        if (er_free <= 0) {
-            er_free += xhci->er_size;
-        }
-        if (er_free < (xhci->er_size/2)) {
-            DPRINTF("xhci_events_update(): event ring still "
-                    "more than half full (hack)\n");
-            return;
-        }
-    }
-
-    while (xhci->ev_buffer_put != xhci->ev_buffer_get) {
-        assert(xhci->er_full);
-        if (((xhci->er_ep_idx+1) % xhci->er_size) == dp_idx) {
-            DPRINTF("xhci_events_update(): event ring full again\n");
-#ifndef ER_FULL_HACK
-            XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
-            xhci_write_event(xhci, &full);
-#endif
-            do_irq = 1;
-            break;
-        }
-        XHCIEvent *event = &xhci->ev_buffer[xhci->ev_buffer_get];
-        xhci_write_event(xhci, event);
-        xhci->ev_buffer_get++;
-        do_irq = 1;
-        if (xhci->ev_buffer_get == EV_QUEUE) {
-            xhci->ev_buffer_get = 0;
-        }
-    }
-
-    if (do_irq) {
-        xhci->erdp_low |= ERDP_EHB;
-        xhci->iman |= IMAN_IP;
-        xhci->usbsts |= USBSTS_EINT;
-        xhci_irq_update(xhci);
-    }
-
-    if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) {
-        DPRINTF("xhci_events_update(): event ring no longer full\n");
-        xhci->er_full = 0;
-    }
-    return;
-}
-
-static void xhci_event(XHCIState *xhci, XHCIEvent *event)
-{
-    target_phys_addr_t erdp;
-    unsigned int dp_idx;
-
-    if (xhci->er_full) {
-        DPRINTF("xhci_event(): ER full, queueing\n");
-        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
-            fprintf(stderr, "xhci: event queue full, dropping event!\n");
-            return;
-        }
-        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
-        if (xhci->ev_buffer_put == EV_QUEUE) {
-            xhci->ev_buffer_put = 0;
-        }
-        return;
-    }
-
-    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
-    if (erdp < xhci->er_start ||
-        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
-        fprintf(stderr, "xhci: ERDP out of bounds: "TARGET_FMT_plx"\n", erdp);
-        fprintf(stderr, "xhci: ER at "TARGET_FMT_plx" len %d\n",
-                xhci->er_start, xhci->er_size);
-        xhci_die(xhci);
-        return;
-    }
-
-    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
-    assert(dp_idx < xhci->er_size);
-
-    if ((xhci->er_ep_idx+1) % xhci->er_size == dp_idx) {
-        DPRINTF("xhci_event(): ER full, queueing\n");
-#ifndef ER_FULL_HACK
-        XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
-        xhci_write_event(xhci, &full);
-#endif
-        xhci->er_full = 1;
-        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
-            fprintf(stderr, "xhci: event queue full, dropping event!\n");
-            return;
-        }
-        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
-        if (xhci->ev_buffer_put == EV_QUEUE) {
-            xhci->ev_buffer_put = 0;
-        }
-    } else {
-        xhci_write_event(xhci, event);
-    }
-
-    xhci->erdp_low |= ERDP_EHB;
-    xhci->iman |= IMAN_IP;
-    xhci->usbsts |= USBSTS_EINT;
-
-    xhci_irq_update(xhci);
-}
-
-static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
-                           target_phys_addr_t base)
-{
-    ring->base = base;
-    ring->dequeue = base;
-    ring->ccs = 1;
-}
-
-static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
-                               target_phys_addr_t *addr)
-{
-    while (1) {
-        TRBType type;
-        cpu_physical_memory_read(ring->dequeue, (uint8_t *) trb, TRB_SIZE);
-        trb->addr = ring->dequeue;
-        trb->ccs = ring->ccs;
-        le64_to_cpus(&trb->parameter);
-        le32_to_cpus(&trb->status);
-        le32_to_cpus(&trb->control);
-
-        DPRINTF("xhci: TRB fetched [" TARGET_FMT_plx "]: "
-                "%016" PRIx64 " %08x %08x %s\n",
-                ring->dequeue, trb->parameter, trb->status, trb->control,
-                trb_name(trb));
-
-        if ((trb->control & TRB_C) != ring->ccs) {
-            return 0;
-        }
-
-        type = TRB_TYPE(*trb);
-
-        if (type != TR_LINK) {
-            if (addr) {
-                *addr = ring->dequeue;
-            }
-            ring->dequeue += TRB_SIZE;
-            return type;
-        } else {
-            ring->dequeue = xhci_mask64(trb->parameter);
-            if (trb->control & TRB_LK_TC) {
-                ring->ccs = !ring->ccs;
-            }
-        }
-    }
-}
-
-static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
-{
-    XHCITRB trb;
-    int length = 0;
-    target_phys_addr_t dequeue = ring->dequeue;
-    bool ccs = ring->ccs;
-    /* hack to bundle together the two/three TDs that make a setup transfer */
-    bool control_td_set = 0;
-
-    while (1) {
-        TRBType type;
-        cpu_physical_memory_read(dequeue, (uint8_t *) &trb, TRB_SIZE);
-        le64_to_cpus(&trb.parameter);
-        le32_to_cpus(&trb.status);
-        le32_to_cpus(&trb.control);
-
-        DPRINTF("xhci: TRB peeked [" TARGET_FMT_plx "]: "
-                "%016" PRIx64 " %08x %08x\n",
-                dequeue, trb.parameter, trb.status, trb.control);
-
-        if ((trb.control & TRB_C) != ccs) {
-            return -length;
-        }
-
-        type = TRB_TYPE(trb);
-
-        if (type == TR_LINK) {
-            dequeue = xhci_mask64(trb.parameter);
-            if (trb.control & TRB_LK_TC) {
-                ccs = !ccs;
-            }
-            continue;
-        }
-
-        length += 1;
-        dequeue += TRB_SIZE;
-
-        if (type == TR_SETUP) {
-            control_td_set = 1;
-        } else if (type == TR_STATUS) {
-            control_td_set = 0;
-        }
-
-        if (!control_td_set && !(trb.control & TRB_TR_CH)) {
-            return length;
-        }
-    }
-}
-
-static void xhci_er_reset(XHCIState *xhci)
-{
-    XHCIEvRingSeg seg;
-
-    /* cache the (sole) event ring segment location */
-    if (xhci->erstsz != 1) {
-        fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", xhci->erstsz);
-        xhci_die(xhci);
-        return;
-    }
-    target_phys_addr_t erstba = xhci_addr64(xhci->erstba_low, xhci->erstba_high);
-    cpu_physical_memory_read(erstba, (uint8_t *) &seg, sizeof(seg));
-    le32_to_cpus(&seg.addr_low);
-    le32_to_cpus(&seg.addr_high);
-    le32_to_cpus(&seg.size);
-    if (seg.size < 16 || seg.size > 4096) {
-        fprintf(stderr, "xhci: invalid value for segment size: %d\n", seg.size);
-        xhci_die(xhci);
-        return;
-    }
-    xhci->er_start = xhci_addr64(seg.addr_low, seg.addr_high);
-    xhci->er_size = seg.size;
-
-    xhci->er_ep_idx = 0;
-    xhci->er_pcs = 1;
-    xhci->er_full = 0;
-
-    DPRINTF("xhci: event ring:" TARGET_FMT_plx " [%d]\n",
-            xhci->er_start, xhci->er_size);
-}
-
-static void xhci_run(XHCIState *xhci)
-{
-    DPRINTF("xhci_run()\n");
-
-    xhci->usbsts &= ~USBSTS_HCH;
-}
-
-static void xhci_stop(XHCIState *xhci)
-{
-    DPRINTF("xhci_stop()\n");
-    xhci->usbsts |= USBSTS_HCH;
-    xhci->crcr_low &= ~CRCR_CRR;
-}
-
-static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
-                              uint32_t state)
-{
-    uint32_t ctx[5];
-    if (epctx->state == state) {
-        return;
-    }
-
-    cpu_physical_memory_read(epctx->pctx, (uint8_t *) ctx, sizeof(ctx));
-    ctx[0] &= ~EP_STATE_MASK;
-    ctx[0] |= state;
-    ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
-    ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
-    DPRINTF("xhci: set epctx: " TARGET_FMT_plx " state=%d dequeue=%08x%08x\n",
-            epctx->pctx, state, ctx[3], ctx[2]);
-    cpu_physical_memory_write(epctx->pctx, (uint8_t *) ctx, sizeof(ctx));
-    epctx->state = state;
-}
-
-static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
-                               unsigned int epid, target_phys_addr_t pctx,
-                               uint32_t *ctx)
-{
-    XHCISlot *slot;
-    XHCIEPContext *epctx;
-    target_phys_addr_t dequeue;
-    int i;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    assert(epid >= 1 && epid <= 31);
-
-    DPRINTF("xhci_enable_ep(%d, %d)\n", slotid, epid);
-
-    slot = &xhci->slots[slotid-1];
-    if (slot->eps[epid-1]) {
-        fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid);
-        return CC_TRB_ERROR;
-    }
-
-    epctx = g_malloc(sizeof(XHCIEPContext));
-    memset(epctx, 0, sizeof(XHCIEPContext));
-
-    slot->eps[epid-1] = epctx;
-
-    dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
-    xhci_ring_init(xhci, &epctx->ring, dequeue);
-    epctx->ring.ccs = ctx[2] & 1;
-
-    epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK;
-    DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type);
-    epctx->pctx = pctx;
-    epctx->max_psize = ctx[1]>>16;
-    epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
-    epctx->has_bg = false;
-    if (epctx->type == ET_ISO_IN) {
-        epctx->has_bg = true;
-    }
-    DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
-            epid/2, epid%2, epctx->max_psize);
-    for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
-        usb_packet_init(&epctx->transfers[i].packet);
-    }
-
-    epctx->state = EP_RUNNING;
-    ctx[0] &= ~EP_STATE_MASK;
-    ctx[0] |= EP_RUNNING;
-
-    return CC_SUCCESS;
-}
-
-static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
-                               unsigned int epid)
-{
-    XHCISlot *slot;
-    XHCIEPContext *epctx;
-    int i, xferi, killed = 0;
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    assert(epid >= 1 && epid <= 31);
-
-    DPRINTF("xhci_ep_nuke_xfers(%d, %d)\n", slotid, epid);
-
-    slot = &xhci->slots[slotid-1];
-
-    if (!slot->eps[epid-1]) {
-        return 0;
-    }
-
-    epctx = slot->eps[epid-1];
-
-    xferi = epctx->next_xfer;
-    for (i = 0; i < TD_QUEUE; i++) {
-        XHCITransfer *t = &epctx->transfers[xferi];
-        if (t->running_async) {
-            usb_cancel_packet(&t->packet);
-            t->running_async = 0;
-            t->cancelled = 1;
-            DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i);
-            killed++;
-        }
-        if (t->running_retry) {
-            t->running_retry = 0;
-            epctx->retry = NULL;
-        }
-        if (t->backgrounded) {
-            t->backgrounded = 0;
-        }
-        if (t->trbs) {
-            g_free(t->trbs);
-        }
-        if (t->data) {
-            g_free(t->data);
-        }
-
-        t->trbs = NULL;
-        t->data = NULL;
-        t->trb_count = t->trb_alloced = 0;
-        t->data_length = t->data_alloced = 0;
-        xferi = (xferi + 1) % TD_QUEUE;
-    }
-    if (epctx->has_bg) {
-        xferi = epctx->next_bg;
-        for (i = 0; i < BG_XFERS; i++) {
-            XHCITransfer *t = &epctx->bg_transfers[xferi];
-            if (t->running_async) {
-                usb_cancel_packet(&t->packet);
-                t->running_async = 0;
-                t->cancelled = 1;
-                DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i);
-                killed++;
-            }
-            if (t->data) {
-                g_free(t->data);
-            }
-
-            t->data = NULL;
-            xferi = (xferi + 1) % BG_XFERS;
-        }
-    }
-    return killed;
-}
-
-static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
-                               unsigned int epid)
-{
-    XHCISlot *slot;
-    XHCIEPContext *epctx;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    assert(epid >= 1 && epid <= 31);
-
-    DPRINTF("xhci_disable_ep(%d, %d)\n", slotid, epid);
-
-    slot = &xhci->slots[slotid-1];
-
-    if (!slot->eps[epid-1]) {
-        DPRINTF("xhci: slot %d ep %d already disabled\n", slotid, epid);
-        return CC_SUCCESS;
-    }
-
-    xhci_ep_nuke_xfers(xhci, slotid, epid);
-
-    epctx = slot->eps[epid-1];
-
-    xhci_set_ep_state(xhci, epctx, EP_DISABLED);
-
-    g_free(epctx);
-    slot->eps[epid-1] = NULL;
-
-    return CC_SUCCESS;
-}
-
-static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
-                             unsigned int epid)
-{
-    XHCISlot *slot;
-    XHCIEPContext *epctx;
-
-    DPRINTF("xhci_stop_ep(%d, %d)\n", slotid, epid);
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-
-    if (epid < 1 || epid > 31) {
-        fprintf(stderr, "xhci: bad ep %d\n", epid);
-        return CC_TRB_ERROR;
-    }
-
-    slot = &xhci->slots[slotid-1];
-
-    if (!slot->eps[epid-1]) {
-        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
-        return CC_EP_NOT_ENABLED_ERROR;
-    }
-
-    if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) {
-        fprintf(stderr, "xhci: FIXME: endpoint stopped w/ xfers running, "
-                "data might be lost\n");
-    }
-
-    epctx = slot->eps[epid-1];
-
-    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
-
-    return CC_SUCCESS;
-}
-
-static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
-                              unsigned int epid)
-{
-    XHCISlot *slot;
-    XHCIEPContext *epctx;
-    USBDevice *dev;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-
-    DPRINTF("xhci_reset_ep(%d, %d)\n", slotid, epid);
-
-    if (epid < 1 || epid > 31) {
-        fprintf(stderr, "xhci: bad ep %d\n", epid);
-        return CC_TRB_ERROR;
-    }
-
-    slot = &xhci->slots[slotid-1];
-
-    if (!slot->eps[epid-1]) {
-        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
-        return CC_EP_NOT_ENABLED_ERROR;
-    }
-
-    epctx = slot->eps[epid-1];
-
-    if (epctx->state != EP_HALTED) {
-        fprintf(stderr, "xhci: reset EP while EP %d not halted (%d)\n",
-                epid, epctx->state);
-        return CC_CONTEXT_STATE_ERROR;
-    }
-
-    if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) {
-        fprintf(stderr, "xhci: FIXME: endpoint reset w/ xfers running, "
-                "data might be lost\n");
-    }
-
-    uint8_t ep = epid>>1;
-
-    if (epid & 1) {
-        ep |= 0x80;
-    }
-
-    dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev;
-    if (!dev) {
-        return CC_USB_TRANSACTION_ERROR;
-    }
-
-    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
-
-    return CC_SUCCESS;
-}
-
-static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
-                                    unsigned int epid, uint64_t pdequeue)
-{
-    XHCISlot *slot;
-    XHCIEPContext *epctx;
-    target_phys_addr_t dequeue;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-
-    if (epid < 1 || epid > 31) {
-        fprintf(stderr, "xhci: bad ep %d\n", epid);
-        return CC_TRB_ERROR;
-    }
-
-    DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue);
-    dequeue = xhci_mask64(pdequeue);
-
-    slot = &xhci->slots[slotid-1];
-
-    if (!slot->eps[epid-1]) {
-        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
-        return CC_EP_NOT_ENABLED_ERROR;
-    }
-
-    epctx = slot->eps[epid-1];
-
-
-    if (epctx->state != EP_STOPPED) {
-        fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
-        return CC_CONTEXT_STATE_ERROR;
-    }
-
-    xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
-    epctx->ring.ccs = dequeue & 1;
-
-    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
-
-    return CC_SUCCESS;
-}
-
-static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
-                          unsigned int length, bool in_xfer, bool out_xfer,
-                          bool report)
-{
-    int i;
-    uint32_t edtla = 0;
-    unsigned int transferred = 0;
-    unsigned int left = length;
-    bool reported = 0;
-    bool shortpkt = 0;
-    XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
-    XHCIState *xhci = xfer->xhci;
-
-    DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n",
-            length, in_xfer, out_xfer, report);
-
-    assert(!(in_xfer && out_xfer));
-
-    for (i = 0; i < xfer->trb_count; i++) {
-        XHCITRB *trb = &xfer->trbs[i];
-        target_phys_addr_t addr;
-        unsigned int chunk = 0;
-
-        switch (TRB_TYPE(*trb)) {
-        case TR_DATA:
-            if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
-                fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n");
-                xhci_die(xhci);
-                return transferred;
-            }
-            /* fallthrough */
-        case TR_NORMAL:
-        case TR_ISOCH:
-            addr = xhci_mask64(trb->parameter);
-            chunk = trb->status & 0x1ffff;
-            if (chunk > left) {
-                chunk = left;
-                shortpkt = 1;
-            }
-            if (in_xfer || out_xfer) {
-                if (trb->control & TRB_TR_IDT) {
-                    uint64_t idata;
-                    if (chunk > 8 || in_xfer) {
-                        fprintf(stderr, "xhci: invalid immediate data TRB\n");
-                        xhci_die(xhci);
-                        return transferred;
-                    }
-                    idata = le64_to_cpu(trb->parameter);
-                    memcpy(data, &idata, chunk);
-                } else {
-                    DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at "
-                            TARGET_FMT_plx "\n", in_xfer, chunk, addr);
-                    if (in_xfer) {
-                        cpu_physical_memory_write(addr, data, chunk);
-                    } else {
-                        cpu_physical_memory_read(addr, data, chunk);
-                    }
-#ifdef DEBUG_DATA
-                    unsigned int count = chunk;
-                    int i;
-                    if (count > 16) {
-                        count = 16;
-                    }
-                    DPRINTF(" ::");
-                    for (i = 0; i < count; i++) {
-                        DPRINTF(" %02x", data[i]);
-                    }
-                    DPRINTF("\n");
-#endif
-                }
-            }
-            left -= chunk;
-            data += chunk;
-            edtla += chunk;
-            transferred += chunk;
-            break;
-        case TR_STATUS:
-            reported = 0;
-            shortpkt = 0;
-            break;
-        }
-
-        if (report && !reported && (trb->control & TRB_TR_IOC ||
-            (shortpkt && (trb->control & TRB_TR_ISP)))) {
-            event.slotid = xfer->slotid;
-            event.epid = xfer->epid;
-            event.length = (trb->status & 0x1ffff) - chunk;
-            event.flags = 0;
-            event.ptr = trb->addr;
-            if (xfer->status == CC_SUCCESS) {
-                event.ccode = shortpkt ? CC_SHORT_PACKET : CC_SUCCESS;
-            } else {
-                event.ccode = xfer->status;
-            }
-            if (TRB_TYPE(*trb) == TR_EVDATA) {
-                event.ptr = trb->parameter;
-                event.flags |= TRB_EV_ED;
-                event.length = edtla & 0xffffff;
-                DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length);
-                edtla = 0;
-            }
-            xhci_event(xhci, &event);
-            reported = 1;
-        }
-    }
-    return transferred;
-}
-
-static void xhci_stall_ep(XHCITransfer *xfer)
-{
-    XHCIState *xhci = xfer->xhci;
-    XHCISlot *slot = &xhci->slots[xfer->slotid-1];
-    XHCIEPContext *epctx = slot->eps[xfer->epid-1];
-
-    epctx->ring.dequeue = xfer->trbs[0].addr;
-    epctx->ring.ccs = xfer->trbs[0].ccs;
-    xhci_set_ep_state(xhci, epctx, EP_HALTED);
-    DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid);
-    DPRINTF("xhci: will continue at "TARGET_FMT_plx"\n", epctx->ring.dequeue);
-}
-
-static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
-                       XHCIEPContext *epctx);
-
-static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx)
-{
-    if (epctx->bg_updating) {
-        return;
-    }
-    DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx);
-    assert(epctx->has_bg);
-    DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg);
-    epctx->bg_updating = 1;
-    while (epctx->transfers[epctx->comp_xfer].backgrounded &&
-           epctx->bg_transfers[epctx->next_bg].complete) {
-        XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer];
-        XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg];
-#if 0
-        DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n",
-                epctx->comp_xfer, epctx->next_bg, bg->cur_pkt,
-                bg->usbxfer->iso_packet_desc[bg->cur_pkt].status
-               );
-#endif
-        assert(epctx->type == ET_ISO_IN);
-        assert(bg->iso_xfer);
-        assert(bg->in_xfer);
-        uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize;
-#if 0
-        int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length;
-        fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status);
-#else
-        int len = 0;
-        FIXME();
-#endif
-        fg->complete = 1;
-        fg->backgrounded = 0;
-
-        if (fg->status == CC_STALL_ERROR) {
-            xhci_stall_ep(fg);
-        }
-
-        xhci_xfer_data(fg, p, len, 1, 0, 1);
-
-        epctx->comp_xfer++;
-        if (epctx->comp_xfer == TD_QUEUE) {
-            epctx->comp_xfer = 0;
-        }
-        DPRINTF("next fg xfer: %d\n", epctx->comp_xfer);
-        bg->cur_pkt++;
-        if (bg->cur_pkt == bg->pkts) {
-            bg->complete = 0;
-            if (xhci_submit(xhci, bg, epctx) < 0) {
-                fprintf(stderr, "xhci: bg resubmit failed\n");
-            }
-            epctx->next_bg++;
-            if (epctx->next_bg == BG_XFERS) {
-                epctx->next_bg = 0;
-            }
-            DPRINTF("next bg xfer: %d\n", epctx->next_bg);
-
-        xhci_kick_ep(xhci, fg->slotid, fg->epid);
-        }
-    }
-    epctx->bg_updating = 0;
-}
-
-#if 0
-static void xhci_xfer_cb(struct libusb_transfer *transfer)
-{
-    XHCIState *xhci;
-    XHCITransfer *xfer;
-
-    xfer = (XHCITransfer *)transfer->user_data;
-    xhci = xfer->xhci;
-
-    DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid,
-            xfer->epid, transfer->status);
-
-    assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS);
-    assert(xfer->epid >= 1 && xfer->epid <= 31);
-
-    if (xfer->cancelled) {
-        DPRINTF("xhci: transfer cancelled, not reporting anything\n");
-        xfer->running = 0;
-        return;
-    }
-
-    XHCIEPContext *epctx;
-    XHCISlot *slot;
-    slot = &xhci->slots[xfer->slotid-1];
-    assert(slot->eps[xfer->epid-1]);
-    epctx = slot->eps[xfer->epid-1];
-
-    if (xfer->bg_xfer) {
-        DPRINTF("xhci: background transfer, updating\n");
-        xfer->complete = 1;
-        xfer->running = 0;
-        xhci_bg_update(xhci, epctx);
-        return;
-    }
-
-    if (xfer->iso_xfer) {
-        transfer->status = transfer->iso_packet_desc[0].status;
-        transfer->actual_length = transfer->iso_packet_desc[0].actual_length;
-    }
-
-    xfer->status = libusb_to_ccode(transfer->status);
-
-    xfer->complete = 1;
-    xfer->running = 0;
-
-    if (transfer->status == LIBUSB_TRANSFER_STALL)
-        xhci_stall_ep(xhci, epctx, xfer);
-
-    DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length);
-
-    if (xfer->in_xfer) {
-        if (xfer->epid == 1) {
-            xhci_xfer_data(xhci, xfer, xfer->data + 8,
-                           transfer->actual_length, 1, 0, 1);
-        } else {
-            xhci_xfer_data(xhci, xfer, xfer->data,
-                           transfer->actual_length, 1, 0, 1);
-        }
-    } else {
-        xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1);
-    }
-
-    xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
-}
-
-static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer,
-                            uint8_t bmRequestType, uint8_t bRequest,
-                            uint16_t wValue, uint16_t wIndex, uint16_t wLength)
-{
-    uint16_t type_req = (bmRequestType << 8) | bRequest;
-
-    switch (type_req) {
-        case 0x0000 | USB_REQ_SET_CONFIGURATION:
-            DPRINTF("xhci: HLE switch configuration\n");
-            return xhci_switch_config(xhci, xfer->slotid, wValue) == 0;
-        case 0x0100 | USB_REQ_SET_INTERFACE:
-            DPRINTF("xhci: HLE set interface altsetting\n");
-            return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0;
-        case 0x0200 | USB_REQ_CLEAR_FEATURE:
-            if (wValue == 0) { // endpoint halt
-                DPRINTF("xhci: HLE clear halt\n");
-                return xhci_clear_halt(xhci, xfer->slotid, wIndex);
-            }
-        case 0x0000 | USB_REQ_SET_ADDRESS:
-            fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n");
-            return 0;
-        default:
-            return 0;
-    }
-}
-#endif
-
-static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
-{
-    USBEndpoint *ep;
-    int dir;
-
-    dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
-    ep = usb_ep_get(dev, dir, xfer->epid >> 1);
-    usb_packet_setup(&xfer->packet, dir, ep);
-    usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
-    DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
-            xfer->packet.pid, dev->addr, ep->nr);
-    return 0;
-}
-
-static int xhci_complete_packet(XHCITransfer *xfer, int ret)
-{
-    if (ret == USB_RET_ASYNC) {
-        xfer->running_async = 1;
-        xfer->running_retry = 0;
-        xfer->complete = 0;
-        xfer->cancelled = 0;
-        return 0;
-    } else if (ret == USB_RET_NAK) {
-        xfer->running_async = 0;
-        xfer->running_retry = 1;
-        xfer->complete = 0;
-        xfer->cancelled = 0;
-        return 0;
-    } else {
-        xfer->running_async = 0;
-        xfer->running_retry = 0;
-        xfer->complete = 1;
-    }
-
-    if (ret >= 0) {
-        xfer->status = CC_SUCCESS;
-        xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
-        return 0;
-    }
-
-    /* error */
-    switch (ret) {
-    case USB_RET_NODEV:
-        xfer->status = CC_USB_TRANSACTION_ERROR;
-        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
-        xhci_stall_ep(xfer);
-        break;
-    case USB_RET_STALL:
-        xfer->status = CC_STALL_ERROR;
-        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
-        xhci_stall_ep(xfer);
-        break;
-    default:
-        fprintf(stderr, "%s: FIXME: ret = %d\n", __FUNCTION__, ret);
-        FIXME();
-    }
-    return 0;
-}
-
-static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
-{
-    if (!(port->portsc & PORTSC_PED)) {
-        return NULL;
-    }
-    return usb_find_device(&port->port, addr);
-}
-
-static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
-{
-    XHCITRB *trb_setup, *trb_status;
-    uint8_t bmRequestType;
-    uint16_t wLength;
-    XHCIPort *port;
-    USBDevice *dev;
-    int ret;
-
-    DPRINTF("xhci_fire_ctl_transfer(slot=%d)\n", xfer->slotid);
-
-    trb_setup = &xfer->trbs[0];
-    trb_status = &xfer->trbs[xfer->trb_count-1];
-
-    /* at most one Event Data TRB allowed after STATUS */
-    if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
-        trb_status--;
-    }
-
-    /* do some sanity checks */
-    if (TRB_TYPE(*trb_setup) != TR_SETUP) {
-        fprintf(stderr, "xhci: ep0 first TD not SETUP: %d\n",
-                TRB_TYPE(*trb_setup));
-        return -1;
-    }
-    if (TRB_TYPE(*trb_status) != TR_STATUS) {
-        fprintf(stderr, "xhci: ep0 last TD not STATUS: %d\n",
-                TRB_TYPE(*trb_status));
-        return -1;
-    }
-    if (!(trb_setup->control & TRB_TR_IDT)) {
-        fprintf(stderr, "xhci: Setup TRB doesn't have IDT set\n");
-        return -1;
-    }
-    if ((trb_setup->status & 0x1ffff) != 8) {
-        fprintf(stderr, "xhci: Setup TRB has bad length (%d)\n",
-                (trb_setup->status & 0x1ffff));
-        return -1;
-    }
-
-    bmRequestType = trb_setup->parameter;
-    wLength = trb_setup->parameter >> 48;
-
-    if (xfer->data && xfer->data_alloced < wLength) {
-        xfer->data_alloced = 0;
-        g_free(xfer->data);
-        xfer->data = NULL;
-    }
-    if (!xfer->data) {
-        DPRINTF("xhci: alloc %d bytes data\n", wLength);
-        xfer->data = g_malloc(wLength+1);
-        xfer->data_alloced = wLength;
-    }
-    xfer->data_length = wLength;
-
-    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
-    dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
-    if (!dev) {
-        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
-                xhci->slots[xfer->slotid-1].port);
-        return -1;
-    }
-
-    xfer->in_xfer = bmRequestType & USB_DIR_IN;
-    xfer->iso_xfer = false;
-
-    xhci_setup_packet(xfer, dev);
-    xfer->packet.parameter = trb_setup->parameter;
-    if (!xfer->in_xfer) {
-        xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
-    }
-
-    ret = usb_handle_packet(dev, &xfer->packet);
-
-    xhci_complete_packet(xfer, ret);
-    if (!xfer->running_async && !xfer->running_retry) {
-        xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
-    }
-    return 0;
-}
-
-static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
-{
-    XHCIPort *port;
-    USBDevice *dev;
-    int ret;
-
-    DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
-
-    xfer->in_xfer = epctx->type>>2;
-
-    if (xfer->data && xfer->data_alloced < xfer->data_length) {
-        xfer->data_alloced = 0;
-        g_free(xfer->data);
-        xfer->data = NULL;
-    }
-    if (!xfer->data && xfer->data_length) {
-        DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length);
-        xfer->data = g_malloc(xfer->data_length);
-        xfer->data_alloced = xfer->data_length;
-    }
-    if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
-        if (!xfer->bg_xfer) {
-            xfer->pkts = 1;
-        }
-    } else {
-        xfer->pkts = 0;
-    }
-
-    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
-    dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
-    if (!dev) {
-        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
-                xhci->slots[xfer->slotid-1].port);
-        return -1;
-    }
-
-    xhci_setup_packet(xfer, dev);
-
-    switch(epctx->type) {
-    case ET_INTR_OUT:
-    case ET_INTR_IN:
-    case ET_BULK_OUT:
-    case ET_BULK_IN:
-        break;
-    case ET_ISO_OUT:
-    case ET_ISO_IN:
-        FIXME();
-        break;
-    default:
-        fprintf(stderr, "xhci: unknown or unhandled EP "
-                "(type %d, in %d, ep %02x)\n",
-                epctx->type, xfer->in_xfer, xfer->epid);
-        return -1;
-    }
-
-    if (!xfer->in_xfer) {
-        xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0);
-    }
-    ret = usb_handle_packet(dev, &xfer->packet);
-
-    xhci_complete_packet(xfer, ret);
-    if (!xfer->running_async && !xfer->running_retry) {
-        xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
-    }
-    return 0;
-}
-
-static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
-{
-    int i;
-    unsigned int length = 0;
-    XHCITRB *trb;
-
-    DPRINTF("xhci_fire_transfer(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
-
-    for (i = 0; i < xfer->trb_count; i++) {
-        trb = &xfer->trbs[i];
-        if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
-            length += trb->status & 0x1ffff;
-        }
-    }
-    DPRINTF("xhci: total TD length=%d\n", length);
-
-    if (!epctx->has_bg) {
-        xfer->data_length = length;
-        xfer->backgrounded = 0;
-        return xhci_submit(xhci, xfer, epctx);
-    } else {
-        if (!epctx->bg_running) {
-            for (i = 0; i < BG_XFERS; i++) {
-                XHCITransfer *t = &epctx->bg_transfers[i];
-                t->xhci = xhci;
-                t->epid = xfer->epid;
-                t->slotid = xfer->slotid;
-                t->pkts = BG_PKTS;
-                t->pktsize = epctx->max_psize;
-                t->data_length = t->pkts * t->pktsize;
-                t->bg_xfer = 1;
-                if (xhci_submit(xhci, t, epctx) < 0) {
-                    fprintf(stderr, "xhci: bg submit failed\n");
-                    return -1;
-                }
-            }
-            epctx->bg_running = 1;
-        }
-        xfer->backgrounded = 1;
-        xhci_bg_update(xhci, epctx);
-        return 0;
-    }
-}
-
-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
-{
-    XHCIEPContext *epctx;
-    int length;
-    int i;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    assert(epid >= 1 && epid <= 31);
-    DPRINTF("xhci_kick_ep(%d, %d)\n", slotid, epid);
-
-    if (!xhci->slots[slotid-1].enabled) {
-        fprintf(stderr, "xhci: xhci_kick_ep for disabled slot %d\n", slotid);
-        return;
-    }
-    epctx = xhci->slots[slotid-1].eps[epid-1];
-    if (!epctx) {
-        fprintf(stderr, "xhci: xhci_kick_ep for disabled endpoint %d,%d\n",
-                epid, slotid);
-        return;
-    }
-
-    if (epctx->retry) {
-        /* retry nak'ed transfer */
-        XHCITransfer *xfer = epctx->retry;
-        int result;
-
-        DPRINTF("xhci: retry nack'ed transfer ...\n");
-        assert(xfer->running_retry);
-        xhci_setup_packet(xfer, xfer->packet.ep->dev);
-        result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
-        if (result == USB_RET_NAK) {
-            DPRINTF("xhci: ... xfer still nacked\n");
-            return;
-        }
-        DPRINTF("xhci: ... result %d\n", result);
-        xhci_complete_packet(xfer, result);
-        assert(!xfer->running_retry);
-        epctx->retry = NULL;
-    }
-
-    if (epctx->state == EP_HALTED) {
-        DPRINTF("xhci: ep halted, not running schedule\n");
-        return;
-    }
-
-    xhci_set_ep_state(xhci, epctx, EP_RUNNING);
-
-    while (1) {
-        XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
-        if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
-            DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n",
-                    epctx->next_xfer, xfer->running_async,
-                    xfer->running_retry, xfer->backgrounded);
-            break;
-        } else {
-            DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer);
-        }
-        length = xhci_ring_chain_length(xhci, &epctx->ring);
-        if (length < 0) {
-            DPRINTF("xhci: incomplete TD (%d TRBs)\n", -length);
-            break;
-        } else if (length == 0) {
-            break;
-        }
-        DPRINTF("xhci: fetching %d-TRB TD\n", length);
-        if (xfer->trbs && xfer->trb_alloced < length) {
-            xfer->trb_count = 0;
-            xfer->trb_alloced = 0;
-            g_free(xfer->trbs);
-            xfer->trbs = NULL;
-        }
-        if (!xfer->trbs) {
-            xfer->trbs = g_malloc(sizeof(XHCITRB) * length);
-            xfer->trb_alloced = length;
-        }
-        xfer->trb_count = length;
-
-        for (i = 0; i < length; i++) {
-            assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL));
-        }
-        xfer->xhci = xhci;
-        xfer->epid = epid;
-        xfer->slotid = slotid;
-
-        if (epid == 1) {
-            if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
-                epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
-            } else {
-                fprintf(stderr, "xhci: error firing CTL transfer\n");
-            }
-        } else {
-            if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
-                epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
-            } else {
-                fprintf(stderr, "xhci: error firing data transfer\n");
-            }
-        }
-
-        if (epctx->state == EP_HALTED) {
-            DPRINTF("xhci: ep halted, stopping schedule\n");
-            break;
-        }
-        if (xfer->running_retry) {
-            DPRINTF("xhci: xfer nacked, stopping schedule\n");
-            epctx->retry = xfer;
-            break;
-        }
-    }
-}
-
-static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
-{
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_enable_slot(%d)\n", slotid);
-    xhci->slots[slotid-1].enabled = 1;
-    xhci->slots[slotid-1].port = 0;
-    memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
-
-    return CC_SUCCESS;
-}
-
-static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
-{
-    int i;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_disable_slot(%d)\n", slotid);
-
-    for (i = 1; i <= 31; i++) {
-        if (xhci->slots[slotid-1].eps[i-1]) {
-            xhci_disable_ep(xhci, slotid, i);
-        }
-    }
-
-    xhci->slots[slotid-1].enabled = 0;
-    return CC_SUCCESS;
-}
-
-static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
-                                  uint64_t pictx, bool bsr)
-{
-    XHCISlot *slot;
-    USBDevice *dev;
-    target_phys_addr_t ictx, octx, dcbaap;
-    uint64_t poctx;
-    uint32_t ictl_ctx[2];
-    uint32_t slot_ctx[4];
-    uint32_t ep0_ctx[5];
-    unsigned int port;
-    int i;
-    TRBCCode res;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_address_slot(%d)\n", slotid);
-
-    dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
-    cpu_physical_memory_read(dcbaap + 8*slotid,
-                             (uint8_t *) &poctx, sizeof(poctx));
-    ictx = xhci_mask64(pictx);
-    octx = xhci_mask64(le64_to_cpu(poctx));
-
-    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
-    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
-
-    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
-
-    if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) {
-        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
-                ictl_ctx[0], ictl_ctx[1]);
-        return CC_TRB_ERROR;
-    }
-
-    cpu_physical_memory_read(ictx+32, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-    cpu_physical_memory_read(ictx+64, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
-
-    DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
-            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-
-    DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
-            ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
-
-    port = (slot_ctx[1]>>16) & 0xFF;
-    dev = xhci->ports[port-1].port.dev;
-
-    if (port < 1 || port > MAXPORTS) {
-        fprintf(stderr, "xhci: bad port %d\n", port);
-        return CC_TRB_ERROR;
-    } else if (!dev) {
-        fprintf(stderr, "xhci: port %d not connected\n", port);
-        return CC_USB_TRANSACTION_ERROR;
-    }
-
-    for (i = 0; i < MAXSLOTS; i++) {
-        if (xhci->slots[i].port == port) {
-            fprintf(stderr, "xhci: port %d already assigned to slot %d\n",
-                    port, i+1);
-            return CC_TRB_ERROR;
-        }
-    }
-
-    slot = &xhci->slots[slotid-1];
-    slot->port = port;
-    slot->ctx = octx;
-
-    if (bsr) {
-        slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
-    } else {
-        slot->devaddr = xhci->devaddr++;
-        slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
-        DPRINTF("xhci: device address is %d\n", slot->devaddr);
-        usb_device_handle_control(dev, NULL,
-                                  DeviceOutRequest | USB_REQ_SET_ADDRESS,
-                                  slot->devaddr, 0, 0, NULL);
-    }
-
-    res = xhci_enable_ep(xhci, slotid, 1, octx+32, ep0_ctx);
-
-    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
-            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-    DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
-            ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
-
-    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-    cpu_physical_memory_write(octx+32, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
-
-    return res;
-}
-
-
-static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
-                                  uint64_t pictx, bool dc)
-{
-    target_phys_addr_t ictx, octx;
-    uint32_t ictl_ctx[2];
-    uint32_t slot_ctx[4];
-    uint32_t islot_ctx[4];
-    uint32_t ep_ctx[5];
-    int i;
-    TRBCCode res;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_configure_slot(%d)\n", slotid);
-
-    ictx = xhci_mask64(pictx);
-    octx = xhci->slots[slotid-1].ctx;
-
-    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
-    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
-
-    if (dc) {
-        for (i = 2; i <= 31; i++) {
-            if (xhci->slots[slotid-1].eps[i-1]) {
-                xhci_disable_ep(xhci, slotid, i);
-            }
-        }
-
-        cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-        slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
-        slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT;
-        DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
-                slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-        cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-
-        return CC_SUCCESS;
-    }
-
-    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
-
-    if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) {
-        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
-                ictl_ctx[0], ictl_ctx[1]);
-        return CC_TRB_ERROR;
-    }
-
-    cpu_physical_memory_read(ictx+32, (uint8_t *) islot_ctx, sizeof(islot_ctx));
-    cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-
-    if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) {
-        fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]);
-        return CC_CONTEXT_STATE_ERROR;
-    }
-
-    for (i = 2; i <= 31; i++) {
-        if (ictl_ctx[0] & (1<<i)) {
-            xhci_disable_ep(xhci, slotid, i);
-        }
-        if (ictl_ctx[1] & (1<<i)) {
-            cpu_physical_memory_read(ictx+32+(32*i),
-                                     (uint8_t *) ep_ctx, sizeof(ep_ctx));
-            DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n",
-                    i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
-                    ep_ctx[3], ep_ctx[4]);
-            xhci_disable_ep(xhci, slotid, i);
-            res = xhci_enable_ep(xhci, slotid, i, octx+(32*i), ep_ctx);
-            if (res != CC_SUCCESS) {
-                return res;
-            }
-            DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n",
-                    i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
-                    ep_ctx[3], ep_ctx[4]);
-            cpu_physical_memory_write(octx+(32*i),
-                                      (uint8_t *) ep_ctx, sizeof(ep_ctx));
-        }
-    }
-
-    slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
-    slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT;
-    slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT);
-    slot_ctx[0] |= islot_ctx[0] & (SLOT_CONTEXT_ENTRIES_MASK <<
-                                   SLOT_CONTEXT_ENTRIES_SHIFT);
-    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
-            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-
-    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-
-    return CC_SUCCESS;
-}
-
-
-static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
-                                   uint64_t pictx)
-{
-    target_phys_addr_t ictx, octx;
-    uint32_t ictl_ctx[2];
-    uint32_t iep0_ctx[5];
-    uint32_t ep0_ctx[5];
-    uint32_t islot_ctx[4];
-    uint32_t slot_ctx[4];
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_evaluate_slot(%d)\n", slotid);
-
-    ictx = xhci_mask64(pictx);
-    octx = xhci->slots[slotid-1].ctx;
-
-    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
-    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
-
-    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
-
-    if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) {
-        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
-                ictl_ctx[0], ictl_ctx[1]);
-        return CC_TRB_ERROR;
-    }
-
-    if (ictl_ctx[1] & 0x1) {
-        cpu_physical_memory_read(ictx+32,
-                                 (uint8_t *) islot_ctx, sizeof(islot_ctx));
-
-        DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
-                islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]);
-
-        cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-
-        slot_ctx[1] &= ~0xFFFF; /* max exit latency */
-        slot_ctx[1] |= islot_ctx[1] & 0xFFFF;
-        slot_ctx[2] &= ~0xFF00000; /* interrupter target */
-        slot_ctx[2] |= islot_ctx[2] & 0xFF000000;
-
-        DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
-                slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-
-        cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-    }
-
-    if (ictl_ctx[1] & 0x2) {
-        cpu_physical_memory_read(ictx+64,
-                                 (uint8_t *) iep0_ctx, sizeof(iep0_ctx));
-
-        DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
-                iep0_ctx[0], iep0_ctx[1], iep0_ctx[2],
-                iep0_ctx[3], iep0_ctx[4]);
-
-        cpu_physical_memory_read(octx+32, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
-
-        ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/
-        ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000;
-
-        DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
-                ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
-
-        cpu_physical_memory_write(octx+32,
-                                  (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
-    }
-
-    return CC_SUCCESS;
-}
-
-static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
-{
-    uint32_t slot_ctx[4];
-    target_phys_addr_t octx;
-    int i;
-
-    assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_reset_slot(%d)\n", slotid);
-
-    octx = xhci->slots[slotid-1].ctx;
-
-    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
-
-    for (i = 2; i <= 31; i++) {
-        if (xhci->slots[slotid-1].eps[i-1]) {
-            xhci_disable_ep(xhci, slotid, i);
-        }
-    }
-
-    cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-    slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
-    slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT;
-    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
-            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
-
-    return CC_SUCCESS;
-}
-
-static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *trb)
-{
-    unsigned int slotid;
-    slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK;
-    if (slotid < 1 || slotid > MAXSLOTS) {
-        fprintf(stderr, "xhci: bad slot id %d\n", slotid);
-        event->ccode = CC_TRB_ERROR;
-        return 0;
-    } else if (!xhci->slots[slotid-1].enabled) {
-        fprintf(stderr, "xhci: slot id %d not enabled\n", slotid);
-        event->ccode = CC_SLOT_NOT_ENABLED_ERROR;
-        return 0;
-    }
-    return slotid;
-}
-
-static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
-{
-    target_phys_addr_t ctx;
-    uint8_t bw_ctx[MAXPORTS+1];
-
-    DPRINTF("xhci_get_port_bandwidth()\n");
-
-    ctx = xhci_mask64(pctx);
-
-    DPRINTF("xhci: bandwidth context at "TARGET_FMT_plx"\n", ctx);
-
-    /* TODO: actually implement real values here */
-    bw_ctx[0] = 0;
-    memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */
-    cpu_physical_memory_write(ctx, bw_ctx, sizeof(bw_ctx));
-
-    return CC_SUCCESS;
-}
-
-static uint32_t rotl(uint32_t v, unsigned count)
-{
-    count &= 31;
-    return (v << count) | (v >> (32 - count));
-}
-
-
-static uint32_t xhci_nec_challenge(uint32_t hi, uint32_t lo)
-{
-    uint32_t val;
-    val = rotl(lo - 0x49434878, 32 - ((hi>>8) & 0x1F));
-    val += rotl(lo + 0x49434878, hi & 0x1F);
-    val -= rotl(hi ^ 0x49434878, (lo >> 16) & 0x1F);
-    return ~val;
-}
-
-static void xhci_via_challenge(uint64_t addr)
-{
-    uint32_t buf[8];
-    uint32_t obuf[8];
-    target_phys_addr_t paddr = xhci_mask64(addr);
-
-    cpu_physical_memory_read(paddr, (uint8_t *) &buf, 32);
-
-    memcpy(obuf, buf, sizeof(obuf));
-
-    if ((buf[0] & 0xff) == 2) {
-        obuf[0] = 0x49932000 + 0x54dc200 * buf[2] + 0x7429b578 * buf[3];
-        obuf[0] |=  (buf[2] * buf[3]) & 0xff;
-        obuf[1] = 0x0132bb37 + 0xe89 * buf[2] + 0xf09 * buf[3];
-        obuf[2] = 0x0066c2e9 + 0x2091 * buf[2] + 0x19bd * buf[3];
-        obuf[3] = 0xd5281342 + 0x2cc9691 * buf[2] + 0x2367662 * buf[3];
-        obuf[4] = 0x0123c75c + 0x1595 * buf[2] + 0x19ec * buf[3];
-        obuf[5] = 0x00f695de + 0x26fd * buf[2] + 0x3e9 * buf[3];
-        obuf[6] = obuf[2] ^ obuf[3] ^ 0x29472956;
-        obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593;
-    }
-
-    cpu_physical_memory_write(paddr, (uint8_t *) &obuf, 32);
-}
-
-static void xhci_process_commands(XHCIState *xhci)
-{
-    XHCITRB trb;
-    TRBType type;
-    XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS};
-    target_phys_addr_t addr;
-    unsigned int i, slotid = 0;
-
-    DPRINTF("xhci_process_commands()\n");
-    if (!xhci_running(xhci)) {
-        DPRINTF("xhci_process_commands() called while xHC stopped or paused\n");
-        return;
-    }
-
-    xhci->crcr_low |= CRCR_CRR;
-
-    while ((type = xhci_ring_fetch(xhci, &xhci->cmd_ring, &trb, &addr))) {
-        event.ptr = addr;
-        switch (type) {
-        case CR_ENABLE_SLOT:
-            for (i = 0; i < MAXSLOTS; i++) {
-                if (!xhci->slots[i].enabled) {
-                    break;
-                }
-            }
-            if (i >= MAXSLOTS) {
-                fprintf(stderr, "xhci: no device slots available\n");
-                event.ccode = CC_NO_SLOTS_ERROR;
-            } else {
-                slotid = i+1;
-                event.ccode = xhci_enable_slot(xhci, slotid);
-            }
-            break;
-        case CR_DISABLE_SLOT:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                event.ccode = xhci_disable_slot(xhci, slotid);
-            }
-            break;
-        case CR_ADDRESS_DEVICE:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                event.ccode = xhci_address_slot(xhci, slotid, trb.parameter,
-                                                trb.control & TRB_CR_BSR);
-            }
-            break;
-        case CR_CONFIGURE_ENDPOINT:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                event.ccode = xhci_configure_slot(xhci, slotid, trb.parameter,
-                                                  trb.control & TRB_CR_DC);
-            }
-            break;
-        case CR_EVALUATE_CONTEXT:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                event.ccode = xhci_evaluate_slot(xhci, slotid, trb.parameter);
-            }
-            break;
-        case CR_STOP_ENDPOINT:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
-                    & TRB_CR_EPID_MASK;
-                event.ccode = xhci_stop_ep(xhci, slotid, epid);
-            }
-            break;
-        case CR_RESET_ENDPOINT:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
-                    & TRB_CR_EPID_MASK;
-                event.ccode = xhci_reset_ep(xhci, slotid, epid);
-            }
-            break;
-        case CR_SET_TR_DEQUEUE:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
-                    & TRB_CR_EPID_MASK;
-                event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid,
-                                                  trb.parameter);
-            }
-            break;
-        case CR_RESET_DEVICE:
-            slotid = xhci_get_slot(xhci, &event, &trb);
-            if (slotid) {
-                event.ccode = xhci_reset_slot(xhci, slotid);
-            }
-            break;
-        case CR_GET_PORT_BANDWIDTH:
-            event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter);
-            break;
-        case CR_VENDOR_VIA_CHALLENGE_RESPONSE:
-            xhci_via_challenge(trb.parameter);
-            break;
-        case CR_VENDOR_NEC_FIRMWARE_REVISION:
-            event.type = 48; /* NEC reply */
-            event.length = 0x3025;
-            break;
-        case CR_VENDOR_NEC_CHALLENGE_RESPONSE:
-        {
-            uint32_t chi = trb.parameter >> 32;
-            uint32_t clo = trb.parameter;
-            uint32_t val = xhci_nec_challenge(chi, clo);
-            event.length = val & 0xFFFF;
-            event.epid = val >> 16;
-            slotid = val >> 24;
-            event.type = 48; /* NEC reply */
-        }
-        break;
-        default:
-            fprintf(stderr, "xhci: unimplemented command %d\n", type);
-            event.ccode = CC_TRB_ERROR;
-            break;
-        }
-        event.slotid = slotid;
-        xhci_event(xhci, &event);
-    }
-}
-
-static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
-{
-    int nr = port->port.index + 1;
-
-    port->portsc = PORTSC_PP;
-    if (port->port.dev && port->port.dev->attached && !is_detach) {
-        port->portsc |= PORTSC_CCS;
-        switch (port->port.dev->speed) {
-        case USB_SPEED_LOW:
-            port->portsc |= PORTSC_SPEED_LOW;
-            break;
-        case USB_SPEED_FULL:
-            port->portsc |= PORTSC_SPEED_FULL;
-            break;
-        case USB_SPEED_HIGH:
-            port->portsc |= PORTSC_SPEED_HIGH;
-            break;
-        }
-    }
-
-    if (xhci_running(xhci)) {
-        port->portsc |= PORTSC_CSC;
-        XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
-        xhci_event(xhci, &ev);
-        DPRINTF("xhci: port change event for port %d\n", nr);
-    }
-}
-
-static void xhci_reset(void *opaque)
-{
-    XHCIState *xhci = opaque;
-    int i;
-
-    DPRINTF("xhci: full reset\n");
-    if (!(xhci->usbsts & USBSTS_HCH)) {
-        fprintf(stderr, "xhci: reset while running!\n");
-    }
-
-    xhci->usbcmd = 0;
-    xhci->usbsts = USBSTS_HCH;
-    xhci->dnctrl = 0;
-    xhci->crcr_low = 0;
-    xhci->crcr_high = 0;
-    xhci->dcbaap_low = 0;
-    xhci->dcbaap_high = 0;
-    xhci->config = 0;
-    xhci->devaddr = 2;
-
-    for (i = 0; i < MAXSLOTS; i++) {
-        xhci_disable_slot(xhci, i+1);
-    }
-
-    for (i = 0; i < MAXPORTS; i++) {
-        xhci_update_port(xhci, xhci->ports + i, 0);
-    }
-
-    xhci->mfindex = 0;
-    xhci->iman = 0;
-    xhci->imod = 0;
-    xhci->erstsz = 0;
-    xhci->erstba_low = 0;
-    xhci->erstba_high = 0;
-    xhci->erdp_low = 0;
-    xhci->erdp_high = 0;
-
-    xhci->er_ep_idx = 0;
-    xhci->er_pcs = 1;
-    xhci->er_full = 0;
-    xhci->ev_buffer_put = 0;
-    xhci->ev_buffer_get = 0;
-}
-
-static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
-{
-    DPRINTF("xhci_cap_read(0x%x)\n", reg);
-
-    switch (reg) {
-    case 0x00: /* HCIVERSION, CAPLENGTH */
-        return 0x01000000 | LEN_CAP;
-    case 0x04: /* HCSPARAMS 1 */
-        return (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
-    case 0x08: /* HCSPARAMS 2 */
-        return 0x0000000f;
-    case 0x0c: /* HCSPARAMS 3 */
-        return 0x00000000;
-    case 0x10: /* HCCPARAMS */
-#if TARGET_PHYS_ADDR_BITS > 32
-        return 0x00081001;
-#else
-        return 0x00081000;
-#endif
-    case 0x14: /* DBOFF */
-        return OFF_DOORBELL;
-    case 0x18: /* RTSOFF */
-        return OFF_RUNTIME;
-
-    /* extended capabilities */
-    case 0x20: /* Supported Protocol:00 */
-#if USB3_PORTS > 0
-        return 0x02000402; /* USB 2.0 */
-#else
-        return 0x02000002; /* USB 2.0 */
-#endif
-    case 0x24: /* Supported Protocol:04 */
-        return 0x20425455; /* "USB " */
-    case 0x28: /* Supported Protocol:08 */
-        return 0x00000001 | (USB2_PORTS<<8);
-    case 0x2c: /* Supported Protocol:0c */
-        return 0x00000000; /* reserved */
-#if USB3_PORTS > 0
-    case 0x30: /* Supported Protocol:00 */
-        return 0x03000002; /* USB 3.0 */
-    case 0x34: /* Supported Protocol:04 */
-        return 0x20425455; /* "USB " */
-    case 0x38: /* Supported Protocol:08 */
-        return 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
-    case 0x3c: /* Supported Protocol:0c */
-        return 0x00000000; /* reserved */
-#endif
-    default:
-        fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg);
-    }
-    return 0;
-}
-
-static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
-{
-    uint32_t port = reg >> 4;
-    if (port >= MAXPORTS) {
-        fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
-        return 0;
-    }
-
-    switch (reg & 0xf) {
-    case 0x00: /* PORTSC */
-        return xhci->ports[port].portsc;
-    case 0x04: /* PORTPMSC */
-    case 0x08: /* PORTLI */
-        return 0;
-    case 0x0c: /* reserved */
-    default:
-        fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
-                port, reg);
-        return 0;
-    }
-}
-
-static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
-{
-    uint32_t port = reg >> 4;
-    uint32_t portsc;
-
-    if (port >= MAXPORTS) {
-        fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
-        return;
-    }
-
-    switch (reg & 0xf) {
-    case 0x00: /* PORTSC */
-        portsc = xhci->ports[port].portsc;
-        /* write-1-to-clear bits*/
-        portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
-                           PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
-        if (val & PORTSC_LWS) {
-            /* overwrite PLS only when LWS=1 */
-            portsc &= ~(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
-            portsc |= val & (PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
-        }
-        /* read/write bits */
-        portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
-        portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
-        /* write-1-to-start bits */
-        if (val & PORTSC_PR) {
-            DPRINTF("xhci: port %d reset\n", port);
-            usb_device_reset(xhci->ports[port].port.dev);
-            portsc |= PORTSC_PRC | PORTSC_PED;
-        }
-        xhci->ports[port].portsc = portsc;
-        break;
-    case 0x04: /* PORTPMSC */
-    case 0x08: /* PORTLI */
-    default:
-        fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n",
-                port, reg);
-    }
-}
-
-static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
-{
-    DPRINTF("xhci_oper_read(0x%x)\n", reg);
-
-    if (reg >= 0x400) {
-        return xhci_port_read(xhci, reg - 0x400);
-    }
-
-    switch (reg) {
-    case 0x00: /* USBCMD */
-        return xhci->usbcmd;
-    case 0x04: /* USBSTS */
-        return xhci->usbsts;
-    case 0x08: /* PAGESIZE */
-        return 1; /* 4KiB */
-    case 0x14: /* DNCTRL */
-        return xhci->dnctrl;
-    case 0x18: /* CRCR low */
-        return xhci->crcr_low & ~0xe;
-    case 0x1c: /* CRCR high */
-        return xhci->crcr_high;
-    case 0x30: /* DCBAAP low */
-        return xhci->dcbaap_low;
-    case 0x34: /* DCBAAP high */
-        return xhci->dcbaap_high;
-    case 0x38: /* CONFIG */
-        return xhci->config;
-    default:
-        fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg);
-    }
-    return 0;
-}
-
-static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
-{
-    DPRINTF("xhci_oper_write(0x%x, 0x%08x)\n", reg, val);
-
-    if (reg >= 0x400) {
-        xhci_port_write(xhci, reg - 0x400, val);
-        return;
-    }
-
-    switch (reg) {
-    case 0x00: /* USBCMD */
-        if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) {
-            xhci_run(xhci);
-        } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) {
-            xhci_stop(xhci);
-        }
-        xhci->usbcmd = val & 0xc0f;
-        if (val & USBCMD_HCRST) {
-            xhci_reset(xhci);
-        }
-        xhci_irq_update(xhci);
-        break;
-
-    case 0x04: /* USBSTS */
-        /* these bits are write-1-to-clear */
-        xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
-        xhci_irq_update(xhci);
-        break;
-
-    case 0x14: /* DNCTRL */
-        xhci->dnctrl = val & 0xffff;
-        break;
-    case 0x18: /* CRCR low */
-        xhci->crcr_low = (val & 0xffffffcf) | (xhci->crcr_low & CRCR_CRR);
-        break;
-    case 0x1c: /* CRCR high */
-        xhci->crcr_high = val;
-        if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) {
-            XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED};
-            xhci->crcr_low &= ~CRCR_CRR;
-            xhci_event(xhci, &event);
-            DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
-        } else {
-            target_phys_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
-            xhci_ring_init(xhci, &xhci->cmd_ring, base);
-        }
-        xhci->crcr_low &= ~(CRCR_CA | CRCR_CS);
-        break;
-    case 0x30: /* DCBAAP low */
-        xhci->dcbaap_low = val & 0xffffffc0;
-        break;
-    case 0x34: /* DCBAAP high */
-        xhci->dcbaap_high = val;
-        break;
-    case 0x38: /* CONFIG */
-        xhci->config = val & 0xff;
-        break;
-    default:
-        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
-    }
-}
-
-static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
-{
-    DPRINTF("xhci_runtime_read(0x%x)\n", reg);
-
-    switch (reg) {
-    case 0x00: /* MFINDEX */
-        fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
-        return xhci->mfindex;
-    case 0x20: /* IMAN */
-        return xhci->iman;
-    case 0x24: /* IMOD */
-        return xhci->imod;
-    case 0x28: /* ERSTSZ */
-        return xhci->erstsz;
-    case 0x30: /* ERSTBA low */
-        return xhci->erstba_low;
-    case 0x34: /* ERSTBA high */
-        return xhci->erstba_high;
-    case 0x38: /* ERDP low */
-        return xhci->erdp_low;
-    case 0x3c: /* ERDP high */
-        return xhci->erdp_high;
-    default:
-        fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
-    }
-    return 0;
-}
-
-static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
-{
-    DPRINTF("xhci_runtime_write(0x%x, 0x%08x)\n", reg, val);
-
-    switch (reg) {
-    case 0x20: /* IMAN */
-        if (val & IMAN_IP) {
-            xhci->iman &= ~IMAN_IP;
-        }
-        xhci->iman &= ~IMAN_IE;
-        xhci->iman |= val & IMAN_IE;
-        xhci_irq_update(xhci);
-        break;
-    case 0x24: /* IMOD */
-        xhci->imod = val;
-        break;
-    case 0x28: /* ERSTSZ */
-        xhci->erstsz = val & 0xffff;
-        break;
-    case 0x30: /* ERSTBA low */
-        /* XXX NEC driver bug: it doesn't align this to 64 bytes
-        xhci->erstba_low = val & 0xffffffc0; */
-        xhci->erstba_low = val & 0xfffffff0;
-        break;
-    case 0x34: /* ERSTBA high */
-        xhci->erstba_high = val;
-        xhci_er_reset(xhci);
-        break;
-    case 0x38: /* ERDP low */
-        if (val & ERDP_EHB) {
-            xhci->erdp_low &= ~ERDP_EHB;
-        }
-        xhci->erdp_low = (val & ~ERDP_EHB) | (xhci->erdp_low & ERDP_EHB);
-        break;
-    case 0x3c: /* ERDP high */
-        xhci->erdp_high = val;
-        xhci_events_update(xhci);
-        break;
-    default:
-        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
-    }
-}
-
-static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg)
-{
-    DPRINTF("xhci_doorbell_read(0x%x)\n", reg);
-    /* doorbells always read as 0 */
-    return 0;
-}
-
-static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
-{
-    DPRINTF("xhci_doorbell_write(0x%x, 0x%08x)\n", reg, val);
-
-    if (!xhci_running(xhci)) {
-        fprintf(stderr, "xhci: wrote doorbell while xHC stopped or paused\n");
-        return;
-    }
-
-    reg >>= 2;
-
-    if (reg == 0) {
-        if (val == 0) {
-            xhci_process_commands(xhci);
-        } else {
-            fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", val);
-        }
-    } else {
-        if (reg > MAXSLOTS) {
-            fprintf(stderr, "xhci: bad doorbell %d\n", reg);
-        } else if (val > 31) {
-            fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", reg, val);
-        } else {
-            xhci_kick_ep(xhci, reg, val);
-        }
-    }
-}
-
-static uint64_t xhci_mem_read(void *ptr, target_phys_addr_t addr,
-                              unsigned size)
-{
-    XHCIState *xhci = ptr;
-
-    /* Only aligned reads are allowed on xHCI */
-    if (addr & 3) {
-        fprintf(stderr, "xhci_mem_read: Mis-aligned read\n");
-        return 0;
-    }
-
-    if (addr < LEN_CAP) {
-        return xhci_cap_read(xhci, addr);
-    } else if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
-        return xhci_oper_read(xhci, addr - OFF_OPER);
-    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
-        return xhci_runtime_read(xhci, addr - OFF_RUNTIME);
-    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
-        return xhci_doorbell_read(xhci, addr - OFF_DOORBELL);
-    } else {
-        fprintf(stderr, "xhci_mem_read: Bad offset %x\n", (int)addr);
-        return 0;
-    }
-}
-
-static void xhci_mem_write(void *ptr, target_phys_addr_t addr,
-                           uint64_t val, unsigned size)
-{
-    XHCIState *xhci = ptr;
-
-    /* Only aligned writes are allowed on xHCI */
-    if (addr & 3) {
-        fprintf(stderr, "xhci_mem_write: Mis-aligned write\n");
-        return;
-    }
-
-    if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
-        xhci_oper_write(xhci, addr - OFF_OPER, val);
-    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
-        xhci_runtime_write(xhci, addr - OFF_RUNTIME, val);
-    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
-        xhci_doorbell_write(xhci, addr - OFF_DOORBELL, val);
-    } else {
-        fprintf(stderr, "xhci_mem_write: Bad offset %x\n", (int)addr);
-    }
-}
-
-static const MemoryRegionOps xhci_mem_ops = {
-    .read = xhci_mem_read,
-    .write = xhci_mem_write,
-    .valid.min_access_size = 4,
-    .valid.max_access_size = 4,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void xhci_attach(USBPort *usbport)
-{
-    XHCIState *xhci = usbport->opaque;
-    XHCIPort *port = &xhci->ports[usbport->index];
-
-    xhci_update_port(xhci, port, 0);
-}
-
-static void xhci_detach(USBPort *usbport)
-{
-    XHCIState *xhci = usbport->opaque;
-    XHCIPort *port = &xhci->ports[usbport->index];
-
-    xhci_update_port(xhci, port, 1);
-}
-
-static void xhci_wakeup(USBPort *usbport)
-{
-    XHCIState *xhci = usbport->opaque;
-    XHCIPort *port = &xhci->ports[usbport->index];
-    int nr = port->port.index + 1;
-    XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
-    uint32_t pls;
-
-    pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
-    if (pls != 3) {
-        return;
-    }
-    port->portsc |= 0xf << PORTSC_PLS_SHIFT;
-    if (port->portsc & PORTSC_PLC) {
-        return;
-    }
-    port->portsc |= PORTSC_PLC;
-    xhci_event(xhci, &ev);
-}
-
-static void xhci_complete(USBPort *port, USBPacket *packet)
-{
-    XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
-
-    xhci_complete_packet(xfer, packet->result);
-    xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
-}
-
-static void xhci_child_detach(USBPort *port, USBDevice *child)
-{
-    FIXME();
-}
-
-static USBPortOps xhci_port_ops = {
-    .attach   = xhci_attach,
-    .detach   = xhci_detach,
-    .wakeup   = xhci_wakeup,
-    .complete = xhci_complete,
-    .child_detach = xhci_child_detach,
-};
-
-static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
-{
-    XHCISlot *slot;
-    int slotid;
-
-    for (slotid = 1; slotid <= MAXSLOTS; slotid++) {
-        slot = &xhci->slots[slotid-1];
-        if (slot->devaddr == dev->addr) {
-            return slotid;
-        }
-    }
-    return 0;
-}
-
-static int xhci_find_epid(USBEndpoint *ep)
-{
-    if (ep->nr == 0) {
-        return 1;
-    }
-    if (ep->pid == USB_TOKEN_IN) {
-        return ep->nr * 2 + 1;
-    } else {
-        return ep->nr * 2;
-    }
-}
-
-static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
-{
-    XHCIState *xhci = container_of(bus, XHCIState, bus);
-    int slotid;
-
-    DPRINTF("%s\n", __func__);
-    slotid = xhci_find_slotid(xhci, ep->dev);
-    if (slotid == 0 || !xhci->slots[slotid-1].enabled) {
-        DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
-        return;
-    }
-    xhci_kick_ep(xhci, slotid, xhci_find_epid(ep));
-}
-
-static USBBusOps xhci_bus_ops = {
-    .wakeup_endpoint = xhci_wakeup_endpoint,
-};
-
-static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
-{
-    int i;
-
-    xhci->usbsts = USBSTS_HCH;
-
-    usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev);
-
-    for (i = 0; i < MAXPORTS; i++) {
-        memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
-        usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
-                          &xhci_port_ops,
-                          USB_SPEED_MASK_LOW  |
-                          USB_SPEED_MASK_FULL |
-                          USB_SPEED_MASK_HIGH);
-    }
-    for (i = 0; i < MAXSLOTS; i++) {
-        xhci->slots[i].enabled = 0;
-    }
-
-    qemu_register_reset(xhci_reset, xhci);
-}
-
-static int usb_xhci_initfn(struct PCIDevice *dev)
-{
-    int ret;
-
-    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
-
-    xhci->pci_dev.config[PCI_CLASS_PROG] = 0x30;    /* xHCI */
-    xhci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
-    xhci->pci_dev.config[PCI_CACHE_LINE_SIZE] = 0x10;
-    xhci->pci_dev.config[0x60] = 0x30; /* release number */
-
-    usb_xhci_init(xhci, &dev->qdev);
-
-    xhci->irq = xhci->pci_dev.irq[0];
-
-    memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci,
-                          "xhci", LEN_REGS);
-    pci_register_bar(&xhci->pci_dev, 0,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
-                     &xhci->mem);
-
-    ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0);
-    assert(ret >= 0);
-
-    if (xhci->msi) {
-        ret = msi_init(&xhci->pci_dev, 0x70, 1, true, false);
-        assert(ret >= 0);
-    }
-
-    return 0;
-}
-
-static void xhci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val,
-                              int len)
-{
-    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
-
-    pci_default_write_config(dev, addr, val, len);
-    if (xhci->msi) {
-        msi_write_config(dev, addr, val, len);
-    }
-}
-
-static const VMStateDescription vmstate_xhci = {
-    .name = "xhci",
-    .unmigratable = 1,
-};
-
-static Property xhci_properties[] = {
-    DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xhci_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->vmsd    = &vmstate_xhci;
-    dc->props   = xhci_properties;
-    k->init         = usb_xhci_initfn;
-    k->vendor_id    = PCI_VENDOR_ID_NEC;
-    k->device_id    = PCI_DEVICE_ID_NEC_UPD720200;
-    k->class_id     = PCI_CLASS_SERIAL_USB;
-    k->revision     = 0x03;
-    k->is_express   = 1;
-    k->config_write = xhci_write_config;
-}
-
-static TypeInfo xhci_info = {
-    .name          = "nec-usb-xhci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(XHCIState),
-    .class_init    = xhci_class_init,
-};
-
-static void xhci_register_types(void)
-{
-    type_register_static(&xhci_info);
-}
-
-type_init(xhci_register_types)
diff --git a/hw/usb.c b/hw/usb.c
deleted file mode 100644 (file)
index 1ec2e90..0000000
--- a/hw/usb.c
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * QEMU USB emulation
- *
- * Copyright (c) 2005 Fabrice Bellard
- *
- * 2008 Generic packet handler rewrite by Max Krasnyansky
- *
- * 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 "usb.h"
-#include "iov.h"
-#include "trace.h"
-
-void usb_attach(USBPort *port)
-{
-    USBDevice *dev = port->dev;
-
-    assert(dev != NULL);
-    assert(dev->attached);
-    assert(dev->state == USB_STATE_NOTATTACHED);
-    port->ops->attach(port);
-    dev->state = USB_STATE_ATTACHED;
-    usb_device_handle_attach(dev);
-}
-
-void usb_detach(USBPort *port)
-{
-    USBDevice *dev = port->dev;
-
-    assert(dev != NULL);
-    assert(dev->state != USB_STATE_NOTATTACHED);
-    port->ops->detach(port);
-    dev->state = USB_STATE_NOTATTACHED;
-}
-
-void usb_port_reset(USBPort *port)
-{
-    USBDevice *dev = port->dev;
-
-    assert(dev != NULL);
-    usb_detach(port);
-    usb_attach(port);
-    usb_device_reset(dev);
-}
-
-void usb_device_reset(USBDevice *dev)
-{
-    if (dev == NULL || !dev->attached) {
-        return;
-    }
-    dev->remote_wakeup = 0;
-    dev->addr = 0;
-    dev->state = USB_STATE_DEFAULT;
-    usb_device_handle_reset(dev);
-}
-
-void usb_wakeup(USBEndpoint *ep)
-{
-    USBDevice *dev = ep->dev;
-    USBBus *bus = usb_bus_from_device(dev);
-
-    if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
-        dev->port->ops->wakeup(dev->port);
-    }
-    if (bus->ops->wakeup_endpoint) {
-        bus->ops->wakeup_endpoint(bus, ep);
-    }
-}
-
-/**********************/
-
-/* generic USB device helpers (you are not forced to use them when
-   writing your USB device driver, but they help handling the
-   protocol)
-*/
-
-#define SETUP_STATE_IDLE  0
-#define SETUP_STATE_SETUP 1
-#define SETUP_STATE_DATA  2
-#define SETUP_STATE_ACK   3
-#define SETUP_STATE_PARAM 4
-
-static int do_token_setup(USBDevice *s, USBPacket *p)
-{
-    int request, value, index;
-    int ret = 0;
-
-    if (p->iov.size != 8) {
-        return USB_RET_STALL;
-    }
-
-    usb_packet_copy(p, s->setup_buf, p->iov.size);
-    s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
-    s->setup_index = 0;
-
-    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
-    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
-    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
-
-    if (s->setup_buf[0] & USB_DIR_IN) {
-        ret = usb_device_handle_control(s, p, request, value, index,
-                                        s->setup_len, s->data_buf);
-        if (ret == USB_RET_ASYNC) {
-             s->setup_state = SETUP_STATE_SETUP;
-             return USB_RET_ASYNC;
-        }
-        if (ret < 0)
-            return ret;
-
-        if (ret < s->setup_len)
-            s->setup_len = ret;
-        s->setup_state = SETUP_STATE_DATA;
-    } else {
-        if (s->setup_len > sizeof(s->data_buf)) {
-            fprintf(stderr,
-                "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
-                s->setup_len, sizeof(s->data_buf));
-            return USB_RET_STALL;
-        }
-        if (s->setup_len == 0)
-            s->setup_state = SETUP_STATE_ACK;
-        else
-            s->setup_state = SETUP_STATE_DATA;
-    }
-
-    return ret;
-}
-
-static int do_token_in(USBDevice *s, USBPacket *p)
-{
-    int request, value, index;
-    int ret = 0;
-
-    assert(p->ep->nr == 0);
-
-    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
-    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
-    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
-    switch(s->setup_state) {
-    case SETUP_STATE_ACK:
-        if (!(s->setup_buf[0] & USB_DIR_IN)) {
-            ret = usb_device_handle_control(s, p, request, value, index,
-                                            s->setup_len, s->data_buf);
-            if (ret == USB_RET_ASYNC) {
-                return USB_RET_ASYNC;
-            }
-            s->setup_state = SETUP_STATE_IDLE;
-            if (ret > 0)
-                return 0;
-            return ret;
-        }
-
-        /* return 0 byte */
-        return 0;
-
-    case SETUP_STATE_DATA:
-        if (s->setup_buf[0] & USB_DIR_IN) {
-            int len = s->setup_len - s->setup_index;
-            if (len > p->iov.size) {
-                len = p->iov.size;
-            }
-            usb_packet_copy(p, s->data_buf + s->setup_index, len);
-            s->setup_index += len;
-            if (s->setup_index >= s->setup_len)
-                s->setup_state = SETUP_STATE_ACK;
-            return len;
-        }
-
-        s->setup_state = SETUP_STATE_IDLE;
-        return USB_RET_STALL;
-
-    default:
-        return USB_RET_STALL;
-    }
-}
-
-static int do_token_out(USBDevice *s, USBPacket *p)
-{
-    assert(p->ep->nr == 0);
-
-    switch(s->setup_state) {
-    case SETUP_STATE_ACK:
-        if (s->setup_buf[0] & USB_DIR_IN) {
-            s->setup_state = SETUP_STATE_IDLE;
-            /* transfer OK */
-        } else {
-            /* ignore additional output */
-        }
-        return 0;
-
-    case SETUP_STATE_DATA:
-        if (!(s->setup_buf[0] & USB_DIR_IN)) {
-            int len = s->setup_len - s->setup_index;
-            if (len > p->iov.size) {
-                len = p->iov.size;
-            }
-            usb_packet_copy(p, s->data_buf + s->setup_index, len);
-            s->setup_index += len;
-            if (s->setup_index >= s->setup_len)
-                s->setup_state = SETUP_STATE_ACK;
-            return len;
-        }
-
-        s->setup_state = SETUP_STATE_IDLE;
-        return USB_RET_STALL;
-
-    default:
-        return USB_RET_STALL;
-    }
-}
-
-static int do_parameter(USBDevice *s, USBPacket *p)
-{
-    int request, value, index;
-    int i, ret = 0;
-
-    for (i = 0; i < 8; i++) {
-        s->setup_buf[i] = p->parameter >> (i*8);
-    }
-
-    s->setup_state = SETUP_STATE_PARAM;
-    s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
-    s->setup_index = 0;
-
-    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
-    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
-    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
-
-    if (s->setup_len > sizeof(s->data_buf)) {
-        fprintf(stderr,
-                "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
-                s->setup_len, sizeof(s->data_buf));
-        return USB_RET_STALL;
-    }
-
-    if (p->pid == USB_TOKEN_OUT) {
-        usb_packet_copy(p, s->data_buf, s->setup_len);
-    }
-
-    ret = usb_device_handle_control(s, p, request, value, index,
-                                    s->setup_len, s->data_buf);
-    if (ret < 0) {
-        return ret;
-    }
-
-    if (ret < s->setup_len) {
-        s->setup_len = ret;
-    }
-    if (p->pid == USB_TOKEN_IN) {
-        usb_packet_copy(p, s->data_buf, s->setup_len);
-    }
-
-    return ret;
-}
-
-/* ctrl complete function for devices which use usb_generic_handle_packet and
-   may return USB_RET_ASYNC from their handle_control callback. Device code
-   which does this *must* call this function instead of the normal
-   usb_packet_complete to complete their async control packets. */
-void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
-{
-    if (p->result < 0) {
-        s->setup_state = SETUP_STATE_IDLE;
-    }
-
-    switch (s->setup_state) {
-    case SETUP_STATE_SETUP:
-        if (p->result < s->setup_len) {
-            s->setup_len = p->result;
-        }
-        s->setup_state = SETUP_STATE_DATA;
-        p->result = 8;
-        break;
-
-    case SETUP_STATE_ACK:
-        s->setup_state = SETUP_STATE_IDLE;
-        p->result = 0;
-        break;
-
-    case SETUP_STATE_PARAM:
-        if (p->result < s->setup_len) {
-            s->setup_len = p->result;
-        }
-        if (p->pid == USB_TOKEN_IN) {
-            p->result = 0;
-            usb_packet_copy(p, s->data_buf, s->setup_len);
-        }
-        break;
-
-    default:
-        break;
-    }
-    usb_packet_complete(s, p);
-}
-
-/* XXX: fix overflow */
-int set_usb_string(uint8_t *buf, const char *str)
-{
-    int len, i;
-    uint8_t *q;
-
-    q = buf;
-    len = strlen(str);
-    *q++ = 2 * len + 2;
-    *q++ = 3;
-    for(i = 0; i < len; i++) {
-        *q++ = str[i];
-        *q++ = 0;
-    }
-    return q - buf;
-}
-
-USBDevice *usb_find_device(USBPort *port, uint8_t addr)
-{
-    USBDevice *dev = port->dev;
-
-    if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) {
-        return NULL;
-    }
-    if (dev->addr == addr) {
-        return dev;
-    }
-    return usb_device_find_device(dev, addr);
-}
-
-static int usb_process_one(USBPacket *p)
-{
-    USBDevice *dev = p->ep->dev;
-
-    if (p->ep->nr == 0) {
-        /* control pipe */
-        if (p->parameter) {
-            return do_parameter(dev, p);
-        }
-        switch (p->pid) {
-        case USB_TOKEN_SETUP:
-            return do_token_setup(dev, p);
-        case USB_TOKEN_IN:
-            return do_token_in(dev, p);
-        case USB_TOKEN_OUT:
-            return do_token_out(dev, p);
-        default:
-            return USB_RET_STALL;
-        }
-    } else {
-        /* data pipe */
-        return usb_device_handle_data(dev, p);
-    }
-}
-
-/* Hand over a packet to a device for processing.  Return value
-   USB_RET_ASYNC indicates the processing isn't finished yet, the
-   driver will call usb_packet_complete() when done processing it. */
-int usb_handle_packet(USBDevice *dev, USBPacket *p)
-{
-    int ret;
-
-    if (dev == NULL) {
-        return USB_RET_NODEV;
-    }
-    assert(dev == p->ep->dev);
-    assert(dev->state == USB_STATE_DEFAULT);
-    assert(p->state == USB_PACKET_SETUP);
-    assert(p->ep != NULL);
-
-    if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
-        ret = usb_process_one(p);
-        if (ret == USB_RET_ASYNC) {
-            usb_packet_set_state(p, USB_PACKET_ASYNC);
-            QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
-        } else {
-            p->result = ret;
-            usb_packet_set_state(p, USB_PACKET_COMPLETE);
-        }
-    } else {
-        ret = USB_RET_ASYNC;
-        usb_packet_set_state(p, USB_PACKET_QUEUED);
-        QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
-    }
-    return ret;
-}
-
-/* Notify the controller that an async packet is complete.  This should only
-   be called for packets previously deferred by returning USB_RET_ASYNC from
-   handle_packet. */
-void usb_packet_complete(USBDevice *dev, USBPacket *p)
-{
-    USBEndpoint *ep = p->ep;
-    int ret;
-
-    assert(p->state == USB_PACKET_ASYNC);
-    assert(QTAILQ_FIRST(&ep->queue) == p);
-    usb_packet_set_state(p, USB_PACKET_COMPLETE);
-    QTAILQ_REMOVE(&ep->queue, p, queue);
-    dev->port->ops->complete(dev->port, p);
-
-    while (!QTAILQ_EMPTY(&ep->queue)) {
-        p = QTAILQ_FIRST(&ep->queue);
-        if (p->state == USB_PACKET_ASYNC) {
-            break;
-        }
-        assert(p->state == USB_PACKET_QUEUED);
-        ret = usb_process_one(p);
-        if (ret == USB_RET_ASYNC) {
-            usb_packet_set_state(p, USB_PACKET_ASYNC);
-            break;
-        }
-        p->result = ret;
-        usb_packet_set_state(p, USB_PACKET_COMPLETE);
-        QTAILQ_REMOVE(&ep->queue, p, queue);
-        dev->port->ops->complete(dev->port, p);
-    }
-}
-
-/* Cancel an active packet.  The packed must have been deferred by
-   returning USB_RET_ASYNC from handle_packet, and not yet
-   completed.  */
-void usb_cancel_packet(USBPacket * p)
-{
-    bool callback = (p->state == USB_PACKET_ASYNC);
-    assert(usb_packet_is_inflight(p));
-    usb_packet_set_state(p, USB_PACKET_CANCELED);
-    QTAILQ_REMOVE(&p->ep->queue, p, queue);
-    if (callback) {
-        usb_device_cancel_packet(p->ep->dev, p);
-    }
-}
-
-
-void usb_packet_init(USBPacket *p)
-{
-    qemu_iovec_init(&p->iov, 1);
-}
-
-void usb_packet_set_state(USBPacket *p, USBPacketState state)
-{
-    static const char *name[] = {
-        [USB_PACKET_UNDEFINED] = "undef",
-        [USB_PACKET_SETUP]     = "setup",
-        [USB_PACKET_QUEUED]    = "queued",
-        [USB_PACKET_ASYNC]     = "async",
-        [USB_PACKET_COMPLETE]  = "complete",
-        [USB_PACKET_CANCELED]  = "canceled",
-    };
-    USBDevice *dev = p->ep->dev;
-    USBBus *bus = usb_bus_from_device(dev);
-
-    trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr,
-                                  p, name[p->state], name[state]);
-    p->state = state;
-}
-
-void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
-{
-    assert(!usb_packet_is_inflight(p));
-    p->pid = pid;
-    p->ep = ep;
-    p->result = 0;
-    p->parameter = 0;
-    qemu_iovec_reset(&p->iov);
-    usb_packet_set_state(p, USB_PACKET_SETUP);
-}
-
-void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
-{
-    qemu_iovec_add(&p->iov, ptr, len);
-}
-
-void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
-{
-    assert(p->result >= 0);
-    assert(p->result + bytes <= p->iov.size);
-    switch (p->pid) {
-    case USB_TOKEN_SETUP:
-    case USB_TOKEN_OUT:
-        iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
-        break;
-    case USB_TOKEN_IN:
-        iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
-        break;
-    default:
-        fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
-        abort();
-    }
-    p->result += bytes;
-}
-
-void usb_packet_skip(USBPacket *p, size_t bytes)
-{
-    assert(p->result >= 0);
-    assert(p->result + bytes <= p->iov.size);
-    if (p->pid == USB_TOKEN_IN) {
-        iov_clear(p->iov.iov, p->iov.niov, p->result, bytes);
-    }
-    p->result += bytes;
-}
-
-void usb_packet_cleanup(USBPacket *p)
-{
-    assert(!usb_packet_is_inflight(p));
-    qemu_iovec_destroy(&p->iov);
-}
-
-void usb_ep_init(USBDevice *dev)
-{
-    int ep;
-
-    dev->ep_ctl.nr = 0;
-    dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
-    dev->ep_ctl.ifnum = 0;
-    dev->ep_ctl.dev = dev;
-    dev->ep_ctl.pipeline = false;
-    QTAILQ_INIT(&dev->ep_ctl.queue);
-    for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
-        dev->ep_in[ep].nr = ep + 1;
-        dev->ep_out[ep].nr = ep + 1;
-        dev->ep_in[ep].pid = USB_TOKEN_IN;
-        dev->ep_out[ep].pid = USB_TOKEN_OUT;
-        dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
-        dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
-        dev->ep_in[ep].ifnum = 0;
-        dev->ep_out[ep].ifnum = 0;
-        dev->ep_in[ep].dev = dev;
-        dev->ep_out[ep].dev = dev;
-        dev->ep_in[ep].pipeline = false;
-        dev->ep_out[ep].pipeline = false;
-        QTAILQ_INIT(&dev->ep_in[ep].queue);
-        QTAILQ_INIT(&dev->ep_out[ep].queue);
-    }
-}
-
-void usb_ep_dump(USBDevice *dev)
-{
-    static const char *tname[] = {
-        [USB_ENDPOINT_XFER_CONTROL] = "control",
-        [USB_ENDPOINT_XFER_ISOC]    = "isoc",
-        [USB_ENDPOINT_XFER_BULK]    = "bulk",
-        [USB_ENDPOINT_XFER_INT]     = "int",
-    };
-    int ifnum, ep, first;
-
-    fprintf(stderr, "Device \"%s\", config %d\n",
-            dev->product_desc, dev->configuration);
-    for (ifnum = 0; ifnum < 16; ifnum++) {
-        first = 1;
-        for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
-            if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID &&
-                dev->ep_in[ep].ifnum == ifnum) {
-                if (first) {
-                    first = 0;
-                    fprintf(stderr, "  Interface %d, alternative %d\n",
-                            ifnum, dev->altsetting[ifnum]);
-                }
-                fprintf(stderr, "    Endpoint %d, IN, %s, %d max\n", ep,
-                        tname[dev->ep_in[ep].type],
-                        dev->ep_in[ep].max_packet_size);
-            }
-            if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID &&
-                dev->ep_out[ep].ifnum == ifnum) {
-                if (first) {
-                    first = 0;
-                    fprintf(stderr, "  Interface %d, alternative %d\n",
-                            ifnum, dev->altsetting[ifnum]);
-                }
-                fprintf(stderr, "    Endpoint %d, OUT, %s, %d max\n", ep,
-                        tname[dev->ep_out[ep].type],
-                        dev->ep_out[ep].max_packet_size);
-            }
-        }
-    }
-    fprintf(stderr, "--\n");
-}
-
-struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
-{
-    struct USBEndpoint *eps;
-
-    if (dev == NULL) {
-        return NULL;
-    }
-    eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out;
-    if (ep == 0) {
-        return &dev->ep_ctl;
-    }
-    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
-    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
-    return eps + ep - 1;
-}
-
-uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep)
-{
-    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
-    return uep->type;
-}
-
-void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type)
-{
-    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
-    uep->type = type;
-}
-
-uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep)
-{
-    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
-    return uep->ifnum;
-}
-
-void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum)
-{
-    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
-    uep->ifnum = ifnum;
-}
-
-void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
-                                uint16_t raw)
-{
-    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
-    int size, microframes;
-
-    size = raw & 0x7ff;
-    switch ((raw >> 11) & 3) {
-    case 1:
-        microframes = 2;
-        break;
-    case 2:
-        microframes = 3;
-        break;
-    default:
-        microframes = 1;
-        break;
-    }
-    uep->max_packet_size = size * microframes;
-}
-
-int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep)
-{
-    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
-    return uep->max_packet_size;
-}
-
-void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
-{
-    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
-    uep->pipeline = enabled;
-}
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
new file mode 100644 (file)
index 0000000..d3f8358
--- /dev/null
@@ -0,0 +1,584 @@
+#include "hw/hw.h"
+#include "hw/usb.h"
+#include "hw/qdev.h"
+#include "sysemu.h"
+#include "monitor.h"
+#include "trace.h"
+
+static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
+
+static char *usb_get_dev_path(DeviceState *dev);
+static char *usb_get_fw_dev_path(DeviceState *qdev);
+static int usb_qdev_exit(DeviceState *qdev);
+
+static struct BusInfo usb_bus_info = {
+    .name      = "USB",
+    .size      = sizeof(USBBus),
+    .print_dev = usb_bus_dev_print,
+    .get_dev_path = usb_get_dev_path,
+    .get_fw_dev_path = usb_get_fw_dev_path,
+    .props      = (Property[]) {
+        DEFINE_PROP_STRING("port", USBDevice, port_path),
+        DEFINE_PROP_END_OF_LIST()
+    },
+};
+static int next_usb_bus = 0;
+static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
+
+const VMStateDescription vmstate_usb_device = {
+    .name = "USBDevice",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT8(addr, USBDevice),
+        VMSTATE_INT32(state, USBDevice),
+        VMSTATE_INT32(remote_wakeup, USBDevice),
+        VMSTATE_INT32(setup_state, USBDevice),
+        VMSTATE_INT32(setup_len, USBDevice),
+        VMSTATE_INT32(setup_index, USBDevice),
+        VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host)
+{
+    qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
+    bus->ops = ops;
+    bus->busnr = next_usb_bus++;
+    bus->qbus.allow_hotplug = 1; /* Yes, we can */
+    QTAILQ_INIT(&bus->free);
+    QTAILQ_INIT(&bus->used);
+    QTAILQ_INSERT_TAIL(&busses, bus, next);
+}
+
+USBBus *usb_bus_find(int busnr)
+{
+    USBBus *bus;
+
+    if (-1 == busnr)
+        return QTAILQ_FIRST(&busses);
+    QTAILQ_FOREACH(bus, &busses, next) {
+        if (bus->busnr == busnr)
+            return bus;
+    }
+    return NULL;
+}
+
+static int usb_device_init(USBDevice *dev)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->init) {
+        return klass->init(dev);
+    }
+    return 0;
+}
+
+USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->find_device) {
+        return klass->find_device(dev, addr);
+    }
+    return NULL;
+}
+
+static void usb_device_handle_destroy(USBDevice *dev)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->handle_destroy) {
+        klass->handle_destroy(dev);
+    }
+}
+
+void usb_device_cancel_packet(USBDevice *dev, USBPacket *p)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->cancel_packet) {
+        klass->cancel_packet(dev, p);
+    }
+}
+
+void usb_device_handle_attach(USBDevice *dev)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->handle_attach) {
+        klass->handle_attach(dev);
+    }
+}
+
+void usb_device_handle_reset(USBDevice *dev)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->handle_reset) {
+        klass->handle_reset(dev);
+    }
+}
+
+int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
+                              int value, int index, int length, uint8_t *data)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->handle_control) {
+        return klass->handle_control(dev, p, request, value, index, length,
+                                         data);
+    }
+    return -ENOSYS;
+}
+
+int usb_device_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->handle_data) {
+        return klass->handle_data(dev, p);
+    }
+    return -ENOSYS;
+}
+
+const char *usb_device_get_product_desc(USBDevice *dev)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    return klass->product_desc;
+}
+
+const USBDesc *usb_device_get_usb_desc(USBDevice *dev)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    return klass->usb_desc;
+}
+
+void usb_device_set_interface(USBDevice *dev, int interface,
+                              int alt_old, int alt_new)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->set_interface) {
+        klass->set_interface(dev, interface, alt_old, alt_new);
+    }
+}
+
+static int usb_qdev_init(DeviceState *qdev)
+{
+    USBDevice *dev = USB_DEVICE(qdev);
+    int rc;
+
+    pstrcpy(dev->product_desc, sizeof(dev->product_desc),
+            usb_device_get_product_desc(dev));
+    dev->auto_attach = 1;
+    QLIST_INIT(&dev->strings);
+    usb_ep_init(dev);
+    rc = usb_claim_port(dev);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = usb_device_init(dev);
+    if (rc != 0) {
+        usb_release_port(dev);
+        return rc;
+    }
+    if (dev->auto_attach) {
+        rc = usb_device_attach(dev);
+        if (rc != 0) {
+            usb_qdev_exit(qdev);
+            return rc;
+        }
+    }
+    return 0;
+}
+
+static int usb_qdev_exit(DeviceState *qdev)
+{
+    USBDevice *dev = USB_DEVICE(qdev);
+
+    if (dev->attached) {
+        usb_device_detach(dev);
+    }
+    usb_device_handle_destroy(dev);
+    if (dev->port) {
+        usb_release_port(dev);
+    }
+    return 0;
+}
+
+typedef struct LegacyUSBFactory
+{
+    const char *name;
+    const char *usbdevice_name;
+    USBDevice *(*usbdevice_init)(USBBus *bus, const char *params);
+} LegacyUSBFactory;
+
+static GSList *legacy_usb_factory;
+
+void usb_legacy_register(const char *typename, const char *usbdevice_name,
+                         USBDevice *(*usbdevice_init)(USBBus *bus,
+                                                      const char *params))
+{
+    if (usbdevice_name) {
+        LegacyUSBFactory *f = g_malloc0(sizeof(*f));
+        f->name = typename;
+        f->usbdevice_name = usbdevice_name;
+        f->usbdevice_init = usbdevice_init;
+        legacy_usb_factory = g_slist_append(legacy_usb_factory, f);
+    }
+}
+
+USBDevice *usb_create(USBBus *bus, const char *name)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(&bus->qbus, name);
+    return USB_DEVICE(dev);
+}
+
+USBDevice *usb_create_simple(USBBus *bus, const char *name)
+{
+    USBDevice *dev = usb_create(bus, name);
+    int rc;
+
+    if (!dev) {
+        error_report("Failed to create USB device '%s'", name);
+        return NULL;
+    }
+    rc = qdev_init(&dev->qdev);
+    if (rc < 0) {
+        error_report("Failed to initialize USB device '%s'", name);
+        return NULL;
+    }
+    return dev;
+}
+
+static void usb_fill_port(USBPort *port, void *opaque, int index,
+                          USBPortOps *ops, int speedmask)
+{
+    port->opaque = opaque;
+    port->index = index;
+    port->ops = ops;
+    port->speedmask = speedmask;
+    usb_port_location(port, NULL, index + 1);
+}
+
+void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
+                       USBPortOps *ops, int speedmask)
+{
+    usb_fill_port(port, opaque, index, ops, speedmask);
+    QTAILQ_INSERT_TAIL(&bus->free, port, next);
+    bus->nfree++;
+}
+
+int usb_register_companion(const char *masterbus, USBPort *ports[],
+                           uint32_t portcount, uint32_t firstport,
+                           void *opaque, USBPortOps *ops, int speedmask)
+{
+    USBBus *bus;
+    int i;
+
+    QTAILQ_FOREACH(bus, &busses, next) {
+        if (strcmp(bus->qbus.name, masterbus) == 0) {
+            break;
+        }
+    }
+
+    if (!bus || !bus->ops->register_companion) {
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
+                      "an USB masterbus");
+        if (bus) {
+            error_printf_unless_qmp(
+                "USB bus '%s' does not allow companion controllers\n",
+                masterbus);
+        }
+        return -1;
+    }
+
+    for (i = 0; i < portcount; i++) {
+        usb_fill_port(ports[i], opaque, i, ops, speedmask);
+    }
+
+    return bus->ops->register_companion(bus, ports, portcount, firstport);
+}
+
+void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
+{
+    if (upstream) {
+        snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
+                 upstream->path, portnr);
+    } else {
+        snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
+    }
+}
+
+void usb_unregister_port(USBBus *bus, USBPort *port)
+{
+    if (port->dev)
+        qdev_free(&port->dev->qdev);
+    QTAILQ_REMOVE(&bus->free, port, next);
+    bus->nfree--;
+}
+
+int usb_claim_port(USBDevice *dev)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    USBPort *port;
+
+    assert(dev->port == NULL);
+
+    if (dev->port_path) {
+        QTAILQ_FOREACH(port, &bus->free, next) {
+            if (strcmp(port->path, dev->port_path) == 0) {
+                break;
+            }
+        }
+        if (port == NULL) {
+            error_report("Error: usb port %s (bus %s) not found (in use?)",
+                         dev->port_path, bus->qbus.name);
+            return -1;
+        }
+    } else {
+        if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) {
+            /* Create a new hub and chain it on */
+            usb_create_simple(bus, "usb-hub");
+        }
+        if (bus->nfree == 0) {
+            error_report("Error: tried to attach usb device %s to a bus "
+                         "with no free ports", dev->product_desc);
+            return -1;
+        }
+        port = QTAILQ_FIRST(&bus->free);
+    }
+    trace_usb_port_claim(bus->busnr, port->path);
+
+    QTAILQ_REMOVE(&bus->free, port, next);
+    bus->nfree--;
+
+    dev->port = port;
+    port->dev = dev;
+
+    QTAILQ_INSERT_TAIL(&bus->used, port, next);
+    bus->nused++;
+    return 0;
+}
+
+void usb_release_port(USBDevice *dev)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    USBPort *port = dev->port;
+
+    assert(port != NULL);
+    trace_usb_port_release(bus->busnr, port->path);
+
+    QTAILQ_REMOVE(&bus->used, port, next);
+    bus->nused--;
+
+    dev->port = NULL;
+    port->dev = NULL;
+
+    QTAILQ_INSERT_TAIL(&bus->free, port, next);
+    bus->nfree++;
+}
+
+int usb_device_attach(USBDevice *dev)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    USBPort *port = dev->port;
+
+    assert(port != NULL);
+    assert(!dev->attached);
+    trace_usb_port_attach(bus->busnr, port->path);
+
+    if (!(port->speedmask & dev->speedmask)) {
+        error_report("Warning: speed mismatch trying to attach "
+                     "usb device %s to bus %s",
+                     dev->product_desc, bus->qbus.name);
+        return -1;
+    }
+
+    dev->attached++;
+    usb_attach(port);
+
+    return 0;
+}
+
+int usb_device_detach(USBDevice *dev)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    USBPort *port = dev->port;
+
+    assert(port != NULL);
+    assert(dev->attached);
+    trace_usb_port_detach(bus->busnr, port->path);
+
+    usb_detach(port);
+    dev->attached--;
+    return 0;
+}
+
+int usb_device_delete_addr(int busnr, int addr)
+{
+    USBBus *bus;
+    USBPort *port;
+    USBDevice *dev;
+
+    bus = usb_bus_find(busnr);
+    if (!bus)
+        return -1;
+
+    QTAILQ_FOREACH(port, &bus->used, next) {
+        if (port->dev->addr == addr)
+            break;
+    }
+    if (!port)
+        return -1;
+    dev = port->dev;
+
+    qdev_free(&dev->qdev);
+    return 0;
+}
+
+static const char *usb_speed(unsigned int speed)
+{
+    static const char *txt[] = {
+        [ USB_SPEED_LOW  ] = "1.5",
+        [ USB_SPEED_FULL ] = "12",
+        [ USB_SPEED_HIGH ] = "480",
+        [ USB_SPEED_SUPER ] = "5000",
+    };
+    if (speed >= ARRAY_SIZE(txt))
+        return "?";
+    return txt[speed];
+}
+
+static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
+{
+    USBDevice *dev = USB_DEVICE(qdev);
+    USBBus *bus = usb_bus_from_device(dev);
+
+    monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
+                   indent, "", bus->busnr, dev->addr,
+                   dev->port ? dev->port->path : "-",
+                   usb_speed(dev->speed), dev->product_desc,
+                   dev->attached ? ", attached" : "");
+}
+
+static char *usb_get_dev_path(DeviceState *qdev)
+{
+    USBDevice *dev = USB_DEVICE(qdev);
+    return g_strdup(dev->port->path);
+}
+
+static char *usb_get_fw_dev_path(DeviceState *qdev)
+{
+    USBDevice *dev = USB_DEVICE(qdev);
+    char *fw_path, *in;
+    ssize_t pos = 0, fw_len;
+    long nr;
+
+    fw_len = 32 + strlen(dev->port->path) * 6;
+    fw_path = g_malloc(fw_len);
+    in = dev->port->path;
+    while (fw_len - pos > 0) {
+        nr = strtol(in, &in, 10);
+        if (in[0] == '.') {
+            /* some hub between root port and device */
+            pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
+            in++;
+        } else {
+            /* the device itself */
+            pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
+                            qdev_fw_name(qdev), nr);
+            break;
+        }
+    }
+    return fw_path;
+}
+
+void usb_info(Monitor *mon)
+{
+    USBBus *bus;
+    USBDevice *dev;
+    USBPort *port;
+
+    if (QTAILQ_EMPTY(&busses)) {
+        monitor_printf(mon, "USB support not enabled\n");
+        return;
+    }
+
+    QTAILQ_FOREACH(bus, &busses, next) {
+        QTAILQ_FOREACH(port, &bus->used, next) {
+            dev = port->dev;
+            if (!dev)
+                continue;
+            monitor_printf(mon, "  Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
+                           bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
+                           dev->product_desc);
+        }
+    }
+}
+
+/* handle legacy -usbdevice cmd line option */
+USBDevice *usbdevice_create(const char *cmdline)
+{
+    USBBus *bus = usb_bus_find(-1 /* any */);
+    LegacyUSBFactory *f = NULL;
+    GSList *i;
+    char driver[32];
+    const char *params;
+    int len;
+
+    params = strchr(cmdline,':');
+    if (params) {
+        params++;
+        len = params - cmdline;
+        if (len > sizeof(driver))
+            len = sizeof(driver);
+        pstrcpy(driver, len, cmdline);
+    } else {
+        params = "";
+        pstrcpy(driver, sizeof(driver), cmdline);
+    }
+
+    for (i = legacy_usb_factory; i; i = i->next) {
+        f = i->data;
+        if (strcmp(f->usbdevice_name, driver) == 0) {
+            break;
+        }
+    }
+    if (i == NULL) {
+#if 0
+        /* no error because some drivers are not converted (yet) */
+        error_report("usbdevice %s not found", driver);
+#endif
+        return NULL;
+    }
+
+    if (!f->usbdevice_init) {
+        if (*params) {
+            error_report("usbdevice %s accepts no params", driver);
+            return NULL;
+        }
+        return usb_create_simple(bus, f->name);
+    }
+    return f->usbdevice_init(bus, params);
+}
+
+static void usb_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->bus_info = &usb_bus_info;
+    k->init     = usb_qdev_init;
+    k->unplug   = qdev_simple_unplug_cb;
+    k->exit     = usb_qdev_exit;
+}
+
+static TypeInfo usb_device_type_info = {
+    .name = TYPE_USB_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(USBDevice),
+    .abstract = true,
+    .class_size = sizeof(USBDeviceClass),
+    .class_init = usb_device_class_init,
+};
+
+static void usb_register_types(void)
+{
+    type_register_static(&usb_device_type_info);
+}
+
+type_init(usb_register_types)
diff --git a/hw/usb/core.c b/hw/usb/core.c
new file mode 100644 (file)
index 0000000..494989a
--- /dev/null
@@ -0,0 +1,663 @@
+/*
+ * QEMU USB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * 2008 Generic packet handler rewrite by Max Krasnyansky
+ *
+ * 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 "iov.h"
+#include "trace.h"
+
+void usb_attach(USBPort *port)
+{
+    USBDevice *dev = port->dev;
+
+    assert(dev != NULL);
+    assert(dev->attached);
+    assert(dev->state == USB_STATE_NOTATTACHED);
+    port->ops->attach(port);
+    dev->state = USB_STATE_ATTACHED;
+    usb_device_handle_attach(dev);
+}
+
+void usb_detach(USBPort *port)
+{
+    USBDevice *dev = port->dev;
+
+    assert(dev != NULL);
+    assert(dev->state != USB_STATE_NOTATTACHED);
+    port->ops->detach(port);
+    dev->state = USB_STATE_NOTATTACHED;
+}
+
+void usb_port_reset(USBPort *port)
+{
+    USBDevice *dev = port->dev;
+
+    assert(dev != NULL);
+    usb_detach(port);
+    usb_attach(port);
+    usb_device_reset(dev);
+}
+
+void usb_device_reset(USBDevice *dev)
+{
+    if (dev == NULL || !dev->attached) {
+        return;
+    }
+    dev->remote_wakeup = 0;
+    dev->addr = 0;
+    dev->state = USB_STATE_DEFAULT;
+    usb_device_handle_reset(dev);
+}
+
+void usb_wakeup(USBEndpoint *ep)
+{
+    USBDevice *dev = ep->dev;
+    USBBus *bus = usb_bus_from_device(dev);
+
+    if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
+        dev->port->ops->wakeup(dev->port);
+    }
+    if (bus->ops->wakeup_endpoint) {
+        bus->ops->wakeup_endpoint(bus, ep);
+    }
+}
+
+/**********************/
+
+/* generic USB device helpers (you are not forced to use them when
+   writing your USB device driver, but they help handling the
+   protocol)
+*/
+
+#define SETUP_STATE_IDLE  0
+#define SETUP_STATE_SETUP 1
+#define SETUP_STATE_DATA  2
+#define SETUP_STATE_ACK   3
+#define SETUP_STATE_PARAM 4
+
+static int do_token_setup(USBDevice *s, USBPacket *p)
+{
+    int request, value, index;
+    int ret = 0;
+
+    if (p->iov.size != 8) {
+        return USB_RET_STALL;
+    }
+
+    usb_packet_copy(p, s->setup_buf, p->iov.size);
+    s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
+    s->setup_index = 0;
+
+    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
+    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
+
+    if (s->setup_buf[0] & USB_DIR_IN) {
+        ret = usb_device_handle_control(s, p, request, value, index,
+                                        s->setup_len, s->data_buf);
+        if (ret == USB_RET_ASYNC) {
+             s->setup_state = SETUP_STATE_SETUP;
+             return USB_RET_ASYNC;
+        }
+        if (ret < 0)
+            return ret;
+
+        if (ret < s->setup_len)
+            s->setup_len = ret;
+        s->setup_state = SETUP_STATE_DATA;
+    } else {
+        if (s->setup_len > sizeof(s->data_buf)) {
+            fprintf(stderr,
+                "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
+                s->setup_len, sizeof(s->data_buf));
+            return USB_RET_STALL;
+        }
+        if (s->setup_len == 0)
+            s->setup_state = SETUP_STATE_ACK;
+        else
+            s->setup_state = SETUP_STATE_DATA;
+    }
+
+    return ret;
+}
+
+static int do_token_in(USBDevice *s, USBPacket *p)
+{
+    int request, value, index;
+    int ret = 0;
+
+    assert(p->ep->nr == 0);
+
+    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
+    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
+    switch(s->setup_state) {
+    case SETUP_STATE_ACK:
+        if (!(s->setup_buf[0] & USB_DIR_IN)) {
+            ret = usb_device_handle_control(s, p, request, value, index,
+                                            s->setup_len, s->data_buf);
+            if (ret == USB_RET_ASYNC) {
+                return USB_RET_ASYNC;
+            }
+            s->setup_state = SETUP_STATE_IDLE;
+            if (ret > 0)
+                return 0;
+            return ret;
+        }
+
+        /* return 0 byte */
+        return 0;
+
+    case SETUP_STATE_DATA:
+        if (s->setup_buf[0] & USB_DIR_IN) {
+            int len = s->setup_len - s->setup_index;
+            if (len > p->iov.size) {
+                len = p->iov.size;
+            }
+            usb_packet_copy(p, s->data_buf + s->setup_index, len);
+            s->setup_index += len;
+            if (s->setup_index >= s->setup_len)
+                s->setup_state = SETUP_STATE_ACK;
+            return len;
+        }
+
+        s->setup_state = SETUP_STATE_IDLE;
+        return USB_RET_STALL;
+
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+static int do_token_out(USBDevice *s, USBPacket *p)
+{
+    assert(p->ep->nr == 0);
+
+    switch(s->setup_state) {
+    case SETUP_STATE_ACK:
+        if (s->setup_buf[0] & USB_DIR_IN) {
+            s->setup_state = SETUP_STATE_IDLE;
+            /* transfer OK */
+        } else {
+            /* ignore additional output */
+        }
+        return 0;
+
+    case SETUP_STATE_DATA:
+        if (!(s->setup_buf[0] & USB_DIR_IN)) {
+            int len = s->setup_len - s->setup_index;
+            if (len > p->iov.size) {
+                len = p->iov.size;
+            }
+            usb_packet_copy(p, s->data_buf + s->setup_index, len);
+            s->setup_index += len;
+            if (s->setup_index >= s->setup_len)
+                s->setup_state = SETUP_STATE_ACK;
+            return len;
+        }
+
+        s->setup_state = SETUP_STATE_IDLE;
+        return USB_RET_STALL;
+
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+static int do_parameter(USBDevice *s, USBPacket *p)
+{
+    int request, value, index;
+    int i, ret = 0;
+
+    for (i = 0; i < 8; i++) {
+        s->setup_buf[i] = p->parameter >> (i*8);
+    }
+
+    s->setup_state = SETUP_STATE_PARAM;
+    s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
+    s->setup_index = 0;
+
+    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
+    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
+
+    if (s->setup_len > sizeof(s->data_buf)) {
+        fprintf(stderr,
+                "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
+                s->setup_len, sizeof(s->data_buf));
+        return USB_RET_STALL;
+    }
+
+    if (p->pid == USB_TOKEN_OUT) {
+        usb_packet_copy(p, s->data_buf, s->setup_len);
+    }
+
+    ret = usb_device_handle_control(s, p, request, value, index,
+                                    s->setup_len, s->data_buf);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (ret < s->setup_len) {
+        s->setup_len = ret;
+    }
+    if (p->pid == USB_TOKEN_IN) {
+        usb_packet_copy(p, s->data_buf, s->setup_len);
+    }
+
+    return ret;
+}
+
+/* ctrl complete function for devices which use usb_generic_handle_packet and
+   may return USB_RET_ASYNC from their handle_control callback. Device code
+   which does this *must* call this function instead of the normal
+   usb_packet_complete to complete their async control packets. */
+void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
+{
+    if (p->result < 0) {
+        s->setup_state = SETUP_STATE_IDLE;
+    }
+
+    switch (s->setup_state) {
+    case SETUP_STATE_SETUP:
+        if (p->result < s->setup_len) {
+            s->setup_len = p->result;
+        }
+        s->setup_state = SETUP_STATE_DATA;
+        p->result = 8;
+        break;
+
+    case SETUP_STATE_ACK:
+        s->setup_state = SETUP_STATE_IDLE;
+        p->result = 0;
+        break;
+
+    case SETUP_STATE_PARAM:
+        if (p->result < s->setup_len) {
+            s->setup_len = p->result;
+        }
+        if (p->pid == USB_TOKEN_IN) {
+            p->result = 0;
+            usb_packet_copy(p, s->data_buf, s->setup_len);
+        }
+        break;
+
+    default:
+        break;
+    }
+    usb_packet_complete(s, p);
+}
+
+/* XXX: fix overflow */
+int set_usb_string(uint8_t *buf, const char *str)
+{
+    int len, i;
+    uint8_t *q;
+
+    q = buf;
+    len = strlen(str);
+    *q++ = 2 * len + 2;
+    *q++ = 3;
+    for(i = 0; i < len; i++) {
+        *q++ = str[i];
+        *q++ = 0;
+    }
+    return q - buf;
+}
+
+USBDevice *usb_find_device(USBPort *port, uint8_t addr)
+{
+    USBDevice *dev = port->dev;
+
+    if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) {
+        return NULL;
+    }
+    if (dev->addr == addr) {
+        return dev;
+    }
+    return usb_device_find_device(dev, addr);
+}
+
+static int usb_process_one(USBPacket *p)
+{
+    USBDevice *dev = p->ep->dev;
+
+    if (p->ep->nr == 0) {
+        /* control pipe */
+        if (p->parameter) {
+            return do_parameter(dev, p);
+        }
+        switch (p->pid) {
+        case USB_TOKEN_SETUP:
+            return do_token_setup(dev, p);
+        case USB_TOKEN_IN:
+            return do_token_in(dev, p);
+        case USB_TOKEN_OUT:
+            return do_token_out(dev, p);
+        default:
+            return USB_RET_STALL;
+        }
+    } else {
+        /* data pipe */
+        return usb_device_handle_data(dev, p);
+    }
+}
+
+/* Hand over a packet to a device for processing.  Return value
+   USB_RET_ASYNC indicates the processing isn't finished yet, the
+   driver will call usb_packet_complete() when done processing it. */
+int usb_handle_packet(USBDevice *dev, USBPacket *p)
+{
+    int ret;
+
+    if (dev == NULL) {
+        return USB_RET_NODEV;
+    }
+    assert(dev == p->ep->dev);
+    assert(dev->state == USB_STATE_DEFAULT);
+    assert(p->state == USB_PACKET_SETUP);
+    assert(p->ep != NULL);
+
+    if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
+        ret = usb_process_one(p);
+        if (ret == USB_RET_ASYNC) {
+            usb_packet_set_state(p, USB_PACKET_ASYNC);
+            QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
+        } else {
+            p->result = ret;
+            usb_packet_set_state(p, USB_PACKET_COMPLETE);
+        }
+    } else {
+        ret = USB_RET_ASYNC;
+        usb_packet_set_state(p, USB_PACKET_QUEUED);
+        QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
+    }
+    return ret;
+}
+
+/* Notify the controller that an async packet is complete.  This should only
+   be called for packets previously deferred by returning USB_RET_ASYNC from
+   handle_packet. */
+void usb_packet_complete(USBDevice *dev, USBPacket *p)
+{
+    USBEndpoint *ep = p->ep;
+    int ret;
+
+    assert(p->state == USB_PACKET_ASYNC);
+    assert(QTAILQ_FIRST(&ep->queue) == p);
+    usb_packet_set_state(p, USB_PACKET_COMPLETE);
+    QTAILQ_REMOVE(&ep->queue, p, queue);
+    dev->port->ops->complete(dev->port, p);
+
+    while (!QTAILQ_EMPTY(&ep->queue)) {
+        p = QTAILQ_FIRST(&ep->queue);
+        if (p->state == USB_PACKET_ASYNC) {
+            break;
+        }
+        assert(p->state == USB_PACKET_QUEUED);
+        ret = usb_process_one(p);
+        if (ret == USB_RET_ASYNC) {
+            usb_packet_set_state(p, USB_PACKET_ASYNC);
+            break;
+        }
+        p->result = ret;
+        usb_packet_set_state(p, USB_PACKET_COMPLETE);
+        QTAILQ_REMOVE(&ep->queue, p, queue);
+        dev->port->ops->complete(dev->port, p);
+    }
+}
+
+/* Cancel an active packet.  The packed must have been deferred by
+   returning USB_RET_ASYNC from handle_packet, and not yet
+   completed.  */
+void usb_cancel_packet(USBPacket * p)
+{
+    bool callback = (p->state == USB_PACKET_ASYNC);
+    assert(usb_packet_is_inflight(p));
+    usb_packet_set_state(p, USB_PACKET_CANCELED);
+    QTAILQ_REMOVE(&p->ep->queue, p, queue);
+    if (callback) {
+        usb_device_cancel_packet(p->ep->dev, p);
+    }
+}
+
+
+void usb_packet_init(USBPacket *p)
+{
+    qemu_iovec_init(&p->iov, 1);
+}
+
+void usb_packet_set_state(USBPacket *p, USBPacketState state)
+{
+    static const char *name[] = {
+        [USB_PACKET_UNDEFINED] = "undef",
+        [USB_PACKET_SETUP]     = "setup",
+        [USB_PACKET_QUEUED]    = "queued",
+        [USB_PACKET_ASYNC]     = "async",
+        [USB_PACKET_COMPLETE]  = "complete",
+        [USB_PACKET_CANCELED]  = "canceled",
+    };
+    USBDevice *dev = p->ep->dev;
+    USBBus *bus = usb_bus_from_device(dev);
+
+    trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr,
+                                  p, name[p->state], name[state]);
+    p->state = state;
+}
+
+void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
+{
+    assert(!usb_packet_is_inflight(p));
+    p->pid = pid;
+    p->ep = ep;
+    p->result = 0;
+    p->parameter = 0;
+    qemu_iovec_reset(&p->iov);
+    usb_packet_set_state(p, USB_PACKET_SETUP);
+}
+
+void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
+{
+    qemu_iovec_add(&p->iov, ptr, len);
+}
+
+void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
+{
+    assert(p->result >= 0);
+    assert(p->result + bytes <= p->iov.size);
+    switch (p->pid) {
+    case USB_TOKEN_SETUP:
+    case USB_TOKEN_OUT:
+        iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
+        break;
+    case USB_TOKEN_IN:
+        iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
+        break;
+    default:
+        fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
+        abort();
+    }
+    p->result += bytes;
+}
+
+void usb_packet_skip(USBPacket *p, size_t bytes)
+{
+    assert(p->result >= 0);
+    assert(p->result + bytes <= p->iov.size);
+    if (p->pid == USB_TOKEN_IN) {
+        iov_clear(p->iov.iov, p->iov.niov, p->result, bytes);
+    }
+    p->result += bytes;
+}
+
+void usb_packet_cleanup(USBPacket *p)
+{
+    assert(!usb_packet_is_inflight(p));
+    qemu_iovec_destroy(&p->iov);
+}
+
+void usb_ep_init(USBDevice *dev)
+{
+    int ep;
+
+    dev->ep_ctl.nr = 0;
+    dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
+    dev->ep_ctl.ifnum = 0;
+    dev->ep_ctl.dev = dev;
+    dev->ep_ctl.pipeline = false;
+    QTAILQ_INIT(&dev->ep_ctl.queue);
+    for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
+        dev->ep_in[ep].nr = ep + 1;
+        dev->ep_out[ep].nr = ep + 1;
+        dev->ep_in[ep].pid = USB_TOKEN_IN;
+        dev->ep_out[ep].pid = USB_TOKEN_OUT;
+        dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
+        dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
+        dev->ep_in[ep].ifnum = 0;
+        dev->ep_out[ep].ifnum = 0;
+        dev->ep_in[ep].dev = dev;
+        dev->ep_out[ep].dev = dev;
+        dev->ep_in[ep].pipeline = false;
+        dev->ep_out[ep].pipeline = false;
+        QTAILQ_INIT(&dev->ep_in[ep].queue);
+        QTAILQ_INIT(&dev->ep_out[ep].queue);
+    }
+}
+
+void usb_ep_dump(USBDevice *dev)
+{
+    static const char *tname[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = "control",
+        [USB_ENDPOINT_XFER_ISOC]    = "isoc",
+        [USB_ENDPOINT_XFER_BULK]    = "bulk",
+        [USB_ENDPOINT_XFER_INT]     = "int",
+    };
+    int ifnum, ep, first;
+
+    fprintf(stderr, "Device \"%s\", config %d\n",
+            dev->product_desc, dev->configuration);
+    for (ifnum = 0; ifnum < 16; ifnum++) {
+        first = 1;
+        for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
+            if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID &&
+                dev->ep_in[ep].ifnum == ifnum) {
+                if (first) {
+                    first = 0;
+                    fprintf(stderr, "  Interface %d, alternative %d\n",
+                            ifnum, dev->altsetting[ifnum]);
+                }
+                fprintf(stderr, "    Endpoint %d, IN, %s, %d max\n", ep,
+                        tname[dev->ep_in[ep].type],
+                        dev->ep_in[ep].max_packet_size);
+            }
+            if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID &&
+                dev->ep_out[ep].ifnum == ifnum) {
+                if (first) {
+                    first = 0;
+                    fprintf(stderr, "  Interface %d, alternative %d\n",
+                            ifnum, dev->altsetting[ifnum]);
+                }
+                fprintf(stderr, "    Endpoint %d, OUT, %s, %d max\n", ep,
+                        tname[dev->ep_out[ep].type],
+                        dev->ep_out[ep].max_packet_size);
+            }
+        }
+    }
+    fprintf(stderr, "--\n");
+}
+
+struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *eps;
+
+    if (dev == NULL) {
+        return NULL;
+    }
+    eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out;
+    if (ep == 0) {
+        return &dev->ep_ctl;
+    }
+    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
+    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
+    return eps + ep - 1;
+}
+
+uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    return uep->type;
+}
+
+void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    uep->type = type;
+}
+
+uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    return uep->ifnum;
+}
+
+void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    uep->ifnum = ifnum;
+}
+
+void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
+                                uint16_t raw)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    int size, microframes;
+
+    size = raw & 0x7ff;
+    switch ((raw >> 11) & 3) {
+    case 1:
+        microframes = 2;
+        break;
+    case 2:
+        microframes = 3;
+        break;
+    default:
+        microframes = 1;
+        break;
+    }
+    uep->max_packet_size = size * microframes;
+}
+
+int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    return uep->max_packet_size;
+}
+
+void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    uep->pipeline = enabled;
+}
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
new file mode 100644 (file)
index 0000000..9847a75
--- /dev/null
@@ -0,0 +1,601 @@
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "trace.h"
+
+/* ------------------------------------------------------------------ */
+
+static uint8_t usb_lo(uint16_t val)
+{
+    return val & 0xff;
+}
+
+static uint8_t usb_hi(uint16_t val)
+{
+    return (val >> 8) & 0xff;
+}
+
+int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
+                    uint8_t *dest, size_t len)
+{
+    uint8_t bLength = 0x12;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_DEVICE;
+
+    dest[0x02] = usb_lo(dev->bcdUSB);
+    dest[0x03] = usb_hi(dev->bcdUSB);
+    dest[0x04] = dev->bDeviceClass;
+    dest[0x05] = dev->bDeviceSubClass;
+    dest[0x06] = dev->bDeviceProtocol;
+    dest[0x07] = dev->bMaxPacketSize0;
+
+    dest[0x08] = usb_lo(id->idVendor);
+    dest[0x09] = usb_hi(id->idVendor);
+    dest[0x0a] = usb_lo(id->idProduct);
+    dest[0x0b] = usb_hi(id->idProduct);
+    dest[0x0c] = usb_lo(id->bcdDevice);
+    dest[0x0d] = usb_hi(id->bcdDevice);
+    dest[0x0e] = id->iManufacturer;
+    dest[0x0f] = id->iProduct;
+    dest[0x10] = id->iSerialNumber;
+
+    dest[0x11] = dev->bNumConfigurations;
+
+    return bLength;
+}
+
+int usb_desc_device_qualifier(const USBDescDevice *dev,
+                              uint8_t *dest, size_t len)
+{
+    uint8_t bLength = 0x0a;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_DEVICE_QUALIFIER;
+
+    dest[0x02] = usb_lo(dev->bcdUSB);
+    dest[0x03] = usb_hi(dev->bcdUSB);
+    dest[0x04] = dev->bDeviceClass;
+    dest[0x05] = dev->bDeviceSubClass;
+    dest[0x06] = dev->bDeviceProtocol;
+    dest[0x07] = dev->bMaxPacketSize0;
+    dest[0x08] = dev->bNumConfigurations;
+    dest[0x09] = 0; /* reserved */
+
+    return bLength;
+}
+
+int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
+{
+    uint8_t  bLength = 0x09;
+    uint16_t wTotalLength = 0;
+    int i, rc;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_CONFIG;
+    dest[0x04] = conf->bNumInterfaces;
+    dest[0x05] = conf->bConfigurationValue;
+    dest[0x06] = conf->iConfiguration;
+    dest[0x07] = conf->bmAttributes;
+    dest[0x08] = conf->bMaxPower;
+    wTotalLength += bLength;
+
+    /* handle grouped interfaces if any*/
+    for (i = 0; i < conf->nif_groups; i++) {
+        rc = usb_desc_iface_group(&(conf->if_groups[i]),
+                                  dest + wTotalLength,
+                                  len - wTotalLength);
+        if (rc < 0) {
+            return rc;
+        }
+        wTotalLength += rc;
+    }
+
+    /* handle normal (ungrouped / no IAD) interfaces if any */
+    for (i = 0; i < conf->nif; i++) {
+        rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
+        if (rc < 0) {
+            return rc;
+        }
+        wTotalLength += rc;
+    }
+
+    dest[0x02] = usb_lo(wTotalLength);
+    dest[0x03] = usb_hi(wTotalLength);
+    return wTotalLength;
+}
+
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+                         size_t len)
+{
+    int pos = 0;
+    int i = 0;
+
+    /* handle interface association descriptor */
+    uint8_t bLength = 0x08;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_INTERFACE_ASSOC;
+    dest[0x02] = iad->bFirstInterface;
+    dest[0x03] = iad->bInterfaceCount;
+    dest[0x04] = iad->bFunctionClass;
+    dest[0x05] = iad->bFunctionSubClass;
+    dest[0x06] = iad->bFunctionProtocol;
+    dest[0x07] = iad->iFunction;
+    pos += bLength;
+
+    /* handle associated interfaces in this group */
+    for (i = 0; i < iad->nif; i++) {
+        int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
+        if (rc < 0) {
+            return rc;
+        }
+        pos += rc;
+    }
+
+    return pos;
+}
+
+int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
+{
+    uint8_t bLength = 0x09;
+    int i, rc, pos = 0;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_INTERFACE;
+    dest[0x02] = iface->bInterfaceNumber;
+    dest[0x03] = iface->bAlternateSetting;
+    dest[0x04] = iface->bNumEndpoints;
+    dest[0x05] = iface->bInterfaceClass;
+    dest[0x06] = iface->bInterfaceSubClass;
+    dest[0x07] = iface->bInterfaceProtocol;
+    dest[0x08] = iface->iInterface;
+    pos += bLength;
+
+    for (i = 0; i < iface->ndesc; i++) {
+        rc = usb_desc_other(iface->descs + i, dest + pos, len - pos);
+        if (rc < 0) {
+            return rc;
+        }
+        pos += rc;
+    }
+
+    for (i = 0; i < iface->bNumEndpoints; i++) {
+        rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
+        if (rc < 0) {
+            return rc;
+        }
+        pos += rc;
+    }
+
+    return pos;
+}
+
+int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
+{
+    uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
+    uint8_t extralen = ep->extra ? ep->extra[0] : 0;
+
+    if (len < bLength + extralen) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_ENDPOINT;
+    dest[0x02] = ep->bEndpointAddress;
+    dest[0x03] = ep->bmAttributes;
+    dest[0x04] = usb_lo(ep->wMaxPacketSize);
+    dest[0x05] = usb_hi(ep->wMaxPacketSize);
+    dest[0x06] = ep->bInterval;
+    if (ep->is_audio) {
+        dest[0x07] = ep->bRefresh;
+        dest[0x08] = ep->bSynchAddress;
+    }
+    if (ep->extra) {
+        memcpy(dest + bLength, ep->extra, extralen);
+    }
+
+    return bLength + extralen;
+}
+
+int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
+{
+    int bLength = desc->length ? desc->length : desc->data[0];
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    memcpy(dest, desc->data, bLength);
+    return bLength;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void usb_desc_ep_init(USBDevice *dev)
+{
+    const USBDescIface *iface;
+    int i, e, pid, ep;
+
+    usb_ep_init(dev);
+    for (i = 0; i < dev->ninterfaces; i++) {
+        iface = dev->ifaces[i];
+        if (iface == NULL) {
+            continue;
+        }
+        for (e = 0; e < iface->bNumEndpoints; e++) {
+            pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
+                USB_TOKEN_IN : USB_TOKEN_OUT;
+            ep = iface->eps[e].bEndpointAddress & 0x0f;
+            usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
+            usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
+            usb_ep_set_max_packet_size(dev, pid, ep,
+                                       iface->eps[e].wMaxPacketSize);
+        }
+    }
+}
+
+static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
+                                                   int nif, int alt)
+{
+    const USBDescIface *iface;
+    int g, i;
+
+    if (!dev->config) {
+        return NULL;
+    }
+    for (g = 0; g < dev->config->nif_groups; g++) {
+        for (i = 0; i < dev->config->if_groups[g].nif; i++) {
+            iface = &dev->config->if_groups[g].ifs[i];
+            if (iface->bInterfaceNumber == nif &&
+                iface->bAlternateSetting == alt) {
+                return iface;
+            }
+        }
+    }
+    for (i = 0; i < dev->config->nif; i++) {
+        iface = &dev->config->ifs[i];
+        if (iface->bInterfaceNumber == nif &&
+            iface->bAlternateSetting == alt) {
+            return iface;
+        }
+    }
+    return NULL;
+}
+
+static int usb_desc_set_interface(USBDevice *dev, int index, int value)
+{
+    const USBDescIface *iface;
+    int old;
+
+    iface = usb_desc_find_interface(dev, index, value);
+    if (iface == NULL) {
+        return -1;
+    }
+
+    old = dev->altsetting[index];
+    dev->altsetting[index] = value;
+    dev->ifaces[index] = iface;
+    usb_desc_ep_init(dev);
+
+    if (old != value) {
+        usb_device_set_interface(dev, index, old, value);
+    }
+    return 0;
+}
+
+static int usb_desc_set_config(USBDevice *dev, int value)
+{
+    int i;
+
+    if (value == 0) {
+        dev->configuration = 0;
+        dev->ninterfaces   = 0;
+        dev->config = NULL;
+    } else {
+        for (i = 0; i < dev->device->bNumConfigurations; i++) {
+            if (dev->device->confs[i].bConfigurationValue == value) {
+                dev->configuration = value;
+                dev->ninterfaces   = dev->device->confs[i].bNumInterfaces;
+                dev->config = dev->device->confs + i;
+                assert(dev->ninterfaces <= USB_MAX_INTERFACES);
+            }
+        }
+        if (i < dev->device->bNumConfigurations) {
+            return -1;
+        }
+    }
+
+    for (i = 0; i < dev->ninterfaces; i++) {
+        usb_desc_set_interface(dev, i, 0);
+    }
+    for (; i < USB_MAX_INTERFACES; i++) {
+        dev->altsetting[i] = 0;
+        dev->ifaces[i] = NULL;
+    }
+
+    return 0;
+}
+
+static void usb_desc_setdefaults(USBDevice *dev)
+{
+    const USBDesc *desc = usb_device_get_usb_desc(dev);
+
+    assert(desc != NULL);
+    switch (dev->speed) {
+    case USB_SPEED_LOW:
+    case USB_SPEED_FULL:
+        dev->device = desc->full;
+        break;
+    case USB_SPEED_HIGH:
+        dev->device = desc->high;
+        break;
+    }
+    usb_desc_set_config(dev, 0);
+}
+
+void usb_desc_init(USBDevice *dev)
+{
+    const USBDesc *desc = usb_device_get_usb_desc(dev);
+
+    assert(desc != NULL);
+    dev->speed = USB_SPEED_FULL;
+    dev->speedmask = 0;
+    if (desc->full) {
+        dev->speedmask |= USB_SPEED_MASK_FULL;
+    }
+    if (desc->high) {
+        dev->speedmask |= USB_SPEED_MASK_HIGH;
+    }
+    usb_desc_setdefaults(dev);
+}
+
+void usb_desc_attach(USBDevice *dev)
+{
+    const USBDesc *desc = usb_device_get_usb_desc(dev);
+
+    assert(desc != NULL);
+    if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
+        dev->speed = USB_SPEED_HIGH;
+    } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
+        dev->speed = USB_SPEED_FULL;
+    } else {
+        fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n",
+                usb_device_get_product_desc(dev));
+        return;
+    }
+    usb_desc_setdefaults(dev);
+}
+
+void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str)
+{
+    USBDescString *s;
+
+    QLIST_FOREACH(s, &dev->strings, next) {
+        if (s->index == index) {
+            break;
+        }
+    }
+    if (s == NULL) {
+        s = g_malloc0(sizeof(*s));
+        s->index = index;
+        QLIST_INSERT_HEAD(&dev->strings, s, next);
+    }
+    g_free(s->str);
+    s->str = g_strdup(str);
+}
+
+const char *usb_desc_get_string(USBDevice *dev, uint8_t index)
+{
+    USBDescString *s;
+
+    QLIST_FOREACH(s, &dev->strings, next) {
+        if (s->index == index) {
+            return s->str;
+        }
+    }
+    return NULL;
+}
+
+int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
+{
+    uint8_t bLength, pos, i;
+    const char *str;
+
+    if (len < 4) {
+        return -1;
+    }
+
+    if (index == 0) {
+        /* language ids */
+        dest[0] = 4;
+        dest[1] = USB_DT_STRING;
+        dest[2] = 0x09;
+        dest[3] = 0x04;
+        return 4;
+    }
+
+    str = usb_desc_get_string(dev, index);
+    if (str == NULL) {
+        str = usb_device_get_usb_desc(dev)->str[index];
+        if (str == NULL) {
+            return 0;
+        }
+    }
+
+    bLength = strlen(str) * 2 + 2;
+    dest[0] = bLength;
+    dest[1] = USB_DT_STRING;
+    i = 0; pos = 2;
+    while (pos+1 < bLength && pos+1 < len) {
+        dest[pos++] = str[i++];
+        dest[pos++] = 0;
+    }
+    return pos;
+}
+
+int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len)
+{
+    const USBDesc *desc = usb_device_get_usb_desc(dev);
+    const USBDescDevice *other_dev;
+    uint8_t buf[256];
+    uint8_t type = value >> 8;
+    uint8_t index = value & 0xff;
+    int ret = -1;
+
+    if (dev->speed == USB_SPEED_HIGH) {
+        other_dev = usb_device_get_usb_desc(dev)->full;
+    } else {
+        other_dev = usb_device_get_usb_desc(dev)->high;
+    }
+
+    switch(type) {
+    case USB_DT_DEVICE:
+        ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
+        trace_usb_desc_device(dev->addr, len, ret);
+        break;
+    case USB_DT_CONFIG:
+        if (index < dev->device->bNumConfigurations) {
+            ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
+        }
+        trace_usb_desc_config(dev->addr, index, len, ret);
+        break;
+    case USB_DT_STRING:
+        ret = usb_desc_string(dev, index, buf, sizeof(buf));
+        trace_usb_desc_string(dev->addr, index, len, ret);
+        break;
+
+    case USB_DT_DEVICE_QUALIFIER:
+        if (other_dev != NULL) {
+            ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
+        }
+        trace_usb_desc_device_qualifier(dev->addr, len, ret);
+        break;
+    case USB_DT_OTHER_SPEED_CONFIG:
+        if (other_dev != NULL && index < other_dev->bNumConfigurations) {
+            ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
+            buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
+        }
+        trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
+        break;
+
+    case USB_DT_DEBUG:
+        /* ignore silently */
+        break;
+
+    default:
+        fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
+                dev->addr, type, len);
+        break;
+    }
+
+    if (ret > 0) {
+        if (ret > len) {
+            ret = len;
+        }
+        memcpy(dest, buf, ret);
+    }
+    return ret;
+}
+
+int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
+        int request, int value, int index, int length, uint8_t *data)
+{
+    const USBDesc *desc = usb_device_get_usb_desc(dev);
+    int ret = -1;
+
+    assert(desc != NULL);
+    switch(request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        trace_usb_set_addr(dev->addr);
+        ret = 0;
+        break;
+
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        ret = usb_desc_get_descriptor(dev, value, data, length);
+        break;
+
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        /*
+         * 9.4.2: 0 should be returned if the device is unconfigured, otherwise
+         * the non zero value of bConfigurationValue.
+         */
+        data[0] = dev->config ? dev->config->bConfigurationValue : 0;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        ret = usb_desc_set_config(dev, value);
+        trace_usb_set_config(dev->addr, value, ret);
+        break;
+
+    case DeviceRequest | USB_REQ_GET_STATUS: {
+        const USBDescConfig *config = dev->config ?
+            dev->config : &dev->device->confs[0];
+
+        data[0] = 0;
+        /*
+         * Default state: Device behavior when this request is received while
+         *                the device is in the Default state is not specified.
+         * We return the same value that a configured device would return if
+         * it used the first configuration.
+         */
+        if (config->bmAttributes & 0x40) {
+            data[0] |= 1 << USB_DEVICE_SELF_POWERED;
+        }
+        if (dev->remote_wakeup) {
+            data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+        }
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    }
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 0;
+            ret = 0;
+        }
+        trace_usb_clear_device_feature(dev->addr, value, ret);
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 1;
+            ret = 0;
+        }
+        trace_usb_set_device_feature(dev->addr, value, ret);
+        break;
+
+    case InterfaceRequest | USB_REQ_GET_INTERFACE:
+        if (index < 0 || index >= dev->ninterfaces) {
+            break;
+        }
+        data[0] = dev->altsetting[index];
+        ret = 1;
+        break;
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = usb_desc_set_interface(dev, index, value);
+        trace_usb_set_interface(dev->addr, index, value, ret);
+        break;
+
+    }
+    return ret;
+}
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
new file mode 100644 (file)
index 0000000..d6e07ea
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef QEMU_HW_USB_DESC_H
+#define QEMU_HW_USB_DESC_H
+
+#include <inttypes.h>
+
+struct USBDescID {
+    uint16_t                  idVendor;
+    uint16_t                  idProduct;
+    uint16_t                  bcdDevice;
+    uint8_t                   iManufacturer;
+    uint8_t                   iProduct;
+    uint8_t                   iSerialNumber;
+};
+
+struct USBDescDevice {
+    uint16_t                  bcdUSB;
+    uint8_t                   bDeviceClass;
+    uint8_t                   bDeviceSubClass;
+    uint8_t                   bDeviceProtocol;
+    uint8_t                   bMaxPacketSize0;
+    uint8_t                   bNumConfigurations;
+
+    const USBDescConfig       *confs;
+};
+
+struct USBDescConfig {
+    uint8_t                   bNumInterfaces;
+    uint8_t                   bConfigurationValue;
+    uint8_t                   iConfiguration;
+    uint8_t                   bmAttributes;
+    uint8_t                   bMaxPower;
+
+    /* grouped interfaces */
+    uint8_t                   nif_groups;
+    const USBDescIfaceAssoc   *if_groups;
+
+    /* "normal" interfaces */
+    uint8_t                   nif;
+    const USBDescIface        *ifs;
+};
+
+/* conceptually an Interface Association Descriptor, and releated interfaces */
+struct USBDescIfaceAssoc {
+    uint8_t                   bFirstInterface;
+    uint8_t                   bInterfaceCount;
+    uint8_t                   bFunctionClass;
+    uint8_t                   bFunctionSubClass;
+    uint8_t                   bFunctionProtocol;
+    uint8_t                   iFunction;
+
+    uint8_t                   nif;
+    const USBDescIface        *ifs;
+};
+
+struct USBDescIface {
+    uint8_t                   bInterfaceNumber;
+    uint8_t                   bAlternateSetting;
+    uint8_t                   bNumEndpoints;
+    uint8_t                   bInterfaceClass;
+    uint8_t                   bInterfaceSubClass;
+    uint8_t                   bInterfaceProtocol;
+    uint8_t                   iInterface;
+
+    uint8_t                   ndesc;
+    USBDescOther              *descs;
+    USBDescEndpoint           *eps;
+};
+
+struct USBDescEndpoint {
+    uint8_t                   bEndpointAddress;
+    uint8_t                   bmAttributes;
+    uint16_t                  wMaxPacketSize;
+    uint8_t                   bInterval;
+    uint8_t                   bRefresh;
+    uint8_t                   bSynchAddress;
+
+    uint8_t                   is_audio; /* has bRefresh + bSynchAddress */
+    uint8_t                   *extra;
+};
+
+struct USBDescOther {
+    uint8_t                   length;
+    const uint8_t             *data;
+};
+
+typedef const char *USBDescStrings[256];
+
+struct USBDesc {
+    USBDescID                 id;
+    const USBDescDevice       *full;
+    const USBDescDevice       *high;
+    const char* const         *str;
+};
+
+/* generate usb packages from structs */
+int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
+                    uint8_t *dest, size_t len);
+int usb_desc_device_qualifier(const USBDescDevice *dev,
+                              uint8_t *dest, size_t len);
+int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+                         size_t len);
+int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
+int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
+int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
+
+/* control message emulation helpers */
+void usb_desc_init(USBDevice *dev);
+void usb_desc_attach(USBDevice *dev);
+void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
+const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
+int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
+int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
+int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
+        int request, int value, int index, int length, uint8_t *data);
+
+#endif /* QEMU_HW_USB_DESC_H */
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
new file mode 100644 (file)
index 0000000..426b95c
--- /dev/null
@@ -0,0 +1,714 @@
+/*
+ * QEMU USB audio device
+ *
+ * written by:
+ *  H. Peter Anvin <hpa@linux.intel.com>
+ *  Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * lousely based on usb net device code which is:
+ *
+ * Copyright (c) 2006 Thomas Sailer
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * 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 "hw/usb/desc.h"
+#include "hw/hw.h"
+#include "hw/audiodev.h"
+#include "audio/audio.h"
+
+#define USBAUDIO_VENDOR_NUM     0x46f4 /* CRC16() of "QEMU" */
+#define USBAUDIO_PRODUCT_NUM    0x0002
+
+#define DEV_CONFIG_VALUE        1 /* The one and only */
+
+/* Descriptor subtypes for AC interfaces */
+#define DST_AC_HEADER           1
+#define DST_AC_INPUT_TERMINAL   2
+#define DST_AC_OUTPUT_TERMINAL  3
+#define DST_AC_FEATURE_UNIT     6
+/* Descriptor subtypes for AS interfaces */
+#define DST_AS_GENERAL          1
+#define DST_AS_FORMAT_TYPE      2
+/* Descriptor subtypes for endpoints */
+#define DST_EP_GENERAL          1
+
+enum usb_audio_strings {
+    STRING_NULL,
+    STRING_MANUFACTURER,
+    STRING_PRODUCT,
+    STRING_SERIALNUMBER,
+    STRING_CONFIG,
+    STRING_USBAUDIO_CONTROL,
+    STRING_INPUT_TERMINAL,
+    STRING_FEATURE_UNIT,
+    STRING_OUTPUT_TERMINAL,
+    STRING_NULL_STREAM,
+    STRING_REAL_STREAM,
+};
+
+static const USBDescStrings usb_audio_stringtable = {
+    [STRING_MANUFACTURER]       = "QEMU",
+    [STRING_PRODUCT]            = "QEMU USB Audio",
+    [STRING_SERIALNUMBER]       = "1",
+    [STRING_CONFIG]             = "Audio Configuration",
+    [STRING_USBAUDIO_CONTROL]   = "Audio Device",
+    [STRING_INPUT_TERMINAL]     = "Audio Output Pipe",
+    [STRING_FEATURE_UNIT]       = "Audio Output Volume Control",
+    [STRING_OUTPUT_TERMINAL]    = "Audio Output Terminal",
+    [STRING_NULL_STREAM]        = "Audio Output - Disabled",
+    [STRING_REAL_STREAM]        = "Audio Output - 48 kHz Stereo",
+};
+
+#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff)
+#define U24(x) U16(x), (((x) >> 16) & 0xff)
+#define U32(x) U24(x), (((x) >> 24) & 0xff)
+
+/*
+ * A Basic Audio Device uses these specific values
+ */
+#define USBAUDIO_PACKET_SIZE     192
+#define USBAUDIO_SAMPLE_RATE     48000
+#define USBAUDIO_PACKET_INTERVAL 1
+
+static const USBDescIface desc_iface[] = {
+    {
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_CONTROL,
+        .bInterfaceProtocol            = 0x04,
+        .iInterface                    = STRING_USBAUDIO_CONTROL,
+        .ndesc                         = 4,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-Specific AC Interface Header Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_HEADER,              /*  u8  bDescriptorSubtype */
+                    U16(0x0100),                /* u16  bcdADC */
+                    U16(0x2b),                  /* u16  wTotalLength */
+                    0x01,                       /*  u8  bInCollection */
+                    0x01,                       /*  u8  baInterfaceNr */
+                }
+            },{
+                /* Generic Stereo Input Terminal ID1 Descriptor */
+                .data = (uint8_t[]) {
+                    0x0c,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_INPUT_TERMINAL,      /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalID */
+                    U16(0x0101),                /* u16  wTerminalType */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x02,                       /* u16  bNrChannels */
+                    U16(0x0003),                /* u16  wChannelConfig */
+                    0x00,                       /*  u8  iChannelNames */
+                    STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
+                }
+            },{
+                /* Generic Stereo Feature Unit ID2 Descriptor */
+                .data = (uint8_t[]) {
+                    0x0d,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_FEATURE_UNIT,        /*  u8  bDescriptorSubtype */
+                    0x02,                       /*  u8  bUnitID */
+                    0x01,                       /*  u8  bSourceID */
+                    0x02,                       /*  u8  bControlSize */
+                    U16(0x0001),                /* u16  bmaControls(0) */
+                    U16(0x0002),                /* u16  bmaControls(1) */
+                    U16(0x0002),                /* u16  bmaControls(2) */
+                    STRING_FEATURE_UNIT,        /*  u8  iFeature */
+                }
+            },{
+                /* Headphone Ouptut Terminal ID3 Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_OUTPUT_TERMINAL,     /*  u8  bDescriptorSubtype */
+                    0x03,                       /*  u8  bUnitID */
+                    U16(0x0301),                /* u16  wTerminalType (SPK) */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x02,                       /*  u8  bSourceID */
+                    STRING_OUTPUT_TERMINAL,     /*  u8  iTerminal */
+                }
+            }
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_NULL_STREAM,
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 1,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_REAL_STREAM,
+        .ndesc                         = 2,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-specific AS General Interface Descriptor */
+                .data = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalLink */
+                    0x00,                       /*  u8  bDelay */
+                    0x01, 0x00,                 /* u16  wFormatTag */
+                }
+            },{
+                /* Headphone Type I Format Type Descriptor */
+                .data = (uint8_t[]) {
+                    0x0b,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bFormatType */
+                    0x02,                       /*  u8  bNrChannels */
+                    0x02,                       /*  u8  bSubFrameSize */
+                    0x10,                       /*  u8  bBitResolution */
+                    0x01,                       /*  u8  bSamFreqType */
+                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
+                }
+            }
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | 0x01,
+                .bmAttributes          = 0x0d,
+                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE,
+                .bInterval             = 1,
+                .is_audio              = 1,
+                /* Stereo Headphone Class-specific
+                   AS Audio Data Endpoint Descriptor */
+                .extra = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
+                    DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x00,                       /*  u8  bmAttributes */
+                    0x00,                       /*  u8  bLockDelayUnits */
+                    U16(0x0000),                /* u16  wLockDelay */
+                },
+            },
+        }
+    }
+};
+
+static const USBDescDevice desc_device = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = DEV_CONFIG_VALUE,
+            .iConfiguration        = STRING_CONFIG,
+            .bmAttributes          = 0xc0,
+            .bMaxPower             = 0x32,
+            .nif = ARRAY_SIZE(desc_iface),
+            .ifs = desc_iface,
+        },
+    },
+};
+
+static const USBDesc desc_audio = {
+    .id = {
+        .idVendor          = USBAUDIO_VENDOR_NUM,
+        .idProduct         = USBAUDIO_PRODUCT_NUM,
+        .bcdDevice         = 0,
+        .iManufacturer     = STRING_MANUFACTURER,
+        .iProduct          = STRING_PRODUCT,
+        .iSerialNumber     = STRING_SERIALNUMBER,
+    },
+    .full = &desc_device,
+    .str  = usb_audio_stringtable,
+};
+
+/*
+ * A USB audio device supports an arbitrary number of alternate
+ * interface settings for each interface.  Each corresponds to a block
+ * diagram of parameterized blocks.  This can thus refer to things like
+ * number of channels, data rates, or in fact completely different
+ * block diagrams.  Alternative setting 0 is always the null block diagram,
+ * which is used by a disabled device.
+ */
+enum usb_audio_altset {
+    ALTSET_OFF  = 0x00,         /* No endpoint */
+    ALTSET_ON   = 0x01,         /* Single endpoint */
+};
+
+/*
+ * Class-specific control requests
+ */
+#define CR_SET_CUR      0x01
+#define CR_GET_CUR      0x81
+#define CR_SET_MIN      0x02
+#define CR_GET_MIN      0x82
+#define CR_SET_MAX      0x03
+#define CR_GET_MAX      0x83
+#define CR_SET_RES      0x04
+#define CR_GET_RES      0x84
+#define CR_SET_MEM      0x05
+#define CR_GET_MEM      0x85
+#define CR_GET_STAT     0xff
+
+/*
+ * Feature Unit Control Selectors
+ */
+#define MUTE_CONTROL                    0x01
+#define VOLUME_CONTROL                  0x02
+#define BASS_CONTROL                    0x03
+#define MID_CONTROL                     0x04
+#define TREBLE_CONTROL                  0x05
+#define GRAPHIC_EQUALIZER_CONTROL       0x06
+#define AUTOMATIC_GAIN_CONTROL          0x07
+#define DELAY_CONTROL                   0x08
+#define BASS_BOOST_CONTROL              0x09
+#define LOUDNESS_CONTROL                0x0a
+
+/*
+ * buffering
+ */
+
+struct streambuf {
+    uint8_t *data;
+    uint32_t size;
+    uint32_t prod;
+    uint32_t cons;
+};
+
+static void streambuf_init(struct streambuf *buf, uint32_t size)
+{
+    g_free(buf->data);
+    buf->size = size - (size % USBAUDIO_PACKET_SIZE);
+    buf->data = g_malloc(buf->size);
+    buf->prod = 0;
+    buf->cons = 0;
+}
+
+static void streambuf_fini(struct streambuf *buf)
+{
+    g_free(buf->data);
+    buf->data = NULL;
+}
+
+static int streambuf_put(struct streambuf *buf, USBPacket *p)
+{
+    uint32_t free = buf->size - (buf->prod - buf->cons);
+
+    if (!free) {
+        return 0;
+    }
+    assert(free >= USBAUDIO_PACKET_SIZE);
+    usb_packet_copy(p, buf->data + (buf->prod % buf->size),
+                    USBAUDIO_PACKET_SIZE);
+    buf->prod += USBAUDIO_PACKET_SIZE;
+    return USBAUDIO_PACKET_SIZE;
+}
+
+static uint8_t *streambuf_get(struct streambuf *buf)
+{
+    uint32_t used = buf->prod - buf->cons;
+    uint8_t *data;
+
+    if (!used) {
+        return NULL;
+    }
+    assert(used >= USBAUDIO_PACKET_SIZE);
+    data = buf->data + (buf->cons % buf->size);
+    buf->cons += USBAUDIO_PACKET_SIZE;
+    return data;
+}
+
+typedef struct USBAudioState {
+    /* qemu interfaces */
+    USBDevice dev;
+    QEMUSoundCard card;
+
+    /* state */
+    struct {
+        enum usb_audio_altset altset;
+        struct audsettings as;
+        SWVoiceOut *voice;
+        bool mute;
+        uint8_t vol[2];
+        struct streambuf buf;
+    } out;
+
+    /* properties */
+    uint32_t debug;
+    uint32_t buffer;
+} USBAudioState;
+
+static void output_callback(void *opaque, int avail)
+{
+    USBAudioState *s = opaque;
+    uint8_t *data;
+
+    for (;;) {
+        if (avail < USBAUDIO_PACKET_SIZE) {
+            return;
+        }
+        data = streambuf_get(&s->out.buf);
+        if (NULL == data) {
+            return;
+        }
+        AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE);
+        avail -= USBAUDIO_PACKET_SIZE;
+    }
+}
+
+static int usb_audio_set_output_altset(USBAudioState *s, int altset)
+{
+    switch (altset) {
+    case ALTSET_OFF:
+        streambuf_init(&s->out.buf, s->buffer);
+        AUD_set_active_out(s->out.voice, false);
+        break;
+    case ALTSET_ON:
+        AUD_set_active_out(s->out.voice, true);
+        break;
+    default:
+        return -1;
+    }
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: set interface %d\n", altset);
+    }
+    s->out.altset = altset;
+    return 0;
+}
+
+/*
+ * Note: we arbitrarily map the volume control range onto -inf..+8 dB
+ */
+#define ATTRIB_ID(cs, attrib, idif)     \
+    (((cs) << 24) | ((attrib) << 16) | (idif))
+
+static int usb_audio_get_control(USBAudioState *s, uint8_t attrib,
+                                 uint16_t cscn, uint16_t idif,
+                                 int length, uint8_t *data)
+{
+    uint8_t cs = cscn >> 8;
+    uint8_t cn = cscn - 1;      /* -1 for the non-present master control */
+    uint32_t aid = ATTRIB_ID(cs, attrib, idif);
+    int ret = USB_RET_STALL;
+
+    switch (aid) {
+    case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200):
+        data[0] = s->out.mute;
+        ret = 1;
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
+        if (cn < 2) {
+            uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
+            data[0] = vol;
+            data[1] = vol >> 8;
+            ret = 2;
+        }
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200):
+        if (cn < 2) {
+            data[0] = 0x01;
+            data[1] = 0x80;
+            ret = 2;
+        }
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200):
+        if (cn < 2) {
+            data[0] = 0x00;
+            data[1] = 0x08;
+            ret = 2;
+        }
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200):
+        if (cn < 2) {
+            data[0] = 0x88;
+            data[1] = 0x00;
+            ret = 2;
+        }
+        break;
+    }
+
+    return ret;
+}
+static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
+                                 uint16_t cscn, uint16_t idif,
+                                 int length, uint8_t *data)
+{
+    uint8_t cs = cscn >> 8;
+    uint8_t cn = cscn - 1;      /* -1 for the non-present master control */
+    uint32_t aid = ATTRIB_ID(cs, attrib, idif);
+    int ret = USB_RET_STALL;
+    bool set_vol = false;
+
+    switch (aid) {
+    case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200):
+        s->out.mute = data[0] & 1;
+        set_vol = true;
+        ret = 0;
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200):
+        if (cn < 2) {
+            uint16_t vol = data[0] + (data[1] << 8);
+
+            if (s->debug) {
+                fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
+            }
+
+            vol -= 0x8000;
+            vol = (vol * 255 + 0x4400) / 0x8800;
+            if (vol > 255) {
+                vol = 255;
+            }
+
+            s->out.vol[cn] = vol;
+            set_vol = true;
+            ret = 0;
+        }
+        break;
+    }
+
+    if (set_vol) {
+        if (s->debug) {
+            fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
+                    s->out.mute, s->out.vol[0], s->out.vol[1]);
+        }
+        AUD_set_volume_out(s->out.voice, s->out.mute,
+                           s->out.vol[0], s->out.vol[1]);
+    }
+
+    return ret;
+}
+
+static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
+                                    int request, int value, int index,
+                                    int length, uint8_t *data)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+    int ret = 0;
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: control transaction: "
+                "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
+                request, value, index, length);
+    }
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    switch (request) {
+    case ClassInterfaceRequest | CR_GET_CUR:
+    case ClassInterfaceRequest | CR_GET_MIN:
+    case ClassInterfaceRequest | CR_GET_MAX:
+    case ClassInterfaceRequest | CR_GET_RES:
+        ret = usb_audio_get_control(s, request & 0xff, value, index,
+                                    length, data);
+        if (ret < 0) {
+            if (s->debug) {
+                fprintf(stderr, "usb-audio: fail: get control\n");
+            }
+            goto fail;
+        }
+        break;
+
+    case ClassInterfaceOutRequest | CR_SET_CUR:
+    case ClassInterfaceOutRequest | CR_SET_MIN:
+    case ClassInterfaceOutRequest | CR_SET_MAX:
+    case ClassInterfaceOutRequest | CR_SET_RES:
+        ret = usb_audio_set_control(s, request & 0xff, value, index,
+                                    length, data);
+        if (ret < 0) {
+            if (s->debug) {
+                fprintf(stderr, "usb-audio: fail: set control\n");
+            }
+            goto fail;
+        }
+        break;
+
+    default:
+fail:
+        if (s->debug) {
+            fprintf(stderr, "usb-audio: failed control transaction: "
+                    "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
+                    request, value, index, length);
+        }
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_audio_set_interface(USBDevice *dev, int iface,
+                                    int old, int value)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    if (iface == 1) {
+        usb_audio_set_output_altset(s, value);
+    }
+}
+
+static void usb_audio_handle_reset(USBDevice *dev)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: reset\n");
+    }
+    usb_audio_set_output_altset(s, ALTSET_OFF);
+}
+
+static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
+{
+    int rc;
+
+    if (s->out.altset == ALTSET_OFF) {
+        return USB_RET_STALL;
+    }
+
+    rc = streambuf_put(&s->out.buf, p);
+    if (rc < p->iov.size && s->debug > 1) {
+        fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
+                p->iov.size - rc);
+    }
+
+    return 0;
+}
+
+static int usb_audio_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBAudioState *s = (USBAudioState *) dev;
+    int ret = 0;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        switch (p->ep->nr) {
+        case 1:
+            ret = usb_audio_handle_dataout(s, p);
+            break;
+        default:
+            goto fail;
+        }
+        break;
+
+    default:
+fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    if (ret == USB_RET_STALL && s->debug) {
+        fprintf(stderr, "usb-audio: failed data transaction: "
+                        "pid 0x%x ep 0x%x len 0x%zx\n",
+                        p->pid, p->ep->nr, p->iov.size);
+    }
+    return ret;
+}
+
+static void usb_audio_handle_destroy(USBDevice *dev)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: destroy\n");
+    }
+
+    usb_audio_set_output_altset(s, ALTSET_OFF);
+    AUD_close_out(&s->card, s->out.voice);
+    AUD_remove_card(&s->card);
+
+    streambuf_fini(&s->out.buf);
+}
+
+static int usb_audio_initfn(USBDevice *dev)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    usb_desc_init(dev);
+    s->dev.opaque = s;
+    AUD_register_card("usb-audio", &s->card);
+
+    s->out.altset        = ALTSET_OFF;
+    s->out.mute          = false;
+    s->out.vol[0]        = 240; /* 0 dB */
+    s->out.vol[1]        = 240; /* 0 dB */
+    s->out.as.freq       = USBAUDIO_SAMPLE_RATE;
+    s->out.as.nchannels  = 2;
+    s->out.as.fmt        = AUD_FMT_S16;
+    s->out.as.endianness = 0;
+    streambuf_init(&s->out.buf, s->buffer);
+
+    s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio",
+                                s, output_callback, &s->out.as);
+    AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
+    AUD_set_active_out(s->out.voice, 0);
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_audio = {
+    .name = "usb-audio",
+    .unmigratable = 1,
+};
+
+static Property usb_audio_properties[] = {
+    DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
+    DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
+                       8 * USBAUDIO_PACKET_SIZE),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_audio_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *k = USB_DEVICE_CLASS(klass);
+
+    dc->vmsd          = &vmstate_usb_audio;
+    dc->props         = usb_audio_properties;
+    k->product_desc   = "QEMU USB Audio Interface";
+    k->usb_desc       = &desc_audio;
+    k->init           = usb_audio_initfn;
+    k->handle_reset   = usb_audio_handle_reset;
+    k->handle_control = usb_audio_handle_control;
+    k->handle_data    = usb_audio_handle_data;
+    k->handle_destroy = usb_audio_handle_destroy;
+    k->set_interface  = usb_audio_set_interface;
+}
+
+static TypeInfo usb_audio_info = {
+    .name          = "usb-audio",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBAudioState),
+    .class_init    = usb_audio_class_init,
+};
+
+static void usb_audio_register_types(void)
+{
+    type_register_static(&usb_audio_info);
+    usb_legacy_register("usb-audio", "audio", NULL);
+}
+
+type_init(usb_audio_register_types)
diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c
new file mode 100644 (file)
index 0000000..195370c
--- /dev/null
@@ -0,0 +1,557 @@
+/*
+ * QEMU Bluetooth HCI USB Transport Layer v1.0
+ *
+ * Copyright (C) 2007 OpenMoko, Inc.
+ * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "net.h"
+#include "hw/bt.h"
+
+struct USBBtState {
+    USBDevice dev;
+    struct HCIInfo *hci;
+
+    int config;
+
+#define CFIFO_LEN_MASK 255
+#define DFIFO_LEN_MASK 4095
+    struct usb_hci_in_fifo_s {
+        uint8_t data[(DFIFO_LEN_MASK + 1) * 2];
+        struct {
+            uint8_t *data;
+            int len;
+        } fifo[CFIFO_LEN_MASK + 1];
+        int dstart, dlen, dsize, start, len;
+    } evt, acl, sco;
+
+    struct usb_hci_out_fifo_s {
+        uint8_t data[4096];
+       int len;
+    } outcmd, outacl, outsco;
+};
+
+#define USB_EVT_EP     1
+#define USB_ACL_EP     2
+#define USB_SCO_EP     3
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
+    [STR_SERIALNUMBER]     = "1",
+};
+
+static const USBDescIface desc_iface_bluetooth[] = {
+    {
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 3,
+        .bInterfaceClass               = 0xe0, /* Wireless */
+        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
+        .bInterfaceProtocol            = 0x01, /* Bluetooth */
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_EVT_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_INT,
+                .wMaxPacketSize        = 0x10,
+                .bInterval             = 0x02,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_OUT | USB_ACL_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+                .wMaxPacketSize        = 0x40,
+                .bInterval             = 0x0a,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_ACL_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+                .wMaxPacketSize        = 0x40,
+                .bInterval             = 0x0a,
+            },
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 0,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = 0xe0, /* Wireless */
+        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
+        .bInterfaceProtocol            = 0x01, /* Bluetooth */
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0,
+                .bInterval             = 0x01,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0,
+                .bInterval             = 0x01,
+            },
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 1,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = 0xe0, /* Wireless */
+        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
+        .bInterfaceProtocol            = 0x01, /* Bluetooth */
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x09,
+                .bInterval             = 0x01,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x09,
+                .bInterval             = 0x01,
+            },
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 2,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = 0xe0, /* Wireless */
+        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
+        .bInterfaceProtocol            = 0x01, /* Bluetooth */
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x11,
+                .bInterval             = 0x01,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x11,
+                .bInterval             = 0x01,
+            },
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 3,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = 0xe0, /* Wireless */
+        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
+        .bInterfaceProtocol            = 0x01, /* Bluetooth */
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x19,
+                .bInterval             = 0x01,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x19,
+                .bInterval             = 0x01,
+            },
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 4,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = 0xe0, /* Wireless */
+        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
+        .bInterfaceProtocol            = 0x01, /* Bluetooth */
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x21,
+                .bInterval             = 0x01,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x21,
+                .bInterval             = 0x01,
+            },
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 5,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = 0xe0, /* Wireless */
+        .bInterfaceSubClass            = 0x01, /* Radio Frequency */
+        .bInterfaceProtocol            = 0x01, /* Bluetooth */
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x31,
+                .bInterval             = 0x01,
+            },
+            {
+                .bEndpointAddress      = USB_DIR_IN | USB_SCO_EP,
+                .bmAttributes          = USB_ENDPOINT_XFER_ISOC,
+                .wMaxPacketSize        = 0x31,
+                .bInterval             = 0x01,
+            },
+        },
+    }
+};
+
+static const USBDescDevice desc_device_bluetooth = {
+    .bcdUSB                        = 0x0110,
+    .bDeviceClass                  = 0xe0, /* Wireless */
+    .bDeviceSubClass               = 0x01, /* Radio Frequency */
+    .bDeviceProtocol               = 0x01, /* Bluetooth */
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = 1,
+            .bmAttributes          = 0xc0,
+            .bMaxPower             = 0,
+            .nif = ARRAY_SIZE(desc_iface_bluetooth),
+            .ifs = desc_iface_bluetooth,
+        },
+    },
+};
+
+static const USBDesc desc_bluetooth = {
+    .id = {
+        .idVendor          = 0x0a12,
+        .idProduct         = 0x0001,
+        .bcdDevice         = 0x1958,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = 0,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_bluetooth,
+    .str  = desc_strings,
+};
+
+static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo)
+{
+    fifo->dstart = 0;
+    fifo->dlen = 0;
+    fifo->dsize = DFIFO_LEN_MASK + 1;
+    fifo->start = 0;
+    fifo->len = 0;
+}
+
+static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo,
+                const uint8_t *data, int len)
+{
+    int off = fifo->dstart + fifo->dlen;
+    uint8_t *buf;
+
+    fifo->dlen += len;
+    if (off <= DFIFO_LEN_MASK) {
+        if (off + len > DFIFO_LEN_MASK + 1 &&
+                        (fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) {
+            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+            exit(-1);
+        }
+        buf = fifo->data + off;
+    } else {
+        if (fifo->dlen > fifo->dsize) {
+            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+            exit(-1);
+        }
+        buf = fifo->data + off - fifo->dsize;
+    }
+
+    off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK;
+    fifo->fifo[off].data = memcpy(buf, data, len);
+    fifo->fifo[off].len = len;
+}
+
+static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
+                USBPacket *p)
+{
+    int len;
+
+    if (likely(!fifo->len))
+        return USB_RET_STALL;
+
+    len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
+    usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
+    if (len == p->iov.size) {
+        fifo->fifo[fifo->start].len -= len;
+        fifo->fifo[fifo->start].data += len;
+    } else {
+        fifo->start ++;
+        fifo->start &= CFIFO_LEN_MASK;
+        fifo->len --;
+    }
+
+    fifo->dstart += len;
+    fifo->dlen -= len;
+    if (fifo->dstart >= fifo->dsize) {
+        fifo->dstart = 0;
+        fifo->dsize = DFIFO_LEN_MASK + 1;
+    }
+
+    return len;
+}
+
+static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
+                struct usb_hci_out_fifo_s *fifo,
+                void (*send)(struct HCIInfo *, const uint8_t *, int),
+                int (*complete)(const uint8_t *, int),
+                USBPacket *p)
+{
+    usb_packet_copy(p, fifo->data + fifo->len, p->iov.size);
+    fifo->len += p->iov.size;
+    if (complete(fifo->data, fifo->len)) {
+        send(s->hci, fifo->data, fifo->len);
+        fifo->len = 0;
+    }
+
+    /* TODO: do we need to loop? */
+}
+
+static int usb_bt_hci_cmd_complete(const uint8_t *data, int len)
+{
+    len -= HCI_COMMAND_HDR_SIZE;
+    return len >= 0 &&
+            len >= ((struct hci_command_hdr *) data)->plen;
+}
+
+static int usb_bt_hci_acl_complete(const uint8_t *data, int len)
+{
+    len -= HCI_ACL_HDR_SIZE;
+    return len >= 0 &&
+            len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen);
+}
+
+static int usb_bt_hci_sco_complete(const uint8_t *data, int len)
+{
+    len -= HCI_SCO_HDR_SIZE;
+    return len >= 0 &&
+            len >= ((struct hci_sco_hdr *) data)->dlen;
+}
+
+static void usb_bt_handle_reset(USBDevice *dev)
+{
+    struct USBBtState *s = (struct USBBtState *) dev->opaque;
+
+    usb_bt_fifo_reset(&s->evt);
+    usb_bt_fifo_reset(&s->acl);
+    usb_bt_fifo_reset(&s->sco);
+    s->outcmd.len = 0;
+    s->outacl.len = 0;
+    s->outsco.len = 0;
+}
+
+static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    struct USBBtState *s = (struct USBBtState *) dev->opaque;
+    int ret;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        switch (request) {
+        case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+            s->config = 0;
+            break;
+        case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+            s->config = 1;
+            usb_bt_fifo_reset(&s->evt);
+            usb_bt_fifo_reset(&s->acl);
+            usb_bt_fifo_reset(&s->sco);
+            break;
+        }
+        return ret;
+    }
+
+    ret = 0;
+    switch (request) {
+    case InterfaceRequest | USB_REQ_GET_STATUS:
+    case EndpointRequest | USB_REQ_GET_STATUS:
+        data[0] = 0x00;
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        goto fail;
+    case InterfaceOutRequest | USB_REQ_SET_FEATURE:
+    case EndpointOutRequest | USB_REQ_SET_FEATURE:
+        goto fail;
+        break;
+    case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
+        if (s->config)
+            usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
+                            usb_bt_hci_cmd_complete, p);
+        break;
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
+{
+    struct USBBtState *s = (struct USBBtState *) dev->opaque;
+    int ret = 0;
+
+    if (!s->config)
+        goto fail;
+
+    switch (p->pid) {
+    case USB_TOKEN_IN:
+        switch (p->ep->nr) {
+        case USB_EVT_EP:
+            ret = usb_bt_fifo_dequeue(&s->evt, p);
+            break;
+
+        case USB_ACL_EP:
+            ret = usb_bt_fifo_dequeue(&s->acl, p);
+            break;
+
+        case USB_SCO_EP:
+            ret = usb_bt_fifo_dequeue(&s->sco, p);
+            break;
+
+        default:
+            goto fail;
+        }
+        break;
+
+    case USB_TOKEN_OUT:
+        switch (p->ep->nr) {
+        case USB_ACL_EP:
+            usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
+                            usb_bt_hci_acl_complete, p);
+            break;
+
+        case USB_SCO_EP:
+            usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send,
+                            usb_bt_hci_sco_complete, p);
+            break;
+
+        default:
+            goto fail;
+        }
+        break;
+
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void usb_bt_out_hci_packet_event(void *opaque,
+                const uint8_t *data, int len)
+{
+    struct USBBtState *s = (struct USBBtState *) opaque;
+
+    usb_bt_fifo_enqueue(&s->evt, data, len);
+}
+
+static void usb_bt_out_hci_packet_acl(void *opaque,
+                const uint8_t *data, int len)
+{
+    struct USBBtState *s = (struct USBBtState *) opaque;
+
+    usb_bt_fifo_enqueue(&s->acl, data, len);
+}
+
+static void usb_bt_handle_destroy(USBDevice *dev)
+{
+    struct USBBtState *s = (struct USBBtState *) dev->opaque;
+
+    s->hci->opaque = NULL;
+    s->hci->evt_recv = NULL;
+    s->hci->acl_recv = NULL;
+}
+
+static int usb_bt_initfn(USBDevice *dev)
+{
+    usb_desc_init(dev);
+    return 0;
+}
+
+USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci)
+{
+    USBDevice *dev;
+    struct USBBtState *s;
+
+    if (!hci)
+        return NULL;
+    dev = usb_create_simple(bus, "usb-bt-dongle");
+    if (!dev) {
+        return NULL;
+    }
+    s = DO_UPCAST(struct USBBtState, dev, dev);
+    s->dev.opaque = s;
+
+    s->hci = hci;
+    s->hci->opaque = s;
+    s->hci->evt_recv = usb_bt_out_hci_packet_event;
+    s->hci->acl_recv = usb_bt_out_hci_packet_acl;
+
+    usb_bt_handle_reset(&s->dev);
+
+    return dev;
+}
+
+static const VMStateDescription vmstate_usb_bt = {
+    .name = "usb-bt",
+    .unmigratable = 1,
+};
+
+static void usb_bt_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_bt_initfn;
+    uc->product_desc   = "QEMU BT dongle";
+    uc->usb_desc       = &desc_bluetooth;
+    uc->handle_reset   = usb_bt_handle_reset;
+    uc->handle_control = usb_bt_handle_control;
+    uc->handle_data    = usb_bt_handle_data;
+    uc->handle_destroy = usb_bt_handle_destroy;
+    dc->vmsd = &vmstate_usb_bt;
+}
+
+static TypeInfo bt_info = {
+    .name          = "usb-bt-dongle",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(struct USBBtState),
+    .class_init    = usb_bt_class_initfn,
+};
+
+static void usb_bt_register_types(void)
+{
+    type_register_static(&bt_info);
+}
+
+type_init(usb_bt_register_types)
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
new file mode 100644 (file)
index 0000000..f29544d
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ * QEMU USB HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
+ *
+ * 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 "hw/hw.h"
+#include "console.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "qemu-timer.h"
+#include "hw/hid.h"
+
+/* HID interface requests */
+#define GET_REPORT   0xa101
+#define GET_IDLE     0xa102
+#define GET_PROTOCOL 0xa103
+#define SET_REPORT   0x2109
+#define SET_IDLE     0x210a
+#define SET_PROTOCOL 0x210b
+
+/* HID descriptor types */
+#define USB_DT_HID    0x21
+#define USB_DT_REPORT 0x22
+#define USB_DT_PHY    0x23
+
+typedef struct USBHIDState {
+    USBDevice dev;
+    USBEndpoint *intr;
+    HIDState hid;
+} USBHIDState;
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT_MOUSE,
+    STR_PRODUCT_TABLET,
+    STR_PRODUCT_KEYBOARD,
+    STR_SERIALNUMBER,
+    STR_CONFIG_MOUSE,
+    STR_CONFIG_TABLET,
+    STR_CONFIG_KEYBOARD,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
+    [STR_PRODUCT_MOUSE]    = "QEMU USB Mouse",
+    [STR_PRODUCT_TABLET]   = "QEMU USB Tablet",
+    [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard",
+    [STR_SERIALNUMBER]     = "42", /* == remote wakeup works */
+    [STR_CONFIG_MOUSE]     = "HID Mouse",
+    [STR_CONFIG_TABLET]    = "HID Tablet",
+    [STR_CONFIG_KEYBOARD]  = "HID Keyboard",
+};
+
+static const USBDescIface desc_iface_mouse = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 1,
+    .bInterfaceClass               = USB_CLASS_HID,
+    .bInterfaceSubClass            = 0x01, /* boot */
+    .bInterfaceProtocol            = 0x02,
+    .ndesc                         = 1,
+    .descs = (USBDescOther[]) {
+        {
+            /* HID descriptor */
+            .data = (uint8_t[]) {
+                0x09,          /*  u8  bLength */
+                USB_DT_HID,    /*  u8  bDescriptorType */
+                0x01, 0x00,    /*  u16 HID_class */
+                0x00,          /*  u8  country_code */
+                0x01,          /*  u8  num_descriptors */
+                USB_DT_REPORT, /*  u8  type: Report */
+                52, 0,         /*  u16 len */
+            },
+        },
+    },
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .wMaxPacketSize        = 4,
+            .bInterval             = 0x0a,
+        },
+    },
+};
+
+static const USBDescIface desc_iface_tablet = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 1,
+    .bInterfaceClass               = USB_CLASS_HID,
+    .bInterfaceProtocol            = 0x02,
+    .ndesc                         = 1,
+    .descs = (USBDescOther[]) {
+        {
+            /* HID descriptor */
+            .data = (uint8_t[]) {
+                0x09,          /*  u8  bLength */
+                USB_DT_HID,    /*  u8  bDescriptorType */
+                0x01, 0x00,    /*  u16 HID_class */
+                0x00,          /*  u8  country_code */
+                0x01,          /*  u8  num_descriptors */
+                USB_DT_REPORT, /*  u8  type: Report */
+                74, 0,         /*  u16 len */
+            },
+        },
+    },
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .wMaxPacketSize        = 8,
+            .bInterval             = 0x0a,
+        },
+    },
+};
+
+static const USBDescIface desc_iface_keyboard = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 1,
+    .bInterfaceClass               = USB_CLASS_HID,
+    .bInterfaceSubClass            = 0x01, /* boot */
+    .bInterfaceProtocol            = 0x01, /* keyboard */
+    .ndesc                         = 1,
+    .descs = (USBDescOther[]) {
+        {
+            /* HID descriptor */
+            .data = (uint8_t[]) {
+                0x09,          /*  u8  bLength */
+                USB_DT_HID,    /*  u8  bDescriptorType */
+                0x11, 0x01,    /*  u16 HID_class */
+                0x00,          /*  u8  country_code */
+                0x01,          /*  u8  num_descriptors */
+                USB_DT_REPORT, /*  u8  type: Report */
+                0x3f, 0,       /*  u16 len */
+            },
+        },
+    },
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .wMaxPacketSize        = 8,
+            .bInterval             = 0x0a,
+        },
+    },
+};
+
+static const USBDescDevice desc_device_mouse = {
+    .bcdUSB                        = 0x0100,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_MOUSE,
+            .bmAttributes          = 0xa0,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface_mouse,
+        },
+    },
+};
+
+static const USBDescDevice desc_device_tablet = {
+    .bcdUSB                        = 0x0100,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_TABLET,
+            .bmAttributes          = 0xa0,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface_tablet,
+        },
+    },
+};
+
+static const USBDescDevice desc_device_keyboard = {
+    .bcdUSB                        = 0x0100,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_KEYBOARD,
+            .bmAttributes          = 0xa0,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface_keyboard,
+        },
+    },
+};
+
+static const USBDesc desc_mouse = {
+    .id = {
+        .idVendor          = 0x0627,
+        .idProduct         = 0x0001,
+        .bcdDevice         = 0,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_MOUSE,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_mouse,
+    .str  = desc_strings,
+};
+
+static const USBDesc desc_tablet = {
+    .id = {
+        .idVendor          = 0x0627,
+        .idProduct         = 0x0001,
+        .bcdDevice         = 0,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_TABLET,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_tablet,
+    .str  = desc_strings,
+};
+
+static const USBDesc desc_keyboard = {
+    .id = {
+        .idVendor          = 0x0627,
+        .idProduct         = 0x0001,
+        .bcdDevice         = 0,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_KEYBOARD,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_keyboard,
+    .str  = desc_strings,
+};
+
+static const uint8_t qemu_mouse_hid_report_descriptor[] = {
+    0x05, 0x01,                /* Usage Page (Generic Desktop) */
+    0x09, 0x02,                /* Usage (Mouse) */
+    0xa1, 0x01,                /* Collection (Application) */
+    0x09, 0x01,                /*   Usage (Pointer) */
+    0xa1, 0x00,                /*   Collection (Physical) */
+    0x05, 0x09,                /*     Usage Page (Button) */
+    0x19, 0x01,                /*     Usage Minimum (1) */
+    0x29, 0x03,                /*     Usage Maximum (3) */
+    0x15, 0x00,                /*     Logical Minimum (0) */
+    0x25, 0x01,                /*     Logical Maximum (1) */
+    0x95, 0x03,                /*     Report Count (3) */
+    0x75, 0x01,                /*     Report Size (1) */
+    0x81, 0x02,                /*     Input (Data, Variable, Absolute) */
+    0x95, 0x01,                /*     Report Count (1) */
+    0x75, 0x05,                /*     Report Size (5) */
+    0x81, 0x01,                /*     Input (Constant) */
+    0x05, 0x01,                /*     Usage Page (Generic Desktop) */
+    0x09, 0x30,                /*     Usage (X) */
+    0x09, 0x31,                /*     Usage (Y) */
+    0x09, 0x38,                /*     Usage (Wheel) */
+    0x15, 0x81,                /*     Logical Minimum (-0x7f) */
+    0x25, 0x7f,                /*     Logical Maximum (0x7f) */
+    0x75, 0x08,                /*     Report Size (8) */
+    0x95, 0x03,                /*     Report Count (3) */
+    0x81, 0x06,                /*     Input (Data, Variable, Relative) */
+    0xc0,              /*   End Collection */
+    0xc0,              /* End Collection */
+};
+
+static const uint8_t qemu_tablet_hid_report_descriptor[] = {
+    0x05, 0x01,                /* Usage Page (Generic Desktop) */
+    0x09, 0x01,                /* Usage (Pointer) */
+    0xa1, 0x01,                /* Collection (Application) */
+    0x09, 0x01,                /*   Usage (Pointer) */
+    0xa1, 0x00,                /*   Collection (Physical) */
+    0x05, 0x09,                /*     Usage Page (Button) */
+    0x19, 0x01,                /*     Usage Minimum (1) */
+    0x29, 0x03,                /*     Usage Maximum (3) */
+    0x15, 0x00,                /*     Logical Minimum (0) */
+    0x25, 0x01,                /*     Logical Maximum (1) */
+    0x95, 0x03,                /*     Report Count (3) */
+    0x75, 0x01,                /*     Report Size (1) */
+    0x81, 0x02,                /*     Input (Data, Variable, Absolute) */
+    0x95, 0x01,                /*     Report Count (1) */
+    0x75, 0x05,                /*     Report Size (5) */
+    0x81, 0x01,                /*     Input (Constant) */
+    0x05, 0x01,                /*     Usage Page (Generic Desktop) */
+    0x09, 0x30,                /*     Usage (X) */
+    0x09, 0x31,                /*     Usage (Y) */
+    0x15, 0x00,                /*     Logical Minimum (0) */
+    0x26, 0xff, 0x7f,  /*     Logical Maximum (0x7fff) */
+    0x35, 0x00,                /*     Physical Minimum (0) */
+    0x46, 0xff, 0x7f,  /*     Physical Maximum (0x7fff) */
+    0x75, 0x10,                /*     Report Size (16) */
+    0x95, 0x02,                /*     Report Count (2) */
+    0x81, 0x02,                /*     Input (Data, Variable, Absolute) */
+    0x05, 0x01,                /*     Usage Page (Generic Desktop) */
+    0x09, 0x38,                /*     Usage (Wheel) */
+    0x15, 0x81,                /*     Logical Minimum (-0x7f) */
+    0x25, 0x7f,                /*     Logical Maximum (0x7f) */
+    0x35, 0x00,                /*     Physical Minimum (same as logical) */
+    0x45, 0x00,                /*     Physical Maximum (same as logical) */
+    0x75, 0x08,                /*     Report Size (8) */
+    0x95, 0x01,                /*     Report Count (1) */
+    0x81, 0x06,                /*     Input (Data, Variable, Relative) */
+    0xc0,              /*   End Collection */
+    0xc0,              /* End Collection */
+};
+
+static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
+    0x05, 0x01,                /* Usage Page (Generic Desktop) */
+    0x09, 0x06,                /* Usage (Keyboard) */
+    0xa1, 0x01,                /* Collection (Application) */
+    0x75, 0x01,                /*   Report Size (1) */
+    0x95, 0x08,                /*   Report Count (8) */
+    0x05, 0x07,                /*   Usage Page (Key Codes) */
+    0x19, 0xe0,                /*   Usage Minimum (224) */
+    0x29, 0xe7,                /*   Usage Maximum (231) */
+    0x15, 0x00,                /*   Logical Minimum (0) */
+    0x25, 0x01,                /*   Logical Maximum (1) */
+    0x81, 0x02,                /*   Input (Data, Variable, Absolute) */
+    0x95, 0x01,                /*   Report Count (1) */
+    0x75, 0x08,                /*   Report Size (8) */
+    0x81, 0x01,                /*   Input (Constant) */
+    0x95, 0x05,                /*   Report Count (5) */
+    0x75, 0x01,                /*   Report Size (1) */
+    0x05, 0x08,                /*   Usage Page (LEDs) */
+    0x19, 0x01,                /*   Usage Minimum (1) */
+    0x29, 0x05,                /*   Usage Maximum (5) */
+    0x91, 0x02,                /*   Output (Data, Variable, Absolute) */
+    0x95, 0x01,                /*   Report Count (1) */
+    0x75, 0x03,                /*   Report Size (3) */
+    0x91, 0x01,                /*   Output (Constant) */
+    0x95, 0x06,                /*   Report Count (6) */
+    0x75, 0x08,                /*   Report Size (8) */
+    0x15, 0x00,                /*   Logical Minimum (0) */
+    0x25, 0xff,                /*   Logical Maximum (255) */
+    0x05, 0x07,                /*   Usage Page (Key Codes) */
+    0x19, 0x00,                /*   Usage Minimum (0) */
+    0x29, 0xff,                /*   Usage Maximum (255) */
+    0x81, 0x00,                /*   Input (Data, Array) */
+    0xc0,              /* End Collection */
+};
+
+static void usb_hid_changed(HIDState *hs)
+{
+    USBHIDState *us = container_of(hs, USBHIDState, hid);
+
+    usb_wakeup(us->intr);
+}
+
+static void usb_hid_handle_reset(USBDevice *dev)
+{
+    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+
+    hid_reset(&us->hid);
+}
+
+static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+    HIDState *hs = &us->hid;
+    int ret;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    ret = 0;
+    switch (request) {
+        /* hid specific requests */
+    case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch (value >> 8) {
+        case 0x22:
+            if (hs->kind == HID_MOUSE) {
+               memcpy(data, qemu_mouse_hid_report_descriptor,
+                      sizeof(qemu_mouse_hid_report_descriptor));
+               ret = sizeof(qemu_mouse_hid_report_descriptor);
+            } else if (hs->kind == HID_TABLET) {
+                memcpy(data, qemu_tablet_hid_report_descriptor,
+                      sizeof(qemu_tablet_hid_report_descriptor));
+               ret = sizeof(qemu_tablet_hid_report_descriptor);
+            } else if (hs->kind == HID_KEYBOARD) {
+                memcpy(data, qemu_keyboard_hid_report_descriptor,
+                       sizeof(qemu_keyboard_hid_report_descriptor));
+                ret = sizeof(qemu_keyboard_hid_report_descriptor);
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case GET_REPORT:
+        if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+            ret = hid_pointer_poll(hs, data, length);
+        } else if (hs->kind == HID_KEYBOARD) {
+            ret = hid_keyboard_poll(hs, data, length);
+        }
+        break;
+    case SET_REPORT:
+        if (hs->kind == HID_KEYBOARD) {
+            ret = hid_keyboard_write(hs, data, length);
+        } else {
+            goto fail;
+        }
+        break;
+    case GET_PROTOCOL:
+        if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
+            goto fail;
+        }
+        ret = 1;
+        data[0] = hs->protocol;
+        break;
+    case SET_PROTOCOL:
+        if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
+            goto fail;
+        }
+        ret = 0;
+        hs->protocol = value;
+        break;
+    case GET_IDLE:
+        ret = 1;
+        data[0] = hs->idle;
+        break;
+    case SET_IDLE:
+        hs->idle = (uint8_t) (value >> 8);
+        hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock));
+        if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+            hid_pointer_activate(hs);
+        }
+        ret = 0;
+        break;
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+    HIDState *hs = &us->hid;
+    uint8_t buf[p->iov.size];
+    int ret = 0;
+
+    switch (p->pid) {
+    case USB_TOKEN_IN:
+        if (p->ep->nr == 1) {
+            int64_t curtime = qemu_get_clock_ns(vm_clock);
+            if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+                hid_pointer_activate(hs);
+            }
+            if (!hid_has_events(hs) &&
+                (!hs->idle || hs->next_idle_clock - curtime > 0)) {
+                return USB_RET_NAK;
+            }
+            hid_set_next_idle(hs, curtime);
+            if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+                ret = hid_pointer_poll(hs, buf, p->iov.size);
+            } else if (hs->kind == HID_KEYBOARD) {
+                ret = hid_keyboard_poll(hs, buf, p->iov.size);
+            }
+            usb_packet_copy(p, buf, ret);
+        } else {
+            goto fail;
+        }
+        break;
+    case USB_TOKEN_OUT:
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_hid_handle_destroy(USBDevice *dev)
+{
+    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+
+    hid_free(&us->hid);
+}
+
+static int usb_hid_initfn(USBDevice *dev, int kind)
+{
+    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+
+    usb_desc_init(dev);
+    us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
+    hid_init(&us->hid, kind, usb_hid_changed);
+    return 0;
+}
+
+static int usb_tablet_initfn(USBDevice *dev)
+{
+    return usb_hid_initfn(dev, HID_TABLET);
+}
+
+static int usb_mouse_initfn(USBDevice *dev)
+{
+    return usb_hid_initfn(dev, HID_MOUSE);
+}
+
+static int usb_keyboard_initfn(USBDevice *dev)
+{
+    return usb_hid_initfn(dev, HID_KEYBOARD);
+}
+
+static int usb_ptr_post_load(void *opaque, int version_id)
+{
+    USBHIDState *s = opaque;
+
+    if (s->dev.remote_wakeup) {
+        hid_pointer_activate(&s->hid);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_ptr = {
+    .name = "usb-ptr",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = usb_ptr_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_USB_DEVICE(dev, USBHIDState),
+        VMSTATE_HID_POINTER_DEVICE(hid, USBHIDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_usb_kbd = {
+    .name = "usb-kbd",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_USB_DEVICE(dev, USBHIDState),
+        VMSTATE_HID_KEYBOARD_DEVICE(hid, USBHIDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void usb_hid_class_initfn(ObjectClass *klass, void *data)
+{
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->handle_reset   = usb_hid_handle_reset;
+    uc->handle_control = usb_hid_handle_control;
+    uc->handle_data    = usb_hid_handle_data;
+    uc->handle_destroy = usb_hid_handle_destroy;
+}
+
+static void usb_tablet_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    usb_hid_class_initfn(klass, data);
+    uc->init           = usb_tablet_initfn;
+    uc->product_desc   = "QEMU USB Tablet";
+    uc->usb_desc       = &desc_tablet;
+    dc->vmsd = &vmstate_usb_ptr;
+}
+
+static TypeInfo usb_tablet_info = {
+    .name          = "usb-tablet",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHIDState),
+    .class_init    = usb_tablet_class_initfn,
+};
+
+static void usb_mouse_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    usb_hid_class_initfn(klass, data);
+    uc->init           = usb_mouse_initfn;
+    uc->product_desc   = "QEMU USB Mouse";
+    uc->usb_desc       = &desc_mouse;
+    dc->vmsd = &vmstate_usb_ptr;
+}
+
+static TypeInfo usb_mouse_info = {
+    .name          = "usb-mouse",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHIDState),
+    .class_init    = usb_mouse_class_initfn,
+};
+
+static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    usb_hid_class_initfn(klass, data);
+    uc->init           = usb_keyboard_initfn;
+    uc->product_desc   = "QEMU USB Keyboard";
+    uc->usb_desc       = &desc_keyboard;
+    dc->vmsd = &vmstate_usb_kbd;
+}
+
+static TypeInfo usb_keyboard_info = {
+    .name          = "usb-kbd",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHIDState),
+    .class_init    = usb_keyboard_class_initfn,
+};
+
+static void usb_hid_register_types(void)
+{
+    type_register_static(&usb_tablet_info);
+    usb_legacy_register("usb-tablet", "tablet", NULL);
+    type_register_static(&usb_mouse_info);
+    usb_legacy_register("usb-mouse", "mouse", NULL);
+    type_register_static(&usb_keyboard_info);
+    usb_legacy_register("usb-kbd", "keyboard", NULL);
+}
+
+type_init(usb_hid_register_types)
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
new file mode 100644 (file)
index 0000000..eb4e711
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * QEMU USB HUB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * 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 "hw/usb/desc.h"
+
+//#define DEBUG
+
+#define NUM_PORTS 8
+
+typedef struct USBHubPort {
+    USBPort port;
+    uint16_t wPortStatus;
+    uint16_t wPortChange;
+} USBHubPort;
+
+typedef struct USBHubState {
+    USBDevice dev;
+    USBEndpoint *intr;
+    USBHubPort ports[NUM_PORTS];
+} USBHubState;
+
+#define ClearHubFeature                (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature       (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor       (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus           (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus          (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature          (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature         (0x2300 | USB_REQ_SET_FEATURE)
+
+#define PORT_STAT_CONNECTION   0x0001
+#define PORT_STAT_ENABLE       0x0002
+#define PORT_STAT_SUSPEND      0x0004
+#define PORT_STAT_OVERCURRENT  0x0008
+#define PORT_STAT_RESET                0x0010
+#define PORT_STAT_POWER                0x0100
+#define PORT_STAT_LOW_SPEED    0x0200
+#define PORT_STAT_HIGH_SPEED    0x0400
+#define PORT_STAT_TEST          0x0800
+#define PORT_STAT_INDICATOR     0x1000
+
+#define PORT_STAT_C_CONNECTION 0x0001
+#define PORT_STAT_C_ENABLE     0x0002
+#define PORT_STAT_C_SUSPEND    0x0004
+#define PORT_STAT_C_OVERCURRENT        0x0008
+#define PORT_STAT_C_RESET      0x0010
+
+#define PORT_CONNECTION                0
+#define PORT_ENABLE            1
+#define PORT_SUSPEND           2
+#define PORT_OVERCURRENT       3
+#define PORT_RESET             4
+#define PORT_POWER             8
+#define PORT_LOWSPEED          9
+#define PORT_HIGHSPEED         10
+#define PORT_C_CONNECTION      16
+#define PORT_C_ENABLE          17
+#define PORT_C_SUSPEND         18
+#define PORT_C_OVERCURRENT     19
+#define PORT_C_RESET           20
+#define PORT_TEST               21
+#define PORT_INDICATOR          22
+
+/* same as Linux kernel root hubs */
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT,
+    STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+    [STR_PRODUCT]      = "QEMU USB Hub",
+    [STR_SERIALNUMBER] = "314159",
+};
+
+static const USBDescIface desc_iface_hub = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 1,
+    .bInterfaceClass               = USB_CLASS_HUB,
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .wMaxPacketSize        = 1 + (NUM_PORTS + 7) / 8,
+            .bInterval             = 0xff,
+        },
+    }
+};
+
+static const USBDescDevice desc_device_hub = {
+    .bcdUSB                        = 0x0110,
+    .bDeviceClass                  = USB_CLASS_HUB,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .bmAttributes          = 0xe0,
+            .nif = 1,
+            .ifs = &desc_iface_hub,
+        },
+    },
+};
+
+static const USBDesc desc_hub = {
+    .id = {
+        .idVendor          = 0x0409,
+        .idProduct         = 0x55aa,
+        .bcdDevice         = 0x0101,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_hub,
+    .str  = desc_strings,
+};
+
+static const uint8_t qemu_hub_hub_descriptor[] =
+{
+       0x00,                   /*  u8  bLength; patched in later */
+       0x29,                   /*  u8  bDescriptorType; Hub-descriptor */
+       0x00,                   /*  u8  bNbrPorts; (patched later) */
+       0x0a,                   /* u16  wHubCharacteristics; */
+       0x00,                   /*   (per-port OC, no power switching) */
+       0x01,                   /*  u8  bPwrOn2pwrGood; 2ms */
+       0x00                    /*  u8  bHubContrCurrent; 0 mA */
+
+        /* DeviceRemovable and PortPwrCtrlMask patched in later */
+};
+
+static void usb_hub_attach(USBPort *port1)
+{
+    USBHubState *s = port1->opaque;
+    USBHubPort *port = &s->ports[port1->index];
+
+    port->wPortStatus |= PORT_STAT_CONNECTION;
+    port->wPortChange |= PORT_STAT_C_CONNECTION;
+    if (port->port.dev->speed == USB_SPEED_LOW) {
+        port->wPortStatus |= PORT_STAT_LOW_SPEED;
+    } else {
+        port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
+    }
+    usb_wakeup(s->intr);
+}
+
+static void usb_hub_detach(USBPort *port1)
+{
+    USBHubState *s = port1->opaque;
+    USBHubPort *port = &s->ports[port1->index];
+
+    usb_wakeup(s->intr);
+
+    /* Let upstream know the device on this port is gone */
+    s->dev.port->ops->child_detach(s->dev.port, port1->dev);
+
+    port->wPortStatus &= ~PORT_STAT_CONNECTION;
+    port->wPortChange |= PORT_STAT_C_CONNECTION;
+    if (port->wPortStatus & PORT_STAT_ENABLE) {
+        port->wPortStatus &= ~PORT_STAT_ENABLE;
+        port->wPortChange |= PORT_STAT_C_ENABLE;
+    }
+}
+
+static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
+{
+    USBHubState *s = port1->opaque;
+
+    /* Pass along upstream */
+    s->dev.port->ops->child_detach(s->dev.port, child);
+}
+
+static void usb_hub_wakeup(USBPort *port1)
+{
+    USBHubState *s = port1->opaque;
+    USBHubPort *port = &s->ports[port1->index];
+
+    if (port->wPortStatus & PORT_STAT_SUSPEND) {
+        port->wPortChange |= PORT_STAT_C_SUSPEND;
+        usb_wakeup(s->intr);
+    }
+}
+
+static void usb_hub_complete(USBPort *port, USBPacket *packet)
+{
+    USBHubState *s = port->opaque;
+
+    /*
+     * Just pass it along upstream for now.
+     *
+     * If we ever implement usb 2.0 split transactions this will
+     * become a little more complicated ...
+     *
+     * Can't use usb_packet_complete() here because packet->owner is
+     * cleared already, go call the ->complete() callback directly
+     * instead.
+     */
+    s->dev.port->ops->complete(s->dev.port, packet);
+}
+
+static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
+{
+    USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+    USBHubPort *port;
+    USBDevice *downstream;
+    int i;
+
+    for (i = 0; i < NUM_PORTS; i++) {
+        port = &s->ports[i];
+        if (!(port->wPortStatus & PORT_STAT_ENABLE)) {
+            continue;
+        }
+        downstream = usb_find_device(&port->port, addr);
+        if (downstream != NULL) {
+            return downstream;
+        }
+    }
+    return NULL;
+}
+
+static void usb_hub_handle_reset(USBDevice *dev)
+{
+    USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+    USBHubPort *port;
+    int i;
+
+    for (i = 0; i < NUM_PORTS; i++) {
+        port = s->ports + i;
+        port->wPortStatus = PORT_STAT_POWER;
+        port->wPortChange = 0;
+        if (port->port.dev && port->port.dev->attached) {
+            port->wPortStatus |= PORT_STAT_CONNECTION;
+            port->wPortChange |= PORT_STAT_C_CONNECTION;
+            if (port->port.dev->speed == USB_SPEED_LOW) {
+                port->wPortStatus |= PORT_STAT_LOW_SPEED;
+            }
+        }
+    }
+}
+
+static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBHubState *s = (USBHubState *)dev;
+    int ret;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    switch(request) {
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == 0 && index != 0x81) { /* clear ep halt */
+            goto fail;
+        }
+        ret = 0;
+        break;
+        /* usb specific requests */
+    case GetHubStatus:
+        data[0] = 0;
+        data[1] = 0;
+        data[2] = 0;
+        data[3] = 0;
+        ret = 4;
+        break;
+    case GetPortStatus:
+        {
+            unsigned int n = index - 1;
+            USBHubPort *port;
+            if (n >= NUM_PORTS) {
+                goto fail;
+            }
+            port = &s->ports[n];
+            data[0] = port->wPortStatus;
+            data[1] = port->wPortStatus >> 8;
+            data[2] = port->wPortChange;
+            data[3] = port->wPortChange >> 8;
+            ret = 4;
+        }
+        break;
+    case SetHubFeature:
+    case ClearHubFeature:
+        if (value == 0 || value == 1) {
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case SetPortFeature:
+        {
+            unsigned int n = index - 1;
+            USBHubPort *port;
+            USBDevice *dev;
+            if (n >= NUM_PORTS) {
+                goto fail;
+            }
+            port = &s->ports[n];
+            dev = port->port.dev;
+            switch(value) {
+            case PORT_SUSPEND:
+                port->wPortStatus |= PORT_STAT_SUSPEND;
+                break;
+            case PORT_RESET:
+                if (dev && dev->attached) {
+                    usb_device_reset(dev);
+                    port->wPortChange |= PORT_STAT_C_RESET;
+                    /* set enable bit */
+                    port->wPortStatus |= PORT_STAT_ENABLE;
+                }
+                break;
+            case PORT_POWER:
+                break;
+            default:
+                goto fail;
+            }
+            ret = 0;
+        }
+        break;
+    case ClearPortFeature:
+        {
+            unsigned int n = index - 1;
+            USBHubPort *port;
+
+            if (n >= NUM_PORTS) {
+                goto fail;
+            }
+            port = &s->ports[n];
+            switch(value) {
+            case PORT_ENABLE:
+                port->wPortStatus &= ~PORT_STAT_ENABLE;
+                break;
+            case PORT_C_ENABLE:
+                port->wPortChange &= ~PORT_STAT_C_ENABLE;
+                break;
+            case PORT_SUSPEND:
+                port->wPortStatus &= ~PORT_STAT_SUSPEND;
+                break;
+            case PORT_C_SUSPEND:
+                port->wPortChange &= ~PORT_STAT_C_SUSPEND;
+                break;
+            case PORT_C_CONNECTION:
+                port->wPortChange &= ~PORT_STAT_C_CONNECTION;
+                break;
+            case PORT_C_OVERCURRENT:
+                port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
+                break;
+            case PORT_C_RESET:
+                port->wPortChange &= ~PORT_STAT_C_RESET;
+                break;
+            default:
+                goto fail;
+            }
+            ret = 0;
+        }
+        break;
+    case GetHubDescriptor:
+        {
+            unsigned int n, limit, var_hub_size = 0;
+            memcpy(data, qemu_hub_hub_descriptor,
+                   sizeof(qemu_hub_hub_descriptor));
+            data[2] = NUM_PORTS;
+
+            /* fill DeviceRemovable bits */
+            limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
+            for (n = 7; n < limit; n++) {
+                data[n] = 0x00;
+                var_hub_size++;
+            }
+
+            /* fill PortPwrCtrlMask bits */
+            limit = limit + ((NUM_PORTS + 7) / 8);
+            for (;n < limit; n++) {
+                data[n] = 0xff;
+                var_hub_size++;
+            }
+
+            ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
+            data[0] = ret;
+            break;
+        }
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHubState *s = (USBHubState *)dev;
+    int ret;
+
+    switch(p->pid) {
+    case USB_TOKEN_IN:
+        if (p->ep->nr == 1) {
+            USBHubPort *port;
+            unsigned int status;
+            uint8_t buf[4];
+            int i, n;
+            n = (NUM_PORTS + 1 + 7) / 8;
+            if (p->iov.size == 1) { /* FreeBSD workaround */
+                n = 1;
+            } else if (n > p->iov.size) {
+                return USB_RET_BABBLE;
+            }
+            status = 0;
+            for(i = 0; i < NUM_PORTS; i++) {
+                port = &s->ports[i];
+                if (port->wPortChange)
+                    status |= (1 << (i + 1));
+            }
+            if (status != 0) {
+                for(i = 0; i < n; i++) {
+                    buf[i] = status >> (8 * i);
+                }
+                usb_packet_copy(p, buf, n);
+                ret = n;
+            } else {
+                ret = USB_RET_NAK; /* usb11 11.13.1 */
+            }
+        } else {
+            goto fail;
+        }
+        break;
+    case USB_TOKEN_OUT:
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_hub_handle_destroy(USBDevice *dev)
+{
+    USBHubState *s = (USBHubState *)dev;
+    int i;
+
+    for (i = 0; i < NUM_PORTS; i++) {
+        usb_unregister_port(usb_bus_from_device(dev),
+                            &s->ports[i].port);
+    }
+}
+
+static USBPortOps usb_hub_port_ops = {
+    .attach = usb_hub_attach,
+    .detach = usb_hub_detach,
+    .child_detach = usb_hub_child_detach,
+    .wakeup = usb_hub_wakeup,
+    .complete = usb_hub_complete,
+};
+
+static int usb_hub_initfn(USBDevice *dev)
+{
+    USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+    USBHubPort *port;
+    int i;
+
+    usb_desc_init(dev);
+    s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
+    for (i = 0; i < NUM_PORTS; i++) {
+        port = &s->ports[i];
+        usb_register_port(usb_bus_from_device(dev),
+                          &port->port, s, i, &usb_hub_port_ops,
+                          USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+        usb_port_location(&port->port, dev->port, i+1);
+    }
+    usb_hub_handle_reset(dev);
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_hub_port = {
+    .name = "usb-hub-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT16(wPortStatus, USBHubPort),
+        VMSTATE_UINT16(wPortChange, USBHubPort),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_usb_hub = {
+    .name = "usb-hub",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_USB_DEVICE(dev, USBHubState),
+        VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0,
+                             vmstate_usb_hub_port, USBHubPort),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void usb_hub_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_hub_initfn;
+    uc->product_desc   = "QEMU USB Hub";
+    uc->usb_desc       = &desc_hub;
+    uc->find_device    = usb_hub_find_device;
+    uc->handle_reset   = usb_hub_handle_reset;
+    uc->handle_control = usb_hub_handle_control;
+    uc->handle_data    = usb_hub_handle_data;
+    uc->handle_destroy = usb_hub_handle_destroy;
+    dc->fw_name = "hub";
+    dc->vmsd = &vmstate_usb_hub;
+}
+
+static TypeInfo hub_info = {
+    .name          = "usb-hub",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHubState),
+    .class_init    = usb_hub_class_initfn,
+};
+
+static void usb_hub_register_types(void)
+{
+    type_register_static(&hub_info);
+}
+
+type_init(usb_hub_register_types)
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
new file mode 100644 (file)
index 0000000..cff55f2
--- /dev/null
@@ -0,0 +1,1423 @@
+/*
+ * QEMU USB Net devices
+ *
+ * Copyright (c) 2006 Thomas Sailer
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * 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 "hw/usb/desc.h"
+#include "net.h"
+#include "qemu-queue.h"
+#include "sysemu.h"
+#include "iov.h"
+
+/*#define TRAFFIC_DEBUG*/
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM          0x0525  /* NetChip */
+#define CDC_PRODUCT_NUM         0xa4a1  /* Linux-USB Ethernet Gadget */
+/* For hardware that can talk RNDIS and either of the above protocols,
+ * use this ID ... the windows INF files will know it.
+ */
+#define RNDIS_VENDOR_NUM        0x0525  /* NetChip */
+#define RNDIS_PRODUCT_NUM       0xa4a2  /* Ethernet/RNDIS Gadget */
+
+enum usbstring_idx {
+    STRING_MANUFACTURER                = 1,
+    STRING_PRODUCT,
+    STRING_ETHADDR,
+    STRING_DATA,
+    STRING_CONTROL,
+    STRING_RNDIS_CONTROL,
+    STRING_CDC,
+    STRING_SUBSET,
+    STRING_RNDIS,
+    STRING_SERIALNUMBER,
+};
+
+#define DEV_CONFIG_VALUE               1       /* CDC or a subset */
+#define DEV_RNDIS_CONFIG_VALUE         2       /* RNDIS; optional */
+
+#define USB_CDC_SUBCLASS_ACM           0x02
+#define USB_CDC_SUBCLASS_ETHERNET      0x06
+
+#define USB_CDC_PROTO_NONE             0
+#define USB_CDC_ACM_PROTO_VENDOR       0xff
+
+#define USB_CDC_HEADER_TYPE            0x00    /* header_desc */
+#define USB_CDC_CALL_MANAGEMENT_TYPE   0x01    /* call_mgmt_descriptor */
+#define USB_CDC_ACM_TYPE               0x02    /* acm_descriptor */
+#define USB_CDC_UNION_TYPE             0x06    /* union_desc */
+#define USB_CDC_ETHERNET_TYPE          0x0f    /* ether_desc */
+
+#define USB_CDC_SEND_ENCAPSULATED_COMMAND      0x00
+#define USB_CDC_GET_ENCAPSULATED_RESPONSE      0x01
+#define USB_CDC_REQ_SET_LINE_CODING            0x20
+#define USB_CDC_REQ_GET_LINE_CODING            0x21
+#define USB_CDC_REQ_SET_CONTROL_LINE_STATE     0x22
+#define USB_CDC_REQ_SEND_BREAK                 0x23
+#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40
+#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41
+#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42
+#define USB_CDC_SET_ETHERNET_PACKET_FILTER     0x43
+#define USB_CDC_GET_ETHERNET_STATISTIC         0x44
+
+#define LOG2_STATUS_INTERVAL_MSEC      5    /* 1 << 5 == 32 msec */
+#define STATUS_BYTECOUNT               16   /* 8 byte header + data */
+
+#define ETH_FRAME_LEN                  1514 /* Max. octets in frame sans FCS */
+
+static const USBDescStrings usb_net_stringtable = {
+    [STRING_MANUFACTURER]       = "QEMU",
+    [STRING_PRODUCT]            = "RNDIS/QEMU USB Network Device",
+    [STRING_ETHADDR]            = "400102030405",
+    [STRING_DATA]               = "QEMU USB Net Data Interface",
+    [STRING_CONTROL]            = "QEMU USB Net Control Interface",
+    [STRING_RNDIS_CONTROL]      = "QEMU USB Net RNDIS Control Interface",
+    [STRING_CDC]                = "QEMU USB Net CDC",
+    [STRING_SUBSET]             = "QEMU USB Net Subset",
+    [STRING_RNDIS]              = "QEMU USB Net RNDIS",
+    [STRING_SERIALNUMBER]       = "1",
+};
+
+static const USBDescIface desc_iface_rndis[] = {
+    {
+        /* RNDIS Control Interface */
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_COMM,
+        .bInterfaceSubClass            = USB_CDC_SUBCLASS_ACM,
+        .bInterfaceProtocol            = USB_CDC_ACM_PROTO_VENDOR,
+        .iInterface                    = STRING_RNDIS_CONTROL,
+        .ndesc                         = 4,
+        .descs = (USBDescOther[]) {
+            {
+                /* Header Descriptor */
+                .data = (uint8_t[]) {
+                    0x05,                       /*  u8    bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
+                    USB_CDC_HEADER_TYPE,        /*  u8    bDescriptorSubType */
+                    0x10, 0x01,                 /*  le16  bcdCDC */
+                },
+            },{
+                /* Call Management Descriptor */
+                .data = (uint8_t[]) {
+                    0x05,                       /*  u8    bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
+                    USB_CDC_CALL_MANAGEMENT_TYPE, /*  u8    bDescriptorSubType */
+                    0x00,                       /*  u8    bmCapabilities */
+                    0x01,                       /*  u8    bDataInterface */
+                },
+            },{
+                /* ACM Descriptor */
+                .data = (uint8_t[]) {
+                    0x04,                       /*  u8    bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
+                    USB_CDC_ACM_TYPE,           /*  u8    bDescriptorSubType */
+                    0x00,                       /*  u8    bmCapabilities */
+                },
+            },{
+                /* Union Descriptor */
+                .data = (uint8_t[]) {
+                    0x05,                       /*  u8    bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
+                    USB_CDC_UNION_TYPE,         /*  u8    bDescriptorSubType */
+                    0x00,                       /*  u8    bMasterInterface0 */
+                    0x01,                       /*  u8    bSlaveInterface0 */
+                },
+            },
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_IN | 0x01,
+                .bmAttributes          = USB_ENDPOINT_XFER_INT,
+                .wMaxPacketSize        = STATUS_BYTECOUNT,
+                .bInterval             = 1 << LOG2_STATUS_INTERVAL_MSEC,
+            },
+        }
+    },{
+        /* RNDIS Data Interface */
+        .bInterfaceNumber              = 1,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = USB_CLASS_CDC_DATA,
+        .iInterface                    = STRING_DATA,
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_IN | 0x02,
+                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+                .wMaxPacketSize        = 0x40,
+            },{
+                .bEndpointAddress      = USB_DIR_OUT | 0x02,
+                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+                .wMaxPacketSize        = 0x40,
+            }
+        }
+    }
+};
+
+static const USBDescIface desc_iface_cdc[] = {
+    {
+        /* CDC Control Interface */
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_COMM,
+        .bInterfaceSubClass            = USB_CDC_SUBCLASS_ETHERNET,
+        .bInterfaceProtocol            = USB_CDC_PROTO_NONE,
+        .iInterface                    = STRING_CONTROL,
+        .ndesc                         = 3,
+        .descs = (USBDescOther[]) {
+            {
+                /* Header Descriptor */
+                .data = (uint8_t[]) {
+                    0x05,                       /*  u8    bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
+                    USB_CDC_HEADER_TYPE,        /*  u8    bDescriptorSubType */
+                    0x10, 0x01,                 /*  le16  bcdCDC */
+                },
+            },{
+                /* Union Descriptor */
+                .data = (uint8_t[]) {
+                    0x05,                       /*  u8    bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
+                    USB_CDC_UNION_TYPE,         /*  u8    bDescriptorSubType */
+                    0x00,                       /*  u8    bMasterInterface0 */
+                    0x01,                       /*  u8    bSlaveInterface0 */
+                },
+            },{
+                /* Ethernet Descriptor */
+                .data = (uint8_t[]) {
+                    0x0d,                       /*  u8    bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8    bDescriptorType */
+                    USB_CDC_ETHERNET_TYPE,      /*  u8    bDescriptorSubType */
+                    STRING_ETHADDR,             /*  u8    iMACAddress */
+                    0x00, 0x00, 0x00, 0x00,     /*  le32  bmEthernetStatistics */
+                    ETH_FRAME_LEN & 0xff,
+                    ETH_FRAME_LEN >> 8,         /*  le16  wMaxSegmentSize */
+                    0x00, 0x00,                 /*  le16  wNumberMCFilters */
+                    0x00,                       /*  u8    bNumberPowerFilters */
+                },
+            },
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_IN | 0x01,
+                .bmAttributes          = USB_ENDPOINT_XFER_INT,
+                .wMaxPacketSize        = STATUS_BYTECOUNT,
+                .bInterval             = 1 << LOG2_STATUS_INTERVAL_MSEC,
+            },
+        }
+    },{
+        /* CDC Data Interface (off) */
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_CDC_DATA,
+    },{
+        /* CDC Data Interface */
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 1,
+        .bNumEndpoints                 = 2,
+        .bInterfaceClass               = USB_CLASS_CDC_DATA,
+        .iInterface                    = STRING_DATA,
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_IN | 0x02,
+                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+                .wMaxPacketSize        = 0x40,
+            },{
+                .bEndpointAddress      = USB_DIR_OUT | 0x02,
+                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+                .wMaxPacketSize        = 0x40,
+            }
+        }
+    }
+};
+
+static const USBDescDevice desc_device_net = {
+    .bcdUSB                        = 0x0200,
+    .bDeviceClass                  = USB_CLASS_COMM,
+    .bMaxPacketSize0               = 0x40,
+    .bNumConfigurations            = 2,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = DEV_RNDIS_CONFIG_VALUE,
+            .iConfiguration        = STRING_RNDIS,
+            .bmAttributes          = 0xc0,
+            .bMaxPower             = 0x32,
+            .nif = ARRAY_SIZE(desc_iface_rndis),
+            .ifs = desc_iface_rndis,
+        },{
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = DEV_CONFIG_VALUE,
+            .iConfiguration        = STRING_CDC,
+            .bmAttributes          = 0xc0,
+            .bMaxPower             = 0x32,
+            .nif = ARRAY_SIZE(desc_iface_cdc),
+            .ifs = desc_iface_cdc,
+        }
+    },
+};
+
+static const USBDesc desc_net = {
+    .id = {
+        .idVendor          = RNDIS_VENDOR_NUM,
+        .idProduct         = RNDIS_PRODUCT_NUM,
+        .bcdDevice         = 0,
+        .iManufacturer     = STRING_MANUFACTURER,
+        .iProduct          = STRING_PRODUCT,
+        .iSerialNumber     = STRING_SERIALNUMBER,
+    },
+    .full = &desc_device_net,
+    .str  = usb_net_stringtable,
+};
+
+/*
+ * RNDIS Definitions - in theory not specific to USB.
+ */
+#define RNDIS_MAXIMUM_FRAME_SIZE       1518
+#define RNDIS_MAX_TOTAL_SIZE           1558
+
+/* Remote NDIS Versions */
+#define RNDIS_MAJOR_VERSION            1
+#define RNDIS_MINOR_VERSION            0
+
+/* Status Values */
+#define RNDIS_STATUS_SUCCESS           0x00000000U /* Success */
+#define RNDIS_STATUS_FAILURE           0xc0000001U /* Unspecified error */
+#define RNDIS_STATUS_INVALID_DATA      0xc0010015U /* Invalid data */
+#define RNDIS_STATUS_NOT_SUPPORTED     0xc00000bbU /* Unsupported request */
+#define RNDIS_STATUS_MEDIA_CONNECT     0x4001000bU /* Device connected */
+#define RNDIS_STATUS_MEDIA_DISCONNECT  0x4001000cU /* Device disconnected */
+
+/* Message Set for Connectionless (802.3) Devices */
+enum {
+    RNDIS_PACKET_MSG           = 1,
+    RNDIS_INITIALIZE_MSG       = 2,    /* Initialize device */
+    RNDIS_HALT_MSG             = 3,
+    RNDIS_QUERY_MSG            = 4,
+    RNDIS_SET_MSG              = 5,
+    RNDIS_RESET_MSG            = 6,
+    RNDIS_INDICATE_STATUS_MSG  = 7,
+    RNDIS_KEEPALIVE_MSG                = 8,
+};
+
+/* Message completion */
+enum {
+    RNDIS_INITIALIZE_CMPLT     = 0x80000002U,
+    RNDIS_QUERY_CMPLT          = 0x80000004U,
+    RNDIS_SET_CMPLT            = 0x80000005U,
+    RNDIS_RESET_CMPLT          = 0x80000006U,
+    RNDIS_KEEPALIVE_CMPLT      = 0x80000008U,
+};
+
+/* Device Flags */
+enum {
+    RNDIS_DF_CONNECTIONLESS    = 1,
+    RNDIS_DF_CONNECTIONORIENTED        = 2,
+};
+
+#define RNDIS_MEDIUM_802_3             0x00000000U
+
+/* from drivers/net/sk98lin/h/skgepnmi.h */
+#define OID_PNP_CAPABILITIES           0xfd010100
+#define OID_PNP_SET_POWER              0xfd010101
+#define OID_PNP_QUERY_POWER            0xfd010102
+#define OID_PNP_ADD_WAKE_UP_PATTERN    0xfd010103
+#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xfd010104
+#define OID_PNP_ENABLE_WAKE_UP         0xfd010106
+
+typedef uint32_t le32;
+
+typedef struct rndis_init_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+    le32 MajorVersion;
+    le32 MinorVersion;
+    le32 MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+    le32 Status;
+    le32 MajorVersion;
+    le32 MinorVersion;
+    le32 DeviceFlags;
+    le32 Medium;
+    le32 MaxPacketsPerTransfer;
+    le32 MaxTransferSize;
+    le32 PacketAlignmentFactor;
+    le32 AFListOffset;
+    le32 AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+    le32 OID;
+    le32 InformationBufferLength;
+    le32 InformationBufferOffset;
+    le32 DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+    le32 Status;
+    le32 InformationBufferLength;
+    le32 InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+    le32 OID;
+    le32 InformationBufferLength;
+    le32 InformationBufferOffset;
+    le32 DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+    le32 Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 Status;
+    le32 AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 Status;
+    le32 StatusBufferLength;
+    le32 StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 RequestID;
+    le32 Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type {
+    le32 MessageType;
+    le32 MessageLength;
+    le32 DataOffset;
+    le32 DataLength;
+    le32 OOBDataOffset;
+    le32 OOBDataLength;
+    le32 NumOOBDataElements;
+    le32 PerPacketInfoOffset;
+    le32 PerPacketInfoLength;
+    le32 VcHandle;
+    le32 Reserved;
+};
+
+struct rndis_config_parameter {
+    le32 ParameterNameOffset;
+    le32 ParameterNameLength;
+    le32 ParameterType;
+    le32 ParameterValueOffset;
+    le32 ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state
+{
+    RNDIS_UNINITIALIZED,
+    RNDIS_INITIALIZED,
+    RNDIS_DATA_INITIALIZED,
+};
+
+/* from ndis.h */
+enum ndis_oid {
+    /* Required Object IDs (OIDs) */
+    OID_GEN_SUPPORTED_LIST             = 0x00010101,
+    OID_GEN_HARDWARE_STATUS            = 0x00010102,
+    OID_GEN_MEDIA_SUPPORTED            = 0x00010103,
+    OID_GEN_MEDIA_IN_USE               = 0x00010104,
+    OID_GEN_MAXIMUM_LOOKAHEAD          = 0x00010105,
+    OID_GEN_MAXIMUM_FRAME_SIZE         = 0x00010106,
+    OID_GEN_LINK_SPEED                 = 0x00010107,
+    OID_GEN_TRANSMIT_BUFFER_SPACE      = 0x00010108,
+    OID_GEN_RECEIVE_BUFFER_SPACE       = 0x00010109,
+    OID_GEN_TRANSMIT_BLOCK_SIZE                = 0x0001010a,
+    OID_GEN_RECEIVE_BLOCK_SIZE         = 0x0001010b,
+    OID_GEN_VENDOR_ID                  = 0x0001010c,
+    OID_GEN_VENDOR_DESCRIPTION         = 0x0001010d,
+    OID_GEN_CURRENT_PACKET_FILTER      = 0x0001010e,
+    OID_GEN_CURRENT_LOOKAHEAD          = 0x0001010f,
+    OID_GEN_DRIVER_VERSION             = 0x00010110,
+    OID_GEN_MAXIMUM_TOTAL_SIZE         = 0x00010111,
+    OID_GEN_PROTOCOL_OPTIONS           = 0x00010112,
+    OID_GEN_MAC_OPTIONS                        = 0x00010113,
+    OID_GEN_MEDIA_CONNECT_STATUS       = 0x00010114,
+    OID_GEN_MAXIMUM_SEND_PACKETS       = 0x00010115,
+    OID_GEN_VENDOR_DRIVER_VERSION      = 0x00010116,
+    OID_GEN_SUPPORTED_GUIDS            = 0x00010117,
+    OID_GEN_NETWORK_LAYER_ADDRESSES    = 0x00010118,
+    OID_GEN_TRANSPORT_HEADER_OFFSET    = 0x00010119,
+    OID_GEN_MACHINE_NAME               = 0x0001021a,
+    OID_GEN_RNDIS_CONFIG_PARAMETER     = 0x0001021b,
+    OID_GEN_VLAN_ID                    = 0x0001021c,
+
+    /* Optional OIDs */
+    OID_GEN_MEDIA_CAPABILITIES         = 0x00010201,
+    OID_GEN_PHYSICAL_MEDIUM            = 0x00010202,
+
+    /* Required statistics OIDs */
+    OID_GEN_XMIT_OK                    = 0x00020101,
+    OID_GEN_RCV_OK                     = 0x00020102,
+    OID_GEN_XMIT_ERROR                 = 0x00020103,
+    OID_GEN_RCV_ERROR                  = 0x00020104,
+    OID_GEN_RCV_NO_BUFFER              = 0x00020105,
+
+    /* Optional statistics OIDs */
+    OID_GEN_DIRECTED_BYTES_XMIT                = 0x00020201,
+    OID_GEN_DIRECTED_FRAMES_XMIT       = 0x00020202,
+    OID_GEN_MULTICAST_BYTES_XMIT       = 0x00020203,
+    OID_GEN_MULTICAST_FRAMES_XMIT      = 0x00020204,
+    OID_GEN_BROADCAST_BYTES_XMIT       = 0x00020205,
+    OID_GEN_BROADCAST_FRAMES_XMIT      = 0x00020206,
+    OID_GEN_DIRECTED_BYTES_RCV         = 0x00020207,
+    OID_GEN_DIRECTED_FRAMES_RCV                = 0x00020208,
+    OID_GEN_MULTICAST_BYTES_RCV                = 0x00020209,
+    OID_GEN_MULTICAST_FRAMES_RCV       = 0x0002020a,
+    OID_GEN_BROADCAST_BYTES_RCV                = 0x0002020b,
+    OID_GEN_BROADCAST_FRAMES_RCV       = 0x0002020c,
+    OID_GEN_RCV_CRC_ERROR              = 0x0002020d,
+    OID_GEN_TRANSMIT_QUEUE_LENGTH      = 0x0002020e,
+    OID_GEN_GET_TIME_CAPS              = 0x0002020f,
+    OID_GEN_GET_NETCARD_TIME           = 0x00020210,
+    OID_GEN_NETCARD_LOAD               = 0x00020211,
+    OID_GEN_DEVICE_PROFILE             = 0x00020212,
+    OID_GEN_INIT_TIME_MS               = 0x00020213,
+    OID_GEN_RESET_COUNTS               = 0x00020214,
+    OID_GEN_MEDIA_SENSE_COUNTS         = 0x00020215,
+    OID_GEN_FRIENDLY_NAME              = 0x00020216,
+    OID_GEN_MINIPORT_INFO              = 0x00020217,
+    OID_GEN_RESET_VERIFY_PARAMETERS    = 0x00020218,
+
+    /* IEEE 802.3 (Ethernet) OIDs */
+    OID_802_3_PERMANENT_ADDRESS                = 0x01010101,
+    OID_802_3_CURRENT_ADDRESS          = 0x01010102,
+    OID_802_3_MULTICAST_LIST           = 0x01010103,
+    OID_802_3_MAXIMUM_LIST_SIZE                = 0x01010104,
+    OID_802_3_MAC_OPTIONS              = 0x01010105,
+    OID_802_3_RCV_ERROR_ALIGNMENT      = 0x01020101,
+    OID_802_3_XMIT_ONE_COLLISION       = 0x01020102,
+    OID_802_3_XMIT_MORE_COLLISIONS     = 0x01020103,
+    OID_802_3_XMIT_DEFERRED            = 0x01020201,
+    OID_802_3_XMIT_MAX_COLLISIONS      = 0x01020202,
+    OID_802_3_RCV_OVERRUN              = 0x01020203,
+    OID_802_3_XMIT_UNDERRUN            = 0x01020204,
+    OID_802_3_XMIT_HEARTBEAT_FAILURE   = 0x01020205,
+    OID_802_3_XMIT_TIMES_CRS_LOST      = 0x01020206,
+    OID_802_3_XMIT_LATE_COLLISIONS     = 0x01020207,
+};
+
+static const uint32_t oid_supported_list[] =
+{
+    /* the general stuff */
+    OID_GEN_SUPPORTED_LIST,
+    OID_GEN_HARDWARE_STATUS,
+    OID_GEN_MEDIA_SUPPORTED,
+    OID_GEN_MEDIA_IN_USE,
+    OID_GEN_MAXIMUM_FRAME_SIZE,
+    OID_GEN_LINK_SPEED,
+    OID_GEN_TRANSMIT_BLOCK_SIZE,
+    OID_GEN_RECEIVE_BLOCK_SIZE,
+    OID_GEN_VENDOR_ID,
+    OID_GEN_VENDOR_DESCRIPTION,
+    OID_GEN_VENDOR_DRIVER_VERSION,
+    OID_GEN_CURRENT_PACKET_FILTER,
+    OID_GEN_MAXIMUM_TOTAL_SIZE,
+    OID_GEN_MEDIA_CONNECT_STATUS,
+    OID_GEN_PHYSICAL_MEDIUM,
+
+    /* the statistical stuff */
+    OID_GEN_XMIT_OK,
+    OID_GEN_RCV_OK,
+    OID_GEN_XMIT_ERROR,
+    OID_GEN_RCV_ERROR,
+    OID_GEN_RCV_NO_BUFFER,
+
+    /* IEEE 802.3 */
+    /* the general stuff */
+    OID_802_3_PERMANENT_ADDRESS,
+    OID_802_3_CURRENT_ADDRESS,
+    OID_802_3_MULTICAST_LIST,
+    OID_802_3_MAC_OPTIONS,
+    OID_802_3_MAXIMUM_LIST_SIZE,
+
+    /* the statistical stuff */
+    OID_802_3_RCV_ERROR_ALIGNMENT,
+    OID_802_3_XMIT_ONE_COLLISION,
+    OID_802_3_XMIT_MORE_COLLISIONS,
+};
+
+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA    (1 << 0)
+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED     (1 << 1)
+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND     (1 << 2)
+#define NDIS_MAC_OPTION_NO_LOOPBACK            (1 << 3)
+#define NDIS_MAC_OPTION_FULL_DUPLEX            (1 << 4)
+#define NDIS_MAC_OPTION_EOTX_INDICATION                (1 << 5)
+#define NDIS_MAC_OPTION_8021P_PRIORITY         (1 << 6)
+
+struct rndis_response {
+    QTAILQ_ENTRY(rndis_response) entries;
+    uint32_t length;
+    uint8_t buf[0];
+};
+
+typedef struct USBNetState {
+    USBDevice dev;
+
+    enum rndis_state rndis_state;
+    uint32_t medium;
+    uint32_t speed;
+    uint32_t media_state;
+    uint16_t filter;
+    uint32_t vendorid;
+
+    unsigned int out_ptr;
+    uint8_t out_buf[2048];
+
+    USBPacket *inpkt;
+    unsigned int in_ptr, in_len;
+    uint8_t in_buf[2048];
+
+    char usbstring_mac[13];
+    NICState *nic;
+    NICConf conf;
+    QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp;
+} USBNetState;
+
+static int is_rndis(USBNetState *s)
+{
+    return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE;
+}
+
+static int ndis_query(USBNetState *s, uint32_t oid,
+                      uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf,
+                      size_t outlen)
+{
+    unsigned int i;
+
+    switch (oid) {
+    /* general oids (table 4-1) */
+    /* mandatory */
+    case OID_GEN_SUPPORTED_LIST:
+        for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++)
+            ((le32 *) outbuf)[i] = cpu_to_le32(oid_supported_list[i]);
+        return sizeof(oid_supported_list);
+
+    /* mandatory */
+    case OID_GEN_HARDWARE_STATUS:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_MEDIA_SUPPORTED:
+        *((le32 *) outbuf) = cpu_to_le32(s->medium);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_MEDIA_IN_USE:
+        *((le32 *) outbuf) = cpu_to_le32(s->medium);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_MAXIMUM_FRAME_SIZE:
+        *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_LINK_SPEED:
+        *((le32 *) outbuf) = cpu_to_le32(s->speed);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_TRANSMIT_BLOCK_SIZE:
+        *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_RECEIVE_BLOCK_SIZE:
+        *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_VENDOR_ID:
+        *((le32 *) outbuf) = cpu_to_le32(s->vendorid);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_VENDOR_DESCRIPTION:
+        pstrcpy((char *)outbuf, outlen, "QEMU USB RNDIS Net");
+        return strlen((char *)outbuf) + 1;
+
+    case OID_GEN_VENDOR_DRIVER_VERSION:
+        *((le32 *) outbuf) = cpu_to_le32(1);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_CURRENT_PACKET_FILTER:
+        *((le32 *) outbuf) = cpu_to_le32(s->filter);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_MAXIMUM_TOTAL_SIZE:
+        *((le32 *) outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_MEDIA_CONNECT_STATUS:
+        *((le32 *) outbuf) = cpu_to_le32(s->media_state);
+        return sizeof(le32);
+
+    case OID_GEN_PHYSICAL_MEDIUM:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    case OID_GEN_MAC_OPTIONS:
+        *((le32 *) outbuf) = cpu_to_le32(
+                        NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
+                        NDIS_MAC_OPTION_FULL_DUPLEX);
+        return sizeof(le32);
+
+    /* statistics OIDs (table 4-2) */
+    /* mandatory */
+    case OID_GEN_XMIT_OK:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_RCV_OK:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_XMIT_ERROR:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_RCV_ERROR:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_GEN_RCV_NO_BUFFER:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* ieee802.3 OIDs (table 4-3) */
+    /* mandatory */
+    case OID_802_3_PERMANENT_ADDRESS:
+        memcpy(outbuf, s->conf.macaddr.a, 6);
+        return 6;
+
+    /* mandatory */
+    case OID_802_3_CURRENT_ADDRESS:
+        memcpy(outbuf, s->conf.macaddr.a, 6);
+        return 6;
+
+    /* mandatory */
+    case OID_802_3_MULTICAST_LIST:
+        *((le32 *) outbuf) = cpu_to_le32(0xe0000000);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_802_3_MAXIMUM_LIST_SIZE:
+        *((le32 *) outbuf) = cpu_to_le32(1);
+        return sizeof(le32);
+
+    case OID_802_3_MAC_OPTIONS:
+        return 0;
+
+    /* ieee802.3 statistics OIDs (table 4-4) */
+    /* mandatory */
+    case OID_802_3_RCV_ERROR_ALIGNMENT:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_802_3_XMIT_ONE_COLLISION:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    /* mandatory */
+    case OID_802_3_XMIT_MORE_COLLISIONS:
+        *((le32 *) outbuf) = cpu_to_le32(0);
+        return sizeof(le32);
+
+    default:
+        fprintf(stderr, "usbnet: unknown OID 0x%08x\n", oid);
+        return 0;
+    }
+    return -1;
+}
+
+static int ndis_set(USBNetState *s, uint32_t oid,
+                uint8_t *inbuf, unsigned int inlen)
+{
+    switch (oid) {
+    case OID_GEN_CURRENT_PACKET_FILTER:
+        s->filter = le32_to_cpup((le32 *) inbuf);
+        if (s->filter) {
+            s->rndis_state = RNDIS_DATA_INITIALIZED;
+        } else {
+            s->rndis_state = RNDIS_INITIALIZED;
+        }
+        return 0;
+
+    case OID_802_3_MULTICAST_LIST:
+        return 0;
+    }
+    return -1;
+}
+
+static int rndis_get_response(USBNetState *s, uint8_t *buf)
+{
+    int ret = 0;
+    struct rndis_response *r = s->rndis_resp.tqh_first;
+
+    if (!r)
+        return ret;
+
+    QTAILQ_REMOVE(&s->rndis_resp, r, entries);
+    ret = r->length;
+    memcpy(buf, r->buf, r->length);
+    g_free(r);
+
+    return ret;
+}
+
+static void *rndis_queue_response(USBNetState *s, unsigned int length)
+{
+    struct rndis_response *r =
+            g_malloc0(sizeof(struct rndis_response) + length);
+
+    QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries);
+    r->length = length;
+
+    return &r->buf[0];
+}
+
+static void rndis_clear_responsequeue(USBNetState *s)
+{
+    struct rndis_response *r;
+
+    while ((r = s->rndis_resp.tqh_first)) {
+        QTAILQ_REMOVE(&s->rndis_resp, r, entries);
+        g_free(r);
+    }
+}
+
+static int rndis_init_response(USBNetState *s, rndis_init_msg_type *buf)
+{
+    rndis_init_cmplt_type *resp =
+            rndis_queue_response(s, sizeof(rndis_init_cmplt_type));
+
+    if (!resp)
+        return USB_RET_STALL;
+
+    resp->MessageType = cpu_to_le32(RNDIS_INITIALIZE_CMPLT);
+    resp->MessageLength = cpu_to_le32(sizeof(rndis_init_cmplt_type));
+    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+    resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
+    resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
+    resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
+    resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
+    resp->MaxPacketsPerTransfer = cpu_to_le32(1);
+    resp->MaxTransferSize = cpu_to_le32(ETH_FRAME_LEN +
+                    sizeof(struct rndis_packet_msg_type) + 22);
+    resp->PacketAlignmentFactor = cpu_to_le32(0);
+    resp->AFListOffset = cpu_to_le32(0);
+    resp->AFListSize = cpu_to_le32(0);
+    return 0;
+}
+
+static int rndis_query_response(USBNetState *s,
+                rndis_query_msg_type *buf, unsigned int length)
+{
+    rndis_query_cmplt_type *resp;
+    /* oid_supported_list is the largest data reply */
+    uint8_t infobuf[sizeof(oid_supported_list)];
+    uint32_t bufoffs, buflen;
+    int infobuflen;
+    unsigned int resplen;
+
+    bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
+    buflen = le32_to_cpu(buf->InformationBufferLength);
+    if (bufoffs + buflen > length)
+        return USB_RET_STALL;
+
+    infobuflen = ndis_query(s, le32_to_cpu(buf->OID),
+                            bufoffs + (uint8_t *) buf, buflen, infobuf,
+                            sizeof(infobuf));
+    resplen = sizeof(rndis_query_cmplt_type) +
+            ((infobuflen < 0) ? 0 : infobuflen);
+    resp = rndis_queue_response(s, resplen);
+    if (!resp)
+        return USB_RET_STALL;
+
+    resp->MessageType = cpu_to_le32(RNDIS_QUERY_CMPLT);
+    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+    resp->MessageLength = cpu_to_le32(resplen);
+
+    if (infobuflen < 0) {
+        /* OID not supported */
+        resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+        resp->InformationBufferLength = cpu_to_le32(0);
+        resp->InformationBufferOffset = cpu_to_le32(0);
+        return 0;
+    }
+
+    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+    resp->InformationBufferOffset =
+            cpu_to_le32(infobuflen ? sizeof(rndis_query_cmplt_type) - 8 : 0);
+    resp->InformationBufferLength = cpu_to_le32(infobuflen);
+    memcpy(resp + 1, infobuf, infobuflen);
+
+    return 0;
+}
+
+static int rndis_set_response(USBNetState *s,
+                rndis_set_msg_type *buf, unsigned int length)
+{
+    rndis_set_cmplt_type *resp =
+            rndis_queue_response(s, sizeof(rndis_set_cmplt_type));
+    uint32_t bufoffs, buflen;
+    int ret;
+
+    if (!resp)
+        return USB_RET_STALL;
+
+    bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
+    buflen = le32_to_cpu(buf->InformationBufferLength);
+    if (bufoffs + buflen > length)
+        return USB_RET_STALL;
+
+    ret = ndis_set(s, le32_to_cpu(buf->OID),
+                    bufoffs + (uint8_t *) buf, buflen);
+    resp->MessageType = cpu_to_le32(RNDIS_SET_CMPLT);
+    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+    resp->MessageLength = cpu_to_le32(sizeof(rndis_set_cmplt_type));
+    if (ret < 0) {
+        /* OID not supported */
+        resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+        return 0;
+    }
+    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+    return 0;
+}
+
+static int rndis_reset_response(USBNetState *s, rndis_reset_msg_type *buf)
+{
+    rndis_reset_cmplt_type *resp =
+            rndis_queue_response(s, sizeof(rndis_reset_cmplt_type));
+
+    if (!resp)
+        return USB_RET_STALL;
+
+    resp->MessageType = cpu_to_le32(RNDIS_RESET_CMPLT);
+    resp->MessageLength = cpu_to_le32(sizeof(rndis_reset_cmplt_type));
+    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+    resp->AddressingReset = cpu_to_le32(1); /* reset information */
+
+    return 0;
+}
+
+static int rndis_keepalive_response(USBNetState *s,
+                rndis_keepalive_msg_type *buf)
+{
+    rndis_keepalive_cmplt_type *resp =
+            rndis_queue_response(s, sizeof(rndis_keepalive_cmplt_type));
+
+    if (!resp)
+        return USB_RET_STALL;
+
+    resp->MessageType = cpu_to_le32(RNDIS_KEEPALIVE_CMPLT);
+    resp->MessageLength = cpu_to_le32(sizeof(rndis_keepalive_cmplt_type));
+    resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+    resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+    return 0;
+}
+
+static int rndis_parse(USBNetState *s, uint8_t *data, int length)
+{
+    uint32_t msg_type;
+    le32 *tmp = (le32 *) data;
+
+    msg_type = le32_to_cpup(tmp);
+
+    switch (msg_type) {
+    case RNDIS_INITIALIZE_MSG:
+        s->rndis_state = RNDIS_INITIALIZED;
+        return rndis_init_response(s, (rndis_init_msg_type *) data);
+
+    case RNDIS_HALT_MSG:
+        s->rndis_state = RNDIS_UNINITIALIZED;
+        return 0;
+
+    case RNDIS_QUERY_MSG:
+        return rndis_query_response(s, (rndis_query_msg_type *) data, length);
+
+    case RNDIS_SET_MSG:
+        return rndis_set_response(s, (rndis_set_msg_type *) data, length);
+
+    case RNDIS_RESET_MSG:
+        rndis_clear_responsequeue(s);
+        s->out_ptr = s->in_ptr = s->in_len = 0;
+        return rndis_reset_response(s, (rndis_reset_msg_type *) data);
+
+    case RNDIS_KEEPALIVE_MSG:
+        /* For USB: host does this every 5 seconds */
+        return rndis_keepalive_response(s, (rndis_keepalive_msg_type *) data);
+    }
+
+    return USB_RET_STALL;
+}
+
+static void usb_net_handle_reset(USBDevice *dev)
+{
+}
+
+static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBNetState *s = (USBNetState *) dev;
+    int ret;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    ret = 0;
+    switch(request) {
+    case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
+        if (!is_rndis(s) || value || index != 0) {
+            goto fail;
+        }
+#ifdef TRAFFIC_DEBUG
+        {
+            unsigned int i;
+            fprintf(stderr, "SEND_ENCAPSULATED_COMMAND:");
+            for (i = 0; i < length; i++) {
+                if (!(i & 15))
+                    fprintf(stderr, "\n%04x:", i);
+                fprintf(stderr, " %02x", data[i]);
+            }
+            fprintf(stderr, "\n\n");
+        }
+#endif
+        ret = rndis_parse(s, data, length);
+        break;
+
+    case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
+        if (!is_rndis(s) || value || index != 0) {
+            goto fail;
+        }
+        ret = rndis_get_response(s, data);
+        if (!ret) {
+            data[0] = 0;
+            ret = 1;
+        }
+#ifdef TRAFFIC_DEBUG
+        {
+            unsigned int i;
+            fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:");
+            for (i = 0; i < ret; i++) {
+                if (!(i & 15))
+                    fprintf(stderr, "\n%04x:", i);
+                fprintf(stderr, " %02x", data[i]);
+            }
+            fprintf(stderr, "\n\n");
+        }
+#endif
+        break;
+
+    default:
+    fail:
+        fprintf(stderr, "usbnet: failed control transaction: "
+                        "request 0x%x value 0x%x index 0x%x length 0x%x\n",
+                        request, value, index, length);
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_net_handle_statusin(USBNetState *s, USBPacket *p)
+{
+    le32 buf[2];
+    int ret = 8;
+
+    if (p->iov.size < 8) {
+        return USB_RET_STALL;
+    }
+
+    buf[0] = cpu_to_le32(1);
+    buf[1] = cpu_to_le32(0);
+    usb_packet_copy(p, buf, 8);
+    if (!s->rndis_resp.tqh_first)
+        ret = USB_RET_NAK;
+
+#ifdef TRAFFIC_DEBUG
+    fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
+            p->iov.size, ret);
+    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
+#endif
+
+    return ret;
+}
+
+static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
+{
+    int ret = USB_RET_NAK;
+
+    if (s->in_ptr > s->in_len) {
+        s->in_ptr = s->in_len = 0;
+        ret = USB_RET_NAK;
+        return ret;
+    }
+    if (!s->in_len) {
+        ret = USB_RET_NAK;
+        return ret;
+    }
+    ret = s->in_len - s->in_ptr;
+    if (ret > p->iov.size) {
+        ret = p->iov.size;
+    }
+    usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
+    s->in_ptr += ret;
+    if (s->in_ptr >= s->in_len &&
+                    (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
+        /* no short packet necessary */
+        s->in_ptr = s->in_len = 0;
+    }
+
+#ifdef TRAFFIC_DEBUG
+    fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret);
+    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
+#endif
+
+    return ret;
+}
+
+static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
+{
+    int ret = p->iov.size;
+    int sz = sizeof(s->out_buf) - s->out_ptr;
+    struct rndis_packet_msg_type *msg =
+            (struct rndis_packet_msg_type *) s->out_buf;
+    uint32_t len;
+
+#ifdef TRAFFIC_DEBUG
+    fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size);
+    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size);
+#endif
+
+    if (sz > ret)
+        sz = ret;
+    usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
+    s->out_ptr += sz;
+
+    if (!is_rndis(s)) {
+        if (ret < 64) {
+            qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
+            s->out_ptr = 0;
+        }
+        return ret;
+    }
+    len = le32_to_cpu(msg->MessageLength);
+    if (s->out_ptr < 8 || s->out_ptr < len)
+        return ret;
+    if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
+        uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
+        uint32_t size = le32_to_cpu(msg->DataLength);
+        if (offs + size <= len)
+            qemu_send_packet(&s->nic->nc, s->out_buf + offs, size);
+    }
+    s->out_ptr -= len;
+    memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
+
+    return ret;
+}
+
+static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBNetState *s = (USBNetState *) dev;
+    int ret = 0;
+
+    switch(p->pid) {
+    case USB_TOKEN_IN:
+        switch (p->ep->nr) {
+        case 1:
+            ret = usb_net_handle_statusin(s, p);
+            break;
+
+        case 2:
+            ret = usb_net_handle_datain(s, p);
+            break;
+
+        default:
+            goto fail;
+        }
+        break;
+
+    case USB_TOKEN_OUT:
+        switch (p->ep->nr) {
+        case 2:
+            ret = usb_net_handle_dataout(s, p);
+            break;
+
+        default:
+            goto fail;
+        }
+        break;
+
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    if (ret == USB_RET_STALL)
+        fprintf(stderr, "usbnet: failed data transaction: "
+                        "pid 0x%x ep 0x%x len 0x%zx\n",
+                        p->pid, p->ep->nr, p->iov.size);
+    return ret;
+}
+
+static ssize_t usbnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+    USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    struct rndis_packet_msg_type *msg;
+
+    if (is_rndis(s)) {
+        msg = (struct rndis_packet_msg_type *) s->in_buf;
+        if (s->rndis_state != RNDIS_DATA_INITIALIZED) {
+            return -1;
+        }
+        if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf))
+            return -1;
+
+        memset(msg, 0, sizeof(struct rndis_packet_msg_type));
+        msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG);
+        msg->MessageLength = cpu_to_le32(size + sizeof(struct rndis_packet_msg_type));
+        msg->DataOffset = cpu_to_le32(sizeof(struct rndis_packet_msg_type) - 8);
+        msg->DataLength = cpu_to_le32(size);
+        /* msg->OOBDataOffset;
+         * msg->OOBDataLength;
+         * msg->NumOOBDataElements;
+         * msg->PerPacketInfoOffset;
+         * msg->PerPacketInfoLength;
+         * msg->VcHandle;
+         * msg->Reserved;
+         */
+        memcpy(msg + 1, buf, size);
+        s->in_len = size + sizeof(struct rndis_packet_msg_type);
+    } else {
+        if (size > sizeof(s->in_buf))
+            return -1;
+        memcpy(s->in_buf, buf, size);
+        s->in_len = size;
+    }
+    s->in_ptr = 0;
+    return size;
+}
+
+static int usbnet_can_receive(VLANClientState *nc)
+{
+    USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) {
+        return 1;
+    }
+
+    return !s->in_len;
+}
+
+static void usbnet_cleanup(VLANClientState *nc)
+{
+    USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    s->nic = NULL;
+}
+
+static void usb_net_handle_destroy(USBDevice *dev)
+{
+    USBNetState *s = (USBNetState *) dev;
+
+    /* TODO: remove the nd_table[] entry */
+    rndis_clear_responsequeue(s);
+    qemu_del_vlan_client(&s->nic->nc);
+}
+
+static NetClientInfo net_usbnet_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = usbnet_can_receive,
+    .receive = usbnet_receive,
+    .cleanup = usbnet_cleanup,
+};
+
+static int usb_net_initfn(USBDevice *dev)
+{
+    USBNetState *s = DO_UPCAST(USBNetState, dev, dev);
+
+    usb_desc_init(dev);
+
+    s->rndis_state = RNDIS_UNINITIALIZED;
+    QTAILQ_INIT(&s->rndis_resp);
+
+    s->medium = 0;     /* NDIS_MEDIUM_802_3 */
+    s->speed = 1000000; /* 100MBps, in 100Bps units */
+    s->media_state = 0;        /* NDIS_MEDIA_STATE_CONNECTED */;
+    s->filter = 0;
+    s->vendorid = 0x1234;
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
+                          object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
+    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+    snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
+             "%02x%02x%02x%02x%02x%02x",
+             0x40,
+             s->conf.macaddr.a[1],
+             s->conf.macaddr.a[2],
+             s->conf.macaddr.a[3],
+             s->conf.macaddr.a[4],
+             s->conf.macaddr.a[5]);
+    usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac);
+
+    add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0");
+    return 0;
+}
+
+static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
+{
+    USBDevice *dev;
+    QemuOpts *opts;
+    int idx;
+
+    opts = qemu_opts_parse(qemu_find_opts("net"), cmdline, 0);
+    if (!opts) {
+        return NULL;
+    }
+    qemu_opt_set(opts, "type", "nic");
+    qemu_opt_set(opts, "model", "usb");
+
+    idx = net_client_init(NULL, opts, 0);
+    if (idx == -1) {
+        return NULL;
+    }
+
+    dev = usb_create(bus, "usb-net");
+    if (!dev) {
+        return NULL;
+    }
+    qdev_set_nic_properties(&dev->qdev, &nd_table[idx]);
+    qdev_init_nofail(&dev->qdev);
+    return dev;
+}
+
+static const VMStateDescription vmstate_usb_net = {
+    .name = "usb-net",
+    .unmigratable = 1,
+};
+
+static Property net_properties[] = {
+    DEFINE_NIC_PROPERTIES(USBNetState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_net_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_net_initfn;
+    uc->product_desc   = "QEMU USB Network Interface";
+    uc->usb_desc       = &desc_net;
+    uc->handle_reset   = usb_net_handle_reset;
+    uc->handle_control = usb_net_handle_control;
+    uc->handle_data    = usb_net_handle_data;
+    uc->handle_destroy = usb_net_handle_destroy;
+    dc->fw_name = "network";
+    dc->vmsd = &vmstate_usb_net;
+    dc->props = net_properties;
+}
+
+static TypeInfo net_info = {
+    .name          = "usb-net",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBNetState),
+    .class_init    = usb_net_class_initfn,
+};
+
+static void usb_net_register_types(void)
+{
+    type_register_static(&net_info);
+    usb_legacy_register("usb-net", "net", usb_net_init);
+}
+
+type_init(usb_net_register_types)
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
new file mode 100644 (file)
index 0000000..8dcac8b
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * FTDI FT232BM Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "qemu-char.h"
+
+//#define DEBUG_Serial
+
+#ifdef DEBUG_Serial
+#define DPRINTF(fmt, ...) \
+do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define RECV_BUF 384
+
+/* Commands */
+#define FTDI_RESET             0
+#define FTDI_SET_MDM_CTRL      1
+#define FTDI_SET_FLOW_CTRL     2
+#define FTDI_SET_BAUD          3
+#define FTDI_SET_DATA          4
+#define FTDI_GET_MDM_ST                5
+#define FTDI_SET_EVENT_CHR     6
+#define FTDI_SET_ERROR_CHR     7
+#define FTDI_SET_LATENCY       9
+#define FTDI_GET_LATENCY       10
+
+#define DeviceOutVendor        ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+
+/* RESET */
+
+#define FTDI_RESET_SIO 0
+#define FTDI_RESET_RX  1
+#define FTDI_RESET_TX  2
+
+/* SET_MDM_CTRL */
+
+#define FTDI_DTR       1
+#define FTDI_SET_DTR   (FTDI_DTR << 8)
+#define FTDI_RTS       2
+#define FTDI_SET_RTS   (FTDI_RTS << 8)
+
+/* SET_FLOW_CTRL */
+
+#define FTDI_RTS_CTS_HS                1
+#define FTDI_DTR_DSR_HS                2
+#define FTDI_XON_XOFF_HS       4
+
+/* SET_DATA */
+
+#define FTDI_PARITY    (0x7 << 8)
+#define FTDI_ODD       (0x1 << 8)
+#define FTDI_EVEN      (0x2 << 8)
+#define FTDI_MARK      (0x3 << 8)
+#define FTDI_SPACE     (0x4 << 8)
+
+#define FTDI_STOP      (0x3 << 11)
+#define FTDI_STOP1     (0x0 << 11)
+#define FTDI_STOP15    (0x1 << 11)
+#define FTDI_STOP2     (0x2 << 11)
+
+/* GET_MDM_ST */
+/* TODO: should be sent every 40ms */
+#define FTDI_CTS  (1<<4)        // CTS line status
+#define FTDI_DSR  (1<<5)        // DSR line status
+#define FTDI_RI   (1<<6)        // RI line status
+#define FTDI_RLSD (1<<7)        // Receive Line Signal Detect
+
+/* Status */
+
+#define FTDI_DR   (1<<0)        // Data Ready
+#define FTDI_OE   (1<<1)        // Overrun Err
+#define FTDI_PE   (1<<2)        // Parity Err
+#define FTDI_FE   (1<<3)        // Framing Err
+#define FTDI_BI   (1<<4)        // Break Interrupt
+#define FTDI_THRE (1<<5)        // Transmitter Holding Register
+#define FTDI_TEMT (1<<6)        // Transmitter Empty
+#define FTDI_FIFO (1<<7)        // Error in FIFO
+
+typedef struct {
+    USBDevice dev;
+    uint8_t recv_buf[RECV_BUF];
+    uint16_t recv_ptr;
+    uint16_t recv_used;
+    uint8_t event_chr;
+    uint8_t error_chr;
+    uint8_t event_trigger;
+    QEMUSerialSetParams params;
+    int latency;        /* ms */
+    CharDriverState *cs;
+} USBSerialState;
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT_SERIAL,
+    STR_PRODUCT_BRAILLE,
+    STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER]    = "QEMU " QEMU_VERSION,
+    [STR_PRODUCT_SERIAL]  = "QEMU USB SERIAL",
+    [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
+    [STR_SERIALNUMBER]    = "1",
+};
+
+static const USBDescIface desc_iface0 = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 2,
+    .bInterfaceClass               = 0xff,
+    .bInterfaceSubClass            = 0xff,
+    .bInterfaceProtocol            = 0xff,
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },{
+            .bEndpointAddress      = USB_DIR_OUT | 0x02,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },
+    }
+};
+
+static const USBDescDevice desc_device = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .bmAttributes          = 0x80,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface0,
+        },
+    },
+};
+
+static const USBDesc desc_serial = {
+    .id = {
+        .idVendor          = 0x0403,
+        .idProduct         = 0x6001,
+        .bcdDevice         = 0x0400,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_SERIAL,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device,
+    .str  = desc_strings,
+};
+
+static const USBDesc desc_braille = {
+    .id = {
+        .idVendor          = 0x0403,
+        .idProduct         = 0xfe72,
+        .bcdDevice         = 0x0400,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_BRAILLE,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device,
+    .str  = desc_strings,
+};
+
+static void usb_serial_reset(USBSerialState *s)
+{
+    /* TODO: Set flow control to none */
+    s->event_chr = 0x0d;
+    s->event_trigger = 0;
+    s->recv_ptr = 0;
+    s->recv_used = 0;
+    /* TODO: purge in char driver */
+}
+
+static void usb_serial_handle_reset(USBDevice *dev)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+
+    DPRINTF("Reset\n");
+
+    usb_serial_reset(s);
+    /* TODO: Reset char device, send BREAK? */
+}
+
+static uint8_t usb_get_modem_lines(USBSerialState *s)
+{
+    int flags;
+    uint8_t ret;
+
+    if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP)
+        return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
+
+    ret = 0;
+    if (flags & CHR_TIOCM_CTS)
+        ret |= FTDI_CTS;
+    if (flags & CHR_TIOCM_DSR)
+        ret |= FTDI_DSR;
+    if (flags & CHR_TIOCM_RI)
+        ret |= FTDI_RI;
+    if (flags & CHR_TIOCM_CAR)
+        ret |= FTDI_RLSD;
+
+    return ret;
+}
+
+static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+    int ret;
+
+    DPRINTF("got control %x, value %x\n",request, value);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    ret = 0;
+    switch (request) {
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        ret = 0;
+        break;
+
+        /* Class specific requests.  */
+    case DeviceOutVendor | FTDI_RESET:
+        switch (value) {
+        case FTDI_RESET_SIO:
+            usb_serial_reset(s);
+            break;
+        case FTDI_RESET_RX:
+            s->recv_ptr = 0;
+            s->recv_used = 0;
+            /* TODO: purge from char device */
+            break;
+        case FTDI_RESET_TX:
+            /* TODO: purge from char device */
+            break;
+        }
+        break;
+    case DeviceOutVendor | FTDI_SET_MDM_CTRL:
+    {
+        static int flags;
+        qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
+        if (value & FTDI_SET_RTS) {
+            if (value & FTDI_RTS)
+                flags |= CHR_TIOCM_RTS;
+            else
+                flags &= ~CHR_TIOCM_RTS;
+        }
+        if (value & FTDI_SET_DTR) {
+            if (value & FTDI_DTR)
+                flags |= CHR_TIOCM_DTR;
+            else
+                flags &= ~CHR_TIOCM_DTR;
+        }
+        qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
+        break;
+    }
+    case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
+        /* TODO: ioctl */
+        break;
+    case DeviceOutVendor | FTDI_SET_BAUD: {
+        static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
+        int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
+                                     | ((index & 1) << 2)];
+        int divisor = value & 0x3fff;
+
+        /* chip special cases */
+        if (divisor == 1 && subdivisor8 == 0)
+            subdivisor8 = 4;
+        if (divisor == 0 && subdivisor8 == 0)
+            divisor = 1;
+
+        s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
+        qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+        break;
+    }
+    case DeviceOutVendor | FTDI_SET_DATA:
+        switch (value & FTDI_PARITY) {
+            case 0:
+                s->params.parity = 'N';
+                break;
+            case FTDI_ODD:
+                s->params.parity = 'O';
+                break;
+            case FTDI_EVEN:
+                s->params.parity = 'E';
+                break;
+            default:
+                DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
+                goto fail;
+        }
+        switch (value & FTDI_STOP) {
+            case FTDI_STOP1:
+                s->params.stop_bits = 1;
+                break;
+            case FTDI_STOP2:
+                s->params.stop_bits = 2;
+                break;
+            default:
+                DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
+                goto fail;
+        }
+        qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+        /* TODO: TX ON/OFF */
+        break;
+    case DeviceInVendor | FTDI_GET_MDM_ST:
+        data[0] = usb_get_modem_lines(s) | 1;
+        data[1] = 0;
+        ret = 2;
+        break;
+    case DeviceOutVendor | FTDI_SET_EVENT_CHR:
+        /* TODO: handle it */
+        s->event_chr = value;
+        break;
+    case DeviceOutVendor | FTDI_SET_ERROR_CHR:
+        /* TODO: handle it */
+        s->error_chr = value;
+        break;
+    case DeviceOutVendor | FTDI_SET_LATENCY:
+        s->latency = value;
+        break;
+    case DeviceInVendor | FTDI_GET_LATENCY:
+        data[0] = s->latency;
+        ret = 1;
+        break;
+    default:
+    fail:
+        DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+    int i, ret = 0;
+    uint8_t devep = p->ep->nr;
+    struct iovec *iov;
+    uint8_t header[2];
+    int first_len, len;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        if (devep != 2)
+            goto fail;
+        for (i = 0; i < p->iov.niov; i++) {
+            iov = p->iov.iov + i;
+            qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len);
+        }
+        break;
+
+    case USB_TOKEN_IN:
+        if (devep != 1)
+            goto fail;
+        first_len = RECV_BUF - s->recv_ptr;
+        len = p->iov.size;
+        if (len <= 2) {
+            ret = USB_RET_NAK;
+            break;
+        }
+        header[0] = usb_get_modem_lines(s) | 1;
+        /* We do not have the uart details */
+        /* handle serial break */
+        if (s->event_trigger && s->event_trigger & FTDI_BI) {
+            s->event_trigger &= ~FTDI_BI;
+            header[1] = FTDI_BI;
+            usb_packet_copy(p, header, 2);
+            ret = 2;
+            break;
+        } else {
+            header[1] = 0;
+        }
+        len -= 2;
+        if (len > s->recv_used)
+            len = s->recv_used;
+        if (!len) {
+            ret = USB_RET_NAK;
+            break;
+        }
+        if (first_len > len)
+            first_len = len;
+        usb_packet_copy(p, header, 2);
+        usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len);
+        if (len > first_len)
+            usb_packet_copy(p, s->recv_buf, len - first_len);
+        s->recv_used -= len;
+        s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
+        ret = len + 2;
+        break;
+
+    default:
+        DPRINTF("Bad token\n");
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void usb_serial_handle_destroy(USBDevice *dev)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+
+    qemu_chr_delete(s->cs);
+}
+
+static int usb_serial_can_read(void *opaque)
+{
+    USBSerialState *s = opaque;
+    return RECV_BUF - s->recv_used;
+}
+
+static void usb_serial_read(void *opaque, const uint8_t *buf, int size)
+{
+    USBSerialState *s = opaque;
+    int first_size, start;
+
+    /* room in the buffer? */
+    if (size > (RECV_BUF - s->recv_used))
+        size = RECV_BUF - s->recv_used;
+
+    start = s->recv_ptr + s->recv_used;
+    if (start < RECV_BUF) {
+        /* copy data to end of buffer */
+        first_size = RECV_BUF - start;
+        if (first_size > size)
+            first_size = size;
+
+        memcpy(s->recv_buf + start, buf, first_size);
+
+        /* wrap around to front if needed */
+        if (size > first_size)
+            memcpy(s->recv_buf, buf + first_size, size - first_size);
+    } else {
+        start -= RECV_BUF;
+        memcpy(s->recv_buf + start, buf, size);
+    }
+    s->recv_used += size;
+}
+
+static void usb_serial_event(void *opaque, int event)
+{
+    USBSerialState *s = opaque;
+
+    switch (event) {
+        case CHR_EVENT_BREAK:
+            s->event_trigger |= FTDI_BI;
+            break;
+        case CHR_EVENT_FOCUS:
+            break;
+        case CHR_EVENT_OPENED:
+            usb_serial_reset(s);
+            /* TODO: Reset USB port */
+            break;
+    }
+}
+
+static int usb_serial_initfn(USBDevice *dev)
+{
+    USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev);
+
+    usb_desc_init(dev);
+
+    if (!s->cs) {
+        error_report("Property chardev is required");
+        return -1;
+    }
+
+    qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read,
+                          usb_serial_event, s);
+    usb_serial_handle_reset(dev);
+    return 0;
+}
+
+static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
+{
+    USBDevice *dev;
+    CharDriverState *cdrv;
+    uint32_t vendorid = 0, productid = 0;
+    char label[32];
+    static int index;
+
+    while (*filename && *filename != ':') {
+        const char *p;
+        char *e;
+        if (strstart(filename, "vendorid=", &p)) {
+            vendorid = strtol(p, &e, 16);
+            if (e == p || (*e && *e != ',' && *e != ':')) {
+                error_report("bogus vendor ID %s", p);
+                return NULL;
+            }
+            filename = e;
+        } else if (strstart(filename, "productid=", &p)) {
+            productid = strtol(p, &e, 16);
+            if (e == p || (*e && *e != ',' && *e != ':')) {
+                error_report("bogus product ID %s", p);
+                return NULL;
+            }
+            filename = e;
+        } else {
+            error_report("unrecognized serial USB option %s", filename);
+            return NULL;
+        }
+        while(*filename == ',')
+            filename++;
+    }
+    if (!*filename) {
+        error_report("character device specification needed");
+        return NULL;
+    }
+    filename++;
+
+    snprintf(label, sizeof(label), "usbserial%d", index++);
+    cdrv = qemu_chr_new(label, filename, NULL);
+    if (!cdrv)
+        return NULL;
+
+    dev = usb_create(bus, "usb-serial");
+    if (!dev) {
+        return NULL;
+    }
+    qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
+    if (vendorid)
+        qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid);
+    if (productid)
+        qdev_prop_set_uint16(&dev->qdev, "productid", productid);
+    qdev_init_nofail(&dev->qdev);
+
+    return dev;
+}
+
+static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
+{
+    USBDevice *dev;
+    CharDriverState *cdrv;
+
+    cdrv = qemu_chr_new("braille", "braille", NULL);
+    if (!cdrv)
+        return NULL;
+
+    dev = usb_create(bus, "usb-braille");
+    qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
+    qdev_init_nofail(&dev->qdev);
+
+    return dev;
+}
+
+static const VMStateDescription vmstate_usb_serial = {
+    .name = "usb-serial",
+    .unmigratable = 1,
+};
+
+static Property serial_properties[] = {
+    DEFINE_PROP_CHR("chardev", USBSerialState, cs),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_serial_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init = usb_serial_initfn;
+    uc->product_desc   = "QEMU USB Serial";
+    uc->usb_desc       = &desc_serial;
+    uc->handle_reset   = usb_serial_handle_reset;
+    uc->handle_control = usb_serial_handle_control;
+    uc->handle_data    = usb_serial_handle_data;
+    uc->handle_destroy = usb_serial_handle_destroy;
+    dc->vmsd = &vmstate_usb_serial;
+    dc->props = serial_properties;
+}
+
+static TypeInfo serial_info = {
+    .name          = "usb-serial",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBSerialState),
+    .class_init    = usb_serial_class_initfn,
+};
+
+static Property braille_properties[] = {
+    DEFINE_PROP_CHR("chardev", USBSerialState, cs),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_braille_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_serial_initfn;
+    uc->product_desc   = "QEMU USB Braille";
+    uc->usb_desc       = &desc_braille;
+    uc->handle_reset   = usb_serial_handle_reset;
+    uc->handle_control = usb_serial_handle_control;
+    uc->handle_data    = usb_serial_handle_data;
+    uc->handle_destroy = usb_serial_handle_destroy;
+    dc->vmsd = &vmstate_usb_serial;
+    dc->props = braille_properties;
+}
+
+static TypeInfo braille_info = {
+    .name          = "usb-braille",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBSerialState),
+    .class_init    = usb_braille_class_initfn,
+};
+
+static void usb_serial_register_types(void)
+{
+    type_register_static(&serial_info);
+    usb_legacy_register("usb-serial", "serial", usb_serial_init);
+    type_register_static(&braille_info);
+    usb_legacy_register("usb-braille", "braille", usb_braille_init);
+}
+
+type_init(usb_serial_register_types)
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
new file mode 100644 (file)
index 0000000..8e66675
--- /dev/null
@@ -0,0 +1,1365 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * CCID Device emulation
+ *
+ * Written by Alon Levy, with contributions from Robert Relyea.
+ *
+ * Based on usb-serial.c, see its copyright and attributions below.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ * ------- (original copyright & attribution for usb-serial.c below) --------
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault,
+ */
+
+/*
+ * References:
+ *
+ * CCID Specification Revision 1.1 April 22nd 2005
+ *  "Universal Serial Bus, Device Class: Smart Card"
+ *  Specification for Integrated Circuit(s) Cards Interface Devices
+ *
+ * Endianness note: from the spec (1.3)
+ *  "Fields that are larger than a byte are stored in little endian"
+ *
+ * KNOWN BUGS
+ * 1. remove/insert can sometimes result in removed state instead of inserted.
+ * This is a result of the following:
+ *  symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This can happen
+ *  when a short packet is sent, as seen in uhci-usb.c, resulting from a urb
+ *  from the guest requesting SPD and us returning a smaller packet.
+ *  Not sure which messages trigger this.
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "monitor.h"
+
+#include "hw/ccid.h"
+
+#define DPRINTF(s, lvl, fmt, ...) \
+do { \
+    if (lvl <= s->debug) { \
+        printf("usb-ccid: " fmt , ## __VA_ARGS__); \
+    } \
+} while (0)
+
+#define D_WARN 1
+#define D_INFO 2
+#define D_MORE_INFO 3
+#define D_VERBOSE 4
+
+#define CCID_DEV_NAME "usb-ccid"
+
+/*
+ * The two options for variable sized buffers:
+ * make them constant size, for large enough constant,
+ * or handle the migration complexity - VMState doesn't handle this case.
+ * sizes are expected never to be exceeded, unless guest misbehaves.
+ */
+#define BULK_OUT_DATA_SIZE 65536
+#define PENDING_ANSWERS_NUM 128
+
+#define BULK_IN_BUF_SIZE 384
+#define BULK_IN_PENDING_NUM 8
+
+#define InterfaceOutClass \
+    ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8)
+
+#define InterfaceInClass  \
+    ((USB_DIR_IN  | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8)
+
+#define CCID_MAX_PACKET_SIZE                64
+
+#define CCID_CONTROL_ABORT                  0x1
+#define CCID_CONTROL_GET_CLOCK_FREQUENCIES  0x2
+#define CCID_CONTROL_GET_DATA_RATES         0x3
+
+#define CCID_PRODUCT_DESCRIPTION        "QEMU USB CCID"
+#define CCID_VENDOR_DESCRIPTION         "QEMU " QEMU_VERSION
+#define CCID_INTERFACE_NAME             "CCID Interface"
+#define CCID_SERIAL_NUMBER_STRING       "1"
+/*
+ * Using Gemplus Vendor and Product id
+ * Effect on various drivers:
+ *  usbccid.sys (winxp, others untested) is a class driver so it doesn't care.
+ *  linux has a number of class drivers, but openct filters based on
+ *   vendor/product (/etc/openct.conf under fedora), hence Gemplus.
+ */
+#define CCID_VENDOR_ID                  0x08e6
+#define CCID_PRODUCT_ID                 0x4433
+#define CCID_DEVICE_VERSION             0x0000
+
+/*
+ * BULK_OUT messages from PC to Reader
+ * Defined in CCID Rev 1.1 6.1 (page 26)
+ */
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn              0x62
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff             0x63
+#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus           0x65
+#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock                0x6f
+#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters           0x6c
+#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters         0x6d
+#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters           0x61
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape                  0x6b
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock                0x6e
+#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU                  0x6a
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure                  0x69
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical              0x71
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort                   0x72
+#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73
+
+/*
+ * BULK_IN messages from Reader to PC
+ * Defined in CCID Rev 1.1 6.2 (page 48)
+ */
+#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock               0x80
+#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus              0x81
+#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters              0x82
+#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape                  0x83
+#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84
+
+/*
+ * INTERRUPT_IN messages from Reader to PC
+ * Defined in CCID Rev 1.1 6.3 (page 56)
+ */
+#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange        0x50
+#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError           0x51
+
+/*
+ * Endpoints for CCID - addresses are up to us to decide.
+ * To support slot insertion and removal we must have an interrupt in ep
+ * in addition we need a bulk in and bulk out ep
+ * 5.2, page 20
+ */
+#define CCID_INT_IN_EP       1
+#define CCID_BULK_IN_EP      2
+#define CCID_BULK_OUT_EP     3
+
+/* bmSlotICCState masks */
+#define SLOT_0_STATE_MASK    1
+#define SLOT_0_CHANGED_MASK  2
+
+/* Status codes that go in bStatus (see 6.2.6) */
+enum {
+    ICC_STATUS_PRESENT_ACTIVE = 0,
+    ICC_STATUS_PRESENT_INACTIVE,
+    ICC_STATUS_NOT_PRESENT
+};
+
+enum {
+    COMMAND_STATUS_NO_ERROR = 0,
+    COMMAND_STATUS_FAILED,
+    COMMAND_STATUS_TIME_EXTENSION_REQUIRED
+};
+
+/* Error codes that go in bError (see 6.2.6) */
+enum {
+    ERROR_CMD_NOT_SUPPORTED = 0,
+    ERROR_CMD_ABORTED       = -1,
+    ERROR_ICC_MUTE          = -2,
+    ERROR_XFR_PARITY_ERROR  = -3,
+    ERROR_XFR_OVERRUN       = -4,
+    ERROR_HW_ERROR          = -5,
+};
+
+/* 6.2.6 RDR_to_PC_SlotStatus definitions */
+enum {
+    CLOCK_STATUS_RUNNING = 0,
+    /*
+     * 0 - Clock Running, 1 - Clock stopped in State L, 2 - H,
+     * 3 - unknown state. rest are RFU
+     */
+};
+
+typedef struct QEMU_PACKED CCID_Header {
+    uint8_t     bMessageType;
+    uint32_t    dwLength;
+    uint8_t     bSlot;
+    uint8_t     bSeq;
+} CCID_Header;
+
+typedef struct QEMU_PACKED CCID_BULK_IN {
+    CCID_Header hdr;
+    uint8_t     bStatus;        /* Only used in BULK_IN */
+    uint8_t     bError;         /* Only used in BULK_IN */
+} CCID_BULK_IN;
+
+typedef struct QEMU_PACKED CCID_SlotStatus {
+    CCID_BULK_IN b;
+    uint8_t     bClockStatus;
+} CCID_SlotStatus;
+
+typedef struct QEMU_PACKED CCID_Parameter {
+    CCID_BULK_IN b;
+    uint8_t     bProtocolNum;
+    uint8_t     abProtocolDataStructure[0];
+} CCID_Parameter;
+
+typedef struct QEMU_PACKED CCID_DataBlock {
+    CCID_BULK_IN b;
+    uint8_t      bChainParameter;
+    uint8_t      abData[0];
+} CCID_DataBlock;
+
+/* 6.1.4 PC_to_RDR_XfrBlock */
+typedef struct QEMU_PACKED CCID_XferBlock {
+    CCID_Header  hdr;
+    uint8_t      bBWI; /* Block Waiting Timeout */
+    uint16_t     wLevelParameter; /* XXX currently unused */
+    uint8_t      abData[0];
+} CCID_XferBlock;
+
+typedef struct QEMU_PACKED CCID_IccPowerOn {
+    CCID_Header hdr;
+    uint8_t     bPowerSelect;
+    uint16_t    abRFU;
+} CCID_IccPowerOn;
+
+typedef struct QEMU_PACKED CCID_IccPowerOff {
+    CCID_Header hdr;
+    uint16_t    abRFU;
+} CCID_IccPowerOff;
+
+typedef struct QEMU_PACKED CCID_SetParameters {
+    CCID_Header hdr;
+    uint8_t     bProtocolNum;
+    uint16_t   abRFU;
+    uint8_t    abProtocolDataStructure[0];
+} CCID_SetParameters;
+
+typedef struct CCID_Notify_Slot_Change {
+    uint8_t     bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */
+    uint8_t     bmSlotICCState;
+} CCID_Notify_Slot_Change;
+
+/* used for DataBlock response to XferBlock */
+typedef struct Answer {
+    uint8_t slot;
+    uint8_t seq;
+} Answer;
+
+/* pending BULK_IN messages */
+typedef struct BulkIn {
+    uint8_t  data[BULK_IN_BUF_SIZE];
+    uint32_t len;
+    uint32_t pos;
+} BulkIn;
+
+enum {
+    MIGRATION_NONE,
+    MIGRATION_MIGRATED,
+};
+
+typedef struct CCIDBus {
+    BusState qbus;
+} CCIDBus;
+
+#define MAX_PROTOCOL_SIZE   7
+
+/*
+ * powered - defaults to true, changed by PowerOn/PowerOff messages
+ */
+typedef struct USBCCIDState {
+    USBDevice dev;
+    USBEndpoint *intr;
+    CCIDBus bus;
+    CCIDCardState *card;
+    BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */
+    uint32_t bulk_in_pending_start;
+    uint32_t bulk_in_pending_end; /* first free */
+    uint32_t bulk_in_pending_num;
+    BulkIn *current_bulk_in;
+    uint8_t  bulk_out_data[BULK_OUT_DATA_SIZE];
+    uint32_t bulk_out_pos;
+    uint64_t last_answer_error;
+    Answer pending_answers[PENDING_ANSWERS_NUM];
+    uint32_t pending_answers_start;
+    uint32_t pending_answers_end;
+    uint32_t pending_answers_num;
+    uint8_t  bError;
+    uint8_t  bmCommandStatus;
+    uint8_t  bProtocolNum;
+    uint8_t  abProtocolDataStructure[MAX_PROTOCOL_SIZE];
+    uint32_t ulProtocolDataStructureSize;
+    uint32_t state_vmstate;
+    uint32_t migration_target_ip;
+    uint16_t migration_target_port;
+    uint8_t  migration_state;
+    uint8_t  bmSlotICCState;
+    uint8_t  powered;
+    uint8_t  notify_slot_change;
+    uint8_t  debug;
+} USBCCIDState;
+
+/*
+ * CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9,
+ * "USB Device Framework", section 9.6.1, in the Universal Serial Bus
+ * Specification.
+ *
+ * This device implemented based on the spec and with an Athena Smart Card
+ * Reader as reference:
+ *   0dc3:1004 Athena Smartcard Solutions, Inc.
+ */
+
+static const uint8_t qemu_ccid_descriptor[] = {
+        /* Smart Card Device Class Descriptor */
+        0x36,       /* u8  bLength; */
+        0x21,       /* u8  bDescriptorType; Functional */
+        0x10, 0x01, /* u16 bcdCCID; CCID Specification Release Number. */
+        0x00,       /*
+                     * u8  bMaxSlotIndex; The index of the highest available
+                     * slot on this device. All slots are consecutive starting
+                     * at 00h.
+                     */
+        0x07,       /* u8  bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */
+
+        0x03, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
+        0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
+                    /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */
+        0xa0, 0x0f, 0x00, 0x00,
+                    /* u32 dwMaximumClock; */
+        0x00, 0x00, 0x01, 0x00,
+        0x00,       /* u8 bNumClockSupported;                 *
+                     *    0 means just the default and max.   */
+                    /* u32 dwDataRate ;bps. 9600 == 00002580h */
+        0x80, 0x25, 0x00, 0x00,
+                    /* u32 dwMaxDataRate ; 11520 bps == 0001C200h */
+        0x00, 0xC2, 0x01, 0x00,
+        0x00,       /* u8  bNumDataRatesSupported; 00 means all rates between
+                     *     default and max */
+                    /* u32 dwMaxIFSD;                                  *
+                     *     maximum IFSD supported by CCID for protocol *
+                     *     T=1 (Maximum seen from various cards)       */
+        0xfe, 0x00, 0x00, 0x00,
+                    /* u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */
+        0x00, 0x00, 0x00, 0x00,
+                    /* u32 dwMechanical;  0 - no special characteristics. */
+        0x00, 0x00, 0x00, 0x00,
+                    /*
+                     * u32 dwFeatures;
+                     * 0 - No special characteristics
+                     * + 2 Automatic parameter configuration based on ATR data
+                     * + 4 Automatic activation of ICC on inserting
+                     * + 8 Automatic ICC voltage selection
+                     * + 10 Automatic ICC clock frequency change
+                     * + 20 Automatic baud rate change
+                     * + 40 Automatic parameters negotiation made by the CCID
+                     * + 80 automatic PPS made by the CCID
+                     * 100 CCID can set ICC in clock stop mode
+                     * 200 NAD value other then 00 accepted (T=1 protocol)
+                     * + 400 Automatic IFSD exchange as first exchange (T=1)
+                     * One of the following only:
+                     * + 10000 TPDU level exchanges with CCID
+                     * 20000 Short APDU level exchange with CCID
+                     * 40000 Short and Extended APDU level exchange with CCID
+                     *
+                     * + 100000 USB Wake up signaling supported on card
+                     * insertion and removal. Must set bit 5 in bmAttributes
+                     * in Configuration descriptor if 100000 is set.
+                     */
+        0xfe, 0x04, 0x11, 0x00,
+                    /*
+                     * u32 dwMaxCCIDMessageLength; For extended APDU in
+                     * [261 + 10 , 65544 + 10]. Otherwise the minimum is
+                     * wMaxPacketSize of the Bulk-OUT endpoint
+                     */
+        0x12, 0x00, 0x01, 0x00,
+        0xFF,       /*
+                     * u8  bClassGetResponse; Significant only for CCID that
+                     * offers an APDU level for exchanges. Indicates the
+                     * default class value used by the CCID when it sends a
+                     * Get Response command to perform the transportation of
+                     * an APDU by T=0 protocol
+                     * FFh indicates that the CCID echos the class of the APDU.
+                     */
+        0xFF,       /*
+                     * u8  bClassEnvelope; EAPDU only. Envelope command for
+                     * T=0
+                     */
+        0x00, 0x00, /*
+                     * u16 wLcdLayout; XXYY Number of lines (XX) and chars per
+                     * line for LCD display used for PIN entry. 0000 - no LCD
+                     */
+        0x01,       /*
+                     * u8  bPINSupport; 01h PIN Verification,
+                     *                  02h PIN Modification
+                     */
+        0x01,       /* u8  bMaxCCIDBusySlots; */
+};
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT,
+    STR_SERIALNUMBER,
+    STR_INTERFACE,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER]  = "QEMU " QEMU_VERSION,
+    [STR_PRODUCT]       = "QEMU USB CCID",
+    [STR_SERIALNUMBER]  = "1",
+    [STR_INTERFACE]     = "CCID Interface",
+};
+
+static const USBDescIface desc_iface0 = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 3,
+    .bInterfaceClass               = 0x0b,
+    .bInterfaceSubClass            = 0x00,
+    .bInterfaceProtocol            = 0x00,
+    .iInterface                    = STR_INTERFACE,
+    .ndesc                         = 1,
+    .descs = (USBDescOther[]) {
+        {
+            /* smartcard descriptor */
+            .data = qemu_ccid_descriptor,
+        },
+    },
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | CCID_INT_IN_EP,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .bInterval             = 255,
+            .wMaxPacketSize        = 64,
+        },{
+            .bEndpointAddress      = USB_DIR_IN | CCID_BULK_IN_EP,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },{
+            .bEndpointAddress      = USB_DIR_OUT | CCID_BULK_OUT_EP,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },
+    }
+};
+
+static const USBDescDevice desc_device = {
+    .bcdUSB                        = 0x0110,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .bmAttributes          = 0xe0,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface0,
+        },
+    },
+};
+
+static const USBDesc desc_ccid = {
+    .id = {
+        .idVendor          = CCID_VENDOR_ID,
+        .idProduct         = CCID_PRODUCT_ID,
+        .bcdDevice         = CCID_DEVICE_VERSION,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device,
+    .str  = desc_strings,
+};
+
+static const uint8_t *ccid_card_get_atr(CCIDCardState *card, uint32_t *len)
+{
+    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
+    if (cc->get_atr) {
+        return cc->get_atr(card, len);
+    }
+    return NULL;
+}
+
+static void ccid_card_apdu_from_guest(CCIDCardState *card,
+                                      const uint8_t *apdu,
+                                      uint32_t len)
+{
+    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
+    if (cc->apdu_from_guest) {
+        cc->apdu_from_guest(card, apdu, len);
+    }
+}
+
+static int ccid_card_exitfn(CCIDCardState *card)
+{
+    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
+    if (cc->exitfn) {
+        return cc->exitfn(card);
+    }
+    return 0;
+}
+
+static int ccid_card_initfn(CCIDCardState *card)
+{
+    CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
+    if (cc->initfn) {
+        return cc->initfn(card);
+    }
+    return 0;
+}
+
+static bool ccid_has_pending_answers(USBCCIDState *s)
+{
+    return s->pending_answers_num > 0;
+}
+
+static void ccid_clear_pending_answers(USBCCIDState *s)
+{
+    s->pending_answers_num = 0;
+    s->pending_answers_start = 0;
+    s->pending_answers_end = 0;
+}
+
+static void ccid_print_pending_answers(USBCCIDState *s)
+{
+    Answer *answer;
+    int i, count;
+
+    DPRINTF(s, D_VERBOSE, "usb-ccid: pending answers:");
+    if (!ccid_has_pending_answers(s)) {
+        DPRINTF(s, D_VERBOSE, " empty\n");
+        return;
+    }
+    for (i = s->pending_answers_start, count = s->pending_answers_num ;
+         count > 0; count--, i++) {
+        answer = &s->pending_answers[i % PENDING_ANSWERS_NUM];
+        if (count == 1) {
+            DPRINTF(s, D_VERBOSE, "%d:%d\n", answer->slot, answer->seq);
+        } else {
+            DPRINTF(s, D_VERBOSE, "%d:%d,", answer->slot, answer->seq);
+        }
+    }
+}
+
+static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr)
+{
+    Answer *answer;
+
+    assert(s->pending_answers_num < PENDING_ANSWERS_NUM);
+    s->pending_answers_num++;
+    answer =
+        &s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM];
+    answer->slot = hdr->bSlot;
+    answer->seq = hdr->bSeq;
+    ccid_print_pending_answers(s);
+}
+
+static void ccid_remove_pending_answer(USBCCIDState *s,
+    uint8_t *slot, uint8_t *seq)
+{
+    Answer *answer;
+
+    assert(s->pending_answers_num > 0);
+    s->pending_answers_num--;
+    answer =
+        &s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM];
+    *slot = answer->slot;
+    *seq = answer->seq;
+    ccid_print_pending_answers(s);
+}
+
+static void ccid_bulk_in_clear(USBCCIDState *s)
+{
+    s->bulk_in_pending_start = 0;
+    s->bulk_in_pending_end = 0;
+    s->bulk_in_pending_num = 0;
+}
+
+static void ccid_bulk_in_release(USBCCIDState *s)
+{
+    assert(s->current_bulk_in != NULL);
+    s->current_bulk_in->pos = 0;
+    s->current_bulk_in = NULL;
+}
+
+static void ccid_bulk_in_get(USBCCIDState *s)
+{
+    if (s->current_bulk_in != NULL || s->bulk_in_pending_num == 0) {
+        return;
+    }
+    assert(s->bulk_in_pending_num > 0);
+    s->bulk_in_pending_num--;
+    s->current_bulk_in =
+        &s->bulk_in_pending[(s->bulk_in_pending_start++) % BULK_IN_PENDING_NUM];
+}
+
+static void *ccid_reserve_recv_buf(USBCCIDState *s, uint16_t len)
+{
+    BulkIn *bulk_in;
+
+    DPRINTF(s, D_VERBOSE, "%s: QUEUE: reserve %d bytes\n", __func__, len);
+
+    /* look for an existing element */
+    if (len > BULK_IN_BUF_SIZE) {
+        DPRINTF(s, D_WARN, "usb-ccid.c: %s: len larger then max (%d>%d). "
+                           "discarding message.\n",
+                           __func__, len, BULK_IN_BUF_SIZE);
+        return NULL;
+    }
+    if (s->bulk_in_pending_num >= BULK_IN_PENDING_NUM) {
+        DPRINTF(s, D_WARN, "usb-ccid.c: %s: No free bulk_in buffers. "
+                           "discarding message.\n", __func__);
+        return NULL;
+    }
+    bulk_in =
+        &s->bulk_in_pending[(s->bulk_in_pending_end++) % BULK_IN_PENDING_NUM];
+    s->bulk_in_pending_num++;
+    bulk_in->len = len;
+    return bulk_in->data;
+}
+
+static void ccid_reset(USBCCIDState *s)
+{
+    ccid_bulk_in_clear(s);
+    ccid_clear_pending_answers(s);
+}
+
+static void ccid_detach(USBCCIDState *s)
+{
+    ccid_reset(s);
+}
+
+static void ccid_handle_reset(USBDevice *dev)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+
+    DPRINTF(s, 1, "Reset\n");
+
+    ccid_reset(s);
+}
+
+static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
+                               int value, int index, int length, uint8_t *data)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+    int ret = 0;
+
+    DPRINTF(s, 1, "got control %x, value %x\n", request, value);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    switch (request) {
+        /* Class specific requests.  */
+    case InterfaceOutClass | CCID_CONTROL_ABORT:
+        DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
+        DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES:
+        DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    default:
+        DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",
+                request, value);
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static bool ccid_card_inserted(USBCCIDState *s)
+{
+    return s->bmSlotICCState & SLOT_0_STATE_MASK;
+}
+
+static uint8_t ccid_card_status(USBCCIDState *s)
+{
+    return ccid_card_inserted(s)
+            ? (s->powered ?
+                ICC_STATUS_PRESENT_ACTIVE
+              : ICC_STATUS_PRESENT_INACTIVE
+              )
+            : ICC_STATUS_NOT_PRESENT;
+}
+
+static uint8_t ccid_calc_status(USBCCIDState *s)
+{
+    /*
+     * page 55, 6.2.6, calculation of bStatus from bmICCStatus and
+     * bmCommandStatus
+     */
+    uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6);
+    DPRINTF(s, D_VERBOSE, "status = %d\n", ret);
+    return ret;
+}
+
+static void ccid_reset_error_status(USBCCIDState *s)
+{
+    s->bError = ERROR_CMD_NOT_SUPPORTED;
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+}
+
+static void ccid_write_slot_status(USBCCIDState *s, CCID_Header *recv)
+{
+    CCID_SlotStatus *h = ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus));
+    if (h == NULL) {
+        return;
+    }
+    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus;
+    h->b.hdr.dwLength = 0;
+    h->b.hdr.bSlot = recv->bSlot;
+    h->b.hdr.bSeq = recv->bSeq;
+    h->b.bStatus = ccid_calc_status(s);
+    h->b.bError = s->bError;
+    h->bClockStatus = CLOCK_STATUS_RUNNING;
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv)
+{
+    CCID_Parameter *h;
+    uint32_t len = s->ulProtocolDataStructureSize;
+
+    h = ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len);
+    if (h == NULL) {
+        return;
+    }
+    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters;
+    h->b.hdr.dwLength = 0;
+    h->b.hdr.bSlot = recv->bSlot;
+    h->b.hdr.bSeq = recv->bSeq;
+    h->b.bStatus = ccid_calc_status(s);
+    h->b.bError = s->bError;
+    h->bProtocolNum = s->bProtocolNum;
+    memcpy(h->abProtocolDataStructure, s->abProtocolDataStructure, len);
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq,
+                                  const uint8_t *data, uint32_t len)
+{
+    CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len);
+
+    if (p == NULL) {
+        return;
+    }
+    p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock;
+    p->b.hdr.dwLength = cpu_to_le32(len);
+    p->b.hdr.bSlot = slot;
+    p->b.hdr.bSeq = seq;
+    p->b.bStatus = ccid_calc_status(s);
+    p->b.bError = s->bError;
+    if (p->b.bError) {
+        DPRINTF(s, D_VERBOSE, "error %d", p->b.bError);
+    }
+    memcpy(p->abData, data, len);
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_data_block_answer(USBCCIDState *s,
+    const uint8_t *data, uint32_t len)
+{
+    uint8_t seq;
+    uint8_t slot;
+
+    if (!ccid_has_pending_answers(s)) {
+        abort();
+    }
+    ccid_remove_pending_answer(s, &slot, &seq);
+    ccid_write_data_block(s, slot, seq, data, len);
+}
+
+static void ccid_write_data_block_atr(USBCCIDState *s, CCID_Header *recv)
+{
+    const uint8_t *atr = NULL;
+    uint32_t len = 0;
+
+    if (s->card) {
+        atr = ccid_card_get_atr(s->card, &len);
+    }
+    ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len);
+}
+
+static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv)
+{
+    CCID_SetParameters *ph = (CCID_SetParameters *) recv;
+    uint32_t len = 0;
+    if ((ph->bProtocolNum & 3) == 0) {
+        len = 5;
+    }
+    if ((ph->bProtocolNum & 3) == 1) {
+        len = 7;
+    }
+    if (len == 0) {
+        s->bmCommandStatus = COMMAND_STATUS_FAILED;
+        s->bError = 7; /* Protocol invalid or not supported */
+        return;
+    }
+    s->bProtocolNum = ph->bProtocolNum;
+    memcpy(s->abProtocolDataStructure, ph->abProtocolDataStructure, len);
+    s->ulProtocolDataStructureSize = len;
+    DPRINTF(s, 1, "%s: using len %d\n", __func__, len);
+}
+
+/*
+ * must be 5 bytes for T=0, 7 bytes for T=1
+ * See page 52
+ */
+static const uint8_t abDefaultProtocolDataStructure[7] = {
+    0x77, 0x00, 0x00, 0x00, 0x00, 0xfe /*IFSC*/, 0x00 /*NAD*/ };
+
+static void ccid_reset_parameters(USBCCIDState *s)
+{
+   uint32_t len = sizeof(abDefaultProtocolDataStructure);
+
+   s->bProtocolNum = 1; /* T=1 */
+   s->ulProtocolDataStructureSize = len;
+   memcpy(s->abProtocolDataStructure, abDefaultProtocolDataStructure, len);
+}
+
+static void ccid_report_error_failed(USBCCIDState *s, uint8_t error)
+{
+    s->bmCommandStatus = COMMAND_STATUS_FAILED;
+    s->bError = error;
+}
+
+/* NOTE: only a single slot is supported (SLOT_0) */
+static void ccid_on_slot_change(USBCCIDState *s, bool full)
+{
+    /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56 */
+    uint8_t current = s->bmSlotICCState;
+    if (full) {
+        s->bmSlotICCState |= SLOT_0_STATE_MASK;
+    } else {
+        s->bmSlotICCState &= ~SLOT_0_STATE_MASK;
+    }
+    if (current != s->bmSlotICCState) {
+        s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
+    }
+    s->notify_slot_change = true;
+    usb_wakeup(s->intr);
+}
+
+static void ccid_write_data_block_error(
+    USBCCIDState *s, uint8_t slot, uint8_t seq)
+{
+    ccid_write_data_block(s, slot, seq, NULL, 0);
+}
+
+static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
+{
+    uint32_t len;
+
+    if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) {
+        DPRINTF(s, 1,
+                "usb-ccid: not sending apdu to client, no card connected\n");
+        ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq);
+        return;
+    }
+    len = le32_to_cpu(recv->hdr.dwLength);
+    DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__,
+                recv->hdr.bSeq, len);
+    ccid_add_pending_answer(s, (CCID_Header *)recv);
+    if (s->card) {
+        ccid_card_apdu_from_guest(s->card, recv->abData, len);
+    } else {
+        DPRINTF(s, D_WARN, "warning: discarded apdu\n");
+    }
+}
+
+/*
+ * Handle a single USB_TOKEN_OUT, return value returned to guest.
+ * Return value:
+ *  0             - all ok
+ *  USB_RET_STALL - failed to handle packet
+ */
+static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
+{
+    CCID_Header *ccid_header;
+
+    if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
+        return USB_RET_STALL;
+    }
+    ccid_header = (CCID_Header *)s->bulk_out_data;
+    usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
+    s->bulk_out_pos += p->iov.size;
+    if (p->iov.size == CCID_MAX_PACKET_SIZE) {
+        DPRINTF(s, D_VERBOSE,
+            "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
+            p->iov.size, ccid_header->dwLength);
+        return 0;
+    }
+    if (s->bulk_out_pos < 10) {
+        DPRINTF(s, 1,
+                "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n",
+                __func__);
+    } else {
+        DPRINTF(s, D_MORE_INFO, "%s %x\n", __func__, ccid_header->bMessageType);
+        switch (ccid_header->bMessageType) {
+        case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
+            ccid_write_slot_status(s, ccid_header);
+            break;
+        case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
+            DPRINTF(s, 1, "PowerOn: %d\n",
+                ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect);
+            s->powered = true;
+            if (!ccid_card_inserted(s)) {
+                ccid_report_error_failed(s, ERROR_ICC_MUTE);
+            }
+            /* atr is written regardless of error. */
+            ccid_write_data_block_atr(s, ccid_header);
+            break;
+        case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
+            DPRINTF(s, 1, "PowerOff\n");
+            ccid_reset_error_status(s);
+            s->powered = false;
+            ccid_write_slot_status(s, ccid_header);
+            break;
+        case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
+            ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data);
+            break;
+        case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
+            ccid_reset_error_status(s);
+            ccid_set_parameters(s, ccid_header);
+            ccid_write_parameters(s, ccid_header);
+            break;
+        case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
+            ccid_reset_error_status(s);
+            ccid_reset_parameters(s);
+            ccid_write_parameters(s, ccid_header);
+            break;
+        case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
+            ccid_reset_error_status(s);
+            ccid_write_parameters(s, ccid_header);
+            break;
+        default:
+            DPRINTF(s, 1,
+                "handle_data: ERROR: unhandled message type %Xh\n",
+                ccid_header->bMessageType);
+            /*
+             * The caller is expecting the device to respond, tell it we
+             * don't support the operation.
+             */
+            ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
+            ccid_write_slot_status(s, ccid_header);
+            break;
+        }
+    }
+    s->bulk_out_pos = 0;
+    return 0;
+}
+
+static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
+{
+    int ret = 0;
+
+    assert(p->iov.size > 0);
+    ccid_bulk_in_get(s);
+    if (s->current_bulk_in != NULL) {
+        ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
+                  p->iov.size);
+        usb_packet_copy(p, s->current_bulk_in->data +
+                        s->current_bulk_in->pos, ret);
+        s->current_bulk_in->pos += ret;
+        if (s->current_bulk_in->pos == s->current_bulk_in->len) {
+            ccid_bulk_in_release(s);
+        }
+    } else {
+        /* return when device has no data - usb 2.0 spec Table 8-4 */
+        ret = USB_RET_NAK;
+    }
+    if (ret > 0) {
+        DPRINTF(s, D_MORE_INFO,
+                "%s: %zd/%d req/act to guest (BULK_IN)\n",
+                __func__, p->iov.size, ret);
+    }
+    if (ret != USB_RET_NAK && ret < p->iov.size) {
+        DPRINTF(s, 1,
+                "%s: returning short (EREMOTEIO) %d < %zd\n",
+                __func__, ret, p->iov.size);
+    }
+    return ret;
+}
+
+static int ccid_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+    int ret = 0;
+    uint8_t buf[2];
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        ret = ccid_handle_bulk_out(s, p);
+        break;
+
+    case USB_TOKEN_IN:
+        switch (p->ep->nr) {
+        case CCID_BULK_IN_EP:
+            if (!p->iov.size) {
+                ret = USB_RET_NAK;
+            } else {
+                ret = ccid_bulk_in_copy_to_guest(s, p);
+            }
+            break;
+        case CCID_INT_IN_EP:
+            if (s->notify_slot_change) {
+                /* page 56, RDR_to_PC_NotifySlotChange */
+                buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
+                buf[1] = s->bmSlotICCState;
+                usb_packet_copy(p, buf, 2);
+                ret = 2;
+                s->notify_slot_change = false;
+                s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
+                DPRINTF(s, D_INFO,
+                        "handle_data: int_in: notify_slot_change %X, "
+                        "requested len %zd\n",
+                        s->bmSlotICCState, p->iov.size);
+            }
+            break;
+        default:
+            DPRINTF(s, 1, "Bad endpoint\n");
+            ret = USB_RET_STALL;
+            break;
+        }
+        break;
+    default:
+        DPRINTF(s, 1, "Bad token\n");
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void ccid_handle_destroy(USBDevice *dev)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+
+    ccid_bulk_in_clear(s);
+}
+
+static void ccid_flush_pending_answers(USBCCIDState *s)
+{
+    while (ccid_has_pending_answers(s)) {
+        ccid_write_data_block_answer(s, NULL, 0);
+    }
+}
+
+static Answer *ccid_peek_next_answer(USBCCIDState *s)
+{
+    return s->pending_answers_num == 0
+        ? NULL
+        : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM];
+}
+
+static struct BusInfo ccid_bus_info = {
+    .name = "ccid-bus",
+    .size = sizeof(CCIDBus),
+    .props = (Property[]) {
+        DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+void ccid_card_send_apdu_to_guest(CCIDCardState *card,
+                                  uint8_t *apdu, uint32_t len)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev,
+                                card->qdev.parent_bus->parent);
+    Answer *answer;
+
+    if (!ccid_has_pending_answers(s)) {
+        DPRINTF(s, 1, "CCID ERROR: got an APDU without pending answers\n");
+        return;
+    }
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+    answer = ccid_peek_next_answer(s);
+    if (answer == NULL) {
+        abort();
+    }
+    DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n",
+        len, answer->seq, answer->slot);
+    ccid_write_data_block_answer(s, apdu, len);
+}
+
+void ccid_card_card_removed(CCIDCardState *card)
+{
+    USBCCIDState *s =
+        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    ccid_on_slot_change(s, false);
+    ccid_flush_pending_answers(s);
+    ccid_reset(s);
+}
+
+int ccid_card_ccid_attach(CCIDCardState *card)
+{
+    USBCCIDState *s =
+        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    DPRINTF(s, 1, "CCID Attach\n");
+    if (s->migration_state == MIGRATION_MIGRATED) {
+        s->migration_state = MIGRATION_NONE;
+    }
+    return 0;
+}
+
+void ccid_card_ccid_detach(CCIDCardState *card)
+{
+    USBCCIDState *s =
+        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    DPRINTF(s, 1, "CCID Detach\n");
+    if (ccid_card_inserted(s)) {
+        ccid_on_slot_change(s, false);
+    }
+    ccid_detach(s);
+}
+
+void ccid_card_card_error(CCIDCardState *card, uint64_t error)
+{
+    USBCCIDState *s =
+        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    s->bmCommandStatus = COMMAND_STATUS_FAILED;
+    s->last_answer_error = error;
+    DPRINTF(s, 1, "VSC_Error: %" PRIX64 "\n", s->last_answer_error);
+    /* TODO: these errors should be more verbose and propagated to the guest.*/
+    /*
+     * We flush all pending answers on CardRemove message in ccid-card-passthru,
+     * so check that first to not trigger abort
+     */
+    if (ccid_has_pending_answers(s)) {
+        ccid_write_data_block_answer(s, NULL, 0);
+    }
+}
+
+void ccid_card_card_inserted(CCIDCardState *card)
+{
+    USBCCIDState *s =
+        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+    ccid_flush_pending_answers(s);
+    ccid_on_slot_change(s, true);
+}
+
+static int ccid_card_exit(DeviceState *qdev)
+{
+    int ret = 0;
+    CCIDCardState *card = CCID_CARD(qdev);
+    USBCCIDState *s =
+        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    if (ccid_card_inserted(s)) {
+        ccid_card_card_removed(card);
+    }
+    ret = ccid_card_exitfn(card);
+    s->card = NULL;
+    return ret;
+}
+
+static int ccid_card_init(DeviceState *qdev)
+{
+    CCIDCardState *card = CCID_CARD(qdev);
+    USBCCIDState *s =
+        DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+    int ret = 0;
+
+    if (card->slot != 0) {
+        error_report("Warning: usb-ccid supports one slot, can't add %d",
+                card->slot);
+        return -1;
+    }
+    if (s->card != NULL) {
+        error_report("Warning: usb-ccid card already full, not adding");
+        return -1;
+    }
+    ret = ccid_card_initfn(card);
+    if (ret == 0) {
+        s->card = card;
+    }
+    return ret;
+}
+
+static int ccid_initfn(USBDevice *dev)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+
+    usb_desc_init(dev);
+    qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL);
+    s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP);
+    s->bus.qbus.allow_hotplug = 1;
+    s->card = NULL;
+    s->migration_state = MIGRATION_NONE;
+    s->migration_target_ip = 0;
+    s->migration_target_port = 0;
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.speedmask = USB_SPEED_MASK_FULL;
+    s->notify_slot_change = false;
+    s->powered = true;
+    s->pending_answers_num = 0;
+    s->last_answer_error = 0;
+    s->bulk_in_pending_start = 0;
+    s->bulk_in_pending_end = 0;
+    s->current_bulk_in = NULL;
+    ccid_reset_error_status(s);
+    s->bulk_out_pos = 0;
+    ccid_reset_parameters(s);
+    ccid_reset(s);
+    return 0;
+}
+
+static int ccid_post_load(void *opaque, int version_id)
+{
+    USBCCIDState *s = opaque;
+
+    /*
+     * This must be done after usb_device_attach, which sets state to ATTACHED,
+     * while it must be DEFAULT in order to accept packets (like it is after
+     * reset, but reset will reset our addr and call our reset handler which
+     * may change state, and we don't want to do that when migrating).
+     */
+    s->dev.state = s->state_vmstate;
+    return 0;
+}
+
+static void ccid_pre_save(void *opaque)
+{
+    USBCCIDState *s = opaque;
+
+    s->state_vmstate = s->dev.state;
+    if (s->dev.attached) {
+        /*
+         * Migrating an open device, ignore reconnection CHR_EVENT to avoid an
+         * erroneous detach.
+         */
+        s->migration_state = MIGRATION_MIGRATED;
+    }
+}
+
+static VMStateDescription bulk_in_vmstate = {
+    .name = "CCID BulkIn state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_BUFFER(data, BulkIn),
+        VMSTATE_UINT32(len, BulkIn),
+        VMSTATE_UINT32(pos, BulkIn),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static VMStateDescription answer_vmstate = {
+    .name = "CCID Answer state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(slot, Answer),
+        VMSTATE_UINT8(seq, Answer),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static VMStateDescription usb_device_vmstate = {
+    .name = "usb_device",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(addr, USBDevice),
+        VMSTATE_BUFFER(setup_buf, USBDevice),
+        VMSTATE_BUFFER(data_buf, USBDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static VMStateDescription ccid_vmstate = {
+    .name = CCID_DEV_NAME,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = ccid_post_load,
+    .pre_save = ccid_pre_save,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice),
+        VMSTATE_UINT8(debug, USBCCIDState),
+        VMSTATE_BUFFER(bulk_out_data, USBCCIDState),
+        VMSTATE_UINT32(bulk_out_pos, USBCCIDState),
+        VMSTATE_UINT8(bmSlotICCState, USBCCIDState),
+        VMSTATE_UINT8(powered, USBCCIDState),
+        VMSTATE_UINT8(notify_slot_change, USBCCIDState),
+        VMSTATE_UINT64(last_answer_error, USBCCIDState),
+        VMSTATE_UINT8(bError, USBCCIDState),
+        VMSTATE_UINT8(bmCommandStatus, USBCCIDState),
+        VMSTATE_UINT8(bProtocolNum, USBCCIDState),
+        VMSTATE_BUFFER(abProtocolDataStructure, USBCCIDState),
+        VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState),
+        VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState,
+                       BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn),
+        VMSTATE_UINT32(bulk_in_pending_start, USBCCIDState),
+        VMSTATE_UINT32(bulk_in_pending_end, USBCCIDState),
+        VMSTATE_STRUCT_ARRAY(pending_answers, USBCCIDState,
+                        PENDING_ANSWERS_NUM, 1, answer_vmstate, Answer),
+        VMSTATE_UINT32(pending_answers_num, USBCCIDState),
+        VMSTATE_UINT8(migration_state, USBCCIDState),
+        VMSTATE_UINT32(state_vmstate, USBCCIDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ccid_properties[] = {
+    DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ccid_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = ccid_initfn;
+    uc->product_desc   = "QEMU USB CCID";
+    uc->usb_desc       = &desc_ccid;
+    uc->handle_reset   = ccid_handle_reset;
+    uc->handle_control = ccid_handle_control;
+    uc->handle_data    = ccid_handle_data;
+    uc->handle_destroy = ccid_handle_destroy;
+    dc->desc = "CCID Rev 1.1 smartcard reader";
+    dc->vmsd = &ccid_vmstate;
+    dc->props = ccid_properties;
+}
+
+static TypeInfo ccid_info = {
+    .name          = CCID_DEV_NAME,
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBCCIDState),
+    .class_init    = ccid_class_initfn,
+};
+
+static void ccid_card_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->bus_info = &ccid_bus_info;
+    k->init = ccid_card_init;
+    k->exit = ccid_card_exit;
+}
+
+static TypeInfo ccid_card_type_info = {
+    .name = TYPE_CCID_CARD,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(CCIDCardState),
+    .abstract = true,
+    .class_size = sizeof(CCIDCardClass),
+    .class_init = ccid_card_class_init,
+};
+
+static void ccid_register_types(void)
+{
+    type_register_static(&ccid_card_type_info);
+    type_register_static(&ccid_info);
+    usb_legacy_register(CCID_DEV_NAME, "ccid", NULL);
+}
+
+type_init(ccid_register_types)
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
new file mode 100644 (file)
index 0000000..6ffaf70
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * USB Mass Storage Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/scsi.h"
+#include "console.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "blockdev.h"
+
+//#define DEBUG_MSD
+
+#ifdef DEBUG_MSD
+#define DPRINTF(fmt, ...) \
+do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+/* USB requests.  */
+#define MassStorageReset  0xff
+#define GetMaxLun         0xfe
+
+enum USBMSDMode {
+    USB_MSDM_CBW, /* Command Block.  */
+    USB_MSDM_DATAOUT, /* Transfer data to device.  */
+    USB_MSDM_DATAIN, /* Transfer data from device.  */
+    USB_MSDM_CSW /* Command Status.  */
+};
+
+struct usb_msd_csw {
+    uint32_t sig;
+    uint32_t tag;
+    uint32_t residue;
+    uint8_t status;
+};
+
+typedef struct {
+    USBDevice dev;
+    enum USBMSDMode mode;
+    uint32_t scsi_len;
+    uint8_t *scsi_buf;
+    uint32_t data_len;
+    uint32_t residue;
+    struct usb_msd_csw csw;
+    SCSIRequest *req;
+    SCSIBus bus;
+    BlockConf conf;
+    char *serial;
+    SCSIDevice *scsi_dev;
+    uint32_t removable;
+    /* For async completion.  */
+    USBPacket *packet;
+} MSDState;
+
+struct usb_msd_cbw {
+    uint32_t sig;
+    uint32_t tag;
+    uint32_t data_len;
+    uint8_t flags;
+    uint8_t lun;
+    uint8_t cmd_len;
+    uint8_t cmd[16];
+};
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT,
+    STR_SERIALNUMBER,
+    STR_CONFIG_FULL,
+    STR_CONFIG_HIGH,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+    [STR_PRODUCT]      = "QEMU USB HARDDRIVE",
+    [STR_SERIALNUMBER] = "1",
+    [STR_CONFIG_FULL]  = "Full speed config (usb 1.1)",
+    [STR_CONFIG_HIGH]  = "High speed config (usb 2.0)",
+};
+
+static const USBDescIface desc_iface_full = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 2,
+    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
+    .bInterfaceSubClass            = 0x06, /* SCSI */
+    .bInterfaceProtocol            = 0x50, /* Bulk */
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },{
+            .bEndpointAddress      = USB_DIR_OUT | 0x02,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },
+    }
+};
+
+static const USBDescDevice desc_device_full = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_FULL,
+            .bmAttributes          = 0xc0,
+            .nif = 1,
+            .ifs = &desc_iface_full,
+        },
+    },
+};
+
+static const USBDescIface desc_iface_high = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 2,
+    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
+    .bInterfaceSubClass            = 0x06, /* SCSI */
+    .bInterfaceProtocol            = 0x50, /* Bulk */
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 512,
+        },{
+            .bEndpointAddress      = USB_DIR_OUT | 0x02,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 512,
+        },
+    }
+};
+
+static const USBDescDevice desc_device_high = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_HIGH,
+            .bmAttributes          = 0xc0,
+            .nif = 1,
+            .ifs = &desc_iface_high,
+        },
+    },
+};
+
+static const USBDesc desc = {
+    .id = {
+        .idVendor          = 0x46f4, /* CRC16() of "QEMU" */
+        .idProduct         = 0x0001,
+        .bcdDevice         = 0,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_full,
+    .high = &desc_device_high,
+    .str  = desc_strings,
+};
+
+static void usb_msd_copy_data(MSDState *s, USBPacket *p)
+{
+    uint32_t len;
+    len = p->iov.size - p->result;
+    if (len > s->scsi_len)
+        len = s->scsi_len;
+    usb_packet_copy(p, s->scsi_buf, len);
+    s->scsi_len -= len;
+    s->scsi_buf += len;
+    s->data_len -= len;
+    if (s->scsi_len == 0 || s->data_len == 0) {
+        scsi_req_continue(s->req);
+    }
+}
+
+static void usb_msd_send_status(MSDState *s, USBPacket *p)
+{
+    int len;
+
+    DPRINTF("Command status %d tag 0x%x, len %zd\n",
+            s->csw.status, s->csw.tag, p->iov.size);
+
+    assert(s->csw.sig == 0x53425355);
+    len = MIN(sizeof(s->csw), p->iov.size);
+    usb_packet_copy(p, &s->csw, len);
+    memset(&s->csw, 0, sizeof(s->csw));
+}
+
+static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
+{
+    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+    USBPacket *p = s->packet;
+
+    assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
+    s->scsi_len = len;
+    s->scsi_buf = scsi_req_get_buf(req);
+    if (p) {
+        usb_msd_copy_data(s, p);
+        p = s->packet;
+        if (p && p->result == p->iov.size) {
+            /* Set s->packet to NULL before calling usb_packet_complete
+               because another request may be issued before
+               usb_packet_complete returns.  */
+            DPRINTF("Packet complete %p\n", p);
+            s->packet = NULL;
+            usb_packet_complete(&s->dev, p);
+        }
+    }
+}
+
+static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
+{
+    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+    USBPacket *p = s->packet;
+
+    DPRINTF("Command complete %d tag 0x%x\n", status, req->tag);
+    s->residue = s->data_len;
+
+    s->csw.sig = cpu_to_le32(0x53425355);
+    s->csw.tag = cpu_to_le32(req->tag);
+    s->csw.residue = s->residue;
+    s->csw.status = status != 0;
+
+    if (s->packet) {
+        if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
+            /* A deferred packet with no write data remaining must be
+               the status read packet.  */
+            usb_msd_send_status(s, p);
+            s->mode = USB_MSDM_CBW;
+        } else {
+            if (s->data_len) {
+                int len = (p->iov.size - p->result);
+                usb_packet_skip(p, len);
+                s->data_len -= len;
+            }
+            if (s->data_len == 0) {
+                s->mode = USB_MSDM_CSW;
+            }
+        }
+        s->packet = NULL;
+        usb_packet_complete(&s->dev, p);
+    } else if (s->data_len == 0) {
+        s->mode = USB_MSDM_CSW;
+    }
+    scsi_req_unref(req);
+    s->req = NULL;
+}
+
+static void usb_msd_request_cancelled(SCSIRequest *req)
+{
+    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+
+    if (req == s->req) {
+        scsi_req_unref(s->req);
+        s->req = NULL;
+        s->packet = NULL;
+        s->scsi_len = 0;
+    }
+}
+
+static void usb_msd_handle_reset(USBDevice *dev)
+{
+    MSDState *s = (MSDState *)dev;
+
+    DPRINTF("Reset\n");
+    if (s->req) {
+        scsi_req_cancel(s->req);
+    }
+    assert(s->req == NULL);
+
+    if (s->packet) {
+        USBPacket *p = s->packet;
+        s->packet = NULL;
+        p->result = USB_RET_STALL;
+        usb_packet_complete(dev, p);
+    }
+
+    s->mode = USB_MSDM_CBW;
+}
+
+static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    MSDState *s = (MSDState *)dev;
+    int ret;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    ret = 0;
+    switch (request) {
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        ret = 0;
+        break;
+        /* Class specific requests.  */
+    case ClassInterfaceOutRequest | MassStorageReset:
+        /* Reset state ready for the next CBW.  */
+        s->mode = USB_MSDM_CBW;
+        ret = 0;
+        break;
+    case ClassInterfaceRequest | GetMaxLun:
+        data[0] = 0;
+        ret = 1;
+        break;
+    default:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
+{
+    MSDState *s = DO_UPCAST(MSDState, dev, dev);
+
+    if (s->req) {
+        scsi_req_cancel(s->req);
+    }
+}
+
+static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
+{
+    MSDState *s = (MSDState *)dev;
+    uint32_t tag;
+    int ret = 0;
+    struct usb_msd_cbw cbw;
+    uint8_t devep = p->ep->nr;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        if (devep != 2)
+            goto fail;
+
+        switch (s->mode) {
+        case USB_MSDM_CBW:
+            if (p->iov.size != 31) {
+                fprintf(stderr, "usb-msd: Bad CBW size");
+                goto fail;
+            }
+            usb_packet_copy(p, &cbw, 31);
+            if (le32_to_cpu(cbw.sig) != 0x43425355) {
+                fprintf(stderr, "usb-msd: Bad signature %08x\n",
+                        le32_to_cpu(cbw.sig));
+                goto fail;
+            }
+            DPRINTF("Command on LUN %d\n", cbw.lun);
+            if (cbw.lun != 0) {
+                fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
+                goto fail;
+            }
+            tag = le32_to_cpu(cbw.tag);
+            s->data_len = le32_to_cpu(cbw.data_len);
+            if (s->data_len == 0) {
+                s->mode = USB_MSDM_CSW;
+            } else if (cbw.flags & 0x80) {
+                s->mode = USB_MSDM_DATAIN;
+            } else {
+                s->mode = USB_MSDM_DATAOUT;
+            }
+            DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
+                    tag, cbw.flags, cbw.cmd_len, s->data_len);
+            s->residue = 0;
+            s->scsi_len = 0;
+            s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL);
+            scsi_req_enqueue(s->req);
+            if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
+                scsi_req_continue(s->req);
+            }
+            ret = p->result;
+            break;
+
+        case USB_MSDM_DATAOUT:
+            DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len);
+            if (p->iov.size > s->data_len) {
+                goto fail;
+            }
+
+            if (s->scsi_len) {
+                usb_msd_copy_data(s, p);
+            }
+            if (s->residue) {
+                int len = p->iov.size - p->result;
+                if (len) {
+                    usb_packet_skip(p, len);
+                    s->data_len -= len;
+                    if (s->data_len == 0) {
+                        s->mode = USB_MSDM_CSW;
+                    }
+                }
+            }
+            if (p->result < p->iov.size) {
+                DPRINTF("Deferring packet %p\n", p);
+                s->packet = p;
+                ret = USB_RET_ASYNC;
+            } else {
+                ret = p->result;
+            }
+            break;
+
+        default:
+            DPRINTF("Unexpected write (len %zd)\n", p->iov.size);
+            goto fail;
+        }
+        break;
+
+    case USB_TOKEN_IN:
+        if (devep != 1)
+            goto fail;
+
+        switch (s->mode) {
+        case USB_MSDM_DATAOUT:
+            if (s->data_len != 0 || p->iov.size < 13) {
+                goto fail;
+            }
+            /* Waiting for SCSI write to complete.  */
+            s->packet = p;
+            ret = USB_RET_ASYNC;
+            break;
+
+        case USB_MSDM_CSW:
+            if (p->iov.size < 13) {
+                goto fail;
+            }
+
+            if (s->req) {
+                /* still in flight */
+                s->packet = p;
+                ret = USB_RET_ASYNC;
+            } else {
+                usb_msd_send_status(s, p);
+                s->mode = USB_MSDM_CBW;
+                ret = 13;
+            }
+            break;
+
+        case USB_MSDM_DATAIN:
+            DPRINTF("Data in %zd/%d, scsi_len %d\n",
+                    p->iov.size, s->data_len, s->scsi_len);
+            if (s->scsi_len) {
+                usb_msd_copy_data(s, p);
+            }
+            if (s->residue) {
+                int len = p->iov.size - p->result;
+                if (len) {
+                    usb_packet_skip(p, len);
+                    s->data_len -= len;
+                    if (s->data_len == 0) {
+                        s->mode = USB_MSDM_CSW;
+                    }
+                }
+            }
+            if (p->result < p->iov.size) {
+                DPRINTF("Deferring packet %p\n", p);
+                s->packet = p;
+                ret = USB_RET_ASYNC;
+            } else {
+                ret = p->result;
+            }
+            break;
+
+        default:
+            DPRINTF("Unexpected read (len %zd)\n", p->iov.size);
+            goto fail;
+        }
+        break;
+
+    default:
+        DPRINTF("Bad token\n");
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void usb_msd_password_cb(void *opaque, int err)
+{
+    MSDState *s = opaque;
+
+    if (!err)
+        err = usb_device_attach(&s->dev);
+
+    if (err)
+        qdev_unplug(&s->dev.qdev);
+}
+
+static const struct SCSIBusInfo usb_msd_scsi_info = {
+    .tcq = false,
+    .max_target = 0,
+    .max_lun = 0,
+
+    .transfer_data = usb_msd_transfer_data,
+    .complete = usb_msd_command_complete,
+    .cancel = usb_msd_request_cancelled
+};
+
+static int usb_msd_initfn(USBDevice *dev)
+{
+    MSDState *s = DO_UPCAST(MSDState, dev, dev);
+    BlockDriverState *bs = s->conf.bs;
+    DriveInfo *dinfo;
+
+    if (!bs) {
+        error_report("drive property not set");
+        return -1;
+    }
+
+    /*
+     * Hack alert: this pretends to be a block device, but it's really
+     * a SCSI bus that can serve only a single device, which it
+     * creates automatically.  But first it needs to detach from its
+     * blockdev, or else scsi_bus_legacy_add_drive() dies when it
+     * attaches again.
+     *
+     * The hack is probably a bad idea.
+     */
+    bdrv_detach_dev(bs, &s->dev.qdev);
+    s->conf.bs = NULL;
+
+    if (!s->serial) {
+        /* try to fall back to value set with legacy -drive serial=... */
+        dinfo = drive_get_by_blockdev(bs);
+        if (*dinfo->serial) {
+            s->serial = strdup(dinfo->serial);
+        }
+    }
+    if (s->serial) {
+        usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial);
+    }
+
+    usb_desc_init(dev);
+    scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info);
+    s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable,
+                                            s->conf.bootindex);
+    if (!s->scsi_dev) {
+        return -1;
+    }
+    s->bus.qbus.allow_hotplug = 0;
+    usb_msd_handle_reset(dev);
+
+    if (bdrv_key_required(bs)) {
+        if (cur_mon) {
+            monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s);
+            s->dev.auto_attach = 0;
+        } else {
+            autostart = 0;
+        }
+    }
+
+    return 0;
+}
+
+static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
+{
+    static int nr=0;
+    char id[8];
+    QemuOpts *opts;
+    DriveInfo *dinfo;
+    USBDevice *dev;
+    const char *p1;
+    char fmt[32];
+
+    /* parse -usbdevice disk: syntax into drive opts */
+    snprintf(id, sizeof(id), "usb%d", nr++);
+    opts = qemu_opts_create(qemu_find_opts("drive"), id, 0);
+
+    p1 = strchr(filename, ':');
+    if (p1++) {
+        const char *p2;
+
+        if (strstart(filename, "format=", &p2)) {
+            int len = MIN(p1 - p2, sizeof(fmt));
+            pstrcpy(fmt, len, p2);
+            qemu_opt_set(opts, "format", fmt);
+        } else if (*filename != ':') {
+            printf("unrecognized USB mass-storage option %s\n", filename);
+            return NULL;
+        }
+        filename = p1;
+    }
+    if (!*filename) {
+        printf("block device specification needed\n");
+        return NULL;
+    }
+    qemu_opt_set(opts, "file", filename);
+    qemu_opt_set(opts, "if", "none");
+
+    /* create host drive */
+    dinfo = drive_init(opts, 0);
+    if (!dinfo) {
+        qemu_opts_del(opts);
+        return NULL;
+    }
+
+    /* create guest device */
+    dev = usb_create(bus, "usb-storage");
+    if (!dev) {
+        return NULL;
+    }
+    if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
+        qdev_free(&dev->qdev);
+        return NULL;
+    }
+    if (qdev_init(&dev->qdev) < 0)
+        return NULL;
+
+    return dev;
+}
+
+static const VMStateDescription vmstate_usb_msd = {
+    .name = "usb-storage",
+    .unmigratable = 1, /* FIXME: handle transactions which are in flight */
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_USB_DEVICE(dev, MSDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property msd_properties[] = {
+    DEFINE_BLOCK_PROPERTIES(MSDState, conf),
+    DEFINE_PROP_STRING("serial", MSDState, serial),
+    DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_msd_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_msd_initfn;
+    uc->product_desc   = "QEMU USB MSD";
+    uc->usb_desc       = &desc;
+    uc->cancel_packet  = usb_msd_cancel_io;
+    uc->handle_attach  = usb_desc_attach;
+    uc->handle_reset   = usb_msd_handle_reset;
+    uc->handle_control = usb_msd_handle_control;
+    uc->handle_data    = usb_msd_handle_data;
+    dc->fw_name = "storage";
+    dc->vmsd = &vmstate_usb_msd;
+    dc->props = msd_properties;
+}
+
+static TypeInfo msd_info = {
+    .name          = "usb-storage",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(MSDState),
+    .class_init    = usb_msd_class_initfn,
+};
+
+static void usb_msd_register_types(void)
+{
+    type_register_static(&msd_info);
+    usb_legacy_register("usb-storage", "disk", usb_msd_init);
+}
+
+type_init(usb_msd_register_types)
diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c
new file mode 100644 (file)
index 0000000..c1cfd74
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Wacom PenPartner USB tablet emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Author: Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Based on hw/usb-hid.c:
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * 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 "hw/hw.h"
+#include "console.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+
+/* Interface requests */
+#define WACOM_GET_REPORT       0x2101
+#define WACOM_SET_REPORT       0x2109
+
+/* HID interface requests */
+#define HID_GET_REPORT         0xa101
+#define HID_GET_IDLE           0xa102
+#define HID_GET_PROTOCOL       0xa103
+#define HID_SET_IDLE           0x210a
+#define HID_SET_PROTOCOL       0x210b
+
+typedef struct USBWacomState {
+    USBDevice dev;
+    QEMUPutMouseEntry *eh_entry;
+    int dx, dy, dz, buttons_state;
+    int x, y;
+    int mouse_grabbed;
+    enum {
+        WACOM_MODE_HID = 1,
+        WACOM_MODE_WACOM = 2,
+    } mode;
+    uint8_t idle;
+    int changed;
+} USBWacomState;
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT,
+    STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
+    [STR_PRODUCT]          = "Wacom PenPartner",
+    [STR_SERIALNUMBER]     = "1",
+};
+
+static const USBDescIface desc_iface_wacom = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 1,
+    .bInterfaceClass               = USB_CLASS_HID,
+    .bInterfaceSubClass            = 0x01, /* boot */
+    .bInterfaceProtocol            = 0x02,
+    .ndesc                         = 1,
+    .descs = (USBDescOther[]) {
+        {
+            /* HID descriptor */
+            .data = (uint8_t[]) {
+                0x09,          /*  u8  bLength */
+                0x21,          /*  u8  bDescriptorType */
+                0x01, 0x10,    /*  u16 HID_class */
+                0x00,          /*  u8  country_code */
+                0x01,          /*  u8  num_descriptors */
+                0x22,          /*  u8  type: Report */
+                0x6e, 0,       /*  u16 len */
+            },
+        },
+    },
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .wMaxPacketSize        = 8,
+            .bInterval             = 0x0a,
+        },
+    },
+};
+
+static const USBDescDevice desc_device_wacom = {
+    .bcdUSB                        = 0x0110,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .bmAttributes          = 0x80,
+            .bMaxPower             = 40,
+            .nif = 1,
+            .ifs = &desc_iface_wacom,
+        },
+    },
+};
+
+static const USBDesc desc_wacom = {
+    .id = {
+        .idVendor          = 0x056a,
+        .idProduct         = 0x0000,
+        .bcdDevice         = 0x4210,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_wacom,
+    .str  = desc_strings,
+};
+
+static void usb_mouse_event(void *opaque,
+                            int dx1, int dy1, int dz1, int buttons_state)
+{
+    USBWacomState *s = opaque;
+
+    s->dx += dx1;
+    s->dy += dy1;
+    s->dz += dz1;
+    s->buttons_state = buttons_state;
+    s->changed = 1;
+}
+
+static void usb_wacom_event(void *opaque,
+                            int x, int y, int dz, int buttons_state)
+{
+    USBWacomState *s = opaque;
+
+    /* scale to Penpartner resolution */
+    s->x = (x * 5040 / 0x7FFF);
+    s->y = (y * 3780 / 0x7FFF);
+    s->dz += dz;
+    s->buttons_state = buttons_state;
+    s->changed = 1;
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+    if (val < vmin)
+        return vmin;
+    else if (val > vmax)
+        return vmax;
+    else
+        return val;
+}
+
+static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
+{
+    int dx, dy, dz, b, l;
+
+    if (!s->mouse_grabbed) {
+        s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
+                        "QEMU PenPartner tablet");
+        qemu_activate_mouse_event_handler(s->eh_entry);
+        s->mouse_grabbed = 1;
+    }
+
+    dx = int_clamp(s->dx, -128, 127);
+    dy = int_clamp(s->dy, -128, 127);
+    dz = int_clamp(s->dz, -128, 127);
+
+    s->dx -= dx;
+    s->dy -= dy;
+    s->dz -= dz;
+
+    b = 0;
+    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+        b |= 0x01;
+    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+        b |= 0x02;
+    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+        b |= 0x04;
+
+    buf[0] = b;
+    buf[1] = dx;
+    buf[2] = dy;
+    l = 3;
+    if (len >= 4) {
+        buf[3] = dz;
+        l = 4;
+    }
+    return l;
+}
+
+static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
+{
+    int b;
+
+    if (!s->mouse_grabbed) {
+        s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
+                        "QEMU PenPartner tablet");
+        qemu_activate_mouse_event_handler(s->eh_entry);
+        s->mouse_grabbed = 1;
+    }
+
+    b = 0;
+    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+        b |= 0x01;
+    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+        b |= 0x40;
+    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+        b |= 0x20; /* eraser */
+
+    if (len < 7)
+        return 0;
+
+    buf[0] = s->mode;
+    buf[5] = 0x00 | (b & 0xf0);
+    buf[1] = s->x & 0xff;
+    buf[2] = s->x >> 8;
+    buf[3] = s->y & 0xff;
+    buf[4] = s->y >> 8;
+    if (b & 0x3f) {
+        buf[6] = 0;
+    } else {
+        buf[6] = (unsigned char) -127;
+    }
+
+    return 7;
+}
+
+static void usb_wacom_handle_reset(USBDevice *dev)
+{
+    USBWacomState *s = (USBWacomState *) dev;
+
+    s->dx = 0;
+    s->dy = 0;
+    s->dz = 0;
+    s->x = 0;
+    s->y = 0;
+    s->buttons_state = 0;
+    s->mode = WACOM_MODE_HID;
+}
+
+static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBWacomState *s = (USBWacomState *) dev;
+    int ret;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    ret = 0;
+    switch (request) {
+    case WACOM_SET_REPORT:
+        if (s->mouse_grabbed) {
+            qemu_remove_mouse_event_handler(s->eh_entry);
+            s->mouse_grabbed = 0;
+        }
+        s->mode = data[0];
+        ret = 0;
+        break;
+    case WACOM_GET_REPORT:
+        data[0] = 0;
+        data[1] = s->mode;
+        ret = 2;
+        break;
+    /* USB HID requests */
+    case HID_GET_REPORT:
+        if (s->mode == WACOM_MODE_HID)
+            ret = usb_mouse_poll(s, data, length);
+        else if (s->mode == WACOM_MODE_WACOM)
+            ret = usb_wacom_poll(s, data, length);
+        break;
+    case HID_GET_IDLE:
+        ret = 1;
+        data[0] = s->idle;
+        break;
+    case HID_SET_IDLE:
+        s->idle = (uint8_t) (value >> 8);
+        ret = 0;
+        break;
+    default:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBWacomState *s = (USBWacomState *) dev;
+    uint8_t buf[p->iov.size];
+    int ret = 0;
+
+    switch (p->pid) {
+    case USB_TOKEN_IN:
+        if (p->ep->nr == 1) {
+            if (!(s->changed || s->idle))
+                return USB_RET_NAK;
+            s->changed = 0;
+            if (s->mode == WACOM_MODE_HID)
+                ret = usb_mouse_poll(s, buf, p->iov.size);
+            else if (s->mode == WACOM_MODE_WACOM)
+                ret = usb_wacom_poll(s, buf, p->iov.size);
+            usb_packet_copy(p, buf, ret);
+            break;
+        }
+        /* Fall through.  */
+    case USB_TOKEN_OUT:
+    default:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_wacom_handle_destroy(USBDevice *dev)
+{
+    USBWacomState *s = (USBWacomState *) dev;
+
+    if (s->mouse_grabbed) {
+        qemu_remove_mouse_event_handler(s->eh_entry);
+        s->mouse_grabbed = 0;
+    }
+}
+
+static int usb_wacom_initfn(USBDevice *dev)
+{
+    USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
+    usb_desc_init(dev);
+    s->changed = 1;
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_wacom = {
+    .name = "usb-wacom",
+    .unmigratable = 1,
+};
+
+static void usb_wacom_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->product_desc   = "QEMU PenPartner Tablet";
+    uc->usb_desc       = &desc_wacom;
+    uc->init           = usb_wacom_initfn;
+    uc->handle_reset   = usb_wacom_handle_reset;
+    uc->handle_control = usb_wacom_handle_control;
+    uc->handle_data    = usb_wacom_handle_data;
+    uc->handle_destroy = usb_wacom_handle_destroy;
+    dc->desc = "QEMU PenPartner Tablet";
+    dc->vmsd = &vmstate_usb_wacom;
+}
+
+static TypeInfo wacom_info = {
+    .name          = "usb-wacom-tablet",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBWacomState),
+    .class_init    = usb_wacom_class_init,
+};
+
+static void usb_wacom_register_types(void)
+{
+    type_register_static(&wacom_info);
+    usb_legacy_register("usb-wacom-tablet", "wacom-tablet", NULL);
+}
+
+type_init(usb_wacom_register_types)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
new file mode 100644 (file)
index 0000000..58811d3
--- /dev/null
@@ -0,0 +1,2345 @@
+/*
+ * QEMU USB EHCI Emulation
+ *
+ * Copyright(c) 2008  Emutex Ltd. (address@hidden)
+ *
+ * EHCI project was started by Mark Burkley, with contributions by
+ * Niels de Vos.  David S. Ahern continued working on it.  Kevin Wolf,
+ * Jan Kiszka and Vincent Palatin contributed bugfixes.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or(at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "qemu-timer.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
+#include "monitor.h"
+#include "trace.h"
+#include "dma.h"
+
+#define EHCI_DEBUG   0
+
+#if EHCI_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+/* internal processing - reset HC to try and recover */
+#define USB_RET_PROCERR   (-99)
+
+#define MMIO_SIZE        0x1000
+
+/* Capability Registers Base Address - section 2.2 */
+#define CAPREGBASE       0x0000
+#define CAPLENGTH        CAPREGBASE + 0x0000  // 1-byte, 0x0001 reserved
+#define HCIVERSION       CAPREGBASE + 0x0002  // 2-bytes, i/f version #
+#define HCSPARAMS        CAPREGBASE + 0x0004  // 4-bytes, structural params
+#define HCCPARAMS        CAPREGBASE + 0x0008  // 4-bytes, capability params
+#define EECP             HCCPARAMS + 1
+#define HCSPPORTROUTE1   CAPREGBASE + 0x000c
+#define HCSPPORTROUTE2   CAPREGBASE + 0x0010
+
+#define OPREGBASE        0x0020        // Operational Registers Base Address
+
+#define USBCMD           OPREGBASE + 0x0000
+#define USBCMD_RUNSTOP   (1 << 0)      // run / Stop
+#define USBCMD_HCRESET   (1 << 1)      // HC Reset
+#define USBCMD_FLS       (3 << 2)      // Frame List Size
+#define USBCMD_FLS_SH    2             // Frame List Size Shift
+#define USBCMD_PSE       (1 << 4)      // Periodic Schedule Enable
+#define USBCMD_ASE       (1 << 5)      // Asynch Schedule Enable
+#define USBCMD_IAAD      (1 << 6)      // Int Asynch Advance Doorbell
+#define USBCMD_LHCR      (1 << 7)      // Light Host Controller Reset
+#define USBCMD_ASPMC     (3 << 8)      // Async Sched Park Mode Count
+#define USBCMD_ASPME     (1 << 11)     // Async Sched Park Mode Enable
+#define USBCMD_ITC       (0x7f << 16)  // Int Threshold Control
+#define USBCMD_ITC_SH    16            // Int Threshold Control Shift
+
+#define USBSTS           OPREGBASE + 0x0004
+#define USBSTS_RO_MASK   0x0000003f
+#define USBSTS_INT       (1 << 0)      // USB Interrupt
+#define USBSTS_ERRINT    (1 << 1)      // Error Interrupt
+#define USBSTS_PCD       (1 << 2)      // Port Change Detect
+#define USBSTS_FLR       (1 << 3)      // Frame List Rollover
+#define USBSTS_HSE       (1 << 4)      // Host System Error
+#define USBSTS_IAA       (1 << 5)      // Interrupt on Async Advance
+#define USBSTS_HALT      (1 << 12)     // HC Halted
+#define USBSTS_REC       (1 << 13)     // Reclamation
+#define USBSTS_PSS       (1 << 14)     // Periodic Schedule Status
+#define USBSTS_ASS       (1 << 15)     // Asynchronous Schedule Status
+
+/*
+ *  Interrupt enable bits correspond to the interrupt active bits in USBSTS
+ *  so no need to redefine here.
+ */
+#define USBINTR              OPREGBASE + 0x0008
+#define USBINTR_MASK         0x0000003f
+
+#define FRINDEX              OPREGBASE + 0x000c
+#define CTRLDSSEGMENT        OPREGBASE + 0x0010
+#define PERIODICLISTBASE     OPREGBASE + 0x0014
+#define ASYNCLISTADDR        OPREGBASE + 0x0018
+#define ASYNCLISTADDR_MASK   0xffffffe0
+
+#define CONFIGFLAG           OPREGBASE + 0x0040
+
+#define PORTSC               (OPREGBASE + 0x0044)
+#define PORTSC_BEGIN         PORTSC
+#define PORTSC_END           (PORTSC + 4 * NB_PORTS)
+/*
+ * Bits that are reserved or are read-only are masked out of values
+ * written to us by software
+ */
+#define PORTSC_RO_MASK       0x007001c0
+#define PORTSC_RWC_MASK      0x0000002a
+#define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
+#define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
+#define PORTSC_WKCN_E        (1 << 20)    // Wake on Connect Enable
+#define PORTSC_PTC           (15 << 16)   // Port Test Control
+#define PORTSC_PTC_SH        16           // Port Test Control shift
+#define PORTSC_PIC           (3 << 14)    // Port Indicator Control
+#define PORTSC_PIC_SH        14           // Port Indicator Control Shift
+#define PORTSC_POWNER        (1 << 13)    // Port Owner
+#define PORTSC_PPOWER        (1 << 12)    // Port Power
+#define PORTSC_LINESTAT      (3 << 10)    // Port Line Status
+#define PORTSC_LINESTAT_SH   10           // Port Line Status Shift
+#define PORTSC_PRESET        (1 << 8)     // Port Reset
+#define PORTSC_SUSPEND       (1 << 7)     // Port Suspend
+#define PORTSC_FPRES         (1 << 6)     // Force Port Resume
+#define PORTSC_OCC           (1 << 5)     // Over Current Change
+#define PORTSC_OCA           (1 << 4)     // Over Current Active
+#define PORTSC_PEDC          (1 << 3)     // Port Enable/Disable Change
+#define PORTSC_PED           (1 << 2)     // Port Enable/Disable
+#define PORTSC_CSC           (1 << 1)     // Connect Status Change
+#define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
+
+#define FRAME_TIMER_FREQ 1000
+#define FRAME_TIMER_NS   (1000000000 / FRAME_TIMER_FREQ)
+
+#define NB_MAXINTRATE    8        // Max rate at which controller issues ints
+#define NB_PORTS         6        // Number of downstream ports
+#define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction
+#define MAX_ITERATIONS   20       // Max number of QH before we break the loop
+#define MAX_QH           100      // Max allowable queue heads in a chain
+
+/*  Internal periodic / asynchronous schedule state machine states
+ */
+typedef enum {
+    EST_INACTIVE = 1000,
+    EST_ACTIVE,
+    EST_EXECUTING,
+    EST_SLEEPING,
+    /*  The following states are internal to the state machine function
+    */
+    EST_WAITLISTHEAD,
+    EST_FETCHENTRY,
+    EST_FETCHQH,
+    EST_FETCHITD,
+    EST_FETCHSITD,
+    EST_ADVANCEQUEUE,
+    EST_FETCHQTD,
+    EST_EXECUTE,
+    EST_WRITEBACK,
+    EST_HORIZONTALQH
+} EHCI_STATES;
+
+/* macros for accessing fields within next link pointer entry */
+#define NLPTR_GET(x)             ((x) & 0xffffffe0)
+#define NLPTR_TYPE_GET(x)        (((x) >> 1) & 3)
+#define NLPTR_TBIT(x)            ((x) & 1)  // 1=invalid, 0=valid
+
+/* link pointer types */
+#define NLPTR_TYPE_ITD           0     // isoc xfer descriptor
+#define NLPTR_TYPE_QH            1     // queue head
+#define NLPTR_TYPE_STITD         2     // split xaction, isoc xfer descriptor
+#define NLPTR_TYPE_FSTN          3     // frame span traversal node
+
+
+/*  EHCI spec version 1.0 Section 3.3
+ */
+typedef struct EHCIitd {
+    uint32_t next;
+
+    uint32_t transact[8];
+#define ITD_XACT_ACTIVE          (1 << 31)
+#define ITD_XACT_DBERROR         (1 << 30)
+#define ITD_XACT_BABBLE          (1 << 29)
+#define ITD_XACT_XACTERR         (1 << 28)
+#define ITD_XACT_LENGTH_MASK     0x0fff0000
+#define ITD_XACT_LENGTH_SH       16
+#define ITD_XACT_IOC             (1 << 15)
+#define ITD_XACT_PGSEL_MASK      0x00007000
+#define ITD_XACT_PGSEL_SH        12
+#define ITD_XACT_OFFSET_MASK     0x00000fff
+
+    uint32_t bufptr[7];
+#define ITD_BUFPTR_MASK          0xfffff000
+#define ITD_BUFPTR_SH            12
+#define ITD_BUFPTR_EP_MASK       0x00000f00
+#define ITD_BUFPTR_EP_SH         8
+#define ITD_BUFPTR_DEVADDR_MASK  0x0000007f
+#define ITD_BUFPTR_DEVADDR_SH    0
+#define ITD_BUFPTR_DIRECTION     (1 << 11)
+#define ITD_BUFPTR_MAXPKT_MASK   0x000007ff
+#define ITD_BUFPTR_MAXPKT_SH     0
+#define ITD_BUFPTR_MULT_MASK     0x00000003
+#define ITD_BUFPTR_MULT_SH       0
+} EHCIitd;
+
+/*  EHCI spec version 1.0 Section 3.4
+ */
+typedef struct EHCIsitd {
+    uint32_t next;                  // Standard next link pointer
+    uint32_t epchar;
+#define SITD_EPCHAR_IO              (1 << 31)
+#define SITD_EPCHAR_PORTNUM_MASK    0x7f000000
+#define SITD_EPCHAR_PORTNUM_SH      24
+#define SITD_EPCHAR_HUBADD_MASK     0x007f0000
+#define SITD_EPCHAR_HUBADDR_SH      16
+#define SITD_EPCHAR_EPNUM_MASK      0x00000f00
+#define SITD_EPCHAR_EPNUM_SH        8
+#define SITD_EPCHAR_DEVADDR_MASK    0x0000007f
+
+    uint32_t uframe;
+#define SITD_UFRAME_CMASK_MASK      0x0000ff00
+#define SITD_UFRAME_CMASK_SH        8
+#define SITD_UFRAME_SMASK_MASK      0x000000ff
+
+    uint32_t results;
+#define SITD_RESULTS_IOC              (1 << 31)
+#define SITD_RESULTS_PGSEL            (1 << 30)
+#define SITD_RESULTS_TBYTES_MASK      0x03ff0000
+#define SITD_RESULTS_TYBYTES_SH       16
+#define SITD_RESULTS_CPROGMASK_MASK   0x0000ff00
+#define SITD_RESULTS_CPROGMASK_SH     8
+#define SITD_RESULTS_ACTIVE           (1 << 7)
+#define SITD_RESULTS_ERR              (1 << 6)
+#define SITD_RESULTS_DBERR            (1 << 5)
+#define SITD_RESULTS_BABBLE           (1 << 4)
+#define SITD_RESULTS_XACTERR          (1 << 3)
+#define SITD_RESULTS_MISSEDUF         (1 << 2)
+#define SITD_RESULTS_SPLITXSTATE      (1 << 1)
+
+    uint32_t bufptr[2];
+#define SITD_BUFPTR_MASK              0xfffff000
+#define SITD_BUFPTR_CURROFF_MASK      0x00000fff
+#define SITD_BUFPTR_TPOS_MASK         0x00000018
+#define SITD_BUFPTR_TPOS_SH           3
+#define SITD_BUFPTR_TCNT_MASK         0x00000007
+
+    uint32_t backptr;                 // Standard next link pointer
+} EHCIsitd;
+
+/*  EHCI spec version 1.0 Section 3.5
+ */
+typedef struct EHCIqtd {
+    uint32_t next;                    // Standard next link pointer
+    uint32_t altnext;                 // Standard next link pointer
+    uint32_t token;
+#define QTD_TOKEN_DTOGGLE             (1 << 31)
+#define QTD_TOKEN_TBYTES_MASK         0x7fff0000
+#define QTD_TOKEN_TBYTES_SH           16
+#define QTD_TOKEN_IOC                 (1 << 15)
+#define QTD_TOKEN_CPAGE_MASK          0x00007000
+#define QTD_TOKEN_CPAGE_SH            12
+#define QTD_TOKEN_CERR_MASK           0x00000c00
+#define QTD_TOKEN_CERR_SH             10
+#define QTD_TOKEN_PID_MASK            0x00000300
+#define QTD_TOKEN_PID_SH              8
+#define QTD_TOKEN_ACTIVE              (1 << 7)
+#define QTD_TOKEN_HALT                (1 << 6)
+#define QTD_TOKEN_DBERR               (1 << 5)
+#define QTD_TOKEN_BABBLE              (1 << 4)
+#define QTD_TOKEN_XACTERR             (1 << 3)
+#define QTD_TOKEN_MISSEDUF            (1 << 2)
+#define QTD_TOKEN_SPLITXSTATE         (1 << 1)
+#define QTD_TOKEN_PING                (1 << 0)
+
+    uint32_t bufptr[5];               // Standard buffer pointer
+#define QTD_BUFPTR_MASK               0xfffff000
+#define QTD_BUFPTR_SH                 12
+} EHCIqtd;
+
+/*  EHCI spec version 1.0 Section 3.6
+ */
+typedef struct EHCIqh {
+    uint32_t next;                    // Standard next link pointer
+
+    /* endpoint characteristics */
+    uint32_t epchar;
+#define QH_EPCHAR_RL_MASK             0xf0000000
+#define QH_EPCHAR_RL_SH               28
+#define QH_EPCHAR_C                   (1 << 27)
+#define QH_EPCHAR_MPLEN_MASK          0x07FF0000
+#define QH_EPCHAR_MPLEN_SH            16
+#define QH_EPCHAR_H                   (1 << 15)
+#define QH_EPCHAR_DTC                 (1 << 14)
+#define QH_EPCHAR_EPS_MASK            0x00003000
+#define QH_EPCHAR_EPS_SH              12
+#define EHCI_QH_EPS_FULL              0
+#define EHCI_QH_EPS_LOW               1
+#define EHCI_QH_EPS_HIGH              2
+#define EHCI_QH_EPS_RESERVED          3
+
+#define QH_EPCHAR_EP_MASK             0x00000f00
+#define QH_EPCHAR_EP_SH               8
+#define QH_EPCHAR_I                   (1 << 7)
+#define QH_EPCHAR_DEVADDR_MASK        0x0000007f
+#define QH_EPCHAR_DEVADDR_SH          0
+
+    /* endpoint capabilities */
+    uint32_t epcap;
+#define QH_EPCAP_MULT_MASK            0xc0000000
+#define QH_EPCAP_MULT_SH              30
+#define QH_EPCAP_PORTNUM_MASK         0x3f800000
+#define QH_EPCAP_PORTNUM_SH           23
+#define QH_EPCAP_HUBADDR_MASK         0x007f0000
+#define QH_EPCAP_HUBADDR_SH           16
+#define QH_EPCAP_CMASK_MASK           0x0000ff00
+#define QH_EPCAP_CMASK_SH             8
+#define QH_EPCAP_SMASK_MASK           0x000000ff
+#define QH_EPCAP_SMASK_SH             0
+
+    uint32_t current_qtd;             // Standard next link pointer
+    uint32_t next_qtd;                // Standard next link pointer
+    uint32_t altnext_qtd;
+#define QH_ALTNEXT_NAKCNT_MASK        0x0000001e
+#define QH_ALTNEXT_NAKCNT_SH          1
+
+    uint32_t token;                   // Same as QTD token
+    uint32_t bufptr[5];               // Standard buffer pointer
+#define BUFPTR_CPROGMASK_MASK         0x000000ff
+#define BUFPTR_FRAMETAG_MASK          0x0000001f
+#define BUFPTR_SBYTES_MASK            0x00000fe0
+#define BUFPTR_SBYTES_SH              5
+} EHCIqh;
+
+/*  EHCI spec version 1.0 Section 3.7
+ */
+typedef struct EHCIfstn {
+    uint32_t next;                    // Standard next link pointer
+    uint32_t backptr;                 // Standard next link pointer
+} EHCIfstn;
+
+typedef struct EHCIQueue EHCIQueue;
+typedef struct EHCIState EHCIState;
+
+enum async_state {
+    EHCI_ASYNC_NONE = 0,
+    EHCI_ASYNC_INFLIGHT,
+    EHCI_ASYNC_FINISHED,
+};
+
+struct EHCIQueue {
+    EHCIState *ehci;
+    QTAILQ_ENTRY(EHCIQueue) next;
+    uint32_t seen;
+    uint64_t ts;
+
+    /* cached data from guest - needs to be flushed
+     * when guest removes an entry (doorbell, handshake sequence)
+     */
+    EHCIqh qh;             // copy of current QH (being worked on)
+    uint32_t qhaddr;       // address QH read from
+    EHCIqtd qtd;           // copy of current QTD (being worked on)
+    uint32_t qtdaddr;      // address QTD read from
+
+    USBPacket packet;
+    QEMUSGList sgl;
+    int pid;
+    uint32_t tbytes;
+    enum async_state async;
+    int usb_status;
+};
+
+typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
+
+struct EHCIState {
+    PCIDevice dev;
+    USBBus bus;
+    qemu_irq irq;
+    MemoryRegion mem;
+    int companion_count;
+
+    /* properties */
+    uint32_t freq;
+    uint32_t maxframes;
+
+    /*
+     *  EHCI spec version 1.0 Section 2.3
+     *  Host Controller Operational Registers
+     */
+    union {
+        uint8_t mmio[MMIO_SIZE];
+        struct {
+            uint8_t cap[OPREGBASE];
+            uint32_t usbcmd;
+            uint32_t usbsts;
+            uint32_t usbintr;
+            uint32_t frindex;
+            uint32_t ctrldssegment;
+            uint32_t periodiclistbase;
+            uint32_t asynclistaddr;
+            uint32_t notused[9];
+            uint32_t configflag;
+            uint32_t portsc[NB_PORTS];
+        };
+    };
+
+    /*
+     *  Internal states, shadow registers, etc
+     */
+    uint32_t sofv;
+    QEMUTimer *frame_timer;
+    int attach_poll_counter;
+    int astate;                        // Current state in asynchronous schedule
+    int pstate;                        // Current state in periodic schedule
+    USBPort ports[NB_PORTS];
+    USBPort *companion_ports[NB_PORTS];
+    uint32_t usbsts_pending;
+    EHCIQueueHead aqueues;
+    EHCIQueueHead pqueues;
+
+    uint32_t a_fetch_addr;   // which address to look at next
+    uint32_t p_fetch_addr;   // which address to look at next
+
+    USBPacket ipacket;
+    QEMUSGList isgl;
+    int isoch_pause;
+
+    uint64_t last_run_ns;
+};
+
+#define SET_LAST_RUN_CLOCK(s) \
+    (s)->last_run_ns = qemu_get_clock_ns(vm_clock);
+
+/* nifty macros from Arnon's EHCI version  */
+#define get_field(data, field) \
+    (((data) & field##_MASK) >> field##_SH)
+
+#define set_field(data, newval, field) do { \
+    uint32_t val = *data; \
+    val &= ~ field##_MASK; \
+    val |= ((newval) << field##_SH) & field##_MASK; \
+    *data = val; \
+    } while(0)
+
+static const char *ehci_state_names[] = {
+    [EST_INACTIVE]     = "INACTIVE",
+    [EST_ACTIVE]       = "ACTIVE",
+    [EST_EXECUTING]    = "EXECUTING",
+    [EST_SLEEPING]     = "SLEEPING",
+    [EST_WAITLISTHEAD] = "WAITLISTHEAD",
+    [EST_FETCHENTRY]   = "FETCH ENTRY",
+    [EST_FETCHQH]      = "FETCH QH",
+    [EST_FETCHITD]     = "FETCH ITD",
+    [EST_ADVANCEQUEUE] = "ADVANCEQUEUE",
+    [EST_FETCHQTD]     = "FETCH QTD",
+    [EST_EXECUTE]      = "EXECUTE",
+    [EST_WRITEBACK]    = "WRITEBACK",
+    [EST_HORIZONTALQH] = "HORIZONTALQH",
+};
+
+static const char *ehci_mmio_names[] = {
+    [CAPLENGTH]         = "CAPLENGTH",
+    [HCIVERSION]        = "HCIVERSION",
+    [HCSPARAMS]         = "HCSPARAMS",
+    [HCCPARAMS]         = "HCCPARAMS",
+    [USBCMD]            = "USBCMD",
+    [USBSTS]            = "USBSTS",
+    [USBINTR]           = "USBINTR",
+    [FRINDEX]           = "FRINDEX",
+    [PERIODICLISTBASE]  = "P-LIST BASE",
+    [ASYNCLISTADDR]     = "A-LIST ADDR",
+    [PORTSC_BEGIN]      = "PORTSC #0",
+    [PORTSC_BEGIN + 4]  = "PORTSC #1",
+    [PORTSC_BEGIN + 8]  = "PORTSC #2",
+    [PORTSC_BEGIN + 12] = "PORTSC #3",
+    [PORTSC_BEGIN + 16] = "PORTSC #4",
+    [PORTSC_BEGIN + 20] = "PORTSC #5",
+    [CONFIGFLAG]        = "CONFIGFLAG",
+};
+
+static const char *nr2str(const char **n, size_t len, uint32_t nr)
+{
+    if (nr < len && n[nr] != NULL) {
+        return n[nr];
+    } else {
+        return "unknown";
+    }
+}
+
+static const char *state2str(uint32_t state)
+{
+    return nr2str(ehci_state_names, ARRAY_SIZE(ehci_state_names), state);
+}
+
+static const char *addr2str(target_phys_addr_t addr)
+{
+    return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
+}
+
+static void ehci_trace_usbsts(uint32_t mask, int state)
+{
+    /* interrupts */
+    if (mask & USBSTS_INT) {
+        trace_usb_ehci_usbsts("INT", state);
+    }
+    if (mask & USBSTS_ERRINT) {
+        trace_usb_ehci_usbsts("ERRINT", state);
+    }
+    if (mask & USBSTS_PCD) {
+        trace_usb_ehci_usbsts("PCD", state);
+    }
+    if (mask & USBSTS_FLR) {
+        trace_usb_ehci_usbsts("FLR", state);
+    }
+    if (mask & USBSTS_HSE) {
+        trace_usb_ehci_usbsts("HSE", state);
+    }
+    if (mask & USBSTS_IAA) {
+        trace_usb_ehci_usbsts("IAA", state);
+    }
+
+    /* status */
+    if (mask & USBSTS_HALT) {
+        trace_usb_ehci_usbsts("HALT", state);
+    }
+    if (mask & USBSTS_REC) {
+        trace_usb_ehci_usbsts("REC", state);
+    }
+    if (mask & USBSTS_PSS) {
+        trace_usb_ehci_usbsts("PSS", state);
+    }
+    if (mask & USBSTS_ASS) {
+        trace_usb_ehci_usbsts("ASS", state);
+    }
+}
+
+static inline void ehci_set_usbsts(EHCIState *s, int mask)
+{
+    if ((s->usbsts & mask) == mask) {
+        return;
+    }
+    ehci_trace_usbsts(mask, 1);
+    s->usbsts |= mask;
+}
+
+static inline void ehci_clear_usbsts(EHCIState *s, int mask)
+{
+    if ((s->usbsts & mask) == 0) {
+        return;
+    }
+    ehci_trace_usbsts(mask, 0);
+    s->usbsts &= ~mask;
+}
+
+static inline void ehci_set_interrupt(EHCIState *s, int intr)
+{
+    int level = 0;
+
+    // TODO honour interrupt threshold requests
+
+    ehci_set_usbsts(s, intr);
+
+    if ((s->usbsts & USBINTR_MASK) & s->usbintr) {
+        level = 1;
+    }
+
+    qemu_set_irq(s->irq, level);
+}
+
+static inline void ehci_record_interrupt(EHCIState *s, int intr)
+{
+    s->usbsts_pending |= intr;
+}
+
+static inline void ehci_commit_interrupt(EHCIState *s)
+{
+    if (!s->usbsts_pending) {
+        return;
+    }
+    ehci_set_interrupt(s, s->usbsts_pending);
+    s->usbsts_pending = 0;
+}
+
+static void ehci_set_state(EHCIState *s, int async, int state)
+{
+    if (async) {
+        trace_usb_ehci_state("async", state2str(state));
+        s->astate = state;
+    } else {
+        trace_usb_ehci_state("periodic", state2str(state));
+        s->pstate = state;
+    }
+}
+
+static int ehci_get_state(EHCIState *s, int async)
+{
+    return async ? s->astate : s->pstate;
+}
+
+static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr)
+{
+    if (async) {
+        s->a_fetch_addr = addr;
+    } else {
+        s->p_fetch_addr = addr;
+    }
+}
+
+static int ehci_get_fetch_addr(EHCIState *s, int async)
+{
+    return async ? s->a_fetch_addr : s->p_fetch_addr;
+}
+
+static void ehci_trace_qh(EHCIQueue *q, target_phys_addr_t addr, EHCIqh *qh)
+{
+    /* need three here due to argument count limits */
+    trace_usb_ehci_qh_ptrs(q, addr, qh->next,
+                           qh->current_qtd, qh->next_qtd, qh->altnext_qtd);
+    trace_usb_ehci_qh_fields(addr,
+                             get_field(qh->epchar, QH_EPCHAR_RL),
+                             get_field(qh->epchar, QH_EPCHAR_MPLEN),
+                             get_field(qh->epchar, QH_EPCHAR_EPS),
+                             get_field(qh->epchar, QH_EPCHAR_EP),
+                             get_field(qh->epchar, QH_EPCHAR_DEVADDR));
+    trace_usb_ehci_qh_bits(addr,
+                           (bool)(qh->epchar & QH_EPCHAR_C),
+                           (bool)(qh->epchar & QH_EPCHAR_H),
+                           (bool)(qh->epchar & QH_EPCHAR_DTC),
+                           (bool)(qh->epchar & QH_EPCHAR_I));
+}
+
+static void ehci_trace_qtd(EHCIQueue *q, target_phys_addr_t addr, EHCIqtd *qtd)
+{
+    /* need three here due to argument count limits */
+    trace_usb_ehci_qtd_ptrs(q, addr, qtd->next, qtd->altnext);
+    trace_usb_ehci_qtd_fields(addr,
+                              get_field(qtd->token, QTD_TOKEN_TBYTES),
+                              get_field(qtd->token, QTD_TOKEN_CPAGE),
+                              get_field(qtd->token, QTD_TOKEN_CERR),
+                              get_field(qtd->token, QTD_TOKEN_PID));
+    trace_usb_ehci_qtd_bits(addr,
+                            (bool)(qtd->token & QTD_TOKEN_IOC),
+                            (bool)(qtd->token & QTD_TOKEN_ACTIVE),
+                            (bool)(qtd->token & QTD_TOKEN_HALT),
+                            (bool)(qtd->token & QTD_TOKEN_BABBLE),
+                            (bool)(qtd->token & QTD_TOKEN_XACTERR));
+}
+
+static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd)
+{
+    trace_usb_ehci_itd(addr, itd->next,
+                       get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT),
+                       get_field(itd->bufptr[2], ITD_BUFPTR_MULT),
+                       get_field(itd->bufptr[0], ITD_BUFPTR_EP),
+                       get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR));
+}
+
+static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
+                            EHCIsitd *sitd)
+{
+    trace_usb_ehci_sitd(addr, sitd->next,
+                        (bool)(sitd->results & SITD_RESULTS_ACTIVE));
+}
+
+/* queue management */
+
+static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async)
+{
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+    EHCIQueue *q;
+
+    q = g_malloc0(sizeof(*q));
+    q->ehci = ehci;
+    QTAILQ_INSERT_HEAD(head, q, next);
+    trace_usb_ehci_queue_action(q, "alloc");
+    return q;
+}
+
+static void ehci_free_queue(EHCIQueue *q, int async)
+{
+    EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues;
+    trace_usb_ehci_queue_action(q, "free");
+    if (q->async == EHCI_ASYNC_INFLIGHT) {
+        usb_cancel_packet(&q->packet);
+    }
+    QTAILQ_REMOVE(head, q, next);
+    g_free(q);
+}
+
+static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
+                                        int async)
+{
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+    EHCIQueue *q;
+
+    QTAILQ_FOREACH(q, head, next) {
+        if (addr == q->qhaddr) {
+            return q;
+        }
+    }
+    return NULL;
+}
+
+static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
+{
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+    EHCIQueue *q, *tmp;
+
+    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
+        if (q->seen) {
+            q->seen = 0;
+            q->ts = ehci->last_run_ns;
+            continue;
+        }
+        if (!flush && ehci->last_run_ns < q->ts + 250000000) {
+            /* allow 0.25 sec idle */
+            continue;
+        }
+        ehci_free_queue(q, async);
+    }
+}
+
+static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
+{
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+    EHCIQueue *q, *tmp;
+
+    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
+        if (!usb_packet_is_inflight(&q->packet) ||
+            q->packet.ep->dev != dev) {
+            continue;
+        }
+        ehci_free_queue(q, async);
+    }
+}
+
+static void ehci_queues_rip_all(EHCIState *ehci, int async)
+{
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+    EHCIQueue *q, *tmp;
+
+    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
+        ehci_free_queue(q, async);
+    }
+}
+
+/* Attach or detach a device on root hub */
+
+static void ehci_attach(USBPort *port)
+{
+    EHCIState *s = port->opaque;
+    uint32_t *portsc = &s->portsc[port->index];
+
+    trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
+
+    if (*portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->dev = port->dev;
+        companion->ops->attach(companion);
+        return;
+    }
+
+    *portsc |= PORTSC_CONNECT;
+    *portsc |= PORTSC_CSC;
+
+    ehci_set_interrupt(s, USBSTS_PCD);
+}
+
+static void ehci_detach(USBPort *port)
+{
+    EHCIState *s = port->opaque;
+    uint32_t *portsc = &s->portsc[port->index];
+
+    trace_usb_ehci_port_detach(port->index);
+
+    if (*portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->ops->detach(companion);
+        companion->dev = NULL;
+        /*
+         * EHCI spec 4.2.2: "When a disconnect occurs... On the event,
+         * the port ownership is returned immediately to the EHCI controller."
+         */
+        *portsc &= ~PORTSC_POWNER;
+        return;
+    }
+
+    ehci_queues_rip_device(s, port->dev, 0);
+    ehci_queues_rip_device(s, port->dev, 1);
+
+    *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
+    *portsc |= PORTSC_CSC;
+
+    ehci_set_interrupt(s, USBSTS_PCD);
+}
+
+static void ehci_child_detach(USBPort *port, USBDevice *child)
+{
+    EHCIState *s = port->opaque;
+    uint32_t portsc = s->portsc[port->index];
+
+    if (portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->ops->child_detach(companion, child);
+        companion->dev = NULL;
+        return;
+    }
+
+    ehci_queues_rip_device(s, child, 0);
+    ehci_queues_rip_device(s, child, 1);
+}
+
+static void ehci_wakeup(USBPort *port)
+{
+    EHCIState *s = port->opaque;
+    uint32_t portsc = s->portsc[port->index];
+
+    if (portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        if (companion->ops->wakeup) {
+            companion->ops->wakeup(companion);
+        }
+    }
+}
+
+static int ehci_register_companion(USBBus *bus, USBPort *ports[],
+                                   uint32_t portcount, uint32_t firstport)
+{
+    EHCIState *s = container_of(bus, EHCIState, bus);
+    uint32_t i;
+
+    if (firstport + portcount > NB_PORTS) {
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport",
+                      "firstport on masterbus");
+        error_printf_unless_qmp(
+            "firstport value of %u makes companion take ports %u - %u, which "
+            "is outside of the valid range of 0 - %u\n", firstport, firstport,
+            firstport + portcount - 1, NB_PORTS - 1);
+        return -1;
+    }
+
+    for (i = 0; i < portcount; i++) {
+        if (s->companion_ports[firstport + i]) {
+            qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
+                          "an USB masterbus");
+            error_printf_unless_qmp(
+                "port %u on masterbus %s already has a companion assigned\n",
+                firstport + i, bus->qbus.name);
+            return -1;
+        }
+    }
+
+    for (i = 0; i < portcount; i++) {
+        s->companion_ports[firstport + i] = ports[i];
+        s->ports[firstport + i].speedmask |=
+            USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL;
+        /* Ensure devs attached before the initial reset go to the companion */
+        s->portsc[firstport + i] = PORTSC_POWNER;
+    }
+
+    s->companion_count++;
+    s->mmio[0x05] = (s->companion_count << 4) | portcount;
+
+    return 0;
+}
+
+static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr)
+{
+    USBDevice *dev;
+    USBPort *port;
+    int i;
+
+    for (i = 0; i < NB_PORTS; i++) {
+        port = &ehci->ports[i];
+        if (!(ehci->portsc[i] & PORTSC_PED)) {
+            DPRINTF("Port %d not enabled\n", i);
+            continue;
+        }
+        dev = usb_find_device(port, addr);
+        if (dev != NULL) {
+            return dev;
+        }
+    }
+    return NULL;
+}
+
+/* 4.1 host controller initialization */
+static void ehci_reset(void *opaque)
+{
+    EHCIState *s = opaque;
+    int i;
+    USBDevice *devs[NB_PORTS];
+
+    trace_usb_ehci_reset();
+
+    /*
+     * Do the detach before touching portsc, so that it correctly gets send to
+     * us or to our companion based on PORTSC_POWNER before the reset.
+     */
+    for(i = 0; i < NB_PORTS; i++) {
+        devs[i] = s->ports[i].dev;
+        if (devs[i] && devs[i]->attached) {
+            usb_detach(&s->ports[i]);
+        }
+    }
+
+    memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
+
+    s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
+    s->usbsts = USBSTS_HALT;
+
+    s->astate = EST_INACTIVE;
+    s->pstate = EST_INACTIVE;
+    s->isoch_pause = -1;
+    s->attach_poll_counter = 0;
+
+    for(i = 0; i < NB_PORTS; i++) {
+        if (s->companion_ports[i]) {
+            s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
+        } else {
+            s->portsc[i] = PORTSC_PPOWER;
+        }
+        if (devs[i] && devs[i]->attached) {
+            usb_attach(&s->ports[i]);
+            usb_device_reset(devs[i]);
+        }
+    }
+    ehci_queues_rip_all(s, 0);
+    ehci_queues_rip_all(s, 1);
+    qemu_del_timer(s->frame_timer);
+}
+
+static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr];
+
+    return val;
+}
+
+static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr] | (s->mmio[addr+1] << 8);
+
+    return val;
+}
+
+static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
+          (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
+
+    trace_usb_ehci_mmio_readl(addr, addr2str(addr), val);
+    return val;
+}
+
+static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
+    exit(1);
+}
+
+static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
+    exit(1);
+}
+
+static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
+{
+    USBDevice *dev = s->ports[port].dev;
+    uint32_t *portsc = &s->portsc[port];
+    uint32_t orig;
+
+    if (s->companion_ports[port] == NULL)
+        return;
+
+    owner = owner & PORTSC_POWNER;
+    orig  = *portsc & PORTSC_POWNER;
+
+    if (!(owner ^ orig)) {
+        return;
+    }
+
+    if (dev && dev->attached) {
+        usb_detach(&s->ports[port]);
+    }
+
+    *portsc &= ~PORTSC_POWNER;
+    *portsc |= owner;
+
+    if (dev && dev->attached) {
+        usb_attach(&s->ports[port]);
+    }
+}
+
+static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
+{
+    uint32_t *portsc = &s->portsc[port];
+    USBDevice *dev = s->ports[port].dev;
+
+    /* Clear rwc bits */
+    *portsc &= ~(val & PORTSC_RWC_MASK);
+    /* The guest may clear, but not set the PED bit */
+    *portsc &= val | ~PORTSC_PED;
+    /* POWNER is masked out by RO_MASK as it is RO when we've no companion */
+    handle_port_owner_write(s, port, val);
+    /* And finally apply RO_MASK */
+    val &= PORTSC_RO_MASK;
+
+    if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
+        trace_usb_ehci_port_reset(port, 1);
+    }
+
+    if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
+        trace_usb_ehci_port_reset(port, 0);
+        if (dev && dev->attached) {
+            usb_port_reset(&s->ports[port]);
+            *portsc &= ~PORTSC_CSC;
+        }
+
+        /*
+         *  Table 2.16 Set the enable bit(and enable bit change) to indicate
+         *  to SW that this port has a high speed device attached
+         */
+        if (dev && dev->attached && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
+            val |= PORTSC_PED;
+        }
+    }
+
+    *portsc &= ~PORTSC_RO_MASK;
+    *portsc |= val;
+}
+
+static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    EHCIState *s = ptr;
+    uint32_t *mmio = (uint32_t *)(&s->mmio[addr]);
+    uint32_t old = *mmio;
+    int i;
+
+    trace_usb_ehci_mmio_writel(addr, addr2str(addr), val);
+
+    /* Only aligned reads are allowed on OHCI */
+    if (addr & 3) {
+        fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
+                TARGET_FMT_plx "\n", addr);
+        return;
+    }
+
+    if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
+        handle_port_status_write(s, (addr-PORTSC)/4, val);
+        trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
+        return;
+    }
+
+    if (addr < OPREGBASE) {
+        fprintf(stderr, "usb-ehci: write attempt to read-only register"
+                TARGET_FMT_plx "\n", addr);
+        return;
+    }
+
+
+    /* Do any register specific pre-write processing here.  */
+    switch(addr) {
+    case USBCMD:
+        if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
+            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
+            SET_LAST_RUN_CLOCK(s);
+            ehci_clear_usbsts(s, USBSTS_HALT);
+        }
+
+        if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            qemu_del_timer(s->frame_timer);
+            ehci_queues_rip_all(s, 0);
+            ehci_queues_rip_all(s, 1);
+            ehci_set_usbsts(s, USBSTS_HALT);
+        }
+
+        if (val & USBCMD_HCRESET) {
+            ehci_reset(s);
+            val = s->usbcmd;
+        }
+
+        /* not supporting dynamic frame list size at the moment */
+        if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
+            fprintf(stderr, "attempt to set frame list size -- value %d\n",
+                    val & USBCMD_FLS);
+            val &= ~USBCMD_FLS;
+        }
+        break;
+
+    case USBSTS:
+        val &= USBSTS_RO_MASK;              // bits 6 thru 31 are RO
+        ehci_clear_usbsts(s, val);          // bits 0 thru 5 are R/WC
+        val = s->usbsts;
+        ehci_set_interrupt(s, 0);
+        break;
+
+    case USBINTR:
+        val &= USBINTR_MASK;
+        break;
+
+    case FRINDEX:
+        s->sofv = val >> 3;
+        break;
+
+    case CONFIGFLAG:
+        val &= 0x1;
+        if (val) {
+            for(i = 0; i < NB_PORTS; i++)
+                handle_port_owner_write(s, i, 0);
+        }
+        break;
+
+    case PERIODICLISTBASE:
+        if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            fprintf(stderr,
+              "ehci: PERIODIC list base register set while periodic schedule\n"
+              "      is enabled and HC is enabled\n");
+        }
+        break;
+
+    case ASYNCLISTADDR:
+        if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            fprintf(stderr,
+              "ehci: ASYNC list address register set while async schedule\n"
+              "      is enabled and HC is enabled\n");
+        }
+        break;
+    }
+
+    *mmio = val;
+    trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
+}
+
+
+// TODO : Put in common header file, duplication from usb-ohci.c
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(EHCIState *ehci, uint32_t addr,
+                             uint32_t *buf, int num)
+{
+    int i;
+
+    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        pci_dma_read(&ehci->dev, addr, buf, sizeof(*buf));
+        *buf = le32_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(EHCIState *ehci, uint32_t addr,
+                             uint32_t *buf, int num)
+{
+    int i;
+
+    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint32_t tmp = cpu_to_le32(*buf);
+        pci_dma_write(&ehci->dev, addr, &tmp, sizeof(tmp));
+    }
+
+    return 1;
+}
+
+// 4.10.2
+
+static int ehci_qh_do_overlay(EHCIQueue *q)
+{
+    int i;
+    int dtoggle;
+    int ping;
+    int eps;
+    int reload;
+
+    // remember values in fields to preserve in qh after overlay
+
+    dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE;
+    ping    = q->qh.token & QTD_TOKEN_PING;
+
+    q->qh.current_qtd = q->qtdaddr;
+    q->qh.next_qtd    = q->qtd.next;
+    q->qh.altnext_qtd = q->qtd.altnext;
+    q->qh.token       = q->qtd.token;
+
+
+    eps = get_field(q->qh.epchar, QH_EPCHAR_EPS);
+    if (eps == EHCI_QH_EPS_HIGH) {
+        q->qh.token &= ~QTD_TOKEN_PING;
+        q->qh.token |= ping;
+    }
+
+    reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
+    set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+
+    for (i = 0; i < 5; i++) {
+        q->qh.bufptr[i] = q->qtd.bufptr[i];
+    }
+
+    if (!(q->qh.epchar & QH_EPCHAR_DTC)) {
+        // preserve QH DT bit
+        q->qh.token &= ~QTD_TOKEN_DTOGGLE;
+        q->qh.token |= dtoggle;
+    }
+
+    q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
+    q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
+
+    put_dwords(q->ehci, NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh,
+               sizeof(EHCIqh) >> 2);
+
+    return 0;
+}
+
+static int ehci_init_transfer(EHCIQueue *q)
+{
+    uint32_t cpage, offset, bytes, plen;
+    dma_addr_t page;
+
+    cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
+    bytes  = get_field(q->qh.token, QTD_TOKEN_TBYTES);
+    offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
+    pci_dma_sglist_init(&q->sgl, &q->ehci->dev, 5);
+
+    while (bytes > 0) {
+        if (cpage > 4) {
+            fprintf(stderr, "cpage out of range (%d)\n", cpage);
+            return USB_RET_PROCERR;
+        }
+
+        page  = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
+        page += offset;
+        plen  = bytes;
+        if (plen > 4096 - offset) {
+            plen = 4096 - offset;
+            offset = 0;
+            cpage++;
+        }
+
+        qemu_sglist_add(&q->sgl, page, plen);
+        bytes -= plen;
+    }
+    return 0;
+}
+
+static void ehci_finish_transfer(EHCIQueue *q, int status)
+{
+    uint32_t cpage, offset;
+
+    qemu_sglist_destroy(&q->sgl);
+
+    if (status > 0) {
+        /* update cpage & offset */
+        cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
+        offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
+
+        offset += status;
+        cpage  += offset >> QTD_BUFPTR_SH;
+        offset &= ~QTD_BUFPTR_MASK;
+
+        set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
+        q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
+        q->qh.bufptr[0] |= offset;
+    }
+}
+
+static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
+{
+    EHCIQueue *q;
+    EHCIState *s = port->opaque;
+    uint32_t portsc = s->portsc[port->index];
+
+    if (portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->ops->complete(companion, packet);
+        return;
+    }
+
+    q = container_of(packet, EHCIQueue, packet);
+    trace_usb_ehci_queue_action(q, "wakeup");
+    assert(q->async == EHCI_ASYNC_INFLIGHT);
+    q->async = EHCI_ASYNC_FINISHED;
+    q->usb_status = packet->result;
+}
+
+static void ehci_execute_complete(EHCIQueue *q)
+{
+    assert(q->async != EHCI_ASYNC_INFLIGHT);
+    q->async = EHCI_ASYNC_NONE;
+
+    DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
+            q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
+
+    if (q->usb_status < 0) {
+        switch(q->usb_status) {
+        case USB_RET_IOERROR:
+        case USB_RET_NODEV:
+            q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
+            set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
+            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+            break;
+        case USB_RET_STALL:
+            q->qh.token |= QTD_TOKEN_HALT;
+            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+            break;
+        case USB_RET_NAK:
+            set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
+            return; /* We're not done yet with this transaction */
+        case USB_RET_BABBLE:
+            q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
+            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+            break;
+        default:
+            /* should not be triggerable */
+            fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status);
+            assert(0);
+            break;
+        }
+    } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) {
+        q->usb_status = USB_RET_BABBLE;
+        q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
+        ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+    } else {
+        // TODO check 4.12 for splits
+
+        if (q->tbytes && q->pid == USB_TOKEN_IN) {
+            q->tbytes -= q->usb_status;
+        } else {
+            q->tbytes = 0;
+        }
+
+        DPRINTF("updating tbytes to %d\n", q->tbytes);
+        set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
+    }
+    ehci_finish_transfer(q, q->usb_status);
+    usb_packet_unmap(&q->packet);
+
+    q->qh.token ^= QTD_TOKEN_DTOGGLE;
+    q->qh.token &= ~QTD_TOKEN_ACTIVE;
+
+    if (q->qh.token & QTD_TOKEN_IOC) {
+        ehci_record_interrupt(q->ehci, USBSTS_INT);
+    }
+}
+
+// 4.10.3
+
+static int ehci_execute(EHCIQueue *q)
+{
+    USBDevice *dev;
+    USBEndpoint *ep;
+    int ret;
+    int endp;
+    int devadr;
+
+    if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) {
+        fprintf(stderr, "Attempting to execute inactive QH\n");
+        return USB_RET_PROCERR;
+    }
+
+    q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+    if (q->tbytes > BUFF_SIZE) {
+        fprintf(stderr, "Request for more bytes than allowed\n");
+        return USB_RET_PROCERR;
+    }
+
+    q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
+    switch(q->pid) {
+        case 0: q->pid = USB_TOKEN_OUT; break;
+        case 1: q->pid = USB_TOKEN_IN; break;
+        case 2: q->pid = USB_TOKEN_SETUP; break;
+        default: fprintf(stderr, "bad token\n"); break;
+    }
+
+    if (ehci_init_transfer(q) != 0) {
+        return USB_RET_PROCERR;
+    }
+
+    endp = get_field(q->qh.epchar, QH_EPCHAR_EP);
+    devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
+
+    /* TODO: associating device with ehci port */
+    dev = ehci_find_device(q->ehci, devadr);
+    ep = usb_ep_get(dev, q->pid, endp);
+
+    usb_packet_setup(&q->packet, q->pid, ep);
+    usb_packet_map(&q->packet, &q->sgl);
+
+    ret = usb_handle_packet(dev, &q->packet);
+    DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
+            "(total %d) endp %x ret %d\n",
+            q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
+            q->packet.iov.size, q->tbytes, endp, ret);
+
+    if (ret > BUFF_SIZE) {
+        fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
+        return USB_RET_PROCERR;
+    }
+
+    return ret;
+}
+
+/*  4.7.2
+ */
+
+static int ehci_process_itd(EHCIState *ehci,
+                            EHCIitd *itd)
+{
+    USBDevice *dev;
+    USBEndpoint *ep;
+    int ret;
+    uint32_t i, len, pid, dir, devaddr, endp;
+    uint32_t pg, off, ptr1, ptr2, max, mult;
+
+    dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
+    devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
+    endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
+    max = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
+    mult = get_field(itd->bufptr[2], ITD_BUFPTR_MULT);
+
+    for(i = 0; i < 8; i++) {
+        if (itd->transact[i] & ITD_XACT_ACTIVE) {
+            pg   = get_field(itd->transact[i], ITD_XACT_PGSEL);
+            off  = itd->transact[i] & ITD_XACT_OFFSET_MASK;
+            ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
+            ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK);
+            len  = get_field(itd->transact[i], ITD_XACT_LENGTH);
+
+            if (len > max * mult) {
+                len = max * mult;
+            }
+
+            if (len > BUFF_SIZE) {
+                return USB_RET_PROCERR;
+            }
+
+            pci_dma_sglist_init(&ehci->isgl, &ehci->dev, 2);
+            if (off + len > 4096) {
+                /* transfer crosses page border */
+                uint32_t len2 = off + len - 4096;
+                uint32_t len1 = len - len2;
+                qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
+                qemu_sglist_add(&ehci->isgl, ptr2, len2);
+            } else {
+                qemu_sglist_add(&ehci->isgl, ptr1 + off, len);
+            }
+
+            pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT;
+
+            dev = ehci_find_device(ehci, devaddr);
+            ep = usb_ep_get(dev, pid, endp);
+            if (ep->type == USB_ENDPOINT_XFER_ISOC) {
+                usb_packet_setup(&ehci->ipacket, pid, ep);
+                usb_packet_map(&ehci->ipacket, &ehci->isgl);
+                ret = usb_handle_packet(dev, &ehci->ipacket);
+                assert(ret != USB_RET_ASYNC);
+                usb_packet_unmap(&ehci->ipacket);
+            } else {
+                DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
+                ret = USB_RET_NAK;
+            }
+            qemu_sglist_destroy(&ehci->isgl);
+
+            if (ret < 0) {
+                switch (ret) {
+                default:
+                    fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
+                    /* Fall through */
+                case USB_RET_IOERROR:
+                case USB_RET_NODEV:
+                    /* 3.3.2: XACTERR is only allowed on IN transactions */
+                    if (dir) {
+                        itd->transact[i] |= ITD_XACT_XACTERR;
+                        ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    }
+                    break;
+                case USB_RET_BABBLE:
+                    itd->transact[i] |= ITD_XACT_BABBLE;
+                    ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    break;
+                case USB_RET_NAK:
+                    /* no data for us, so do a zero-length transfer */
+                    ret = 0;
+                    break;
+                }
+            }
+            if (ret >= 0) {
+                if (!dir) {
+                    /* OUT */
+                    set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
+                } else {
+                    /* IN */
+                    set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
+                }
+            }
+            if (itd->transact[i] & ITD_XACT_IOC) {
+                ehci_record_interrupt(ehci, USBSTS_INT);
+            }
+            itd->transact[i] &= ~ITD_XACT_ACTIVE;
+        }
+    }
+    return 0;
+}
+
+/*  This state is the entry point for asynchronous schedule
+ *  processing.  Entry here consitutes a EHCI start event state (4.8.5)
+ */
+static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
+{
+    EHCIqh qh;
+    int i = 0;
+    int again = 0;
+    uint32_t entry = ehci->asynclistaddr;
+
+    /* set reclamation flag at start event (4.8.6) */
+    if (async) {
+        ehci_set_usbsts(ehci, USBSTS_REC);
+    }
+
+    ehci_queues_rip_unused(ehci, async, 0);
+
+    /*  Find the head of the list (4.9.1.1) */
+    for(i = 0; i < MAX_QH; i++) {
+        get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh,
+                   sizeof(EHCIqh) >> 2);
+        ehci_trace_qh(NULL, NLPTR_GET(entry), &qh);
+
+        if (qh.epchar & QH_EPCHAR_H) {
+            if (async) {
+                entry |= (NLPTR_TYPE_QH << 1);
+            }
+
+            ehci_set_fetch_addr(ehci, async, entry);
+            ehci_set_state(ehci, async, EST_FETCHENTRY);
+            again = 1;
+            goto out;
+        }
+
+        entry = qh.next;
+        if (entry == ehci->asynclistaddr) {
+            break;
+        }
+    }
+
+    /* no head found for list. */
+
+    ehci_set_state(ehci, async, EST_ACTIVE);
+
+out:
+    return again;
+}
+
+
+/*  This state is the entry point for periodic schedule processing as
+ *  well as being a continuation state for async processing.
+ */
+static int ehci_state_fetchentry(EHCIState *ehci, int async)
+{
+    int again = 0;
+    uint32_t entry = ehci_get_fetch_addr(ehci, async);
+
+    if (NLPTR_TBIT(entry)) {
+        ehci_set_state(ehci, async, EST_ACTIVE);
+        goto out;
+    }
+
+    /* section 4.8, only QH in async schedule */
+    if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) {
+        fprintf(stderr, "non queue head request in async schedule\n");
+        return -1;
+    }
+
+    switch (NLPTR_TYPE_GET(entry)) {
+    case NLPTR_TYPE_QH:
+        ehci_set_state(ehci, async, EST_FETCHQH);
+        again = 1;
+        break;
+
+    case NLPTR_TYPE_ITD:
+        ehci_set_state(ehci, async, EST_FETCHITD);
+        again = 1;
+        break;
+
+    case NLPTR_TYPE_STITD:
+        ehci_set_state(ehci, async, EST_FETCHSITD);
+        again = 1;
+        break;
+
+    default:
+        /* TODO: handle FSTN type */
+        fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
+                "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
+        return -1;
+    }
+
+out:
+    return again;
+}
+
+static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
+{
+    uint32_t entry;
+    EHCIQueue *q;
+
+    entry = ehci_get_fetch_addr(ehci, async);
+    q = ehci_find_queue_by_qh(ehci, entry, async);
+    if (NULL == q) {
+        q = ehci_alloc_queue(ehci, async);
+    }
+    q->qhaddr = entry;
+    q->seen++;
+
+    if (q->seen > 1) {
+        /* we are going in circles -- stop processing */
+        ehci_set_state(ehci, async, EST_ACTIVE);
+        q = NULL;
+        goto out;
+    }
+
+    get_dwords(ehci, NLPTR_GET(q->qhaddr),
+               (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
+    ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
+
+    if (q->async == EHCI_ASYNC_INFLIGHT) {
+        /* I/O still in progress -- skip queue */
+        ehci_set_state(ehci, async, EST_HORIZONTALQH);
+        goto out;
+    }
+    if (q->async == EHCI_ASYNC_FINISHED) {
+        /* I/O finished -- continue processing queue */
+        trace_usb_ehci_queue_action(q, "resume");
+        ehci_set_state(ehci, async, EST_EXECUTING);
+        goto out;
+    }
+
+    if (async && (q->qh.epchar & QH_EPCHAR_H)) {
+
+        /*  EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
+        if (ehci->usbsts & USBSTS_REC) {
+            ehci_clear_usbsts(ehci, USBSTS_REC);
+        } else {
+            DPRINTF("FETCHQH:  QH 0x%08x. H-bit set, reclamation status reset"
+                       " - done processing\n", q->qhaddr);
+            ehci_set_state(ehci, async, EST_ACTIVE);
+            q = NULL;
+            goto out;
+        }
+    }
+
+#if EHCI_DEBUG
+    if (q->qhaddr != q->qh.next) {
+    DPRINTF("FETCHQH:  QH 0x%08x (h %x halt %x active %x) next 0x%08x\n",
+               q->qhaddr,
+               q->qh.epchar & QH_EPCHAR_H,
+               q->qh.token & QTD_TOKEN_HALT,
+               q->qh.token & QTD_TOKEN_ACTIVE,
+               q->qh.next);
+    }
+#endif
+
+    if (q->qh.token & QTD_TOKEN_HALT) {
+        ehci_set_state(ehci, async, EST_HORIZONTALQH);
+
+    } else if ((q->qh.token & QTD_TOKEN_ACTIVE) &&
+               (NLPTR_TBIT(q->qh.current_qtd) == 0)) {
+        q->qtdaddr = q->qh.current_qtd;
+        ehci_set_state(ehci, async, EST_FETCHQTD);
+
+    } else {
+        /*  EHCI spec version 1.0 Section 4.10.2 */
+        ehci_set_state(ehci, async, EST_ADVANCEQUEUE);
+    }
+
+out:
+    return q;
+}
+
+static int ehci_state_fetchitd(EHCIState *ehci, int async)
+{
+    uint32_t entry;
+    EHCIitd itd;
+
+    assert(!async);
+    entry = ehci_get_fetch_addr(ehci, async);
+
+    get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
+               sizeof(EHCIitd) >> 2);
+    ehci_trace_itd(ehci, entry, &itd);
+
+    if (ehci_process_itd(ehci, &itd) != 0) {
+        return -1;
+    }
+
+    put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
+               sizeof(EHCIitd) >> 2);
+    ehci_set_fetch_addr(ehci, async, itd.next);
+    ehci_set_state(ehci, async, EST_FETCHENTRY);
+
+    return 1;
+}
+
+static int ehci_state_fetchsitd(EHCIState *ehci, int async)
+{
+    uint32_t entry;
+    EHCIsitd sitd;
+
+    assert(!async);
+    entry = ehci_get_fetch_addr(ehci, async);
+
+    get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd,
+               sizeof(EHCIsitd) >> 2);
+    ehci_trace_sitd(ehci, entry, &sitd);
+
+    if (!(sitd.results & SITD_RESULTS_ACTIVE)) {
+        /* siTD is not active, nothing to do */;
+    } else {
+        /* TODO: split transfers are not implemented */
+        fprintf(stderr, "WARNING: Skipping active siTD\n");
+    }
+
+    ehci_set_fetch_addr(ehci, async, sitd.next);
+    ehci_set_state(ehci, async, EST_FETCHENTRY);
+    return 1;
+}
+
+/* Section 4.10.2 - paragraph 3 */
+static int ehci_state_advqueue(EHCIQueue *q, int async)
+{
+#if 0
+    /* TO-DO: 4.10.2 - paragraph 2
+     * if I-bit is set to 1 and QH is not active
+     * go to horizontal QH
+     */
+    if (I-bit set) {
+        ehci_set_state(ehci, async, EST_HORIZONTALQH);
+        goto out;
+    }
+#endif
+
+    /*
+     * want data and alt-next qTD is valid
+     */
+    if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
+        (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
+        q->qtdaddr = q->qh.altnext_qtd;
+        ehci_set_state(q->ehci, async, EST_FETCHQTD);
+
+    /*
+     *  next qTD is valid
+     */
+    } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) {
+        q->qtdaddr = q->qh.next_qtd;
+        ehci_set_state(q->ehci, async, EST_FETCHQTD);
+
+    /*
+     *  no valid qTD, try next QH
+     */
+    } else {
+        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+    }
+
+    return 1;
+}
+
+/* Section 4.10.2 - paragraph 4 */
+static int ehci_state_fetchqtd(EHCIQueue *q, int async)
+{
+    int again = 0;
+
+    get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qtd,
+               sizeof(EHCIqtd) >> 2);
+    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd);
+
+    if (q->qtd.token & QTD_TOKEN_ACTIVE) {
+        ehci_set_state(q->ehci, async, EST_EXECUTE);
+        again = 1;
+    } else {
+        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+        again = 1;
+    }
+
+    return again;
+}
+
+static int ehci_state_horizqh(EHCIQueue *q, int async)
+{
+    int again = 0;
+
+    if (ehci_get_fetch_addr(q->ehci, async) != q->qh.next) {
+        ehci_set_fetch_addr(q->ehci, async, q->qh.next);
+        ehci_set_state(q->ehci, async, EST_FETCHENTRY);
+        again = 1;
+    } else {
+        ehci_set_state(q->ehci, async, EST_ACTIVE);
+    }
+
+    return again;
+}
+
+/*
+ *  Write the qh back to guest physical memory.  This step isn't
+ *  in the EHCI spec but we need to do it since we don't share
+ *  physical memory with our guest VM.
+ *
+ *  The first three dwords are read-only for the EHCI, so skip them
+ *  when writing back the qh.
+ */
+static void ehci_flush_qh(EHCIQueue *q)
+{
+    uint32_t *qh = (uint32_t *) &q->qh;
+    uint32_t dwords = sizeof(EHCIqh) >> 2;
+    uint32_t addr = NLPTR_GET(q->qhaddr);
+
+    put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
+}
+
+static int ehci_state_execute(EHCIQueue *q, int async)
+{
+    int again = 0;
+
+    if (ehci_qh_do_overlay(q) != 0) {
+        return -1;
+    }
+
+    // TODO verify enough time remains in the uframe as in 4.4.1.1
+    // TODO write back ptr to async list when done or out of time
+    // TODO Windows does not seem to ever set the MULT field
+
+    if (!async) {
+        int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
+        if (!transactCtr) {
+            ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+            again = 1;
+            goto out;
+        }
+    }
+
+    if (async) {
+        ehci_set_usbsts(q->ehci, USBSTS_REC);
+    }
+
+    q->usb_status = ehci_execute(q);
+    if (q->usb_status == USB_RET_PROCERR) {
+        again = -1;
+        goto out;
+    }
+    if (q->usb_status == USB_RET_ASYNC) {
+        ehci_flush_qh(q);
+        trace_usb_ehci_queue_action(q, "suspend");
+        q->async = EHCI_ASYNC_INFLIGHT;
+        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+        again = 1;
+        goto out;
+    }
+
+    ehci_set_state(q->ehci, async, EST_EXECUTING);
+    again = 1;
+
+out:
+    return again;
+}
+
+static int ehci_state_executing(EHCIQueue *q, int async)
+{
+    int again = 0;
+
+    ehci_execute_complete(q);
+    if (q->usb_status == USB_RET_ASYNC) {
+        goto out;
+    }
+    if (q->usb_status == USB_RET_PROCERR) {
+        again = -1;
+        goto out;
+    }
+
+    // 4.10.3
+    if (!async) {
+        int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
+        transactCtr--;
+        set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT);
+        // 4.10.3, bottom of page 82, should exit this state when transaction
+        // counter decrements to 0
+    }
+
+    /* 4.10.5 */
+    if (q->usb_status == USB_RET_NAK) {
+        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+    } else {
+        ehci_set_state(q->ehci, async, EST_WRITEBACK);
+    }
+
+    again = 1;
+
+out:
+    ehci_flush_qh(q);
+    return again;
+}
+
+
+static int ehci_state_writeback(EHCIQueue *q, int async)
+{
+    int again = 0;
+
+    /*  Write back the QTD from the QH area */
+    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd);
+    put_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qh.next_qtd,
+               sizeof(EHCIqtd) >> 2);
+
+    /*
+     * EHCI specs say go horizontal here.
+     *
+     * We can also advance the queue here for performance reasons.  We
+     * need to take care to only take that shortcut in case we've
+     * processed the qtd just written back without errors, i.e. halt
+     * bit is clear.
+     */
+    if (q->qh.token & QTD_TOKEN_HALT) {
+        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+        again = 1;
+    } else {
+        ehci_set_state(q->ehci, async, EST_ADVANCEQUEUE);
+        again = 1;
+    }
+    return again;
+}
+
+/*
+ * This is the state machine that is common to both async and periodic
+ */
+
+static void ehci_advance_state(EHCIState *ehci,
+                               int async)
+{
+    EHCIQueue *q = NULL;
+    int again;
+    int iter = 0;
+
+    do {
+        if (ehci_get_state(ehci, async) == EST_FETCHQH) {
+            iter++;
+            /* if we are roaming a lot of QH without executing a qTD
+             * something is wrong with the linked list. TO-DO: why is
+             * this hack needed?
+             */
+            assert(iter < MAX_ITERATIONS);
+#if 0
+            if (iter > MAX_ITERATIONS) {
+                DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n");
+                ehci_set_state(ehci, async, EST_ACTIVE);
+                break;
+            }
+#endif
+        }
+        switch(ehci_get_state(ehci, async)) {
+        case EST_WAITLISTHEAD:
+            again = ehci_state_waitlisthead(ehci, async);
+            break;
+
+        case EST_FETCHENTRY:
+            again = ehci_state_fetchentry(ehci, async);
+            break;
+
+        case EST_FETCHQH:
+            q = ehci_state_fetchqh(ehci, async);
+            again = q ? 1 : 0;
+            break;
+
+        case EST_FETCHITD:
+            again = ehci_state_fetchitd(ehci, async);
+            break;
+
+        case EST_FETCHSITD:
+            again = ehci_state_fetchsitd(ehci, async);
+            break;
+
+        case EST_ADVANCEQUEUE:
+            again = ehci_state_advqueue(q, async);
+            break;
+
+        case EST_FETCHQTD:
+            again = ehci_state_fetchqtd(q, async);
+            break;
+
+        case EST_HORIZONTALQH:
+            again = ehci_state_horizqh(q, async);
+            break;
+
+        case EST_EXECUTE:
+            iter = 0;
+            again = ehci_state_execute(q, async);
+            break;
+
+        case EST_EXECUTING:
+            assert(q != NULL);
+            again = ehci_state_executing(q, async);
+            break;
+
+        case EST_WRITEBACK:
+            assert(q != NULL);
+            again = ehci_state_writeback(q, async);
+            break;
+
+        default:
+            fprintf(stderr, "Bad state!\n");
+            again = -1;
+            assert(0);
+            break;
+        }
+
+        if (again < 0) {
+            fprintf(stderr, "processing error - resetting ehci HC\n");
+            ehci_reset(ehci);
+            again = 0;
+            assert(0);
+        }
+    }
+    while (again);
+
+    ehci_commit_interrupt(ehci);
+}
+
+static void ehci_advance_async_state(EHCIState *ehci)
+{
+    const int async = 1;
+
+    switch(ehci_get_state(ehci, async)) {
+    case EST_INACTIVE:
+        if (!(ehci->usbcmd & USBCMD_ASE)) {
+            break;
+        }
+        ehci_set_usbsts(ehci, USBSTS_ASS);
+        ehci_set_state(ehci, async, EST_ACTIVE);
+        // No break, fall through to ACTIVE
+
+    case EST_ACTIVE:
+        if ( !(ehci->usbcmd & USBCMD_ASE)) {
+            ehci_queues_rip_all(ehci, async);
+            ehci_clear_usbsts(ehci, USBSTS_ASS);
+            ehci_set_state(ehci, async, EST_INACTIVE);
+            break;
+        }
+
+        /* make sure guest has acknowledged the doorbell interrupt */
+        /* TO-DO: is this really needed? */
+        if (ehci->usbsts & USBSTS_IAA) {
+            DPRINTF("IAA status bit still set.\n");
+            break;
+        }
+
+        /* check that address register has been set */
+        if (ehci->asynclistaddr == 0) {
+            break;
+        }
+
+        ehci_set_state(ehci, async, EST_WAITLISTHEAD);
+        ehci_advance_state(ehci, async);
+
+        /* If the doorbell is set, the guest wants to make a change to the
+         * schedule. The host controller needs to release cached data.
+         * (section 4.8.2)
+         */
+        if (ehci->usbcmd & USBCMD_IAAD) {
+            /* Remove all unseen qhs from the async qhs queue */
+            ehci_queues_rip_unused(ehci, async, 1);
+            DPRINTF("ASYNC: doorbell request acknowledged\n");
+            ehci->usbcmd &= ~USBCMD_IAAD;
+            ehci_set_interrupt(ehci, USBSTS_IAA);
+        }
+        break;
+
+    default:
+        /* this should only be due to a developer mistake */
+        fprintf(stderr, "ehci: Bad asynchronous state %d. "
+                "Resetting to active\n", ehci->astate);
+        assert(0);
+    }
+}
+
+static void ehci_advance_periodic_state(EHCIState *ehci)
+{
+    uint32_t entry;
+    uint32_t list;
+    const int async = 0;
+
+    // 4.6
+
+    switch(ehci_get_state(ehci, async)) {
+    case EST_INACTIVE:
+        if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
+            ehci_set_usbsts(ehci, USBSTS_PSS);
+            ehci_set_state(ehci, async, EST_ACTIVE);
+            // No break, fall through to ACTIVE
+        } else
+            break;
+
+    case EST_ACTIVE:
+        if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
+            ehci_queues_rip_all(ehci, async);
+            ehci_clear_usbsts(ehci, USBSTS_PSS);
+            ehci_set_state(ehci, async, EST_INACTIVE);
+            break;
+        }
+
+        list = ehci->periodiclistbase & 0xfffff000;
+        /* check that register has been set */
+        if (list == 0) {
+            break;
+        }
+        list |= ((ehci->frindex & 0x1ff8) >> 1);
+
+        pci_dma_read(&ehci->dev, list, &entry, sizeof entry);
+        entry = le32_to_cpu(entry);
+
+        DPRINTF("PERIODIC state adv fr=%d.  [%08X] -> %08X\n",
+                ehci->frindex / 8, list, entry);
+        ehci_set_fetch_addr(ehci, async,entry);
+        ehci_set_state(ehci, async, EST_FETCHENTRY);
+        ehci_advance_state(ehci, async);
+        ehci_queues_rip_unused(ehci, async, 0);
+        break;
+
+    default:
+        /* this should only be due to a developer mistake */
+        fprintf(stderr, "ehci: Bad periodic state %d. "
+                "Resetting to active\n", ehci->pstate);
+        assert(0);
+    }
+}
+
+static void ehci_frame_timer(void *opaque)
+{
+    EHCIState *ehci = opaque;
+    int64_t expire_time, t_now;
+    uint64_t ns_elapsed;
+    int frames;
+    int i;
+    int skipped_frames = 0;
+
+    t_now = qemu_get_clock_ns(vm_clock);
+    expire_time = t_now + (get_ticks_per_sec() / ehci->freq);
+
+    ns_elapsed = t_now - ehci->last_run_ns;
+    frames = ns_elapsed / FRAME_TIMER_NS;
+
+    for (i = 0; i < frames; i++) {
+        if ( !(ehci->usbsts & USBSTS_HALT)) {
+            if (ehci->isoch_pause <= 0) {
+                ehci->frindex += 8;
+            }
+
+            if (ehci->frindex > 0x00001fff) {
+                ehci->frindex = 0;
+                ehci_set_interrupt(ehci, USBSTS_FLR);
+            }
+
+            ehci->sofv = (ehci->frindex - 1) >> 3;
+            ehci->sofv &= 0x000003ff;
+        }
+
+        if (frames - i > ehci->maxframes) {
+            skipped_frames++;
+        } else {
+            ehci_advance_periodic_state(ehci);
+        }
+
+        ehci->last_run_ns += FRAME_TIMER_NS;
+    }
+
+#if 0
+    if (skipped_frames) {
+        DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
+    }
+#endif
+
+    /*  Async is not inside loop since it executes everything it can once
+     *  called
+     */
+    ehci_advance_async_state(ehci);
+
+    qemu_mod_timer(ehci->frame_timer, expire_time);
+}
+
+
+static const MemoryRegionOps ehci_mem_ops = {
+    .old_mmio = {
+        .read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl },
+        .write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel },
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int usb_ehci_initfn(PCIDevice *dev);
+
+static USBPortOps ehci_port_ops = {
+    .attach = ehci_attach,
+    .detach = ehci_detach,
+    .child_detach = ehci_child_detach,
+    .wakeup = ehci_wakeup,
+    .complete = ehci_async_complete_packet,
+};
+
+static USBBusOps ehci_bus_ops = {
+    .register_companion = ehci_register_companion,
+};
+
+static const VMStateDescription vmstate_ehci = {
+    .name = "ehci",
+    .unmigratable = 1,
+};
+
+static Property ehci_properties[] = {
+    DEFINE_PROP_UINT32("freq",      EHCIState, freq, FRAME_TIMER_FREQ),
+    DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ehci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_ehci_initfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */
+    k->revision = 0x10;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_ehci;
+    dc->props = ehci_properties;
+}
+
+static TypeInfo ehci_info = {
+    .name          = "usb-ehci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(EHCIState),
+    .class_init    = ehci_class_init,
+};
+
+static void ich9_ehci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_ehci_initfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1;
+    k->revision = 0x03;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_ehci;
+    dc->props = ehci_properties;
+}
+
+static TypeInfo ich9_ehci_info = {
+    .name          = "ich9-usb-ehci1",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(EHCIState),
+    .class_init    = ich9_ehci_class_init,
+};
+
+static int usb_ehci_initfn(PCIDevice *dev)
+{
+    EHCIState *s = DO_UPCAST(EHCIState, dev, dev);
+    uint8_t *pci_conf = s->dev.config;
+    int i;
+
+    pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
+
+    /* capabilities pointer */
+    pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
+    //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
+
+    pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */
+    pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
+    pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
+
+    // pci_conf[0x50] = 0x01; // power management caps
+
+    pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); // release number (2.1.4)
+    pci_set_byte(&pci_conf[0x61], 0x20);  // frame length adjustment (2.1.5)
+    pci_set_word(&pci_conf[0x62], 0x00);  // port wake up capability (2.1.6)
+
+    pci_conf[0x64] = 0x00;
+    pci_conf[0x65] = 0x00;
+    pci_conf[0x66] = 0x00;
+    pci_conf[0x67] = 0x00;
+    pci_conf[0x68] = 0x01;
+    pci_conf[0x69] = 0x00;
+    pci_conf[0x6a] = 0x00;
+    pci_conf[0x6b] = 0x00;  // USBLEGSUP
+    pci_conf[0x6c] = 0x00;
+    pci_conf[0x6d] = 0x00;
+    pci_conf[0x6e] = 0x00;
+    pci_conf[0x6f] = 0xc0;  // USBLEFCTLSTS
+
+    // 2.2 host controller interface version
+    s->mmio[0x00] = (uint8_t) OPREGBASE;
+    s->mmio[0x01] = 0x00;
+    s->mmio[0x02] = 0x00;
+    s->mmio[0x03] = 0x01;        // HC version
+    s->mmio[0x04] = NB_PORTS;    // Number of downstream ports
+    s->mmio[0x05] = 0x00;        // No companion ports at present
+    s->mmio[0x06] = 0x00;
+    s->mmio[0x07] = 0x00;
+    s->mmio[0x08] = 0x80;        // We can cache whole frame, not 64-bit capable
+    s->mmio[0x09] = 0x68;        // EECP
+    s->mmio[0x0a] = 0x00;
+    s->mmio[0x0b] = 0x00;
+
+    s->irq = s->dev.irq[3];
+
+    usb_bus_new(&s->bus, &ehci_bus_ops, &s->dev.qdev);
+    for(i = 0; i < NB_PORTS; i++) {
+        usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
+                          USB_SPEED_MASK_HIGH);
+        s->ports[i].dev = 0;
+    }
+
+    s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
+    QTAILQ_INIT(&s->aqueues);
+    QTAILQ_INIT(&s->pqueues);
+
+    qemu_register_reset(ehci_reset, s);
+
+    memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
+
+    return 0;
+}
+
+static void ehci_register_types(void)
+{
+    type_register_static(&ehci_info);
+    type_register_static(&ich9_ehci_info);
+}
+
+type_init(ehci_register_types)
+
+/*
+ * vim: expandtab ts=4
+ */
diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c
new file mode 100644 (file)
index 0000000..fa9385e
--- /dev/null
@@ -0,0 +1,1544 @@
+/*
+ * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics,
+ * USB2.0 OTG compliant core used in various chips.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Only host-mode and non-DMA accesses are currently supported.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "hw/usb.h"
+#include "hw/irq.h"
+#include "hw/hw.h"
+
+/* Common USB registers */
+#define MUSB_HDRC_FADDR                0x00    /* 8-bit */
+#define MUSB_HDRC_POWER                0x01    /* 8-bit */
+
+#define MUSB_HDRC_INTRTX       0x02    /* 16-bit */
+#define MUSB_HDRC_INTRRX       0x04
+#define MUSB_HDRC_INTRTXE      0x06  
+#define MUSB_HDRC_INTRRXE      0x08  
+#define MUSB_HDRC_INTRUSB      0x0a    /* 8 bit */
+#define MUSB_HDRC_INTRUSBE     0x0b    /* 8 bit */
+#define MUSB_HDRC_FRAME                0x0c    /* 16-bit */
+#define MUSB_HDRC_INDEX                0x0e    /* 8 bit */
+#define MUSB_HDRC_TESTMODE     0x0f    /* 8 bit */
+
+/* Per-EP registers in indexed mode */
+#define MUSB_HDRC_EP_IDX       0x10    /* 8-bit */
+
+/* EP FIFOs */
+#define MUSB_HDRC_FIFO         0x20
+
+/* Additional Control Registers */
+#define        MUSB_HDRC_DEVCTL        0x60    /* 8 bit */
+
+/* These are indexed */
+#define MUSB_HDRC_TXFIFOSZ     0x62    /* 8 bit (see masks) */
+#define MUSB_HDRC_RXFIFOSZ     0x63    /* 8 bit (see masks) */
+#define MUSB_HDRC_TXFIFOADDR   0x64    /* 16 bit offset shifted right 3 */
+#define MUSB_HDRC_RXFIFOADDR   0x66    /* 16 bit offset shifted right 3 */
+
+/* Some more registers */
+#define MUSB_HDRC_VCTRL                0x68    /* 8 bit */
+#define MUSB_HDRC_HWVERS       0x6c    /* 8 bit */
+
+/* Added in HDRC 1.9(?) & MHDRC 1.4 */
+/* ULPI pass-through */
+#define MUSB_HDRC_ULPI_VBUSCTL 0x70
+#define MUSB_HDRC_ULPI_REGDATA 0x74
+#define MUSB_HDRC_ULPI_REGADDR 0x75
+#define MUSB_HDRC_ULPI_REGCTL  0x76
+
+/* Extended config & PHY control */
+#define MUSB_HDRC_ENDCOUNT     0x78    /* 8 bit */
+#define MUSB_HDRC_DMARAMCFG    0x79    /* 8 bit */
+#define MUSB_HDRC_PHYWAIT      0x7a    /* 8 bit */
+#define MUSB_HDRC_PHYVPLEN     0x7b    /* 8 bit */
+#define MUSB_HDRC_HS_EOF1      0x7c    /* 8 bit, units of 546.1 us */
+#define MUSB_HDRC_FS_EOF1      0x7d    /* 8 bit, units of 533.3 ns */
+#define MUSB_HDRC_LS_EOF1      0x7e    /* 8 bit, units of 1.067 us */
+
+/* Per-EP BUSCTL registers */
+#define MUSB_HDRC_BUSCTL       0x80
+
+/* Per-EP registers in flat mode */
+#define MUSB_HDRC_EP           0x100
+
+/* offsets to registers in flat model */
+#define MUSB_HDRC_TXMAXP       0x00    /* 16 bit apparently */
+#define MUSB_HDRC_TXCSR                0x02    /* 16 bit apparently */
+#define MUSB_HDRC_CSR0         MUSB_HDRC_TXCSR         /* re-used for EP0 */
+#define MUSB_HDRC_RXMAXP       0x04    /* 16 bit apparently */
+#define MUSB_HDRC_RXCSR                0x06    /* 16 bit apparently */
+#define MUSB_HDRC_RXCOUNT      0x08    /* 16 bit apparently */
+#define MUSB_HDRC_COUNT0       MUSB_HDRC_RXCOUNT       /* re-used for EP0 */
+#define MUSB_HDRC_TXTYPE       0x0a    /* 8 bit apparently */
+#define MUSB_HDRC_TYPE0                MUSB_HDRC_TXTYPE        /* re-used for EP0 */
+#define MUSB_HDRC_TXINTERVAL   0x0b    /* 8 bit apparently */
+#define MUSB_HDRC_NAKLIMIT0    MUSB_HDRC_TXINTERVAL    /* re-used for EP0 */
+#define MUSB_HDRC_RXTYPE       0x0c    /* 8 bit apparently */
+#define MUSB_HDRC_RXINTERVAL   0x0d    /* 8 bit apparently */
+#define MUSB_HDRC_FIFOSIZE     0x0f    /* 8 bit apparently */
+#define MUSB_HDRC_CONFIGDATA   MGC_O_HDRC_FIFOSIZE     /* re-used for EP0 */
+
+/* "Bus control" registers */
+#define MUSB_HDRC_TXFUNCADDR   0x00
+#define MUSB_HDRC_TXHUBADDR    0x02
+#define MUSB_HDRC_TXHUBPORT    0x03
+
+#define MUSB_HDRC_RXFUNCADDR   0x04
+#define MUSB_HDRC_RXHUBADDR    0x06
+#define MUSB_HDRC_RXHUBPORT    0x07
+
+/*
+ * MUSBHDRC Register bit masks
+ */
+
+/* POWER */
+#define MGC_M_POWER_ISOUPDATE          0x80 
+#define        MGC_M_POWER_SOFTCONN            0x40
+#define        MGC_M_POWER_HSENAB              0x20
+#define        MGC_M_POWER_HSMODE              0x10
+#define MGC_M_POWER_RESET              0x08
+#define MGC_M_POWER_RESUME             0x04
+#define MGC_M_POWER_SUSPENDM           0x02
+#define MGC_M_POWER_ENSUSPEND          0x01
+
+/* INTRUSB */
+#define MGC_M_INTR_SUSPEND             0x01
+#define MGC_M_INTR_RESUME              0x02
+#define MGC_M_INTR_RESET               0x04
+#define MGC_M_INTR_BABBLE              0x04
+#define MGC_M_INTR_SOF                 0x08 
+#define MGC_M_INTR_CONNECT             0x10
+#define MGC_M_INTR_DISCONNECT          0x20
+#define MGC_M_INTR_SESSREQ             0x40
+#define MGC_M_INTR_VBUSERROR           0x80    /* FOR SESSION END */
+#define MGC_M_INTR_EP0                 0x01    /* FOR EP0 INTERRUPT */
+
+/* DEVCTL */
+#define MGC_M_DEVCTL_BDEVICE           0x80   
+#define MGC_M_DEVCTL_FSDEV             0x40
+#define MGC_M_DEVCTL_LSDEV             0x20
+#define MGC_M_DEVCTL_VBUS              0x18
+#define MGC_S_DEVCTL_VBUS              3
+#define MGC_M_DEVCTL_HM                        0x04
+#define MGC_M_DEVCTL_HR                        0x02
+#define MGC_M_DEVCTL_SESSION           0x01
+
+/* TESTMODE */
+#define MGC_M_TEST_FORCE_HOST          0x80
+#define MGC_M_TEST_FIFO_ACCESS         0x40
+#define MGC_M_TEST_FORCE_FS            0x20
+#define MGC_M_TEST_FORCE_HS            0x10
+#define MGC_M_TEST_PACKET              0x08
+#define MGC_M_TEST_K                   0x04
+#define MGC_M_TEST_J                   0x02
+#define MGC_M_TEST_SE0_NAK             0x01
+
+/* CSR0 */
+#define        MGC_M_CSR0_FLUSHFIFO            0x0100
+#define MGC_M_CSR0_TXPKTRDY            0x0002
+#define MGC_M_CSR0_RXPKTRDY            0x0001
+
+/* CSR0 in Peripheral mode */
+#define MGC_M_CSR0_P_SVDSETUPEND       0x0080
+#define MGC_M_CSR0_P_SVDRXPKTRDY       0x0040
+#define MGC_M_CSR0_P_SENDSTALL         0x0020
+#define MGC_M_CSR0_P_SETUPEND          0x0010
+#define MGC_M_CSR0_P_DATAEND           0x0008
+#define MGC_M_CSR0_P_SENTSTALL         0x0004
+
+/* CSR0 in Host mode */
+#define MGC_M_CSR0_H_NO_PING           0x0800
+#define MGC_M_CSR0_H_WR_DATATOGGLE     0x0400  /* set to allow setting: */
+#define MGC_M_CSR0_H_DATATOGGLE                0x0200  /* data toggle control */
+#define        MGC_M_CSR0_H_NAKTIMEOUT         0x0080
+#define MGC_M_CSR0_H_STATUSPKT         0x0040
+#define MGC_M_CSR0_H_REQPKT            0x0020
+#define MGC_M_CSR0_H_ERROR             0x0010
+#define MGC_M_CSR0_H_SETUPPKT          0x0008
+#define MGC_M_CSR0_H_RXSTALL           0x0004
+
+/* CONFIGDATA */
+#define MGC_M_CONFIGDATA_MPRXE         0x80    /* auto bulk pkt combining */
+#define MGC_M_CONFIGDATA_MPTXE         0x40    /* auto bulk pkt splitting */
+#define MGC_M_CONFIGDATA_BIGENDIAN     0x20
+#define MGC_M_CONFIGDATA_HBRXE         0x10    /* HB-ISO for RX */
+#define MGC_M_CONFIGDATA_HBTXE         0x08    /* HB-ISO for TX */
+#define MGC_M_CONFIGDATA_DYNFIFO       0x04    /* dynamic FIFO sizing */
+#define MGC_M_CONFIGDATA_SOFTCONE      0x02    /* SoftConnect */
+#define MGC_M_CONFIGDATA_UTMIDW                0x01    /* Width, 0 => 8b, 1 => 16b */
+
+/* TXCSR in Peripheral and Host mode */
+#define MGC_M_TXCSR_AUTOSET            0x8000
+#define MGC_M_TXCSR_ISO                        0x4000
+#define MGC_M_TXCSR_MODE               0x2000
+#define MGC_M_TXCSR_DMAENAB            0x1000
+#define MGC_M_TXCSR_FRCDATATOG         0x0800
+#define MGC_M_TXCSR_DMAMODE            0x0400
+#define MGC_M_TXCSR_CLRDATATOG         0x0040
+#define MGC_M_TXCSR_FLUSHFIFO          0x0008
+#define MGC_M_TXCSR_FIFONOTEMPTY       0x0002
+#define MGC_M_TXCSR_TXPKTRDY           0x0001
+
+/* TXCSR in Peripheral mode */
+#define MGC_M_TXCSR_P_INCOMPTX         0x0080
+#define MGC_M_TXCSR_P_SENTSTALL                0x0020
+#define MGC_M_TXCSR_P_SENDSTALL                0x0010
+#define MGC_M_TXCSR_P_UNDERRUN         0x0004
+
+/* TXCSR in Host mode */
+#define MGC_M_TXCSR_H_WR_DATATOGGLE    0x0200
+#define MGC_M_TXCSR_H_DATATOGGLE       0x0100
+#define MGC_M_TXCSR_H_NAKTIMEOUT       0x0080
+#define MGC_M_TXCSR_H_RXSTALL          0x0020
+#define MGC_M_TXCSR_H_ERROR            0x0004
+
+/* RXCSR in Peripheral and Host mode */
+#define MGC_M_RXCSR_AUTOCLEAR          0x8000
+#define MGC_M_RXCSR_DMAENAB            0x2000
+#define MGC_M_RXCSR_DISNYET            0x1000
+#define MGC_M_RXCSR_DMAMODE            0x0800
+#define MGC_M_RXCSR_INCOMPRX           0x0100
+#define MGC_M_RXCSR_CLRDATATOG         0x0080
+#define MGC_M_RXCSR_FLUSHFIFO          0x0010
+#define MGC_M_RXCSR_DATAERROR          0x0008
+#define MGC_M_RXCSR_FIFOFULL           0x0002
+#define MGC_M_RXCSR_RXPKTRDY           0x0001
+
+/* RXCSR in Peripheral mode */
+#define MGC_M_RXCSR_P_ISO              0x4000
+#define MGC_M_RXCSR_P_SENTSTALL                0x0040
+#define MGC_M_RXCSR_P_SENDSTALL                0x0020
+#define MGC_M_RXCSR_P_OVERRUN          0x0004
+
+/* RXCSR in Host mode */
+#define MGC_M_RXCSR_H_AUTOREQ          0x4000
+#define MGC_M_RXCSR_H_WR_DATATOGGLE    0x0400
+#define MGC_M_RXCSR_H_DATATOGGLE       0x0200
+#define MGC_M_RXCSR_H_RXSTALL          0x0040
+#define MGC_M_RXCSR_H_REQPKT           0x0020
+#define MGC_M_RXCSR_H_ERROR            0x0004
+
+/* HUBADDR */
+#define MGC_M_HUBADDR_MULTI_TT         0x80
+
+/* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */
+#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02
+#define MGC_M_ULPI_VBCTL_USEEXTVBUS    0x01
+#define MGC_M_ULPI_REGCTL_INT_ENABLE   0x08
+#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04
+#define MGC_M_ULPI_REGCTL_COMPLETE     0x02
+#define MGC_M_ULPI_REGCTL_REG          0x01
+
+/* #define MUSB_DEBUG */
+
+#ifdef MUSB_DEBUG
+#define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \
+                               __LINE__, ##__VA_ARGS__)
+#else
+#define TRACE(...)
+#endif
+
+
+static void musb_attach(USBPort *port);
+static void musb_detach(USBPort *port);
+static void musb_child_detach(USBPort *port, USBDevice *child);
+static void musb_schedule_cb(USBPort *port, USBPacket *p);
+static void musb_async_cancel_device(MUSBState *s, USBDevice *dev);
+
+static USBPortOps musb_port_ops = {
+    .attach = musb_attach,
+    .detach = musb_detach,
+    .child_detach = musb_child_detach,
+    .complete = musb_schedule_cb,
+};
+
+static USBBusOps musb_bus_ops = {
+};
+
+typedef struct MUSBPacket MUSBPacket;
+typedef struct MUSBEndPoint MUSBEndPoint;
+
+struct MUSBPacket {
+    USBPacket p;
+    MUSBEndPoint *ep;
+    int dir;
+};
+
+struct MUSBEndPoint {
+    uint16_t faddr[2];
+    uint8_t haddr[2];
+    uint8_t hport[2];
+    uint16_t csr[2];
+    uint16_t maxp[2];
+    uint16_t rxcount;
+    uint8_t type[2];
+    uint8_t interval[2];
+    uint8_t config;
+    uint8_t fifosize;
+    int timeout[2];    /* Always in microframes */
+
+    uint8_t *buf[2];
+    int fifolen[2];
+    int fifostart[2];
+    int fifoaddr[2];
+    MUSBPacket packey[2];
+    int status[2];
+    int ext_size[2];
+
+    /* For callbacks' use */
+    int epnum;
+    int interrupt[2];
+    MUSBState *musb;
+    USBCallback *delayed_cb[2];
+    QEMUTimer *intv_timer[2];
+};
+
+struct MUSBState {
+    qemu_irq irqs[musb_irq_max];
+    USBBus bus;
+    USBPort port;
+
+    int idx;
+    uint8_t devctl;
+    uint8_t power;
+    uint8_t faddr;
+
+    uint8_t intr;
+    uint8_t mask;
+    uint16_t tx_intr;
+    uint16_t tx_mask;
+    uint16_t rx_intr;
+    uint16_t rx_mask;
+
+    int setup_len;
+    int session;
+
+    uint8_t buf[0x8000];
+
+        /* Duplicating the world since 2008!...  probably we should have 32
+         * logical, single endpoints instead.  */
+    MUSBEndPoint ep[16];
+};
+
+void musb_reset(MUSBState *s)
+{
+    int i;
+
+    s->faddr = 0x00;
+    s->devctl = 0;
+    s->power = MGC_M_POWER_HSENAB;
+    s->tx_intr = 0x0000;
+    s->rx_intr = 0x0000;
+    s->tx_mask = 0xffff;
+    s->rx_mask = 0xffff;
+    s->intr = 0x00;
+    s->mask = 0x06;
+    s->idx = 0;
+
+    s->setup_len = 0;
+    s->session = 0;
+    memset(s->buf, 0, sizeof(s->buf));
+
+    /* TODO: _DW */
+    s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO;
+    for (i = 0; i < 16; i ++) {
+        s->ep[i].fifosize = 64;
+        s->ep[i].maxp[0] = 0x40;
+        s->ep[i].maxp[1] = 0x40;
+        s->ep[i].musb = s;
+        s->ep[i].epnum = i;
+        usb_packet_init(&s->ep[i].packey[0].p);
+        usb_packet_init(&s->ep[i].packey[1].p);
+    }
+}
+
+struct MUSBState *musb_init(DeviceState *parent_device, int gpio_base)
+{
+    MUSBState *s = g_malloc0(sizeof(*s));
+    int i;
+
+    for (i = 0; i < musb_irq_max; i++) {
+        s->irqs[i] = qdev_get_gpio_in(parent_device, gpio_base + i);
+    }
+
+    musb_reset(s);
+
+    usb_bus_new(&s->bus, &musb_bus_ops, parent_device);
+    usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
+                      USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+
+    return s;
+}
+
+static void musb_vbus_set(MUSBState *s, int level)
+{
+    if (level)
+        s->devctl |= 3 << MGC_S_DEVCTL_VBUS;
+    else
+        s->devctl &= ~MGC_M_DEVCTL_VBUS;
+
+    qemu_set_irq(s->irqs[musb_set_vbus], level);
+}
+
+static void musb_intr_set(MUSBState *s, int line, int level)
+{
+    if (!level) {
+        s->intr &= ~(1 << line);
+        qemu_irq_lower(s->irqs[line]);
+    } else if (s->mask & (1 << line)) {
+        s->intr |= 1 << line;
+        qemu_irq_raise(s->irqs[line]);
+    }
+}
+
+static void musb_tx_intr_set(MUSBState *s, int line, int level)
+{
+    if (!level) {
+        s->tx_intr &= ~(1 << line);
+        if (!s->tx_intr)
+            qemu_irq_lower(s->irqs[musb_irq_tx]);
+    } else if (s->tx_mask & (1 << line)) {
+        s->tx_intr |= 1 << line;
+        qemu_irq_raise(s->irqs[musb_irq_tx]);
+    }
+}
+
+static void musb_rx_intr_set(MUSBState *s, int line, int level)
+{
+    if (line) {
+        if (!level) {
+            s->rx_intr &= ~(1 << line);
+            if (!s->rx_intr)
+                qemu_irq_lower(s->irqs[musb_irq_rx]);
+        } else if (s->rx_mask & (1 << line)) {
+            s->rx_intr |= 1 << line;
+            qemu_irq_raise(s->irqs[musb_irq_rx]);
+        }
+    } else
+        musb_tx_intr_set(s, line, level);
+}
+
+uint32_t musb_core_intr_get(MUSBState *s)
+{
+    return (s->rx_intr << 15) | s->tx_intr;
+}
+
+void musb_core_intr_clear(MUSBState *s, uint32_t mask)
+{
+    if (s->rx_intr) {
+        s->rx_intr &= mask >> 15;
+        if (!s->rx_intr)
+            qemu_irq_lower(s->irqs[musb_irq_rx]);
+    }
+
+    if (s->tx_intr) {
+        s->tx_intr &= mask & 0xffff;
+        if (!s->tx_intr)
+            qemu_irq_lower(s->irqs[musb_irq_tx]);
+    }
+}
+
+void musb_set_size(MUSBState *s, int epnum, int size, int is_tx)
+{
+    s->ep[epnum].ext_size[!is_tx] = size;
+    s->ep[epnum].fifostart[0] = 0;
+    s->ep[epnum].fifostart[1] = 0;
+    s->ep[epnum].fifolen[0] = 0;
+    s->ep[epnum].fifolen[1] = 0;
+}
+
+static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess)
+{
+    int detect_prev = prev_dev && prev_sess;
+    int detect = !!s->port.dev && s->session;
+
+    if (detect && !detect_prev) {
+        /* Let's skip the ID pin sense and VBUS sense formalities and
+         * and signal a successful SRP directly.  This should work at least
+         * for the Linux driver stack.  */
+        musb_intr_set(s, musb_irq_connect, 1);
+
+        if (s->port.dev->speed == USB_SPEED_LOW) {
+            s->devctl &= ~MGC_M_DEVCTL_FSDEV;
+            s->devctl |= MGC_M_DEVCTL_LSDEV;
+        } else {
+            s->devctl |= MGC_M_DEVCTL_FSDEV;
+            s->devctl &= ~MGC_M_DEVCTL_LSDEV;
+        }
+
+        /* A-mode?  */
+        s->devctl &= ~MGC_M_DEVCTL_BDEVICE;
+
+        /* Host-mode bit?  */
+        s->devctl |= MGC_M_DEVCTL_HM;
+#if 1
+        musb_vbus_set(s, 1);
+#endif
+    } else if (!detect && detect_prev) {
+#if 1
+        musb_vbus_set(s, 0);
+#endif
+    }
+}
+
+/* Attach or detach a device on our only port.  */
+static void musb_attach(USBPort *port)
+{
+    MUSBState *s = (MUSBState *) port->opaque;
+
+    musb_intr_set(s, musb_irq_vbus_request, 1);
+    musb_session_update(s, 0, s->session);
+}
+
+static void musb_detach(USBPort *port)
+{
+    MUSBState *s = (MUSBState *) port->opaque;
+
+    musb_async_cancel_device(s, port->dev);
+
+    musb_intr_set(s, musb_irq_disconnect, 1);
+    musb_session_update(s, 1, s->session);
+}
+
+static void musb_child_detach(USBPort *port, USBDevice *child)
+{
+    MUSBState *s = (MUSBState *) port->opaque;
+
+    musb_async_cancel_device(s, child);
+}
+
+static void musb_cb_tick0(void *opaque)
+{
+    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+
+    ep->delayed_cb[0](&ep->packey[0].p, opaque);
+}
+
+static void musb_cb_tick1(void *opaque)
+{
+    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+
+    ep->delayed_cb[1](&ep->packey[1].p, opaque);
+}
+
+#define musb_cb_tick   (dir ? musb_cb_tick1 : musb_cb_tick0)
+
+static void musb_schedule_cb(USBPort *port, USBPacket *packey)
+{
+    MUSBPacket *p = container_of(packey, MUSBPacket, p);
+    MUSBEndPoint *ep = p->ep;
+    int dir = p->dir;
+    int timeout = 0;
+
+    if (ep->status[dir] == USB_RET_NAK)
+        timeout = ep->timeout[dir];
+    else if (ep->interrupt[dir])
+        timeout = 8;
+    else
+        return musb_cb_tick(ep);
+
+    if (!ep->intv_timer[dir])
+        ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, ep);
+
+    qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock_ns(vm_clock) +
+                   muldiv64(timeout, get_ticks_per_sec(), 8000));
+}
+
+static int musb_timeout(int ttype, int speed, int val)
+{
+#if 1
+    return val << 3;
+#endif
+
+    switch (ttype) {
+    case USB_ENDPOINT_XFER_CONTROL:
+        if (val < 2)
+            return 0;
+        else if (speed == USB_SPEED_HIGH)
+            return 1 << (val - 1);
+        else
+            return 8 << (val - 1);
+
+    case USB_ENDPOINT_XFER_INT:
+        if (speed == USB_SPEED_HIGH)
+            if (val < 2)
+                return 0;
+            else
+                return 1 << (val - 1);
+        else
+            return val << 3;
+
+    case USB_ENDPOINT_XFER_BULK:
+    case USB_ENDPOINT_XFER_ISOC:
+        if (val < 2)
+            return 0;
+        else if (speed == USB_SPEED_HIGH)
+            return 1 << (val - 1);
+        else
+            return 8 << (val - 1);
+        /* TODO: what with low-speed Bulk and Isochronous?  */
+    }
+
+    hw_error("bad interval\n");
+}
+
+static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
+                int epnum, int pid, int len, USBCallback cb, int dir)
+{
+    USBDevice *dev;
+    USBEndpoint *uep;
+    int ret;
+    int idx = epnum && dir;
+    int ttype;
+
+    /* ep->type[0,1] contains:
+     * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow)
+     * in bits 5:4 the transfer type (BULK / INT)
+     * in bits 3:0 the EP num
+     */
+    ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0;
+
+    ep->timeout[dir] = musb_timeout(ttype,
+                    ep->type[idx] >> 6, ep->interval[idx]);
+    ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT;
+    ep->delayed_cb[dir] = cb;
+
+    /* A wild guess on the FADDR semantics... */
+    dev = usb_find_device(&s->port, ep->faddr[idx]);
+    uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
+    usb_packet_setup(&ep->packey[dir].p, pid, uep);
+    usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
+    ep->packey[dir].ep = ep;
+    ep->packey[dir].dir = dir;
+
+    ret = usb_handle_packet(dev, &ep->packey[dir].p);
+
+    if (ret == USB_RET_ASYNC) {
+        ep->status[dir] = len;
+        return;
+    }
+
+    ep->status[dir] = ret;
+    musb_schedule_cb(&s->port, &ep->packey[dir].p);
+}
+
+static void musb_tx_packet_complete(USBPacket *packey, void *opaque)
+{
+    /* Unfortunately we can't use packey->devep because that's the remote
+     * endpoint number and may be different than our local.  */
+    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+    int epnum = ep->epnum;
+    MUSBState *s = ep->musb;
+
+    ep->fifostart[0] = 0;
+    ep->fifolen[0] = 0;
+#ifdef CLEAR_NAK
+    if (ep->status[0] != USB_RET_NAK) {
+#endif
+        if (epnum)
+            ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY);
+        else
+            ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY;
+#ifdef CLEAR_NAK
+    }
+#endif
+
+    /* Clear all of the error bits first */
+    if (epnum)
+        ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL |
+                        MGC_M_TXCSR_H_NAKTIMEOUT);
+    else
+        ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
+                        MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
+
+    if (ep->status[0] == USB_RET_STALL) {
+        /* Command not supported by target! */
+        ep->status[0] = 0;
+
+        if (epnum)
+            ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL;
+        else
+            ep->csr[0] |= MGC_M_CSR0_H_RXSTALL;
+    }
+
+    if (ep->status[0] == USB_RET_NAK) {
+        ep->status[0] = 0;
+
+        /* NAK timeouts are only generated in Bulk transfers and
+         * Data-errors in Isochronous.  */
+        if (ep->interrupt[0]) {
+            return;
+        }
+
+        if (epnum)
+            ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT;
+        else
+            ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT;
+    }
+
+    if (ep->status[0] < 0) {
+        if (ep->status[0] == USB_RET_BABBLE)
+            musb_intr_set(s, musb_irq_rst_babble, 1);
+
+        /* Pretend we've tried three times already and failed (in
+         * case of USB_TOKEN_SETUP).  */
+        if (epnum)
+            ep->csr[0] |= MGC_M_TXCSR_H_ERROR;
+        else
+            ep->csr[0] |= MGC_M_CSR0_H_ERROR;
+
+        musb_tx_intr_set(s, epnum, 1);
+        return;
+    }
+    /* TODO: check len for over/underruns of an OUT packet?  */
+
+#ifdef SETUPLEN_HACK
+    if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP)
+        s->setup_len = ep->packey[0].data[6];
+#endif
+
+    /* In DMA mode: if no error, assert DMA request for this EP,
+     * and skip the interrupt.  */
+    musb_tx_intr_set(s, epnum, 1);
+}
+
+static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
+{
+    /* Unfortunately we can't use packey->devep because that's the remote
+     * endpoint number and may be different than our local.  */
+    MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+    int epnum = ep->epnum;
+    MUSBState *s = ep->musb;
+
+    ep->fifostart[1] = 0;
+    ep->fifolen[1] = 0;
+
+#ifdef CLEAR_NAK
+    if (ep->status[1] != USB_RET_NAK) {
+#endif
+        ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
+        if (!epnum)
+            ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT;
+#ifdef CLEAR_NAK
+    }
+#endif
+
+    /* Clear all of the imaginable error bits first */
+    ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL |
+                    MGC_M_RXCSR_DATAERROR);
+    if (!epnum)
+        ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
+                        MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
+
+    if (ep->status[1] == USB_RET_STALL) {
+        ep->status[1] = 0;
+        packey->result = 0;
+
+        ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
+        if (!epnum)
+            ep->csr[0] |= MGC_M_CSR0_H_RXSTALL;
+    }
+
+    if (ep->status[1] == USB_RET_NAK) {
+        ep->status[1] = 0;
+
+        /* NAK timeouts are only generated in Bulk transfers and
+         * Data-errors in Isochronous.  */
+        if (ep->interrupt[1])
+            return musb_packet(s, ep, epnum, USB_TOKEN_IN,
+                            packey->iov.size, musb_rx_packet_complete, 1);
+
+        ep->csr[1] |= MGC_M_RXCSR_DATAERROR;
+        if (!epnum)
+            ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT;
+    }
+
+    if (ep->status[1] < 0) {
+        if (ep->status[1] == USB_RET_BABBLE) {
+            musb_intr_set(s, musb_irq_rst_babble, 1);
+            return;
+        }
+
+        /* Pretend we've tried three times already and failed (in
+         * case of a control transfer).  */
+        ep->csr[1] |= MGC_M_RXCSR_H_ERROR;
+        if (!epnum)
+            ep->csr[0] |= MGC_M_CSR0_H_ERROR;
+
+        musb_rx_intr_set(s, epnum, 1);
+        return;
+    }
+    /* TODO: check len for over/underruns of an OUT packet?  */
+    /* TODO: perhaps make use of e->ext_size[1] here.  */
+
+    packey->result = ep->status[1];
+
+    if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
+        ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
+        if (!epnum)
+            ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
+
+        ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */
+        /* In DMA mode: assert DMA request for this EP */
+    }
+
+    /* Only if DMA has not been asserted */
+    musb_rx_intr_set(s, epnum, 1);
+}
+
+static void musb_async_cancel_device(MUSBState *s, USBDevice *dev)
+{
+    int ep, dir;
+
+    for (ep = 0; ep < 16; ep++) {
+        for (dir = 0; dir < 2; dir++) {
+            if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) ||
+                s->ep[ep].packey[dir].p.ep->dev != dev) {
+                continue;
+            }
+            usb_cancel_packet(&s->ep[ep].packey[dir].p);
+            /* status updates needed here? */
+        }
+    }
+}
+
+static void musb_tx_rdy(MUSBState *s, int epnum)
+{
+    MUSBEndPoint *ep = s->ep + epnum;
+    int pid;
+    int total, valid = 0;
+    TRACE("start %d, len %d",  ep->fifostart[0], ep->fifolen[0] );
+    ep->fifostart[0] += ep->fifolen[0];
+    ep->fifolen[0] = 0;
+
+    /* XXX: how's the total size of the packet retrieved exactly in
+     * the generic case?  */
+    total = ep->maxp[0] & 0x3ff;
+
+    if (ep->ext_size[0]) {
+        total = ep->ext_size[0];
+        ep->ext_size[0] = 0;
+        valid = 1;
+    }
+
+    /* If the packet is not fully ready yet, wait for a next segment.  */
+    if (epnum && (ep->fifostart[0]) < total)
+        return;
+
+    if (!valid)
+        total = ep->fifostart[0];
+
+    pid = USB_TOKEN_OUT;
+    if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) {
+        pid = USB_TOKEN_SETUP;
+        if (total != 8) {
+            TRACE("illegal SETUPPKT length of %i bytes", total);
+        }
+        /* Controller should retry SETUP packets three times on errors
+         * but it doesn't make sense for us to do that.  */
+    }
+
+    return musb_packet(s, ep, epnum, pid,
+                    total, musb_tx_packet_complete, 0);
+}
+
+static void musb_rx_req(MUSBState *s, int epnum)
+{
+    MUSBEndPoint *ep = s->ep + epnum;
+    int total;
+
+    /* If we already have a packet, which didn't fit into the
+     * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */
+    if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
+                    (ep->fifostart[1]) + ep->rxcount <
+                    ep->packey[1].p.iov.size) {
+        TRACE("0x%08x, %d",  ep->fifostart[1], ep->rxcount );
+        ep->fifostart[1] += ep->rxcount;
+        ep->fifolen[1] = 0;
+
+        ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]),
+                        ep->maxp[1]);
+
+        ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
+        if (!epnum)
+            ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT;
+
+        /* Clear all of the error bits first */
+        ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL |
+                        MGC_M_RXCSR_DATAERROR);
+        if (!epnum)
+            ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
+                            MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
+
+        ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
+        if (!epnum)
+            ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
+        musb_rx_intr_set(s, epnum, 1);
+        return;
+    }
+
+    /* The driver sets maxp[1] to 64 or less because it knows the hardware
+     * FIFO is this deep.  Bigger packets get split in
+     * usb_generic_handle_packet but we can also do the splitting locally
+     * for performance.  It turns out we can also have a bigger FIFO and
+     * ignore the limit set in ep->maxp[1].  The Linux MUSB driver deals
+     * OK with single packets of even 32KB and we avoid splitting, however
+     * usb_msd.c sometimes sends a packet bigger than what Linux expects
+     * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN.  Splitting
+     * hides this overrun from Linux.  Up to 4096 everything is fine
+     * though.  Currently this is disabled.
+     *
+     * XXX: mind ep->fifosize.  */
+    total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf));
+
+#ifdef SETUPLEN_HACK
+    /* Why should *we* do that instead of Linux?  */
+    if (!epnum) {
+        if (ep->packey[0].p.devaddr == 2) {
+            total = MIN(s->setup_len, 8);
+        } else {
+            total = MIN(s->setup_len, 64);
+        }
+        s->setup_len -= total;
+    }
+#endif
+
+    return musb_packet(s, ep, epnum, USB_TOKEN_IN,
+                    total, musb_rx_packet_complete, 1);
+}
+
+static uint8_t musb_read_fifo(MUSBEndPoint *ep)
+{
+    uint8_t value;
+    if (ep->fifolen[1] >= 64) {
+        /* We have a FIFO underrun */
+        TRACE("EP%d FIFO is now empty, stop reading", ep->epnum);
+        return 0x00000000;
+    }
+    /* In DMA mode clear RXPKTRDY and set REQPKT automatically
+     * (if AUTOREQ is set) */
+
+    ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL;
+    value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++];
+    TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] );
+    return value;
+}
+
+static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value)
+{
+    TRACE("EP%d = %02x", ep->epnum, value);
+    if (ep->fifolen[0] >= 64) {
+        /* We have a FIFO overrun */
+        TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum);
+        return;
+     }
+
+     ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value;
+     ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY;
+}
+
+static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir)
+{
+    if (ep->intv_timer[dir])
+        qemu_del_timer(ep->intv_timer[dir]);
+}
+
+/* Bus control */
+static uint8_t musb_busctl_readb(void *opaque, int ep, int addr)
+{
+    MUSBState *s = (MUSBState *) opaque;
+
+    switch (addr) {
+    /* For USB2.0 HS hubs only */
+    case MUSB_HDRC_TXHUBADDR:
+        return s->ep[ep].haddr[0];
+    case MUSB_HDRC_TXHUBPORT:
+        return s->ep[ep].hport[0];
+    case MUSB_HDRC_RXHUBADDR:
+        return s->ep[ep].haddr[1];
+    case MUSB_HDRC_RXHUBPORT:
+        return s->ep[ep].hport[1];
+
+    default:
+        TRACE("unknown register 0x%02x", addr);
+        return 0x00;
+    };
+}
+
+static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value)
+{
+    MUSBState *s = (MUSBState *) opaque;
+
+    switch (addr) {
+    case MUSB_HDRC_TXFUNCADDR:
+        s->ep[ep].faddr[0] = value;
+        break;
+    case MUSB_HDRC_RXFUNCADDR:
+        s->ep[ep].faddr[1] = value;
+        break;
+    case MUSB_HDRC_TXHUBADDR:
+        s->ep[ep].haddr[0] = value;
+        break;
+    case MUSB_HDRC_TXHUBPORT:
+        s->ep[ep].hport[0] = value;
+        break;
+    case MUSB_HDRC_RXHUBADDR:
+        s->ep[ep].haddr[1] = value;
+        break;
+    case MUSB_HDRC_RXHUBPORT:
+        s->ep[ep].hport[1] = value;
+        break;
+
+    default:
+        TRACE("unknown register 0x%02x", addr);
+        break;
+    };
+}
+
+static uint16_t musb_busctl_readh(void *opaque, int ep, int addr)
+{
+    MUSBState *s = (MUSBState *) opaque;
+
+    switch (addr) {
+    case MUSB_HDRC_TXFUNCADDR:
+        return s->ep[ep].faddr[0];
+    case MUSB_HDRC_RXFUNCADDR:
+        return s->ep[ep].faddr[1];
+
+    default:
+        return musb_busctl_readb(s, ep, addr) |
+                (musb_busctl_readb(s, ep, addr | 1) << 8);
+    };
+}
+
+static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value)
+{
+    MUSBState *s = (MUSBState *) opaque;
+
+    switch (addr) {
+    case MUSB_HDRC_TXFUNCADDR:
+        s->ep[ep].faddr[0] = value;
+        break;
+    case MUSB_HDRC_RXFUNCADDR:
+        s->ep[ep].faddr[1] = value;
+        break;
+
+    default:
+        musb_busctl_writeb(s, ep, addr, value & 0xff);
+        musb_busctl_writeb(s, ep, addr | 1, value >> 8);
+    };
+}
+
+/* Endpoint control */
+static uint8_t musb_ep_readb(void *opaque, int ep, int addr)
+{
+    MUSBState *s = (MUSBState *) opaque;
+
+    switch (addr) {
+    case MUSB_HDRC_TXTYPE:
+        return s->ep[ep].type[0];
+    case MUSB_HDRC_TXINTERVAL:
+        return s->ep[ep].interval[0];
+    case MUSB_HDRC_RXTYPE:
+        return s->ep[ep].type[1];
+    case MUSB_HDRC_RXINTERVAL:
+        return s->ep[ep].interval[1];
+    case (MUSB_HDRC_FIFOSIZE & ~1):
+        return 0x00;
+    case MUSB_HDRC_FIFOSIZE:
+        return ep ? s->ep[ep].fifosize : s->ep[ep].config;
+    case MUSB_HDRC_RXCOUNT:
+        return s->ep[ep].rxcount;
+
+    default:
+        TRACE("unknown register 0x%02x", addr);
+        return 0x00;
+    };
+}
+
+static void musb_ep_writeb(void *opaque, int ep, int addr, uint8_t value)
+{
+    MUSBState *s = (MUSBState *) opaque;
+
+    switch (addr) {
+    case MUSB_HDRC_TXTYPE:
+        s->ep[ep].type[0] = value;
+        break;
+    case MUSB_HDRC_TXINTERVAL:
+        s->ep[ep].interval[0] = value;
+        musb_ep_frame_cancel(&s->ep[ep], 0);
+        break;
+    case MUSB_HDRC_RXTYPE:
+        s->ep[ep].type[1] = value;
+        break;
+    case MUSB_HDRC_RXINTERVAL:
+        s->ep[ep].interval[1] = value;
+        musb_ep_frame_cancel(&s->ep[ep], 1);
+        break;
+    case (MUSB_HDRC_FIFOSIZE & ~1):
+        break;
+    case MUSB_HDRC_FIFOSIZE:
+        TRACE("somebody messes with fifosize (now %i bytes)", value);
+        s->ep[ep].fifosize = value;
+        break;
+    default:
+        TRACE("unknown register 0x%02x", addr);
+        break;
+    };
+}
+
+static uint16_t musb_ep_readh(void *opaque, int ep, int addr)
+{
+    MUSBState *s = (MUSBState *) opaque;
+    uint16_t ret;
+
+    switch (addr) {
+    case MUSB_HDRC_TXMAXP:
+        return s->ep[ep].maxp[0];
+    case MUSB_HDRC_TXCSR:
+        return s->ep[ep].csr[0];
+    case MUSB_HDRC_RXMAXP:
+        return s->ep[ep].maxp[1];
+    case MUSB_HDRC_RXCSR:
+        ret = s->ep[ep].csr[1];
+
+        /* TODO: This and other bits probably depend on
+         * ep->csr[1] & MGC_M_RXCSR_AUTOCLEAR.  */
+        if (s->ep[ep].csr[1] & MGC_M_RXCSR_AUTOCLEAR)
+            s->ep[ep].csr[1] &= ~MGC_M_RXCSR_RXPKTRDY;
+
+        return ret;
+    case MUSB_HDRC_RXCOUNT:
+        return s->ep[ep].rxcount;
+
+    default:
+        return musb_ep_readb(s, ep, addr) |
+                (musb_ep_readb(s, ep, addr | 1) << 8);
+    };
+}
+
+static void musb_ep_writeh(void *opaque, int ep, int addr, uint16_t value)
+{
+    MUSBState *s = (MUSBState *) opaque;
+
+    switch (addr) {
+    case MUSB_HDRC_TXMAXP:
+        s->ep[ep].maxp[0] = value;
+        break;
+    case MUSB_HDRC_TXCSR:
+        if (ep) {
+            s->ep[ep].csr[0] &= value & 0xa6;
+            s->ep[ep].csr[0] |= value & 0xff59;
+        } else {
+            s->ep[ep].csr[0] &= value & 0x85;
+            s->ep[ep].csr[0] |= value & 0xf7a;
+        }
+
+        musb_ep_frame_cancel(&s->ep[ep], 0);
+
+        if ((ep && (value & MGC_M_TXCSR_FLUSHFIFO)) ||
+                        (!ep && (value & MGC_M_CSR0_FLUSHFIFO))) {
+            s->ep[ep].fifolen[0] = 0;
+            s->ep[ep].fifostart[0] = 0;
+            if (ep)
+                s->ep[ep].csr[0] &=
+                        ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY);
+            else
+                s->ep[ep].csr[0] &=
+                        ~(MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_RXPKTRDY);
+        }
+        if (
+                        (ep &&
+#ifdef CLEAR_NAK
+                         (value & MGC_M_TXCSR_TXPKTRDY) &&
+                         !(value & MGC_M_TXCSR_H_NAKTIMEOUT)) ||
+#else
+                         (value & MGC_M_TXCSR_TXPKTRDY)) ||
+#endif
+                        (!ep &&
+#ifdef CLEAR_NAK
+                         (value & MGC_M_CSR0_TXPKTRDY) &&
+                         !(value & MGC_M_CSR0_H_NAKTIMEOUT)))
+#else
+                         (value & MGC_M_CSR0_TXPKTRDY)))
+#endif
+            musb_tx_rdy(s, ep);
+        if (!ep &&
+                        (value & MGC_M_CSR0_H_REQPKT) &&
+#ifdef CLEAR_NAK
+                        !(value & (MGC_M_CSR0_H_NAKTIMEOUT |
+                                        MGC_M_CSR0_RXPKTRDY)))
+#else
+                        !(value & MGC_M_CSR0_RXPKTRDY))
+#endif
+            musb_rx_req(s, ep);
+        break;
+
+    case MUSB_HDRC_RXMAXP:
+        s->ep[ep].maxp[1] = value;
+        break;
+    case MUSB_HDRC_RXCSR:
+        /* (DMA mode only) */
+        if (
+                (value & MGC_M_RXCSR_H_AUTOREQ) &&
+                !(value & MGC_M_RXCSR_RXPKTRDY) &&
+                (s->ep[ep].csr[1] & MGC_M_RXCSR_RXPKTRDY))
+            value |= MGC_M_RXCSR_H_REQPKT;
+
+        s->ep[ep].csr[1] &= 0x102 | (value & 0x4d);
+        s->ep[ep].csr[1] |= value & 0xfeb0;
+
+        musb_ep_frame_cancel(&s->ep[ep], 1);
+
+        if (value & MGC_M_RXCSR_FLUSHFIFO) {
+            s->ep[ep].fifolen[1] = 0;
+            s->ep[ep].fifostart[1] = 0;
+            s->ep[ep].csr[1] &= ~(MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY);
+            /* If double buffering and we have two packets ready, flush
+             * only the first one and set up the fifo at the second packet.  */
+        }
+#ifdef CLEAR_NAK
+        if ((value & MGC_M_RXCSR_H_REQPKT) && !(value & MGC_M_RXCSR_DATAERROR))
+#else
+        if (value & MGC_M_RXCSR_H_REQPKT)
+#endif
+            musb_rx_req(s, ep);
+        break;
+    case MUSB_HDRC_RXCOUNT:
+        s->ep[ep].rxcount = value;
+        break;
+
+    default:
+        musb_ep_writeb(s, ep, addr, value & 0xff);
+        musb_ep_writeb(s, ep, addr | 1, value >> 8);
+    };
+}
+
+/* Generic control */
+static uint32_t musb_readb(void *opaque, target_phys_addr_t addr)
+{
+    MUSBState *s = (MUSBState *) opaque;
+    int ep, i;
+    uint8_t ret;
+
+    switch (addr) {
+    case MUSB_HDRC_FADDR:
+        return s->faddr;
+    case MUSB_HDRC_POWER:
+        return s->power;
+    case MUSB_HDRC_INTRUSB:
+        ret = s->intr;
+        for (i = 0; i < sizeof(ret) * 8; i ++)
+            if (ret & (1 << i))
+                musb_intr_set(s, i, 0);
+        return ret;
+    case MUSB_HDRC_INTRUSBE:
+        return s->mask;
+    case MUSB_HDRC_INDEX:
+        return s->idx;
+    case MUSB_HDRC_TESTMODE:
+        return 0x00;
+
+    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+        return musb_ep_readb(s, s->idx, addr & 0xf);
+
+    case MUSB_HDRC_DEVCTL:
+        return s->devctl;
+
+    case MUSB_HDRC_TXFIFOSZ:
+    case MUSB_HDRC_RXFIFOSZ:
+    case MUSB_HDRC_VCTRL:
+        /* TODO */
+        return 0x00;
+
+    case MUSB_HDRC_HWVERS:
+        return (1 << 10) | 400;
+
+    case (MUSB_HDRC_VCTRL | 1):
+    case (MUSB_HDRC_HWVERS | 1):
+    case (MUSB_HDRC_DEVCTL | 1):
+        return 0x00;
+
+    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+        ep = (addr >> 3) & 0xf;
+        return musb_busctl_readb(s, ep, addr & 0x7);
+
+    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+        ep = (addr >> 4) & 0xf;
+        return musb_ep_readb(s, ep, addr & 0xf);
+
+    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+        return musb_read_fifo(s->ep + ep);
+
+    default:
+        TRACE("unknown register 0x%02x", (int) addr);
+        return 0x00;
+    };
+}
+
+static void musb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    MUSBState *s = (MUSBState *) opaque;
+    int ep;
+
+    switch (addr) {
+    case MUSB_HDRC_FADDR:
+        s->faddr = value & 0x7f;
+        break;
+    case MUSB_HDRC_POWER:
+        s->power = (value & 0xef) | (s->power & 0x10);
+        /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */
+        if ((value & MGC_M_POWER_RESET) && s->port.dev) {
+            usb_device_reset(s->port.dev);
+            /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set.  */
+            if ((value & MGC_M_POWER_HSENAB) &&
+                            s->port.dev->speed == USB_SPEED_HIGH)
+                s->power |= MGC_M_POWER_HSMODE;        /* Success */
+            /* Restart frame counting.  */
+        }
+        if (value & MGC_M_POWER_SUSPENDM) {
+            /* When all transfers finish, suspend and if MGC_M_POWER_ENSUSPEND
+             * is set, also go into low power mode.  Frame counting stops.  */
+            /* XXX: Cleared when the interrupt register is read */
+        }
+        if (value & MGC_M_POWER_RESUME) {
+            /* Wait 20ms and signal resuming on the bus.  Frame counting
+             * restarts.  */
+        }
+        break;
+    case MUSB_HDRC_INTRUSB:
+        break;
+    case MUSB_HDRC_INTRUSBE:
+        s->mask = value & 0xff;
+        break;
+    case MUSB_HDRC_INDEX:
+        s->idx = value & 0xf;
+        break;
+    case MUSB_HDRC_TESTMODE:
+        break;
+
+    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+        musb_ep_writeb(s, s->idx, addr & 0xf, value);
+        break;
+
+    case MUSB_HDRC_DEVCTL:
+        s->session = !!(value & MGC_M_DEVCTL_SESSION);
+        musb_session_update(s,
+                        !!s->port.dev,
+                        !!(s->devctl & MGC_M_DEVCTL_SESSION));
+
+        /* It seems this is the only R/W bit in this register?  */
+        s->devctl &= ~MGC_M_DEVCTL_SESSION;
+        s->devctl |= value & MGC_M_DEVCTL_SESSION;
+        break;
+
+    case MUSB_HDRC_TXFIFOSZ:
+    case MUSB_HDRC_RXFIFOSZ:
+    case MUSB_HDRC_VCTRL:
+        /* TODO */
+        break;
+
+    case (MUSB_HDRC_VCTRL | 1):
+    case (MUSB_HDRC_DEVCTL | 1):
+        break;
+
+    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+        ep = (addr >> 3) & 0xf;
+        musb_busctl_writeb(s, ep, addr & 0x7, value);
+        break;
+
+    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+        ep = (addr >> 4) & 0xf;
+        musb_ep_writeb(s, ep, addr & 0xf, value);
+        break;
+
+    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+        musb_write_fifo(s->ep + ep, value & 0xff);
+        break;
+
+    default:
+        TRACE("unknown register 0x%02x", (int) addr);
+        break;
+    };
+}
+
+static uint32_t musb_readh(void *opaque, target_phys_addr_t addr)
+{
+    MUSBState *s = (MUSBState *) opaque;
+    int ep, i;
+    uint16_t ret;
+
+    switch (addr) {
+    case MUSB_HDRC_INTRTX:
+        ret = s->tx_intr;
+        /* Auto clear */
+        for (i = 0; i < sizeof(ret) * 8; i ++)
+            if (ret & (1 << i))
+                musb_tx_intr_set(s, i, 0);
+        return ret;
+    case MUSB_HDRC_INTRRX:
+        ret = s->rx_intr;
+        /* Auto clear */
+        for (i = 0; i < sizeof(ret) * 8; i ++)
+            if (ret & (1 << i))
+                musb_rx_intr_set(s, i, 0);
+        return ret;
+    case MUSB_HDRC_INTRTXE:
+        return s->tx_mask;
+    case MUSB_HDRC_INTRRXE:
+        return s->rx_mask;
+
+    case MUSB_HDRC_FRAME:
+        /* TODO */
+        return 0x0000;
+    case MUSB_HDRC_TXFIFOADDR:
+        return s->ep[s->idx].fifoaddr[0];
+    case MUSB_HDRC_RXFIFOADDR:
+        return s->ep[s->idx].fifoaddr[1];
+
+    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+        return musb_ep_readh(s, s->idx, addr & 0xf);
+
+    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+        ep = (addr >> 3) & 0xf;
+        return musb_busctl_readh(s, ep, addr & 0x7);
+
+    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+        ep = (addr >> 4) & 0xf;
+        return musb_ep_readh(s, ep, addr & 0xf);
+
+    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+        return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8);
+
+    default:
+        return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8);
+    };
+}
+
+static void musb_writeh(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    MUSBState *s = (MUSBState *) opaque;
+    int ep;
+
+    switch (addr) {
+    case MUSB_HDRC_INTRTXE:
+        s->tx_mask = value;
+        /* XXX: the masks seem to apply on the raising edge like with
+         * edge-triggered interrupts, thus no need to update.  I may be
+         * wrong though.  */
+        break;
+    case MUSB_HDRC_INTRRXE:
+        s->rx_mask = value;
+        break;
+
+    case MUSB_HDRC_FRAME:
+        /* TODO */
+        break;
+    case MUSB_HDRC_TXFIFOADDR:
+        s->ep[s->idx].fifoaddr[0] = value;
+        s->ep[s->idx].buf[0] =
+                s->buf + ((value << 3) & 0x7ff );
+        break;
+    case MUSB_HDRC_RXFIFOADDR:
+        s->ep[s->idx].fifoaddr[1] = value;
+        s->ep[s->idx].buf[1] =
+                s->buf + ((value << 3) & 0x7ff);
+        break;
+
+    case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+        musb_ep_writeh(s, s->idx, addr & 0xf, value);
+        break;
+
+    case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+        ep = (addr >> 3) & 0xf;
+        musb_busctl_writeh(s, ep, addr & 0x7, value);
+        break;
+
+    case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+        ep = (addr >> 4) & 0xf;
+        musb_ep_writeh(s, ep, addr & 0xf, value);
+        break;
+
+    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+        musb_write_fifo(s->ep + ep, value & 0xff);
+        musb_write_fifo(s->ep + ep, (value >> 8) & 0xff);
+        break;
+
+    default:
+        musb_writeb(s, addr, value & 0xff);
+        musb_writeb(s, addr | 1, value >> 8);
+    };
+}
+
+static uint32_t musb_readw(void *opaque, target_phys_addr_t addr)
+{
+    MUSBState *s = (MUSBState *) opaque;
+    int ep;
+
+    switch (addr) {
+    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+        return ( musb_read_fifo(s->ep + ep)       |
+                 musb_read_fifo(s->ep + ep) << 8  |
+                 musb_read_fifo(s->ep + ep) << 16 |
+                 musb_read_fifo(s->ep + ep) << 24 );
+    default:
+        TRACE("unknown register 0x%02x", (int) addr);
+        return 0x00000000;
+    };
+}
+
+static void musb_writew(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    MUSBState *s = (MUSBState *) opaque;
+    int ep;
+
+    switch (addr) {
+    case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+        ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+        musb_write_fifo(s->ep + ep, value & 0xff);
+        musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff);
+        musb_write_fifo(s->ep + ep, (value >> 16) & 0xff);
+        musb_write_fifo(s->ep + ep, (value >> 24) & 0xff);
+            break;
+    default:
+        TRACE("unknown register 0x%02x", (int) addr);
+        break;
+    };
+}
+
+CPUReadMemoryFunc * const musb_read[] = {
+    musb_readb,
+    musb_readh,
+    musb_readw,
+};
+
+CPUWriteMemoryFunc * const musb_write[] = {
+    musb_writeb,
+    musb_writeh,
+    musb_writew,
+};
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
new file mode 100644 (file)
index 0000000..dd79cef
--- /dev/null
@@ -0,0 +1,1898 @@
+/*
+ * QEMU USB OHCI Emulation
+ * Copyright (c) 2004 Gianni Tedesco
+ * Copyright (c) 2006 CodeSourcery
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * TODO:
+ *  o Isochronous transfers
+ *  o Allocate bandwidth in frames properly
+ *  o Disable timers when nothing needs to be done, or remove timer usage
+ *    all together.
+ *  o Handle unrecoverable errors properly
+ *  o BIOS work to boot from USB storage
+*/
+
+#include "hw/hw.h"
+#include "qemu-timer.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
+#include "hw/usb-ohci.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-addr.h"
+
+//#define DEBUG_OHCI
+/* Dump packet contents.  */
+//#define DEBUG_PACKET
+//#define DEBUG_ISOCH
+/* This causes frames to occur 1000x slower */
+//#define OHCI_TIME_WARP 1
+
+#ifdef DEBUG_OHCI
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+/* Number of Downstream Ports on the root hub.  */
+
+#define OHCI_MAX_PORTS 15
+
+static int64_t usb_frame_time;
+static int64_t usb_bit_time;
+
+typedef struct OHCIPort {
+    USBPort port;
+    uint32_t ctrl;
+} OHCIPort;
+
+typedef struct {
+    USBBus bus;
+    qemu_irq irq;
+    MemoryRegion mem;
+    int num_ports;
+    const char *name;
+
+    QEMUTimer *eof_timer;
+    int64_t sof_time;
+
+    /* OHCI state */
+    /* Control partition */
+    uint32_t ctl, status;
+    uint32_t intr_status;
+    uint32_t intr;
+
+    /* memory pointer partition */
+    uint32_t hcca;
+    uint32_t ctrl_head, ctrl_cur;
+    uint32_t bulk_head, bulk_cur;
+    uint32_t per_cur;
+    uint32_t done;
+    int done_count;
+
+    /* Frame counter partition */
+    uint32_t fsmps:15;
+    uint32_t fit:1;
+    uint32_t fi:14;
+    uint32_t frt:1;
+    uint16_t frame_number;
+    uint16_t padding;
+    uint32_t pstart;
+    uint32_t lst;
+
+    /* Root Hub partition */
+    uint32_t rhdesc_a, rhdesc_b;
+    uint32_t rhstatus;
+    OHCIPort rhport[OHCI_MAX_PORTS];
+
+    /* PXA27x Non-OHCI events */
+    uint32_t hstatus;
+    uint32_t hmask;
+    uint32_t hreset;
+    uint32_t htest;
+
+    /* SM501 local memory offset */
+    target_phys_addr_t localmem_base;
+
+    /* Active packets.  */
+    uint32_t old_ctl;
+    USBPacket usb_packet;
+    uint8_t usb_buf[8192];
+    uint32_t async_td;
+    int async_complete;
+
+} OHCIState;
+
+/* Host Controller Communications Area */
+struct ohci_hcca {
+    uint32_t intr[32];
+    uint16_t frame, pad;
+    uint32_t done;
+};
+
+static void ohci_bus_stop(OHCIState *ohci);
+static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
+
+/* Bitfields for the first word of an Endpoint Desciptor.  */
+#define OHCI_ED_FA_SHIFT  0
+#define OHCI_ED_FA_MASK   (0x7f<<OHCI_ED_FA_SHIFT)
+#define OHCI_ED_EN_SHIFT  7
+#define OHCI_ED_EN_MASK   (0xf<<OHCI_ED_EN_SHIFT)
+#define OHCI_ED_D_SHIFT   11
+#define OHCI_ED_D_MASK    (3<<OHCI_ED_D_SHIFT)
+#define OHCI_ED_S         (1<<13)
+#define OHCI_ED_K         (1<<14)
+#define OHCI_ED_F         (1<<15)
+#define OHCI_ED_MPS_SHIFT 16
+#define OHCI_ED_MPS_MASK  (0x7ff<<OHCI_ED_MPS_SHIFT)
+
+/* Flags in the head field of an Endpoint Desciptor.  */
+#define OHCI_ED_H         1
+#define OHCI_ED_C         2
+
+/* Bitfields for the first word of a Transfer Desciptor.  */
+#define OHCI_TD_R         (1<<18)
+#define OHCI_TD_DP_SHIFT  19
+#define OHCI_TD_DP_MASK   (3<<OHCI_TD_DP_SHIFT)
+#define OHCI_TD_DI_SHIFT  21
+#define OHCI_TD_DI_MASK   (7<<OHCI_TD_DI_SHIFT)
+#define OHCI_TD_T0        (1<<24)
+#define OHCI_TD_T1        (1<<25)
+#define OHCI_TD_EC_SHIFT  26
+#define OHCI_TD_EC_MASK   (3<<OHCI_TD_EC_SHIFT)
+#define OHCI_TD_CC_SHIFT  28
+#define OHCI_TD_CC_MASK   (0xf<<OHCI_TD_CC_SHIFT)
+
+/* Bitfields for the first word of an Isochronous Transfer Desciptor.  */
+/* CC & DI - same as in the General Transfer Desciptor */
+#define OHCI_TD_SF_SHIFT  0
+#define OHCI_TD_SF_MASK   (0xffff<<OHCI_TD_SF_SHIFT)
+#define OHCI_TD_FC_SHIFT  24
+#define OHCI_TD_FC_MASK   (7<<OHCI_TD_FC_SHIFT)
+
+/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */
+#define OHCI_TD_PSW_CC_SHIFT 12
+#define OHCI_TD_PSW_CC_MASK  (0xf<<OHCI_TD_PSW_CC_SHIFT)
+#define OHCI_TD_PSW_SIZE_SHIFT 0
+#define OHCI_TD_PSW_SIZE_MASK  (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
+
+#define OHCI_PAGE_MASK    0xfffff000
+#define OHCI_OFFSET_MASK  0xfff
+
+#define OHCI_DPTR_MASK    0xfffffff0
+
+#define OHCI_BM(val, field) \
+  (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT)
+
+#define OHCI_SET_BM(val, field, newval) do { \
+    val &= ~OHCI_##field##_MASK; \
+    val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \
+    } while(0)
+
+/* endpoint descriptor */
+struct ohci_ed {
+    uint32_t flags;
+    uint32_t tail;
+    uint32_t head;
+    uint32_t next;
+};
+
+/* General transfer descriptor */
+struct ohci_td {
+    uint32_t flags;
+    uint32_t cbp;
+    uint32_t next;
+    uint32_t be;
+};
+
+/* Isochronous transfer descriptor */
+struct ohci_iso_td {
+    uint32_t flags;
+    uint32_t bp;
+    uint32_t next;
+    uint32_t be;
+    uint16_t offset[8];
+};
+
+#define USB_HZ                      12000000
+
+/* OHCI Local stuff */
+#define OHCI_CTL_CBSR         ((1<<0)|(1<<1))
+#define OHCI_CTL_PLE          (1<<2)
+#define OHCI_CTL_IE           (1<<3)
+#define OHCI_CTL_CLE          (1<<4)
+#define OHCI_CTL_BLE          (1<<5)
+#define OHCI_CTL_HCFS         ((1<<6)|(1<<7))
+#define  OHCI_USB_RESET       0x00
+#define  OHCI_USB_RESUME      0x40
+#define  OHCI_USB_OPERATIONAL 0x80
+#define  OHCI_USB_SUSPEND     0xc0
+#define OHCI_CTL_IR           (1<<8)
+#define OHCI_CTL_RWC          (1<<9)
+#define OHCI_CTL_RWE          (1<<10)
+
+#define OHCI_STATUS_HCR       (1<<0)
+#define OHCI_STATUS_CLF       (1<<1)
+#define OHCI_STATUS_BLF       (1<<2)
+#define OHCI_STATUS_OCR       (1<<3)
+#define OHCI_STATUS_SOC       ((1<<6)|(1<<7))
+
+#define OHCI_INTR_SO          (1<<0) /* Scheduling overrun */
+#define OHCI_INTR_WD          (1<<1) /* HcDoneHead writeback */
+#define OHCI_INTR_SF          (1<<2) /* Start of frame */
+#define OHCI_INTR_RD          (1<<3) /* Resume detect */
+#define OHCI_INTR_UE          (1<<4) /* Unrecoverable error */
+#define OHCI_INTR_FNO         (1<<5) /* Frame number overflow */
+#define OHCI_INTR_RHSC        (1<<6) /* Root hub status change */
+#define OHCI_INTR_OC          (1<<30) /* Ownership change */
+#define OHCI_INTR_MIE         (1<<31) /* Master Interrupt Enable */
+
+#define OHCI_HCCA_SIZE        0x100
+#define OHCI_HCCA_MASK        0xffffff00
+
+#define OHCI_EDPTR_MASK       0xfffffff0
+
+#define OHCI_FMI_FI           0x00003fff
+#define OHCI_FMI_FSMPS        0xffff0000
+#define OHCI_FMI_FIT          0x80000000
+
+#define OHCI_FR_RT            (1<<31)
+
+#define OHCI_LS_THRESH        0x628
+
+#define OHCI_RHA_RW_MASK      0x00000000 /* Mask of supported features.  */
+#define OHCI_RHA_PSM          (1<<8)
+#define OHCI_RHA_NPS          (1<<9)
+#define OHCI_RHA_DT           (1<<10)
+#define OHCI_RHA_OCPM         (1<<11)
+#define OHCI_RHA_NOCP         (1<<12)
+#define OHCI_RHA_POTPGT_MASK  0xff000000
+
+#define OHCI_RHS_LPS          (1<<0)
+#define OHCI_RHS_OCI          (1<<1)
+#define OHCI_RHS_DRWE         (1<<15)
+#define OHCI_RHS_LPSC         (1<<16)
+#define OHCI_RHS_OCIC         (1<<17)
+#define OHCI_RHS_CRWE         (1<<31)
+
+#define OHCI_PORT_CCS         (1<<0)
+#define OHCI_PORT_PES         (1<<1)
+#define OHCI_PORT_PSS         (1<<2)
+#define OHCI_PORT_POCI        (1<<3)
+#define OHCI_PORT_PRS         (1<<4)
+#define OHCI_PORT_PPS         (1<<8)
+#define OHCI_PORT_LSDA        (1<<9)
+#define OHCI_PORT_CSC         (1<<16)
+#define OHCI_PORT_PESC        (1<<17)
+#define OHCI_PORT_PSSC        (1<<18)
+#define OHCI_PORT_OCIC        (1<<19)
+#define OHCI_PORT_PRSC        (1<<20)
+#define OHCI_PORT_WTC         (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \
+                               |OHCI_PORT_OCIC|OHCI_PORT_PRSC)
+
+#define OHCI_TD_DIR_SETUP     0x0
+#define OHCI_TD_DIR_OUT       0x1
+#define OHCI_TD_DIR_IN        0x2
+#define OHCI_TD_DIR_RESERVED  0x3
+
+#define OHCI_CC_NOERROR             0x0
+#define OHCI_CC_CRC                 0x1
+#define OHCI_CC_BITSTUFFING         0x2
+#define OHCI_CC_DATATOGGLEMISMATCH  0x3
+#define OHCI_CC_STALL               0x4
+#define OHCI_CC_DEVICENOTRESPONDING 0x5
+#define OHCI_CC_PIDCHECKFAILURE     0x6
+#define OHCI_CC_UNDEXPETEDPID       0x7
+#define OHCI_CC_DATAOVERRUN         0x8
+#define OHCI_CC_DATAUNDERRUN        0x9
+#define OHCI_CC_BUFFEROVERRUN       0xc
+#define OHCI_CC_BUFFERUNDERRUN      0xd
+
+#define OHCI_HRESET_FSBIR       (1 << 0)
+
+/* Update IRQ levels */
+static inline void ohci_intr_update(OHCIState *ohci)
+{
+    int level = 0;
+
+    if ((ohci->intr & OHCI_INTR_MIE) &&
+        (ohci->intr_status & ohci->intr))
+        level = 1;
+
+    qemu_set_irq(ohci->irq, level);
+}
+
+/* Set an interrupt */
+static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
+{
+    ohci->intr_status |= intr;
+    ohci_intr_update(ohci);
+}
+
+/* Attach or detach a device on a root hub port.  */
+static void ohci_attach(USBPort *port1)
+{
+    OHCIState *s = port1->opaque;
+    OHCIPort *port = &s->rhport[port1->index];
+    uint32_t old_state = port->ctrl;
+
+    /* set connect status */
+    port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
+
+    /* update speed */
+    if (port->port.dev->speed == USB_SPEED_LOW) {
+        port->ctrl |= OHCI_PORT_LSDA;
+    } else {
+        port->ctrl &= ~OHCI_PORT_LSDA;
+    }
+
+    /* notify of remote-wakeup */
+    if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
+        ohci_set_interrupt(s, OHCI_INTR_RD);
+    }
+
+    DPRINTF("usb-ohci: Attached port %d\n", port1->index);
+
+    if (old_state != port->ctrl) {
+        ohci_set_interrupt(s, OHCI_INTR_RHSC);
+    }
+}
+
+static void ohci_detach(USBPort *port1)
+{
+    OHCIState *s = port1->opaque;
+    OHCIPort *port = &s->rhport[port1->index];
+    uint32_t old_state = port->ctrl;
+
+    ohci_async_cancel_device(s, port1->dev);
+
+    /* set connect status */
+    if (port->ctrl & OHCI_PORT_CCS) {
+        port->ctrl &= ~OHCI_PORT_CCS;
+        port->ctrl |= OHCI_PORT_CSC;
+    }
+    /* disable port */
+    if (port->ctrl & OHCI_PORT_PES) {
+        port->ctrl &= ~OHCI_PORT_PES;
+        port->ctrl |= OHCI_PORT_PESC;
+    }
+    DPRINTF("usb-ohci: Detached port %d\n", port1->index);
+
+    if (old_state != port->ctrl) {
+        ohci_set_interrupt(s, OHCI_INTR_RHSC);
+    }
+}
+
+static void ohci_wakeup(USBPort *port1)
+{
+    OHCIState *s = port1->opaque;
+    OHCIPort *port = &s->rhport[port1->index];
+    uint32_t intr = 0;
+    if (port->ctrl & OHCI_PORT_PSS) {
+        DPRINTF("usb-ohci: port %d: wakeup\n", port1->index);
+        port->ctrl |= OHCI_PORT_PSSC;
+        port->ctrl &= ~OHCI_PORT_PSS;
+        intr = OHCI_INTR_RHSC;
+    }
+    /* Note that the controller can be suspended even if this port is not */
+    if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
+        DPRINTF("usb-ohci: remote-wakeup: SUSPEND->RESUME\n");
+        /* This is the one state transition the controller can do by itself */
+        s->ctl &= ~OHCI_CTL_HCFS;
+        s->ctl |= OHCI_USB_RESUME;
+        /* In suspend mode only ResumeDetected is possible, not RHSC:
+         * see the OHCI spec 5.1.2.3.
+         */
+        intr = OHCI_INTR_RD;
+    }
+    ohci_set_interrupt(s, intr);
+}
+
+static void ohci_child_detach(USBPort *port1, USBDevice *child)
+{
+    OHCIState *s = port1->opaque;
+
+    ohci_async_cancel_device(s, child);
+}
+
+static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr)
+{
+    USBDevice *dev;
+    int i;
+
+    for (i = 0; i < ohci->num_ports; i++) {
+        if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) {
+            continue;
+        }
+        dev = usb_find_device(&ohci->rhport[i].port, addr);
+        if (dev != NULL) {
+            return dev;
+        }
+    }
+    return NULL;
+}
+
+/* Reset the controller */
+static void ohci_reset(void *opaque)
+{
+    OHCIState *ohci = opaque;
+    OHCIPort *port;
+    int i;
+
+    ohci_bus_stop(ohci);
+    ohci->ctl = 0;
+    ohci->old_ctl = 0;
+    ohci->status = 0;
+    ohci->intr_status = 0;
+    ohci->intr = OHCI_INTR_MIE;
+
+    ohci->hcca = 0;
+    ohci->ctrl_head = ohci->ctrl_cur = 0;
+    ohci->bulk_head = ohci->bulk_cur = 0;
+    ohci->per_cur = 0;
+    ohci->done = 0;
+    ohci->done_count = 7;
+
+    /* FSMPS is marked TBD in OCHI 1.0, what gives ffs?
+     * I took the value linux sets ...
+     */
+    ohci->fsmps = 0x2778;
+    ohci->fi = 0x2edf;
+    ohci->fit = 0;
+    ohci->frt = 0;
+    ohci->frame_number = 0;
+    ohci->pstart = 0;
+    ohci->lst = OHCI_LS_THRESH;
+
+    ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
+    ohci->rhdesc_b = 0x0; /* Impl. specific */
+    ohci->rhstatus = 0;
+
+    for (i = 0; i < ohci->num_ports; i++)
+      {
+        port = &ohci->rhport[i];
+        port->ctrl = 0;
+        if (port->port.dev && port->port.dev->attached) {
+            usb_port_reset(&port->port);
+        }
+      }
+    if (ohci->async_td) {
+        usb_cancel_packet(&ohci->usb_packet);
+        ohci->async_td = 0;
+    }
+    DPRINTF("usb-ohci: Reset %s\n", ohci->name);
+}
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(OHCIState *ohci,
+                             uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    addr += ohci->localmem_base;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        cpu_physical_memory_read(addr, buf, sizeof(*buf));
+        *buf = le32_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(OHCIState *ohci,
+                             uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    addr += ohci->localmem_base;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint32_t tmp = cpu_to_le32(*buf);
+        cpu_physical_memory_write(addr, &tmp, sizeof(tmp));
+    }
+
+    return 1;
+}
+
+/* Get an array of words from main memory */
+static inline int get_words(OHCIState *ohci,
+                            uint32_t addr, uint16_t *buf, int num)
+{
+    int i;
+
+    addr += ohci->localmem_base;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        cpu_physical_memory_read(addr, buf, sizeof(*buf));
+        *buf = le16_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of words in to main memory */
+static inline int put_words(OHCIState *ohci,
+                            uint32_t addr, uint16_t *buf, int num)
+{
+    int i;
+
+    addr += ohci->localmem_base;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint16_t tmp = cpu_to_le16(*buf);
+        cpu_physical_memory_write(addr, &tmp, sizeof(tmp));
+    }
+
+    return 1;
+}
+
+static inline int ohci_read_ed(OHCIState *ohci,
+                               uint32_t addr, struct ohci_ed *ed)
+{
+    return get_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_read_td(OHCIState *ohci,
+                               uint32_t addr, struct ohci_td *td)
+{
+    return get_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_read_iso_td(OHCIState *ohci,
+                                   uint32_t addr, struct ohci_iso_td *td)
+{
+    return (get_dwords(ohci, addr, (uint32_t *)td, 4) &&
+            get_words(ohci, addr + 16, td->offset, 8));
+}
+
+static inline int ohci_read_hcca(OHCIState *ohci,
+                                 uint32_t addr, struct ohci_hcca *hcca)
+{
+    cpu_physical_memory_read(addr + ohci->localmem_base, hcca, sizeof(*hcca));
+    return 1;
+}
+
+static inline int ohci_put_ed(OHCIState *ohci,
+                              uint32_t addr, struct ohci_ed *ed)
+{
+    return put_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_put_td(OHCIState *ohci,
+                              uint32_t addr, struct ohci_td *td)
+{
+    return put_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_put_iso_td(OHCIState *ohci,
+                                  uint32_t addr, struct ohci_iso_td *td)
+{
+    return (put_dwords(ohci, addr, (uint32_t *)td, 4) &&
+            put_words(ohci, addr + 16, td->offset, 8));
+}
+
+static inline int ohci_put_hcca(OHCIState *ohci,
+                                uint32_t addr, struct ohci_hcca *hcca)
+{
+    cpu_physical_memory_write(addr + ohci->localmem_base, hcca, sizeof(*hcca));
+    return 1;
+}
+
+/* Read/Write the contents of a TD from/to main memory.  */
+static void ohci_copy_td(OHCIState *ohci, struct ohci_td *td,
+                         uint8_t *buf, int len, int write)
+{
+    uint32_t ptr;
+    uint32_t n;
+
+    ptr = td->cbp;
+    n = 0x1000 - (ptr & 0xfff);
+    if (n > len)
+        n = len;
+    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
+    if (n == len)
+        return;
+    ptr = td->be & ~0xfffu;
+    buf += n;
+    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
+}
+
+/* Read/Write the contents of an ISO TD from/to main memory.  */
+static void ohci_copy_iso_td(OHCIState *ohci,
+                             uint32_t start_addr, uint32_t end_addr,
+                             uint8_t *buf, int len, int write)
+{
+    uint32_t ptr;
+    uint32_t n;
+
+    ptr = start_addr;
+    n = 0x1000 - (ptr & 0xfff);
+    if (n > len)
+        n = len;
+    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
+    if (n == len)
+        return;
+    ptr = end_addr & ~0xfffu;
+    buf += n;
+    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
+}
+
+static void ohci_process_lists(OHCIState *ohci, int completion);
+
+static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
+{
+    OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
+#ifdef DEBUG_PACKET
+    DPRINTF("Async packet complete\n");
+#endif
+    ohci->async_complete = 1;
+    ohci_process_lists(ohci, 1);
+}
+
+#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
+
+static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
+                               int completion)
+{
+    int dir;
+    size_t len = 0;
+#ifdef DEBUG_ISOCH
+    const char *str = NULL;
+#endif
+    int pid;
+    int ret;
+    int i;
+    USBDevice *dev;
+    USBEndpoint *ep;
+    struct ohci_iso_td iso_td;
+    uint32_t addr;
+    uint16_t starting_frame;
+    int16_t relative_frame_number;
+    int frame_count;
+    uint32_t start_offset, next_offset, end_offset = 0;
+    uint32_t start_addr, end_addr;
+
+    addr = ed->head & OHCI_DPTR_MASK;
+
+    if (!ohci_read_iso_td(ohci, addr, &iso_td)) {
+        printf("usb-ohci: ISO_TD read error at %x\n", addr);
+        return 0;
+    }
+
+    starting_frame = OHCI_BM(iso_td.flags, TD_SF);
+    frame_count = OHCI_BM(iso_td.flags, TD_FC);
+    relative_frame_number = USUB(ohci->frame_number, starting_frame); 
+
+#ifdef DEBUG_ISOCH
+    printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "frame_number 0x%.8x starting_frame 0x%.8x\n"
+           "frame_count  0x%.8x relative %d\n"
+           "di 0x%.8x cc 0x%.8x\n",
+           ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK,
+           iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
+           iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
+           iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
+           ohci->frame_number, starting_frame, 
+           frame_count, relative_frame_number,         
+           OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
+#endif
+
+    if (relative_frame_number < 0) {
+        DPRINTF("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
+        return 1;
+    } else if (relative_frame_number > frame_count) {
+        /* ISO TD expired - retire the TD to the Done Queue and continue with
+           the next ISO TD of the same ED */
+        DPRINTF("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, 
+               frame_count);
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+        ohci_put_iso_td(ohci, addr, &iso_td);
+        return 0;
+    }
+
+    dir = OHCI_BM(ed->flags, ED_D);
+    switch (dir) {
+    case OHCI_TD_DIR_IN:
+#ifdef DEBUG_ISOCH
+        str = "in";
+#endif
+        pid = USB_TOKEN_IN;
+        break;
+    case OHCI_TD_DIR_OUT:
+#ifdef DEBUG_ISOCH
+        str = "out";
+#endif
+        pid = USB_TOKEN_OUT;
+        break;
+    case OHCI_TD_DIR_SETUP:
+#ifdef DEBUG_ISOCH
+        str = "setup";
+#endif
+        pid = USB_TOKEN_SETUP;
+        break;
+    default:
+        printf("usb-ohci: Bad direction %d\n", dir);
+        return 1;
+    }
+
+    if (!iso_td.bp || !iso_td.be) {
+        printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be);
+        return 1;
+    }
+
+    start_offset = iso_td.offset[relative_frame_number];
+    next_offset = iso_td.offset[relative_frame_number + 1];
+
+    if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || 
+        ((relative_frame_number < frame_count) && 
+         !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
+        printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
+               start_offset, next_offset);
+        return 1;
+    }
+
+    if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
+        printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n",
+                start_offset, next_offset);
+        return 1;
+    }
+
+    if ((start_offset & 0x1000) == 0) {
+        start_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    } else {
+        start_addr = (iso_td.be & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    }
+
+    if (relative_frame_number < frame_count) {
+        end_offset = next_offset - 1;
+        if ((end_offset & 0x1000) == 0) {
+            end_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        } else {
+            end_addr = (iso_td.be & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        }
+    } else {
+        /* Last packet in the ISO TD */
+        end_addr = iso_td.be;
+    }
+
+    if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
+        len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
+            - (start_addr & OHCI_OFFSET_MASK);
+    } else {
+        len = end_addr - start_addr + 1;
+    }
+
+    if (len && dir != OHCI_TD_DIR_IN) {
+        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, 0);
+    }
+
+    if (completion) {
+        ret = ohci->usb_packet.result;
+    } else {
+        dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
+        ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
+        usb_packet_setup(&ohci->usb_packet, pid, ep);
+        usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
+        ret = usb_handle_packet(dev, &ohci->usb_packet);
+        if (ret == USB_RET_ASYNC) {
+            return 1;
+        }
+    }
+
+#ifdef DEBUG_ISOCH
+    printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
+           start_offset, end_offset, start_addr, end_addr, str, len, ret);
+#endif
+
+    /* Writeback */
+    if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
+        /* IN transfer succeeded */
+        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, 1);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
+    } else if (dir == OHCI_TD_DIR_OUT && ret == len) {
+        /* OUT transfer succeeded */
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
+    } else {
+        if (ret > (ssize_t) len) {
+            printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAOVERRUN);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                        len);
+        } else if (ret >= 0) {
+            printf("usb-ohci: DataUnderrun %d\n", ret);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAUNDERRUN);
+        } else {
+            switch (ret) {
+            case USB_RET_IOERROR:
+            case USB_RET_NODEV:
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_DEVICENOTRESPONDING);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            case USB_RET_NAK:
+            case USB_RET_STALL:
+                printf("usb-ohci: got NAK/STALL %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_STALL);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            default:
+                printf("usb-ohci: Bad device response %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_UNDEXPETEDPID);
+                break;
+            }
+        }
+    }
+
+    if (relative_frame_number == frame_count) {
+        /* Last data packet of ISO TD - retire the TD to the Done Queue */
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+    }
+    ohci_put_iso_td(ohci, addr, &iso_td);
+    return 1;
+}
+
+/* Service a transport descriptor.
+   Returns nonzero to terminate processing of this endpoint.  */
+
+static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
+{
+    int dir;
+    size_t len = 0, pktlen = 0;
+#ifdef DEBUG_PACKET
+    const char *str = NULL;
+#endif
+    int pid;
+    int ret;
+    int i;
+    USBDevice *dev;
+    USBEndpoint *ep;
+    struct ohci_td td;
+    uint32_t addr;
+    int flag_r;
+    int completion;
+
+    addr = ed->head & OHCI_DPTR_MASK;
+    /* See if this TD has already been submitted to the device.  */
+    completion = (addr == ohci->async_td);
+    if (completion && !ohci->async_complete) {
+#ifdef DEBUG_PACKET
+        DPRINTF("Skipping async TD\n");
+#endif
+        return 1;
+    }
+    if (!ohci_read_td(ohci, addr, &td)) {
+        fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
+        return 0;
+    }
+
+    dir = OHCI_BM(ed->flags, ED_D);
+    switch (dir) {
+    case OHCI_TD_DIR_OUT:
+    case OHCI_TD_DIR_IN:
+        /* Same value.  */
+        break;
+    default:
+        dir = OHCI_BM(td.flags, TD_DP);
+        break;
+    }
+
+    switch (dir) {
+    case OHCI_TD_DIR_IN:
+#ifdef DEBUG_PACKET
+        str = "in";
+#endif
+        pid = USB_TOKEN_IN;
+        break;
+    case OHCI_TD_DIR_OUT:
+#ifdef DEBUG_PACKET
+        str = "out";
+#endif
+        pid = USB_TOKEN_OUT;
+        break;
+    case OHCI_TD_DIR_SETUP:
+#ifdef DEBUG_PACKET
+        str = "setup";
+#endif
+        pid = USB_TOKEN_SETUP;
+        break;
+    default:
+        fprintf(stderr, "usb-ohci: Bad direction\n");
+        return 1;
+    }
+    if (td.cbp && td.be) {
+        if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) {
+            len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff);
+        } else {
+            len = (td.be - td.cbp) + 1;
+        }
+
+        pktlen = len;
+        if (len && dir != OHCI_TD_DIR_IN) {
+            /* The endpoint may not allow us to transfer it all now */
+            pktlen = (ed->flags & OHCI_ED_MPS_MASK) >> OHCI_ED_MPS_SHIFT;
+            if (pktlen > len) {
+                pktlen = len;
+            }
+            if (!completion) {
+                ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen, 0);
+            }
+        }
+    }
+
+    flag_r = (td.flags & OHCI_TD_R) != 0;
+#ifdef DEBUG_PACKET
+    DPRINTF(" TD @ 0x%.8x %" PRId64 " of %" PRId64
+            " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
+            addr, (int64_t)pktlen, (int64_t)len, str, flag_r, td.cbp, td.be);
+
+    if (pktlen > 0 && dir != OHCI_TD_DIR_IN) {
+        DPRINTF("  data:");
+        for (i = 0; i < pktlen; i++) {
+            printf(" %.2x", ohci->usb_buf[i]);
+        }
+        DPRINTF("\n");
+    }
+#endif
+    if (completion) {
+        ret = ohci->usb_packet.result;
+        ohci->async_td = 0;
+        ohci->async_complete = 0;
+    } else {
+        if (ohci->async_td) {
+            /* ??? The hardware should allow one active packet per
+               endpoint.  We only allow one active packet per controller.
+               This should be sufficient as long as devices respond in a
+               timely manner.
+            */
+#ifdef DEBUG_PACKET
+            DPRINTF("Too many pending packets\n");
+#endif
+            return 1;
+        }
+        dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
+        ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
+        usb_packet_setup(&ohci->usb_packet, pid, ep);
+        usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
+        ret = usb_handle_packet(dev, &ohci->usb_packet);
+#ifdef DEBUG_PACKET
+        DPRINTF("ret=%d\n", ret);
+#endif
+        if (ret == USB_RET_ASYNC) {
+            ohci->async_td = addr;
+            return 1;
+        }
+    }
+    if (ret >= 0) {
+        if (dir == OHCI_TD_DIR_IN) {
+            ohci_copy_td(ohci, &td, ohci->usb_buf, ret, 1);
+#ifdef DEBUG_PACKET
+            DPRINTF("  data:");
+            for (i = 0; i < ret; i++)
+                printf(" %.2x", ohci->usb_buf[i]);
+            DPRINTF("\n");
+#endif
+        } else {
+            ret = pktlen;
+        }
+    }
+
+    /* Writeback */
+    if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
+        /* Transmission succeeded.  */
+        if (ret == len) {
+            td.cbp = 0;
+        } else {
+            if ((td.cbp & 0xfff) + ret > 0xfff) {
+                td.cbp = (td.be & ~0xfff) + ((td.cbp + ret) & 0xfff);
+            } else {
+                td.cbp += ret;
+            }
+        }
+        td.flags |= OHCI_TD_T1;
+        td.flags ^= OHCI_TD_T0;
+        OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
+        OHCI_SET_BM(td.flags, TD_EC, 0);
+
+        if ((dir != OHCI_TD_DIR_IN) && (ret != len)) {
+            /* Partial packet transfer: TD not ready to retire yet */
+            goto exit_no_retire;
+        }
+
+        /* Setting ED_C is part of the TD retirement process */
+        ed->head &= ~OHCI_ED_C;
+        if (td.flags & OHCI_TD_T0)
+            ed->head |= OHCI_ED_C;
+    } else {
+        if (ret >= 0) {
+            DPRINTF("usb-ohci: Underrun\n");
+            OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
+        } else {
+            switch (ret) {
+            case USB_RET_IOERROR:
+            case USB_RET_NODEV:
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
+            case USB_RET_NAK:
+                DPRINTF("usb-ohci: got NAK\n");
+                return 1;
+            case USB_RET_STALL:
+                DPRINTF("usb-ohci: got STALL\n");
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL);
+                break;
+            case USB_RET_BABBLE:
+                DPRINTF("usb-ohci: got BABBLE\n");
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+                break;
+            default:
+                fprintf(stderr, "usb-ohci: Bad device response %d\n", ret);
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID);
+                OHCI_SET_BM(td.flags, TD_EC, 3);
+                break;
+            }
+        }
+        ed->head |= OHCI_ED_H;
+    }
+
+    /* Retire this TD */
+    ed->head &= ~OHCI_DPTR_MASK;
+    ed->head |= td.next & OHCI_DPTR_MASK;
+    td.next = ohci->done;
+    ohci->done = addr;
+    i = OHCI_BM(td.flags, TD_DI);
+    if (i < ohci->done_count)
+        ohci->done_count = i;
+exit_no_retire:
+    ohci_put_td(ohci, addr, &td);
+    return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
+}
+
+/* Service an endpoint list.  Returns nonzero if active TD were found.  */
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
+{
+    struct ohci_ed ed;
+    uint32_t next_ed;
+    uint32_t cur;
+    int active;
+
+    active = 0;
+
+    if (head == 0)
+        return 0;
+
+    for (cur = head; cur; cur = next_ed) {
+        if (!ohci_read_ed(ohci, cur, &ed)) {
+            fprintf(stderr, "usb-ohci: ED read error at %x\n", cur);
+            return 0;
+        }
+
+        next_ed = ed.next & OHCI_DPTR_MASK;
+
+        if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
+            uint32_t addr;
+            /* Cancel pending packets for ED that have been paused.  */
+            addr = ed.head & OHCI_DPTR_MASK;
+            if (ohci->async_td && addr == ohci->async_td) {
+                usb_cancel_packet(&ohci->usb_packet);
+                ohci->async_td = 0;
+            }
+            continue;
+        }
+
+        while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
+#ifdef DEBUG_PACKET
+            DPRINTF("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
+                    "h=%u c=%u\n  head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur,
+                    OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN),
+                    OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0,
+                    (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0,
+                    OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0,
+                    (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
+                    ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
+#endif
+            active = 1;
+
+            if ((ed.flags & OHCI_ED_F) == 0) {
+                if (ohci_service_td(ohci, &ed))
+                    break;
+            } else {
+                /* Handle isochronous endpoints */
+                if (ohci_service_iso_td(ohci, &ed, completion))
+                    break;
+            }
+        }
+
+        ohci_put_ed(ohci, cur, &ed);
+    }
+
+    return active;
+}
+
+/* Generate a SOF event, and set a timer for EOF */
+static void ohci_sof(OHCIState *ohci)
+{
+    ohci->sof_time = qemu_get_clock_ns(vm_clock);
+    qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
+    ohci_set_interrupt(ohci, OHCI_INTR_SF);
+}
+
+/* Process Control and Bulk lists.  */
+static void ohci_process_lists(OHCIState *ohci, int completion)
+{
+    if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
+        if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
+            DPRINTF("usb-ohci: head %x, cur %x\n",
+                    ohci->ctrl_head, ohci->ctrl_cur);
+        }
+        if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
+            ohci->ctrl_cur = 0;
+            ohci->status &= ~OHCI_STATUS_CLF;
+        }
+    }
+
+    if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
+        if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
+            ohci->bulk_cur = 0;
+            ohci->status &= ~OHCI_STATUS_BLF;
+        }
+    }
+}
+
+/* Do frame processing on frame boundary */
+static void ohci_frame_boundary(void *opaque)
+{
+    OHCIState *ohci = opaque;
+    struct ohci_hcca hcca;
+
+    ohci_read_hcca(ohci, ohci->hcca, &hcca);
+
+    /* Process all the lists at the end of the frame */
+    if (ohci->ctl & OHCI_CTL_PLE) {
+        int n;
+
+        n = ohci->frame_number & 0x1f;
+        ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
+    }
+
+    /* Cancel all pending packets if either of the lists has been disabled.  */
+    if (ohci->async_td &&
+        ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
+        usb_cancel_packet(&ohci->usb_packet);
+        ohci->async_td = 0;
+    }
+    ohci->old_ctl = ohci->ctl;
+    ohci_process_lists(ohci, 0);
+
+    /* Frame boundary, so do EOF stuf here */
+    ohci->frt = ohci->fit;
+
+    /* Increment frame number and take care of endianness. */
+    ohci->frame_number = (ohci->frame_number + 1) & 0xffff;
+    hcca.frame = cpu_to_le16(ohci->frame_number);
+
+    if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) {
+        if (!ohci->done)
+            abort();
+        if (ohci->intr & ohci->intr_status)
+            ohci->done |= 1;
+        hcca.done = cpu_to_le32(ohci->done);
+        ohci->done = 0;
+        ohci->done_count = 7;
+        ohci_set_interrupt(ohci, OHCI_INTR_WD);
+    }
+
+    if (ohci->done_count != 7 && ohci->done_count != 0)
+        ohci->done_count--;
+
+    /* Do SOF stuff here */
+    ohci_sof(ohci);
+
+    /* Writeback HCCA */
+    ohci_put_hcca(ohci, ohci->hcca, &hcca);
+}
+
+/* Start sending SOF tokens across the USB bus, lists are processed in
+ * next frame
+ */
+static int ohci_bus_start(OHCIState *ohci)
+{
+    ohci->eof_timer = qemu_new_timer_ns(vm_clock,
+                    ohci_frame_boundary,
+                    ohci);
+
+    if (ohci->eof_timer == NULL) {
+        fprintf(stderr, "usb-ohci: %s: qemu_new_timer_ns failed\n", ohci->name);
+        /* TODO: Signal unrecoverable error */
+        return 0;
+    }
+
+    DPRINTF("usb-ohci: %s: USB Operational\n", ohci->name);
+
+    ohci_sof(ohci);
+
+    return 1;
+}
+
+/* Stop sending SOF tokens on the bus */
+static void ohci_bus_stop(OHCIState *ohci)
+{
+    if (ohci->eof_timer)
+        qemu_del_timer(ohci->eof_timer);
+    ohci->eof_timer = NULL;
+}
+
+/* Sets a flag in a port status register but only set it if the port is
+ * connected, if not set ConnectStatusChange flag. If flag is enabled
+ * return 1.
+ */
+static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val)
+{
+    int ret = 1;
+
+    /* writing a 0 has no effect */
+    if (val == 0)
+        return 0;
+
+    /* If CurrentConnectStatus is cleared we set
+     * ConnectStatusChange
+     */
+    if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) {
+        ohci->rhport[i].ctrl |= OHCI_PORT_CSC;
+        if (ohci->rhstatus & OHCI_RHS_DRWE) {
+            /* TODO: CSC is a wakeup event */
+        }
+        return 0;
+    }
+
+    if (ohci->rhport[i].ctrl & val)
+        ret = 0;
+
+    /* set the bit */
+    ohci->rhport[i].ctrl |= val;
+
+    return ret;
+}
+
+/* Set the frame interval - frame interval toggle is manipulated by the hcd only */
+static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
+{
+    val &= OHCI_FMI_FI;
+
+    if (val != ohci->fi) {
+        DPRINTF("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
+            ohci->name, ohci->fi, ohci->fi);
+    }
+
+    ohci->fi = val;
+}
+
+static void ohci_port_power(OHCIState *ohci, int i, int p)
+{
+    if (p) {
+        ohci->rhport[i].ctrl |= OHCI_PORT_PPS;
+    } else {
+        ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS|
+                    OHCI_PORT_CCS|
+                    OHCI_PORT_PSS|
+                    OHCI_PORT_PRS);
+    }
+}
+
+/* Set HcControlRegister */
+static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
+{
+    uint32_t old_state;
+    uint32_t new_state;
+
+    old_state = ohci->ctl & OHCI_CTL_HCFS;
+    ohci->ctl = val;
+    new_state = ohci->ctl & OHCI_CTL_HCFS;
+
+    /* no state change */
+    if (old_state == new_state)
+        return;
+
+    switch (new_state) {
+    case OHCI_USB_OPERATIONAL:
+        ohci_bus_start(ohci);
+        break;
+    case OHCI_USB_SUSPEND:
+        ohci_bus_stop(ohci);
+        DPRINTF("usb-ohci: %s: USB Suspended\n", ohci->name);
+        break;
+    case OHCI_USB_RESUME:
+        DPRINTF("usb-ohci: %s: USB Resume\n", ohci->name);
+        break;
+    case OHCI_USB_RESET:
+        ohci_reset(ohci);
+        DPRINTF("usb-ohci: %s: USB Reset\n", ohci->name);
+        break;
+    }
+}
+
+static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
+{
+    uint16_t fr;
+    int64_t tks;
+
+    if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL)
+        return (ohci->frt << 31);
+
+    /* Being in USB operational state guarnatees sof_time was
+     * set already.
+     */
+    tks = qemu_get_clock_ns(vm_clock) - ohci->sof_time;
+
+    /* avoid muldiv if possible */
+    if (tks >= usb_frame_time)
+        return (ohci->frt << 31);
+
+    tks = muldiv64(1, tks, usb_bit_time);
+    fr = (uint16_t)(ohci->fi - tks);
+
+    return (ohci->frt << 31) | fr;
+}
+
+
+/* Set root hub status */
+static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
+{
+    uint32_t old_state;
+
+    old_state = ohci->rhstatus;
+
+    /* write 1 to clear OCIC */
+    if (val & OHCI_RHS_OCIC)
+        ohci->rhstatus &= ~OHCI_RHS_OCIC;
+
+    if (val & OHCI_RHS_LPS) {
+        int i;
+
+        for (i = 0; i < ohci->num_ports; i++)
+            ohci_port_power(ohci, i, 0);
+        DPRINTF("usb-ohci: powered down all ports\n");
+    }
+
+    if (val & OHCI_RHS_LPSC) {
+        int i;
+
+        for (i = 0; i < ohci->num_ports; i++)
+            ohci_port_power(ohci, i, 1);
+        DPRINTF("usb-ohci: powered up all ports\n");
+    }
+
+    if (val & OHCI_RHS_DRWE)
+        ohci->rhstatus |= OHCI_RHS_DRWE;
+
+    if (val & OHCI_RHS_CRWE)
+        ohci->rhstatus &= ~OHCI_RHS_DRWE;
+
+    if (old_state != ohci->rhstatus)
+        ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+}
+
+/* Set root hub port status */
+static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
+{
+    uint32_t old_state;
+    OHCIPort *port;
+
+    port = &ohci->rhport[portnum];
+    old_state = port->ctrl;
+
+    /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */
+    if (val & OHCI_PORT_WTC)
+        port->ctrl &= ~(val & OHCI_PORT_WTC);
+
+    if (val & OHCI_PORT_CCS)
+        port->ctrl &= ~OHCI_PORT_PES;
+
+    ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
+
+    if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) {
+        DPRINTF("usb-ohci: port %d: SUSPEND\n", portnum);
+    }
+
+    if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
+        DPRINTF("usb-ohci: port %d: RESET\n", portnum);
+        usb_device_reset(port->port.dev);
+        port->ctrl &= ~OHCI_PORT_PRS;
+        /* ??? Should this also set OHCI_PORT_PESC.  */
+        port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
+    }
+
+    /* Invert order here to ensure in ambiguous case, device is
+     * powered up...
+     */
+    if (val & OHCI_PORT_LSDA)
+        ohci_port_power(ohci, portnum, 0);
+    if (val & OHCI_PORT_PPS)
+        ohci_port_power(ohci, portnum, 1);
+
+    if (old_state != port->ctrl)
+        ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+
+    return;
+}
+
+static uint64_t ohci_mem_read(void *opaque,
+                              target_phys_addr_t addr,
+                              unsigned size)
+{
+    OHCIState *ohci = opaque;
+    uint32_t retval;
+
+    /* Only aligned reads are allowed on OHCI */
+    if (addr & 3) {
+        fprintf(stderr, "usb-ohci: Mis-aligned read\n");
+        return 0xffffffff;
+    } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+        /* HcRhPortStatus */
+        retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS;
+    } else {
+        switch (addr >> 2) {
+        case 0: /* HcRevision */
+            retval = 0x10;
+            break;
+
+        case 1: /* HcControl */
+            retval = ohci->ctl;
+            break;
+
+        case 2: /* HcCommandStatus */
+            retval = ohci->status;
+            break;
+
+        case 3: /* HcInterruptStatus */
+            retval = ohci->intr_status;
+            break;
+
+        case 4: /* HcInterruptEnable */
+        case 5: /* HcInterruptDisable */
+            retval = ohci->intr;
+            break;
+
+        case 6: /* HcHCCA */
+            retval = ohci->hcca;
+            break;
+
+        case 7: /* HcPeriodCurrentED */
+            retval = ohci->per_cur;
+            break;
+
+        case 8: /* HcControlHeadED */
+            retval = ohci->ctrl_head;
+            break;
+
+        case 9: /* HcControlCurrentED */
+            retval = ohci->ctrl_cur;
+            break;
+
+        case 10: /* HcBulkHeadED */
+            retval = ohci->bulk_head;
+            break;
+
+        case 11: /* HcBulkCurrentED */
+            retval = ohci->bulk_cur;
+            break;
+
+        case 12: /* HcDoneHead */
+            retval = ohci->done;
+            break;
+
+        case 13: /* HcFmInterretval */
+            retval = (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi);
+            break;
+
+        case 14: /* HcFmRemaining */
+            retval = ohci_get_frame_remaining(ohci);
+            break;
+
+        case 15: /* HcFmNumber */
+            retval = ohci->frame_number;
+            break;
+
+        case 16: /* HcPeriodicStart */
+            retval = ohci->pstart;
+            break;
+
+        case 17: /* HcLSThreshold */
+            retval = ohci->lst;
+            break;
+
+        case 18: /* HcRhDescriptorA */
+            retval = ohci->rhdesc_a;
+            break;
+
+        case 19: /* HcRhDescriptorB */
+            retval = ohci->rhdesc_b;
+            break;
+
+        case 20: /* HcRhStatus */
+            retval = ohci->rhstatus;
+            break;
+
+        /* PXA27x specific registers */
+        case 24: /* HcStatus */
+            retval = ohci->hstatus & ohci->hmask;
+            break;
+
+        case 25: /* HcHReset */
+            retval = ohci->hreset;
+            break;
+
+        case 26: /* HcHInterruptEnable */
+            retval = ohci->hmask;
+            break;
+
+        case 27: /* HcHInterruptTest */
+            retval = ohci->htest;
+            break;
+
+        default:
+            fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr);
+            retval = 0xffffffff;
+        }
+    }
+
+    return retval;
+}
+
+static void ohci_mem_write(void *opaque,
+                           target_phys_addr_t addr,
+                           uint64_t val,
+                           unsigned size)
+{
+    OHCIState *ohci = opaque;
+
+    /* Only aligned reads are allowed on OHCI */
+    if (addr & 3) {
+        fprintf(stderr, "usb-ohci: Mis-aligned write\n");
+        return;
+    }
+
+    if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+        /* HcRhPortStatus */
+        ohci_port_set_status(ohci, (addr - 0x54) >> 2, val);
+        return;
+    }
+
+    switch (addr >> 2) {
+    case 1: /* HcControl */
+        ohci_set_ctl(ohci, val);
+        break;
+
+    case 2: /* HcCommandStatus */
+        /* SOC is read-only */
+        val = (val & ~OHCI_STATUS_SOC);
+
+        /* Bits written as '0' remain unchanged in the register */
+        ohci->status |= val;
+
+        if (ohci->status & OHCI_STATUS_HCR)
+            ohci_reset(ohci);
+        break;
+
+    case 3: /* HcInterruptStatus */
+        ohci->intr_status &= ~val;
+        ohci_intr_update(ohci);
+        break;
+
+    case 4: /* HcInterruptEnable */
+        ohci->intr |= val;
+        ohci_intr_update(ohci);
+        break;
+
+    case 5: /* HcInterruptDisable */
+        ohci->intr &= ~val;
+        ohci_intr_update(ohci);
+        break;
+
+    case 6: /* HcHCCA */
+        ohci->hcca = val & OHCI_HCCA_MASK;
+        break;
+
+    case 7: /* HcPeriodCurrentED */
+        /* Ignore writes to this read-only register, Linux does them */
+        break;
+
+    case 8: /* HcControlHeadED */
+        ohci->ctrl_head = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 9: /* HcControlCurrentED */
+        ohci->ctrl_cur = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 10: /* HcBulkHeadED */
+        ohci->bulk_head = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 11: /* HcBulkCurrentED */
+        ohci->bulk_cur = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 13: /* HcFmInterval */
+        ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16;
+        ohci->fit = (val & OHCI_FMI_FIT) >> 31;
+        ohci_set_frame_interval(ohci, val);
+        break;
+
+    case 15: /* HcFmNumber */
+        break;
+
+    case 16: /* HcPeriodicStart */
+        ohci->pstart = val & 0xffff;
+        break;
+
+    case 17: /* HcLSThreshold */
+        ohci->lst = val & 0xffff;
+        break;
+
+    case 18: /* HcRhDescriptorA */
+        ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK;
+        ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK;
+        break;
+
+    case 19: /* HcRhDescriptorB */
+        break;
+
+    case 20: /* HcRhStatus */
+        ohci_set_hub_status(ohci, val);
+        break;
+
+    /* PXA27x specific registers */
+    case 24: /* HcStatus */
+        ohci->hstatus &= ~(val & ohci->hmask);
+
+    case 25: /* HcHReset */
+        ohci->hreset = val & ~OHCI_HRESET_FSBIR;
+        if (val & OHCI_HRESET_FSBIR)
+            ohci_reset(ohci);
+        break;
+
+    case 26: /* HcHInterruptEnable */
+        ohci->hmask = val;
+        break;
+
+    case 27: /* HcHInterruptTest */
+        ohci->htest = val;
+        break;
+
+    default:
+        fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr);
+        break;
+    }
+}
+
+static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
+{
+    if (ohci->async_td &&
+        usb_packet_is_inflight(&ohci->usb_packet) &&
+        ohci->usb_packet.ep->dev == dev) {
+        usb_cancel_packet(&ohci->usb_packet);
+        ohci->async_td = 0;
+    }
+}
+
+static const MemoryRegionOps ohci_mem_ops = {
+    .read = ohci_mem_read,
+    .write = ohci_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static USBPortOps ohci_port_ops = {
+    .attach = ohci_attach,
+    .detach = ohci_detach,
+    .child_detach = ohci_child_detach,
+    .wakeup = ohci_wakeup,
+    .complete = ohci_async_complete_packet,
+};
+
+static USBBusOps ohci_bus_ops = {
+};
+
+static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
+                         int num_ports, uint32_t localmem_base,
+                         char *masterbus, uint32_t firstport)
+{
+    int i;
+
+    if (usb_frame_time == 0) {
+#ifdef OHCI_TIME_WARP
+        usb_frame_time = get_ticks_per_sec();
+        usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ/1000);
+#else
+        usb_frame_time = muldiv64(1, get_ticks_per_sec(), 1000);
+        if (get_ticks_per_sec() >= USB_HZ) {
+            usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ);
+        } else {
+            usb_bit_time = 1;
+        }
+#endif
+        DPRINTF("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n",
+                usb_frame_time, usb_bit_time);
+    }
+
+    ohci->num_ports = num_ports;
+    if (masterbus) {
+        USBPort *ports[OHCI_MAX_PORTS];
+        for(i = 0; i < num_ports; i++) {
+            ports[i] = &ohci->rhport[i].port;
+        }
+        if (usb_register_companion(masterbus, ports, num_ports,
+                firstport, ohci, &ohci_port_ops,
+                USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
+            return -1;
+        }
+    } else {
+        usb_bus_new(&ohci->bus, &ohci_bus_ops, dev);
+        for (i = 0; i < num_ports; i++) {
+            usb_register_port(&ohci->bus, &ohci->rhport[i].port,
+                              ohci, i, &ohci_port_ops,
+                              USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+        }
+    }
+
+    memory_region_init_io(&ohci->mem, &ohci_mem_ops, ohci, "ohci", 256);
+    ohci->localmem_base = localmem_base;
+
+    ohci->name = object_get_typename(OBJECT(dev));
+    usb_packet_init(&ohci->usb_packet);
+
+    ohci->async_td = 0;
+    qemu_register_reset(ohci_reset, ohci);
+
+    return 0;
+}
+
+typedef struct {
+    PCIDevice pci_dev;
+    OHCIState state;
+    char *masterbus;
+    uint32_t num_ports;
+    uint32_t firstport;
+} OHCIPCIState;
+
+static int usb_ohci_initfn_pci(struct PCIDevice *dev)
+{
+    OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev);
+
+    ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */
+    ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+    if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0,
+                      ohci->masterbus, ohci->firstport) != 0) {
+        return -1;
+    }
+    ohci->state.irq = ohci->pci_dev.irq[0];
+
+    /* TODO: avoid cast below by using dev */
+    pci_register_bar(&ohci->pci_dev, 0, 0, &ohci->state.mem);
+    return 0;
+}
+
+void usb_ohci_init_pci(struct PCIBus *bus, int devfn)
+{
+    pci_create_simple(bus, devfn, "pci-ohci");
+}
+
+typedef struct {
+    SysBusDevice busdev;
+    OHCIState ohci;
+    uint32_t num_ports;
+    target_phys_addr_t dma_offset;
+} OHCISysBusState;
+
+static int ohci_init_pxa(SysBusDevice *dev)
+{
+    OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev);
+
+    /* Cannot fail as we pass NULL for masterbus */
+    usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0);
+    sysbus_init_irq(dev, &s->ohci.irq);
+    sysbus_init_mmio(dev, &s->ohci.mem);
+
+    return 0;
+}
+
+static Property ohci_pci_properties[] = {
+    DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus),
+    DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3),
+    DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ohci_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_ohci_initfn_pci;
+    k->vendor_id = PCI_VENDOR_ID_APPLE;
+    k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->desc = "Apple USB Controller";
+    dc->props = ohci_pci_properties;
+}
+
+static TypeInfo ohci_pci_info = {
+    .name          = "pci-ohci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(OHCIPCIState),
+    .class_init    = ohci_pci_class_init,
+};
+
+static Property ohci_sysbus_properties[] = {
+    DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3),
+    DEFINE_PROP_TADDR("dma-offset", OHCISysBusState, dma_offset, 3),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ohci_sysbus_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sbc->init = ohci_init_pxa;
+    dc->desc = "OHCI USB Controller";
+    dc->props = ohci_sysbus_properties;
+}
+
+static TypeInfo ohci_sysbus_info = {
+    .name          = "sysbus-ohci",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(OHCISysBusState),
+    .class_init    = ohci_sysbus_class_init,
+};
+
+static void ohci_register_types(void)
+{
+    type_register_static(&ohci_pci_info);
+    type_register_static(&ohci_sysbus_info);
+}
+
+type_init(ohci_register_types)
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
new file mode 100644 (file)
index 0000000..7c2e9b3
--- /dev/null
@@ -0,0 +1,1408 @@
+/*
+ * USB UHCI controller emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *     Magor rewrite of the UHCI data structures parser and frame processor
+ *     Support for fully async operation and multiple outstanding transactions
+ *
+ * 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 "hw/hw.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
+#include "qemu-timer.h"
+#include "hw/usb-uhci.h"
+#include "iov.h"
+#include "dma.h"
+
+//#define DEBUG
+//#define DEBUG_DUMP_DATA
+
+#define UHCI_CMD_FGR      (1 << 4)
+#define UHCI_CMD_EGSM     (1 << 3)
+#define UHCI_CMD_GRESET   (1 << 2)
+#define UHCI_CMD_HCRESET  (1 << 1)
+#define UHCI_CMD_RS       (1 << 0)
+
+#define UHCI_STS_HCHALTED (1 << 5)
+#define UHCI_STS_HCPERR   (1 << 4)
+#define UHCI_STS_HSERR    (1 << 3)
+#define UHCI_STS_RD       (1 << 2)
+#define UHCI_STS_USBERR   (1 << 1)
+#define UHCI_STS_USBINT   (1 << 0)
+
+#define TD_CTRL_SPD     (1 << 29)
+#define TD_CTRL_ERROR_SHIFT  27
+#define TD_CTRL_IOS     (1 << 25)
+#define TD_CTRL_IOC     (1 << 24)
+#define TD_CTRL_ACTIVE  (1 << 23)
+#define TD_CTRL_STALL   (1 << 22)
+#define TD_CTRL_BABBLE  (1 << 20)
+#define TD_CTRL_NAK     (1 << 19)
+#define TD_CTRL_TIMEOUT (1 << 18)
+
+#define UHCI_PORT_SUSPEND (1 << 12)
+#define UHCI_PORT_RESET (1 << 9)
+#define UHCI_PORT_LSDA  (1 << 8)
+#define UHCI_PORT_RD    (1 << 6)
+#define UHCI_PORT_ENC   (1 << 3)
+#define UHCI_PORT_EN    (1 << 2)
+#define UHCI_PORT_CSC   (1 << 1)
+#define UHCI_PORT_CCS   (1 << 0)
+
+#define UHCI_PORT_READ_ONLY    (0x1bb)
+#define UHCI_PORT_WRITE_CLEAR  (UHCI_PORT_CSC | UHCI_PORT_ENC)
+
+#define FRAME_TIMER_FREQ 1000
+
+#define FRAME_MAX_LOOPS  256
+
+#define NB_PORTS 2
+
+#ifdef DEBUG
+#define DPRINTF printf
+
+static const char *pid2str(int pid)
+{
+    switch (pid) {
+    case USB_TOKEN_SETUP: return "SETUP";
+    case USB_TOKEN_IN:    return "IN";
+    case USB_TOKEN_OUT:   return "OUT";
+    }
+    return "?";
+}
+
+#else
+#define DPRINTF(...)
+#endif
+
+typedef struct UHCIState UHCIState;
+typedef struct UHCIAsync UHCIAsync;
+typedef struct UHCIQueue UHCIQueue;
+
+/* 
+ * Pending async transaction.
+ * 'packet' must be the first field because completion
+ * handler does "(UHCIAsync *) pkt" cast.
+ */
+
+struct UHCIAsync {
+    USBPacket packet;
+    QEMUSGList sgl;
+    UHCIQueue *queue;
+    QTAILQ_ENTRY(UHCIAsync) next;
+    uint32_t  td;
+    uint8_t   isoc;
+    uint8_t   done;
+};
+
+struct UHCIQueue {
+    uint32_t  token;
+    UHCIState *uhci;
+    QTAILQ_ENTRY(UHCIQueue) next;
+    QTAILQ_HEAD(, UHCIAsync) asyncs;
+    int8_t    valid;
+};
+
+typedef struct UHCIPort {
+    USBPort port;
+    uint16_t ctrl;
+} UHCIPort;
+
+struct UHCIState {
+    PCIDevice dev;
+    MemoryRegion io_bar;
+    USBBus bus; /* Note unused when we're a companion controller */
+    uint16_t cmd; /* cmd register */
+    uint16_t status;
+    uint16_t intr; /* interrupt enable register */
+    uint16_t frnum; /* frame number */
+    uint32_t fl_base_addr; /* frame list base address */
+    uint8_t sof_timing;
+    uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
+    int64_t expire_time;
+    QEMUTimer *frame_timer;
+    UHCIPort ports[NB_PORTS];
+
+    /* Interrupts that should be raised at the end of the current frame.  */
+    uint32_t pending_int_mask;
+
+    /* Active packets */
+    QTAILQ_HEAD(, UHCIQueue) queues;
+    uint8_t num_ports_vmstate;
+
+    /* Properties */
+    char *masterbus;
+    uint32_t firstport;
+};
+
+typedef struct UHCI_TD {
+    uint32_t link;
+    uint32_t ctrl; /* see TD_CTRL_xxx */
+    uint32_t token;
+    uint32_t buffer;
+} UHCI_TD;
+
+typedef struct UHCI_QH {
+    uint32_t link;
+    uint32_t el_link;
+} UHCI_QH;
+
+static inline int32_t uhci_queue_token(UHCI_TD *td)
+{
+    /* covers ep, dev, pid -> identifies the endpoint */
+    return td->token & 0x7ffff;
+}
+
+static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
+{
+    uint32_t token = uhci_queue_token(td);
+    UHCIQueue *queue;
+
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        if (queue->token == token) {
+            return queue;
+        }
+    }
+
+    queue = g_new0(UHCIQueue, 1);
+    queue->uhci = s;
+    queue->token = token;
+    QTAILQ_INIT(&queue->asyncs);
+    QTAILQ_INSERT_HEAD(&s->queues, queue, next);
+    return queue;
+}
+
+static void uhci_queue_free(UHCIQueue *queue)
+{
+    UHCIState *s = queue->uhci;
+
+    QTAILQ_REMOVE(&s->queues, queue, next);
+    g_free(queue);
+}
+
+static UHCIAsync *uhci_async_alloc(UHCIQueue *queue)
+{
+    UHCIAsync *async = g_new0(UHCIAsync, 1);
+
+    async->queue = queue;
+    usb_packet_init(&async->packet);
+    pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
+
+    return async;
+}
+
+static void uhci_async_free(UHCIAsync *async)
+{
+    usb_packet_cleanup(&async->packet);
+    qemu_sglist_destroy(&async->sgl);
+    g_free(async);
+}
+
+static void uhci_async_link(UHCIAsync *async)
+{
+    UHCIQueue *queue = async->queue;
+    QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
+}
+
+static void uhci_async_unlink(UHCIAsync *async)
+{
+    UHCIQueue *queue = async->queue;
+    QTAILQ_REMOVE(&queue->asyncs, async, next);
+}
+
+static void uhci_async_cancel(UHCIAsync *async)
+{
+    DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
+           async->td, async->token, async->done);
+
+    if (!async->done)
+        usb_cancel_packet(&async->packet);
+    uhci_async_free(async);
+}
+
+/*
+ * Mark all outstanding async packets as invalid.
+ * This is used for canceling them when TDs are removed by the HCD.
+ */
+static void uhci_async_validate_begin(UHCIState *s)
+{
+    UHCIQueue *queue;
+
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        queue->valid--;
+    }
+}
+
+/*
+ * Cancel async packets that are no longer valid
+ */
+static void uhci_async_validate_end(UHCIState *s)
+{
+    UHCIQueue *queue, *n;
+    UHCIAsync *async;
+
+    QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
+        if (queue->valid > 0) {
+            continue;
+        }
+        while (!QTAILQ_EMPTY(&queue->asyncs)) {
+            async = QTAILQ_FIRST(&queue->asyncs);
+            uhci_async_unlink(async);
+            uhci_async_cancel(async);
+        }
+        uhci_queue_free(queue);
+    }
+}
+
+static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
+{
+    UHCIQueue *queue;
+    UHCIAsync *curr, *n;
+
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+            if (!usb_packet_is_inflight(&curr->packet) ||
+                curr->packet.ep->dev != dev) {
+                continue;
+            }
+            uhci_async_unlink(curr);
+            uhci_async_cancel(curr);
+        }
+    }
+}
+
+static void uhci_async_cancel_all(UHCIState *s)
+{
+    UHCIQueue *queue;
+    UHCIAsync *curr, *n;
+
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+            uhci_async_unlink(curr);
+            uhci_async_cancel(curr);
+        }
+    }
+}
+
+static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
+{
+    uint32_t token = uhci_queue_token(td);
+    UHCIQueue *queue;
+    UHCIAsync *async;
+
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        if (queue->token == token) {
+            break;
+        }
+    }
+    if (queue == NULL) {
+        return NULL;
+    }
+
+    QTAILQ_FOREACH(async, &queue->asyncs, next) {
+        if (async->td == addr) {
+            return async;
+        }
+    }
+
+    return NULL;
+}
+
+static void uhci_update_irq(UHCIState *s)
+{
+    int level;
+    if (((s->status2 & 1) && (s->intr & (1 << 2))) ||
+        ((s->status2 & 2) && (s->intr & (1 << 3))) ||
+        ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) ||
+        ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) ||
+        (s->status & UHCI_STS_HSERR) ||
+        (s->status & UHCI_STS_HCPERR)) {
+        level = 1;
+    } else {
+        level = 0;
+    }
+    qemu_set_irq(s->dev.irq[3], level);
+}
+
+static void uhci_reset(void *opaque)
+{
+    UHCIState *s = opaque;
+    uint8_t *pci_conf;
+    int i;
+    UHCIPort *port;
+
+    DPRINTF("uhci: full reset\n");
+
+    pci_conf = s->dev.config;
+
+    pci_conf[0x6a] = 0x01; /* usb clock */
+    pci_conf[0x6b] = 0x00;
+    s->cmd = 0;
+    s->status = 0;
+    s->status2 = 0;
+    s->intr = 0;
+    s->fl_base_addr = 0;
+    s->sof_timing = 64;
+
+    for(i = 0; i < NB_PORTS; i++) {
+        port = &s->ports[i];
+        port->ctrl = 0x0080;
+        if (port->port.dev && port->port.dev->attached) {
+            usb_port_reset(&port->port);
+        }
+    }
+
+    uhci_async_cancel_all(s);
+}
+
+static void uhci_pre_save(void *opaque)
+{
+    UHCIState *s = opaque;
+
+    uhci_async_cancel_all(s);
+}
+
+static const VMStateDescription vmstate_uhci_port = {
+    .name = "uhci port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT16(ctrl, UHCIPort),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_uhci = {
+    .name = "uhci",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = uhci_pre_save,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, UHCIState),
+        VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
+        VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1,
+                             vmstate_uhci_port, UHCIPort),
+        VMSTATE_UINT16(cmd, UHCIState),
+        VMSTATE_UINT16(status, UHCIState),
+        VMSTATE_UINT16(intr, UHCIState),
+        VMSTATE_UINT16(frnum, UHCIState),
+        VMSTATE_UINT32(fl_base_addr, UHCIState),
+        VMSTATE_UINT8(sof_timing, UHCIState),
+        VMSTATE_UINT8(status2, UHCIState),
+        VMSTATE_TIMER(frame_timer, UHCIState),
+        VMSTATE_INT64_V(expire_time, UHCIState, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    UHCIState *s = opaque;
+
+    addr &= 0x1f;
+    switch(addr) {
+    case 0x0c:
+        s->sof_timing = val;
+        break;
+    }
+}
+
+static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr)
+{
+    UHCIState *s = opaque;
+    uint32_t val;
+
+    addr &= 0x1f;
+    switch(addr) {
+    case 0x0c:
+        val = s->sof_timing;
+        break;
+    default:
+        val = 0xff;
+        break;
+    }
+    return val;
+}
+
+static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+    UHCIState *s = opaque;
+
+    addr &= 0x1f;
+    DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val);
+
+    switch(addr) {
+    case 0x00:
+        if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
+            /* start frame processing */
+            s->expire_time = qemu_get_clock_ns(vm_clock) +
+                (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
+            s->status &= ~UHCI_STS_HCHALTED;
+        } else if (!(val & UHCI_CMD_RS)) {
+            s->status |= UHCI_STS_HCHALTED;
+        }
+        if (val & UHCI_CMD_GRESET) {
+            UHCIPort *port;
+            int i;
+
+            /* send reset on the USB bus */
+            for(i = 0; i < NB_PORTS; i++) {
+                port = &s->ports[i];
+                usb_device_reset(port->port.dev);
+            }
+            uhci_reset(s);
+            return;
+        }
+        if (val & UHCI_CMD_HCRESET) {
+            uhci_reset(s);
+            return;
+        }
+        s->cmd = val;
+        break;
+    case 0x02:
+        s->status &= ~val;
+        /* XXX: the chip spec is not coherent, so we add a hidden
+           register to distinguish between IOC and SPD */
+        if (val & UHCI_STS_USBINT)
+            s->status2 = 0;
+        uhci_update_irq(s);
+        break;
+    case 0x04:
+        s->intr = val;
+        uhci_update_irq(s);
+        break;
+    case 0x06:
+        if (s->status & UHCI_STS_HCHALTED)
+            s->frnum = val & 0x7ff;
+        break;
+    case 0x10 ... 0x1f:
+        {
+            UHCIPort *port;
+            USBDevice *dev;
+            int n;
+
+            n = (addr >> 1) & 7;
+            if (n >= NB_PORTS)
+                return;
+            port = &s->ports[n];
+            dev = port->port.dev;
+            if (dev && dev->attached) {
+                /* port reset */
+                if ( (val & UHCI_PORT_RESET) &&
+                     !(port->ctrl & UHCI_PORT_RESET) ) {
+                    usb_device_reset(dev);
+                }
+            }
+            port->ctrl &= UHCI_PORT_READ_ONLY;
+            port->ctrl |= (val & ~UHCI_PORT_READ_ONLY);
+            /* some bits are reset when a '1' is written to them */
+            port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR);
+        }
+        break;
+    }
+}
+
+static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr)
+{
+    UHCIState *s = opaque;
+    uint32_t val;
+
+    addr &= 0x1f;
+    switch(addr) {
+    case 0x00:
+        val = s->cmd;
+        break;
+    case 0x02:
+        val = s->status;
+        break;
+    case 0x04:
+        val = s->intr;
+        break;
+    case 0x06:
+        val = s->frnum;
+        break;
+    case 0x10 ... 0x1f:
+        {
+            UHCIPort *port;
+            int n;
+            n = (addr >> 1) & 7;
+            if (n >= NB_PORTS)
+                goto read_default;
+            port = &s->ports[n];
+            val = port->ctrl;
+        }
+        break;
+    default:
+    read_default:
+        val = 0xff7f; /* disabled port */
+        break;
+    }
+
+    DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val);
+
+    return val;
+}
+
+static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+    UHCIState *s = opaque;
+
+    addr &= 0x1f;
+    DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val);
+
+    switch(addr) {
+    case 0x08:
+        s->fl_base_addr = val & ~0xfff;
+        break;
+    }
+}
+
+static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr)
+{
+    UHCIState *s = opaque;
+    uint32_t val;
+
+    addr &= 0x1f;
+    switch(addr) {
+    case 0x08:
+        val = s->fl_base_addr;
+        break;
+    default:
+        val = 0xffffffff;
+        break;
+    }
+    return val;
+}
+
+/* signal resume if controller suspended */
+static void uhci_resume (void *opaque)
+{
+    UHCIState *s = (UHCIState *)opaque;
+
+    if (!s)
+        return;
+
+    if (s->cmd & UHCI_CMD_EGSM) {
+        s->cmd |= UHCI_CMD_FGR;
+        s->status |= UHCI_STS_RD;
+        uhci_update_irq(s);
+    }
+}
+
+static void uhci_attach(USBPort *port1)
+{
+    UHCIState *s = port1->opaque;
+    UHCIPort *port = &s->ports[port1->index];
+
+    /* set connect status */
+    port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
+
+    /* update speed */
+    if (port->port.dev->speed == USB_SPEED_LOW) {
+        port->ctrl |= UHCI_PORT_LSDA;
+    } else {
+        port->ctrl &= ~UHCI_PORT_LSDA;
+    }
+
+    uhci_resume(s);
+}
+
+static void uhci_detach(USBPort *port1)
+{
+    UHCIState *s = port1->opaque;
+    UHCIPort *port = &s->ports[port1->index];
+
+    uhci_async_cancel_device(s, port1->dev);
+
+    /* set connect status */
+    if (port->ctrl & UHCI_PORT_CCS) {
+        port->ctrl &= ~UHCI_PORT_CCS;
+        port->ctrl |= UHCI_PORT_CSC;
+    }
+    /* disable port */
+    if (port->ctrl & UHCI_PORT_EN) {
+        port->ctrl &= ~UHCI_PORT_EN;
+        port->ctrl |= UHCI_PORT_ENC;
+    }
+
+    uhci_resume(s);
+}
+
+static void uhci_child_detach(USBPort *port1, USBDevice *child)
+{
+    UHCIState *s = port1->opaque;
+
+    uhci_async_cancel_device(s, child);
+}
+
+static void uhci_wakeup(USBPort *port1)
+{
+    UHCIState *s = port1->opaque;
+    UHCIPort *port = &s->ports[port1->index];
+
+    if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) {
+        port->ctrl |= UHCI_PORT_RD;
+        uhci_resume(s);
+    }
+}
+
+static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr)
+{
+    USBDevice *dev;
+    int i;
+
+    for (i = 0; i < NB_PORTS; i++) {
+        UHCIPort *port = &s->ports[i];
+        if (!(port->ctrl & UHCI_PORT_EN)) {
+            continue;
+        }
+        dev = usb_find_device(&port->port, addr);
+        if (dev != NULL) {
+            return dev;
+        }
+    }
+    return NULL;
+}
+
+static void uhci_async_complete(USBPort *port, USBPacket *packet);
+static void uhci_process_frame(UHCIState *s);
+
+/* return -1 if fatal error (frame must be stopped)
+          0 if TD successful
+          1 if TD unsuccessful or inactive
+*/
+static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
+{
+    int len = 0, max_len, err, ret;
+    uint8_t pid;
+
+    max_len = ((td->token >> 21) + 1) & 0x7ff;
+    pid = td->token & 0xff;
+
+    ret = async->packet.result;
+
+    if (td->ctrl & TD_CTRL_IOS)
+        td->ctrl &= ~TD_CTRL_ACTIVE;
+
+    if (ret < 0)
+        goto out;
+
+    len = async->packet.result;
+    td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
+
+    /* The NAK bit may have been set by a previous frame, so clear it
+       here.  The docs are somewhat unclear, but win2k relies on this
+       behavior.  */
+    td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK);
+    if (td->ctrl & TD_CTRL_IOC)
+        *int_mask |= 0x01;
+
+    if (pid == USB_TOKEN_IN) {
+        if (len > max_len) {
+            ret = USB_RET_BABBLE;
+            goto out;
+        }
+
+        if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
+            *int_mask |= 0x02;
+            /* short packet: do not update QH */
+            DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token);
+            return 1;
+        }
+    }
+
+    /* success */
+    return 0;
+
+out:
+    switch(ret) {
+    case USB_RET_STALL:
+        td->ctrl |= TD_CTRL_STALL;
+        td->ctrl &= ~TD_CTRL_ACTIVE;
+        s->status |= UHCI_STS_USBERR;
+        if (td->ctrl & TD_CTRL_IOC) {
+            *int_mask |= 0x01;
+        }
+        uhci_update_irq(s);
+        return 1;
+
+    case USB_RET_BABBLE:
+        td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
+        td->ctrl &= ~TD_CTRL_ACTIVE;
+        s->status |= UHCI_STS_USBERR;
+        if (td->ctrl & TD_CTRL_IOC) {
+            *int_mask |= 0x01;
+        }
+        uhci_update_irq(s);
+        /* frame interrupted */
+        return -1;
+
+    case USB_RET_NAK:
+        td->ctrl |= TD_CTRL_NAK;
+        if (pid == USB_TOKEN_SETUP)
+            break;
+       return 1;
+
+    case USB_RET_IOERROR:
+    case USB_RET_NODEV:
+    default:
+       break;
+    }
+
+    /* Retry the TD if error count is not zero */
+
+    td->ctrl |= TD_CTRL_TIMEOUT;
+    err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3;
+    if (err != 0) {
+        err--;
+        if (err == 0) {
+            td->ctrl &= ~TD_CTRL_ACTIVE;
+            s->status |= UHCI_STS_USBERR;
+            if (td->ctrl & TD_CTRL_IOC)
+                *int_mask |= 0x01;
+            uhci_update_irq(s);
+        }
+    }
+    td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) |
+        (err << TD_CTRL_ERROR_SHIFT);
+    return 1;
+}
+
+static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
+{
+    UHCIAsync *async;
+    int len = 0, max_len;
+    uint8_t pid;
+    USBDevice *dev;
+    USBEndpoint *ep;
+
+    /* Is active ? */
+    if (!(td->ctrl & TD_CTRL_ACTIVE))
+        return 1;
+
+    async = uhci_async_find_td(s, addr, td);
+    if (async) {
+        /* Already submitted */
+        async->queue->valid = 32;
+
+        if (!async->done)
+            return 1;
+
+        uhci_async_unlink(async);
+        goto done;
+    }
+
+    /* Allocate new packet */
+    async = uhci_async_alloc(uhci_queue_get(s, td));
+    if (!async)
+        return 1;
+
+    /* valid needs to be large enough to handle 10 frame delay
+     * for initial isochronous requests
+     */
+    async->queue->valid = 32;
+    async->td    = addr;
+    async->isoc  = td->ctrl & TD_CTRL_IOS;
+
+    max_len = ((td->token >> 21) + 1) & 0x7ff;
+    pid = td->token & 0xff;
+
+    dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
+    ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
+    usb_packet_setup(&async->packet, pid, ep);
+    qemu_sglist_add(&async->sgl, td->buffer, max_len);
+    usb_packet_map(&async->packet, &async->sgl);
+
+    switch(pid) {
+    case USB_TOKEN_OUT:
+    case USB_TOKEN_SETUP:
+        len = usb_handle_packet(dev, &async->packet);
+        if (len >= 0)
+            len = max_len;
+        break;
+
+    case USB_TOKEN_IN:
+        len = usb_handle_packet(dev, &async->packet);
+        break;
+
+    default:
+        /* invalid pid : frame interrupted */
+        uhci_async_free(async);
+        s->status |= UHCI_STS_HCPERR;
+        uhci_update_irq(s);
+        return -1;
+    }
+    if (len == USB_RET_ASYNC) {
+        uhci_async_link(async);
+        return 2;
+    }
+
+    async->packet.result = len;
+
+done:
+    len = uhci_complete_td(s, td, async, int_mask);
+    usb_packet_unmap(&async->packet);
+    uhci_async_free(async);
+    return len;
+}
+
+static void uhci_async_complete(USBPort *port, USBPacket *packet)
+{
+    UHCIAsync *async = container_of(packet, UHCIAsync, packet);
+    UHCIState *s = async->queue->uhci;
+
+    DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
+
+    if (async->isoc) {
+        UHCI_TD td;
+        uint32_t link = async->td;
+        uint32_t int_mask = 0, val;
+
+        pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td));
+        le32_to_cpus(&td.link);
+        le32_to_cpus(&td.ctrl);
+        le32_to_cpus(&td.token);
+        le32_to_cpus(&td.buffer);
+
+        uhci_async_unlink(async);
+        uhci_complete_td(s, &td, async, &int_mask);
+        s->pending_int_mask |= int_mask;
+
+        /* update the status bits of the TD */
+        val = cpu_to_le32(td.ctrl);
+        pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
+        uhci_async_free(async);
+    } else {
+        async->done = 1;
+        uhci_process_frame(s);
+    }
+}
+
+static int is_valid(uint32_t link)
+{
+    return (link & 1) == 0;
+}
+
+static int is_qh(uint32_t link)
+{
+    return (link & 2) != 0;
+}
+
+static int depth_first(uint32_t link)
+{
+    return (link & 4) != 0;
+}
+
+/* QH DB used for detecting QH loops */
+#define UHCI_MAX_QUEUES 128
+typedef struct {
+    uint32_t addr[UHCI_MAX_QUEUES];
+    int      count;
+} QhDb;
+
+static void qhdb_reset(QhDb *db)
+{
+    db->count = 0;
+}
+
+/* Add QH to DB. Returns 1 if already present or DB is full. */
+static int qhdb_insert(QhDb *db, uint32_t addr)
+{
+    int i;
+    for (i = 0; i < db->count; i++)
+        if (db->addr[i] == addr)
+            return 1;
+
+    if (db->count >= UHCI_MAX_QUEUES)
+        return 1;
+
+    db->addr[db->count++] = addr;
+    return 0;
+}
+
+static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
+{
+    uint32_t int_mask = 0;
+    uint32_t plink = td->link;
+    uint32_t token = uhci_queue_token(td);
+    UHCI_TD ptd;
+    int ret;
+
+    while (is_valid(plink)) {
+        pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
+        le32_to_cpus(&ptd.link);
+        le32_to_cpus(&ptd.ctrl);
+        le32_to_cpus(&ptd.token);
+        le32_to_cpus(&ptd.buffer);
+        if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
+            break;
+        }
+        if (uhci_queue_token(&ptd) != token) {
+            break;
+        }
+        ret = uhci_handle_td(s, plink, &ptd, &int_mask);
+        assert(ret == 2); /* got USB_RET_ASYNC */
+        assert(int_mask == 0);
+        plink = ptd.link;
+    }
+}
+
+static void uhci_process_frame(UHCIState *s)
+{
+    uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
+    uint32_t curr_qh, td_count = 0, bytes_count = 0;
+    int cnt, ret;
+    UHCI_TD td;
+    UHCI_QH qh;
+    QhDb qhdb;
+
+    frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
+
+    DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr);
+
+    pci_dma_read(&s->dev, frame_addr, &link, 4);
+    le32_to_cpus(&link);
+
+    int_mask = 0;
+    curr_qh  = 0;
+
+    qhdb_reset(&qhdb);
+
+    for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
+        if (is_qh(link)) {
+            /* QH */
+
+            if (qhdb_insert(&qhdb, link)) {
+                /*
+                 * We're going in circles. Which is not a bug because
+                 * HCD is allowed to do that as part of the BW management.
+                 *
+                 * Stop processing here if
+                 *  (a) no transaction has been done since we've been
+                 *      here last time, or
+                 *  (b) we've reached the usb 1.1 bandwidth, which is
+                 *      1280 bytes/frame.
+                 */
+                DPRINTF("uhci: detected loop. qh 0x%x\n", link);
+                if (td_count == 0) {
+                    DPRINTF("uhci: no transaction last round, stop\n");
+                    break;
+                } else if (bytes_count >= 1280) {
+                    DPRINTF("uhci: bandwidth limit reached, stop\n");
+                    break;
+                } else {
+                    td_count = 0;
+                    qhdb_reset(&qhdb);
+                    qhdb_insert(&qhdb, link);
+                }
+            }
+
+            pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh));
+            le32_to_cpus(&qh.link);
+            le32_to_cpus(&qh.el_link);
+
+            DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n",
+                    link, qh.link, qh.el_link);
+
+            if (!is_valid(qh.el_link)) {
+                /* QH w/o elements */
+                curr_qh = 0;
+                link = qh.link;
+            } else {
+                /* QH with elements */
+               curr_qh = link;
+               link = qh.el_link;
+            }
+            continue;
+        }
+
+        /* TD */
+        pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td));
+        le32_to_cpus(&td.link);
+        le32_to_cpus(&td.ctrl);
+        le32_to_cpus(&td.token);
+        le32_to_cpus(&td.buffer);
+
+        DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
+                link, td.link, td.ctrl, td.token, curr_qh);
+
+        old_td_ctrl = td.ctrl;
+        ret = uhci_handle_td(s, link, &td, &int_mask);
+        if (old_td_ctrl != td.ctrl) {
+            /* update the status bits of the TD */
+            val = cpu_to_le32(td.ctrl);
+            pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
+        }
+
+        switch (ret) {
+        case -1: /* interrupted frame */
+            goto out;
+
+        case 1: /* goto next queue */
+            DPRINTF("uhci: TD 0x%x skip. "
+                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+                    link, td.link, td.ctrl, td.token, curr_qh);
+            link = curr_qh ? qh.link : td.link;
+            continue;
+
+        case 2: /* got USB_RET_ASYNC */
+            DPRINTF("uhci: TD 0x%x async. "
+                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+                    link, td.link, td.ctrl, td.token, curr_qh);
+            if (is_valid(td.link)) {
+                uhci_fill_queue(s, &td);
+            }
+            link = curr_qh ? qh.link : td.link;
+            continue;
+
+        case 0: /* completed TD */
+            DPRINTF("uhci: TD 0x%x done. "
+                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+                    link, td.link, td.ctrl, td.token, curr_qh);
+
+            link = td.link;
+            td_count++;
+            bytes_count += (td.ctrl & 0x7ff) + 1;
+
+            if (curr_qh) {
+                /* update QH element link */
+                qh.el_link = link;
+                val = cpu_to_le32(qh.el_link);
+                pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
+
+                if (!depth_first(link)) {
+                    /* done with this QH */
+
+                    DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
+                            curr_qh, qh.link, qh.el_link);
+
+                    curr_qh = 0;
+                    link    = qh.link;
+                }
+            }
+            break;
+
+        default:
+            assert(!"unknown return code");
+        }
+
+        /* go to the next entry */
+    }
+
+out:
+    s->pending_int_mask |= int_mask;
+}
+
+static void uhci_frame_timer(void *opaque)
+{
+    UHCIState *s = opaque;
+
+    /* prepare the timer for the next frame */
+    s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+
+    if (!(s->cmd & UHCI_CMD_RS)) {
+        /* Full stop */
+        qemu_del_timer(s->frame_timer);
+        /* set hchalted bit in status - UHCI11D 2.1.2 */
+        s->status |= UHCI_STS_HCHALTED;
+
+        DPRINTF("uhci: halted\n");
+        return;
+    }
+
+    /* Complete the previous frame */
+    if (s->pending_int_mask) {
+        s->status2 |= s->pending_int_mask;
+        s->status  |= UHCI_STS_USBINT;
+        uhci_update_irq(s);
+    }
+    s->pending_int_mask = 0;
+
+    /* Start new frame */
+    s->frnum = (s->frnum + 1) & 0x7ff;
+
+    DPRINTF("uhci: new frame #%u\n" , s->frnum);
+
+    uhci_async_validate_begin(s);
+
+    uhci_process_frame(s);
+
+    uhci_async_validate_end(s);
+
+    qemu_mod_timer(s->frame_timer, s->expire_time);
+}
+
+static const MemoryRegionPortio uhci_portio[] = {
+    { 0, 32, 2, .write = uhci_ioport_writew, },
+    { 0, 32, 2, .read = uhci_ioport_readw, },
+    { 0, 32, 4, .write = uhci_ioport_writel, },
+    { 0, 32, 4, .read = uhci_ioport_readl, },
+    { 0, 32, 1, .write = uhci_ioport_writeb, },
+    { 0, 32, 1, .read = uhci_ioport_readb, },
+    PORTIO_END_OF_LIST()
+};
+
+static const MemoryRegionOps uhci_ioport_ops = {
+    .old_portio = uhci_portio,
+};
+
+static USBPortOps uhci_port_ops = {
+    .attach = uhci_attach,
+    .detach = uhci_detach,
+    .child_detach = uhci_child_detach,
+    .wakeup = uhci_wakeup,
+    .complete = uhci_async_complete,
+};
+
+static USBBusOps uhci_bus_ops = {
+};
+
+static int usb_uhci_common_initfn(PCIDevice *dev)
+{
+    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+    uint8_t *pci_conf = s->dev.config;
+    int i;
+
+    pci_conf[PCI_CLASS_PROG] = 0x00;
+    /* TODO: reset value should be 0. */
+    pci_conf[PCI_INTERRUPT_PIN] = 4; /* interrupt pin D */
+    pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
+
+    if (s->masterbus) {
+        USBPort *ports[NB_PORTS];
+        for(i = 0; i < NB_PORTS; i++) {
+            ports[i] = &s->ports[i].port;
+        }
+        if (usb_register_companion(s->masterbus, ports, NB_PORTS,
+                s->firstport, s, &uhci_port_ops,
+                USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
+            return -1;
+        }
+    } else {
+        usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
+        for (i = 0; i < NB_PORTS; i++) {
+            usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
+                              USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+        }
+    }
+    s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
+    s->num_ports_vmstate = NB_PORTS;
+    QTAILQ_INIT(&s->queues);
+
+    qemu_register_reset(uhci_reset, s);
+
+    memory_region_init_io(&s->io_bar, &uhci_ioport_ops, s, "uhci", 0x20);
+    /* Use region 4 for consistency with real hardware.  BSD guests seem
+       to rely on this.  */
+    pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
+
+    return 0;
+}
+
+static int usb_uhci_vt82c686b_initfn(PCIDevice *dev)
+{
+    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+    uint8_t *pci_conf = s->dev.config;
+
+    /* USB misc control 1/2 */
+    pci_set_long(pci_conf + 0x40,0x00001000);
+    /* PM capability */
+    pci_set_long(pci_conf + 0x80,0x00020001);
+    /* USB legacy support  */
+    pci_set_long(pci_conf + 0xc0,0x00002000);
+
+    return usb_uhci_common_initfn(dev);
+}
+
+static int usb_uhci_exit(PCIDevice *dev)
+{
+    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+
+    memory_region_destroy(&s->io_bar);
+    return 0;
+}
+
+static Property uhci_properties[] = {
+    DEFINE_PROP_STRING("masterbus", UHCIState, masterbus),
+    DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void piix3_uhci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_uhci_common_initfn;
+    k->exit = usb_uhci_exit;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82371SB_2;
+    k->revision = 0x01;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_uhci;
+    dc->props = uhci_properties;
+}
+
+static TypeInfo piix3_uhci_info = {
+    .name          = "piix3-usb-uhci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(UHCIState),
+    .class_init    = piix3_uhci_class_init,
+};
+
+static void piix4_uhci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_uhci_common_initfn;
+    k->exit = usb_uhci_exit;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82371AB_2;
+    k->revision = 0x01;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_uhci;
+    dc->props = uhci_properties;
+}
+
+static TypeInfo piix4_uhci_info = {
+    .name          = "piix4-usb-uhci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(UHCIState),
+    .class_init    = piix4_uhci_class_init,
+};
+
+static void vt82c686b_uhci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_uhci_vt82c686b_initfn;
+    k->exit = usb_uhci_exit;
+    k->vendor_id = PCI_VENDOR_ID_VIA;
+    k->device_id = PCI_DEVICE_ID_VIA_UHCI;
+    k->revision = 0x01;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_uhci;
+    dc->props = uhci_properties;
+}
+
+static TypeInfo vt82c686b_uhci_info = {
+    .name          = "vt82c686b-usb-uhci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(UHCIState),
+    .class_init    = vt82c686b_uhci_class_init,
+};
+
+static void ich9_uhci1_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_uhci_common_initfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1;
+    k->revision = 0x03;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_uhci;
+    dc->props = uhci_properties;
+}
+
+static TypeInfo ich9_uhci1_info = {
+    .name          = "ich9-usb-uhci1",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(UHCIState),
+    .class_init    = ich9_uhci1_class_init,
+};
+
+static void ich9_uhci2_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_uhci_common_initfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2;
+    k->revision = 0x03;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_uhci;
+    dc->props = uhci_properties;
+}
+
+static TypeInfo ich9_uhci2_info = {
+    .name          = "ich9-usb-uhci2",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(UHCIState),
+    .class_init    = ich9_uhci2_class_init,
+};
+
+static void ich9_uhci3_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = usb_uhci_common_initfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3;
+    k->revision = 0x03;
+    k->class_id = PCI_CLASS_SERIAL_USB;
+    dc->vmsd = &vmstate_uhci;
+    dc->props = uhci_properties;
+}
+
+static TypeInfo ich9_uhci3_info = {
+    .name          = "ich9-usb-uhci3",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(UHCIState),
+    .class_init    = ich9_uhci3_class_init,
+};
+
+static void uhci_register_types(void)
+{
+    type_register_static(&piix3_uhci_info);
+    type_register_static(&piix4_uhci_info);
+    type_register_static(&vt82c686b_uhci_info);
+    type_register_static(&ich9_uhci1_info);
+    type_register_static(&ich9_uhci2_info);
+    type_register_static(&ich9_uhci3_info);
+}
+
+type_init(uhci_register_types)
+
+void usb_uhci_piix3_init(PCIBus *bus, int devfn)
+{
+    pci_create_simple(bus, devfn, "piix3-usb-uhci");
+}
+
+void usb_uhci_piix4_init(PCIBus *bus, int devfn)
+{
+    pci_create_simple(bus, devfn, "piix4-usb-uhci");
+}
+
+void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn)
+{
+    pci_create_simple(bus, devfn, "vt82c686b-usb-uhci");
+}
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
new file mode 100644 (file)
index 0000000..73b0c7f
--- /dev/null
@@ -0,0 +1,2925 @@
+/*
+ * USB xHCI controller emulation
+ *
+ * Copyright (c) 2011 Securiforest
+ * Date: 2011-05-11 ;  Author: Hector Martin <hector@marcansoft.com>
+ * Based on usb-ohci.c, emulates Renesas NEC USB 3.0
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "hw/hw.h"
+#include "qemu-timer.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
+#include "hw/qdev-addr.h"
+#include "hw/msi.h"
+
+//#define DEBUG_XHCI
+//#define DEBUG_DATA
+
+#ifdef DEBUG_XHCI
+#define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINTF(...) do {} while (0)
+#endif
+#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
+                             __func__, __LINE__); abort(); } while (0)
+
+#define MAXSLOTS 8
+#define MAXINTRS 1
+
+#define USB2_PORTS 4
+#define USB3_PORTS 4
+
+#define MAXPORTS (USB2_PORTS+USB3_PORTS)
+
+#define TD_QUEUE 24
+#define BG_XFERS 8
+#define BG_PKTS 8
+
+/* Very pessimistic, let's hope it's enough for all cases */
+#define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS)
+/* Do not deliver ER Full events. NEC's driver does some things not bound
+ * to the specs when it gets them */
+#define ER_FULL_HACK
+
+#define LEN_CAP         0x40
+#define OFF_OPER        LEN_CAP
+#define LEN_OPER        (0x400 + 0x10 * MAXPORTS)
+#define OFF_RUNTIME     ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f)
+#define LEN_RUNTIME     (0x20 + MAXINTRS * 0x20)
+#define OFF_DOORBELL    (OFF_RUNTIME + LEN_RUNTIME)
+#define LEN_DOORBELL    ((MAXSLOTS + 1) * 0x20)
+
+/* must be power of 2 */
+#define LEN_REGS        0x2000
+
+#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS
+# error Increase LEN_REGS
+#endif
+
+#if MAXINTRS > 1
+# error TODO: only one interrupter supported
+#endif
+
+/* bit definitions */
+#define USBCMD_RS       (1<<0)
+#define USBCMD_HCRST    (1<<1)
+#define USBCMD_INTE     (1<<2)
+#define USBCMD_HSEE     (1<<3)
+#define USBCMD_LHCRST   (1<<7)
+#define USBCMD_CSS      (1<<8)
+#define USBCMD_CRS      (1<<9)
+#define USBCMD_EWE      (1<<10)
+#define USBCMD_EU3S     (1<<11)
+
+#define USBSTS_HCH      (1<<0)
+#define USBSTS_HSE      (1<<2)
+#define USBSTS_EINT     (1<<3)
+#define USBSTS_PCD      (1<<4)
+#define USBSTS_SSS      (1<<8)
+#define USBSTS_RSS      (1<<9)
+#define USBSTS_SRE      (1<<10)
+#define USBSTS_CNR      (1<<11)
+#define USBSTS_HCE      (1<<12)
+
+
+#define PORTSC_CCS          (1<<0)
+#define PORTSC_PED          (1<<1)
+#define PORTSC_OCA          (1<<3)
+#define PORTSC_PR           (1<<4)
+#define PORTSC_PLS_SHIFT        5
+#define PORTSC_PLS_MASK     0xf
+#define PORTSC_PP           (1<<9)
+#define PORTSC_SPEED_SHIFT      10
+#define PORTSC_SPEED_MASK   0xf
+#define PORTSC_SPEED_FULL   (1<<10)
+#define PORTSC_SPEED_LOW    (2<<10)
+#define PORTSC_SPEED_HIGH   (3<<10)
+#define PORTSC_SPEED_SUPER  (4<<10)
+#define PORTSC_PIC_SHIFT        14
+#define PORTSC_PIC_MASK     0x3
+#define PORTSC_LWS          (1<<16)
+#define PORTSC_CSC          (1<<17)
+#define PORTSC_PEC          (1<<18)
+#define PORTSC_WRC          (1<<19)
+#define PORTSC_OCC          (1<<20)
+#define PORTSC_PRC          (1<<21)
+#define PORTSC_PLC          (1<<22)
+#define PORTSC_CEC          (1<<23)
+#define PORTSC_CAS          (1<<24)
+#define PORTSC_WCE          (1<<25)
+#define PORTSC_WDE          (1<<26)
+#define PORTSC_WOE          (1<<27)
+#define PORTSC_DR           (1<<30)
+#define PORTSC_WPR          (1<<31)
+
+#define CRCR_RCS        (1<<0)
+#define CRCR_CS         (1<<1)
+#define CRCR_CA         (1<<2)
+#define CRCR_CRR        (1<<3)
+
+#define IMAN_IP         (1<<0)
+#define IMAN_IE         (1<<1)
+
+#define ERDP_EHB        (1<<3)
+
+#define TRB_SIZE 16
+typedef struct XHCITRB {
+    uint64_t parameter;
+    uint32_t status;
+    uint32_t control;
+    target_phys_addr_t addr;
+    bool ccs;
+} XHCITRB;
+
+
+typedef enum TRBType {
+    TRB_RESERVED = 0,
+    TR_NORMAL,
+    TR_SETUP,
+    TR_DATA,
+    TR_STATUS,
+    TR_ISOCH,
+    TR_LINK,
+    TR_EVDATA,
+    TR_NOOP,
+    CR_ENABLE_SLOT,
+    CR_DISABLE_SLOT,
+    CR_ADDRESS_DEVICE,
+    CR_CONFIGURE_ENDPOINT,
+    CR_EVALUATE_CONTEXT,
+    CR_RESET_ENDPOINT,
+    CR_STOP_ENDPOINT,
+    CR_SET_TR_DEQUEUE,
+    CR_RESET_DEVICE,
+    CR_FORCE_EVENT,
+    CR_NEGOTIATE_BW,
+    CR_SET_LATENCY_TOLERANCE,
+    CR_GET_PORT_BANDWIDTH,
+    CR_FORCE_HEADER,
+    CR_NOOP,
+    ER_TRANSFER = 32,
+    ER_COMMAND_COMPLETE,
+    ER_PORT_STATUS_CHANGE,
+    ER_BANDWIDTH_REQUEST,
+    ER_DOORBELL,
+    ER_HOST_CONTROLLER,
+    ER_DEVICE_NOTIFICATION,
+    ER_MFINDEX_WRAP,
+    /* vendor specific bits */
+    CR_VENDOR_VIA_CHALLENGE_RESPONSE = 48,
+    CR_VENDOR_NEC_FIRMWARE_REVISION  = 49,
+    CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50,
+} TRBType;
+
+#define CR_LINK TR_LINK
+
+typedef enum TRBCCode {
+    CC_INVALID = 0,
+    CC_SUCCESS,
+    CC_DATA_BUFFER_ERROR,
+    CC_BABBLE_DETECTED,
+    CC_USB_TRANSACTION_ERROR,
+    CC_TRB_ERROR,
+    CC_STALL_ERROR,
+    CC_RESOURCE_ERROR,
+    CC_BANDWIDTH_ERROR,
+    CC_NO_SLOTS_ERROR,
+    CC_INVALID_STREAM_TYPE_ERROR,
+    CC_SLOT_NOT_ENABLED_ERROR,
+    CC_EP_NOT_ENABLED_ERROR,
+    CC_SHORT_PACKET,
+    CC_RING_UNDERRUN,
+    CC_RING_OVERRUN,
+    CC_VF_ER_FULL,
+    CC_PARAMETER_ERROR,
+    CC_BANDWIDTH_OVERRUN,
+    CC_CONTEXT_STATE_ERROR,
+    CC_NO_PING_RESPONSE_ERROR,
+    CC_EVENT_RING_FULL_ERROR,
+    CC_INCOMPATIBLE_DEVICE_ERROR,
+    CC_MISSED_SERVICE_ERROR,
+    CC_COMMAND_RING_STOPPED,
+    CC_COMMAND_ABORTED,
+    CC_STOPPED,
+    CC_STOPPED_LENGTH_INVALID,
+    CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29,
+    CC_ISOCH_BUFFER_OVERRUN = 31,
+    CC_EVENT_LOST_ERROR,
+    CC_UNDEFINED_ERROR,
+    CC_INVALID_STREAM_ID_ERROR,
+    CC_SECONDARY_BANDWIDTH_ERROR,
+    CC_SPLIT_TRANSACTION_ERROR
+} TRBCCode;
+
+#define TRB_C               (1<<0)
+#define TRB_TYPE_SHIFT          10
+#define TRB_TYPE_MASK       0x3f
+#define TRB_TYPE(t)         (((t).control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
+
+#define TRB_EV_ED           (1<<2)
+
+#define TRB_TR_ENT          (1<<1)
+#define TRB_TR_ISP          (1<<2)
+#define TRB_TR_NS           (1<<3)
+#define TRB_TR_CH           (1<<4)
+#define TRB_TR_IOC          (1<<5)
+#define TRB_TR_IDT          (1<<6)
+#define TRB_TR_TBC_SHIFT        7
+#define TRB_TR_TBC_MASK     0x3
+#define TRB_TR_BEI          (1<<9)
+#define TRB_TR_TLBPC_SHIFT      16
+#define TRB_TR_TLBPC_MASK   0xf
+#define TRB_TR_FRAMEID_SHIFT    20
+#define TRB_TR_FRAMEID_MASK 0x7ff
+#define TRB_TR_SIA          (1<<31)
+
+#define TRB_TR_DIR          (1<<16)
+
+#define TRB_CR_SLOTID_SHIFT     24
+#define TRB_CR_SLOTID_MASK  0xff
+#define TRB_CR_EPID_SHIFT       16
+#define TRB_CR_EPID_MASK    0x1f
+
+#define TRB_CR_BSR          (1<<9)
+#define TRB_CR_DC           (1<<9)
+
+#define TRB_LK_TC           (1<<1)
+
+#define EP_TYPE_MASK        0x7
+#define EP_TYPE_SHIFT           3
+
+#define EP_STATE_MASK       0x7
+#define EP_DISABLED         (0<<0)
+#define EP_RUNNING          (1<<0)
+#define EP_HALTED           (2<<0)
+#define EP_STOPPED          (3<<0)
+#define EP_ERROR            (4<<0)
+
+#define SLOT_STATE_MASK     0x1f
+#define SLOT_STATE_SHIFT        27
+#define SLOT_STATE(s)       (((s)>>SLOT_STATE_SHIFT)&SLOT_STATE_MASK)
+#define SLOT_ENABLED        0
+#define SLOT_DEFAULT        1
+#define SLOT_ADDRESSED      2
+#define SLOT_CONFIGURED     3
+
+#define SLOT_CONTEXT_ENTRIES_MASK 0x1f
+#define SLOT_CONTEXT_ENTRIES_SHIFT 27
+
+typedef enum EPType {
+    ET_INVALID = 0,
+    ET_ISO_OUT,
+    ET_BULK_OUT,
+    ET_INTR_OUT,
+    ET_CONTROL,
+    ET_ISO_IN,
+    ET_BULK_IN,
+    ET_INTR_IN,
+} EPType;
+
+typedef struct XHCIRing {
+    target_phys_addr_t base;
+    target_phys_addr_t dequeue;
+    bool ccs;
+} XHCIRing;
+
+typedef struct XHCIPort {
+    USBPort port;
+    uint32_t portsc;
+} XHCIPort;
+
+struct XHCIState;
+typedef struct XHCIState XHCIState;
+
+typedef struct XHCITransfer {
+    XHCIState *xhci;
+    USBPacket packet;
+    bool running_async;
+    bool running_retry;
+    bool cancelled;
+    bool complete;
+    bool backgrounded;
+    unsigned int iso_pkts;
+    unsigned int slotid;
+    unsigned int epid;
+    bool in_xfer;
+    bool iso_xfer;
+    bool bg_xfer;
+
+    unsigned int trb_count;
+    unsigned int trb_alloced;
+    XHCITRB *trbs;
+
+    unsigned int data_length;
+    unsigned int data_alloced;
+    uint8_t *data;
+
+    TRBCCode status;
+
+    unsigned int pkts;
+    unsigned int pktsize;
+    unsigned int cur_pkt;
+} XHCITransfer;
+
+typedef struct XHCIEPContext {
+    XHCIRing ring;
+    unsigned int next_xfer;
+    unsigned int comp_xfer;
+    XHCITransfer transfers[TD_QUEUE];
+    XHCITransfer *retry;
+    bool bg_running;
+    bool bg_updating;
+    unsigned int next_bg;
+    XHCITransfer bg_transfers[BG_XFERS];
+    EPType type;
+    target_phys_addr_t pctx;
+    unsigned int max_psize;
+    bool has_bg;
+    uint32_t state;
+} XHCIEPContext;
+
+typedef struct XHCISlot {
+    bool enabled;
+    target_phys_addr_t ctx;
+    unsigned int port;
+    unsigned int devaddr;
+    XHCIEPContext * eps[31];
+} XHCISlot;
+
+typedef struct XHCIEvent {
+    TRBType type;
+    TRBCCode ccode;
+    uint64_t ptr;
+    uint32_t length;
+    uint32_t flags;
+    uint8_t slotid;
+    uint8_t epid;
+} XHCIEvent;
+
+struct XHCIState {
+    PCIDevice pci_dev;
+    USBBus bus;
+    qemu_irq irq;
+    MemoryRegion mem;
+    const char *name;
+    uint32_t msi;
+    unsigned int devaddr;
+
+    /* Operational Registers */
+    uint32_t usbcmd;
+    uint32_t usbsts;
+    uint32_t dnctrl;
+    uint32_t crcr_low;
+    uint32_t crcr_high;
+    uint32_t dcbaap_low;
+    uint32_t dcbaap_high;
+    uint32_t config;
+
+    XHCIPort ports[MAXPORTS];
+    XHCISlot slots[MAXSLOTS];
+
+    /* Runtime Registers */
+    uint32_t mfindex;
+    /* note: we only support one interrupter */
+    uint32_t iman;
+    uint32_t imod;
+    uint32_t erstsz;
+    uint32_t erstba_low;
+    uint32_t erstba_high;
+    uint32_t erdp_low;
+    uint32_t erdp_high;
+
+    target_phys_addr_t er_start;
+    uint32_t er_size;
+    bool er_pcs;
+    unsigned int er_ep_idx;
+    bool er_full;
+
+    XHCIEvent ev_buffer[EV_QUEUE];
+    unsigned int ev_buffer_put;
+    unsigned int ev_buffer_get;
+
+    XHCIRing cmd_ring;
+};
+
+typedef struct XHCIEvRingSeg {
+    uint32_t addr_low;
+    uint32_t addr_high;
+    uint32_t size;
+    uint32_t rsvd;
+} XHCIEvRingSeg;
+
+#ifdef DEBUG_XHCI
+static const char *TRBType_names[] = {
+    [TRB_RESERVED]                     = "TRB_RESERVED",
+    [TR_NORMAL]                        = "TR_NORMAL",
+    [TR_SETUP]                         = "TR_SETUP",
+    [TR_DATA]                          = "TR_DATA",
+    [TR_STATUS]                        = "TR_STATUS",
+    [TR_ISOCH]                         = "TR_ISOCH",
+    [TR_LINK]                          = "TR_LINK",
+    [TR_EVDATA]                        = "TR_EVDATA",
+    [TR_NOOP]                          = "TR_NOOP",
+    [CR_ENABLE_SLOT]                   = "CR_ENABLE_SLOT",
+    [CR_DISABLE_SLOT]                  = "CR_DISABLE_SLOT",
+    [CR_ADDRESS_DEVICE]                = "CR_ADDRESS_DEVICE",
+    [CR_CONFIGURE_ENDPOINT]            = "CR_CONFIGURE_ENDPOINT",
+    [CR_EVALUATE_CONTEXT]              = "CR_EVALUATE_CONTEXT",
+    [CR_RESET_ENDPOINT]                = "CR_RESET_ENDPOINT",
+    [CR_STOP_ENDPOINT]                 = "CR_STOP_ENDPOINT",
+    [CR_SET_TR_DEQUEUE]                = "CR_SET_TR_DEQUEUE",
+    [CR_RESET_DEVICE]                  = "CR_RESET_DEVICE",
+    [CR_FORCE_EVENT]                   = "CR_FORCE_EVENT",
+    [CR_NEGOTIATE_BW]                  = "CR_NEGOTIATE_BW",
+    [CR_SET_LATENCY_TOLERANCE]         = "CR_SET_LATENCY_TOLERANCE",
+    [CR_GET_PORT_BANDWIDTH]            = "CR_GET_PORT_BANDWIDTH",
+    [CR_FORCE_HEADER]                  = "CR_FORCE_HEADER",
+    [CR_NOOP]                          = "CR_NOOP",
+    [ER_TRANSFER]                      = "ER_TRANSFER",
+    [ER_COMMAND_COMPLETE]              = "ER_COMMAND_COMPLETE",
+    [ER_PORT_STATUS_CHANGE]            = "ER_PORT_STATUS_CHANGE",
+    [ER_BANDWIDTH_REQUEST]             = "ER_BANDWIDTH_REQUEST",
+    [ER_DOORBELL]                      = "ER_DOORBELL",
+    [ER_HOST_CONTROLLER]               = "ER_HOST_CONTROLLER",
+    [ER_DEVICE_NOTIFICATION]           = "ER_DEVICE_NOTIFICATION",
+    [ER_MFINDEX_WRAP]                  = "ER_MFINDEX_WRAP",
+    [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE",
+    [CR_VENDOR_NEC_FIRMWARE_REVISION]  = "CR_VENDOR_NEC_FIRMWARE_REVISION",
+    [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
+};
+
+static const char *lookup_name(uint32_t index, const char **list, uint32_t llen)
+{
+    if (index >= llen || list[index] == NULL) {
+        return "???";
+    }
+    return list[index];
+}
+
+static const char *trb_name(XHCITRB *trb)
+{
+    return lookup_name(TRB_TYPE(*trb), TRBType_names,
+                       ARRAY_SIZE(TRBType_names));
+}
+#endif
+
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+                         unsigned int epid);
+
+static inline target_phys_addr_t xhci_addr64(uint32_t low, uint32_t high)
+{
+#if TARGET_PHYS_ADDR_BITS > 32
+    return low | ((target_phys_addr_t)high << 32);
+#else
+    return low;
+#endif
+}
+
+static inline target_phys_addr_t xhci_mask64(uint64_t addr)
+{
+#if TARGET_PHYS_ADDR_BITS > 32
+    return addr;
+#else
+    return addr & 0xffffffff;
+#endif
+}
+
+static void xhci_irq_update(XHCIState *xhci)
+{
+    int level = 0;
+
+    if (xhci->iman & IMAN_IP && xhci->iman & IMAN_IE &&
+        xhci->usbcmd && USBCMD_INTE) {
+        level = 1;
+    }
+
+    DPRINTF("xhci_irq_update(): %d\n", level);
+
+    if (xhci->msi && msi_enabled(&xhci->pci_dev)) {
+        if (level) {
+            DPRINTF("xhci_irq_update(): MSI signal\n");
+            msi_notify(&xhci->pci_dev, 0);
+        }
+    } else {
+        qemu_set_irq(xhci->irq, level);
+    }
+}
+
+static inline int xhci_running(XHCIState *xhci)
+{
+    return !(xhci->usbsts & USBSTS_HCH) && !xhci->er_full;
+}
+
+static void xhci_die(XHCIState *xhci)
+{
+    xhci->usbsts |= USBSTS_HCE;
+    fprintf(stderr, "xhci: asserted controller error\n");
+}
+
+static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
+{
+    XHCITRB ev_trb;
+    target_phys_addr_t addr;
+
+    ev_trb.parameter = cpu_to_le64(event->ptr);
+    ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24));
+    ev_trb.control = (event->slotid << 24) | (event->epid << 16) |
+                     event->flags | (event->type << TRB_TYPE_SHIFT);
+    if (xhci->er_pcs) {
+        ev_trb.control |= TRB_C;
+    }
+    ev_trb.control = cpu_to_le32(ev_trb.control);
+
+    DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n",
+            xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control,
+            trb_name(&ev_trb));
+
+    addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
+    cpu_physical_memory_write(addr, (uint8_t *) &ev_trb, TRB_SIZE);
+
+    xhci->er_ep_idx++;
+    if (xhci->er_ep_idx >= xhci->er_size) {
+        xhci->er_ep_idx = 0;
+        xhci->er_pcs = !xhci->er_pcs;
+    }
+}
+
+static void xhci_events_update(XHCIState *xhci)
+{
+    target_phys_addr_t erdp;
+    unsigned int dp_idx;
+    bool do_irq = 0;
+
+    if (xhci->usbsts & USBSTS_HCH) {
+        return;
+    }
+
+    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
+    if (erdp < xhci->er_start ||
+        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
+        fprintf(stderr, "xhci: ERDP out of bounds: "TARGET_FMT_plx"\n", erdp);
+        fprintf(stderr, "xhci: ER at "TARGET_FMT_plx" len %d\n",
+                xhci->er_start, xhci->er_size);
+        xhci_die(xhci);
+        return;
+    }
+    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
+    assert(dp_idx < xhci->er_size);
+
+    /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus
+     * deadlocks when the ER is full. Hack it by holding off events until
+     * the driver decides to free at least half of the ring */
+    if (xhci->er_full) {
+        int er_free = dp_idx - xhci->er_ep_idx;
+        if (er_free <= 0) {
+            er_free += xhci->er_size;
+        }
+        if (er_free < (xhci->er_size/2)) {
+            DPRINTF("xhci_events_update(): event ring still "
+                    "more than half full (hack)\n");
+            return;
+        }
+    }
+
+    while (xhci->ev_buffer_put != xhci->ev_buffer_get) {
+        assert(xhci->er_full);
+        if (((xhci->er_ep_idx+1) % xhci->er_size) == dp_idx) {
+            DPRINTF("xhci_events_update(): event ring full again\n");
+#ifndef ER_FULL_HACK
+            XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
+            xhci_write_event(xhci, &full);
+#endif
+            do_irq = 1;
+            break;
+        }
+        XHCIEvent *event = &xhci->ev_buffer[xhci->ev_buffer_get];
+        xhci_write_event(xhci, event);
+        xhci->ev_buffer_get++;
+        do_irq = 1;
+        if (xhci->ev_buffer_get == EV_QUEUE) {
+            xhci->ev_buffer_get = 0;
+        }
+    }
+
+    if (do_irq) {
+        xhci->erdp_low |= ERDP_EHB;
+        xhci->iman |= IMAN_IP;
+        xhci->usbsts |= USBSTS_EINT;
+        xhci_irq_update(xhci);
+    }
+
+    if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) {
+        DPRINTF("xhci_events_update(): event ring no longer full\n");
+        xhci->er_full = 0;
+    }
+    return;
+}
+
+static void xhci_event(XHCIState *xhci, XHCIEvent *event)
+{
+    target_phys_addr_t erdp;
+    unsigned int dp_idx;
+
+    if (xhci->er_full) {
+        DPRINTF("xhci_event(): ER full, queueing\n");
+        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
+            fprintf(stderr, "xhci: event queue full, dropping event!\n");
+            return;
+        }
+        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
+        if (xhci->ev_buffer_put == EV_QUEUE) {
+            xhci->ev_buffer_put = 0;
+        }
+        return;
+    }
+
+    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
+    if (erdp < xhci->er_start ||
+        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
+        fprintf(stderr, "xhci: ERDP out of bounds: "TARGET_FMT_plx"\n", erdp);
+        fprintf(stderr, "xhci: ER at "TARGET_FMT_plx" len %d\n",
+                xhci->er_start, xhci->er_size);
+        xhci_die(xhci);
+        return;
+    }
+
+    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
+    assert(dp_idx < xhci->er_size);
+
+    if ((xhci->er_ep_idx+1) % xhci->er_size == dp_idx) {
+        DPRINTF("xhci_event(): ER full, queueing\n");
+#ifndef ER_FULL_HACK
+        XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
+        xhci_write_event(xhci, &full);
+#endif
+        xhci->er_full = 1;
+        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
+            fprintf(stderr, "xhci: event queue full, dropping event!\n");
+            return;
+        }
+        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
+        if (xhci->ev_buffer_put == EV_QUEUE) {
+            xhci->ev_buffer_put = 0;
+        }
+    } else {
+        xhci_write_event(xhci, event);
+    }
+
+    xhci->erdp_low |= ERDP_EHB;
+    xhci->iman |= IMAN_IP;
+    xhci->usbsts |= USBSTS_EINT;
+
+    xhci_irq_update(xhci);
+}
+
+static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
+                           target_phys_addr_t base)
+{
+    ring->base = base;
+    ring->dequeue = base;
+    ring->ccs = 1;
+}
+
+static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
+                               target_phys_addr_t *addr)
+{
+    while (1) {
+        TRBType type;
+        cpu_physical_memory_read(ring->dequeue, (uint8_t *) trb, TRB_SIZE);
+        trb->addr = ring->dequeue;
+        trb->ccs = ring->ccs;
+        le64_to_cpus(&trb->parameter);
+        le32_to_cpus(&trb->status);
+        le32_to_cpus(&trb->control);
+
+        DPRINTF("xhci: TRB fetched [" TARGET_FMT_plx "]: "
+                "%016" PRIx64 " %08x %08x %s\n",
+                ring->dequeue, trb->parameter, trb->status, trb->control,
+                trb_name(trb));
+
+        if ((trb->control & TRB_C) != ring->ccs) {
+            return 0;
+        }
+
+        type = TRB_TYPE(*trb);
+
+        if (type != TR_LINK) {
+            if (addr) {
+                *addr = ring->dequeue;
+            }
+            ring->dequeue += TRB_SIZE;
+            return type;
+        } else {
+            ring->dequeue = xhci_mask64(trb->parameter);
+            if (trb->control & TRB_LK_TC) {
+                ring->ccs = !ring->ccs;
+            }
+        }
+    }
+}
+
+static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
+{
+    XHCITRB trb;
+    int length = 0;
+    target_phys_addr_t dequeue = ring->dequeue;
+    bool ccs = ring->ccs;
+    /* hack to bundle together the two/three TDs that make a setup transfer */
+    bool control_td_set = 0;
+
+    while (1) {
+        TRBType type;
+        cpu_physical_memory_read(dequeue, (uint8_t *) &trb, TRB_SIZE);
+        le64_to_cpus(&trb.parameter);
+        le32_to_cpus(&trb.status);
+        le32_to_cpus(&trb.control);
+
+        DPRINTF("xhci: TRB peeked [" TARGET_FMT_plx "]: "
+                "%016" PRIx64 " %08x %08x\n",
+                dequeue, trb.parameter, trb.status, trb.control);
+
+        if ((trb.control & TRB_C) != ccs) {
+            return -length;
+        }
+
+        type = TRB_TYPE(trb);
+
+        if (type == TR_LINK) {
+            dequeue = xhci_mask64(trb.parameter);
+            if (trb.control & TRB_LK_TC) {
+                ccs = !ccs;
+            }
+            continue;
+        }
+
+        length += 1;
+        dequeue += TRB_SIZE;
+
+        if (type == TR_SETUP) {
+            control_td_set = 1;
+        } else if (type == TR_STATUS) {
+            control_td_set = 0;
+        }
+
+        if (!control_td_set && !(trb.control & TRB_TR_CH)) {
+            return length;
+        }
+    }
+}
+
+static void xhci_er_reset(XHCIState *xhci)
+{
+    XHCIEvRingSeg seg;
+
+    /* cache the (sole) event ring segment location */
+    if (xhci->erstsz != 1) {
+        fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", xhci->erstsz);
+        xhci_die(xhci);
+        return;
+    }
+    target_phys_addr_t erstba = xhci_addr64(xhci->erstba_low, xhci->erstba_high);
+    cpu_physical_memory_read(erstba, (uint8_t *) &seg, sizeof(seg));
+    le32_to_cpus(&seg.addr_low);
+    le32_to_cpus(&seg.addr_high);
+    le32_to_cpus(&seg.size);
+    if (seg.size < 16 || seg.size > 4096) {
+        fprintf(stderr, "xhci: invalid value for segment size: %d\n", seg.size);
+        xhci_die(xhci);
+        return;
+    }
+    xhci->er_start = xhci_addr64(seg.addr_low, seg.addr_high);
+    xhci->er_size = seg.size;
+
+    xhci->er_ep_idx = 0;
+    xhci->er_pcs = 1;
+    xhci->er_full = 0;
+
+    DPRINTF("xhci: event ring:" TARGET_FMT_plx " [%d]\n",
+            xhci->er_start, xhci->er_size);
+}
+
+static void xhci_run(XHCIState *xhci)
+{
+    DPRINTF("xhci_run()\n");
+
+    xhci->usbsts &= ~USBSTS_HCH;
+}
+
+static void xhci_stop(XHCIState *xhci)
+{
+    DPRINTF("xhci_stop()\n");
+    xhci->usbsts |= USBSTS_HCH;
+    xhci->crcr_low &= ~CRCR_CRR;
+}
+
+static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
+                              uint32_t state)
+{
+    uint32_t ctx[5];
+    if (epctx->state == state) {
+        return;
+    }
+
+    cpu_physical_memory_read(epctx->pctx, (uint8_t *) ctx, sizeof(ctx));
+    ctx[0] &= ~EP_STATE_MASK;
+    ctx[0] |= state;
+    ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
+    ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
+    DPRINTF("xhci: set epctx: " TARGET_FMT_plx " state=%d dequeue=%08x%08x\n",
+            epctx->pctx, state, ctx[3], ctx[2]);
+    cpu_physical_memory_write(epctx->pctx, (uint8_t *) ctx, sizeof(ctx));
+    epctx->state = state;
+}
+
+static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
+                               unsigned int epid, target_phys_addr_t pctx,
+                               uint32_t *ctx)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    target_phys_addr_t dequeue;
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+
+    DPRINTF("xhci_enable_ep(%d, %d)\n", slotid, epid);
+
+    slot = &xhci->slots[slotid-1];
+    if (slot->eps[epid-1]) {
+        fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid);
+        return CC_TRB_ERROR;
+    }
+
+    epctx = g_malloc(sizeof(XHCIEPContext));
+    memset(epctx, 0, sizeof(XHCIEPContext));
+
+    slot->eps[epid-1] = epctx;
+
+    dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
+    xhci_ring_init(xhci, &epctx->ring, dequeue);
+    epctx->ring.ccs = ctx[2] & 1;
+
+    epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK;
+    DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type);
+    epctx->pctx = pctx;
+    epctx->max_psize = ctx[1]>>16;
+    epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
+    epctx->has_bg = false;
+    if (epctx->type == ET_ISO_IN) {
+        epctx->has_bg = true;
+    }
+    DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
+            epid/2, epid%2, epctx->max_psize);
+    for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
+        usb_packet_init(&epctx->transfers[i].packet);
+    }
+
+    epctx->state = EP_RUNNING;
+    ctx[0] &= ~EP_STATE_MASK;
+    ctx[0] |= EP_RUNNING;
+
+    return CC_SUCCESS;
+}
+
+static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
+                               unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    int i, xferi, killed = 0;
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+
+    DPRINTF("xhci_ep_nuke_xfers(%d, %d)\n", slotid, epid);
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        return 0;
+    }
+
+    epctx = slot->eps[epid-1];
+
+    xferi = epctx->next_xfer;
+    for (i = 0; i < TD_QUEUE; i++) {
+        XHCITransfer *t = &epctx->transfers[xferi];
+        if (t->running_async) {
+            usb_cancel_packet(&t->packet);
+            t->running_async = 0;
+            t->cancelled = 1;
+            DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i);
+            killed++;
+        }
+        if (t->running_retry) {
+            t->running_retry = 0;
+            epctx->retry = NULL;
+        }
+        if (t->backgrounded) {
+            t->backgrounded = 0;
+        }
+        if (t->trbs) {
+            g_free(t->trbs);
+        }
+        if (t->data) {
+            g_free(t->data);
+        }
+
+        t->trbs = NULL;
+        t->data = NULL;
+        t->trb_count = t->trb_alloced = 0;
+        t->data_length = t->data_alloced = 0;
+        xferi = (xferi + 1) % TD_QUEUE;
+    }
+    if (epctx->has_bg) {
+        xferi = epctx->next_bg;
+        for (i = 0; i < BG_XFERS; i++) {
+            XHCITransfer *t = &epctx->bg_transfers[xferi];
+            if (t->running_async) {
+                usb_cancel_packet(&t->packet);
+                t->running_async = 0;
+                t->cancelled = 1;
+                DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i);
+                killed++;
+            }
+            if (t->data) {
+                g_free(t->data);
+            }
+
+            t->data = NULL;
+            xferi = (xferi + 1) % BG_XFERS;
+        }
+    }
+    return killed;
+}
+
+static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
+                               unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+
+    DPRINTF("xhci_disable_ep(%d, %d)\n", slotid, epid);
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d already disabled\n", slotid, epid);
+        return CC_SUCCESS;
+    }
+
+    xhci_ep_nuke_xfers(xhci, slotid, epid);
+
+    epctx = slot->eps[epid-1];
+
+    xhci_set_ep_state(xhci, epctx, EP_DISABLED);
+
+    g_free(epctx);
+    slot->eps[epid-1] = NULL;
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
+                             unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+
+    DPRINTF("xhci_stop_ep(%d, %d)\n", slotid, epid);
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+
+    if (epid < 1 || epid > 31) {
+        fprintf(stderr, "xhci: bad ep %d\n", epid);
+        return CC_TRB_ERROR;
+    }
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
+        return CC_EP_NOT_ENABLED_ERROR;
+    }
+
+    if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) {
+        fprintf(stderr, "xhci: FIXME: endpoint stopped w/ xfers running, "
+                "data might be lost\n");
+    }
+
+    epctx = slot->eps[epid-1];
+
+    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
+                              unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    USBDevice *dev;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+
+    DPRINTF("xhci_reset_ep(%d, %d)\n", slotid, epid);
+
+    if (epid < 1 || epid > 31) {
+        fprintf(stderr, "xhci: bad ep %d\n", epid);
+        return CC_TRB_ERROR;
+    }
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
+        return CC_EP_NOT_ENABLED_ERROR;
+    }
+
+    epctx = slot->eps[epid-1];
+
+    if (epctx->state != EP_HALTED) {
+        fprintf(stderr, "xhci: reset EP while EP %d not halted (%d)\n",
+                epid, epctx->state);
+        return CC_CONTEXT_STATE_ERROR;
+    }
+
+    if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) {
+        fprintf(stderr, "xhci: FIXME: endpoint reset w/ xfers running, "
+                "data might be lost\n");
+    }
+
+    uint8_t ep = epid>>1;
+
+    if (epid & 1) {
+        ep |= 0x80;
+    }
+
+    dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev;
+    if (!dev) {
+        return CC_USB_TRANSACTION_ERROR;
+    }
+
+    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
+                                    unsigned int epid, uint64_t pdequeue)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    target_phys_addr_t dequeue;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+
+    if (epid < 1 || epid > 31) {
+        fprintf(stderr, "xhci: bad ep %d\n", epid);
+        return CC_TRB_ERROR;
+    }
+
+    DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue);
+    dequeue = xhci_mask64(pdequeue);
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
+        return CC_EP_NOT_ENABLED_ERROR;
+    }
+
+    epctx = slot->eps[epid-1];
+
+
+    if (epctx->state != EP_STOPPED) {
+        fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
+        return CC_CONTEXT_STATE_ERROR;
+    }
+
+    xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
+    epctx->ring.ccs = dequeue & 1;
+
+    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+
+    return CC_SUCCESS;
+}
+
+static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
+                          unsigned int length, bool in_xfer, bool out_xfer,
+                          bool report)
+{
+    int i;
+    uint32_t edtla = 0;
+    unsigned int transferred = 0;
+    unsigned int left = length;
+    bool reported = 0;
+    bool shortpkt = 0;
+    XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
+    XHCIState *xhci = xfer->xhci;
+
+    DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n",
+            length, in_xfer, out_xfer, report);
+
+    assert(!(in_xfer && out_xfer));
+
+    for (i = 0; i < xfer->trb_count; i++) {
+        XHCITRB *trb = &xfer->trbs[i];
+        target_phys_addr_t addr;
+        unsigned int chunk = 0;
+
+        switch (TRB_TYPE(*trb)) {
+        case TR_DATA:
+            if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
+                fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n");
+                xhci_die(xhci);
+                return transferred;
+            }
+            /* fallthrough */
+        case TR_NORMAL:
+        case TR_ISOCH:
+            addr = xhci_mask64(trb->parameter);
+            chunk = trb->status & 0x1ffff;
+            if (chunk > left) {
+                chunk = left;
+                shortpkt = 1;
+            }
+            if (in_xfer || out_xfer) {
+                if (trb->control & TRB_TR_IDT) {
+                    uint64_t idata;
+                    if (chunk > 8 || in_xfer) {
+                        fprintf(stderr, "xhci: invalid immediate data TRB\n");
+                        xhci_die(xhci);
+                        return transferred;
+                    }
+                    idata = le64_to_cpu(trb->parameter);
+                    memcpy(data, &idata, chunk);
+                } else {
+                    DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at "
+                            TARGET_FMT_plx "\n", in_xfer, chunk, addr);
+                    if (in_xfer) {
+                        cpu_physical_memory_write(addr, data, chunk);
+                    } else {
+                        cpu_physical_memory_read(addr, data, chunk);
+                    }
+#ifdef DEBUG_DATA
+                    unsigned int count = chunk;
+                    int i;
+                    if (count > 16) {
+                        count = 16;
+                    }
+                    DPRINTF(" ::");
+                    for (i = 0; i < count; i++) {
+                        DPRINTF(" %02x", data[i]);
+                    }
+                    DPRINTF("\n");
+#endif
+                }
+            }
+            left -= chunk;
+            data += chunk;
+            edtla += chunk;
+            transferred += chunk;
+            break;
+        case TR_STATUS:
+            reported = 0;
+            shortpkt = 0;
+            break;
+        }
+
+        if (report && !reported && (trb->control & TRB_TR_IOC ||
+            (shortpkt && (trb->control & TRB_TR_ISP)))) {
+            event.slotid = xfer->slotid;
+            event.epid = xfer->epid;
+            event.length = (trb->status & 0x1ffff) - chunk;
+            event.flags = 0;
+            event.ptr = trb->addr;
+            if (xfer->status == CC_SUCCESS) {
+                event.ccode = shortpkt ? CC_SHORT_PACKET : CC_SUCCESS;
+            } else {
+                event.ccode = xfer->status;
+            }
+            if (TRB_TYPE(*trb) == TR_EVDATA) {
+                event.ptr = trb->parameter;
+                event.flags |= TRB_EV_ED;
+                event.length = edtla & 0xffffff;
+                DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length);
+                edtla = 0;
+            }
+            xhci_event(xhci, &event);
+            reported = 1;
+        }
+    }
+    return transferred;
+}
+
+static void xhci_stall_ep(XHCITransfer *xfer)
+{
+    XHCIState *xhci = xfer->xhci;
+    XHCISlot *slot = &xhci->slots[xfer->slotid-1];
+    XHCIEPContext *epctx = slot->eps[xfer->epid-1];
+
+    epctx->ring.dequeue = xfer->trbs[0].addr;
+    epctx->ring.ccs = xfer->trbs[0].ccs;
+    xhci_set_ep_state(xhci, epctx, EP_HALTED);
+    DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid);
+    DPRINTF("xhci: will continue at "TARGET_FMT_plx"\n", epctx->ring.dequeue);
+}
+
+static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
+                       XHCIEPContext *epctx);
+
+static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx)
+{
+    if (epctx->bg_updating) {
+        return;
+    }
+    DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx);
+    assert(epctx->has_bg);
+    DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg);
+    epctx->bg_updating = 1;
+    while (epctx->transfers[epctx->comp_xfer].backgrounded &&
+           epctx->bg_transfers[epctx->next_bg].complete) {
+        XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer];
+        XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg];
+#if 0
+        DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n",
+                epctx->comp_xfer, epctx->next_bg, bg->cur_pkt,
+                bg->usbxfer->iso_packet_desc[bg->cur_pkt].status
+               );
+#endif
+        assert(epctx->type == ET_ISO_IN);
+        assert(bg->iso_xfer);
+        assert(bg->in_xfer);
+        uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize;
+#if 0
+        int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length;
+        fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status);
+#else
+        int len = 0;
+        FIXME();
+#endif
+        fg->complete = 1;
+        fg->backgrounded = 0;
+
+        if (fg->status == CC_STALL_ERROR) {
+            xhci_stall_ep(fg);
+        }
+
+        xhci_xfer_data(fg, p, len, 1, 0, 1);
+
+        epctx->comp_xfer++;
+        if (epctx->comp_xfer == TD_QUEUE) {
+            epctx->comp_xfer = 0;
+        }
+        DPRINTF("next fg xfer: %d\n", epctx->comp_xfer);
+        bg->cur_pkt++;
+        if (bg->cur_pkt == bg->pkts) {
+            bg->complete = 0;
+            if (xhci_submit(xhci, bg, epctx) < 0) {
+                fprintf(stderr, "xhci: bg resubmit failed\n");
+            }
+            epctx->next_bg++;
+            if (epctx->next_bg == BG_XFERS) {
+                epctx->next_bg = 0;
+            }
+            DPRINTF("next bg xfer: %d\n", epctx->next_bg);
+
+        xhci_kick_ep(xhci, fg->slotid, fg->epid);
+        }
+    }
+    epctx->bg_updating = 0;
+}
+
+#if 0
+static void xhci_xfer_cb(struct libusb_transfer *transfer)
+{
+    XHCIState *xhci;
+    XHCITransfer *xfer;
+
+    xfer = (XHCITransfer *)transfer->user_data;
+    xhci = xfer->xhci;
+
+    DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid,
+            xfer->epid, transfer->status);
+
+    assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS);
+    assert(xfer->epid >= 1 && xfer->epid <= 31);
+
+    if (xfer->cancelled) {
+        DPRINTF("xhci: transfer cancelled, not reporting anything\n");
+        xfer->running = 0;
+        return;
+    }
+
+    XHCIEPContext *epctx;
+    XHCISlot *slot;
+    slot = &xhci->slots[xfer->slotid-1];
+    assert(slot->eps[xfer->epid-1]);
+    epctx = slot->eps[xfer->epid-1];
+
+    if (xfer->bg_xfer) {
+        DPRINTF("xhci: background transfer, updating\n");
+        xfer->complete = 1;
+        xfer->running = 0;
+        xhci_bg_update(xhci, epctx);
+        return;
+    }
+
+    if (xfer->iso_xfer) {
+        transfer->status = transfer->iso_packet_desc[0].status;
+        transfer->actual_length = transfer->iso_packet_desc[0].actual_length;
+    }
+
+    xfer->status = libusb_to_ccode(transfer->status);
+
+    xfer->complete = 1;
+    xfer->running = 0;
+
+    if (transfer->status == LIBUSB_TRANSFER_STALL)
+        xhci_stall_ep(xhci, epctx, xfer);
+
+    DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length);
+
+    if (xfer->in_xfer) {
+        if (xfer->epid == 1) {
+            xhci_xfer_data(xhci, xfer, xfer->data + 8,
+                           transfer->actual_length, 1, 0, 1);
+        } else {
+            xhci_xfer_data(xhci, xfer, xfer->data,
+                           transfer->actual_length, 1, 0, 1);
+        }
+    } else {
+        xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1);
+    }
+
+    xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+}
+
+static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer,
+                            uint8_t bmRequestType, uint8_t bRequest,
+                            uint16_t wValue, uint16_t wIndex, uint16_t wLength)
+{
+    uint16_t type_req = (bmRequestType << 8) | bRequest;
+
+    switch (type_req) {
+        case 0x0000 | USB_REQ_SET_CONFIGURATION:
+            DPRINTF("xhci: HLE switch configuration\n");
+            return xhci_switch_config(xhci, xfer->slotid, wValue) == 0;
+        case 0x0100 | USB_REQ_SET_INTERFACE:
+            DPRINTF("xhci: HLE set interface altsetting\n");
+            return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0;
+        case 0x0200 | USB_REQ_CLEAR_FEATURE:
+            if (wValue == 0) { // endpoint halt
+                DPRINTF("xhci: HLE clear halt\n");
+                return xhci_clear_halt(xhci, xfer->slotid, wIndex);
+            }
+        case 0x0000 | USB_REQ_SET_ADDRESS:
+            fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n");
+            return 0;
+        default:
+            return 0;
+    }
+}
+#endif
+
+static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
+{
+    USBEndpoint *ep;
+    int dir;
+
+    dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
+    ep = usb_ep_get(dev, dir, xfer->epid >> 1);
+    usb_packet_setup(&xfer->packet, dir, ep);
+    usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
+    DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
+            xfer->packet.pid, dev->addr, ep->nr);
+    return 0;
+}
+
+static int xhci_complete_packet(XHCITransfer *xfer, int ret)
+{
+    if (ret == USB_RET_ASYNC) {
+        xfer->running_async = 1;
+        xfer->running_retry = 0;
+        xfer->complete = 0;
+        xfer->cancelled = 0;
+        return 0;
+    } else if (ret == USB_RET_NAK) {
+        xfer->running_async = 0;
+        xfer->running_retry = 1;
+        xfer->complete = 0;
+        xfer->cancelled = 0;
+        return 0;
+    } else {
+        xfer->running_async = 0;
+        xfer->running_retry = 0;
+        xfer->complete = 1;
+    }
+
+    if (ret >= 0) {
+        xfer->status = CC_SUCCESS;
+        xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
+        return 0;
+    }
+
+    /* error */
+    switch (ret) {
+    case USB_RET_NODEV:
+        xfer->status = CC_USB_TRANSACTION_ERROR;
+        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+        xhci_stall_ep(xfer);
+        break;
+    case USB_RET_STALL:
+        xfer->status = CC_STALL_ERROR;
+        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+        xhci_stall_ep(xfer);
+        break;
+    default:
+        fprintf(stderr, "%s: FIXME: ret = %d\n", __FUNCTION__, ret);
+        FIXME();
+    }
+    return 0;
+}
+
+static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
+{
+    if (!(port->portsc & PORTSC_PED)) {
+        return NULL;
+    }
+    return usb_find_device(&port->port, addr);
+}
+
+static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+{
+    XHCITRB *trb_setup, *trb_status;
+    uint8_t bmRequestType;
+    uint16_t wLength;
+    XHCIPort *port;
+    USBDevice *dev;
+    int ret;
+
+    DPRINTF("xhci_fire_ctl_transfer(slot=%d)\n", xfer->slotid);
+
+    trb_setup = &xfer->trbs[0];
+    trb_status = &xfer->trbs[xfer->trb_count-1];
+
+    /* at most one Event Data TRB allowed after STATUS */
+    if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
+        trb_status--;
+    }
+
+    /* do some sanity checks */
+    if (TRB_TYPE(*trb_setup) != TR_SETUP) {
+        fprintf(stderr, "xhci: ep0 first TD not SETUP: %d\n",
+                TRB_TYPE(*trb_setup));
+        return -1;
+    }
+    if (TRB_TYPE(*trb_status) != TR_STATUS) {
+        fprintf(stderr, "xhci: ep0 last TD not STATUS: %d\n",
+                TRB_TYPE(*trb_status));
+        return -1;
+    }
+    if (!(trb_setup->control & TRB_TR_IDT)) {
+        fprintf(stderr, "xhci: Setup TRB doesn't have IDT set\n");
+        return -1;
+    }
+    if ((trb_setup->status & 0x1ffff) != 8) {
+        fprintf(stderr, "xhci: Setup TRB has bad length (%d)\n",
+                (trb_setup->status & 0x1ffff));
+        return -1;
+    }
+
+    bmRequestType = trb_setup->parameter;
+    wLength = trb_setup->parameter >> 48;
+
+    if (xfer->data && xfer->data_alloced < wLength) {
+        xfer->data_alloced = 0;
+        g_free(xfer->data);
+        xfer->data = NULL;
+    }
+    if (!xfer->data) {
+        DPRINTF("xhci: alloc %d bytes data\n", wLength);
+        xfer->data = g_malloc(wLength+1);
+        xfer->data_alloced = wLength;
+    }
+    xfer->data_length = wLength;
+
+    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+    dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
+    if (!dev) {
+        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
+                xhci->slots[xfer->slotid-1].port);
+        return -1;
+    }
+
+    xfer->in_xfer = bmRequestType & USB_DIR_IN;
+    xfer->iso_xfer = false;
+
+    xhci_setup_packet(xfer, dev);
+    xfer->packet.parameter = trb_setup->parameter;
+    if (!xfer->in_xfer) {
+        xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
+    }
+
+    ret = usb_handle_packet(dev, &xfer->packet);
+
+    xhci_complete_packet(xfer, ret);
+    if (!xfer->running_async && !xfer->running_retry) {
+        xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+    }
+    return 0;
+}
+
+static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+{
+    XHCIPort *port;
+    USBDevice *dev;
+    int ret;
+
+    DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
+
+    xfer->in_xfer = epctx->type>>2;
+
+    if (xfer->data && xfer->data_alloced < xfer->data_length) {
+        xfer->data_alloced = 0;
+        g_free(xfer->data);
+        xfer->data = NULL;
+    }
+    if (!xfer->data && xfer->data_length) {
+        DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length);
+        xfer->data = g_malloc(xfer->data_length);
+        xfer->data_alloced = xfer->data_length;
+    }
+    if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
+        if (!xfer->bg_xfer) {
+            xfer->pkts = 1;
+        }
+    } else {
+        xfer->pkts = 0;
+    }
+
+    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+    dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
+    if (!dev) {
+        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
+                xhci->slots[xfer->slotid-1].port);
+        return -1;
+    }
+
+    xhci_setup_packet(xfer, dev);
+
+    switch(epctx->type) {
+    case ET_INTR_OUT:
+    case ET_INTR_IN:
+    case ET_BULK_OUT:
+    case ET_BULK_IN:
+        break;
+    case ET_ISO_OUT:
+    case ET_ISO_IN:
+        FIXME();
+        break;
+    default:
+        fprintf(stderr, "xhci: unknown or unhandled EP "
+                "(type %d, in %d, ep %02x)\n",
+                epctx->type, xfer->in_xfer, xfer->epid);
+        return -1;
+    }
+
+    if (!xfer->in_xfer) {
+        xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0);
+    }
+    ret = usb_handle_packet(dev, &xfer->packet);
+
+    xhci_complete_packet(xfer, ret);
+    if (!xfer->running_async && !xfer->running_retry) {
+        xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+    }
+    return 0;
+}
+
+static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+{
+    int i;
+    unsigned int length = 0;
+    XHCITRB *trb;
+
+    DPRINTF("xhci_fire_transfer(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
+
+    for (i = 0; i < xfer->trb_count; i++) {
+        trb = &xfer->trbs[i];
+        if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
+            length += trb->status & 0x1ffff;
+        }
+    }
+    DPRINTF("xhci: total TD length=%d\n", length);
+
+    if (!epctx->has_bg) {
+        xfer->data_length = length;
+        xfer->backgrounded = 0;
+        return xhci_submit(xhci, xfer, epctx);
+    } else {
+        if (!epctx->bg_running) {
+            for (i = 0; i < BG_XFERS; i++) {
+                XHCITransfer *t = &epctx->bg_transfers[i];
+                t->xhci = xhci;
+                t->epid = xfer->epid;
+                t->slotid = xfer->slotid;
+                t->pkts = BG_PKTS;
+                t->pktsize = epctx->max_psize;
+                t->data_length = t->pkts * t->pktsize;
+                t->bg_xfer = 1;
+                if (xhci_submit(xhci, t, epctx) < 0) {
+                    fprintf(stderr, "xhci: bg submit failed\n");
+                    return -1;
+                }
+            }
+            epctx->bg_running = 1;
+        }
+        xfer->backgrounded = 1;
+        xhci_bg_update(xhci, epctx);
+        return 0;
+    }
+}
+
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
+{
+    XHCIEPContext *epctx;
+    int length;
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+    DPRINTF("xhci_kick_ep(%d, %d)\n", slotid, epid);
+
+    if (!xhci->slots[slotid-1].enabled) {
+        fprintf(stderr, "xhci: xhci_kick_ep for disabled slot %d\n", slotid);
+        return;
+    }
+    epctx = xhci->slots[slotid-1].eps[epid-1];
+    if (!epctx) {
+        fprintf(stderr, "xhci: xhci_kick_ep for disabled endpoint %d,%d\n",
+                epid, slotid);
+        return;
+    }
+
+    if (epctx->retry) {
+        /* retry nak'ed transfer */
+        XHCITransfer *xfer = epctx->retry;
+        int result;
+
+        DPRINTF("xhci: retry nack'ed transfer ...\n");
+        assert(xfer->running_retry);
+        xhci_setup_packet(xfer, xfer->packet.ep->dev);
+        result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+        if (result == USB_RET_NAK) {
+            DPRINTF("xhci: ... xfer still nacked\n");
+            return;
+        }
+        DPRINTF("xhci: ... result %d\n", result);
+        xhci_complete_packet(xfer, result);
+        assert(!xfer->running_retry);
+        epctx->retry = NULL;
+    }
+
+    if (epctx->state == EP_HALTED) {
+        DPRINTF("xhci: ep halted, not running schedule\n");
+        return;
+    }
+
+    xhci_set_ep_state(xhci, epctx, EP_RUNNING);
+
+    while (1) {
+        XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
+        if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
+            DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n",
+                    epctx->next_xfer, xfer->running_async,
+                    xfer->running_retry, xfer->backgrounded);
+            break;
+        } else {
+            DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer);
+        }
+        length = xhci_ring_chain_length(xhci, &epctx->ring);
+        if (length < 0) {
+            DPRINTF("xhci: incomplete TD (%d TRBs)\n", -length);
+            break;
+        } else if (length == 0) {
+            break;
+        }
+        DPRINTF("xhci: fetching %d-TRB TD\n", length);
+        if (xfer->trbs && xfer->trb_alloced < length) {
+            xfer->trb_count = 0;
+            xfer->trb_alloced = 0;
+            g_free(xfer->trbs);
+            xfer->trbs = NULL;
+        }
+        if (!xfer->trbs) {
+            xfer->trbs = g_malloc(sizeof(XHCITRB) * length);
+            xfer->trb_alloced = length;
+        }
+        xfer->trb_count = length;
+
+        for (i = 0; i < length; i++) {
+            assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL));
+        }
+        xfer->xhci = xhci;
+        xfer->epid = epid;
+        xfer->slotid = slotid;
+
+        if (epid == 1) {
+            if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
+                epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
+            } else {
+                fprintf(stderr, "xhci: error firing CTL transfer\n");
+            }
+        } else {
+            if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
+                epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
+            } else {
+                fprintf(stderr, "xhci: error firing data transfer\n");
+            }
+        }
+
+        if (epctx->state == EP_HALTED) {
+            DPRINTF("xhci: ep halted, stopping schedule\n");
+            break;
+        }
+        if (xfer->running_retry) {
+            DPRINTF("xhci: xfer nacked, stopping schedule\n");
+            epctx->retry = xfer;
+            break;
+        }
+    }
+}
+
+static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
+{
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_enable_slot(%d)\n", slotid);
+    xhci->slots[slotid-1].enabled = 1;
+    xhci->slots[slotid-1].port = 0;
+    memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
+{
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_disable_slot(%d)\n", slotid);
+
+    for (i = 1; i <= 31; i++) {
+        if (xhci->slots[slotid-1].eps[i-1]) {
+            xhci_disable_ep(xhci, slotid, i);
+        }
+    }
+
+    xhci->slots[slotid-1].enabled = 0;
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
+                                  uint64_t pictx, bool bsr)
+{
+    XHCISlot *slot;
+    USBDevice *dev;
+    target_phys_addr_t ictx, octx, dcbaap;
+    uint64_t poctx;
+    uint32_t ictl_ctx[2];
+    uint32_t slot_ctx[4];
+    uint32_t ep0_ctx[5];
+    unsigned int port;
+    int i;
+    TRBCCode res;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_address_slot(%d)\n", slotid);
+
+    dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
+    cpu_physical_memory_read(dcbaap + 8*slotid,
+                             (uint8_t *) &poctx, sizeof(poctx));
+    ictx = xhci_mask64(pictx);
+    octx = xhci_mask64(le64_to_cpu(poctx));
+
+    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
+
+    if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) {
+        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
+                ictl_ctx[0], ictl_ctx[1]);
+        return CC_TRB_ERROR;
+    }
+
+    cpu_physical_memory_read(ictx+32, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    cpu_physical_memory_read(ictx+64, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+
+    DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+
+    DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
+            ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
+
+    port = (slot_ctx[1]>>16) & 0xFF;
+    dev = xhci->ports[port-1].port.dev;
+
+    if (port < 1 || port > MAXPORTS) {
+        fprintf(stderr, "xhci: bad port %d\n", port);
+        return CC_TRB_ERROR;
+    } else if (!dev) {
+        fprintf(stderr, "xhci: port %d not connected\n", port);
+        return CC_USB_TRANSACTION_ERROR;
+    }
+
+    for (i = 0; i < MAXSLOTS; i++) {
+        if (xhci->slots[i].port == port) {
+            fprintf(stderr, "xhci: port %d already assigned to slot %d\n",
+                    port, i+1);
+            return CC_TRB_ERROR;
+        }
+    }
+
+    slot = &xhci->slots[slotid-1];
+    slot->port = port;
+    slot->ctx = octx;
+
+    if (bsr) {
+        slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
+    } else {
+        slot->devaddr = xhci->devaddr++;
+        slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
+        DPRINTF("xhci: device address is %d\n", slot->devaddr);
+        usb_device_handle_control(dev, NULL,
+                                  DeviceOutRequest | USB_REQ_SET_ADDRESS,
+                                  slot->devaddr, 0, 0, NULL);
+    }
+
+    res = xhci_enable_ep(xhci, slotid, 1, octx+32, ep0_ctx);
+
+    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+    DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
+            ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
+
+    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    cpu_physical_memory_write(octx+32, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+
+    return res;
+}
+
+
+static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
+                                  uint64_t pictx, bool dc)
+{
+    target_phys_addr_t ictx, octx;
+    uint32_t ictl_ctx[2];
+    uint32_t slot_ctx[4];
+    uint32_t islot_ctx[4];
+    uint32_t ep_ctx[5];
+    int i;
+    TRBCCode res;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_configure_slot(%d)\n", slotid);
+
+    ictx = xhci_mask64(pictx);
+    octx = xhci->slots[slotid-1].ctx;
+
+    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    if (dc) {
+        for (i = 2; i <= 31; i++) {
+            if (xhci->slots[slotid-1].eps[i-1]) {
+                xhci_disable_ep(xhci, slotid, i);
+            }
+        }
+
+        cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+        slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
+        slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT;
+        DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+                slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+        cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+        return CC_SUCCESS;
+    }
+
+    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
+
+    if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) {
+        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
+                ictl_ctx[0], ictl_ctx[1]);
+        return CC_TRB_ERROR;
+    }
+
+    cpu_physical_memory_read(ictx+32, (uint8_t *) islot_ctx, sizeof(islot_ctx));
+    cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+    if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) {
+        fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]);
+        return CC_CONTEXT_STATE_ERROR;
+    }
+
+    for (i = 2; i <= 31; i++) {
+        if (ictl_ctx[0] & (1<<i)) {
+            xhci_disable_ep(xhci, slotid, i);
+        }
+        if (ictl_ctx[1] & (1<<i)) {
+            cpu_physical_memory_read(ictx+32+(32*i),
+                                     (uint8_t *) ep_ctx, sizeof(ep_ctx));
+            DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n",
+                    i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
+                    ep_ctx[3], ep_ctx[4]);
+            xhci_disable_ep(xhci, slotid, i);
+            res = xhci_enable_ep(xhci, slotid, i, octx+(32*i), ep_ctx);
+            if (res != CC_SUCCESS) {
+                return res;
+            }
+            DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n",
+                    i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
+                    ep_ctx[3], ep_ctx[4]);
+            cpu_physical_memory_write(octx+(32*i),
+                                      (uint8_t *) ep_ctx, sizeof(ep_ctx));
+        }
+    }
+
+    slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
+    slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT;
+    slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT);
+    slot_ctx[0] |= islot_ctx[0] & (SLOT_CONTEXT_ENTRIES_MASK <<
+                                   SLOT_CONTEXT_ENTRIES_SHIFT);
+    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+
+    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+    return CC_SUCCESS;
+}
+
+
+static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
+                                   uint64_t pictx)
+{
+    target_phys_addr_t ictx, octx;
+    uint32_t ictl_ctx[2];
+    uint32_t iep0_ctx[5];
+    uint32_t ep0_ctx[5];
+    uint32_t islot_ctx[4];
+    uint32_t slot_ctx[4];
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_evaluate_slot(%d)\n", slotid);
+
+    ictx = xhci_mask64(pictx);
+    octx = xhci->slots[slotid-1].ctx;
+
+    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
+
+    if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) {
+        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
+                ictl_ctx[0], ictl_ctx[1]);
+        return CC_TRB_ERROR;
+    }
+
+    if (ictl_ctx[1] & 0x1) {
+        cpu_physical_memory_read(ictx+32,
+                                 (uint8_t *) islot_ctx, sizeof(islot_ctx));
+
+        DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
+                islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]);
+
+        cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+        slot_ctx[1] &= ~0xFFFF; /* max exit latency */
+        slot_ctx[1] |= islot_ctx[1] & 0xFFFF;
+        slot_ctx[2] &= ~0xFF00000; /* interrupter target */
+        slot_ctx[2] |= islot_ctx[2] & 0xFF000000;
+
+        DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+                slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+
+        cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    }
+
+    if (ictl_ctx[1] & 0x2) {
+        cpu_physical_memory_read(ictx+64,
+                                 (uint8_t *) iep0_ctx, sizeof(iep0_ctx));
+
+        DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
+                iep0_ctx[0], iep0_ctx[1], iep0_ctx[2],
+                iep0_ctx[3], iep0_ctx[4]);
+
+        cpu_physical_memory_read(octx+32, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+
+        ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/
+        ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000;
+
+        DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
+                ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
+
+        cpu_physical_memory_write(octx+32,
+                                  (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+    }
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
+{
+    uint32_t slot_ctx[4];
+    target_phys_addr_t octx;
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_reset_slot(%d)\n", slotid);
+
+    octx = xhci->slots[slotid-1].ctx;
+
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    for (i = 2; i <= 31; i++) {
+        if (xhci->slots[slotid-1].eps[i-1]) {
+            xhci_disable_ep(xhci, slotid, i);
+        }
+    }
+
+    cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
+    slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT;
+    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+    return CC_SUCCESS;
+}
+
+static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *trb)
+{
+    unsigned int slotid;
+    slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK;
+    if (slotid < 1 || slotid > MAXSLOTS) {
+        fprintf(stderr, "xhci: bad slot id %d\n", slotid);
+        event->ccode = CC_TRB_ERROR;
+        return 0;
+    } else if (!xhci->slots[slotid-1].enabled) {
+        fprintf(stderr, "xhci: slot id %d not enabled\n", slotid);
+        event->ccode = CC_SLOT_NOT_ENABLED_ERROR;
+        return 0;
+    }
+    return slotid;
+}
+
+static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
+{
+    target_phys_addr_t ctx;
+    uint8_t bw_ctx[MAXPORTS+1];
+
+    DPRINTF("xhci_get_port_bandwidth()\n");
+
+    ctx = xhci_mask64(pctx);
+
+    DPRINTF("xhci: bandwidth context at "TARGET_FMT_plx"\n", ctx);
+
+    /* TODO: actually implement real values here */
+    bw_ctx[0] = 0;
+    memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */
+    cpu_physical_memory_write(ctx, bw_ctx, sizeof(bw_ctx));
+
+    return CC_SUCCESS;
+}
+
+static uint32_t rotl(uint32_t v, unsigned count)
+{
+    count &= 31;
+    return (v << count) | (v >> (32 - count));
+}
+
+
+static uint32_t xhci_nec_challenge(uint32_t hi, uint32_t lo)
+{
+    uint32_t val;
+    val = rotl(lo - 0x49434878, 32 - ((hi>>8) & 0x1F));
+    val += rotl(lo + 0x49434878, hi & 0x1F);
+    val -= rotl(hi ^ 0x49434878, (lo >> 16) & 0x1F);
+    return ~val;
+}
+
+static void xhci_via_challenge(uint64_t addr)
+{
+    uint32_t buf[8];
+    uint32_t obuf[8];
+    target_phys_addr_t paddr = xhci_mask64(addr);
+
+    cpu_physical_memory_read(paddr, (uint8_t *) &buf, 32);
+
+    memcpy(obuf, buf, sizeof(obuf));
+
+    if ((buf[0] & 0xff) == 2) {
+        obuf[0] = 0x49932000 + 0x54dc200 * buf[2] + 0x7429b578 * buf[3];
+        obuf[0] |=  (buf[2] * buf[3]) & 0xff;
+        obuf[1] = 0x0132bb37 + 0xe89 * buf[2] + 0xf09 * buf[3];
+        obuf[2] = 0x0066c2e9 + 0x2091 * buf[2] + 0x19bd * buf[3];
+        obuf[3] = 0xd5281342 + 0x2cc9691 * buf[2] + 0x2367662 * buf[3];
+        obuf[4] = 0x0123c75c + 0x1595 * buf[2] + 0x19ec * buf[3];
+        obuf[5] = 0x00f695de + 0x26fd * buf[2] + 0x3e9 * buf[3];
+        obuf[6] = obuf[2] ^ obuf[3] ^ 0x29472956;
+        obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593;
+    }
+
+    cpu_physical_memory_write(paddr, (uint8_t *) &obuf, 32);
+}
+
+static void xhci_process_commands(XHCIState *xhci)
+{
+    XHCITRB trb;
+    TRBType type;
+    XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS};
+    target_phys_addr_t addr;
+    unsigned int i, slotid = 0;
+
+    DPRINTF("xhci_process_commands()\n");
+    if (!xhci_running(xhci)) {
+        DPRINTF("xhci_process_commands() called while xHC stopped or paused\n");
+        return;
+    }
+
+    xhci->crcr_low |= CRCR_CRR;
+
+    while ((type = xhci_ring_fetch(xhci, &xhci->cmd_ring, &trb, &addr))) {
+        event.ptr = addr;
+        switch (type) {
+        case CR_ENABLE_SLOT:
+            for (i = 0; i < MAXSLOTS; i++) {
+                if (!xhci->slots[i].enabled) {
+                    break;
+                }
+            }
+            if (i >= MAXSLOTS) {
+                fprintf(stderr, "xhci: no device slots available\n");
+                event.ccode = CC_NO_SLOTS_ERROR;
+            } else {
+                slotid = i+1;
+                event.ccode = xhci_enable_slot(xhci, slotid);
+            }
+            break;
+        case CR_DISABLE_SLOT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_disable_slot(xhci, slotid);
+            }
+            break;
+        case CR_ADDRESS_DEVICE:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_address_slot(xhci, slotid, trb.parameter,
+                                                trb.control & TRB_CR_BSR);
+            }
+            break;
+        case CR_CONFIGURE_ENDPOINT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_configure_slot(xhci, slotid, trb.parameter,
+                                                  trb.control & TRB_CR_DC);
+            }
+            break;
+        case CR_EVALUATE_CONTEXT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_evaluate_slot(xhci, slotid, trb.parameter);
+            }
+            break;
+        case CR_STOP_ENDPOINT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
+                    & TRB_CR_EPID_MASK;
+                event.ccode = xhci_stop_ep(xhci, slotid, epid);
+            }
+            break;
+        case CR_RESET_ENDPOINT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
+                    & TRB_CR_EPID_MASK;
+                event.ccode = xhci_reset_ep(xhci, slotid, epid);
+            }
+            break;
+        case CR_SET_TR_DEQUEUE:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
+                    & TRB_CR_EPID_MASK;
+                event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid,
+                                                  trb.parameter);
+            }
+            break;
+        case CR_RESET_DEVICE:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_reset_slot(xhci, slotid);
+            }
+            break;
+        case CR_GET_PORT_BANDWIDTH:
+            event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter);
+            break;
+        case CR_VENDOR_VIA_CHALLENGE_RESPONSE:
+            xhci_via_challenge(trb.parameter);
+            break;
+        case CR_VENDOR_NEC_FIRMWARE_REVISION:
+            event.type = 48; /* NEC reply */
+            event.length = 0x3025;
+            break;
+        case CR_VENDOR_NEC_CHALLENGE_RESPONSE:
+        {
+            uint32_t chi = trb.parameter >> 32;
+            uint32_t clo = trb.parameter;
+            uint32_t val = xhci_nec_challenge(chi, clo);
+            event.length = val & 0xFFFF;
+            event.epid = val >> 16;
+            slotid = val >> 24;
+            event.type = 48; /* NEC reply */
+        }
+        break;
+        default:
+            fprintf(stderr, "xhci: unimplemented command %d\n", type);
+            event.ccode = CC_TRB_ERROR;
+            break;
+        }
+        event.slotid = slotid;
+        xhci_event(xhci, &event);
+    }
+}
+
+static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
+{
+    int nr = port->port.index + 1;
+
+    port->portsc = PORTSC_PP;
+    if (port->port.dev && port->port.dev->attached && !is_detach) {
+        port->portsc |= PORTSC_CCS;
+        switch (port->port.dev->speed) {
+        case USB_SPEED_LOW:
+            port->portsc |= PORTSC_SPEED_LOW;
+            break;
+        case USB_SPEED_FULL:
+            port->portsc |= PORTSC_SPEED_FULL;
+            break;
+        case USB_SPEED_HIGH:
+            port->portsc |= PORTSC_SPEED_HIGH;
+            break;
+        }
+    }
+
+    if (xhci_running(xhci)) {
+        port->portsc |= PORTSC_CSC;
+        XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
+        xhci_event(xhci, &ev);
+        DPRINTF("xhci: port change event for port %d\n", nr);
+    }
+}
+
+static void xhci_reset(void *opaque)
+{
+    XHCIState *xhci = opaque;
+    int i;
+
+    DPRINTF("xhci: full reset\n");
+    if (!(xhci->usbsts & USBSTS_HCH)) {
+        fprintf(stderr, "xhci: reset while running!\n");
+    }
+
+    xhci->usbcmd = 0;
+    xhci->usbsts = USBSTS_HCH;
+    xhci->dnctrl = 0;
+    xhci->crcr_low = 0;
+    xhci->crcr_high = 0;
+    xhci->dcbaap_low = 0;
+    xhci->dcbaap_high = 0;
+    xhci->config = 0;
+    xhci->devaddr = 2;
+
+    for (i = 0; i < MAXSLOTS; i++) {
+        xhci_disable_slot(xhci, i+1);
+    }
+
+    for (i = 0; i < MAXPORTS; i++) {
+        xhci_update_port(xhci, xhci->ports + i, 0);
+    }
+
+    xhci->mfindex = 0;
+    xhci->iman = 0;
+    xhci->imod = 0;
+    xhci->erstsz = 0;
+    xhci->erstba_low = 0;
+    xhci->erstba_high = 0;
+    xhci->erdp_low = 0;
+    xhci->erdp_high = 0;
+
+    xhci->er_ep_idx = 0;
+    xhci->er_pcs = 1;
+    xhci->er_full = 0;
+    xhci->ev_buffer_put = 0;
+    xhci->ev_buffer_get = 0;
+}
+
+static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_cap_read(0x%x)\n", reg);
+
+    switch (reg) {
+    case 0x00: /* HCIVERSION, CAPLENGTH */
+        return 0x01000000 | LEN_CAP;
+    case 0x04: /* HCSPARAMS 1 */
+        return (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+    case 0x08: /* HCSPARAMS 2 */
+        return 0x0000000f;
+    case 0x0c: /* HCSPARAMS 3 */
+        return 0x00000000;
+    case 0x10: /* HCCPARAMS */
+#if TARGET_PHYS_ADDR_BITS > 32
+        return 0x00081001;
+#else
+        return 0x00081000;
+#endif
+    case 0x14: /* DBOFF */
+        return OFF_DOORBELL;
+    case 0x18: /* RTSOFF */
+        return OFF_RUNTIME;
+
+    /* extended capabilities */
+    case 0x20: /* Supported Protocol:00 */
+#if USB3_PORTS > 0
+        return 0x02000402; /* USB 2.0 */
+#else
+        return 0x02000002; /* USB 2.0 */
+#endif
+    case 0x24: /* Supported Protocol:04 */
+        return 0x20425455; /* "USB " */
+    case 0x28: /* Supported Protocol:08 */
+        return 0x00000001 | (USB2_PORTS<<8);
+    case 0x2c: /* Supported Protocol:0c */
+        return 0x00000000; /* reserved */
+#if USB3_PORTS > 0
+    case 0x30: /* Supported Protocol:00 */
+        return 0x03000002; /* USB 3.0 */
+    case 0x34: /* Supported Protocol:04 */
+        return 0x20425455; /* "USB " */
+    case 0x38: /* Supported Protocol:08 */
+        return 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+    case 0x3c: /* Supported Protocol:0c */
+        return 0x00000000; /* reserved */
+#endif
+    default:
+        fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg);
+    }
+    return 0;
+}
+
+static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
+{
+    uint32_t port = reg >> 4;
+    if (port >= MAXPORTS) {
+        fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
+        return 0;
+    }
+
+    switch (reg & 0xf) {
+    case 0x00: /* PORTSC */
+        return xhci->ports[port].portsc;
+    case 0x04: /* PORTPMSC */
+    case 0x08: /* PORTLI */
+        return 0;
+    case 0x0c: /* reserved */
+    default:
+        fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
+                port, reg);
+        return 0;
+    }
+}
+
+static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    uint32_t port = reg >> 4;
+    uint32_t portsc;
+
+    if (port >= MAXPORTS) {
+        fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
+        return;
+    }
+
+    switch (reg & 0xf) {
+    case 0x00: /* PORTSC */
+        portsc = xhci->ports[port].portsc;
+        /* write-1-to-clear bits*/
+        portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
+                           PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
+        if (val & PORTSC_LWS) {
+            /* overwrite PLS only when LWS=1 */
+            portsc &= ~(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
+            portsc |= val & (PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
+        }
+        /* read/write bits */
+        portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
+        portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
+        /* write-1-to-start bits */
+        if (val & PORTSC_PR) {
+            DPRINTF("xhci: port %d reset\n", port);
+            usb_device_reset(xhci->ports[port].port.dev);
+            portsc |= PORTSC_PRC | PORTSC_PED;
+        }
+        xhci->ports[port].portsc = portsc;
+        break;
+    case 0x04: /* PORTPMSC */
+    case 0x08: /* PORTLI */
+    default:
+        fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n",
+                port, reg);
+    }
+}
+
+static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_oper_read(0x%x)\n", reg);
+
+    if (reg >= 0x400) {
+        return xhci_port_read(xhci, reg - 0x400);
+    }
+
+    switch (reg) {
+    case 0x00: /* USBCMD */
+        return xhci->usbcmd;
+    case 0x04: /* USBSTS */
+        return xhci->usbsts;
+    case 0x08: /* PAGESIZE */
+        return 1; /* 4KiB */
+    case 0x14: /* DNCTRL */
+        return xhci->dnctrl;
+    case 0x18: /* CRCR low */
+        return xhci->crcr_low & ~0xe;
+    case 0x1c: /* CRCR high */
+        return xhci->crcr_high;
+    case 0x30: /* DCBAAP low */
+        return xhci->dcbaap_low;
+    case 0x34: /* DCBAAP high */
+        return xhci->dcbaap_high;
+    case 0x38: /* CONFIG */
+        return xhci->config;
+    default:
+        fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg);
+    }
+    return 0;
+}
+
+static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    DPRINTF("xhci_oper_write(0x%x, 0x%08x)\n", reg, val);
+
+    if (reg >= 0x400) {
+        xhci_port_write(xhci, reg - 0x400, val);
+        return;
+    }
+
+    switch (reg) {
+    case 0x00: /* USBCMD */
+        if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) {
+            xhci_run(xhci);
+        } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) {
+            xhci_stop(xhci);
+        }
+        xhci->usbcmd = val & 0xc0f;
+        if (val & USBCMD_HCRST) {
+            xhci_reset(xhci);
+        }
+        xhci_irq_update(xhci);
+        break;
+
+    case 0x04: /* USBSTS */
+        /* these bits are write-1-to-clear */
+        xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
+        xhci_irq_update(xhci);
+        break;
+
+    case 0x14: /* DNCTRL */
+        xhci->dnctrl = val & 0xffff;
+        break;
+    case 0x18: /* CRCR low */
+        xhci->crcr_low = (val & 0xffffffcf) | (xhci->crcr_low & CRCR_CRR);
+        break;
+    case 0x1c: /* CRCR high */
+        xhci->crcr_high = val;
+        if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) {
+            XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED};
+            xhci->crcr_low &= ~CRCR_CRR;
+            xhci_event(xhci, &event);
+            DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
+        } else {
+            target_phys_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
+            xhci_ring_init(xhci, &xhci->cmd_ring, base);
+        }
+        xhci->crcr_low &= ~(CRCR_CA | CRCR_CS);
+        break;
+    case 0x30: /* DCBAAP low */
+        xhci->dcbaap_low = val & 0xffffffc0;
+        break;
+    case 0x34: /* DCBAAP high */
+        xhci->dcbaap_high = val;
+        break;
+    case 0x38: /* CONFIG */
+        xhci->config = val & 0xff;
+        break;
+    default:
+        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+    }
+}
+
+static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_runtime_read(0x%x)\n", reg);
+
+    switch (reg) {
+    case 0x00: /* MFINDEX */
+        fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
+        return xhci->mfindex;
+    case 0x20: /* IMAN */
+        return xhci->iman;
+    case 0x24: /* IMOD */
+        return xhci->imod;
+    case 0x28: /* ERSTSZ */
+        return xhci->erstsz;
+    case 0x30: /* ERSTBA low */
+        return xhci->erstba_low;
+    case 0x34: /* ERSTBA high */
+        return xhci->erstba_high;
+    case 0x38: /* ERDP low */
+        return xhci->erdp_low;
+    case 0x3c: /* ERDP high */
+        return xhci->erdp_high;
+    default:
+        fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
+    }
+    return 0;
+}
+
+static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    DPRINTF("xhci_runtime_write(0x%x, 0x%08x)\n", reg, val);
+
+    switch (reg) {
+    case 0x20: /* IMAN */
+        if (val & IMAN_IP) {
+            xhci->iman &= ~IMAN_IP;
+        }
+        xhci->iman &= ~IMAN_IE;
+        xhci->iman |= val & IMAN_IE;
+        xhci_irq_update(xhci);
+        break;
+    case 0x24: /* IMOD */
+        xhci->imod = val;
+        break;
+    case 0x28: /* ERSTSZ */
+        xhci->erstsz = val & 0xffff;
+        break;
+    case 0x30: /* ERSTBA low */
+        /* XXX NEC driver bug: it doesn't align this to 64 bytes
+        xhci->erstba_low = val & 0xffffffc0; */
+        xhci->erstba_low = val & 0xfffffff0;
+        break;
+    case 0x34: /* ERSTBA high */
+        xhci->erstba_high = val;
+        xhci_er_reset(xhci);
+        break;
+    case 0x38: /* ERDP low */
+        if (val & ERDP_EHB) {
+            xhci->erdp_low &= ~ERDP_EHB;
+        }
+        xhci->erdp_low = (val & ~ERDP_EHB) | (xhci->erdp_low & ERDP_EHB);
+        break;
+    case 0x3c: /* ERDP high */
+        xhci->erdp_high = val;
+        xhci_events_update(xhci);
+        break;
+    default:
+        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+    }
+}
+
+static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_doorbell_read(0x%x)\n", reg);
+    /* doorbells always read as 0 */
+    return 0;
+}
+
+static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    DPRINTF("xhci_doorbell_write(0x%x, 0x%08x)\n", reg, val);
+
+    if (!xhci_running(xhci)) {
+        fprintf(stderr, "xhci: wrote doorbell while xHC stopped or paused\n");
+        return;
+    }
+
+    reg >>= 2;
+
+    if (reg == 0) {
+        if (val == 0) {
+            xhci_process_commands(xhci);
+        } else {
+            fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", val);
+        }
+    } else {
+        if (reg > MAXSLOTS) {
+            fprintf(stderr, "xhci: bad doorbell %d\n", reg);
+        } else if (val > 31) {
+            fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", reg, val);
+        } else {
+            xhci_kick_ep(xhci, reg, val);
+        }
+    }
+}
+
+static uint64_t xhci_mem_read(void *ptr, target_phys_addr_t addr,
+                              unsigned size)
+{
+    XHCIState *xhci = ptr;
+
+    /* Only aligned reads are allowed on xHCI */
+    if (addr & 3) {
+        fprintf(stderr, "xhci_mem_read: Mis-aligned read\n");
+        return 0;
+    }
+
+    if (addr < LEN_CAP) {
+        return xhci_cap_read(xhci, addr);
+    } else if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
+        return xhci_oper_read(xhci, addr - OFF_OPER);
+    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
+        return xhci_runtime_read(xhci, addr - OFF_RUNTIME);
+    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
+        return xhci_doorbell_read(xhci, addr - OFF_DOORBELL);
+    } else {
+        fprintf(stderr, "xhci_mem_read: Bad offset %x\n", (int)addr);
+        return 0;
+    }
+}
+
+static void xhci_mem_write(void *ptr, target_phys_addr_t addr,
+                           uint64_t val, unsigned size)
+{
+    XHCIState *xhci = ptr;
+
+    /* Only aligned writes are allowed on xHCI */
+    if (addr & 3) {
+        fprintf(stderr, "xhci_mem_write: Mis-aligned write\n");
+        return;
+    }
+
+    if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
+        xhci_oper_write(xhci, addr - OFF_OPER, val);
+    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
+        xhci_runtime_write(xhci, addr - OFF_RUNTIME, val);
+    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
+        xhci_doorbell_write(xhci, addr - OFF_DOORBELL, val);
+    } else {
+        fprintf(stderr, "xhci_mem_write: Bad offset %x\n", (int)addr);
+    }
+}
+
+static const MemoryRegionOps xhci_mem_ops = {
+    .read = xhci_mem_read,
+    .write = xhci_mem_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void xhci_attach(USBPort *usbport)
+{
+    XHCIState *xhci = usbport->opaque;
+    XHCIPort *port = &xhci->ports[usbport->index];
+
+    xhci_update_port(xhci, port, 0);
+}
+
+static void xhci_detach(USBPort *usbport)
+{
+    XHCIState *xhci = usbport->opaque;
+    XHCIPort *port = &xhci->ports[usbport->index];
+
+    xhci_update_port(xhci, port, 1);
+}
+
+static void xhci_wakeup(USBPort *usbport)
+{
+    XHCIState *xhci = usbport->opaque;
+    XHCIPort *port = &xhci->ports[usbport->index];
+    int nr = port->port.index + 1;
+    XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
+    uint32_t pls;
+
+    pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
+    if (pls != 3) {
+        return;
+    }
+    port->portsc |= 0xf << PORTSC_PLS_SHIFT;
+    if (port->portsc & PORTSC_PLC) {
+        return;
+    }
+    port->portsc |= PORTSC_PLC;
+    xhci_event(xhci, &ev);
+}
+
+static void xhci_complete(USBPort *port, USBPacket *packet)
+{
+    XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
+
+    xhci_complete_packet(xfer, packet->result);
+    xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
+}
+
+static void xhci_child_detach(USBPort *port, USBDevice *child)
+{
+    FIXME();
+}
+
+static USBPortOps xhci_port_ops = {
+    .attach   = xhci_attach,
+    .detach   = xhci_detach,
+    .wakeup   = xhci_wakeup,
+    .complete = xhci_complete,
+    .child_detach = xhci_child_detach,
+};
+
+static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
+{
+    XHCISlot *slot;
+    int slotid;
+
+    for (slotid = 1; slotid <= MAXSLOTS; slotid++) {
+        slot = &xhci->slots[slotid-1];
+        if (slot->devaddr == dev->addr) {
+            return slotid;
+        }
+    }
+    return 0;
+}
+
+static int xhci_find_epid(USBEndpoint *ep)
+{
+    if (ep->nr == 0) {
+        return 1;
+    }
+    if (ep->pid == USB_TOKEN_IN) {
+        return ep->nr * 2 + 1;
+    } else {
+        return ep->nr * 2;
+    }
+}
+
+static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
+{
+    XHCIState *xhci = container_of(bus, XHCIState, bus);
+    int slotid;
+
+    DPRINTF("%s\n", __func__);
+    slotid = xhci_find_slotid(xhci, ep->dev);
+    if (slotid == 0 || !xhci->slots[slotid-1].enabled) {
+        DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
+        return;
+    }
+    xhci_kick_ep(xhci, slotid, xhci_find_epid(ep));
+}
+
+static USBBusOps xhci_bus_ops = {
+    .wakeup_endpoint = xhci_wakeup_endpoint,
+};
+
+static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
+{
+    int i;
+
+    xhci->usbsts = USBSTS_HCH;
+
+    usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev);
+
+    for (i = 0; i < MAXPORTS; i++) {
+        memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
+        usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
+                          &xhci_port_ops,
+                          USB_SPEED_MASK_LOW  |
+                          USB_SPEED_MASK_FULL |
+                          USB_SPEED_MASK_HIGH);
+    }
+    for (i = 0; i < MAXSLOTS; i++) {
+        xhci->slots[i].enabled = 0;
+    }
+
+    qemu_register_reset(xhci_reset, xhci);
+}
+
+static int usb_xhci_initfn(struct PCIDevice *dev)
+{
+    int ret;
+
+    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
+
+    xhci->pci_dev.config[PCI_CLASS_PROG] = 0x30;    /* xHCI */
+    xhci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
+    xhci->pci_dev.config[PCI_CACHE_LINE_SIZE] = 0x10;
+    xhci->pci_dev.config[0x60] = 0x30; /* release number */
+
+    usb_xhci_init(xhci, &dev->qdev);
+
+    xhci->irq = xhci->pci_dev.irq[0];
+
+    memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci,
+                          "xhci", LEN_REGS);
+    pci_register_bar(&xhci->pci_dev, 0,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
+                     &xhci->mem);
+
+    ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0);
+    assert(ret >= 0);
+
+    if (xhci->msi) {
+        ret = msi_init(&xhci->pci_dev, 0x70, 1, true, false);
+        assert(ret >= 0);
+    }
+
+    return 0;
+}
+
+static void xhci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val,
+                              int len)
+{
+    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
+
+    pci_default_write_config(dev, addr, val, len);
+    if (xhci->msi) {
+        msi_write_config(dev, addr, val, len);
+    }
+}
+
+static const VMStateDescription vmstate_xhci = {
+    .name = "xhci",
+    .unmigratable = 1,
+};
+
+static Property xhci_properties[] = {
+    DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xhci_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_xhci;
+    dc->props   = xhci_properties;
+    k->init         = usb_xhci_initfn;
+    k->vendor_id    = PCI_VENDOR_ID_NEC;
+    k->device_id    = PCI_DEVICE_ID_NEC_UPD720200;
+    k->class_id     = PCI_CLASS_SERIAL_USB;
+    k->revision     = 0x03;
+    k->is_express   = 1;
+    k->config_write = xhci_write_config;
+}
+
+static TypeInfo xhci_info = {
+    .name          = "nec-usb-xhci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(XHCIState),
+    .class_init    = xhci_class_init,
+};
+
+static void xhci_register_types(void)
+{
+    type_register_static(&xhci_info);
+}
+
+type_init(xhci_register_types)
diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c
new file mode 100644 (file)
index 0000000..ec26266
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * BSD host USB redirector
+ *
+ * Copyright (c) 2006 Lonnie Mendez
+ * Portions of code and concepts borrowed from
+ * usb-linux.c and libusb's bsd.c and are copyright their respective owners.
+ *
+ * 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 "monitor.h"
+#include "hw/usb.h"
+
+/* usb.h declares these */
+#undef USB_SPEED_HIGH
+#undef USB_SPEED_FULL
+#undef USB_SPEED_LOW
+
+#include <sys/ioctl.h>
+#ifndef __DragonFly__
+#include <dev/usb/usb.h>
+#else
+#include <bus/usb/usb.h>
+#endif
+
+/* This value has maximum potential at 16.
+ * You should also set hw.usb.debug to gain
+ * more detailed view.
+ */
+//#define DEBUG
+#define UGEN_DEBUG_LEVEL 0
+
+
+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);
+
+typedef struct USBHostDevice {
+    USBDevice dev;
+    int ep_fd[USB_MAX_ENDPOINTS];
+    int devfd;
+    char devpath[32];
+} USBHostDevice;
+
+
+static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
+{
+    char buf[32];
+    int fd;
+
+    /* Get the address for this endpoint */
+    ep = UE_GET_ADDR(ep);
+
+    if (dev->ep_fd[ep] < 0) {
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep);
+#else
+        snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep);
+#endif
+        /* Try to open it O_RDWR first for those devices which have in and out
+         * endpoints with the same address (eg 0x02 and 0x82)
+         */
+        fd = open(buf, O_RDWR);
+        if (fd < 0 && errno == ENXIO)
+            fd = open(buf, mode);
+        if (fd < 0) {
+#ifdef DEBUG
+            printf("ensure_ep_open: failed to open device endpoint %s: %s\n",
+                   buf, strerror(errno));
+#endif
+        }
+        dev->ep_fd[ep] = fd;
+    }
+
+    return dev->ep_fd[ep];
+}
+
+static void ensure_eps_closed(USBHostDevice *dev)
+{
+    int epnum = 1;
+
+    if (!dev)
+        return;
+
+    while (epnum < USB_MAX_ENDPOINTS) {
+        if (dev->ep_fd[epnum] >= 0) {
+            close(dev->ep_fd[epnum]);
+            dev->ep_fd[epnum] = -1;
+        }
+        epnum++;
+    }
+}
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+#if 0
+    USBHostDevice *s = (USBHostDevice *)dev;
+#endif
+}
+
+/* XXX:
+ * -check device states against transfer requests
+ *  and return appropriate response
+ */
+static int usb_host_handle_control(USBDevice *dev,
+                                   USBPacket *p,
+                                   int request,
+                                   int value,
+                                   int index,
+                                   int length,
+                                   uint8_t *data)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+    struct usb_ctl_request req;
+    struct usb_alt_interface aiface;
+    int ret, timeout = 50;
+
+    if ((request >> 8) == UT_WRITE_DEVICE &&
+        (request & 0xff) == UR_SET_ADDRESS) {
+
+        /* specific SET_ADDRESS support */
+        dev->addr = value;
+        return 0;
+    } else if ((request >> 8) == UT_WRITE_DEVICE &&
+               (request & 0xff) == UR_SET_CONFIG) {
+
+        ensure_eps_closed(s); /* can't do this without all eps closed */
+
+        ret = ioctl(s->devfd, USB_SET_CONFIG, &value);
+        if (ret < 0) {
+#ifdef DEBUG
+            printf("handle_control: failed to set configuration - %s\n",
+                   strerror(errno));
+#endif
+            return USB_RET_STALL;
+        }
+
+        return 0;
+    } else if ((request >> 8) == UT_WRITE_INTERFACE &&
+               (request & 0xff) == UR_SET_INTERFACE) {
+
+        aiface.uai_interface_index = index;
+        aiface.uai_alt_no = value;
+
+        ensure_eps_closed(s); /* can't do this without all eps closed */
+        ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface);
+        if (ret < 0) {
+#ifdef DEBUG
+            printf("handle_control: failed to set alternate interface - %s\n",
+                   strerror(errno));
+#endif
+            return USB_RET_STALL;
+        }
+
+        return 0;
+    } else {
+        req.ucr_request.bmRequestType = request >> 8;
+        req.ucr_request.bRequest = request & 0xff;
+        USETW(req.ucr_request.wValue, value);
+        USETW(req.ucr_request.wIndex, index);
+        USETW(req.ucr_request.wLength, length);
+        req.ucr_data = data;
+        req.ucr_flags = USBD_SHORT_XFER_OK;
+
+        ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+        if (ret < 0 && errno != EINVAL) {
+#else
+        if (ret < 0) {
+#endif
+#ifdef DEBUG
+            printf("handle_control: setting timeout failed - %s\n",
+                   strerror(errno));
+#endif
+        }
+
+        ret = ioctl(s->devfd, USB_DO_REQUEST, &req);
+        /* ugen returns EIO for usbd_do_request_ no matter what
+         * happens with the transfer */
+        if (ret < 0) {
+#ifdef DEBUG
+            printf("handle_control: error after request - %s\n",
+                   strerror(errno));
+#endif
+            return USB_RET_NAK; // STALL
+        } else {
+            return req.ucr_actlen;
+        }
+    }
+}
+
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+    int ret, fd, mode;
+    int one = 1, shortpacket = 0, timeout = 50;
+    sigset_t new_mask, old_mask;
+    uint8_t devep = p->ep->nr;
+
+    /* protect data transfers from SIGALRM signal */
+    sigemptyset(&new_mask);
+    sigaddset(&new_mask, SIGALRM);
+    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
+
+    if (p->pid == USB_TOKEN_IN) {
+        devep |= 0x80;
+        mode = O_RDONLY;
+        shortpacket = 1;
+    } else {
+        mode = O_WRONLY;
+    }
+
+    fd = ensure_ep_open(s, devep, mode);
+    if (fd < 0) {
+        sigprocmask(SIG_SETMASK, &old_mask, NULL);
+        return USB_RET_NODEV;
+    }
+
+    if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
+#ifdef DEBUG
+        printf("handle_data: failed to set timeout - %s\n",
+               strerror(errno));
+#endif
+    }
+
+    if (shortpacket) {
+        if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) {
+#ifdef DEBUG
+            printf("handle_data: failed to set short xfer mode - %s\n",
+                   strerror(errno));
+#endif
+            sigprocmask(SIG_SETMASK, &old_mask, NULL);
+        }
+    }
+
+    if (p->pid == USB_TOKEN_IN)
+        ret = readv(fd, p->iov.iov, p->iov.niov);
+    else
+        ret = writev(fd, p->iov.iov, p->iov.niov);
+
+    sigprocmask(SIG_SETMASK, &old_mask, NULL);
+
+    if (ret < 0) {
+#ifdef DEBUG
+        printf("handle_data: error after %s data - %s\n",
+               pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno));
+#endif
+        switch(errno) {
+        case ETIMEDOUT:
+        case EINTR:
+            return USB_RET_NAK;
+        default:
+            return USB_RET_STALL;
+        }
+    } else {
+        return ret;
+    }
+}
+
+static void usb_host_handle_destroy(USBDevice *opaque)
+{
+    USBHostDevice *s = (USBHostDevice *)opaque;
+    int i;
+
+    for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+        if (s->ep_fd[i] >= 0)
+            close(s->ep_fd[i]);
+
+    if (s->devfd < 0)
+        return;
+
+    close(s->devfd);
+
+    g_free(s);
+}
+
+static int usb_host_initfn(USBDevice *dev)
+{
+    return 0;
+}
+
+USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname)
+{
+    struct usb_device_info bus_info, dev_info;
+    USBDevice *d = NULL, *ret = NULL;
+    USBHostDevice *dev;
+    char ctlpath[PATH_MAX + 1];
+    char buspath[PATH_MAX + 1];
+    int bfd, dfd, bus, address, i;
+    int ugendebug = UGEN_DEBUG_LEVEL;
+
+    if (usb_host_find_device(&bus, &address, devname) < 0) {
+        goto fail;
+    }
+
+    snprintf(buspath, PATH_MAX, "/dev/usb%d", bus);
+
+    bfd = open(buspath, O_RDWR);
+    if (bfd < 0) {
+#ifdef DEBUG
+        printf("usb_host_device_open: failed to open usb bus - %s\n",
+               strerror(errno));
+#endif
+        goto fail;
+    }
+
+    bus_info.udi_addr = address;
+    if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) {
+#ifdef DEBUG
+        printf("usb_host_device_open: failed to grab bus information - %s\n",
+               strerror(errno));
+#endif
+        goto fail_bfd;
+    }
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+    snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]);
+#else
+    snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]);
+#endif
+
+    dfd  = open(ctlpath, O_RDWR);
+    if (dfd < 0) {
+        dfd = open(ctlpath, O_RDONLY);
+        if (dfd < 0) {
+#ifdef DEBUG
+            printf("usb_host_device_open: failed to open usb device %s - %s\n",
+                   ctlpath, strerror(errno));
+#endif
+        }
+        goto fail_dfd;
+    }
+
+    if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) {
+#ifdef DEBUG
+        printf("usb_host_device_open: failed to grab device info - %s\n",
+               strerror(errno));
+#endif
+        goto fail_dfd;
+    }
+
+    d = usb_create(guest_bus, "usb-host");
+    dev = DO_UPCAST(USBHostDevice, dev, d);
+
+    if (dev_info.udi_speed == 1) {
+        dev->dev.speed = USB_SPEED_LOW - 1;
+        dev->dev.speedmask = USB_SPEED_MASK_LOW;
+    } else {
+        dev->dev.speed = USB_SPEED_FULL - 1;
+        dev->dev.speedmask = USB_SPEED_MASK_FULL;
+    }
+
+    if (strncmp(dev_info.udi_product, "product", 7) != 0) {
+        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                dev_info.udi_product);
+    } else {
+        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                 "host:%s", devname);
+    }
+
+    pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/");
+    pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]);
+
+    /* Mark the endpoints as not yet open */
+    for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+        dev->ep_fd[i] = -1;
+    }
+
+    ioctl(dfd, USB_SETDEBUG, &ugendebug);
+
+    ret = (USBDevice *)dev;
+
+fail_dfd:
+    close(dfd);
+fail_bfd:
+    close(bfd);
+fail:
+    return ret;
+}
+
+static void usb_host_class_initfn(ObjectClass *klass, void *data)
+{
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->product_desc   = "USB Host Device";
+    uc->init           = usb_host_initfn;
+    uc->handle_reset   = usb_host_handle_reset;
+    uc->handle_control = usb_host_handle_control;
+    uc->handle_data    = usb_host_handle_data;
+    uc->handle_destroy = usb_host_handle_destroy;
+}
+
+static TypeInfo usb_host_dev_info = {
+    .name          = "usb-host",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHostDevice),
+    .class_init    = usb_host_class_initfn,
+};
+
+static void usb_host_register_types(void)
+{
+    type_register_static(&usb_host_dev_info);
+}
+
+type_init(usb_host_register_types)
+
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+    struct usb_device_info bus_info;
+    struct usb_device_info dev_info;
+    uint16_t vendor_id, product_id, class_id, speed;
+    int bfd, dfd, bus, address;
+    char busbuf[20], devbuf[20], product_name[256];
+    int ret = 0;
+
+    for (bus = 0; bus < 10; bus++) {
+
+        snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus);
+        bfd = open(busbuf, O_RDWR);
+        if (bfd < 0)
+           continue;
+
+        for (address = 1; address < 127; address++) {
+
+            bus_info.udi_addr = address;
+            if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0)
+                continue;
+
+            /* only list devices that can be used by generic layer */
+            if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0)
+                continue;
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]);
+#else
+            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]);
+#endif
+
+            dfd = open(devbuf, O_RDONLY);
+            if (dfd < 0) {
+#ifdef DEBUG
+                printf("usb_host_scan: couldn't open device %s - %s\n", devbuf,
+                       strerror(errno));
+#endif
+                continue;
+            }
+
+            if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0)
+                printf("usb_host_scan: couldn't get device information for %s - %s\n",
+                       devbuf, strerror(errno));
+
+            /* XXX: might need to fixup endianness of word values before copying over */
+
+            vendor_id = dev_info.udi_vendorNo;
+            product_id = dev_info.udi_productNo;
+            class_id = dev_info.udi_class;
+            speed = dev_info.udi_speed;
+
+            if (strncmp(dev_info.udi_product, "product", 7) != 0)
+                pstrcpy(product_name, sizeof(product_name),
+                        dev_info.udi_product);
+            else
+                product_name[0] = '\0';
+
+            ret = func(opaque, bus, address, class_id, vendor_id,
+                       product_id, product_name, speed);
+
+            close(dfd);
+
+            if (ret)
+                goto the_end;
+        }
+
+        close(bfd);
+    }
+
+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 (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)
+{
+    const char *p;
+    int ret;
+    FindDeviceState fs;
+
+    p = strchr(devname, '.');
+    if (p) {
+        *pbus_num = strtoul(devname, NULL, 0);
+        *paddr = strtoul(p + 1, NULL, 0);
+        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, 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;
+}
+
+static void usb_info_device(Monitor *mon, 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;
+    }
+
+    monitor_printf(mon, "  Device %d.%d, speed %s Mb/s\n",
+                   bus_num, addr, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str)
+        monitor_printf(mon, "    %s:", class_str);
+    else
+        monitor_printf(mon, "    Class %02x:", class_id);
+    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0')
+        monitor_printf(mon, ", %s", product_name);
+    monitor_printf(mon, "\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)
+{
+    Monitor *mon = opaque;
+
+    usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+void usb_host_info(Monitor *mon)
+{
+    usb_host_scan(mon, usb_host_info_device);
+}
+
+/* XXX add this */
+int usb_host_device_close(const char *devname)
+{
+    return 0;
+}
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
new file mode 100644 (file)
index 0000000..90919c2
--- /dev/null
@@ -0,0 +1,1913 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *      Support for host device auto connect & disconnect
+ *      Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ *      to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * 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 "qemu-timer.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "trace.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#include "hw/usb.h"
+
+/* We redefine it to avoid version problems */
+struct usb_ctrltransfer {
+    uint8_t  bRequestType;
+    uint8_t  bRequest;
+    uint16_t wValue;
+    uint16_t wIndex;
+    uint16_t wLength;
+    uint32_t timeout;
+    void *data;
+};
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
+                        int class_id, int vendor_id, int product_id,
+                        const char *product_name, int speed);
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#define PRODUCT_NAME_SZ 32
+#define MAX_PORTLEN 16
+
+/* endpoint association data */
+#define ISO_FRAME_DESC_PER_URB 32
+
+/* devio.c limits single requests to 16k */
+#define MAX_USBFS_BUFFER_SIZE 16384
+
+typedef struct AsyncURB AsyncURB;
+
+struct endp_data {
+    uint8_t halted;
+    uint8_t iso_started;
+    AsyncURB *iso_urb;
+    int iso_urb_idx;
+    int iso_buffer_used;
+    int inflight;
+};
+
+struct USBAutoFilter {
+    uint32_t bus_num;
+    uint32_t addr;
+    char     *port;
+    uint32_t vendor_id;
+    uint32_t product_id;
+};
+
+typedef struct USBHostDevice {
+    USBDevice dev;
+    int       fd;
+    int       hub_fd;
+    int       hub_port;
+
+    uint8_t   descr[8192];
+    int       descr_len;
+    int       closing;
+    uint32_t  iso_urb_count;
+    Notifier  exit;
+
+    struct endp_data ep_in[USB_MAX_ENDPOINTS];
+    struct endp_data ep_out[USB_MAX_ENDPOINTS];
+    QLIST_HEAD(, AsyncURB) aurbs;
+
+    /* Host side address */
+    int bus_num;
+    int addr;
+    char port[MAX_PORTLEN];
+    struct USBAutoFilter match;
+    int seen, errcount;
+
+    QTAILQ_ENTRY(USBHostDevice) next;
+} USBHostDevice;
+
+static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
+
+static int usb_host_close(USBHostDevice *dev);
+static int parse_filter(const char *spec, struct USBAutoFilter *f);
+static void usb_host_auto_check(void *unused);
+static int usb_host_read_file(char *line, size_t line_size,
+                            const char *device_file, const char *device_name);
+static int usb_linux_update_endp_table(USBHostDevice *s);
+
+static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p)
+{
+    static const int usbfs[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL,
+        [USB_ENDPOINT_XFER_ISOC]    = USBDEVFS_URB_TYPE_ISO,
+        [USB_ENDPOINT_XFER_BULK]    = USBDEVFS_URB_TYPE_BULK,
+        [USB_ENDPOINT_XFER_INT]     = USBDEVFS_URB_TYPE_INTERRUPT,
+    };
+    uint8_t type = p->ep->type;
+    assert(type < ARRAY_SIZE(usbfs));
+    return usbfs[type];
+}
+
+static int usb_host_do_reset(USBHostDevice *dev)
+{
+    struct timeval s, e;
+    uint32_t usecs;
+    int ret;
+
+    gettimeofday(&s, NULL);
+    ret = ioctl(dev->fd, USBDEVFS_RESET);
+    gettimeofday(&e, NULL);
+    usecs = (e.tv_sec  - s.tv_sec) * 1000000;
+    usecs += e.tv_usec - s.tv_usec;
+    if (usecs > 1000000) {
+        /* more than a second, something is fishy, broken usb device? */
+        fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n",
+                dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000);
+    }
+    return ret;
+}
+
+static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
+{
+    struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
+    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
+    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
+    return eps + ep - 1;
+}
+
+static int is_isoc(USBHostDevice *s, int pid, int ep)
+{
+    return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
+}
+
+static int is_valid(USBHostDevice *s, int pid, int ep)
+{
+    return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
+}
+
+static int is_halted(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->halted;
+}
+
+static void clear_halt(USBHostDevice *s, int pid, int ep)
+{
+    trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep);
+    get_endp(s, pid, ep)->halted = 0;
+}
+
+static void set_halt(USBHostDevice *s, int pid, int ep)
+{
+    if (ep != 0) {
+        trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep);
+        get_endp(s, pid, ep)->halted = 1;
+    }
+}
+
+static int is_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_started;
+}
+
+static void clear_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep);
+    get_endp(s, pid, ep)->iso_started = 0;
+}
+
+static void set_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    struct endp_data *e = get_endp(s, pid, ep);
+
+    trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep);
+    if (!e->iso_started) {
+        e->iso_started = 1;
+        e->inflight = 0;
+    }
+}
+
+static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
+{
+    struct endp_data *e = get_endp(s, pid, ep);
+
+    e->inflight += value;
+    return e->inflight;
+}
+
+static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
+{
+    get_endp(s, pid, ep)->iso_urb = iso_urb;
+}
+
+static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_urb;
+}
+
+static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
+{
+    get_endp(s, pid, ep)->iso_urb_idx = i;
+}
+
+static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_urb_idx;
+}
+
+static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
+{
+    get_endp(s, pid, ep)->iso_buffer_used = i;
+}
+
+static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_buffer_used;
+}
+
+/*
+ * Async URB state.
+ * We always allocate iso packet descriptors even for bulk transfers
+ * to simplify allocation and casts.
+ */
+struct AsyncURB
+{
+    struct usbdevfs_urb urb;
+    struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
+    USBHostDevice *hdev;
+    QLIST_ENTRY(AsyncURB) next;
+
+    /* For regular async urbs */
+    USBPacket     *packet;
+    int more; /* large transfer, more urbs follow */
+
+    /* For buffered iso handling */
+    int iso_frame_idx; /* -1 means in flight */
+};
+
+static AsyncURB *async_alloc(USBHostDevice *s)
+{
+    AsyncURB *aurb = g_malloc0(sizeof(AsyncURB));
+    aurb->hdev = s;
+    QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
+    return aurb;
+}
+
+static void async_free(AsyncURB *aurb)
+{
+    QLIST_REMOVE(aurb, next);
+    g_free(aurb);
+}
+
+static void do_disconnect(USBHostDevice *s)
+{
+    usb_host_close(s);
+    usb_host_auto_check(NULL);
+}
+
+static void async_complete(void *opaque)
+{
+    USBHostDevice *s = opaque;
+    AsyncURB *aurb;
+    int urbs = 0;
+
+    while (1) {
+        USBPacket *p;
+
+        int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
+        if (r < 0) {
+            if (errno == EAGAIN) {
+                if (urbs > 2) {
+                    fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs);
+                }
+                return;
+            }
+            if (errno == ENODEV) {
+                if (!s->closing) {
+                    trace_usb_host_disconnect(s->bus_num, s->addr);
+                    do_disconnect(s);
+                }
+                return;
+            }
+
+            perror("USBDEVFS_REAPURBNDELAY");
+            return;
+        }
+
+        DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
+                aurb, aurb->urb.status, aurb->urb.actual_length);
+
+        /* If this is a buffered iso urb mark it as complete and don't do
+           anything else (it is handled further in usb_host_handle_iso_data) */
+        if (aurb->iso_frame_idx == -1) {
+            int inflight;
+            int pid = (aurb->urb.endpoint & USB_DIR_IN) ?
+                USB_TOKEN_IN : USB_TOKEN_OUT;
+            int ep = aurb->urb.endpoint & 0xf;
+            if (aurb->urb.status == -EPIPE) {
+                set_halt(s, pid, ep);
+            }
+            aurb->iso_frame_idx = 0;
+            urbs++;
+            inflight = change_iso_inflight(s, pid, ep, -1);
+            if (inflight == 0 && is_iso_started(s, pid, ep)) {
+                fprintf(stderr, "husb: out of buffers for iso stream\n");
+            }
+            continue;
+        }
+
+        p = aurb->packet;
+        trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status,
+                                    aurb->urb.actual_length, aurb->more);
+
+        if (p) {
+            switch (aurb->urb.status) {
+            case 0:
+                p->result += aurb->urb.actual_length;
+                break;
+
+            case -EPIPE:
+                set_halt(s, p->pid, p->ep->nr);
+                p->result = USB_RET_STALL;
+                break;
+
+            case -EOVERFLOW:
+                p->result = USB_RET_BABBLE;
+                break;
+
+            default:
+                p->result = USB_RET_IOERROR;
+                break;
+            }
+
+            if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
+                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+                usb_generic_async_ctrl_complete(&s->dev, p);
+            } else if (!aurb->more) {
+                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+                usb_packet_complete(&s->dev, p);
+            }
+        }
+
+        async_free(aurb);
+    }
+}
+
+static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    AsyncURB *aurb;
+
+    QLIST_FOREACH(aurb, &s->aurbs, next) {
+        if (p != aurb->packet) {
+            continue;
+        }
+
+        DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
+
+        /* Mark it as dead (see async_complete above) */
+        aurb->packet = NULL;
+
+        int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+        if (r < 0) {
+            DPRINTF("husb: async. discard urb failed errno %d\n", errno);
+        }
+    }
+}
+
+static int usb_host_open_device(int bus, int addr)
+{
+    const char *usbfs = NULL;
+    char filename[32];
+    struct stat st;
+    int fd, rc;
+
+    rc = stat("/dev/bus/usb", &st);
+    if (rc == 0 && S_ISDIR(st.st_mode)) {
+        /* udev-created device nodes available */
+        usbfs = "/dev/bus/usb";
+    } else {
+        /* fallback: usbfs mounted below /proc */
+        usbfs = "/proc/bus/usb";
+    }
+
+    snprintf(filename, sizeof(filename), "%s/%03d/%03d",
+             usbfs, bus, addr);
+    fd = open(filename, O_RDWR | O_NONBLOCK);
+    if (fd < 0) {
+        fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno));
+    }
+    return fd;
+}
+
+static int usb_host_claim_port(USBHostDevice *s)
+{
+#ifdef USBDEVFS_CLAIM_PORT
+    char *h, hub_name[64], line[1024];
+    int hub_addr, ret;
+
+    snprintf(hub_name, sizeof(hub_name), "%d-%s",
+             s->match.bus_num, s->match.port);
+
+    /* try strip off last ".$portnr" to get hub */
+    h = strrchr(hub_name, '.');
+    if (h != NULL) {
+        s->hub_port = atoi(h+1);
+        *h = '\0';
+    } else {
+        /* no dot in there -> it is the root hub */
+        snprintf(hub_name, sizeof(hub_name), "usb%d",
+                 s->match.bus_num);
+        s->hub_port = atoi(s->match.port);
+    }
+
+    if (!usb_host_read_file(line, sizeof(line), "devnum",
+                            hub_name)) {
+        return -1;
+    }
+    if (sscanf(line, "%d", &hub_addr) != 1) {
+        return -1;
+    }
+
+    s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
+    if (s->hub_fd < 0) {
+        return -1;
+    }
+
+    ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port);
+    if (ret < 0) {
+        close(s->hub_fd);
+        s->hub_fd = -1;
+        return -1;
+    }
+
+    trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port);
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+static void usb_host_release_port(USBHostDevice *s)
+{
+    if (s->hub_fd == -1) {
+        return;
+    }
+#ifdef USBDEVFS_RELEASE_PORT
+    ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port);
+#endif
+    close(s->hub_fd);
+    s->hub_fd = -1;
+}
+
+static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
+{
+    /* earlier Linux 2.4 do not support that */
+#ifdef USBDEVFS_DISCONNECT
+    struct usbdevfs_ioctl ctrl;
+    int ret, interface;
+
+    for (interface = 0; interface < nb_interfaces; interface++) {
+        ctrl.ioctl_code = USBDEVFS_DISCONNECT;
+        ctrl.ifno = interface;
+        ctrl.data = 0;
+        ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
+        if (ret < 0 && errno != ENODATA) {
+            perror("USBDEVFS_DISCONNECT");
+            return -1;
+        }
+    }
+#endif
+    return 0;
+}
+
+static int usb_linux_get_num_interfaces(USBHostDevice *s)
+{
+    char device_name[64], line[1024];
+    int num_interfaces = 0;
+
+    sprintf(device_name, "%d-%s", s->bus_num, s->port);
+    if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
+                            device_name)) {
+        return -1;
+    }
+    if (sscanf(line, "%d", &num_interfaces) != 1) {
+        return -1;
+    }
+    return num_interfaces;
+}
+
+static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
+{
+    const char *op = NULL;
+    int dev_descr_len, config_descr_len;
+    int interface, nb_interfaces;
+    int ret, i;
+
+    for (i = 0; i < USB_MAX_INTERFACES; i++) {
+        dev->dev.altsetting[i] = 0;
+    }
+
+    if (configuration == 0) { /* address state - ignore */
+        dev->dev.ninterfaces   = 0;
+        dev->dev.configuration = 0;
+        return 1;
+    }
+
+    DPRINTF("husb: claiming interfaces. config %d\n", configuration);
+
+    i = 0;
+    dev_descr_len = dev->descr[0];
+    if (dev_descr_len > dev->descr_len) {
+        fprintf(stderr, "husb: update iface failed. descr too short\n");
+        return 0;
+    }
+
+    i += dev_descr_len;
+    while (i < dev->descr_len) {
+        DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
+                i, dev->descr_len,
+               dev->descr[i], dev->descr[i+1]);
+
+        if (dev->descr[i+1] != USB_DT_CONFIG) {
+            i += dev->descr[i];
+            continue;
+        }
+        config_descr_len = dev->descr[i];
+
+        DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
+
+        if (configuration == dev->descr[i + 5]) {
+            configuration = dev->descr[i + 5];
+            break;
+        }
+
+        i += config_descr_len;
+    }
+
+    if (i >= dev->descr_len) {
+        fprintf(stderr,
+                "husb: update iface failed. no matching configuration\n");
+        return 0;
+    }
+    nb_interfaces = dev->descr[i + 4];
+
+    if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
+        goto fail;
+    }
+
+    /* XXX: only grab if all interfaces are free */
+    for (interface = 0; interface < nb_interfaces; interface++) {
+        op = "USBDEVFS_CLAIMINTERFACE";
+        ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+        if (ret < 0) {
+            goto fail;
+        }
+    }
+
+    trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
+                                    nb_interfaces, configuration);
+
+    dev->dev.ninterfaces   = nb_interfaces;
+    dev->dev.configuration = configuration;
+    return 1;
+
+fail:
+    if (errno == ENODEV) {
+        do_disconnect(dev);
+    }
+    perror(op);
+    return 0;
+}
+
+static int usb_host_release_interfaces(USBHostDevice *s)
+{
+    int ret, i;
+
+    trace_usb_host_release_interfaces(s->bus_num, s->addr);
+
+    for (i = 0; i < s->dev.ninterfaces; i++) {
+        ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
+        if (ret < 0) {
+            perror("USBDEVFS_RELEASEINTERFACE");
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+
+    trace_usb_host_reset(s->bus_num, s->addr);
+
+    usb_host_do_reset(s);;
+
+    usb_host_claim_interfaces(s, 0);
+    usb_linux_update_endp_table(s);
+}
+
+static void usb_host_handle_destroy(USBDevice *dev)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+
+    usb_host_release_port(s);
+    usb_host_close(s);
+    QTAILQ_REMOVE(&hostdevs, s, next);
+    qemu_remove_exit_notifier(&s->exit);
+}
+
+/* iso data is special, we need to keep enough urbs in flight to make sure
+   that the controller never runs out of them, otherwise the device will
+   likely suffer a buffer underrun / overrun. */
+static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
+{
+    AsyncURB *aurb;
+    int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
+
+    aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
+    for (i = 0; i < s->iso_urb_count; i++) {
+        aurb[i].urb.endpoint      = ep;
+        aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
+        aurb[i].urb.buffer        = g_malloc(aurb[i].urb.buffer_length);
+        aurb[i].urb.type          = USBDEVFS_URB_TYPE_ISO;
+        aurb[i].urb.flags         = USBDEVFS_URB_ISO_ASAP;
+        aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
+        for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
+            aurb[i].urb.iso_frame_desc[j].length = len;
+        if (pid == USB_TOKEN_IN) {
+            aurb[i].urb.endpoint |= 0x80;
+            /* Mark as fully consumed (idle) */
+            aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
+        }
+    }
+    set_iso_urb(s, pid, ep, aurb);
+
+    return aurb;
+}
+
+static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
+{
+    AsyncURB *aurb;
+    int i, ret, killed = 0, free = 1;
+
+    aurb = get_iso_urb(s, pid, ep);
+    if (!aurb) {
+        return;
+    }
+
+    for (i = 0; i < s->iso_urb_count; i++) {
+        /* in flight? */
+        if (aurb[i].iso_frame_idx == -1) {
+            ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
+            if (ret < 0) {
+                perror("USBDEVFS_DISCARDURB");
+                free = 0;
+                continue;
+            }
+            killed++;
+        }
+    }
+
+    /* Make sure any urbs we've killed are reaped before we free them */
+    if (killed) {
+        async_complete(s);
+    }
+
+    for (i = 0; i < s->iso_urb_count; i++) {
+        g_free(aurb[i].urb.buffer);
+    }
+
+    if (free)
+        g_free(aurb);
+    else
+        printf("husb: leaking iso urbs because of discard failure\n");
+    set_iso_urb(s, pid, ep, NULL);
+    set_iso_urb_idx(s, pid, ep, 0);
+    clear_iso_started(s, pid, ep);
+}
+
+static int urb_status_to_usb_ret(int status)
+{
+    switch (status) {
+    case -EPIPE:
+        return USB_RET_STALL;
+    case -EOVERFLOW:
+        return USB_RET_BABBLE;
+    default:
+        return USB_RET_IOERROR;
+    }
+}
+
+static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
+{
+    AsyncURB *aurb;
+    int i, j, ret, max_packet_size, offset, len = 0;
+    uint8_t *buf;
+
+    max_packet_size = p->ep->max_packet_size;
+    if (max_packet_size == 0)
+        return USB_RET_NAK;
+
+    aurb = get_iso_urb(s, p->pid, p->ep->nr);
+    if (!aurb) {
+        aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr);
+    }
+
+    i = get_iso_urb_idx(s, p->pid, p->ep->nr);
+    j = aurb[i].iso_frame_idx;
+    if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
+        if (in) {
+            /* Check urb status  */
+            if (aurb[i].urb.status) {
+                len = urb_status_to_usb_ret(aurb[i].urb.status);
+                /* Move to the next urb */
+                aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
+            /* Check frame status */
+            } else if (aurb[i].urb.iso_frame_desc[j].status) {
+                len = urb_status_to_usb_ret(
+                                        aurb[i].urb.iso_frame_desc[j].status);
+            /* Check the frame fits */
+            } else if (aurb[i].urb.iso_frame_desc[j].actual_length
+                       > p->iov.size) {
+                printf("husb: received iso data is larger then packet\n");
+                len = USB_RET_BABBLE;
+            /* All good copy data over */
+            } else {
+                len = aurb[i].urb.iso_frame_desc[j].actual_length;
+                buf  = aurb[i].urb.buffer +
+                    j * aurb[i].urb.iso_frame_desc[0].length;
+                usb_packet_copy(p, buf, len);
+            }
+        } else {
+            len = p->iov.size;
+            offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr);
+
+            /* Check the frame fits */
+            if (len > max_packet_size) {
+                printf("husb: send iso data is larger then max packet size\n");
+                return USB_RET_NAK;
+            }
+
+            /* All good copy data over */
+            usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
+            aurb[i].urb.iso_frame_desc[j].length = len;
+            offset += len;
+            set_iso_buffer_used(s, p->pid, p->ep->nr, offset);
+
+            /* Start the stream once we have buffered enough data */
+            if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) {
+                set_iso_started(s, p->pid, p->ep->nr);
+            }
+        }
+        aurb[i].iso_frame_idx++;
+        if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
+            i = (i + 1) % s->iso_urb_count;
+            set_iso_urb_idx(s, p->pid, p->ep->nr, i);
+        }
+    } else {
+        if (in) {
+            set_iso_started(s, p->pid, p->ep->nr);
+        } else {
+            DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
+        }
+    }
+
+    if (is_iso_started(s, p->pid, p->ep->nr)) {
+        /* (Re)-submit all fully consumed / filled urbs */
+        for (i = 0; i < s->iso_urb_count; i++) {
+            if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
+                ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
+                if (ret < 0) {
+                    perror("USBDEVFS_SUBMITURB");
+                    if (!in || len == 0) {
+                        switch(errno) {
+                        case ETIMEDOUT:
+                            len = USB_RET_NAK;
+                            break;
+                        case EPIPE:
+                        default:
+                            len = USB_RET_STALL;
+                        }
+                    }
+                    break;
+                }
+                aurb[i].iso_frame_idx = -1;
+                change_iso_inflight(s, p->pid, p->ep->nr, 1);
+            }
+        }
+    }
+
+    return len;
+}
+
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    struct usbdevfs_urb *urb;
+    AsyncURB *aurb;
+    int ret, rem, prem, v;
+    uint8_t *pbuf;
+    uint8_t ep;
+
+    trace_usb_host_req_data(s->bus_num, s->addr,
+                            p->pid == USB_TOKEN_IN,
+                            p->ep->nr, p->iov.size);
+
+    if (!is_valid(s, p->pid, p->ep->nr)) {
+        trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+        return USB_RET_NAK;
+    }
+
+    if (p->pid == USB_TOKEN_IN) {
+        ep = p->ep->nr | 0x80;
+    } else {
+        ep = p->ep->nr;
+    }
+
+    if (is_halted(s, p->pid, p->ep->nr)) {
+        unsigned int arg = ep;
+        ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
+        if (ret < 0) {
+            perror("USBDEVFS_CLEAR_HALT");
+            trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+            return USB_RET_NAK;
+        }
+        clear_halt(s, p->pid, p->ep->nr);
+    }
+
+    if (is_isoc(s, p->pid, p->ep->nr)) {
+        return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
+    }
+
+    v = 0;
+    prem = p->iov.iov[v].iov_len;
+    pbuf = p->iov.iov[v].iov_base;
+    rem = p->iov.size;
+    while (rem) {
+        if (prem == 0) {
+            v++;
+            assert(v < p->iov.niov);
+            prem = p->iov.iov[v].iov_len;
+            pbuf = p->iov.iov[v].iov_base;
+            assert(prem <= rem);
+        }
+        aurb = async_alloc(s);
+        aurb->packet = p;
+
+        urb = &aurb->urb;
+        urb->endpoint      = ep;
+        urb->type          = usb_host_usbfs_type(s, p);
+        urb->usercontext   = s;
+        urb->buffer        = pbuf;
+        urb->buffer_length = prem;
+
+        if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
+            urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
+        }
+        pbuf += urb->buffer_length;
+        prem -= urb->buffer_length;
+        rem  -= urb->buffer_length;
+        if (rem) {
+            aurb->more         = 1;
+        }
+
+        trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
+                                  urb->buffer_length, aurb->more);
+        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+        DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
+                urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
+
+        if (ret < 0) {
+            perror("USBDEVFS_SUBMITURB");
+            async_free(aurb);
+
+            switch(errno) {
+            case ETIMEDOUT:
+                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+                return USB_RET_NAK;
+            case EPIPE:
+            default:
+                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL);
+                return USB_RET_STALL;
+            }
+        }
+    }
+
+    return USB_RET_ASYNC;
+}
+
+static int ctrl_error(void)
+{
+    if (errno == ETIMEDOUT) {
+        return USB_RET_NAK;
+    } else {
+        return USB_RET_STALL;
+    }
+}
+
+static int usb_host_set_address(USBHostDevice *s, int addr)
+{
+    trace_usb_host_set_address(s->bus_num, s->addr, addr);
+    s->dev.addr = addr;
+    return 0;
+}
+
+static int usb_host_set_config(USBHostDevice *s, int config)
+{
+    int ret, first = 1;
+
+    trace_usb_host_set_config(s->bus_num, s->addr, config);
+
+    usb_host_release_interfaces(s);
+
+again:
+    ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+
+    DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
+
+    if (ret < 0 && errno == EBUSY && first) {
+        /* happens if usb device is in use by host drivers */
+        int count = usb_linux_get_num_interfaces(s);
+        if (count > 0) {
+            DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
+            usb_host_disconnect_ifaces(s, count);
+            first = 0;
+            goto again;
+        }
+    }
+
+    if (ret < 0) {
+        return ctrl_error();
+    }
+    usb_host_claim_interfaces(s, config);
+    usb_linux_update_endp_table(s);
+    return 0;
+}
+
+static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
+{
+    struct usbdevfs_setinterface si;
+    int i, ret;
+
+    trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
+
+    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
+        if (is_isoc(s, USB_TOKEN_IN, i)) {
+            usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
+        }
+        if (is_isoc(s, USB_TOKEN_OUT, i)) {
+            usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i);
+        }
+    }
+
+    if (iface >= USB_MAX_INTERFACES) {
+        return USB_RET_STALL;
+    }
+
+    si.interface  = iface;
+    si.altsetting = alt;
+    ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
+
+    DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n",
+            iface, alt, ret, errno);
+
+    if (ret < 0) {
+        return ctrl_error();
+    }
+
+    s->dev.altsetting[iface] = alt;
+    usb_linux_update_endp_table(s);
+    return 0;
+}
+
+static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    struct usbdevfs_urb *urb;
+    AsyncURB *aurb;
+    int ret;
+
+    /*
+     * Process certain standard device requests.
+     * These are infrequent and are processed synchronously.
+     */
+
+    /* Note request is (bRequestType << 8) | bRequest */
+    trace_usb_host_req_control(s->bus_num, s->addr, request, value, index);
+
+    switch (request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        return usb_host_set_address(s, value);
+
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        return usb_host_set_config(s, value & 0xff);
+
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        return usb_host_set_interface(s, index, value);
+    }
+
+    /* The rest are asynchronous */
+
+    if (length > sizeof(dev->data_buf)) {
+        fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
+                length, sizeof(dev->data_buf));
+        return USB_RET_STALL;
+    }
+
+    aurb = async_alloc(s);
+    aurb->packet = p;
+
+    /*
+     * Setup ctrl transfer.
+     *
+     * s->ctrl is laid out such that data buffer immediately follows
+     * 'req' struct which is exactly what usbdevfs expects.
+     */
+    urb = &aurb->urb;
+
+    urb->type     = USBDEVFS_URB_TYPE_CONTROL;
+    urb->endpoint = p->ep->nr;
+
+    urb->buffer        = &dev->setup_buf;
+    urb->buffer_length = length + 8;
+
+    urb->usercontext = s;
+
+    trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
+                              urb->buffer_length, aurb->more);
+    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+    DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
+
+    if (ret < 0) {
+        DPRINTF("husb: submit failed. errno %d\n", errno);
+        async_free(aurb);
+
+        switch(errno) {
+        case ETIMEDOUT:
+            return USB_RET_NAK;
+        case EPIPE:
+        default:
+            return USB_RET_STALL;
+        }
+    }
+
+    return USB_RET_ASYNC;
+}
+
+static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
+    uint8_t configuration, uint8_t interface)
+{
+    char device_name[64], line[1024];
+    int alt_setting;
+
+    sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
+            (int)configuration, (int)interface);
+
+    if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
+                            device_name)) {
+        /* Assume alt 0 on error */
+        return 0;
+    }
+    if (sscanf(line, "%d", &alt_setting) != 1) {
+        /* Assume alt 0 on error */
+        return 0;
+    }
+    return alt_setting;
+}
+
+/* returns 1 on problem encountered or 0 for success */
+static int usb_linux_update_endp_table(USBHostDevice *s)
+{
+    uint8_t *descriptors;
+    uint8_t devep, type, alt_interface;
+    uint16_t raw;
+    int interface, length, i, ep, pid;
+    struct endp_data *epd;
+
+    usb_ep_init(&s->dev);
+
+    if (s->dev.configuration == 0) {
+        /* not configured yet -- leave all endpoints disabled */
+        return 0;
+    }
+
+    /* get the desired configuration, interface, and endpoint descriptors
+     * from device description */
+    descriptors = &s->descr[18];
+    length = s->descr_len - 18;
+    i = 0;
+
+    while (i < length) {
+        if (descriptors[i + 1] != USB_DT_CONFIG) {
+            fprintf(stderr, "invalid descriptor data\n");
+            return 1;
+        } else if (descriptors[i + 5] != s->dev.configuration) {
+            DPRINTF("not requested configuration %d\n", s->dev.configuration);
+            i += (descriptors[i + 3] << 8) + descriptors[i + 2];
+            continue;
+        }
+        i += descriptors[i];
+
+        if (descriptors[i + 1] != USB_DT_INTERFACE ||
+            (descriptors[i + 1] == USB_DT_INTERFACE &&
+             descriptors[i + 4] == 0)) {
+            i += descriptors[i];
+            continue;
+        }
+
+        interface = descriptors[i + 2];
+        alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
+                                                  interface);
+
+        /* the current interface descriptor is the active interface
+         * and has endpoints */
+        if (descriptors[i + 3] != alt_interface) {
+            i += descriptors[i];
+            continue;
+        }
+
+        /* advance to the endpoints */
+        while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) {
+            i += descriptors[i];
+        }
+
+        if (i >= length)
+            break;
+
+        while (i < length) {
+            if (descriptors[i + 1] != USB_DT_ENDPOINT) {
+                break;
+            }
+
+            devep = descriptors[i + 2];
+            pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+            ep = devep & 0xf;
+            if (ep == 0) {
+                fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n");
+                return 1;
+            }
+
+            type = descriptors[i + 3] & 0x3;
+            raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
+            usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
+            assert(usb_ep_get_type(&s->dev, pid, ep) ==
+                   USB_ENDPOINT_XFER_INVALID);
+            usb_ep_set_type(&s->dev, pid, ep, type);
+            usb_ep_set_ifnum(&s->dev, pid, ep, interface);
+            if (type == USB_ENDPOINT_XFER_BULK) {
+                usb_ep_set_pipeline(&s->dev, pid, ep, true);
+            }
+
+            epd = get_endp(s, pid, ep);
+            epd->halted = 0;
+
+            i += descriptors[i];
+        }
+    }
+#ifdef DEBUG
+    usb_ep_dump(&s->dev);
+#endif
+    return 0;
+}
+
+/*
+ * Check if we can safely redirect a usb2 device to a usb1 virtual controller,
+ * this function assumes this is safe, if:
+ * 1) There are no isoc endpoints
+ * 2) There are no interrupt endpoints with a max_packet_size > 64
+ * Note bulk endpoints with a max_packet_size > 64 in theory also are not
+ * usb1 compatible, but in practice this seems to work fine.
+ */
+static int usb_linux_full_speed_compat(USBHostDevice *dev)
+{
+    int i, packet_size;
+
+    /*
+     * usb_linux_update_endp_table only registers info about ep in the current
+     * interface altsettings, so we need to parse the descriptors again.
+     */
+    for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) {
+        if (dev->descr[i + 1] == USB_DT_ENDPOINT) {
+            switch (dev->descr[i + 3] & 0x3) {
+            case 0x00: /* CONTROL */
+                break;
+            case 0x01: /* ISO */
+                return 0;
+            case 0x02: /* BULK */
+                break;
+            case 0x03: /* INTERRUPT */
+                packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8);
+                if (packet_size > 64)
+                    return 0;
+                break;
+            }
+        }
+    }
+    return 1;
+}
+
+static int usb_host_open(USBHostDevice *dev, int bus_num,
+                         int addr, const char *port,
+                         const char *prod_name, int speed)
+{
+    int fd = -1, ret;
+
+    trace_usb_host_open_started(bus_num, addr);
+
+    if (dev->fd != -1) {
+        goto fail;
+    }
+
+    fd = usb_host_open_device(bus_num, addr);
+    if (fd < 0) {
+        goto fail;
+    }
+    DPRINTF("husb: opened %s\n", buf);
+
+    dev->bus_num = bus_num;
+    dev->addr = addr;
+    strcpy(dev->port, port);
+    dev->fd = fd;
+
+    /* read the device description */
+    dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
+    if (dev->descr_len <= 0) {
+        perror("husb: reading device data failed");
+        goto fail;
+    }
+
+#ifdef DEBUG
+    {
+        int x;
+        printf("=== begin dumping device descriptor data ===\n");
+        for (x = 0; x < dev->descr_len; x++) {
+            printf("%02x ", dev->descr[x]);
+        }
+        printf("\n=== end dumping device descriptor data ===\n");
+    }
+#endif
+
+
+    /* start unconfigured -- we'll wait for the guest to set a configuration */
+    if (!usb_host_claim_interfaces(dev, 0)) {
+        goto fail;
+    }
+
+    ret = usb_linux_update_endp_table(dev);
+    if (ret) {
+        goto fail;
+    }
+
+    if (speed == -1) {
+        struct usbdevfs_connectinfo ci;
+
+        ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
+        if (ret < 0) {
+            perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
+            goto fail;
+        }
+
+        if (ci.slow) {
+            speed = USB_SPEED_LOW;
+        } else {
+            speed = USB_SPEED_HIGH;
+        }
+    }
+    dev->dev.speed = speed;
+    dev->dev.speedmask = (1 << speed);
+    if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
+        dev->dev.speedmask |= USB_SPEED_MASK_FULL;
+    }
+
+    trace_usb_host_open_success(bus_num, addr);
+
+    if (!prod_name || prod_name[0] == '\0') {
+        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                 "host:%d.%d", bus_num, addr);
+    } else {
+        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                prod_name);
+    }
+
+    ret = usb_device_attach(&dev->dev);
+    if (ret) {
+        goto fail;
+    }
+
+    /* USB devio uses 'write' flag to check for async completions */
+    qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
+
+    return 0;
+
+fail:
+    trace_usb_host_open_failure(bus_num, addr);
+    if (dev->fd != -1) {
+        close(dev->fd);
+        dev->fd = -1;
+    }
+    return -1;
+}
+
+static int usb_host_close(USBHostDevice *dev)
+{
+    int i;
+
+    if (dev->fd == -1) {
+        return -1;
+    }
+
+    trace_usb_host_close(dev->bus_num, dev->addr);
+
+    qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
+    dev->closing = 1;
+    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
+        if (is_isoc(dev, USB_TOKEN_IN, i)) {
+            usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
+        }
+        if (is_isoc(dev, USB_TOKEN_OUT, i)) {
+            usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i);
+        }
+    }
+    async_complete(dev);
+    dev->closing = 0;
+    if (dev->dev.attached) {
+        usb_device_detach(&dev->dev);
+    }
+    usb_host_do_reset(dev);
+    close(dev->fd);
+    dev->fd = -1;
+    return 0;
+}
+
+static void usb_host_exit_notifier(struct Notifier *n, void *data)
+{
+    USBHostDevice *s = container_of(n, USBHostDevice, exit);
+
+    usb_host_release_port(s);
+    if (s->fd != -1) {
+        usb_host_do_reset(s);;
+    }
+}
+
+static int usb_host_initfn(USBDevice *dev)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+
+    dev->auto_attach = 0;
+    s->fd = -1;
+    s->hub_fd = -1;
+
+    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
+    s->exit.notify = usb_host_exit_notifier;
+    qemu_add_exit_notifier(&s->exit);
+    usb_host_auto_check(NULL);
+
+    if (s->match.bus_num != 0 && s->match.port != NULL) {
+        usb_host_claim_port(s);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_host = {
+    .name = "usb-host",
+    .unmigratable = 1,
+};
+
+static Property usb_host_dev_properties[] = {
+    DEFINE_PROP_UINT32("hostbus",  USBHostDevice, match.bus_num,    0),
+    DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr,       0),
+    DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
+    DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
+    DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
+    DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_host_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_host_initfn;
+    uc->product_desc   = "USB Host Device";
+    uc->cancel_packet  = usb_host_async_cancel;
+    uc->handle_data    = usb_host_handle_data;
+    uc->handle_control = usb_host_handle_control;
+    uc->handle_reset   = usb_host_handle_reset;
+    uc->handle_destroy = usb_host_handle_destroy;
+    dc->vmsd = &vmstate_usb_host;
+    dc->props = usb_host_dev_properties;
+}
+
+static TypeInfo usb_host_dev_info = {
+    .name          = "usb-host",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHostDevice),
+    .class_init    = usb_host_class_initfn,
+};
+
+static void usb_host_register_types(void)
+{
+    type_register_static(&usb_host_dev_info);
+    usb_legacy_register("usb-host", "host", usb_host_device_open);
+}
+
+type_init(usb_host_register_types)
+
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
+{
+    struct USBAutoFilter filter;
+    USBDevice *dev;
+    char *p;
+
+    dev = usb_create(bus, "usb-host");
+
+    if (strstr(devname, "auto:")) {
+        if (parse_filter(devname, &filter) < 0) {
+            goto fail;
+        }
+    } else {
+        if ((p = strchr(devname, '.'))) {
+            filter.bus_num    = strtoul(devname, NULL, 0);
+            filter.addr       = strtoul(p + 1, NULL, 0);
+            filter.vendor_id  = 0;
+            filter.product_id = 0;
+        } else if ((p = strchr(devname, ':'))) {
+            filter.bus_num    = 0;
+            filter.addr       = 0;
+            filter.vendor_id  = strtoul(devname, NULL, 16);
+            filter.product_id = strtoul(p + 1, NULL, 16);
+        } else {
+            goto fail;
+        }
+    }
+
+    qdev_prop_set_uint32(&dev->qdev, "hostbus",   filter.bus_num);
+    qdev_prop_set_uint32(&dev->qdev, "hostaddr",  filter.addr);
+    qdev_prop_set_uint32(&dev->qdev, "vendorid",  filter.vendor_id);
+    qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
+    qdev_init_nofail(&dev->qdev);
+    return dev;
+
+fail:
+    qdev_free(&dev->qdev);
+    return NULL;
+}
+
+int usb_host_device_close(const char *devname)
+{
+#if 0
+    char product_name[PRODUCT_NAME_SZ];
+    int bus_num, addr;
+    USBHostDevice *s;
+
+    if (strstr(devname, "auto:")) {
+        return usb_host_auto_del(devname);
+    }
+    if (usb_host_find_device(&bus_num, &addr, product_name,
+                                    sizeof(product_name), devname) < 0) {
+        return -1;
+    }
+    s = hostdev_find(bus_num, addr);
+    if (s) {
+        usb_device_delete_addr(s->bus_num, s->dev.addr);
+        return 0;
+    }
+#endif
+
+    return -1;
+}
+
+/*
+ * Read sys file-system device file
+ *
+ * @line address of buffer to put file contents in
+ * @line_size size of line
+ * @device_file path to device file (printf format string)
+ * @device_name device being opened (inserted into device_file)
+ *
+ * @return 0 failed, 1 succeeded ('line' contains data)
+ */
+static int usb_host_read_file(char *line, size_t line_size,
+                              const char *device_file, const char *device_name)
+{
+    FILE *f;
+    int ret = 0;
+    char filename[PATH_MAX];
+
+    snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
+             device_file);
+    f = fopen(filename, "r");
+    if (f) {
+        ret = fgets(line, line_size, f) != NULL;
+        fclose(f);
+    }
+
+    return ret;
+}
+
+/*
+ * Use /sys/bus/usb/devices/ directory to determine host's USB
+ * devices.
+ *
+ * This code is based on Robert Schiele's original patches posted to
+ * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
+ */
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+    DIR *dir = NULL;
+    char line[1024];
+    int bus_num, addr, speed, class_id, product_id, vendor_id;
+    int ret = 0;
+    char port[MAX_PORTLEN];
+    char product_name[512];
+    struct dirent *de;
+
+    dir = opendir("/sys/bus/usb/devices");
+    if (!dir) {
+        perror("husb: opendir /sys/bus/usb/devices");
+        fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
+        goto the_end;
+    }
+
+    while ((de = readdir(dir))) {
+        if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
+            if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
+                continue;
+            }
+
+            if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%d", &addr) != 1) {
+                goto the_end;
+            }
+            if (!usb_host_read_file(line, sizeof(line), "bDeviceClass",
+                                    de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%x", &class_id) != 1) {
+                goto the_end;
+            }
+
+            if (!usb_host_read_file(line, sizeof(line), "idVendor",
+                                    de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%x", &vendor_id) != 1) {
+                goto the_end;
+            }
+            if (!usb_host_read_file(line, sizeof(line), "idProduct",
+                                    de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%x", &product_id) != 1) {
+                goto the_end;
+            }
+            if (!usb_host_read_file(line, sizeof(line), "product",
+                                    de->d_name)) {
+                *product_name = 0;
+            } else {
+                if (strlen(line) > 0) {
+                    line[strlen(line) - 1] = '\0';
+                }
+                pstrcpy(product_name, sizeof(product_name), line);
+            }
+
+            if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
+                goto the_end;
+            }
+            if (!strcmp(line, "5000\n")) {
+                speed = USB_SPEED_SUPER;
+            } else if (!strcmp(line, "480\n")) {
+                speed = USB_SPEED_HIGH;
+            } else if (!strcmp(line, "1.5\n")) {
+                speed = USB_SPEED_LOW;
+            } else {
+                speed = USB_SPEED_FULL;
+            }
+
+            ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
+                       product_id, product_name, speed);
+            if (ret) {
+                goto the_end;
+            }
+        }
+    }
+ the_end:
+    if (dir) {
+        closedir(dir);
+    }
+    return ret;
+}
+
+static QEMUTimer *usb_auto_timer;
+
+static int usb_host_auto_scan(void *opaque, int bus_num,
+                              int addr, const char *port,
+                              int class_id, int vendor_id, int product_id,
+                              const char *product_name, int speed)
+{
+    struct USBAutoFilter *f;
+    struct USBHostDevice *s;
+
+    /* Ignore hubs */
+    if (class_id == 9)
+        return 0;
+
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        f = &s->match;
+
+        if (f->bus_num > 0 && f->bus_num != bus_num) {
+            continue;
+        }
+        if (f->addr > 0 && f->addr != addr) {
+            continue;
+        }
+        if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) {
+            continue;
+        }
+
+        if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
+            continue;
+        }
+
+        if (f->product_id > 0 && f->product_id != product_id) {
+            continue;
+        }
+        /* We got a match */
+        s->seen++;
+        if (s->errcount >= 3) {
+            return 0;
+        }
+
+        /* Already attached ? */
+        if (s->fd != -1) {
+            return 0;
+        }
+        DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
+
+        if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) {
+            s->errcount++;
+        }
+        break;
+    }
+
+    return 0;
+}
+
+static void usb_host_auto_check(void *unused)
+{
+    struct USBHostDevice *s;
+    int unconnected = 0;
+
+    usb_host_scan(NULL, usb_host_auto_scan);
+
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        if (s->fd == -1) {
+            unconnected++;
+        }
+        if (s->seen == 0) {
+            s->errcount = 0;
+        }
+        s->seen = 0;
+    }
+
+    if (unconnected == 0) {
+        /* nothing to watch */
+        if (usb_auto_timer) {
+            qemu_del_timer(usb_auto_timer);
+            trace_usb_host_auto_scan_disabled();
+        }
+        return;
+    }
+
+    if (!usb_auto_timer) {
+        usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
+        if (!usb_auto_timer) {
+            return;
+        }
+        trace_usb_host_auto_scan_enabled();
+    }
+    qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
+}
+
+/*
+ * Autoconnect filter
+ * Format:
+ *    auto:bus:dev[:vid:pid]
+ *    auto:bus.dev[:vid:pid]
+ *
+ *    bus  - bus number    (dec, * means any)
+ *    dev  - device number (dec, * means any)
+ *    vid  - vendor id     (hex, * means any)
+ *    pid  - product id    (hex, * means any)
+ *
+ *    See 'lsusb' output.
+ */
+static int parse_filter(const char *spec, struct USBAutoFilter *f)
+{
+    enum { BUS, DEV, VID, PID, DONE };
+    const char *p = spec;
+    int i;
+
+    f->bus_num    = 0;
+    f->addr       = 0;
+    f->vendor_id  = 0;
+    f->product_id = 0;
+
+    for (i = BUS; i < DONE; i++) {
+        p = strpbrk(p, ":.");
+        if (!p) {
+            break;
+        }
+        p++;
+
+        if (*p == '*') {
+            continue;
+        }
+        switch(i) {
+        case BUS: f->bus_num = strtol(p, NULL, 10);    break;
+        case DEV: f->addr    = strtol(p, NULL, 10);    break;
+        case VID: f->vendor_id  = strtol(p, NULL, 16); break;
+        case PID: f->product_id = strtol(p, NULL, 16); break;
+        }
+    }
+
+    if (i < DEV) {
+        fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
+        return -1;
+    }
+
+    return 0;
+}
+
+/**********************/
+/* 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;
+}
+
+static void usb_info_device(Monitor *mon, int bus_num,
+                            int addr, const char *port,
+                            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;
+    case USB_SPEED_SUPER:
+        speed_str = "5000";
+        break;
+    default:
+        speed_str = "?";
+        break;
+    }
+
+    monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+                   bus_num, addr, port, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str) {
+        monitor_printf(mon, "    %s:", class_str);
+    } else {
+        monitor_printf(mon, "    Class %02x:", class_id);
+    }
+    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0') {
+        monitor_printf(mon, ", %s", product_name);
+    }
+    monitor_printf(mon, "\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr,
+                                const char *path, int class_id,
+                                int vendor_id, int product_id,
+                                const char *product_name,
+                                int speed)
+{
+    Monitor *mon = opaque;
+
+    usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+static void dec2str(int val, char *str, size_t size)
+{
+    if (val == 0) {
+        snprintf(str, size, "*");
+    } else {
+        snprintf(str, size, "%d", val);
+    }
+}
+
+static void hex2str(int val, char *str, size_t size)
+{
+    if (val == 0) {
+        snprintf(str, size, "*");
+    } else {
+        snprintf(str, size, "%04x", val);
+    }
+}
+
+void usb_host_info(Monitor *mon)
+{
+    struct USBAutoFilter *f;
+    struct USBHostDevice *s;
+
+    usb_host_scan(mon, usb_host_info_device);
+
+    if (QTAILQ_EMPTY(&hostdevs)) {
+        return;
+    }
+
+    monitor_printf(mon, "  Auto filters:\n");
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        char bus[10], addr[10], vid[10], pid[10];
+        f = &s->match;
+        dec2str(f->bus_num, bus, sizeof(bus));
+        dec2str(f->addr, addr, sizeof(addr));
+        hex2str(f->vendor_id, vid, sizeof(vid));
+        hex2str(f->product_id, pid, sizeof(pid));
+        monitor_printf(mon, "    Bus %s, Addr %s, Port %s, ID %s:%s\n",
+                       bus, addr, f->port ? f->port : "*", vid, pid);
+    }
+}
diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c
new file mode 100644 (file)
index 0000000..b4e10c1
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Stub host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *      Support for host device auto connect & disconnect
+ *      Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ *      to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * 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 "console.h"
+#include "hw/usb.h"
+#include "monitor.h"
+
+void usb_host_info(Monitor *mon)
+{
+    monitor_printf(mon, "USB host devices not supported\n");
+}
+
+/* XXX: modify configure to compile the right host driver */
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
+{
+    return NULL;
+}
+
+int usb_host_device_close(const char *devname)
+{
+    return 0;
+}
diff --git a/hw/usb/libhw.c b/hw/usb/libhw.c
new file mode 100644 (file)
index 0000000..2462351
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * QEMU USB emulation, libhw bits.
+ *
+ * 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 "cpu-common.h"
+#include "hw/usb.h"
+#include "dma.h"
+
+int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
+{
+    int is_write = (p->pid == USB_TOKEN_IN);
+    target_phys_addr_t len;
+    void *mem;
+    int i;
+
+    for (i = 0; i < sgl->nsg; i++) {
+        len = sgl->sg[i].len;
+        mem = cpu_physical_memory_map(sgl->sg[i].base, &len,
+                                      is_write);
+        if (!mem) {
+            goto err;
+        }
+        qemu_iovec_add(&p->iov, mem, len);
+        if (len != sgl->sg[i].len) {
+            goto err;
+        }
+    }
+    return 0;
+
+err:
+    usb_packet_unmap(p);
+    return -1;
+}
+
+void usb_packet_unmap(USBPacket *p)
+{
+    int is_write = (p->pid == USB_TOKEN_IN);
+    int i;
+
+    for (i = 0; i < p->iov.niov; i++) {
+        cpu_physical_memory_unmap(p->iov.iov[i].iov_base,
+                                  p->iov.iov[i].iov_len, is_write,
+                                  p->iov.iov[i].iov_len);
+    }
+}
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
new file mode 100644 (file)
index 0000000..8e9f175
--- /dev/null
@@ -0,0 +1,1485 @@
+/*
+ * USB redirector usb-guest
+ *
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Red Hat Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * 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 "qemu-timer.h"
+#include "monitor.h"
+#include "sysemu.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <usbredirparser.h>
+#include <usbredirfilter.h>
+
+#include "hw/usb.h"
+
+#define MAX_ENDPOINTS 32
+#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
+#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
+
+typedef struct AsyncURB AsyncURB;
+typedef struct USBRedirDevice USBRedirDevice;
+
+/* Struct to hold buffered packets (iso or int input packets) */
+struct buf_packet {
+    uint8_t *data;
+    int len;
+    int status;
+    QTAILQ_ENTRY(buf_packet)next;
+};
+
+struct endp_data {
+    uint8_t type;
+    uint8_t interval;
+    uint8_t interface; /* bInterfaceNumber this ep belongs to */
+    uint8_t iso_started;
+    uint8_t iso_error; /* For reporting iso errors to the HC */
+    uint8_t interrupt_started;
+    uint8_t interrupt_error;
+    uint8_t bufpq_prefilled;
+    uint8_t bufpq_dropping_packets;
+    QTAILQ_HEAD(, buf_packet) bufpq;
+    int bufpq_size;
+    int bufpq_target_size;
+};
+
+struct USBRedirDevice {
+    USBDevice dev;
+    /* Properties */
+    CharDriverState *cs;
+    uint8_t debug;
+    char *filter_str;
+    /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
+    const uint8_t *read_buf;
+    int read_buf_size;
+    /* For async handling of open/close */
+    QEMUBH *open_close_bh;
+    /* To delay the usb attach in case of quick chardev close + open */
+    QEMUTimer *attach_timer;
+    int64_t next_attach_time;
+    struct usbredirparser *parser;
+    struct endp_data endpoint[MAX_ENDPOINTS];
+    uint32_t packet_id;
+    QTAILQ_HEAD(, AsyncURB) asyncq;
+    /* Data for device filtering */
+    struct usb_redir_device_connect_header device_info;
+    struct usb_redir_interface_info_header interface_info;
+    struct usbredirfilter_rule *filter_rules;
+    int filter_rules_count;
+};
+
+struct AsyncURB {
+    USBRedirDevice *dev;
+    USBPacket *packet;
+    uint32_t packet_id;
+    int get;
+    union {
+        struct usb_redir_control_packet_header control_packet;
+        struct usb_redir_bulk_packet_header bulk_packet;
+        struct usb_redir_interrupt_packet_header interrupt_packet;
+    };
+    QTAILQ_ENTRY(AsyncURB)next;
+};
+
+static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
+static void usbredir_device_connect(void *priv,
+    struct usb_redir_device_connect_header *device_connect);
+static void usbredir_device_disconnect(void *priv);
+static void usbredir_interface_info(void *priv,
+    struct usb_redir_interface_info_header *interface_info);
+static void usbredir_ep_info(void *priv,
+    struct usb_redir_ep_info_header *ep_info);
+static void usbredir_configuration_status(void *priv, uint32_t id,
+    struct usb_redir_configuration_status_header *configuration_status);
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+    struct usb_redir_alt_setting_status_header *alt_setting_status);
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+    struct usb_redir_iso_stream_status_header *iso_stream_status);
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+    struct usb_redir_interrupt_receiving_status_header
+    *interrupt_receiving_status);
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+    struct usb_redir_bulk_streams_status_header *bulk_streams_status);
+static void usbredir_control_packet(void *priv, uint32_t id,
+    struct usb_redir_control_packet_header *control_packet,
+    uint8_t *data, int data_len);
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+    struct usb_redir_bulk_packet_header *bulk_packet,
+    uint8_t *data, int data_len);
+static void usbredir_iso_packet(void *priv, uint32_t id,
+    struct usb_redir_iso_packet_header *iso_packet,
+    uint8_t *data, int data_len);
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+    struct usb_redir_interrupt_packet_header *interrupt_header,
+    uint8_t *data, int data_len);
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+                                       int status, int actual_len);
+
+#define VERSION "qemu usb-redir guest " QEMU_VERSION
+
+/*
+ * Logging stuff
+ */
+
+#define ERROR(...) \
+    do { \
+        if (dev->debug >= usbredirparser_error) { \
+            error_report("usb-redir error: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define WARNING(...) \
+    do { \
+        if (dev->debug >= usbredirparser_warning) { \
+            error_report("usb-redir warning: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define INFO(...) \
+    do { \
+        if (dev->debug >= usbredirparser_info) { \
+            error_report("usb-redir: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define DPRINTF(...) \
+    do { \
+        if (dev->debug >= usbredirparser_debug) { \
+            error_report("usb-redir: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define DPRINTF2(...) \
+    do { \
+        if (dev->debug >= usbredirparser_debug_data) { \
+            error_report("usb-redir: " __VA_ARGS__); \
+        } \
+    } while (0)
+
+static void usbredir_log(void *priv, int level, const char *msg)
+{
+    USBRedirDevice *dev = priv;
+
+    if (dev->debug < level) {
+        return;
+    }
+
+    error_report("%s", msg);
+}
+
+static void usbredir_log_data(USBRedirDevice *dev, const char *desc,
+    const uint8_t *data, int len)
+{
+    int i, j, n;
+
+    if (dev->debug < usbredirparser_debug_data) {
+        return;
+    }
+
+    for (i = 0; i < len; i += j) {
+        char buf[128];
+
+        n = sprintf(buf, "%s", desc);
+        for (j = 0; j < 8 && i + j < len; j++) {
+            n += sprintf(buf + n, " %02X", data[i + j]);
+        }
+        error_report("%s", buf);
+    }
+}
+
+/*
+ * usbredirparser io functions
+ */
+
+static int usbredir_read(void *priv, uint8_t *data, int count)
+{
+    USBRedirDevice *dev = priv;
+
+    if (dev->read_buf_size < count) {
+        count = dev->read_buf_size;
+    }
+
+    memcpy(data, dev->read_buf, count);
+
+    dev->read_buf_size -= count;
+    if (dev->read_buf_size) {
+        dev->read_buf += count;
+    } else {
+        dev->read_buf = NULL;
+    }
+
+    return count;
+}
+
+static int usbredir_write(void *priv, uint8_t *data, int count)
+{
+    USBRedirDevice *dev = priv;
+
+    if (!dev->cs->opened) {
+        return 0;
+    }
+
+    return qemu_chr_fe_write(dev->cs, data, count);
+}
+
+/*
+ * Async and buffered packets helpers
+ */
+
+static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
+{
+    AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB));
+    aurb->dev = dev;
+    aurb->packet = p;
+    aurb->packet_id = dev->packet_id;
+    QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
+    dev->packet_id++;
+
+    return aurb;
+}
+
+static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
+{
+    QTAILQ_REMOVE(&dev->asyncq, aurb, next);
+    g_free(aurb);
+}
+
+static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
+{
+    AsyncURB *aurb;
+
+    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+        if (aurb->packet_id == packet_id) {
+            return aurb;
+        }
+    }
+    ERROR("could not find async urb for packet_id %u\n", packet_id);
+    return NULL;
+}
+
+static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    AsyncURB *aurb;
+
+    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+        if (p != aurb->packet) {
+            continue;
+        }
+
+        DPRINTF("async cancel id %u\n", aurb->packet_id);
+        usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
+        usbredirparser_do_write(dev->parser);
+
+        /* Mark it as dead */
+        aurb->packet = NULL;
+        break;
+    }
+}
+
+static void bufp_alloc(USBRedirDevice *dev,
+    uint8_t *data, int len, int status, uint8_t ep)
+{
+    struct buf_packet *bufp;
+
+    if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets &&
+        dev->endpoint[EP2I(ep)].bufpq_size >
+            2 * dev->endpoint[EP2I(ep)].bufpq_target_size) {
+        DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1;
+    }
+    /* Since we're interupting the stream anyways, drop enough packets to get
+       back to our target buffer size */
+    if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) {
+        if (dev->endpoint[EP2I(ep)].bufpq_size >
+                dev->endpoint[EP2I(ep)].bufpq_target_size) {
+            free(data);
+            return;
+        }
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
+    }
+
+    bufp = g_malloc(sizeof(struct buf_packet));
+    bufp->data   = data;
+    bufp->len    = len;
+    bufp->status = status;
+    QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+    dev->endpoint[EP2I(ep)].bufpq_size++;
+}
+
+static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
+    uint8_t ep)
+{
+    QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+    dev->endpoint[EP2I(ep)].bufpq_size--;
+    free(bufp->data);
+    g_free(bufp);
+}
+
+static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep)
+{
+    struct buf_packet *buf, *buf_next;
+
+    QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) {
+        bufp_free(dev, buf, ep);
+    }
+}
+
+/*
+ * USBDevice callbacks
+ */
+
+static void usbredir_handle_reset(USBDevice *udev)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+    DPRINTF("reset device\n");
+    usbredirparser_send_reset(dev->parser);
+    usbredirparser_do_write(dev->parser);
+}
+
+static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
+                                     uint8_t ep)
+{
+    int status, len;
+    if (!dev->endpoint[EP2I(ep)].iso_started &&
+            !dev->endpoint[EP2I(ep)].iso_error) {
+        struct usb_redir_start_iso_stream_header start_iso = {
+            .endpoint = ep,
+        };
+        int pkts_per_sec;
+
+        if (dev->dev.speed == USB_SPEED_HIGH) {
+            pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval;
+        } else {
+            pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval;
+        }
+        /* Testing has shown that we need circa 60 ms buffer */
+        dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000;
+
+        /* Aim for approx 100 interrupts / second on the client to
+           balance latency and interrupt load */
+        start_iso.pkts_per_urb = pkts_per_sec / 100;
+        if (start_iso.pkts_per_urb < 1) {
+            start_iso.pkts_per_urb = 1;
+        } else if (start_iso.pkts_per_urb > 32) {
+            start_iso.pkts_per_urb = 32;
+        }
+
+        start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size +
+                             start_iso.pkts_per_urb - 1) /
+                            start_iso.pkts_per_urb;
+        /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest
+           as overflow buffer. Also see the usbredir protocol documentation */
+        if (!(ep & USB_DIR_IN)) {
+            start_iso.no_urbs *= 2;
+        }
+        if (start_iso.no_urbs > 16) {
+            start_iso.no_urbs = 16;
+        }
+
+        /* No id, we look at the ep when receiving a status back */
+        usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
+        usbredirparser_do_write(dev->parser);
+        DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n",
+                pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep);
+        dev->endpoint[EP2I(ep)].iso_started = 1;
+        dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
+    }
+
+    if (ep & USB_DIR_IN) {
+        struct buf_packet *isop;
+
+        if (dev->endpoint[EP2I(ep)].iso_started &&
+                !dev->endpoint[EP2I(ep)].bufpq_prefilled) {
+            if (dev->endpoint[EP2I(ep)].bufpq_size <
+                    dev->endpoint[EP2I(ep)].bufpq_target_size) {
+                return usbredir_handle_status(dev, 0, 0);
+            }
+            dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
+        }
+
+        isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+        if (isop == NULL) {
+            DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n",
+                    ep, dev->endpoint[EP2I(ep)].iso_error);
+            /* Re-fill the buffer */
+            dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
+            /* Check iso_error for stream errors, otherwise its an underrun */
+            status = dev->endpoint[EP2I(ep)].iso_error;
+            dev->endpoint[EP2I(ep)].iso_error = 0;
+            return status ? USB_RET_IOERROR : 0;
+        }
+        DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
+                 isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
+
+        status = isop->status;
+        if (status != usb_redir_success) {
+            bufp_free(dev, isop, ep);
+            return USB_RET_IOERROR;
+        }
+
+        len = isop->len;
+        if (len > p->iov.size) {
+            ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
+                  ep, len, (int)p->iov.size);
+            bufp_free(dev, isop, ep);
+            return USB_RET_BABBLE;
+        }
+        usb_packet_copy(p, isop->data, len);
+        bufp_free(dev, isop, ep);
+        return len;
+    } else {
+        /* If the stream was not started because of a pending error don't
+           send the packet to the usb-host */
+        if (dev->endpoint[EP2I(ep)].iso_started) {
+            struct usb_redir_iso_packet_header iso_packet = {
+                .endpoint = ep,
+                .length = p->iov.size
+            };
+            uint8_t buf[p->iov.size];
+            /* No id, we look at the ep when receiving a status back */
+            usb_packet_copy(p, buf, p->iov.size);
+            usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
+                                           buf, p->iov.size);
+            usbredirparser_do_write(dev->parser);
+        }
+        status = dev->endpoint[EP2I(ep)].iso_error;
+        dev->endpoint[EP2I(ep)].iso_error = 0;
+        DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
+                 p->iov.size);
+        return usbredir_handle_status(dev, status, p->iov.size);
+    }
+}
+
+static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
+{
+    struct usb_redir_stop_iso_stream_header stop_iso_stream = {
+        .endpoint = ep
+    };
+    if (dev->endpoint[EP2I(ep)].iso_started) {
+        usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream);
+        DPRINTF("iso stream stopped ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].iso_started = 0;
+    }
+    dev->endpoint[EP2I(ep)].iso_error = 0;
+    usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+                                      uint8_t ep)
+{
+    AsyncURB *aurb = async_alloc(dev, p);
+    struct usb_redir_bulk_packet_header bulk_packet;
+
+    DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
+            p->iov.size, aurb->packet_id);
+
+    bulk_packet.endpoint  = ep;
+    bulk_packet.length    = p->iov.size;
+    bulk_packet.stream_id = 0;
+    aurb->bulk_packet = bulk_packet;
+
+    if (ep & USB_DIR_IN) {
+        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+                                        &bulk_packet, NULL, 0);
+    } else {
+        uint8_t buf[p->iov.size];
+        usb_packet_copy(p, buf, p->iov.size);
+        usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
+        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+                                        &bulk_packet, buf, p->iov.size);
+    }
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+                                           USBPacket *p, uint8_t ep)
+{
+    if (ep & USB_DIR_IN) {
+        /* Input interrupt endpoint, buffered packet input */
+        struct buf_packet *intp;
+        int status, len;
+
+        if (!dev->endpoint[EP2I(ep)].interrupt_started &&
+                !dev->endpoint[EP2I(ep)].interrupt_error) {
+            struct usb_redir_start_interrupt_receiving_header start_int = {
+                .endpoint = ep,
+            };
+            /* No id, we look at the ep when receiving a status back */
+            usbredirparser_send_start_interrupt_receiving(dev->parser, 0,
+                                                          &start_int);
+            usbredirparser_do_write(dev->parser);
+            DPRINTF("interrupt recv started ep %02X\n", ep);
+            dev->endpoint[EP2I(ep)].interrupt_started = 1;
+            /* We don't really want to drop interrupt packets ever, but
+               having some upper limit to how much we buffer is good. */
+            dev->endpoint[EP2I(ep)].bufpq_target_size = 1000;
+            dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
+        }
+
+        intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+        if (intp == NULL) {
+            DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
+            /* Check interrupt_error for stream errors */
+            status = dev->endpoint[EP2I(ep)].interrupt_error;
+            dev->endpoint[EP2I(ep)].interrupt_error = 0;
+            if (status) {
+                return usbredir_handle_status(dev, status, 0);
+            }
+            return USB_RET_NAK;
+        }
+        DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
+                intp->status, intp->len);
+
+        status = intp->status;
+        if (status != usb_redir_success) {
+            bufp_free(dev, intp, ep);
+            return usbredir_handle_status(dev, status, 0);
+        }
+
+        len = intp->len;
+        if (len > p->iov.size) {
+            ERROR("received int data is larger then packet ep %02X\n", ep);
+            bufp_free(dev, intp, ep);
+            return USB_RET_BABBLE;
+        }
+        usb_packet_copy(p, intp->data, len);
+        bufp_free(dev, intp, ep);
+        return len;
+    } else {
+        /* Output interrupt endpoint, normal async operation */
+        AsyncURB *aurb = async_alloc(dev, p);
+        struct usb_redir_interrupt_packet_header interrupt_packet;
+        uint8_t buf[p->iov.size];
+
+        DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
+                aurb->packet_id);
+
+        interrupt_packet.endpoint  = ep;
+        interrupt_packet.length    = p->iov.size;
+        aurb->interrupt_packet     = interrupt_packet;
+
+        usb_packet_copy(p, buf, p->iov.size);
+        usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
+        usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
+                                        &interrupt_packet, buf, p->iov.size);
+        usbredirparser_do_write(dev->parser);
+        return USB_RET_ASYNC;
+    }
+}
+
+static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
+    uint8_t ep)
+{
+    struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = {
+        .endpoint = ep
+    };
+    if (dev->endpoint[EP2I(ep)].interrupt_started) {
+        usbredirparser_send_stop_interrupt_receiving(dev->parser, 0,
+                                                     &stop_interrupt_recv);
+        DPRINTF("interrupt recv stopped ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].interrupt_started = 0;
+    }
+    dev->endpoint[EP2I(ep)].interrupt_error = 0;
+    usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    uint8_t ep;
+
+    ep = p->ep->nr;
+    if (p->pid == USB_TOKEN_IN) {
+        ep |= USB_DIR_IN;
+    }
+
+    switch (dev->endpoint[EP2I(ep)].type) {
+    case USB_ENDPOINT_XFER_CONTROL:
+        ERROR("handle_data called for control transfer on ep %02X\n", ep);
+        return USB_RET_NAK;
+    case USB_ENDPOINT_XFER_ISOC:
+        return usbredir_handle_iso_data(dev, p, ep);
+    case USB_ENDPOINT_XFER_BULK:
+        return usbredir_handle_bulk_data(dev, p, ep);
+    case USB_ENDPOINT_XFER_INT:
+        return usbredir_handle_interrupt_data(dev, p, ep);
+    default:
+        ERROR("handle_data ep %02X has unknown type %d\n", ep,
+              dev->endpoint[EP2I(ep)].type);
+        return USB_RET_NAK;
+    }
+}
+
+static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
+                                int config)
+{
+    struct usb_redir_set_configuration_header set_config;
+    AsyncURB *aurb = async_alloc(dev, p);
+    int i;
+
+    DPRINTF("set config %d id %u\n", config, aurb->packet_id);
+
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        switch (dev->endpoint[i].type) {
+        case USB_ENDPOINT_XFER_ISOC:
+            usbredir_stop_iso_stream(dev, I2EP(i));
+            break;
+        case USB_ENDPOINT_XFER_INT:
+            if (i & 0x10) {
+                usbredir_stop_interrupt_receiving(dev, I2EP(i));
+            }
+            break;
+        }
+        usbredir_free_bufpq(dev, I2EP(i));
+    }
+
+    set_config.configuration = config;
+    usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
+                                          &set_config);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
+{
+    AsyncURB *aurb = async_alloc(dev, p);
+
+    DPRINTF("get config id %u\n", aurb->packet_id);
+
+    aurb->get = 1;
+    usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
+                                   int interface, int alt)
+{
+    struct usb_redir_set_alt_setting_header set_alt;
+    AsyncURB *aurb = async_alloc(dev, p);
+    int i;
+
+    DPRINTF("set interface %d alt %d id %u\n", interface, alt,
+            aurb->packet_id);
+
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        if (dev->endpoint[i].interface == interface) {
+            switch (dev->endpoint[i].type) {
+            case USB_ENDPOINT_XFER_ISOC:
+                usbredir_stop_iso_stream(dev, I2EP(i));
+                break;
+            case USB_ENDPOINT_XFER_INT:
+                if (i & 0x10) {
+                    usbredir_stop_interrupt_receiving(dev, I2EP(i));
+                }
+                break;
+            }
+            usbredir_free_bufpq(dev, I2EP(i));
+        }
+    }
+
+    set_alt.interface = interface;
+    set_alt.alt = alt;
+    usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
+                                        &set_alt);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
+                                   int interface)
+{
+    struct usb_redir_get_alt_setting_header get_alt;
+    AsyncURB *aurb = async_alloc(dev, p);
+
+    DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
+
+    get_alt.interface = interface;
+    aurb->get = 1;
+    usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
+                                        &get_alt);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+        int request, int value, int index, int length, uint8_t *data)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    struct usb_redir_control_packet_header control_packet;
+    AsyncURB *aurb;
+
+    /* Special cases for certain standard device requests */
+    switch (request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        DPRINTF("set address %d\n", value);
+        dev->dev.addr = value;
+        return 0;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        return usbredir_set_config(dev, p, value & 0xff);
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        return usbredir_get_config(dev, p);
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        return usbredir_set_interface(dev, p, index, value);
+    case InterfaceRequest | USB_REQ_GET_INTERFACE:
+        return usbredir_get_interface(dev, p, index);
+    }
+
+    /* "Normal" ctrl requests */
+    aurb = async_alloc(dev, p);
+
+    /* Note request is (bRequestType << 8) | bRequest */
+    DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
+            request >> 8, request & 0xff, value, index, length,
+            aurb->packet_id);
+
+    control_packet.request     = request & 0xFF;
+    control_packet.requesttype = request >> 8;
+    control_packet.endpoint    = control_packet.requesttype & USB_DIR_IN;
+    control_packet.value       = value;
+    control_packet.index       = index;
+    control_packet.length      = length;
+    aurb->control_packet       = control_packet;
+
+    if (control_packet.requesttype & USB_DIR_IN) {
+        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+                                           &control_packet, NULL, 0);
+    } else {
+        usbredir_log_data(dev, "ctrl data out:", data, length);
+        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+                                           &control_packet, data, length);
+    }
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+/*
+ * Close events can be triggered by usbredirparser_do_write which gets called
+ * from within the USBDevice data / control packet callbacks and doing a
+ * usb_detach from within these callbacks is not a good idea.
+ *
+ * So we use a bh handler to take care of close events. We also handle
+ * open events from this callback to make sure that a close directly followed
+ * by an open gets handled in the right order.
+ */
+static void usbredir_open_close_bh(void *opaque)
+{
+    USBRedirDevice *dev = opaque;
+    uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
+
+    usbredir_device_disconnect(dev);
+
+    if (dev->parser) {
+        usbredirparser_destroy(dev->parser);
+        dev->parser = NULL;
+    }
+
+    if (dev->cs->opened) {
+        dev->parser = qemu_oom_check(usbredirparser_create());
+        dev->parser->priv = dev;
+        dev->parser->log_func = usbredir_log;
+        dev->parser->read_func = usbredir_read;
+        dev->parser->write_func = usbredir_write;
+        dev->parser->hello_func = usbredir_hello;
+        dev->parser->device_connect_func = usbredir_device_connect;
+        dev->parser->device_disconnect_func = usbredir_device_disconnect;
+        dev->parser->interface_info_func = usbredir_interface_info;
+        dev->parser->ep_info_func = usbredir_ep_info;
+        dev->parser->configuration_status_func = usbredir_configuration_status;
+        dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
+        dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
+        dev->parser->interrupt_receiving_status_func =
+            usbredir_interrupt_receiving_status;
+        dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
+        dev->parser->control_packet_func = usbredir_control_packet;
+        dev->parser->bulk_packet_func = usbredir_bulk_packet;
+        dev->parser->iso_packet_func = usbredir_iso_packet;
+        dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
+        dev->read_buf = NULL;
+        dev->read_buf_size = 0;
+
+        usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+        usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+        usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
+        usbredirparser_do_write(dev->parser);
+    }
+}
+
+static void usbredir_do_attach(void *opaque)
+{
+    USBRedirDevice *dev = opaque;
+
+    usb_device_attach(&dev->dev);
+}
+
+/*
+ * chardev callbacks
+ */
+
+static int usbredir_chardev_can_read(void *opaque)
+{
+    USBRedirDevice *dev = opaque;
+
+    if (dev->parser) {
+        /* usbredir_parser_do_read will consume *all* data we give it */
+        return 1024 * 1024;
+    } else {
+        /* usbredir_open_close_bh hasn't handled the open event yet */
+        return 0;
+    }
+}
+
+static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
+{
+    USBRedirDevice *dev = opaque;
+
+    /* No recursion allowed! */
+    assert(dev->read_buf == NULL);
+
+    dev->read_buf = buf;
+    dev->read_buf_size = size;
+
+    usbredirparser_do_read(dev->parser);
+    /* Send any acks, etc. which may be queued now */
+    usbredirparser_do_write(dev->parser);
+}
+
+static void usbredir_chardev_event(void *opaque, int event)
+{
+    USBRedirDevice *dev = opaque;
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+    case CHR_EVENT_CLOSED:
+        qemu_bh_schedule(dev->open_close_bh);
+        break;
+    }
+}
+
+/*
+ * init + destroy
+ */
+
+static int usbredir_initfn(USBDevice *udev)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    int i;
+
+    if (dev->cs == NULL) {
+        qerror_report(QERR_MISSING_PARAMETER, "chardev");
+        return -1;
+    }
+
+    if (dev->filter_str) {
+        i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|",
+                                           &dev->filter_rules,
+                                           &dev->filter_rules_count);
+        if (i) {
+            qerror_report(QERR_INVALID_PARAMETER_VALUE, "filter",
+                          "a usb device filter string");
+            return -1;
+        }
+    }
+
+    dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
+    dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+
+    QTAILQ_INIT(&dev->asyncq);
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        QTAILQ_INIT(&dev->endpoint[i].bufpq);
+    }
+
+    /* We'll do the attach once we receive the speed from the usb-host */
+    udev->auto_attach = 0;
+
+    /* Let the backend know we are ready */
+    qemu_chr_fe_open(dev->cs);
+    qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
+                          usbredir_chardev_read, usbredir_chardev_event, dev);
+
+    return 0;
+}
+
+static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
+{
+    AsyncURB *aurb, *next_aurb;
+    int i;
+
+    QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
+        async_free(dev, aurb);
+    }
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        usbredir_free_bufpq(dev, I2EP(i));
+    }
+}
+
+static void usbredir_handle_destroy(USBDevice *udev)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+    qemu_chr_fe_close(dev->cs);
+    qemu_chr_delete(dev->cs);
+    /* Note must be done after qemu_chr_close, as that causes a close event */
+    qemu_bh_delete(dev->open_close_bh);
+
+    qemu_del_timer(dev->attach_timer);
+    qemu_free_timer(dev->attach_timer);
+
+    usbredir_cleanup_device_queues(dev);
+
+    if (dev->parser) {
+        usbredirparser_destroy(dev->parser);
+    }
+
+    free(dev->filter_rules);
+}
+
+static int usbredir_check_filter(USBRedirDevice *dev)
+{
+    if (dev->interface_info.interface_count == 0) {
+        ERROR("No interface info for device\n");
+        goto error;
+    }
+
+    if (dev->filter_rules) {
+        if (!usbredirparser_peer_has_cap(dev->parser,
+                                    usb_redir_cap_connect_device_version)) {
+            ERROR("Device filter specified and peer does not have the "
+                  "connect_device_version capability\n");
+            goto error;
+        }
+
+        if (usbredirfilter_check(
+                dev->filter_rules,
+                dev->filter_rules_count,
+                dev->device_info.device_class,
+                dev->device_info.device_subclass,
+                dev->device_info.device_protocol,
+                dev->interface_info.interface_class,
+                dev->interface_info.interface_subclass,
+                dev->interface_info.interface_protocol,
+                dev->interface_info.interface_count,
+                dev->device_info.vendor_id,
+                dev->device_info.product_id,
+                dev->device_info.device_version_bcd,
+                0) != 0) {
+            goto error;
+        }
+    }
+
+    return 0;
+
+error:
+    usbredir_device_disconnect(dev);
+    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+        usbredirparser_send_filter_reject(dev->parser);
+        usbredirparser_do_write(dev->parser);
+    }
+    return -1;
+}
+
+/*
+ * usbredirparser packet complete callbacks
+ */
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+                                       int status, int actual_len)
+{
+    switch (status) {
+    case usb_redir_success:
+        return actual_len;
+    case usb_redir_stall:
+        return USB_RET_STALL;
+    case usb_redir_cancelled:
+        WARNING("returning cancelled packet to HC?\n");
+        return USB_RET_NAK;
+    case usb_redir_inval:
+        WARNING("got invalid param error from usb-host?\n");
+        return USB_RET_NAK;
+    case usb_redir_ioerror:
+    case usb_redir_timeout:
+    default:
+        return USB_RET_IOERROR;
+    }
+}
+
+static void usbredir_hello(void *priv, struct usb_redir_hello_header *h)
+{
+    USBRedirDevice *dev = priv;
+
+    /* Try to send the filter info now that we've the usb-host's caps */
+    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) &&
+            dev->filter_rules) {
+        usbredirparser_send_filter_filter(dev->parser, dev->filter_rules,
+                                          dev->filter_rules_count);
+        usbredirparser_do_write(dev->parser);
+    }
+}
+
+static void usbredir_device_connect(void *priv,
+    struct usb_redir_device_connect_header *device_connect)
+{
+    USBRedirDevice *dev = priv;
+    const char *speed;
+
+    if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
+        ERROR("Received device connect while already connected\n");
+        return;
+    }
+
+    switch (device_connect->speed) {
+    case usb_redir_speed_low:
+        speed = "low speed";
+        dev->dev.speed = USB_SPEED_LOW;
+        break;
+    case usb_redir_speed_full:
+        speed = "full speed";
+        dev->dev.speed = USB_SPEED_FULL;
+        break;
+    case usb_redir_speed_high:
+        speed = "high speed";
+        dev->dev.speed = USB_SPEED_HIGH;
+        break;
+    case usb_redir_speed_super:
+        speed = "super speed";
+        dev->dev.speed = USB_SPEED_SUPER;
+        break;
+    default:
+        speed = "unknown speed";
+        dev->dev.speed = USB_SPEED_FULL;
+    }
+
+    if (usbredirparser_peer_has_cap(dev->parser,
+                                    usb_redir_cap_connect_device_version)) {
+        INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
+             speed, device_connect->vendor_id, device_connect->product_id,
+             ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 +
+             ((device_connect->device_version_bcd & 0x0f00) >>  8),
+             ((device_connect->device_version_bcd & 0x00f0) >>  4) * 10 +
+             ((device_connect->device_version_bcd & 0x000f) >>  0),
+             device_connect->device_class);
+    } else {
+        INFO("attaching %s device %04x:%04x class %02x\n", speed,
+             device_connect->vendor_id, device_connect->product_id,
+             device_connect->device_class);
+    }
+
+    dev->dev.speedmask = (1 << dev->dev.speed);
+    dev->device_info = *device_connect;
+
+    if (usbredir_check_filter(dev)) {
+        WARNING("Device %04x:%04x rejected by device filter, not attaching\n",
+                device_connect->vendor_id, device_connect->product_id);
+        return;
+    }
+
+    qemu_mod_timer(dev->attach_timer, dev->next_attach_time);
+}
+
+static void usbredir_device_disconnect(void *priv)
+{
+    USBRedirDevice *dev = priv;
+    int i;
+
+    /* Stop any pending attaches */
+    qemu_del_timer(dev->attach_timer);
+
+    if (dev->dev.attached) {
+        usb_device_detach(&dev->dev);
+        /*
+         * Delay next usb device attach to give the guest a chance to see
+         * see the detach / attach in case of quick close / open succession
+         */
+        dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200;
+    }
+
+    /* Reset state so that the next dev connected starts with a clean slate */
+    usbredir_cleanup_device_queues(dev);
+    memset(dev->endpoint, 0, sizeof(dev->endpoint));
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        QTAILQ_INIT(&dev->endpoint[i].bufpq);
+    }
+    usb_ep_init(&dev->dev);
+    dev->interface_info.interface_count = 0;
+}
+
+static void usbredir_interface_info(void *priv,
+    struct usb_redir_interface_info_header *interface_info)
+{
+    USBRedirDevice *dev = priv;
+
+    dev->interface_info = *interface_info;
+
+    /*
+     * If we receive interface info after the device has already been
+     * connected (ie on a set_config), re-check the filter.
+     */
+    if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
+        if (usbredir_check_filter(dev)) {
+            ERROR("Device no longer matches filter after interface info "
+                  "change, disconnecting!\n");
+        }
+    }
+}
+
+static void usbredir_ep_info(void *priv,
+    struct usb_redir_ep_info_header *ep_info)
+{
+    USBRedirDevice *dev = priv;
+    struct USBEndpoint *usb_ep;
+    int i;
+
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        dev->endpoint[i].type = ep_info->type[i];
+        dev->endpoint[i].interval = ep_info->interval[i];
+        dev->endpoint[i].interface = ep_info->interface[i];
+        switch (dev->endpoint[i].type) {
+        case usb_redir_type_invalid:
+            break;
+        case usb_redir_type_iso:
+        case usb_redir_type_interrupt:
+            if (dev->endpoint[i].interval == 0) {
+                ERROR("Received 0 interval for isoc or irq endpoint\n");
+                usbredir_device_disconnect(dev);
+            }
+            /* Fall through */
+        case usb_redir_type_control:
+        case usb_redir_type_bulk:
+            DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
+                    dev->endpoint[i].type, dev->endpoint[i].interface);
+            break;
+        default:
+            ERROR("Received invalid endpoint type\n");
+            usbredir_device_disconnect(dev);
+            return;
+        }
+        usb_ep = usb_ep_get(&dev->dev,
+                            (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
+                            i & 0x0f);
+        usb_ep->type = dev->endpoint[i].type;
+        usb_ep->ifnum = dev->endpoint[i].interface;
+    }
+}
+
+static void usbredir_configuration_status(void *priv, uint32_t id,
+    struct usb_redir_configuration_status_header *config_status)
+{
+    USBRedirDevice *dev = priv;
+    AsyncURB *aurb;
+    int len = 0;
+
+    DPRINTF("set config status %d config %d id %u\n", config_status->status,
+            config_status->configuration, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        return;
+    }
+    if (aurb->packet) {
+        if (aurb->get) {
+            dev->dev.data_buf[0] = config_status->configuration;
+            len = 1;
+        }
+        aurb->packet->result =
+            usbredir_handle_status(dev, config_status->status, len);
+        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+}
+
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+    struct usb_redir_alt_setting_status_header *alt_setting_status)
+{
+    USBRedirDevice *dev = priv;
+    AsyncURB *aurb;
+    int len = 0;
+
+    DPRINTF("alt status %d intf %d alt %d id: %u\n",
+            alt_setting_status->status,
+            alt_setting_status->interface,
+            alt_setting_status->alt, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        return;
+    }
+    if (aurb->packet) {
+        if (aurb->get) {
+            dev->dev.data_buf[0] = alt_setting_status->alt;
+            len = 1;
+        }
+        aurb->packet->result =
+            usbredir_handle_status(dev, alt_setting_status->status, len);
+        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+}
+
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+    struct usb_redir_iso_stream_status_header *iso_stream_status)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = iso_stream_status->endpoint;
+
+    DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
+            ep, id);
+
+    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
+        return;
+    }
+
+    dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status;
+    if (iso_stream_status->status == usb_redir_stall) {
+        DPRINTF("iso stream stopped by peer ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].iso_started = 0;
+    }
+}
+
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+    struct usb_redir_interrupt_receiving_status_header
+    *interrupt_receiving_status)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = interrupt_receiving_status->endpoint;
+
+    DPRINTF("interrupt recv status %d ep %02X id %u\n",
+            interrupt_receiving_status->status, ep, id);
+
+    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
+        return;
+    }
+
+    dev->endpoint[EP2I(ep)].interrupt_error =
+        interrupt_receiving_status->status;
+    if (interrupt_receiving_status->status == usb_redir_stall) {
+        DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].interrupt_started = 0;
+    }
+}
+
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+    struct usb_redir_bulk_streams_status_header *bulk_streams_status)
+{
+}
+
+static void usbredir_control_packet(void *priv, uint32_t id,
+    struct usb_redir_control_packet_header *control_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    int len = control_packet->length;
+    AsyncURB *aurb;
+
+    DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
+            len, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        free(data);
+        return;
+    }
+
+    aurb->control_packet.status = control_packet->status;
+    aurb->control_packet.length = control_packet->length;
+    if (memcmp(&aurb->control_packet, control_packet,
+               sizeof(*control_packet))) {
+        ERROR("return control packet mismatch, please report this!\n");
+        len = USB_RET_NAK;
+    }
+
+    if (aurb->packet) {
+        len = usbredir_handle_status(dev, control_packet->status, len);
+        if (len > 0) {
+            usbredir_log_data(dev, "ctrl data in:", data, data_len);
+            if (data_len <= sizeof(dev->dev.data_buf)) {
+                memcpy(dev->dev.data_buf, data, data_len);
+            } else {
+                ERROR("ctrl buffer too small (%d > %zu)\n",
+                      data_len, sizeof(dev->dev.data_buf));
+                len = USB_RET_STALL;
+            }
+        }
+        aurb->packet->result = len;
+        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+    free(data);
+}
+
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+    struct usb_redir_bulk_packet_header *bulk_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = bulk_packet->endpoint;
+    int len = bulk_packet->length;
+    AsyncURB *aurb;
+
+    DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
+            ep, len, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        free(data);
+        return;
+    }
+
+    if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
+            aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
+        ERROR("return bulk packet mismatch, please report this!\n");
+        len = USB_RET_NAK;
+    }
+
+    if (aurb->packet) {
+        len = usbredir_handle_status(dev, bulk_packet->status, len);
+        if (len > 0) {
+            usbredir_log_data(dev, "bulk data in:", data, data_len);
+            if (data_len <= aurb->packet->iov.size) {
+                usb_packet_copy(aurb->packet, data, data_len);
+            } else {
+                ERROR("bulk buffer too small (%d > %zd)\n", data_len,
+                      aurb->packet->iov.size);
+                len = USB_RET_STALL;
+            }
+        }
+        aurb->packet->result = len;
+        usb_packet_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+    free(data);
+}
+
+static void usbredir_iso_packet(void *priv, uint32_t id,
+    struct usb_redir_iso_packet_header *iso_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = iso_packet->endpoint;
+
+    DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
+             data_len, id);
+
+    if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
+        ERROR("received iso packet for non iso endpoint %02X\n", ep);
+        free(data);
+        return;
+    }
+
+    if (dev->endpoint[EP2I(ep)].iso_started == 0) {
+        DPRINTF("received iso packet for non started stream ep %02X\n", ep);
+        free(data);
+        return;
+    }
+
+    /* bufp_alloc also adds the packet to the ep queue */
+    bufp_alloc(dev, data, data_len, iso_packet->status, ep);
+}
+
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+    struct usb_redir_interrupt_packet_header *interrupt_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = interrupt_packet->endpoint;
+
+    DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
+            interrupt_packet->status, ep, data_len, id);
+
+    if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
+        ERROR("received int packet for non interrupt endpoint %02X\n", ep);
+        free(data);
+        return;
+    }
+
+    if (ep & USB_DIR_IN) {
+        if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
+            DPRINTF("received int packet while not started ep %02X\n", ep);
+            free(data);
+            return;
+        }
+
+        /* bufp_alloc also adds the packet to the ep queue */
+        bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
+    } else {
+        int len = interrupt_packet->length;
+
+        AsyncURB *aurb = async_find(dev, id);
+        if (!aurb) {
+            return;
+        }
+
+        if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
+            ERROR("return int packet mismatch, please report this!\n");
+            len = USB_RET_NAK;
+        }
+
+        if (aurb->packet) {
+            aurb->packet->result = usbredir_handle_status(dev,
+                                               interrupt_packet->status, len);
+            usb_packet_complete(&dev->dev, aurb->packet);
+        }
+        async_free(dev, aurb);
+    }
+}
+
+static Property usbredir_properties[] = {
+    DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
+    DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
+    DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usbredir_class_initfn(ObjectClass *klass, void *data)
+{
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    uc->init           = usbredir_initfn;
+    uc->product_desc   = "USB Redirection Device";
+    uc->handle_destroy = usbredir_handle_destroy;
+    uc->cancel_packet  = usbredir_cancel_packet;
+    uc->handle_reset   = usbredir_handle_reset;
+    uc->handle_data    = usbredir_handle_data;
+    uc->handle_control = usbredir_handle_control;
+    dc->props          = usbredir_properties;
+}
+
+static TypeInfo usbredir_dev_info = {
+    .name          = "usb-redir",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBRedirDevice),
+    .class_init    = usbredir_class_initfn,
+};
+
+static void usbredir_register_types(void)
+{
+    type_register_static(&usbredir_dev_info);
+}
+
+type_init(usbredir_register_types)
index 74bb92af4fc5996ebc89db03dac600c595eea269..bcfe13a9f781a571a71119995c9ccb13e6f106ac 100644 (file)
@@ -227,16 +227,16 @@ sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags
 sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
 sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64
 
-# hw/usb.c
+# hw/usb/core.c
 usb_packet_state_change(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s -> %s"
 
-# hw/usb-bus.c
+# hw/usb/bus.c
 usb_port_claim(int bus, const char *port) "bus %d, port %s"
 usb_port_attach(int bus, const char *port) "bus %d, port %s"
 usb_port_detach(int bus, const char *port) "bus %d, port %s"
 usb_port_release(int bus, const char *port) "bus %d, port %s"
 
-# hw/usb-ehci.c
+# hw/usb/hcd-ehci.c
 usb_ehci_reset(void) "=== RESET ==="
 usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
 usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
@@ -269,7 +269,7 @@ usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d,
 usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
 usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
 
-# usb-linux.c
+# hw/usb/host-linux.c
 usb_host_open_started(int bus, int addr) "dev %d:%d"
 usb_host_open_success(int bus, int addr) "dev %d:%d"
 usb_host_open_failure(int bus, int addr) "dev %d:%d"
diff --git a/usb-bsd.c b/usb-bsd.c
deleted file mode 100644 (file)
index ec26266..0000000
--- a/usb-bsd.c
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * BSD host USB redirector
- *
- * Copyright (c) 2006 Lonnie Mendez
- * Portions of code and concepts borrowed from
- * usb-linux.c and libusb's bsd.c and are copyright their respective owners.
- *
- * 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 "monitor.h"
-#include "hw/usb.h"
-
-/* usb.h declares these */
-#undef USB_SPEED_HIGH
-#undef USB_SPEED_FULL
-#undef USB_SPEED_LOW
-
-#include <sys/ioctl.h>
-#ifndef __DragonFly__
-#include <dev/usb/usb.h>
-#else
-#include <bus/usb/usb.h>
-#endif
-
-/* This value has maximum potential at 16.
- * You should also set hw.usb.debug to gain
- * more detailed view.
- */
-//#define DEBUG
-#define UGEN_DEBUG_LEVEL 0
-
-
-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);
-
-typedef struct USBHostDevice {
-    USBDevice dev;
-    int ep_fd[USB_MAX_ENDPOINTS];
-    int devfd;
-    char devpath[32];
-} USBHostDevice;
-
-
-static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
-{
-    char buf[32];
-    int fd;
-
-    /* Get the address for this endpoint */
-    ep = UE_GET_ADDR(ep);
-
-    if (dev->ep_fd[ep] < 0) {
-#if defined(__FreeBSD__) || defined(__DragonFly__)
-        snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep);
-#else
-        snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep);
-#endif
-        /* Try to open it O_RDWR first for those devices which have in and out
-         * endpoints with the same address (eg 0x02 and 0x82)
-         */
-        fd = open(buf, O_RDWR);
-        if (fd < 0 && errno == ENXIO)
-            fd = open(buf, mode);
-        if (fd < 0) {
-#ifdef DEBUG
-            printf("ensure_ep_open: failed to open device endpoint %s: %s\n",
-                   buf, strerror(errno));
-#endif
-        }
-        dev->ep_fd[ep] = fd;
-    }
-
-    return dev->ep_fd[ep];
-}
-
-static void ensure_eps_closed(USBHostDevice *dev)
-{
-    int epnum = 1;
-
-    if (!dev)
-        return;
-
-    while (epnum < USB_MAX_ENDPOINTS) {
-        if (dev->ep_fd[epnum] >= 0) {
-            close(dev->ep_fd[epnum]);
-            dev->ep_fd[epnum] = -1;
-        }
-        epnum++;
-    }
-}
-
-static void usb_host_handle_reset(USBDevice *dev)
-{
-#if 0
-    USBHostDevice *s = (USBHostDevice *)dev;
-#endif
-}
-
-/* XXX:
- * -check device states against transfer requests
- *  and return appropriate response
- */
-static int usb_host_handle_control(USBDevice *dev,
-                                   USBPacket *p,
-                                   int request,
-                                   int value,
-                                   int index,
-                                   int length,
-                                   uint8_t *data)
-{
-    USBHostDevice *s = (USBHostDevice *)dev;
-    struct usb_ctl_request req;
-    struct usb_alt_interface aiface;
-    int ret, timeout = 50;
-
-    if ((request >> 8) == UT_WRITE_DEVICE &&
-        (request & 0xff) == UR_SET_ADDRESS) {
-
-        /* specific SET_ADDRESS support */
-        dev->addr = value;
-        return 0;
-    } else if ((request >> 8) == UT_WRITE_DEVICE &&
-               (request & 0xff) == UR_SET_CONFIG) {
-
-        ensure_eps_closed(s); /* can't do this without all eps closed */
-
-        ret = ioctl(s->devfd, USB_SET_CONFIG, &value);
-        if (ret < 0) {
-#ifdef DEBUG
-            printf("handle_control: failed to set configuration - %s\n",
-                   strerror(errno));
-#endif
-            return USB_RET_STALL;
-        }
-
-        return 0;
-    } else if ((request >> 8) == UT_WRITE_INTERFACE &&
-               (request & 0xff) == UR_SET_INTERFACE) {
-
-        aiface.uai_interface_index = index;
-        aiface.uai_alt_no = value;
-
-        ensure_eps_closed(s); /* can't do this without all eps closed */
-        ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface);
-        if (ret < 0) {
-#ifdef DEBUG
-            printf("handle_control: failed to set alternate interface - %s\n",
-                   strerror(errno));
-#endif
-            return USB_RET_STALL;
-        }
-
-        return 0;
-    } else {
-        req.ucr_request.bmRequestType = request >> 8;
-        req.ucr_request.bRequest = request & 0xff;
-        USETW(req.ucr_request.wValue, value);
-        USETW(req.ucr_request.wIndex, index);
-        USETW(req.ucr_request.wLength, length);
-        req.ucr_data = data;
-        req.ucr_flags = USBD_SHORT_XFER_OK;
-
-        ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout);
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-        if (ret < 0 && errno != EINVAL) {
-#else
-        if (ret < 0) {
-#endif
-#ifdef DEBUG
-            printf("handle_control: setting timeout failed - %s\n",
-                   strerror(errno));
-#endif
-        }
-
-        ret = ioctl(s->devfd, USB_DO_REQUEST, &req);
-        /* ugen returns EIO for usbd_do_request_ no matter what
-         * happens with the transfer */
-        if (ret < 0) {
-#ifdef DEBUG
-            printf("handle_control: error after request - %s\n",
-                   strerror(errno));
-#endif
-            return USB_RET_NAK; // STALL
-        } else {
-            return req.ucr_actlen;
-        }
-    }
-}
-
-static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = (USBHostDevice *)dev;
-    int ret, fd, mode;
-    int one = 1, shortpacket = 0, timeout = 50;
-    sigset_t new_mask, old_mask;
-    uint8_t devep = p->ep->nr;
-
-    /* protect data transfers from SIGALRM signal */
-    sigemptyset(&new_mask);
-    sigaddset(&new_mask, SIGALRM);
-    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
-
-    if (p->pid == USB_TOKEN_IN) {
-        devep |= 0x80;
-        mode = O_RDONLY;
-        shortpacket = 1;
-    } else {
-        mode = O_WRONLY;
-    }
-
-    fd = ensure_ep_open(s, devep, mode);
-    if (fd < 0) {
-        sigprocmask(SIG_SETMASK, &old_mask, NULL);
-        return USB_RET_NODEV;
-    }
-
-    if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
-#ifdef DEBUG
-        printf("handle_data: failed to set timeout - %s\n",
-               strerror(errno));
-#endif
-    }
-
-    if (shortpacket) {
-        if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) {
-#ifdef DEBUG
-            printf("handle_data: failed to set short xfer mode - %s\n",
-                   strerror(errno));
-#endif
-            sigprocmask(SIG_SETMASK, &old_mask, NULL);
-        }
-    }
-
-    if (p->pid == USB_TOKEN_IN)
-        ret = readv(fd, p->iov.iov, p->iov.niov);
-    else
-        ret = writev(fd, p->iov.iov, p->iov.niov);
-
-    sigprocmask(SIG_SETMASK, &old_mask, NULL);
-
-    if (ret < 0) {
-#ifdef DEBUG
-        printf("handle_data: error after %s data - %s\n",
-               pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno));
-#endif
-        switch(errno) {
-        case ETIMEDOUT:
-        case EINTR:
-            return USB_RET_NAK;
-        default:
-            return USB_RET_STALL;
-        }
-    } else {
-        return ret;
-    }
-}
-
-static void usb_host_handle_destroy(USBDevice *opaque)
-{
-    USBHostDevice *s = (USBHostDevice *)opaque;
-    int i;
-
-    for (i = 0; i < USB_MAX_ENDPOINTS; i++)
-        if (s->ep_fd[i] >= 0)
-            close(s->ep_fd[i]);
-
-    if (s->devfd < 0)
-        return;
-
-    close(s->devfd);
-
-    g_free(s);
-}
-
-static int usb_host_initfn(USBDevice *dev)
-{
-    return 0;
-}
-
-USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname)
-{
-    struct usb_device_info bus_info, dev_info;
-    USBDevice *d = NULL, *ret = NULL;
-    USBHostDevice *dev;
-    char ctlpath[PATH_MAX + 1];
-    char buspath[PATH_MAX + 1];
-    int bfd, dfd, bus, address, i;
-    int ugendebug = UGEN_DEBUG_LEVEL;
-
-    if (usb_host_find_device(&bus, &address, devname) < 0) {
-        goto fail;
-    }
-
-    snprintf(buspath, PATH_MAX, "/dev/usb%d", bus);
-
-    bfd = open(buspath, O_RDWR);
-    if (bfd < 0) {
-#ifdef DEBUG
-        printf("usb_host_device_open: failed to open usb bus - %s\n",
-               strerror(errno));
-#endif
-        goto fail;
-    }
-
-    bus_info.udi_addr = address;
-    if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) {
-#ifdef DEBUG
-        printf("usb_host_device_open: failed to grab bus information - %s\n",
-               strerror(errno));
-#endif
-        goto fail_bfd;
-    }
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-    snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]);
-#else
-    snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]);
-#endif
-
-    dfd  = open(ctlpath, O_RDWR);
-    if (dfd < 0) {
-        dfd = open(ctlpath, O_RDONLY);
-        if (dfd < 0) {
-#ifdef DEBUG
-            printf("usb_host_device_open: failed to open usb device %s - %s\n",
-                   ctlpath, strerror(errno));
-#endif
-        }
-        goto fail_dfd;
-    }
-
-    if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) {
-#ifdef DEBUG
-        printf("usb_host_device_open: failed to grab device info - %s\n",
-               strerror(errno));
-#endif
-        goto fail_dfd;
-    }
-
-    d = usb_create(guest_bus, "usb-host");
-    dev = DO_UPCAST(USBHostDevice, dev, d);
-
-    if (dev_info.udi_speed == 1) {
-        dev->dev.speed = USB_SPEED_LOW - 1;
-        dev->dev.speedmask = USB_SPEED_MASK_LOW;
-    } else {
-        dev->dev.speed = USB_SPEED_FULL - 1;
-        dev->dev.speedmask = USB_SPEED_MASK_FULL;
-    }
-
-    if (strncmp(dev_info.udi_product, "product", 7) != 0) {
-        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                dev_info.udi_product);
-    } else {
-        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                 "host:%s", devname);
-    }
-
-    pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/");
-    pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]);
-
-    /* Mark the endpoints as not yet open */
-    for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
-        dev->ep_fd[i] = -1;
-    }
-
-    ioctl(dfd, USB_SETDEBUG, &ugendebug);
-
-    ret = (USBDevice *)dev;
-
-fail_dfd:
-    close(dfd);
-fail_bfd:
-    close(bfd);
-fail:
-    return ret;
-}
-
-static void usb_host_class_initfn(ObjectClass *klass, void *data)
-{
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->product_desc   = "USB Host Device";
-    uc->init           = usb_host_initfn;
-    uc->handle_reset   = usb_host_handle_reset;
-    uc->handle_control = usb_host_handle_control;
-    uc->handle_data    = usb_host_handle_data;
-    uc->handle_destroy = usb_host_handle_destroy;
-}
-
-static TypeInfo usb_host_dev_info = {
-    .name          = "usb-host",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHostDevice),
-    .class_init    = usb_host_class_initfn,
-};
-
-static void usb_host_register_types(void)
-{
-    type_register_static(&usb_host_dev_info);
-}
-
-type_init(usb_host_register_types)
-
-static int usb_host_scan(void *opaque, USBScanFunc *func)
-{
-    struct usb_device_info bus_info;
-    struct usb_device_info dev_info;
-    uint16_t vendor_id, product_id, class_id, speed;
-    int bfd, dfd, bus, address;
-    char busbuf[20], devbuf[20], product_name[256];
-    int ret = 0;
-
-    for (bus = 0; bus < 10; bus++) {
-
-        snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus);
-        bfd = open(busbuf, O_RDWR);
-        if (bfd < 0)
-           continue;
-
-        for (address = 1; address < 127; address++) {
-
-            bus_info.udi_addr = address;
-            if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0)
-                continue;
-
-            /* only list devices that can be used by generic layer */
-            if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0)
-                continue;
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]);
-#else
-            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]);
-#endif
-
-            dfd = open(devbuf, O_RDONLY);
-            if (dfd < 0) {
-#ifdef DEBUG
-                printf("usb_host_scan: couldn't open device %s - %s\n", devbuf,
-                       strerror(errno));
-#endif
-                continue;
-            }
-
-            if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0)
-                printf("usb_host_scan: couldn't get device information for %s - %s\n",
-                       devbuf, strerror(errno));
-
-            /* XXX: might need to fixup endianness of word values before copying over */
-
-            vendor_id = dev_info.udi_vendorNo;
-            product_id = dev_info.udi_productNo;
-            class_id = dev_info.udi_class;
-            speed = dev_info.udi_speed;
-
-            if (strncmp(dev_info.udi_product, "product", 7) != 0)
-                pstrcpy(product_name, sizeof(product_name),
-                        dev_info.udi_product);
-            else
-                product_name[0] = '\0';
-
-            ret = func(opaque, bus, address, class_id, vendor_id,
-                       product_id, product_name, speed);
-
-            close(dfd);
-
-            if (ret)
-                goto the_end;
-        }
-
-        close(bfd);
-    }
-
-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 (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)
-{
-    const char *p;
-    int ret;
-    FindDeviceState fs;
-
-    p = strchr(devname, '.');
-    if (p) {
-        *pbus_num = strtoul(devname, NULL, 0);
-        *paddr = strtoul(p + 1, NULL, 0);
-        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, 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;
-}
-
-static void usb_info_device(Monitor *mon, 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;
-    }
-
-    monitor_printf(mon, "  Device %d.%d, speed %s Mb/s\n",
-                   bus_num, addr, speed_str);
-    class_str = usb_class_str(class_id);
-    if (class_str)
-        monitor_printf(mon, "    %s:", class_str);
-    else
-        monitor_printf(mon, "    Class %02x:", class_id);
-    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
-    if (product_name[0] != '\0')
-        monitor_printf(mon, ", %s", product_name);
-    monitor_printf(mon, "\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)
-{
-    Monitor *mon = opaque;
-
-    usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
-                    product_name, speed);
-    return 0;
-}
-
-void usb_host_info(Monitor *mon)
-{
-    usb_host_scan(mon, usb_host_info_device);
-}
-
-/* XXX add this */
-int usb_host_device_close(const char *devname)
-{
-    return 0;
-}
diff --git a/usb-linux.c b/usb-linux.c
deleted file mode 100644 (file)
index 90919c2..0000000
+++ /dev/null
@@ -1,1913 +0,0 @@
-/*
- * Linux host USB redirector
- *
- * Copyright (c) 2005 Fabrice Bellard
- *
- * Copyright (c) 2008 Max Krasnyansky
- *      Support for host device auto connect & disconnect
- *      Major rewrite to support fully async operation
- *
- * Copyright 2008 TJ <linux@tjworld.net>
- *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
- *      to the legacy /proc/bus/usb USB device discovery and handling
- *
- * 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 "qemu-timer.h"
-#include "monitor.h"
-#include "sysemu.h"
-#include "trace.h"
-
-#include <dirent.h>
-#include <sys/ioctl.h>
-
-#include <linux/usbdevice_fs.h>
-#include <linux/version.h>
-#include "hw/usb.h"
-
-/* We redefine it to avoid version problems */
-struct usb_ctrltransfer {
-    uint8_t  bRequestType;
-    uint8_t  bRequest;
-    uint16_t wValue;
-    uint16_t wIndex;
-    uint16_t wLength;
-    uint32_t timeout;
-    void *data;
-};
-
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
-                        int class_id, int vendor_id, int product_id,
-                        const char *product_name, int speed);
-
-//#define DEBUG
-
-#ifdef DEBUG
-#define DPRINTF printf
-#else
-#define DPRINTF(...)
-#endif
-
-#define PRODUCT_NAME_SZ 32
-#define MAX_PORTLEN 16
-
-/* endpoint association data */
-#define ISO_FRAME_DESC_PER_URB 32
-
-/* devio.c limits single requests to 16k */
-#define MAX_USBFS_BUFFER_SIZE 16384
-
-typedef struct AsyncURB AsyncURB;
-
-struct endp_data {
-    uint8_t halted;
-    uint8_t iso_started;
-    AsyncURB *iso_urb;
-    int iso_urb_idx;
-    int iso_buffer_used;
-    int inflight;
-};
-
-struct USBAutoFilter {
-    uint32_t bus_num;
-    uint32_t addr;
-    char     *port;
-    uint32_t vendor_id;
-    uint32_t product_id;
-};
-
-typedef struct USBHostDevice {
-    USBDevice dev;
-    int       fd;
-    int       hub_fd;
-    int       hub_port;
-
-    uint8_t   descr[8192];
-    int       descr_len;
-    int       closing;
-    uint32_t  iso_urb_count;
-    Notifier  exit;
-
-    struct endp_data ep_in[USB_MAX_ENDPOINTS];
-    struct endp_data ep_out[USB_MAX_ENDPOINTS];
-    QLIST_HEAD(, AsyncURB) aurbs;
-
-    /* Host side address */
-    int bus_num;
-    int addr;
-    char port[MAX_PORTLEN];
-    struct USBAutoFilter match;
-    int seen, errcount;
-
-    QTAILQ_ENTRY(USBHostDevice) next;
-} USBHostDevice;
-
-static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
-
-static int usb_host_close(USBHostDevice *dev);
-static int parse_filter(const char *spec, struct USBAutoFilter *f);
-static void usb_host_auto_check(void *unused);
-static int usb_host_read_file(char *line, size_t line_size,
-                            const char *device_file, const char *device_name);
-static int usb_linux_update_endp_table(USBHostDevice *s);
-
-static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p)
-{
-    static const int usbfs[] = {
-        [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL,
-        [USB_ENDPOINT_XFER_ISOC]    = USBDEVFS_URB_TYPE_ISO,
-        [USB_ENDPOINT_XFER_BULK]    = USBDEVFS_URB_TYPE_BULK,
-        [USB_ENDPOINT_XFER_INT]     = USBDEVFS_URB_TYPE_INTERRUPT,
-    };
-    uint8_t type = p->ep->type;
-    assert(type < ARRAY_SIZE(usbfs));
-    return usbfs[type];
-}
-
-static int usb_host_do_reset(USBHostDevice *dev)
-{
-    struct timeval s, e;
-    uint32_t usecs;
-    int ret;
-
-    gettimeofday(&s, NULL);
-    ret = ioctl(dev->fd, USBDEVFS_RESET);
-    gettimeofday(&e, NULL);
-    usecs = (e.tv_sec  - s.tv_sec) * 1000000;
-    usecs += e.tv_usec - s.tv_usec;
-    if (usecs > 1000000) {
-        /* more than a second, something is fishy, broken usb device? */
-        fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n",
-                dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000);
-    }
-    return ret;
-}
-
-static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
-{
-    struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
-    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
-    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
-    return eps + ep - 1;
-}
-
-static int is_isoc(USBHostDevice *s, int pid, int ep)
-{
-    return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
-}
-
-static int is_valid(USBHostDevice *s, int pid, int ep)
-{
-    return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
-}
-
-static int is_halted(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->halted;
-}
-
-static void clear_halt(USBHostDevice *s, int pid, int ep)
-{
-    trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep);
-    get_endp(s, pid, ep)->halted = 0;
-}
-
-static void set_halt(USBHostDevice *s, int pid, int ep)
-{
-    if (ep != 0) {
-        trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep);
-        get_endp(s, pid, ep)->halted = 1;
-    }
-}
-
-static int is_iso_started(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_started;
-}
-
-static void clear_iso_started(USBHostDevice *s, int pid, int ep)
-{
-    trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep);
-    get_endp(s, pid, ep)->iso_started = 0;
-}
-
-static void set_iso_started(USBHostDevice *s, int pid, int ep)
-{
-    struct endp_data *e = get_endp(s, pid, ep);
-
-    trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep);
-    if (!e->iso_started) {
-        e->iso_started = 1;
-        e->inflight = 0;
-    }
-}
-
-static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
-{
-    struct endp_data *e = get_endp(s, pid, ep);
-
-    e->inflight += value;
-    return e->inflight;
-}
-
-static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
-{
-    get_endp(s, pid, ep)->iso_urb = iso_urb;
-}
-
-static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_urb;
-}
-
-static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
-{
-    get_endp(s, pid, ep)->iso_urb_idx = i;
-}
-
-static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_urb_idx;
-}
-
-static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
-{
-    get_endp(s, pid, ep)->iso_buffer_used = i;
-}
-
-static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_buffer_used;
-}
-
-/*
- * Async URB state.
- * We always allocate iso packet descriptors even for bulk transfers
- * to simplify allocation and casts.
- */
-struct AsyncURB
-{
-    struct usbdevfs_urb urb;
-    struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
-    USBHostDevice *hdev;
-    QLIST_ENTRY(AsyncURB) next;
-
-    /* For regular async urbs */
-    USBPacket     *packet;
-    int more; /* large transfer, more urbs follow */
-
-    /* For buffered iso handling */
-    int iso_frame_idx; /* -1 means in flight */
-};
-
-static AsyncURB *async_alloc(USBHostDevice *s)
-{
-    AsyncURB *aurb = g_malloc0(sizeof(AsyncURB));
-    aurb->hdev = s;
-    QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
-    return aurb;
-}
-
-static void async_free(AsyncURB *aurb)
-{
-    QLIST_REMOVE(aurb, next);
-    g_free(aurb);
-}
-
-static void do_disconnect(USBHostDevice *s)
-{
-    usb_host_close(s);
-    usb_host_auto_check(NULL);
-}
-
-static void async_complete(void *opaque)
-{
-    USBHostDevice *s = opaque;
-    AsyncURB *aurb;
-    int urbs = 0;
-
-    while (1) {
-        USBPacket *p;
-
-        int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
-        if (r < 0) {
-            if (errno == EAGAIN) {
-                if (urbs > 2) {
-                    fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs);
-                }
-                return;
-            }
-            if (errno == ENODEV) {
-                if (!s->closing) {
-                    trace_usb_host_disconnect(s->bus_num, s->addr);
-                    do_disconnect(s);
-                }
-                return;
-            }
-
-            perror("USBDEVFS_REAPURBNDELAY");
-            return;
-        }
-
-        DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
-                aurb, aurb->urb.status, aurb->urb.actual_length);
-
-        /* If this is a buffered iso urb mark it as complete and don't do
-           anything else (it is handled further in usb_host_handle_iso_data) */
-        if (aurb->iso_frame_idx == -1) {
-            int inflight;
-            int pid = (aurb->urb.endpoint & USB_DIR_IN) ?
-                USB_TOKEN_IN : USB_TOKEN_OUT;
-            int ep = aurb->urb.endpoint & 0xf;
-            if (aurb->urb.status == -EPIPE) {
-                set_halt(s, pid, ep);
-            }
-            aurb->iso_frame_idx = 0;
-            urbs++;
-            inflight = change_iso_inflight(s, pid, ep, -1);
-            if (inflight == 0 && is_iso_started(s, pid, ep)) {
-                fprintf(stderr, "husb: out of buffers for iso stream\n");
-            }
-            continue;
-        }
-
-        p = aurb->packet;
-        trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status,
-                                    aurb->urb.actual_length, aurb->more);
-
-        if (p) {
-            switch (aurb->urb.status) {
-            case 0:
-                p->result += aurb->urb.actual_length;
-                break;
-
-            case -EPIPE:
-                set_halt(s, p->pid, p->ep->nr);
-                p->result = USB_RET_STALL;
-                break;
-
-            case -EOVERFLOW:
-                p->result = USB_RET_BABBLE;
-                break;
-
-            default:
-                p->result = USB_RET_IOERROR;
-                break;
-            }
-
-            if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
-                usb_generic_async_ctrl_complete(&s->dev, p);
-            } else if (!aurb->more) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
-                usb_packet_complete(&s->dev, p);
-            }
-        }
-
-        async_free(aurb);
-    }
-}
-
-static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-    AsyncURB *aurb;
-
-    QLIST_FOREACH(aurb, &s->aurbs, next) {
-        if (p != aurb->packet) {
-            continue;
-        }
-
-        DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
-
-        /* Mark it as dead (see async_complete above) */
-        aurb->packet = NULL;
-
-        int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
-        if (r < 0) {
-            DPRINTF("husb: async. discard urb failed errno %d\n", errno);
-        }
-    }
-}
-
-static int usb_host_open_device(int bus, int addr)
-{
-    const char *usbfs = NULL;
-    char filename[32];
-    struct stat st;
-    int fd, rc;
-
-    rc = stat("/dev/bus/usb", &st);
-    if (rc == 0 && S_ISDIR(st.st_mode)) {
-        /* udev-created device nodes available */
-        usbfs = "/dev/bus/usb";
-    } else {
-        /* fallback: usbfs mounted below /proc */
-        usbfs = "/proc/bus/usb";
-    }
-
-    snprintf(filename, sizeof(filename), "%s/%03d/%03d",
-             usbfs, bus, addr);
-    fd = open(filename, O_RDWR | O_NONBLOCK);
-    if (fd < 0) {
-        fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno));
-    }
-    return fd;
-}
-
-static int usb_host_claim_port(USBHostDevice *s)
-{
-#ifdef USBDEVFS_CLAIM_PORT
-    char *h, hub_name[64], line[1024];
-    int hub_addr, ret;
-
-    snprintf(hub_name, sizeof(hub_name), "%d-%s",
-             s->match.bus_num, s->match.port);
-
-    /* try strip off last ".$portnr" to get hub */
-    h = strrchr(hub_name, '.');
-    if (h != NULL) {
-        s->hub_port = atoi(h+1);
-        *h = '\0';
-    } else {
-        /* no dot in there -> it is the root hub */
-        snprintf(hub_name, sizeof(hub_name), "usb%d",
-                 s->match.bus_num);
-        s->hub_port = atoi(s->match.port);
-    }
-
-    if (!usb_host_read_file(line, sizeof(line), "devnum",
-                            hub_name)) {
-        return -1;
-    }
-    if (sscanf(line, "%d", &hub_addr) != 1) {
-        return -1;
-    }
-
-    s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
-    if (s->hub_fd < 0) {
-        return -1;
-    }
-
-    ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port);
-    if (ret < 0) {
-        close(s->hub_fd);
-        s->hub_fd = -1;
-        return -1;
-    }
-
-    trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port);
-    return 0;
-#else
-    return -1;
-#endif
-}
-
-static void usb_host_release_port(USBHostDevice *s)
-{
-    if (s->hub_fd == -1) {
-        return;
-    }
-#ifdef USBDEVFS_RELEASE_PORT
-    ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port);
-#endif
-    close(s->hub_fd);
-    s->hub_fd = -1;
-}
-
-static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
-{
-    /* earlier Linux 2.4 do not support that */
-#ifdef USBDEVFS_DISCONNECT
-    struct usbdevfs_ioctl ctrl;
-    int ret, interface;
-
-    for (interface = 0; interface < nb_interfaces; interface++) {
-        ctrl.ioctl_code = USBDEVFS_DISCONNECT;
-        ctrl.ifno = interface;
-        ctrl.data = 0;
-        ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
-        if (ret < 0 && errno != ENODATA) {
-            perror("USBDEVFS_DISCONNECT");
-            return -1;
-        }
-    }
-#endif
-    return 0;
-}
-
-static int usb_linux_get_num_interfaces(USBHostDevice *s)
-{
-    char device_name[64], line[1024];
-    int num_interfaces = 0;
-
-    sprintf(device_name, "%d-%s", s->bus_num, s->port);
-    if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
-                            device_name)) {
-        return -1;
-    }
-    if (sscanf(line, "%d", &num_interfaces) != 1) {
-        return -1;
-    }
-    return num_interfaces;
-}
-
-static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
-{
-    const char *op = NULL;
-    int dev_descr_len, config_descr_len;
-    int interface, nb_interfaces;
-    int ret, i;
-
-    for (i = 0; i < USB_MAX_INTERFACES; i++) {
-        dev->dev.altsetting[i] = 0;
-    }
-
-    if (configuration == 0) { /* address state - ignore */
-        dev->dev.ninterfaces   = 0;
-        dev->dev.configuration = 0;
-        return 1;
-    }
-
-    DPRINTF("husb: claiming interfaces. config %d\n", configuration);
-
-    i = 0;
-    dev_descr_len = dev->descr[0];
-    if (dev_descr_len > dev->descr_len) {
-        fprintf(stderr, "husb: update iface failed. descr too short\n");
-        return 0;
-    }
-
-    i += dev_descr_len;
-    while (i < dev->descr_len) {
-        DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
-                i, dev->descr_len,
-               dev->descr[i], dev->descr[i+1]);
-
-        if (dev->descr[i+1] != USB_DT_CONFIG) {
-            i += dev->descr[i];
-            continue;
-        }
-        config_descr_len = dev->descr[i];
-
-        DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
-
-        if (configuration == dev->descr[i + 5]) {
-            configuration = dev->descr[i + 5];
-            break;
-        }
-
-        i += config_descr_len;
-    }
-
-    if (i >= dev->descr_len) {
-        fprintf(stderr,
-                "husb: update iface failed. no matching configuration\n");
-        return 0;
-    }
-    nb_interfaces = dev->descr[i + 4];
-
-    if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
-        goto fail;
-    }
-
-    /* XXX: only grab if all interfaces are free */
-    for (interface = 0; interface < nb_interfaces; interface++) {
-        op = "USBDEVFS_CLAIMINTERFACE";
-        ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
-        if (ret < 0) {
-            goto fail;
-        }
-    }
-
-    trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
-                                    nb_interfaces, configuration);
-
-    dev->dev.ninterfaces   = nb_interfaces;
-    dev->dev.configuration = configuration;
-    return 1;
-
-fail:
-    if (errno == ENODEV) {
-        do_disconnect(dev);
-    }
-    perror(op);
-    return 0;
-}
-
-static int usb_host_release_interfaces(USBHostDevice *s)
-{
-    int ret, i;
-
-    trace_usb_host_release_interfaces(s->bus_num, s->addr);
-
-    for (i = 0; i < s->dev.ninterfaces; i++) {
-        ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
-        if (ret < 0) {
-            perror("USBDEVFS_RELEASEINTERFACE");
-            return 0;
-        }
-    }
-    return 1;
-}
-
-static void usb_host_handle_reset(USBDevice *dev)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-
-    trace_usb_host_reset(s->bus_num, s->addr);
-
-    usb_host_do_reset(s);;
-
-    usb_host_claim_interfaces(s, 0);
-    usb_linux_update_endp_table(s);
-}
-
-static void usb_host_handle_destroy(USBDevice *dev)
-{
-    USBHostDevice *s = (USBHostDevice *)dev;
-
-    usb_host_release_port(s);
-    usb_host_close(s);
-    QTAILQ_REMOVE(&hostdevs, s, next);
-    qemu_remove_exit_notifier(&s->exit);
-}
-
-/* iso data is special, we need to keep enough urbs in flight to make sure
-   that the controller never runs out of them, otherwise the device will
-   likely suffer a buffer underrun / overrun. */
-static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
-{
-    AsyncURB *aurb;
-    int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
-
-    aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
-    for (i = 0; i < s->iso_urb_count; i++) {
-        aurb[i].urb.endpoint      = ep;
-        aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
-        aurb[i].urb.buffer        = g_malloc(aurb[i].urb.buffer_length);
-        aurb[i].urb.type          = USBDEVFS_URB_TYPE_ISO;
-        aurb[i].urb.flags         = USBDEVFS_URB_ISO_ASAP;
-        aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
-        for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
-            aurb[i].urb.iso_frame_desc[j].length = len;
-        if (pid == USB_TOKEN_IN) {
-            aurb[i].urb.endpoint |= 0x80;
-            /* Mark as fully consumed (idle) */
-            aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
-        }
-    }
-    set_iso_urb(s, pid, ep, aurb);
-
-    return aurb;
-}
-
-static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
-{
-    AsyncURB *aurb;
-    int i, ret, killed = 0, free = 1;
-
-    aurb = get_iso_urb(s, pid, ep);
-    if (!aurb) {
-        return;
-    }
-
-    for (i = 0; i < s->iso_urb_count; i++) {
-        /* in flight? */
-        if (aurb[i].iso_frame_idx == -1) {
-            ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
-            if (ret < 0) {
-                perror("USBDEVFS_DISCARDURB");
-                free = 0;
-                continue;
-            }
-            killed++;
-        }
-    }
-
-    /* Make sure any urbs we've killed are reaped before we free them */
-    if (killed) {
-        async_complete(s);
-    }
-
-    for (i = 0; i < s->iso_urb_count; i++) {
-        g_free(aurb[i].urb.buffer);
-    }
-
-    if (free)
-        g_free(aurb);
-    else
-        printf("husb: leaking iso urbs because of discard failure\n");
-    set_iso_urb(s, pid, ep, NULL);
-    set_iso_urb_idx(s, pid, ep, 0);
-    clear_iso_started(s, pid, ep);
-}
-
-static int urb_status_to_usb_ret(int status)
-{
-    switch (status) {
-    case -EPIPE:
-        return USB_RET_STALL;
-    case -EOVERFLOW:
-        return USB_RET_BABBLE;
-    default:
-        return USB_RET_IOERROR;
-    }
-}
-
-static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
-{
-    AsyncURB *aurb;
-    int i, j, ret, max_packet_size, offset, len = 0;
-    uint8_t *buf;
-
-    max_packet_size = p->ep->max_packet_size;
-    if (max_packet_size == 0)
-        return USB_RET_NAK;
-
-    aurb = get_iso_urb(s, p->pid, p->ep->nr);
-    if (!aurb) {
-        aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr);
-    }
-
-    i = get_iso_urb_idx(s, p->pid, p->ep->nr);
-    j = aurb[i].iso_frame_idx;
-    if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
-        if (in) {
-            /* Check urb status  */
-            if (aurb[i].urb.status) {
-                len = urb_status_to_usb_ret(aurb[i].urb.status);
-                /* Move to the next urb */
-                aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
-            /* Check frame status */
-            } else if (aurb[i].urb.iso_frame_desc[j].status) {
-                len = urb_status_to_usb_ret(
-                                        aurb[i].urb.iso_frame_desc[j].status);
-            /* Check the frame fits */
-            } else if (aurb[i].urb.iso_frame_desc[j].actual_length
-                       > p->iov.size) {
-                printf("husb: received iso data is larger then packet\n");
-                len = USB_RET_BABBLE;
-            /* All good copy data over */
-            } else {
-                len = aurb[i].urb.iso_frame_desc[j].actual_length;
-                buf  = aurb[i].urb.buffer +
-                    j * aurb[i].urb.iso_frame_desc[0].length;
-                usb_packet_copy(p, buf, len);
-            }
-        } else {
-            len = p->iov.size;
-            offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr);
-
-            /* Check the frame fits */
-            if (len > max_packet_size) {
-                printf("husb: send iso data is larger then max packet size\n");
-                return USB_RET_NAK;
-            }
-
-            /* All good copy data over */
-            usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
-            aurb[i].urb.iso_frame_desc[j].length = len;
-            offset += len;
-            set_iso_buffer_used(s, p->pid, p->ep->nr, offset);
-
-            /* Start the stream once we have buffered enough data */
-            if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) {
-                set_iso_started(s, p->pid, p->ep->nr);
-            }
-        }
-        aurb[i].iso_frame_idx++;
-        if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
-            i = (i + 1) % s->iso_urb_count;
-            set_iso_urb_idx(s, p->pid, p->ep->nr, i);
-        }
-    } else {
-        if (in) {
-            set_iso_started(s, p->pid, p->ep->nr);
-        } else {
-            DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
-        }
-    }
-
-    if (is_iso_started(s, p->pid, p->ep->nr)) {
-        /* (Re)-submit all fully consumed / filled urbs */
-        for (i = 0; i < s->iso_urb_count; i++) {
-            if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
-                ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
-                if (ret < 0) {
-                    perror("USBDEVFS_SUBMITURB");
-                    if (!in || len == 0) {
-                        switch(errno) {
-                        case ETIMEDOUT:
-                            len = USB_RET_NAK;
-                            break;
-                        case EPIPE:
-                        default:
-                            len = USB_RET_STALL;
-                        }
-                    }
-                    break;
-                }
-                aurb[i].iso_frame_idx = -1;
-                change_iso_inflight(s, p->pid, p->ep->nr, 1);
-            }
-        }
-    }
-
-    return len;
-}
-
-static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-    struct usbdevfs_urb *urb;
-    AsyncURB *aurb;
-    int ret, rem, prem, v;
-    uint8_t *pbuf;
-    uint8_t ep;
-
-    trace_usb_host_req_data(s->bus_num, s->addr,
-                            p->pid == USB_TOKEN_IN,
-                            p->ep->nr, p->iov.size);
-
-    if (!is_valid(s, p->pid, p->ep->nr)) {
-        trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
-        return USB_RET_NAK;
-    }
-
-    if (p->pid == USB_TOKEN_IN) {
-        ep = p->ep->nr | 0x80;
-    } else {
-        ep = p->ep->nr;
-    }
-
-    if (is_halted(s, p->pid, p->ep->nr)) {
-        unsigned int arg = ep;
-        ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
-        if (ret < 0) {
-            perror("USBDEVFS_CLEAR_HALT");
-            trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
-            return USB_RET_NAK;
-        }
-        clear_halt(s, p->pid, p->ep->nr);
-    }
-
-    if (is_isoc(s, p->pid, p->ep->nr)) {
-        return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
-    }
-
-    v = 0;
-    prem = p->iov.iov[v].iov_len;
-    pbuf = p->iov.iov[v].iov_base;
-    rem = p->iov.size;
-    while (rem) {
-        if (prem == 0) {
-            v++;
-            assert(v < p->iov.niov);
-            prem = p->iov.iov[v].iov_len;
-            pbuf = p->iov.iov[v].iov_base;
-            assert(prem <= rem);
-        }
-        aurb = async_alloc(s);
-        aurb->packet = p;
-
-        urb = &aurb->urb;
-        urb->endpoint      = ep;
-        urb->type          = usb_host_usbfs_type(s, p);
-        urb->usercontext   = s;
-        urb->buffer        = pbuf;
-        urb->buffer_length = prem;
-
-        if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
-            urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
-        }
-        pbuf += urb->buffer_length;
-        prem -= urb->buffer_length;
-        rem  -= urb->buffer_length;
-        if (rem) {
-            aurb->more         = 1;
-        }
-
-        trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
-                                  urb->buffer_length, aurb->more);
-        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
-
-        DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
-                urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
-
-        if (ret < 0) {
-            perror("USBDEVFS_SUBMITURB");
-            async_free(aurb);
-
-            switch(errno) {
-            case ETIMEDOUT:
-                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
-                return USB_RET_NAK;
-            case EPIPE:
-            default:
-                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL);
-                return USB_RET_STALL;
-            }
-        }
-    }
-
-    return USB_RET_ASYNC;
-}
-
-static int ctrl_error(void)
-{
-    if (errno == ETIMEDOUT) {
-        return USB_RET_NAK;
-    } else {
-        return USB_RET_STALL;
-    }
-}
-
-static int usb_host_set_address(USBHostDevice *s, int addr)
-{
-    trace_usb_host_set_address(s->bus_num, s->addr, addr);
-    s->dev.addr = addr;
-    return 0;
-}
-
-static int usb_host_set_config(USBHostDevice *s, int config)
-{
-    int ret, first = 1;
-
-    trace_usb_host_set_config(s->bus_num, s->addr, config);
-
-    usb_host_release_interfaces(s);
-
-again:
-    ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
-
-    DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
-
-    if (ret < 0 && errno == EBUSY && first) {
-        /* happens if usb device is in use by host drivers */
-        int count = usb_linux_get_num_interfaces(s);
-        if (count > 0) {
-            DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
-            usb_host_disconnect_ifaces(s, count);
-            first = 0;
-            goto again;
-        }
-    }
-
-    if (ret < 0) {
-        return ctrl_error();
-    }
-    usb_host_claim_interfaces(s, config);
-    usb_linux_update_endp_table(s);
-    return 0;
-}
-
-static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
-{
-    struct usbdevfs_setinterface si;
-    int i, ret;
-
-    trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
-
-    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
-        if (is_isoc(s, USB_TOKEN_IN, i)) {
-            usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
-        }
-        if (is_isoc(s, USB_TOKEN_OUT, i)) {
-            usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i);
-        }
-    }
-
-    if (iface >= USB_MAX_INTERFACES) {
-        return USB_RET_STALL;
-    }
-
-    si.interface  = iface;
-    si.altsetting = alt;
-    ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
-
-    DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n",
-            iface, alt, ret, errno);
-
-    if (ret < 0) {
-        return ctrl_error();
-    }
-
-    s->dev.altsetting[iface] = alt;
-    usb_linux_update_endp_table(s);
-    return 0;
-}
-
-static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-    struct usbdevfs_urb *urb;
-    AsyncURB *aurb;
-    int ret;
-
-    /*
-     * Process certain standard device requests.
-     * These are infrequent and are processed synchronously.
-     */
-
-    /* Note request is (bRequestType << 8) | bRequest */
-    trace_usb_host_req_control(s->bus_num, s->addr, request, value, index);
-
-    switch (request) {
-    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-        return usb_host_set_address(s, value);
-
-    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        return usb_host_set_config(s, value & 0xff);
-
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        return usb_host_set_interface(s, index, value);
-    }
-
-    /* The rest are asynchronous */
-
-    if (length > sizeof(dev->data_buf)) {
-        fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
-                length, sizeof(dev->data_buf));
-        return USB_RET_STALL;
-    }
-
-    aurb = async_alloc(s);
-    aurb->packet = p;
-
-    /*
-     * Setup ctrl transfer.
-     *
-     * s->ctrl is laid out such that data buffer immediately follows
-     * 'req' struct which is exactly what usbdevfs expects.
-     */
-    urb = &aurb->urb;
-
-    urb->type     = USBDEVFS_URB_TYPE_CONTROL;
-    urb->endpoint = p->ep->nr;
-
-    urb->buffer        = &dev->setup_buf;
-    urb->buffer_length = length + 8;
-
-    urb->usercontext = s;
-
-    trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
-                              urb->buffer_length, aurb->more);
-    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
-
-    DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
-
-    if (ret < 0) {
-        DPRINTF("husb: submit failed. errno %d\n", errno);
-        async_free(aurb);
-
-        switch(errno) {
-        case ETIMEDOUT:
-            return USB_RET_NAK;
-        case EPIPE:
-        default:
-            return USB_RET_STALL;
-        }
-    }
-
-    return USB_RET_ASYNC;
-}
-
-static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
-    uint8_t configuration, uint8_t interface)
-{
-    char device_name[64], line[1024];
-    int alt_setting;
-
-    sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
-            (int)configuration, (int)interface);
-
-    if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
-                            device_name)) {
-        /* Assume alt 0 on error */
-        return 0;
-    }
-    if (sscanf(line, "%d", &alt_setting) != 1) {
-        /* Assume alt 0 on error */
-        return 0;
-    }
-    return alt_setting;
-}
-
-/* returns 1 on problem encountered or 0 for success */
-static int usb_linux_update_endp_table(USBHostDevice *s)
-{
-    uint8_t *descriptors;
-    uint8_t devep, type, alt_interface;
-    uint16_t raw;
-    int interface, length, i, ep, pid;
-    struct endp_data *epd;
-
-    usb_ep_init(&s->dev);
-
-    if (s->dev.configuration == 0) {
-        /* not configured yet -- leave all endpoints disabled */
-        return 0;
-    }
-
-    /* get the desired configuration, interface, and endpoint descriptors
-     * from device description */
-    descriptors = &s->descr[18];
-    length = s->descr_len - 18;
-    i = 0;
-
-    while (i < length) {
-        if (descriptors[i + 1] != USB_DT_CONFIG) {
-            fprintf(stderr, "invalid descriptor data\n");
-            return 1;
-        } else if (descriptors[i + 5] != s->dev.configuration) {
-            DPRINTF("not requested configuration %d\n", s->dev.configuration);
-            i += (descriptors[i + 3] << 8) + descriptors[i + 2];
-            continue;
-        }
-        i += descriptors[i];
-
-        if (descriptors[i + 1] != USB_DT_INTERFACE ||
-            (descriptors[i + 1] == USB_DT_INTERFACE &&
-             descriptors[i + 4] == 0)) {
-            i += descriptors[i];
-            continue;
-        }
-
-        interface = descriptors[i + 2];
-        alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
-                                                  interface);
-
-        /* the current interface descriptor is the active interface
-         * and has endpoints */
-        if (descriptors[i + 3] != alt_interface) {
-            i += descriptors[i];
-            continue;
-        }
-
-        /* advance to the endpoints */
-        while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) {
-            i += descriptors[i];
-        }
-
-        if (i >= length)
-            break;
-
-        while (i < length) {
-            if (descriptors[i + 1] != USB_DT_ENDPOINT) {
-                break;
-            }
-
-            devep = descriptors[i + 2];
-            pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
-            ep = devep & 0xf;
-            if (ep == 0) {
-                fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n");
-                return 1;
-            }
-
-            type = descriptors[i + 3] & 0x3;
-            raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
-            usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
-            assert(usb_ep_get_type(&s->dev, pid, ep) ==
-                   USB_ENDPOINT_XFER_INVALID);
-            usb_ep_set_type(&s->dev, pid, ep, type);
-            usb_ep_set_ifnum(&s->dev, pid, ep, interface);
-            if (type == USB_ENDPOINT_XFER_BULK) {
-                usb_ep_set_pipeline(&s->dev, pid, ep, true);
-            }
-
-            epd = get_endp(s, pid, ep);
-            epd->halted = 0;
-
-            i += descriptors[i];
-        }
-    }
-#ifdef DEBUG
-    usb_ep_dump(&s->dev);
-#endif
-    return 0;
-}
-
-/*
- * Check if we can safely redirect a usb2 device to a usb1 virtual controller,
- * this function assumes this is safe, if:
- * 1) There are no isoc endpoints
- * 2) There are no interrupt endpoints with a max_packet_size > 64
- * Note bulk endpoints with a max_packet_size > 64 in theory also are not
- * usb1 compatible, but in practice this seems to work fine.
- */
-static int usb_linux_full_speed_compat(USBHostDevice *dev)
-{
-    int i, packet_size;
-
-    /*
-     * usb_linux_update_endp_table only registers info about ep in the current
-     * interface altsettings, so we need to parse the descriptors again.
-     */
-    for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) {
-        if (dev->descr[i + 1] == USB_DT_ENDPOINT) {
-            switch (dev->descr[i + 3] & 0x3) {
-            case 0x00: /* CONTROL */
-                break;
-            case 0x01: /* ISO */
-                return 0;
-            case 0x02: /* BULK */
-                break;
-            case 0x03: /* INTERRUPT */
-                packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8);
-                if (packet_size > 64)
-                    return 0;
-                break;
-            }
-        }
-    }
-    return 1;
-}
-
-static int usb_host_open(USBHostDevice *dev, int bus_num,
-                         int addr, const char *port,
-                         const char *prod_name, int speed)
-{
-    int fd = -1, ret;
-
-    trace_usb_host_open_started(bus_num, addr);
-
-    if (dev->fd != -1) {
-        goto fail;
-    }
-
-    fd = usb_host_open_device(bus_num, addr);
-    if (fd < 0) {
-        goto fail;
-    }
-    DPRINTF("husb: opened %s\n", buf);
-
-    dev->bus_num = bus_num;
-    dev->addr = addr;
-    strcpy(dev->port, port);
-    dev->fd = fd;
-
-    /* read the device description */
-    dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
-    if (dev->descr_len <= 0) {
-        perror("husb: reading device data failed");
-        goto fail;
-    }
-
-#ifdef DEBUG
-    {
-        int x;
-        printf("=== begin dumping device descriptor data ===\n");
-        for (x = 0; x < dev->descr_len; x++) {
-            printf("%02x ", dev->descr[x]);
-        }
-        printf("\n=== end dumping device descriptor data ===\n");
-    }
-#endif
-
-
-    /* start unconfigured -- we'll wait for the guest to set a configuration */
-    if (!usb_host_claim_interfaces(dev, 0)) {
-        goto fail;
-    }
-
-    ret = usb_linux_update_endp_table(dev);
-    if (ret) {
-        goto fail;
-    }
-
-    if (speed == -1) {
-        struct usbdevfs_connectinfo ci;
-
-        ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
-        if (ret < 0) {
-            perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
-            goto fail;
-        }
-
-        if (ci.slow) {
-            speed = USB_SPEED_LOW;
-        } else {
-            speed = USB_SPEED_HIGH;
-        }
-    }
-    dev->dev.speed = speed;
-    dev->dev.speedmask = (1 << speed);
-    if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
-        dev->dev.speedmask |= USB_SPEED_MASK_FULL;
-    }
-
-    trace_usb_host_open_success(bus_num, addr);
-
-    if (!prod_name || prod_name[0] == '\0') {
-        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                 "host:%d.%d", bus_num, addr);
-    } else {
-        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                prod_name);
-    }
-
-    ret = usb_device_attach(&dev->dev);
-    if (ret) {
-        goto fail;
-    }
-
-    /* USB devio uses 'write' flag to check for async completions */
-    qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
-
-    return 0;
-
-fail:
-    trace_usb_host_open_failure(bus_num, addr);
-    if (dev->fd != -1) {
-        close(dev->fd);
-        dev->fd = -1;
-    }
-    return -1;
-}
-
-static int usb_host_close(USBHostDevice *dev)
-{
-    int i;
-
-    if (dev->fd == -1) {
-        return -1;
-    }
-
-    trace_usb_host_close(dev->bus_num, dev->addr);
-
-    qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
-    dev->closing = 1;
-    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
-        if (is_isoc(dev, USB_TOKEN_IN, i)) {
-            usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
-        }
-        if (is_isoc(dev, USB_TOKEN_OUT, i)) {
-            usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i);
-        }
-    }
-    async_complete(dev);
-    dev->closing = 0;
-    if (dev->dev.attached) {
-        usb_device_detach(&dev->dev);
-    }
-    usb_host_do_reset(dev);
-    close(dev->fd);
-    dev->fd = -1;
-    return 0;
-}
-
-static void usb_host_exit_notifier(struct Notifier *n, void *data)
-{
-    USBHostDevice *s = container_of(n, USBHostDevice, exit);
-
-    usb_host_release_port(s);
-    if (s->fd != -1) {
-        usb_host_do_reset(s);;
-    }
-}
-
-static int usb_host_initfn(USBDevice *dev)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-
-    dev->auto_attach = 0;
-    s->fd = -1;
-    s->hub_fd = -1;
-
-    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
-    s->exit.notify = usb_host_exit_notifier;
-    qemu_add_exit_notifier(&s->exit);
-    usb_host_auto_check(NULL);
-
-    if (s->match.bus_num != 0 && s->match.port != NULL) {
-        usb_host_claim_port(s);
-    }
-    return 0;
-}
-
-static const VMStateDescription vmstate_usb_host = {
-    .name = "usb-host",
-    .unmigratable = 1,
-};
-
-static Property usb_host_dev_properties[] = {
-    DEFINE_PROP_UINT32("hostbus",  USBHostDevice, match.bus_num,    0),
-    DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr,       0),
-    DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
-    DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
-    DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
-    DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usb_host_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = usb_host_initfn;
-    uc->product_desc   = "USB Host Device";
-    uc->cancel_packet  = usb_host_async_cancel;
-    uc->handle_data    = usb_host_handle_data;
-    uc->handle_control = usb_host_handle_control;
-    uc->handle_reset   = usb_host_handle_reset;
-    uc->handle_destroy = usb_host_handle_destroy;
-    dc->vmsd = &vmstate_usb_host;
-    dc->props = usb_host_dev_properties;
-}
-
-static TypeInfo usb_host_dev_info = {
-    .name          = "usb-host",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHostDevice),
-    .class_init    = usb_host_class_initfn,
-};
-
-static void usb_host_register_types(void)
-{
-    type_register_static(&usb_host_dev_info);
-    usb_legacy_register("usb-host", "host", usb_host_device_open);
-}
-
-type_init(usb_host_register_types)
-
-USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
-{
-    struct USBAutoFilter filter;
-    USBDevice *dev;
-    char *p;
-
-    dev = usb_create(bus, "usb-host");
-
-    if (strstr(devname, "auto:")) {
-        if (parse_filter(devname, &filter) < 0) {
-            goto fail;
-        }
-    } else {
-        if ((p = strchr(devname, '.'))) {
-            filter.bus_num    = strtoul(devname, NULL, 0);
-            filter.addr       = strtoul(p + 1, NULL, 0);
-            filter.vendor_id  = 0;
-            filter.product_id = 0;
-        } else if ((p = strchr(devname, ':'))) {
-            filter.bus_num    = 0;
-            filter.addr       = 0;
-            filter.vendor_id  = strtoul(devname, NULL, 16);
-            filter.product_id = strtoul(p + 1, NULL, 16);
-        } else {
-            goto fail;
-        }
-    }
-
-    qdev_prop_set_uint32(&dev->qdev, "hostbus",   filter.bus_num);
-    qdev_prop_set_uint32(&dev->qdev, "hostaddr",  filter.addr);
-    qdev_prop_set_uint32(&dev->qdev, "vendorid",  filter.vendor_id);
-    qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
-    qdev_init_nofail(&dev->qdev);
-    return dev;
-
-fail:
-    qdev_free(&dev->qdev);
-    return NULL;
-}
-
-int usb_host_device_close(const char *devname)
-{
-#if 0
-    char product_name[PRODUCT_NAME_SZ];
-    int bus_num, addr;
-    USBHostDevice *s;
-
-    if (strstr(devname, "auto:")) {
-        return usb_host_auto_del(devname);
-    }
-    if (usb_host_find_device(&bus_num, &addr, product_name,
-                                    sizeof(product_name), devname) < 0) {
-        return -1;
-    }
-    s = hostdev_find(bus_num, addr);
-    if (s) {
-        usb_device_delete_addr(s->bus_num, s->dev.addr);
-        return 0;
-    }
-#endif
-
-    return -1;
-}
-
-/*
- * Read sys file-system device file
- *
- * @line address of buffer to put file contents in
- * @line_size size of line
- * @device_file path to device file (printf format string)
- * @device_name device being opened (inserted into device_file)
- *
- * @return 0 failed, 1 succeeded ('line' contains data)
- */
-static int usb_host_read_file(char *line, size_t line_size,
-                              const char *device_file, const char *device_name)
-{
-    FILE *f;
-    int ret = 0;
-    char filename[PATH_MAX];
-
-    snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
-             device_file);
-    f = fopen(filename, "r");
-    if (f) {
-        ret = fgets(line, line_size, f) != NULL;
-        fclose(f);
-    }
-
-    return ret;
-}
-
-/*
- * Use /sys/bus/usb/devices/ directory to determine host's USB
- * devices.
- *
- * This code is based on Robert Schiele's original patches posted to
- * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
- */
-static int usb_host_scan(void *opaque, USBScanFunc *func)
-{
-    DIR *dir = NULL;
-    char line[1024];
-    int bus_num, addr, speed, class_id, product_id, vendor_id;
-    int ret = 0;
-    char port[MAX_PORTLEN];
-    char product_name[512];
-    struct dirent *de;
-
-    dir = opendir("/sys/bus/usb/devices");
-    if (!dir) {
-        perror("husb: opendir /sys/bus/usb/devices");
-        fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
-        goto the_end;
-    }
-
-    while ((de = readdir(dir))) {
-        if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
-            if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
-                continue;
-            }
-
-            if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%d", &addr) != 1) {
-                goto the_end;
-            }
-            if (!usb_host_read_file(line, sizeof(line), "bDeviceClass",
-                                    de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%x", &class_id) != 1) {
-                goto the_end;
-            }
-
-            if (!usb_host_read_file(line, sizeof(line), "idVendor",
-                                    de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%x", &vendor_id) != 1) {
-                goto the_end;
-            }
-            if (!usb_host_read_file(line, sizeof(line), "idProduct",
-                                    de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%x", &product_id) != 1) {
-                goto the_end;
-            }
-            if (!usb_host_read_file(line, sizeof(line), "product",
-                                    de->d_name)) {
-                *product_name = 0;
-            } else {
-                if (strlen(line) > 0) {
-                    line[strlen(line) - 1] = '\0';
-                }
-                pstrcpy(product_name, sizeof(product_name), line);
-            }
-
-            if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
-                goto the_end;
-            }
-            if (!strcmp(line, "5000\n")) {
-                speed = USB_SPEED_SUPER;
-            } else if (!strcmp(line, "480\n")) {
-                speed = USB_SPEED_HIGH;
-            } else if (!strcmp(line, "1.5\n")) {
-                speed = USB_SPEED_LOW;
-            } else {
-                speed = USB_SPEED_FULL;
-            }
-
-            ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
-                       product_id, product_name, speed);
-            if (ret) {
-                goto the_end;
-            }
-        }
-    }
- the_end:
-    if (dir) {
-        closedir(dir);
-    }
-    return ret;
-}
-
-static QEMUTimer *usb_auto_timer;
-
-static int usb_host_auto_scan(void *opaque, int bus_num,
-                              int addr, const char *port,
-                              int class_id, int vendor_id, int product_id,
-                              const char *product_name, int speed)
-{
-    struct USBAutoFilter *f;
-    struct USBHostDevice *s;
-
-    /* Ignore hubs */
-    if (class_id == 9)
-        return 0;
-
-    QTAILQ_FOREACH(s, &hostdevs, next) {
-        f = &s->match;
-
-        if (f->bus_num > 0 && f->bus_num != bus_num) {
-            continue;
-        }
-        if (f->addr > 0 && f->addr != addr) {
-            continue;
-        }
-        if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) {
-            continue;
-        }
-
-        if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
-            continue;
-        }
-
-        if (f->product_id > 0 && f->product_id != product_id) {
-            continue;
-        }
-        /* We got a match */
-        s->seen++;
-        if (s->errcount >= 3) {
-            return 0;
-        }
-
-        /* Already attached ? */
-        if (s->fd != -1) {
-            return 0;
-        }
-        DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
-
-        if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) {
-            s->errcount++;
-        }
-        break;
-    }
-
-    return 0;
-}
-
-static void usb_host_auto_check(void *unused)
-{
-    struct USBHostDevice *s;
-    int unconnected = 0;
-
-    usb_host_scan(NULL, usb_host_auto_scan);
-
-    QTAILQ_FOREACH(s, &hostdevs, next) {
-        if (s->fd == -1) {
-            unconnected++;
-        }
-        if (s->seen == 0) {
-            s->errcount = 0;
-        }
-        s->seen = 0;
-    }
-
-    if (unconnected == 0) {
-        /* nothing to watch */
-        if (usb_auto_timer) {
-            qemu_del_timer(usb_auto_timer);
-            trace_usb_host_auto_scan_disabled();
-        }
-        return;
-    }
-
-    if (!usb_auto_timer) {
-        usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
-        if (!usb_auto_timer) {
-            return;
-        }
-        trace_usb_host_auto_scan_enabled();
-    }
-    qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
-}
-
-/*
- * Autoconnect filter
- * Format:
- *    auto:bus:dev[:vid:pid]
- *    auto:bus.dev[:vid:pid]
- *
- *    bus  - bus number    (dec, * means any)
- *    dev  - device number (dec, * means any)
- *    vid  - vendor id     (hex, * means any)
- *    pid  - product id    (hex, * means any)
- *
- *    See 'lsusb' output.
- */
-static int parse_filter(const char *spec, struct USBAutoFilter *f)
-{
-    enum { BUS, DEV, VID, PID, DONE };
-    const char *p = spec;
-    int i;
-
-    f->bus_num    = 0;
-    f->addr       = 0;
-    f->vendor_id  = 0;
-    f->product_id = 0;
-
-    for (i = BUS; i < DONE; i++) {
-        p = strpbrk(p, ":.");
-        if (!p) {
-            break;
-        }
-        p++;
-
-        if (*p == '*') {
-            continue;
-        }
-        switch(i) {
-        case BUS: f->bus_num = strtol(p, NULL, 10);    break;
-        case DEV: f->addr    = strtol(p, NULL, 10);    break;
-        case VID: f->vendor_id  = strtol(p, NULL, 16); break;
-        case PID: f->product_id = strtol(p, NULL, 16); break;
-        }
-    }
-
-    if (i < DEV) {
-        fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
-        return -1;
-    }
-
-    return 0;
-}
-
-/**********************/
-/* 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;
-}
-
-static void usb_info_device(Monitor *mon, int bus_num,
-                            int addr, const char *port,
-                            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;
-    case USB_SPEED_SUPER:
-        speed_str = "5000";
-        break;
-    default:
-        speed_str = "?";
-        break;
-    }
-
-    monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
-                   bus_num, addr, port, speed_str);
-    class_str = usb_class_str(class_id);
-    if (class_str) {
-        monitor_printf(mon, "    %s:", class_str);
-    } else {
-        monitor_printf(mon, "    Class %02x:", class_id);
-    }
-    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
-    if (product_name[0] != '\0') {
-        monitor_printf(mon, ", %s", product_name);
-    }
-    monitor_printf(mon, "\n");
-}
-
-static int usb_host_info_device(void *opaque, int bus_num, int addr,
-                                const char *path, int class_id,
-                                int vendor_id, int product_id,
-                                const char *product_name,
-                                int speed)
-{
-    Monitor *mon = opaque;
-
-    usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
-                    product_name, speed);
-    return 0;
-}
-
-static void dec2str(int val, char *str, size_t size)
-{
-    if (val == 0) {
-        snprintf(str, size, "*");
-    } else {
-        snprintf(str, size, "%d", val);
-    }
-}
-
-static void hex2str(int val, char *str, size_t size)
-{
-    if (val == 0) {
-        snprintf(str, size, "*");
-    } else {
-        snprintf(str, size, "%04x", val);
-    }
-}
-
-void usb_host_info(Monitor *mon)
-{
-    struct USBAutoFilter *f;
-    struct USBHostDevice *s;
-
-    usb_host_scan(mon, usb_host_info_device);
-
-    if (QTAILQ_EMPTY(&hostdevs)) {
-        return;
-    }
-
-    monitor_printf(mon, "  Auto filters:\n");
-    QTAILQ_FOREACH(s, &hostdevs, next) {
-        char bus[10], addr[10], vid[10], pid[10];
-        f = &s->match;
-        dec2str(f->bus_num, bus, sizeof(bus));
-        dec2str(f->addr, addr, sizeof(addr));
-        hex2str(f->vendor_id, vid, sizeof(vid));
-        hex2str(f->product_id, pid, sizeof(pid));
-        monitor_printf(mon, "    Bus %s, Addr %s, Port %s, ID %s:%s\n",
-                       bus, addr, f->port ? f->port : "*", vid, pid);
-    }
-}
diff --git a/usb-redir.c b/usb-redir.c
deleted file mode 100644 (file)
index 8e9f175..0000000
+++ /dev/null
@@ -1,1485 +0,0 @@
-/*
- * USB redirector usb-guest
- *
- * Copyright (c) 2011 Red Hat, Inc.
- *
- * Red Hat Authors:
- * Hans de Goede <hdegoede@redhat.com>
- *
- * 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 "qemu-timer.h"
-#include "monitor.h"
-#include "sysemu.h"
-
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <signal.h>
-#include <usbredirparser.h>
-#include <usbredirfilter.h>
-
-#include "hw/usb.h"
-
-#define MAX_ENDPOINTS 32
-#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
-#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
-
-typedef struct AsyncURB AsyncURB;
-typedef struct USBRedirDevice USBRedirDevice;
-
-/* Struct to hold buffered packets (iso or int input packets) */
-struct buf_packet {
-    uint8_t *data;
-    int len;
-    int status;
-    QTAILQ_ENTRY(buf_packet)next;
-};
-
-struct endp_data {
-    uint8_t type;
-    uint8_t interval;
-    uint8_t interface; /* bInterfaceNumber this ep belongs to */
-    uint8_t iso_started;
-    uint8_t iso_error; /* For reporting iso errors to the HC */
-    uint8_t interrupt_started;
-    uint8_t interrupt_error;
-    uint8_t bufpq_prefilled;
-    uint8_t bufpq_dropping_packets;
-    QTAILQ_HEAD(, buf_packet) bufpq;
-    int bufpq_size;
-    int bufpq_target_size;
-};
-
-struct USBRedirDevice {
-    USBDevice dev;
-    /* Properties */
-    CharDriverState *cs;
-    uint8_t debug;
-    char *filter_str;
-    /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
-    const uint8_t *read_buf;
-    int read_buf_size;
-    /* For async handling of open/close */
-    QEMUBH *open_close_bh;
-    /* To delay the usb attach in case of quick chardev close + open */
-    QEMUTimer *attach_timer;
-    int64_t next_attach_time;
-    struct usbredirparser *parser;
-    struct endp_data endpoint[MAX_ENDPOINTS];
-    uint32_t packet_id;
-    QTAILQ_HEAD(, AsyncURB) asyncq;
-    /* Data for device filtering */
-    struct usb_redir_device_connect_header device_info;
-    struct usb_redir_interface_info_header interface_info;
-    struct usbredirfilter_rule *filter_rules;
-    int filter_rules_count;
-};
-
-struct AsyncURB {
-    USBRedirDevice *dev;
-    USBPacket *packet;
-    uint32_t packet_id;
-    int get;
-    union {
-        struct usb_redir_control_packet_header control_packet;
-        struct usb_redir_bulk_packet_header bulk_packet;
-        struct usb_redir_interrupt_packet_header interrupt_packet;
-    };
-    QTAILQ_ENTRY(AsyncURB)next;
-};
-
-static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
-static void usbredir_device_connect(void *priv,
-    struct usb_redir_device_connect_header *device_connect);
-static void usbredir_device_disconnect(void *priv);
-static void usbredir_interface_info(void *priv,
-    struct usb_redir_interface_info_header *interface_info);
-static void usbredir_ep_info(void *priv,
-    struct usb_redir_ep_info_header *ep_info);
-static void usbredir_configuration_status(void *priv, uint32_t id,
-    struct usb_redir_configuration_status_header *configuration_status);
-static void usbredir_alt_setting_status(void *priv, uint32_t id,
-    struct usb_redir_alt_setting_status_header *alt_setting_status);
-static void usbredir_iso_stream_status(void *priv, uint32_t id,
-    struct usb_redir_iso_stream_status_header *iso_stream_status);
-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
-    struct usb_redir_interrupt_receiving_status_header
-    *interrupt_receiving_status);
-static void usbredir_bulk_streams_status(void *priv, uint32_t id,
-    struct usb_redir_bulk_streams_status_header *bulk_streams_status);
-static void usbredir_control_packet(void *priv, uint32_t id,
-    struct usb_redir_control_packet_header *control_packet,
-    uint8_t *data, int data_len);
-static void usbredir_bulk_packet(void *priv, uint32_t id,
-    struct usb_redir_bulk_packet_header *bulk_packet,
-    uint8_t *data, int data_len);
-static void usbredir_iso_packet(void *priv, uint32_t id,
-    struct usb_redir_iso_packet_header *iso_packet,
-    uint8_t *data, int data_len);
-static void usbredir_interrupt_packet(void *priv, uint32_t id,
-    struct usb_redir_interrupt_packet_header *interrupt_header,
-    uint8_t *data, int data_len);
-
-static int usbredir_handle_status(USBRedirDevice *dev,
-                                       int status, int actual_len);
-
-#define VERSION "qemu usb-redir guest " QEMU_VERSION
-
-/*
- * Logging stuff
- */
-
-#define ERROR(...) \
-    do { \
-        if (dev->debug >= usbredirparser_error) { \
-            error_report("usb-redir error: " __VA_ARGS__); \
-        } \
-    } while (0)
-#define WARNING(...) \
-    do { \
-        if (dev->debug >= usbredirparser_warning) { \
-            error_report("usb-redir warning: " __VA_ARGS__); \
-        } \
-    } while (0)
-#define INFO(...) \
-    do { \
-        if (dev->debug >= usbredirparser_info) { \
-            error_report("usb-redir: " __VA_ARGS__); \
-        } \
-    } while (0)
-#define DPRINTF(...) \
-    do { \
-        if (dev->debug >= usbredirparser_debug) { \
-            error_report("usb-redir: " __VA_ARGS__); \
-        } \
-    } while (0)
-#define DPRINTF2(...) \
-    do { \
-        if (dev->debug >= usbredirparser_debug_data) { \
-            error_report("usb-redir: " __VA_ARGS__); \
-        } \
-    } while (0)
-
-static void usbredir_log(void *priv, int level, const char *msg)
-{
-    USBRedirDevice *dev = priv;
-
-    if (dev->debug < level) {
-        return;
-    }
-
-    error_report("%s", msg);
-}
-
-static void usbredir_log_data(USBRedirDevice *dev, const char *desc,
-    const uint8_t *data, int len)
-{
-    int i, j, n;
-
-    if (dev->debug < usbredirparser_debug_data) {
-        return;
-    }
-
-    for (i = 0; i < len; i += j) {
-        char buf[128];
-
-        n = sprintf(buf, "%s", desc);
-        for (j = 0; j < 8 && i + j < len; j++) {
-            n += sprintf(buf + n, " %02X", data[i + j]);
-        }
-        error_report("%s", buf);
-    }
-}
-
-/*
- * usbredirparser io functions
- */
-
-static int usbredir_read(void *priv, uint8_t *data, int count)
-{
-    USBRedirDevice *dev = priv;
-
-    if (dev->read_buf_size < count) {
-        count = dev->read_buf_size;
-    }
-
-    memcpy(data, dev->read_buf, count);
-
-    dev->read_buf_size -= count;
-    if (dev->read_buf_size) {
-        dev->read_buf += count;
-    } else {
-        dev->read_buf = NULL;
-    }
-
-    return count;
-}
-
-static int usbredir_write(void *priv, uint8_t *data, int count)
-{
-    USBRedirDevice *dev = priv;
-
-    if (!dev->cs->opened) {
-        return 0;
-    }
-
-    return qemu_chr_fe_write(dev->cs, data, count);
-}
-
-/*
- * Async and buffered packets helpers
- */
-
-static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
-{
-    AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB));
-    aurb->dev = dev;
-    aurb->packet = p;
-    aurb->packet_id = dev->packet_id;
-    QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
-    dev->packet_id++;
-
-    return aurb;
-}
-
-static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
-{
-    QTAILQ_REMOVE(&dev->asyncq, aurb, next);
-    g_free(aurb);
-}
-
-static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
-{
-    AsyncURB *aurb;
-
-    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
-        if (aurb->packet_id == packet_id) {
-            return aurb;
-        }
-    }
-    ERROR("could not find async urb for packet_id %u\n", packet_id);
-    return NULL;
-}
-
-static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
-{
-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
-    AsyncURB *aurb;
-
-    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
-        if (p != aurb->packet) {
-            continue;
-        }
-
-        DPRINTF("async cancel id %u\n", aurb->packet_id);
-        usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
-        usbredirparser_do_write(dev->parser);
-
-        /* Mark it as dead */
-        aurb->packet = NULL;
-        break;
-    }
-}
-
-static void bufp_alloc(USBRedirDevice *dev,
-    uint8_t *data, int len, int status, uint8_t ep)
-{
-    struct buf_packet *bufp;
-
-    if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets &&
-        dev->endpoint[EP2I(ep)].bufpq_size >
-            2 * dev->endpoint[EP2I(ep)].bufpq_target_size) {
-        DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep);
-        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1;
-    }
-    /* Since we're interupting the stream anyways, drop enough packets to get
-       back to our target buffer size */
-    if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) {
-        if (dev->endpoint[EP2I(ep)].bufpq_size >
-                dev->endpoint[EP2I(ep)].bufpq_target_size) {
-            free(data);
-            return;
-        }
-        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
-    }
-
-    bufp = g_malloc(sizeof(struct buf_packet));
-    bufp->data   = data;
-    bufp->len    = len;
-    bufp->status = status;
-    QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
-    dev->endpoint[EP2I(ep)].bufpq_size++;
-}
-
-static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
-    uint8_t ep)
-{
-    QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
-    dev->endpoint[EP2I(ep)].bufpq_size--;
-    free(bufp->data);
-    g_free(bufp);
-}
-
-static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep)
-{
-    struct buf_packet *buf, *buf_next;
-
-    QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) {
-        bufp_free(dev, buf, ep);
-    }
-}
-
-/*
- * USBDevice callbacks
- */
-
-static void usbredir_handle_reset(USBDevice *udev)
-{
-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
-
-    DPRINTF("reset device\n");
-    usbredirparser_send_reset(dev->parser);
-    usbredirparser_do_write(dev->parser);
-}
-
-static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
-                                     uint8_t ep)
-{
-    int status, len;
-    if (!dev->endpoint[EP2I(ep)].iso_started &&
-            !dev->endpoint[EP2I(ep)].iso_error) {
-        struct usb_redir_start_iso_stream_header start_iso = {
-            .endpoint = ep,
-        };
-        int pkts_per_sec;
-
-        if (dev->dev.speed == USB_SPEED_HIGH) {
-            pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval;
-        } else {
-            pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval;
-        }
-        /* Testing has shown that we need circa 60 ms buffer */
-        dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000;
-
-        /* Aim for approx 100 interrupts / second on the client to
-           balance latency and interrupt load */
-        start_iso.pkts_per_urb = pkts_per_sec / 100;
-        if (start_iso.pkts_per_urb < 1) {
-            start_iso.pkts_per_urb = 1;
-        } else if (start_iso.pkts_per_urb > 32) {
-            start_iso.pkts_per_urb = 32;
-        }
-
-        start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size +
-                             start_iso.pkts_per_urb - 1) /
-                            start_iso.pkts_per_urb;
-        /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest
-           as overflow buffer. Also see the usbredir protocol documentation */
-        if (!(ep & USB_DIR_IN)) {
-            start_iso.no_urbs *= 2;
-        }
-        if (start_iso.no_urbs > 16) {
-            start_iso.no_urbs = 16;
-        }
-
-        /* No id, we look at the ep when receiving a status back */
-        usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
-        usbredirparser_do_write(dev->parser);
-        DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n",
-                pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep);
-        dev->endpoint[EP2I(ep)].iso_started = 1;
-        dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
-        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
-    }
-
-    if (ep & USB_DIR_IN) {
-        struct buf_packet *isop;
-
-        if (dev->endpoint[EP2I(ep)].iso_started &&
-                !dev->endpoint[EP2I(ep)].bufpq_prefilled) {
-            if (dev->endpoint[EP2I(ep)].bufpq_size <
-                    dev->endpoint[EP2I(ep)].bufpq_target_size) {
-                return usbredir_handle_status(dev, 0, 0);
-            }
-            dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
-        }
-
-        isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
-        if (isop == NULL) {
-            DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n",
-                    ep, dev->endpoint[EP2I(ep)].iso_error);
-            /* Re-fill the buffer */
-            dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
-            /* Check iso_error for stream errors, otherwise its an underrun */
-            status = dev->endpoint[EP2I(ep)].iso_error;
-            dev->endpoint[EP2I(ep)].iso_error = 0;
-            return status ? USB_RET_IOERROR : 0;
-        }
-        DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
-                 isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
-
-        status = isop->status;
-        if (status != usb_redir_success) {
-            bufp_free(dev, isop, ep);
-            return USB_RET_IOERROR;
-        }
-
-        len = isop->len;
-        if (len > p->iov.size) {
-            ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
-                  ep, len, (int)p->iov.size);
-            bufp_free(dev, isop, ep);
-            return USB_RET_BABBLE;
-        }
-        usb_packet_copy(p, isop->data, len);
-        bufp_free(dev, isop, ep);
-        return len;
-    } else {
-        /* If the stream was not started because of a pending error don't
-           send the packet to the usb-host */
-        if (dev->endpoint[EP2I(ep)].iso_started) {
-            struct usb_redir_iso_packet_header iso_packet = {
-                .endpoint = ep,
-                .length = p->iov.size
-            };
-            uint8_t buf[p->iov.size];
-            /* No id, we look at the ep when receiving a status back */
-            usb_packet_copy(p, buf, p->iov.size);
-            usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
-                                           buf, p->iov.size);
-            usbredirparser_do_write(dev->parser);
-        }
-        status = dev->endpoint[EP2I(ep)].iso_error;
-        dev->endpoint[EP2I(ep)].iso_error = 0;
-        DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
-                 p->iov.size);
-        return usbredir_handle_status(dev, status, p->iov.size);
-    }
-}
-
-static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
-{
-    struct usb_redir_stop_iso_stream_header stop_iso_stream = {
-        .endpoint = ep
-    };
-    if (dev->endpoint[EP2I(ep)].iso_started) {
-        usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream);
-        DPRINTF("iso stream stopped ep %02X\n", ep);
-        dev->endpoint[EP2I(ep)].iso_started = 0;
-    }
-    dev->endpoint[EP2I(ep)].iso_error = 0;
-    usbredir_free_bufpq(dev, ep);
-}
-
-static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
-                                      uint8_t ep)
-{
-    AsyncURB *aurb = async_alloc(dev, p);
-    struct usb_redir_bulk_packet_header bulk_packet;
-
-    DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
-            p->iov.size, aurb->packet_id);
-
-    bulk_packet.endpoint  = ep;
-    bulk_packet.length    = p->iov.size;
-    bulk_packet.stream_id = 0;
-    aurb->bulk_packet = bulk_packet;
-
-    if (ep & USB_DIR_IN) {
-        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
-                                        &bulk_packet, NULL, 0);
-    } else {
-        uint8_t buf[p->iov.size];
-        usb_packet_copy(p, buf, p->iov.size);
-        usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
-        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
-                                        &bulk_packet, buf, p->iov.size);
-    }
-    usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
-}
-
-static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
-                                           USBPacket *p, uint8_t ep)
-{
-    if (ep & USB_DIR_IN) {
-        /* Input interrupt endpoint, buffered packet input */
-        struct buf_packet *intp;
-        int status, len;
-
-        if (!dev->endpoint[EP2I(ep)].interrupt_started &&
-                !dev->endpoint[EP2I(ep)].interrupt_error) {
-            struct usb_redir_start_interrupt_receiving_header start_int = {
-                .endpoint = ep,
-            };
-            /* No id, we look at the ep when receiving a status back */
-            usbredirparser_send_start_interrupt_receiving(dev->parser, 0,
-                                                          &start_int);
-            usbredirparser_do_write(dev->parser);
-            DPRINTF("interrupt recv started ep %02X\n", ep);
-            dev->endpoint[EP2I(ep)].interrupt_started = 1;
-            /* We don't really want to drop interrupt packets ever, but
-               having some upper limit to how much we buffer is good. */
-            dev->endpoint[EP2I(ep)].bufpq_target_size = 1000;
-            dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
-        }
-
-        intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
-        if (intp == NULL) {
-            DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
-            /* Check interrupt_error for stream errors */
-            status = dev->endpoint[EP2I(ep)].interrupt_error;
-            dev->endpoint[EP2I(ep)].interrupt_error = 0;
-            if (status) {
-                return usbredir_handle_status(dev, status, 0);
-            }
-            return USB_RET_NAK;
-        }
-        DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
-                intp->status, intp->len);
-
-        status = intp->status;
-        if (status != usb_redir_success) {
-            bufp_free(dev, intp, ep);
-            return usbredir_handle_status(dev, status, 0);
-        }
-
-        len = intp->len;
-        if (len > p->iov.size) {
-            ERROR("received int data is larger then packet ep %02X\n", ep);
-            bufp_free(dev, intp, ep);
-            return USB_RET_BABBLE;
-        }
-        usb_packet_copy(p, intp->data, len);
-        bufp_free(dev, intp, ep);
-        return len;
-    } else {
-        /* Output interrupt endpoint, normal async operation */
-        AsyncURB *aurb = async_alloc(dev, p);
-        struct usb_redir_interrupt_packet_header interrupt_packet;
-        uint8_t buf[p->iov.size];
-
-        DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
-                aurb->packet_id);
-
-        interrupt_packet.endpoint  = ep;
-        interrupt_packet.length    = p->iov.size;
-        aurb->interrupt_packet     = interrupt_packet;
-
-        usb_packet_copy(p, buf, p->iov.size);
-        usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
-        usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
-                                        &interrupt_packet, buf, p->iov.size);
-        usbredirparser_do_write(dev->parser);
-        return USB_RET_ASYNC;
-    }
-}
-
-static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
-    uint8_t ep)
-{
-    struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = {
-        .endpoint = ep
-    };
-    if (dev->endpoint[EP2I(ep)].interrupt_started) {
-        usbredirparser_send_stop_interrupt_receiving(dev->parser, 0,
-                                                     &stop_interrupt_recv);
-        DPRINTF("interrupt recv stopped ep %02X\n", ep);
-        dev->endpoint[EP2I(ep)].interrupt_started = 0;
-    }
-    dev->endpoint[EP2I(ep)].interrupt_error = 0;
-    usbredir_free_bufpq(dev, ep);
-}
-
-static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
-{
-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
-    uint8_t ep;
-
-    ep = p->ep->nr;
-    if (p->pid == USB_TOKEN_IN) {
-        ep |= USB_DIR_IN;
-    }
-
-    switch (dev->endpoint[EP2I(ep)].type) {
-    case USB_ENDPOINT_XFER_CONTROL:
-        ERROR("handle_data called for control transfer on ep %02X\n", ep);
-        return USB_RET_NAK;
-    case USB_ENDPOINT_XFER_ISOC:
-        return usbredir_handle_iso_data(dev, p, ep);
-    case USB_ENDPOINT_XFER_BULK:
-        return usbredir_handle_bulk_data(dev, p, ep);
-    case USB_ENDPOINT_XFER_INT:
-        return usbredir_handle_interrupt_data(dev, p, ep);
-    default:
-        ERROR("handle_data ep %02X has unknown type %d\n", ep,
-              dev->endpoint[EP2I(ep)].type);
-        return USB_RET_NAK;
-    }
-}
-
-static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
-                                int config)
-{
-    struct usb_redir_set_configuration_header set_config;
-    AsyncURB *aurb = async_alloc(dev, p);
-    int i;
-
-    DPRINTF("set config %d id %u\n", config, aurb->packet_id);
-
-    for (i = 0; i < MAX_ENDPOINTS; i++) {
-        switch (dev->endpoint[i].type) {
-        case USB_ENDPOINT_XFER_ISOC:
-            usbredir_stop_iso_stream(dev, I2EP(i));
-            break;
-        case USB_ENDPOINT_XFER_INT:
-            if (i & 0x10) {
-                usbredir_stop_interrupt_receiving(dev, I2EP(i));
-            }
-            break;
-        }
-        usbredir_free_bufpq(dev, I2EP(i));
-    }
-
-    set_config.configuration = config;
-    usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
-                                          &set_config);
-    usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
-}
-
-static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
-{
-    AsyncURB *aurb = async_alloc(dev, p);
-
-    DPRINTF("get config id %u\n", aurb->packet_id);
-
-    aurb->get = 1;
-    usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
-    usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
-}
-
-static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
-                                   int interface, int alt)
-{
-    struct usb_redir_set_alt_setting_header set_alt;
-    AsyncURB *aurb = async_alloc(dev, p);
-    int i;
-
-    DPRINTF("set interface %d alt %d id %u\n", interface, alt,
-            aurb->packet_id);
-
-    for (i = 0; i < MAX_ENDPOINTS; i++) {
-        if (dev->endpoint[i].interface == interface) {
-            switch (dev->endpoint[i].type) {
-            case USB_ENDPOINT_XFER_ISOC:
-                usbredir_stop_iso_stream(dev, I2EP(i));
-                break;
-            case USB_ENDPOINT_XFER_INT:
-                if (i & 0x10) {
-                    usbredir_stop_interrupt_receiving(dev, I2EP(i));
-                }
-                break;
-            }
-            usbredir_free_bufpq(dev, I2EP(i));
-        }
-    }
-
-    set_alt.interface = interface;
-    set_alt.alt = alt;
-    usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
-                                        &set_alt);
-    usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
-}
-
-static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
-                                   int interface)
-{
-    struct usb_redir_get_alt_setting_header get_alt;
-    AsyncURB *aurb = async_alloc(dev, p);
-
-    DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
-
-    get_alt.interface = interface;
-    aurb->get = 1;
-    usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
-                                        &get_alt);
-    usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
-}
-
-static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
-        int request, int value, int index, int length, uint8_t *data)
-{
-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
-    struct usb_redir_control_packet_header control_packet;
-    AsyncURB *aurb;
-
-    /* Special cases for certain standard device requests */
-    switch (request) {
-    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-        DPRINTF("set address %d\n", value);
-        dev->dev.addr = value;
-        return 0;
-    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        return usbredir_set_config(dev, p, value & 0xff);
-    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-        return usbredir_get_config(dev, p);
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        return usbredir_set_interface(dev, p, index, value);
-    case InterfaceRequest | USB_REQ_GET_INTERFACE:
-        return usbredir_get_interface(dev, p, index);
-    }
-
-    /* "Normal" ctrl requests */
-    aurb = async_alloc(dev, p);
-
-    /* Note request is (bRequestType << 8) | bRequest */
-    DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
-            request >> 8, request & 0xff, value, index, length,
-            aurb->packet_id);
-
-    control_packet.request     = request & 0xFF;
-    control_packet.requesttype = request >> 8;
-    control_packet.endpoint    = control_packet.requesttype & USB_DIR_IN;
-    control_packet.value       = value;
-    control_packet.index       = index;
-    control_packet.length      = length;
-    aurb->control_packet       = control_packet;
-
-    if (control_packet.requesttype & USB_DIR_IN) {
-        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
-                                           &control_packet, NULL, 0);
-    } else {
-        usbredir_log_data(dev, "ctrl data out:", data, length);
-        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
-                                           &control_packet, data, length);
-    }
-    usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
-}
-
-/*
- * Close events can be triggered by usbredirparser_do_write which gets called
- * from within the USBDevice data / control packet callbacks and doing a
- * usb_detach from within these callbacks is not a good idea.
- *
- * So we use a bh handler to take care of close events. We also handle
- * open events from this callback to make sure that a close directly followed
- * by an open gets handled in the right order.
- */
-static void usbredir_open_close_bh(void *opaque)
-{
-    USBRedirDevice *dev = opaque;
-    uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
-
-    usbredir_device_disconnect(dev);
-
-    if (dev->parser) {
-        usbredirparser_destroy(dev->parser);
-        dev->parser = NULL;
-    }
-
-    if (dev->cs->opened) {
-        dev->parser = qemu_oom_check(usbredirparser_create());
-        dev->parser->priv = dev;
-        dev->parser->log_func = usbredir_log;
-        dev->parser->read_func = usbredir_read;
-        dev->parser->write_func = usbredir_write;
-        dev->parser->hello_func = usbredir_hello;
-        dev->parser->device_connect_func = usbredir_device_connect;
-        dev->parser->device_disconnect_func = usbredir_device_disconnect;
-        dev->parser->interface_info_func = usbredir_interface_info;
-        dev->parser->ep_info_func = usbredir_ep_info;
-        dev->parser->configuration_status_func = usbredir_configuration_status;
-        dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
-        dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
-        dev->parser->interrupt_receiving_status_func =
-            usbredir_interrupt_receiving_status;
-        dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
-        dev->parser->control_packet_func = usbredir_control_packet;
-        dev->parser->bulk_packet_func = usbredir_bulk_packet;
-        dev->parser->iso_packet_func = usbredir_iso_packet;
-        dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
-        dev->read_buf = NULL;
-        dev->read_buf_size = 0;
-
-        usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
-        usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
-        usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
-        usbredirparser_do_write(dev->parser);
-    }
-}
-
-static void usbredir_do_attach(void *opaque)
-{
-    USBRedirDevice *dev = opaque;
-
-    usb_device_attach(&dev->dev);
-}
-
-/*
- * chardev callbacks
- */
-
-static int usbredir_chardev_can_read(void *opaque)
-{
-    USBRedirDevice *dev = opaque;
-
-    if (dev->parser) {
-        /* usbredir_parser_do_read will consume *all* data we give it */
-        return 1024 * 1024;
-    } else {
-        /* usbredir_open_close_bh hasn't handled the open event yet */
-        return 0;
-    }
-}
-
-static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
-{
-    USBRedirDevice *dev = opaque;
-
-    /* No recursion allowed! */
-    assert(dev->read_buf == NULL);
-
-    dev->read_buf = buf;
-    dev->read_buf_size = size;
-
-    usbredirparser_do_read(dev->parser);
-    /* Send any acks, etc. which may be queued now */
-    usbredirparser_do_write(dev->parser);
-}
-
-static void usbredir_chardev_event(void *opaque, int event)
-{
-    USBRedirDevice *dev = opaque;
-
-    switch (event) {
-    case CHR_EVENT_OPENED:
-    case CHR_EVENT_CLOSED:
-        qemu_bh_schedule(dev->open_close_bh);
-        break;
-    }
-}
-
-/*
- * init + destroy
- */
-
-static int usbredir_initfn(USBDevice *udev)
-{
-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
-    int i;
-
-    if (dev->cs == NULL) {
-        qerror_report(QERR_MISSING_PARAMETER, "chardev");
-        return -1;
-    }
-
-    if (dev->filter_str) {
-        i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|",
-                                           &dev->filter_rules,
-                                           &dev->filter_rules_count);
-        if (i) {
-            qerror_report(QERR_INVALID_PARAMETER_VALUE, "filter",
-                          "a usb device filter string");
-            return -1;
-        }
-    }
-
-    dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
-    dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
-
-    QTAILQ_INIT(&dev->asyncq);
-    for (i = 0; i < MAX_ENDPOINTS; i++) {
-        QTAILQ_INIT(&dev->endpoint[i].bufpq);
-    }
-
-    /* We'll do the attach once we receive the speed from the usb-host */
-    udev->auto_attach = 0;
-
-    /* Let the backend know we are ready */
-    qemu_chr_fe_open(dev->cs);
-    qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
-                          usbredir_chardev_read, usbredir_chardev_event, dev);
-
-    return 0;
-}
-
-static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
-{
-    AsyncURB *aurb, *next_aurb;
-    int i;
-
-    QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
-        async_free(dev, aurb);
-    }
-    for (i = 0; i < MAX_ENDPOINTS; i++) {
-        usbredir_free_bufpq(dev, I2EP(i));
-    }
-}
-
-static void usbredir_handle_destroy(USBDevice *udev)
-{
-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
-
-    qemu_chr_fe_close(dev->cs);
-    qemu_chr_delete(dev->cs);
-    /* Note must be done after qemu_chr_close, as that causes a close event */
-    qemu_bh_delete(dev->open_close_bh);
-
-    qemu_del_timer(dev->attach_timer);
-    qemu_free_timer(dev->attach_timer);
-
-    usbredir_cleanup_device_queues(dev);
-
-    if (dev->parser) {
-        usbredirparser_destroy(dev->parser);
-    }
-
-    free(dev->filter_rules);
-}
-
-static int usbredir_check_filter(USBRedirDevice *dev)
-{
-    if (dev->interface_info.interface_count == 0) {
-        ERROR("No interface info for device\n");
-        goto error;
-    }
-
-    if (dev->filter_rules) {
-        if (!usbredirparser_peer_has_cap(dev->parser,
-                                    usb_redir_cap_connect_device_version)) {
-            ERROR("Device filter specified and peer does not have the "
-                  "connect_device_version capability\n");
-            goto error;
-        }
-
-        if (usbredirfilter_check(
-                dev->filter_rules,
-                dev->filter_rules_count,
-                dev->device_info.device_class,
-                dev->device_info.device_subclass,
-                dev->device_info.device_protocol,
-                dev->interface_info.interface_class,
-                dev->interface_info.interface_subclass,
-                dev->interface_info.interface_protocol,
-                dev->interface_info.interface_count,
-                dev->device_info.vendor_id,
-                dev->device_info.product_id,
-                dev->device_info.device_version_bcd,
-                0) != 0) {
-            goto error;
-        }
-    }
-
-    return 0;
-
-error:
-    usbredir_device_disconnect(dev);
-    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
-        usbredirparser_send_filter_reject(dev->parser);
-        usbredirparser_do_write(dev->parser);
-    }
-    return -1;
-}
-
-/*
- * usbredirparser packet complete callbacks
- */
-
-static int usbredir_handle_status(USBRedirDevice *dev,
-                                       int status, int actual_len)
-{
-    switch (status) {
-    case usb_redir_success:
-        return actual_len;
-    case usb_redir_stall:
-        return USB_RET_STALL;
-    case usb_redir_cancelled:
-        WARNING("returning cancelled packet to HC?\n");
-        return USB_RET_NAK;
-    case usb_redir_inval:
-        WARNING("got invalid param error from usb-host?\n");
-        return USB_RET_NAK;
-    case usb_redir_ioerror:
-    case usb_redir_timeout:
-    default:
-        return USB_RET_IOERROR;
-    }
-}
-
-static void usbredir_hello(void *priv, struct usb_redir_hello_header *h)
-{
-    USBRedirDevice *dev = priv;
-
-    /* Try to send the filter info now that we've the usb-host's caps */
-    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) &&
-            dev->filter_rules) {
-        usbredirparser_send_filter_filter(dev->parser, dev->filter_rules,
-                                          dev->filter_rules_count);
-        usbredirparser_do_write(dev->parser);
-    }
-}
-
-static void usbredir_device_connect(void *priv,
-    struct usb_redir_device_connect_header *device_connect)
-{
-    USBRedirDevice *dev = priv;
-    const char *speed;
-
-    if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
-        ERROR("Received device connect while already connected\n");
-        return;
-    }
-
-    switch (device_connect->speed) {
-    case usb_redir_speed_low:
-        speed = "low speed";
-        dev->dev.speed = USB_SPEED_LOW;
-        break;
-    case usb_redir_speed_full:
-        speed = "full speed";
-        dev->dev.speed = USB_SPEED_FULL;
-        break;
-    case usb_redir_speed_high:
-        speed = "high speed";
-        dev->dev.speed = USB_SPEED_HIGH;
-        break;
-    case usb_redir_speed_super:
-        speed = "super speed";
-        dev->dev.speed = USB_SPEED_SUPER;
-        break;
-    default:
-        speed = "unknown speed";
-        dev->dev.speed = USB_SPEED_FULL;
-    }
-
-    if (usbredirparser_peer_has_cap(dev->parser,
-                                    usb_redir_cap_connect_device_version)) {
-        INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
-             speed, device_connect->vendor_id, device_connect->product_id,
-             ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 +
-             ((device_connect->device_version_bcd & 0x0f00) >>  8),
-             ((device_connect->device_version_bcd & 0x00f0) >>  4) * 10 +
-             ((device_connect->device_version_bcd & 0x000f) >>  0),
-             device_connect->device_class);
-    } else {
-        INFO("attaching %s device %04x:%04x class %02x\n", speed,
-             device_connect->vendor_id, device_connect->product_id,
-             device_connect->device_class);
-    }
-
-    dev->dev.speedmask = (1 << dev->dev.speed);
-    dev->device_info = *device_connect;
-
-    if (usbredir_check_filter(dev)) {
-        WARNING("Device %04x:%04x rejected by device filter, not attaching\n",
-                device_connect->vendor_id, device_connect->product_id);
-        return;
-    }
-
-    qemu_mod_timer(dev->attach_timer, dev->next_attach_time);
-}
-
-static void usbredir_device_disconnect(void *priv)
-{
-    USBRedirDevice *dev = priv;
-    int i;
-
-    /* Stop any pending attaches */
-    qemu_del_timer(dev->attach_timer);
-
-    if (dev->dev.attached) {
-        usb_device_detach(&dev->dev);
-        /*
-         * Delay next usb device attach to give the guest a chance to see
-         * see the detach / attach in case of quick close / open succession
-         */
-        dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200;
-    }
-
-    /* Reset state so that the next dev connected starts with a clean slate */
-    usbredir_cleanup_device_queues(dev);
-    memset(dev->endpoint, 0, sizeof(dev->endpoint));
-    for (i = 0; i < MAX_ENDPOINTS; i++) {
-        QTAILQ_INIT(&dev->endpoint[i].bufpq);
-    }
-    usb_ep_init(&dev->dev);
-    dev->interface_info.interface_count = 0;
-}
-
-static void usbredir_interface_info(void *priv,
-    struct usb_redir_interface_info_header *interface_info)
-{
-    USBRedirDevice *dev = priv;
-
-    dev->interface_info = *interface_info;
-
-    /*
-     * If we receive interface info after the device has already been
-     * connected (ie on a set_config), re-check the filter.
-     */
-    if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
-        if (usbredir_check_filter(dev)) {
-            ERROR("Device no longer matches filter after interface info "
-                  "change, disconnecting!\n");
-        }
-    }
-}
-
-static void usbredir_ep_info(void *priv,
-    struct usb_redir_ep_info_header *ep_info)
-{
-    USBRedirDevice *dev = priv;
-    struct USBEndpoint *usb_ep;
-    int i;
-
-    for (i = 0; i < MAX_ENDPOINTS; i++) {
-        dev->endpoint[i].type = ep_info->type[i];
-        dev->endpoint[i].interval = ep_info->interval[i];
-        dev->endpoint[i].interface = ep_info->interface[i];
-        switch (dev->endpoint[i].type) {
-        case usb_redir_type_invalid:
-            break;
-        case usb_redir_type_iso:
-        case usb_redir_type_interrupt:
-            if (dev->endpoint[i].interval == 0) {
-                ERROR("Received 0 interval for isoc or irq endpoint\n");
-                usbredir_device_disconnect(dev);
-            }
-            /* Fall through */
-        case usb_redir_type_control:
-        case usb_redir_type_bulk:
-            DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
-                    dev->endpoint[i].type, dev->endpoint[i].interface);
-            break;
-        default:
-            ERROR("Received invalid endpoint type\n");
-            usbredir_device_disconnect(dev);
-            return;
-        }
-        usb_ep = usb_ep_get(&dev->dev,
-                            (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
-                            i & 0x0f);
-        usb_ep->type = dev->endpoint[i].type;
-        usb_ep->ifnum = dev->endpoint[i].interface;
-    }
-}
-
-static void usbredir_configuration_status(void *priv, uint32_t id,
-    struct usb_redir_configuration_status_header *config_status)
-{
-    USBRedirDevice *dev = priv;
-    AsyncURB *aurb;
-    int len = 0;
-
-    DPRINTF("set config status %d config %d id %u\n", config_status->status,
-            config_status->configuration, id);
-
-    aurb = async_find(dev, id);
-    if (!aurb) {
-        return;
-    }
-    if (aurb->packet) {
-        if (aurb->get) {
-            dev->dev.data_buf[0] = config_status->configuration;
-            len = 1;
-        }
-        aurb->packet->result =
-            usbredir_handle_status(dev, config_status->status, len);
-        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
-    }
-    async_free(dev, aurb);
-}
-
-static void usbredir_alt_setting_status(void *priv, uint32_t id,
-    struct usb_redir_alt_setting_status_header *alt_setting_status)
-{
-    USBRedirDevice *dev = priv;
-    AsyncURB *aurb;
-    int len = 0;
-
-    DPRINTF("alt status %d intf %d alt %d id: %u\n",
-            alt_setting_status->status,
-            alt_setting_status->interface,
-            alt_setting_status->alt, id);
-
-    aurb = async_find(dev, id);
-    if (!aurb) {
-        return;
-    }
-    if (aurb->packet) {
-        if (aurb->get) {
-            dev->dev.data_buf[0] = alt_setting_status->alt;
-            len = 1;
-        }
-        aurb->packet->result =
-            usbredir_handle_status(dev, alt_setting_status->status, len);
-        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
-    }
-    async_free(dev, aurb);
-}
-
-static void usbredir_iso_stream_status(void *priv, uint32_t id,
-    struct usb_redir_iso_stream_status_header *iso_stream_status)
-{
-    USBRedirDevice *dev = priv;
-    uint8_t ep = iso_stream_status->endpoint;
-
-    DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
-            ep, id);
-
-    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
-        return;
-    }
-
-    dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status;
-    if (iso_stream_status->status == usb_redir_stall) {
-        DPRINTF("iso stream stopped by peer ep %02X\n", ep);
-        dev->endpoint[EP2I(ep)].iso_started = 0;
-    }
-}
-
-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
-    struct usb_redir_interrupt_receiving_status_header
-    *interrupt_receiving_status)
-{
-    USBRedirDevice *dev = priv;
-    uint8_t ep = interrupt_receiving_status->endpoint;
-
-    DPRINTF("interrupt recv status %d ep %02X id %u\n",
-            interrupt_receiving_status->status, ep, id);
-
-    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
-        return;
-    }
-
-    dev->endpoint[EP2I(ep)].interrupt_error =
-        interrupt_receiving_status->status;
-    if (interrupt_receiving_status->status == usb_redir_stall) {
-        DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep);
-        dev->endpoint[EP2I(ep)].interrupt_started = 0;
-    }
-}
-
-static void usbredir_bulk_streams_status(void *priv, uint32_t id,
-    struct usb_redir_bulk_streams_status_header *bulk_streams_status)
-{
-}
-
-static void usbredir_control_packet(void *priv, uint32_t id,
-    struct usb_redir_control_packet_header *control_packet,
-    uint8_t *data, int data_len)
-{
-    USBRedirDevice *dev = priv;
-    int len = control_packet->length;
-    AsyncURB *aurb;
-
-    DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
-            len, id);
-
-    aurb = async_find(dev, id);
-    if (!aurb) {
-        free(data);
-        return;
-    }
-
-    aurb->control_packet.status = control_packet->status;
-    aurb->control_packet.length = control_packet->length;
-    if (memcmp(&aurb->control_packet, control_packet,
-               sizeof(*control_packet))) {
-        ERROR("return control packet mismatch, please report this!\n");
-        len = USB_RET_NAK;
-    }
-
-    if (aurb->packet) {
-        len = usbredir_handle_status(dev, control_packet->status, len);
-        if (len > 0) {
-            usbredir_log_data(dev, "ctrl data in:", data, data_len);
-            if (data_len <= sizeof(dev->dev.data_buf)) {
-                memcpy(dev->dev.data_buf, data, data_len);
-            } else {
-                ERROR("ctrl buffer too small (%d > %zu)\n",
-                      data_len, sizeof(dev->dev.data_buf));
-                len = USB_RET_STALL;
-            }
-        }
-        aurb->packet->result = len;
-        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
-    }
-    async_free(dev, aurb);
-    free(data);
-}
-
-static void usbredir_bulk_packet(void *priv, uint32_t id,
-    struct usb_redir_bulk_packet_header *bulk_packet,
-    uint8_t *data, int data_len)
-{
-    USBRedirDevice *dev = priv;
-    uint8_t ep = bulk_packet->endpoint;
-    int len = bulk_packet->length;
-    AsyncURB *aurb;
-
-    DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
-            ep, len, id);
-
-    aurb = async_find(dev, id);
-    if (!aurb) {
-        free(data);
-        return;
-    }
-
-    if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
-            aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
-        ERROR("return bulk packet mismatch, please report this!\n");
-        len = USB_RET_NAK;
-    }
-
-    if (aurb->packet) {
-        len = usbredir_handle_status(dev, bulk_packet->status, len);
-        if (len > 0) {
-            usbredir_log_data(dev, "bulk data in:", data, data_len);
-            if (data_len <= aurb->packet->iov.size) {
-                usb_packet_copy(aurb->packet, data, data_len);
-            } else {
-                ERROR("bulk buffer too small (%d > %zd)\n", data_len,
-                      aurb->packet->iov.size);
-                len = USB_RET_STALL;
-            }
-        }
-        aurb->packet->result = len;
-        usb_packet_complete(&dev->dev, aurb->packet);
-    }
-    async_free(dev, aurb);
-    free(data);
-}
-
-static void usbredir_iso_packet(void *priv, uint32_t id,
-    struct usb_redir_iso_packet_header *iso_packet,
-    uint8_t *data, int data_len)
-{
-    USBRedirDevice *dev = priv;
-    uint8_t ep = iso_packet->endpoint;
-
-    DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
-             data_len, id);
-
-    if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
-        ERROR("received iso packet for non iso endpoint %02X\n", ep);
-        free(data);
-        return;
-    }
-
-    if (dev->endpoint[EP2I(ep)].iso_started == 0) {
-        DPRINTF("received iso packet for non started stream ep %02X\n", ep);
-        free(data);
-        return;
-    }
-
-    /* bufp_alloc also adds the packet to the ep queue */
-    bufp_alloc(dev, data, data_len, iso_packet->status, ep);
-}
-
-static void usbredir_interrupt_packet(void *priv, uint32_t id,
-    struct usb_redir_interrupt_packet_header *interrupt_packet,
-    uint8_t *data, int data_len)
-{
-    USBRedirDevice *dev = priv;
-    uint8_t ep = interrupt_packet->endpoint;
-
-    DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
-            interrupt_packet->status, ep, data_len, id);
-
-    if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
-        ERROR("received int packet for non interrupt endpoint %02X\n", ep);
-        free(data);
-        return;
-    }
-
-    if (ep & USB_DIR_IN) {
-        if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
-            DPRINTF("received int packet while not started ep %02X\n", ep);
-            free(data);
-            return;
-        }
-
-        /* bufp_alloc also adds the packet to the ep queue */
-        bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
-    } else {
-        int len = interrupt_packet->length;
-
-        AsyncURB *aurb = async_find(dev, id);
-        if (!aurb) {
-            return;
-        }
-
-        if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
-            ERROR("return int packet mismatch, please report this!\n");
-            len = USB_RET_NAK;
-        }
-
-        if (aurb->packet) {
-            aurb->packet->result = usbredir_handle_status(dev,
-                                               interrupt_packet->status, len);
-            usb_packet_complete(&dev->dev, aurb->packet);
-        }
-        async_free(dev, aurb);
-    }
-}
-
-static Property usbredir_properties[] = {
-    DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
-    DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
-    DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usbredir_class_initfn(ObjectClass *klass, void *data)
-{
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    uc->init           = usbredir_initfn;
-    uc->product_desc   = "USB Redirection Device";
-    uc->handle_destroy = usbredir_handle_destroy;
-    uc->cancel_packet  = usbredir_cancel_packet;
-    uc->handle_reset   = usbredir_handle_reset;
-    uc->handle_data    = usbredir_handle_data;
-    uc->handle_control = usbredir_handle_control;
-    dc->props          = usbredir_properties;
-}
-
-static TypeInfo usbredir_dev_info = {
-    .name          = "usb-redir",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBRedirDevice),
-    .class_init    = usbredir_class_initfn,
-};
-
-static void usbredir_register_types(void)
-{
-    type_register_static(&usbredir_dev_info);
-}
-
-type_init(usbredir_register_types)
diff --git a/usb-stub.c b/usb-stub.c
deleted file mode 100644 (file)
index b4e10c1..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Stub host USB redirector
- *
- * Copyright (c) 2005 Fabrice Bellard
- *
- * Copyright (c) 2008 Max Krasnyansky
- *      Support for host device auto connect & disconnect
- *      Major rewrite to support fully async operation
- *
- * Copyright 2008 TJ <linux@tjworld.net>
- *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
- *      to the legacy /proc/bus/usb USB device discovery and handling
- *
- * 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 "console.h"
-#include "hw/usb.h"
-#include "monitor.h"
-
-void usb_host_info(Monitor *mon)
-{
-    monitor_printf(mon, "USB host devices not supported\n");
-}
-
-/* XXX: modify configure to compile the right host driver */
-USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
-{
-    return NULL;
-}
-
-int usb_host_device_close(const char *devname)
-{
-    return 0;
-}