summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/display/jazz_led.c1
-rw-r--r--hw/input/hid.c220
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/lm32_sys.c179
-rw-r--r--hw/net/cadence_gem.c2
-rw-r--r--hw/pci/pci.c4
-rw-r--r--hw/usb/dev-hid.c13
-rw-r--r--hw/usb/dev-mtp.c28
-rw-r--r--hw/usb/hcd-xhci.c17
-rw-r--r--hw/usb/host-libusb.c83
-rw-r--r--hw/usb/redirect.c137
11 files changed, 403 insertions, 282 deletions
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
index e9bb005413..12b1707cb2 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -173,6 +173,7 @@ static void jazz_led_update_display(void *opaque)
             case 16:
                 color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa);
                 color_led = rgb_to_pixel16(0x00, 0xff, 0x00);
+                break;
             case 24:
                 color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa);
                 color_led = rgb_to_pixel24(0x00, 0xff, 0x00);
diff --git a/hw/input/hid.c b/hw/input/hid.c
index bb0fa6a619..295bdab652 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -105,70 +105,135 @@ void hid_set_next_idle(HIDState *hs)
     }
 }
 
-static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
+static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
+                              InputEvent *evt)
 {
-    e->xdx = e->ydy = e->dz = 0;
-    e->buttons_state = buttons;
-}
+    static const int bmap[INPUT_BUTTON_MAX] = {
+        [INPUT_BUTTON_LEFT]   = 0x01,
+        [INPUT_BUTTON_RIGHT]  = 0x02,
+        [INPUT_BUTTON_MIDDLE] = 0x04,
+    };
+    HIDState *hs = (HIDState *)dev;
+    HIDPointerEvent *e;
 
-static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
-                                      int x1, int y1, int z1) {
-    if (xyrel) {
-        e->xdx += x1;
-        e->ydy += y1;
-    } else {
-        e->xdx = x1;
-        e->ydy = y1;
-        /* Windows drivers do not like the 0/0 position and ignore such
-         * events. */
-        if (!(x1 | y1)) {
-            e->xdx = 1;
+    assert(hs->n < QUEUE_LENGTH);
+    e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
+
+    switch (evt->kind) {
+    case INPUT_EVENT_KIND_REL:
+        if (evt->rel->axis == INPUT_AXIS_X) {
+            e->xdx += evt->rel->value;
+        } else if (evt->rel->axis == INPUT_AXIS_Y) {
+            e->ydy -= evt->rel->value;
+        }
+        break;
+
+    case INPUT_EVENT_KIND_ABS:
+        if (evt->rel->axis == INPUT_AXIS_X) {
+            e->xdx = evt->rel->value;
+        } else if (evt->rel->axis == INPUT_AXIS_Y) {
+            e->ydy = evt->rel->value;
         }
+        break;
+
+    case INPUT_EVENT_KIND_BTN:
+        if (evt->btn->down) {
+            e->buttons_state |= bmap[evt->btn->button];
+            if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+                e->dz--;
+            } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+                e->dz++;
+            }
+        } else {
+            e->buttons_state &= ~bmap[evt->btn->button];
+        }
+        break;
+
+    default:
+        /* keep gcc happy */
+        break;
     }
-    e->dz += z1;
+
 }
 
-static void hid_pointer_event(void *opaque,
-                              int x1, int y1, int z1, int buttons_state)
+static void hid_pointer_sync(DeviceState *dev)
 {
-    HIDState *hs = opaque;
-    unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
-    unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
-
-    /* We combine events where feasible to keep the queue small.  We shouldn't
-     * combine anything with the first event of a particular button state, as
-     * that would change the location of the button state change.  When the
-     * queue is empty, a second event is needed because we don't know if
-     * the first event changed the button state.  */
-    if (hs->n == QUEUE_LENGTH) {
-        /* Queue full.  Discard old button state, combine motion normally.  */
-        hs->ptr.queue[use_slot].buttons_state = buttons_state;
-    } else if (hs->n < 2 ||
-               hs->ptr.queue[use_slot].buttons_state != buttons_state ||
-               hs->ptr.queue[previous_slot].buttons_state !=
-               hs->ptr.queue[use_slot].buttons_state) {
-        /* Cannot or should not combine, so add an empty item to the queue.  */
-        QUEUE_INCR(use_slot);
+    HIDState *hs = (HIDState *)dev;
+    HIDPointerEvent *prev, *curr, *next;
+    bool event_compression = false;
+
+    if (hs->n == QUEUE_LENGTH-1) {
+        /*
+         * Queue full.  We are loosing information, but we at least
+         * keep track of most recent button state.
+         */
+        return;
+    }
+
+    prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
+    curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
+    next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
+
+    if (hs->n > 0) {
+        /*
+         * No button state change between previous and current event
+         * (and previous wasn't seen by the guest yet), so there is
+         * motion information only and we can combine the two event
+         * into one.
+         */
+        if (curr->buttons_state == prev->buttons_state) {
+            event_compression = true;
+        }
+    }
+
+    if (event_compression) {
+        /* add current motion to previous, clear current */
+        if (hs->kind == HID_MOUSE) {
+            prev->xdx += curr->xdx;
+            curr->xdx = 0;
+            prev->ydy -= curr->ydy;
+            curr->ydy = 0;
+        } else {
+            prev->xdx = curr->xdx;
+            prev->ydy = curr->ydy;
+        }
+        prev->dz += curr->dz;
+        curr->dz = 0;
+    } else {
+        /* prepate next (clear rel, copy abs + btns) */
+        if (hs->kind == HID_MOUSE) {
+            next->xdx = 0;
+            next->ydy = 0;
+        } else {
+            next->xdx = curr->xdx;
+            next->ydy = curr->ydy;
+        }
+        next->dz = 0;
+        next->buttons_state = curr->buttons_state;
+        /* make current guest visible, notify guest */
         hs->n++;
-        hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
+        hs->event(hs);
     }
-    hid_pointer_event_combine(&hs->ptr.queue[use_slot],
-                              hs->kind == HID_MOUSE,
-                              x1, y1, z1);
-    hs->event(hs);
 }
 
-static void hid_keyboard_event(void *opaque, int keycode)
+static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
+                               InputEvent *evt)
 {
-    HIDState *hs = opaque;
+    HIDState *hs = (HIDState *)dev;
+    int scancodes[3], i, count;
     int slot;
 
-    if (hs->n == QUEUE_LENGTH) {
+    count = qemu_input_key_value_to_scancode(evt->key->key,
+                                             evt->key->down,
+                                             scancodes);
+    if (hs->n + count > QUEUE_LENGTH) {
         fprintf(stderr, "usb-kbd: warning: key event queue full\n");
         return;
     }
-    slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
-    hs->kbd.keycodes[slot] = keycode;
+    for (i = 0; i < count; i++) {
+        slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
+        hs->kbd.keycodes[slot] = scancodes[i];
+    }
     hs->event(hs);
 }
 
@@ -247,14 +312,14 @@ static inline int int_clamp(int val, int vmin, int vmax)
 void hid_pointer_activate(HIDState *hs)
 {
     if (!hs->ptr.mouse_grabbed) {
-        qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
+        qemu_input_handler_activate(hs->s);
         hs->ptr.mouse_grabbed = 1;
     }
 }
 
 int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
 {
-    int dx, dy, dz, b, l;
+    int dx, dy, dz, l;
     int index;
     HIDPointerEvent *e;
 
@@ -279,17 +344,6 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
     dz = int_clamp(e->dz, -127, 127);
     e->dz -= dz;
 
-    b = 0;
-    if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
-        b |= 0x01;
-    }
-    if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
-        b |= 0x02;
-    }
-    if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
-        b |= 0x04;
-    }
-
     if (hs->n &&
         !e->dz &&
         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
@@ -304,7 +358,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
     switch (hs->kind) {
     case HID_MOUSE:
         if (len > l) {
-            buf[l++] = b;
+            buf[l++] = e->buttons_state;
         }
         if (len > l) {
             buf[l++] = dx;
@@ -319,7 +373,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
 
     case HID_TABLET:
         if (len > l) {
-            buf[l++] = b;
+            buf[l++] = e->buttons_state;
         }
         if (len > l) {
             buf[l++] = dx & 0xff;
@@ -413,31 +467,45 @@ void hid_reset(HIDState *hs)
 
 void hid_free(HIDState *hs)
 {
-    switch (hs->kind) {
-    case HID_KEYBOARD:
-        qemu_remove_kbd_event_handler(hs->kbd.eh_entry);
-        break;
-    case HID_MOUSE:
-    case HID_TABLET:
-        qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
-        break;
-    }
+    qemu_input_handler_unregister(hs->s);
     hid_del_idle_timer(hs);
 }
 
+static QemuInputHandler hid_keyboard_handler = {
+    .name  = "QEMU HID Keyboard",
+    .mask  = INPUT_EVENT_MASK_KEY,
+    .event = hid_keyboard_event,
+};
+
+static QemuInputHandler hid_mouse_handler = {
+    .name  = "QEMU HID Mouse",
+    .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
+    .event = hid_pointer_event,
+    .sync  = hid_pointer_sync,
+};
+
+static QemuInputHandler hid_tablet_handler = {
+    .name  = "QEMU HID Tablet",
+    .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
+    .event = hid_pointer_event,
+    .sync  = hid_pointer_sync,
+};
+
 void hid_init(HIDState *hs, int kind, HIDEventFunc event)
 {
     hs->kind = kind;
     hs->event = event;
 
     if (hs->kind == HID_KEYBOARD) {
-        hs->kbd.eh_entry = qemu_add_kbd_event_handler(hid_keyboard_event, hs);
+        hs->s = qemu_input_handler_register((DeviceState *)hs,
+                                            &hid_keyboard_handler);
+        qemu_input_handler_activate(hs->s);
     } else if (hs->kind == HID_MOUSE) {
-        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
-                                                        0, "QEMU HID Mouse");
+        hs->s = qemu_input_handler_register((DeviceState *)hs,
+                                            &hid_mouse_handler);
     } else if (hs->kind == HID_TABLET) {
-        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
-                                                        1, "QEMU HID Tablet");
+        hs->s = qemu_input_handler_register((DeviceState *)hs,
+                                            &hid_tablet_handler);
     }
 }
 
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index f6743659f7..979e532fdf 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -29,7 +29,6 @@ obj-$(CONFIG_NSERIES) += cbus.o
 obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
 obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
 obj-$(CONFIG_IMX) += imx_ccm.o
-obj-$(CONFIG_LM32) += lm32_sys.o
 obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
 obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/lm32_sys.c b/hw/misc/lm32_sys.c
deleted file mode 100644
index 778eb6e042..0000000000
--- a/hw/misc/lm32_sys.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- *  QEMU model of the LatticeMico32 system control block.
- *
- *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
- *
- * 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/>.
- */
-
-/*
- * This model is mainly intended for testing purposes and doesn't fit to any
- * real hardware. On the one hand it provides a control register (R_CTRL) on
- * the other hand it supports the lm32 tests.
- *
- * A write to the control register causes a system shutdown.
- * Tests first write the pointer to a test name to the test name register
- * (R_TESTNAME) and then write a zero to the pass/fail register (R_PASSFAIL) if
- * the test is passed or any non-zero value to it if the test is failed.
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "trace.h"
-#include "qemu/log.h"
-#include "qemu/error-report.h"
-#include "sysemu/sysemu.h"
-
-enum {
-    R_CTRL = 0,
-    R_PASSFAIL,
-    R_TESTNAME,
-    R_MAX
-};
-
-#define MAX_TESTNAME_LEN 32
-
-#define TYPE_LM32_SYS "lm32-sys"
-#define LM32_SYS(obj) OBJECT_CHECK(LM32SysState, (obj), TYPE_LM32_SYS)
-
-struct LM32SysState {
-    SysBusDevice parent_obj;
-
-    MemoryRegion iomem;
-    uint32_t base;
-    uint32_t regs[R_MAX];
-    uint8_t testname[MAX_TESTNAME_LEN];
-};
-typedef struct LM32SysState LM32SysState;
-
-static void copy_testname(LM32SysState *s)
-{
-    cpu_physical_memory_read(s->regs[R_TESTNAME], s->testname,
-            MAX_TESTNAME_LEN);
-    s->testname[MAX_TESTNAME_LEN - 1] = '\0';
-}
-
-static void sys_write(void *opaque, hwaddr addr,
-                      uint64_t value, unsigned size)
-{
-    LM32SysState *s = opaque;
-    char *testname;
-
-    trace_lm32_sys_memory_write(addr, value);
-
-    addr >>= 2;
-    switch (addr) {
-    case R_CTRL:
-        qemu_system_shutdown_request();
-        break;
-    case R_PASSFAIL:
-        s->regs[addr] = value;
-        testname = (char *)s->testname;
-        fprintf(stderr, "TC  %-*s %s\n", MAX_TESTNAME_LEN,
-                testname, (value) ? "FAILED" : "OK");
-        if (value) {
-            cpu_dump_state(qemu_get_cpu(0), stderr, fprintf, 0);
-        }
-        break;
-    case R_TESTNAME:
-        s->regs[addr] = value;
-        copy_testname(s);
-        break;
-
-    default:
-        error_report("lm32_sys: write access to unknown register 0x"
-                TARGET_FMT_plx, addr << 2);
-        break;
-    }
-}
-
-static bool sys_ops_accepts(void *opaque, hwaddr addr,
-                            unsigned size, bool is_write)
-{
-    return is_write && size == 4;
-}
-
-static const MemoryRegionOps sys_ops = {
-    .write = sys_write,
-    .valid.accepts = sys_ops_accepts,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void sys_reset(DeviceState *d)
-{
-    LM32SysState *s = LM32_SYS(d);
-    int i;
-
-    for (i = 0; i < R_MAX; i++) {
-        s->regs[i] = 0;
-    }
-    memset(s->testname, 0, MAX_TESTNAME_LEN);
-}
-
-static int lm32_sys_init(SysBusDevice *dev)
-{
-    LM32SysState *s = LM32_SYS(dev);
-
-    memory_region_init_io(&s->iomem, OBJECT(dev), &sys_ops , s,
-                          "sys", R_MAX * 4);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    /* Note: This device is not created in the board initialization,
-     * instead it has to be added with the -device parameter. Therefore,
-     * the device maps itself. */
-    sysbus_mmio_map(dev, 0, s->base);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_lm32_sys = {
-    .name = "lm32-sys",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32_ARRAY(regs, LM32SysState, R_MAX),
-        VMSTATE_BUFFER(testname, LM32SysState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property lm32_sys_properties[] = {
-    DEFINE_PROP_UINT32("base", LM32SysState, base, 0xffff0000),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void lm32_sys_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = lm32_sys_init;
-    dc->reset = sys_reset;
-    dc->vmsd = &vmstate_lm32_sys;
-    dc->props = lm32_sys_properties;
-}
-
-static const TypeInfo lm32_sys_info = {
-    .name          = TYPE_LM32_SYS,
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(LM32SysState),
-    .class_init    = lm32_sys_class_init,
-};
-
-static void lm32_sys_register_types(void)
-{
-    type_register_static(&lm32_sys_info);
-}
-
-type_init(lm32_sys_register_types)
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 47e70381fe..a26861e2ae 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1,5 +1,5 @@
 /*
- * QEMU Xilinx GEM emulation
+ * QEMU Cadence GEM emulation
  *
  * Copyright (c) 2011 Xilinx, Inc.
  *
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 22fe5eec36..8d6a8d4e74 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -605,13 +605,13 @@ PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr)
     int dom, bus;
     unsigned slot;
 
-    assert(!root->parent_dev);
-
     if (!root) {
         fprintf(stderr, "No primary PCI bus\n");
         return NULL;
     }
 
+    assert(!root->parent_dev);
+
     if (!devaddr) {
         *devfnp = -1;
         return pci_find_bus_nr(root, 0);
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index d097d937ea..67a57f1dcd 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -47,6 +47,8 @@ typedef struct USBHIDState {
     USBEndpoint *intr;
     HIDState hid;
     uint32_t usb_version;
+    char *display;
+    uint32_t head;
 } USBHIDState;
 
 enum {
@@ -574,6 +576,9 @@ static int usb_hid_initfn(USBDevice *dev, int kind)
     usb_desc_init(dev);
     us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
     hid_init(&us->hid, kind, usb_hid_changed);
+    if (us->display && us->hid.s) {
+        qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL);
+    }
     return 0;
 }
 
@@ -653,6 +658,8 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data)
 
 static Property usb_tablet_properties[] = {
         DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
+        DEFINE_PROP_STRING("display", USBHIDState, display),
+        DEFINE_PROP_UINT32("head", USBHIDState, head, 0),
         DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -696,6 +703,11 @@ static const TypeInfo usb_mouse_info = {
     .class_init    = usb_mouse_class_initfn,
 };
 
+static Property usb_keyboard_properties[] = {
+        DEFINE_PROP_STRING("display", USBHIDState, display),
+        DEFINE_PROP_END_OF_LIST(),
+};
+
 static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -706,6 +718,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
     uc->product_desc   = "QEMU USB Keyboard";
     uc->usb_desc       = &desc_keyboard;
     dc->vmsd = &vmstate_usb_kbd;
+    dc->props = usb_keyboard_properties;
     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 }
 
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 943f930404..380b465621 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -46,6 +46,7 @@ enum mtp_code {
 
     /* response codes */
     RES_OK                         = 0x2001,
+    RES_GENERAL_ERROR              = 0x2002,
     RES_SESSION_NOT_OPEN           = 0x2003,
     RES_INVALID_TRANSACTION_ID     = 0x2004,
     RES_OPERATION_NOT_SUPPORTED    = 0x2005,
@@ -109,7 +110,8 @@ struct MTPObject {
     struct stat  stat;
     MTPObject    *parent;
     MTPObject    **children;
-    int32_t      nchildren;
+    uint32_t     nchildren;
+    bool         have_children;
     QTAILQ_ENTRY(MTPObject) next;
 };
 
@@ -273,7 +275,6 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
     o->handle = handle;
     o->parent = parent;
     o->name = g_strdup(name);
-    o->nchildren = -1;
     if (parent == NULL) {
         o->path = g_strdup(name);
     } else {
@@ -340,7 +341,11 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
     struct dirent *entry;
     DIR *dir;
 
-    o->nchildren = 0;
+    if (o->have_children) {
+        return;
+    }
+    o->have_children = true;
+
     dir = opendir(o->path);
     if (!dir) {
         return;
@@ -698,7 +703,10 @@ static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c,
     if (offset > o->stat.st_size) {
         offset = o->stat.st_size;
     }
-    lseek(d->fd, offset, SEEK_SET);
+    if (lseek(d->fd, offset, SEEK_SET) < 0) {
+        usb_mtp_data_free(d);
+        return NULL;
+    }
 
     d->length = c->argv[2];
     if (d->length > o->stat.st_size - offset) {
@@ -789,9 +797,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
                                  c->trans, 0, 0, 0);
             return;
         }
-        if (o->nchildren == -1) {
-            usb_mtp_object_readdir(s, o);
-        }
+        usb_mtp_object_readdir(s, o);
         if (c->code == CMD_GET_NUM_OBJECTS) {
             trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path);
             nres = 1;
@@ -823,7 +829,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
         }
         data_in = usb_mtp_get_object(s, c, o);
         if (NULL == data_in) {
-            fprintf(stderr, "%s: TODO: handle error\n", __func__);
+            usb_mtp_queue_result(s, RES_GENERAL_ERROR,
+                                 c->trans, 0, 0, 0);
+            return;
         }
         break;
     case CMD_GET_PARTIAL_OBJECT:
@@ -840,7 +848,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
         }
         data_in = usb_mtp_get_partial_object(s, c, o);
         if (NULL == data_in) {
-            fprintf(stderr, "%s: TODO: handle error\n", __func__);
+            usb_mtp_queue_result(s, RES_GENERAL_ERROR,
+                                 c->trans, 0, 0, 0);
+            return;
         }
         nres = 1;
         res0 = data_in->length;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index ef3177aee9..54dea16009 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -621,6 +621,11 @@ static const char *ep_state_name(uint32_t state)
                        ARRAY_SIZE(ep_state_names));
 }
 
+static bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit)
+{
+    return xhci->flags & (1 << bit);
+}
+
 static uint64_t xhci_mfindex_get(XHCIState *xhci)
 {
     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -3435,7 +3440,7 @@ static void xhci_child_detach(USBPort *uport, USBDevice *child)
     USBBus *bus = usb_bus_from_device(child);
     XHCIState *xhci = container_of(bus, XHCIState, bus);
 
-    xhci_detach_slot(xhci, uport);
+    xhci_detach_slot(xhci, child->port);
 }
 
 static USBPortOps xhci_uport_ops = {
@@ -3594,13 +3599,15 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
                      PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
                      &xhci->mem);
 
-    ret = pcie_endpoint_cap_init(dev, 0xa0);
-    assert(ret >= 0);
+    if (pci_bus_is_express(dev->bus)) {
+        ret = pcie_endpoint_cap_init(dev, 0xa0);
+        assert(ret >= 0);
+    }
 
-    if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
+    if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) {
         msi_init(dev, 0x70, xhci->numintrs, true, false);
     }
-    if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) {
+    if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) {
         msix_init(dev, xhci->numintrs,
                   &xhci->mem, 0, OFF_MSIX_TABLE,
                   &xhci->mem, 0, OFF_MSIX_PBA,
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 57bed09a1e..8007d1d156 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -720,6 +720,9 @@ static void usb_host_ep_update(USBHostDevice *s)
     struct libusb_config_descriptor *conf;
     const struct libusb_interface_descriptor *intf;
     const struct libusb_endpoint_descriptor *endp;
+#if LIBUSBX_API_VERSION >= 0x01000103
+    struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
+#endif
     uint8_t devep, type;
     int pid, ep;
     int rc, i, e;
@@ -765,6 +768,15 @@ static void usb_host_ep_update(USBHostDevice *s)
             usb_ep_set_type(udev, pid, ep, type);
             usb_ep_set_ifnum(udev, pid, ep, i);
             usb_ep_set_halted(udev, pid, ep, 0);
+#if LIBUSBX_API_VERSION >= 0x01000103
+            if (type == LIBUSB_TRANSFER_TYPE_BULK &&
+                    libusb_get_ss_endpoint_companion_descriptor(ctx, endp,
+                        &endp_ss_comp) == LIBUSB_SUCCESS) {
+                usb_ep_set_max_streams(udev, pid, ep,
+                                       endp_ss_comp->bmAttributes);
+                libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp);
+            }
+#endif
         }
     }
 
@@ -1202,10 +1214,23 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
             usb_packet_copy(p, r->buffer, size);
         }
         ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
-        libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
-                                  r->buffer, size,
-                                  usb_host_req_complete_data, r,
-                                  BULK_TIMEOUT);
+        if (p->stream) {
+#if LIBUSBX_API_VERSION >= 0x01000103
+            libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream,
+                                             r->buffer, size,
+                                             usb_host_req_complete_data, r,
+                                             BULK_TIMEOUT);
+#else
+            usb_host_req_free(r);
+            p->status = USB_RET_STALL;
+            return;
+#endif
+        } else {
+            libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
+                                      r->buffer, size,
+                                      usb_host_req_complete_data, r,
+                                      BULK_TIMEOUT);
+        }
         break;
     case USB_ENDPOINT_XFER_INT:
         r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size);
@@ -1268,6 +1293,54 @@ static void usb_host_handle_reset(USBDevice *udev)
     }
 }
 
+static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
+                                  int nr_eps, int streams)
+{
+#if LIBUSBX_API_VERSION >= 0x01000103
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    unsigned char endpoints[30];
+    int i, rc;
+
+    for (i = 0; i < nr_eps; i++) {
+        endpoints[i] = eps[i]->nr;
+        if (eps[i]->pid == USB_TOKEN_IN) {
+            endpoints[i] |= 0x80;
+        }
+    }
+    rc = libusb_alloc_streams(s->dh, streams, endpoints, nr_eps);
+    if (rc < 0) {
+        usb_host_libusb_error("libusb_alloc_streams", rc);
+    } else if (rc != streams) {
+        fprintf(stderr,
+            "libusb_alloc_streams: got less streams then requested %d < %d\n",
+            rc, streams);
+    }
+
+    return (rc == streams) ? 0 : -1;
+#else
+    fprintf(stderr, "libusb_alloc_streams: error not implemented\n");
+    return -1;
+#endif
+}
+
+static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
+                                  int nr_eps)
+{
+#if LIBUSBX_API_VERSION >= 0x01000103
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    unsigned char endpoints[30];
+    int i;
+
+    for (i = 0; i < nr_eps; i++) {
+        endpoints[i] = eps[i]->nr;
+        if (eps[i]->pid == USB_TOKEN_IN) {
+            endpoints[i] |= 0x80;
+        }
+    }
+    libusb_free_streams(s->dh, endpoints, nr_eps);
+#endif
+}
+
 /*
  * This is *NOT* about restoring state.  We have absolutely no idea
  * what state the host device is in at the moment and whenever it is
@@ -1349,6 +1422,8 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data)
     uc->handle_reset   = usb_host_handle_reset;
     uc->handle_destroy = usb_host_handle_destroy;
     uc->flush_ep_queue = usb_host_flush_ep_queue;
+    uc->alloc_streams  = usb_host_alloc_streams;
+    uc->free_streams   = usb_host_free_streams;
     dc->vmsd = &vmstate_usb_host;
     dc->props = usb_host_dev_properties;
     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 287a505b48..4c6187bebd 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -50,6 +50,10 @@
                        ((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \
                        (i) & 0x0f))
 
+#ifndef USBREDIR_VERSION /* This is not defined in older usbredir versions */
+#define USBREDIR_VERSION 0
+#endif
+
 typedef struct USBRedirDevice USBRedirDevice;
 
 /* Struct to hold buffered packets */
@@ -68,6 +72,7 @@ struct endp_data {
     uint8_t interval;
     uint8_t interface; /* bInterfaceNumber this ep belongs to */
     uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */
+    uint32_t max_streams;
     uint8_t iso_started;
     uint8_t iso_error; /* For reporting iso errors to the HC */
     uint8_t interrupt_started;
@@ -106,8 +111,9 @@ struct USBRedirDevice {
     int read_buf_size;
     /* Active chardev-watch-tag */
     guint watch;
-    /* For async handling of close */
+    /* For async handling of close / reject */
     QEMUBH *chardev_close_bh;
+    QEMUBH *device_reject_bh;
     /* To delay the usb attach in case of quick chardev close + open */
     QEMUTimer *attach_timer;
     int64_t next_attach_time;
@@ -780,11 +786,12 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
         dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0;
     }
 
-    DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id);
+    DPRINTF("bulk-out ep %02X stream %u len %zd id %"PRIu64"\n",
+            ep, p->stream, size, p->id);
 
     bulk_packet.endpoint  = ep;
     bulk_packet.length    = size;
-    bulk_packet.stream_id = 0;
+    bulk_packet.stream_id = p->stream;
     bulk_packet.length_high = size >> 16;
     assert(bulk_packet.length_high == 0 ||
            usbredirparser_peer_has_cap(dev->parser,
@@ -1091,6 +1098,66 @@ static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
     p->status = USB_RET_ASYNC;
 }
 
+static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps,
+                                  int nr_eps, int streams)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+#if USBREDIR_VERSION >= 0x000700
+    struct usb_redir_alloc_bulk_streams_header alloc_streams;
+    int i;
+
+    if (!usbredirparser_peer_has_cap(dev->parser,
+                                     usb_redir_cap_bulk_streams)) {
+        ERROR("peer does not support streams\n");
+        goto reject;
+    }
+
+    if (streams == 0) {
+        ERROR("request to allocate 0 streams\n");
+        return -1;
+    }
+
+    alloc_streams.no_streams = streams;
+    alloc_streams.endpoints = 0;
+    for (i = 0; i < nr_eps; i++) {
+        alloc_streams.endpoints |= 1 << USBEP2I(eps[i]);
+    }
+    usbredirparser_send_alloc_bulk_streams(dev->parser, 0, &alloc_streams);
+    usbredirparser_do_write(dev->parser);
+
+    return 0;
+#else
+    ERROR("usbredir_alloc_streams not implemented\n");
+    goto reject;
+#endif
+reject:
+    ERROR("streams are not available, disconnecting\n");
+    qemu_bh_schedule(dev->device_reject_bh);
+    return -1;
+}
+
+static void usbredir_free_streams(USBDevice *udev, USBEndpoint **eps,
+                                  int nr_eps)
+{
+#if USBREDIR_VERSION >= 0x000700
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    struct usb_redir_free_bulk_streams_header free_streams;
+    int i;
+
+    if (!usbredirparser_peer_has_cap(dev->parser,
+                                     usb_redir_cap_bulk_streams)) {
+        return;
+    }
+
+    free_streams.endpoints = 0;
+    for (i = 0; i < nr_eps; i++) {
+        free_streams.endpoints |= 1 << USBEP2I(eps[i]);
+    }
+    usbredirparser_send_free_bulk_streams(dev->parser, 0, &free_streams);
+    usbredirparser_do_write(dev->parser);
+#endif
+}
+
 /*
  * Close events can be triggered by usbredirparser_do_write which gets called
  * from within the USBDevice data / control packet callbacks and doing a
@@ -1102,6 +1169,7 @@ static void usbredir_chardev_close_bh(void *opaque)
 {
     USBRedirDevice *dev = opaque;
 
+    qemu_bh_cancel(dev->device_reject_bh);
     usbredir_device_disconnect(dev);
 
     if (dev->parser) {
@@ -1153,6 +1221,9 @@ static void usbredir_create_parser(USBRedirDevice *dev)
     usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
     usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
     usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
+#if USBREDIR_VERSION >= 0x000700
+    usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+#endif
 
     if (runstate_check(RUN_STATE_INMIGRATE)) {
         flags |= usbredirparser_fl_no_hello;
@@ -1171,6 +1242,17 @@ static void usbredir_reject_device(USBRedirDevice *dev)
     }
 }
 
+/*
+ * We may need to reject the device when the hcd calls alloc_streams, doing
+ * an usb_detach from within a hcd call is not a good idea, hence this bh.
+ */
+static void usbredir_device_reject_bh(void *opaque)
+{
+    USBRedirDevice *dev = opaque;
+
+    usbredir_reject_device(dev);
+}
+
 static void usbredir_do_attach(void *opaque)
 {
     USBRedirDevice *dev = opaque;
@@ -1297,6 +1379,7 @@ static int usbredir_initfn(USBDevice *udev)
     }
 
     dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
+    dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev);
     dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev);
 
     packet_id_queue_init(&dev->cancelled, dev, "cancelled");
@@ -1337,6 +1420,7 @@ static void usbredir_handle_destroy(USBDevice *udev)
     dev->cs = NULL;
     /* Note must be done after qemu_chr_close, as that causes a close event */
     qemu_bh_delete(dev->chardev_close_bh);
+    qemu_bh_delete(dev->device_reject_bh);
 
     timer_del(dev->attach_timer);
     timer_free(dev->attach_timer);
@@ -1628,6 +1712,7 @@ static void usbredir_setup_usb_eps(USBRedirDevice *dev)
         usb_ep->type = dev->endpoint[i].type;
         usb_ep->ifnum = dev->endpoint[i].interface;
         usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
+        usb_ep->max_streams = dev->endpoint[i].max_streams;
         usbredir_set_pipeline(dev, usb_ep);
     }
 }
@@ -1646,6 +1731,12 @@ static void usbredir_ep_info(void *priv,
                                      usb_redir_cap_ep_info_max_packet_size)) {
             dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i];
         }
+#if USBREDIR_VERSION >= 0x000700
+        if (usbredirparser_peer_has_cap(dev->parser,
+                                        usb_redir_cap_bulk_streams)) {
+            dev->endpoint[i].max_streams = ep_info->max_streams[i];
+        }
+#endif
         switch (dev->endpoint[i].type) {
         case usb_redir_type_invalid:
             break;
@@ -1779,6 +1870,20 @@ static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
 static void usbredir_bulk_streams_status(void *priv, uint64_t id,
     struct usb_redir_bulk_streams_status_header *bulk_streams_status)
 {
+#if USBREDIR_VERSION >= 0x000700
+    USBRedirDevice *dev = priv;
+
+    if (bulk_streams_status->status == usb_redir_success) {
+        DPRINTF("bulk streams status %d eps %08x\n",
+                bulk_streams_status->status, bulk_streams_status->endpoints);
+    } else {
+        ERROR("bulk streams %s failed status %d eps %08x\n",
+              (bulk_streams_status->no_streams == 0) ? "free" : "alloc",
+              bulk_streams_status->status, bulk_streams_status->endpoints);
+        ERROR("usb-redir-host does not provide streams, disconnecting\n");
+        usbredir_reject_device(dev);
+    }
+#endif
 }
 
 static void usbredir_bulk_receiving_status(void *priv, uint64_t id,
@@ -1850,8 +1955,8 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
     int len = (bulk_packet->length_high << 16) | bulk_packet->length;
     USBPacket *p;
 
-    DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
-            bulk_packet->status, ep, len, id);
+    DPRINTF("bulk-in status %d ep %02X stream %u len %d id %"PRIu64"\n",
+            bulk_packet->status, ep, bulk_packet->stream_id, len, id);
 
     p = usbredir_find_packet_by_id(dev, ep, id);
     if (p) {
@@ -2165,6 +2270,23 @@ static bool usbredir_bulk_receiving_needed(void *priv)
     return endp->bulk_receiving_started;
 }
 
+static const VMStateDescription usbredir_stream_vmstate = {
+    .name = "usb-redir-ep/stream-state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(max_streams, struct endp_data),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static bool usbredir_stream_needed(void *priv)
+{
+    struct endp_data *endp = priv;
+
+    return endp->max_streams;
+}
+
 static const VMStateDescription usbredir_ep_vmstate = {
     .name = "usb-redir-ep",
     .version_id = 1,
@@ -2197,6 +2319,9 @@ static const VMStateDescription usbredir_ep_vmstate = {
             .vmsd = &usbredir_bulk_receiving_vmstate,
             .needed = usbredir_bulk_receiving_needed,
         }, {
+            .vmsd = &usbredir_stream_vmstate,
+            .needed = usbredir_stream_needed,
+        }, {
             /* empty */
         }
     }
@@ -2361,6 +2486,8 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
     uc->handle_control = usbredir_handle_control;
     uc->flush_ep_queue = usbredir_flush_ep_queue;
     uc->ep_stopped     = usbredir_ep_stopped;
+    uc->alloc_streams  = usbredir_alloc_streams;
+    uc->free_streams   = usbredir_free_streams;
     dc->vmsd           = &usbredir_vmstate;
     dc->props          = usbredir_properties;
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);