summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/audio/hda-codec.c18
-rw-r--r--hw/block/virtio-blk.c2
-rw-r--r--hw/i386/pc_piix.c4
-rw-r--r--hw/ide/core.c2
-rw-r--r--hw/scsi/scsi-bus.c2
-rw-r--r--hw/scsi/scsi-disk.c3
-rw-r--r--hw/scsi/scsi-generic.c2
-rw-r--r--hw/scsi/virtio-scsi.c6
-rw-r--r--hw/usb/Makefile.objs2
-rw-r--r--hw/usb/bus.c2
-rw-r--r--hw/usb/desc-msos.c234
-rw-r--r--hw/usb/desc.c37
-rw-r--r--hw/usb/desc.h11
-rw-r--r--hw/usb/dev-hid.c8
-rw-r--r--hw/virtio/dataplane/vring.c2
15 files changed, 323 insertions, 12 deletions
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 07a43bfe89..986f2a9c92 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -559,6 +559,21 @@ static int hda_audio_post_load(void *opaque, int version)
     return 0;
 }
 
+static void hda_audio_reset(DeviceState *dev)
+{
+    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda.qdev, dev);
+    HDAAudioStream *st;
+    int i;
+
+    dprint(a, 1, "%s\n", __func__);
+    for (i = 0; i < ARRAY_SIZE(a->st); i++) {
+        st = a->st + i;
+        if (st->node != NULL) {
+            hda_audio_set_running(st, false);
+        }
+    }
+}
+
 static const VMStateDescription vmstate_hda_audio_stream = {
     .name = "hda-audio-stream",
     .version_id = 1,
@@ -640,6 +655,7 @@ static void hda_audio_output_class_init(ObjectClass *klass, void *data)
     k->stream = hda_audio_stream;
     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     dc->desc = "HDA Audio Codec, output-only (line-out)";
+    dc->reset = hda_audio_reset;
     dc->vmsd = &vmstate_hda_audio;
     dc->props = hda_audio_properties;
 }
@@ -662,6 +678,7 @@ static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
     k->stream = hda_audio_stream;
     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
+    dc->reset = hda_audio_reset;
     dc->vmsd = &vmstate_hda_audio;
     dc->props = hda_audio_properties;
 }
@@ -684,6 +701,7 @@ static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
     k->stream = hda_audio_stream;
     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
+    dc->reset = hda_audio_reset;
     dc->vmsd = &vmstate_hda_audio;
     dc->props = hda_audio_properties;
 }
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 19d0961a47..8a568e5edb 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -731,7 +731,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
     register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
                     virtio_blk_save, virtio_blk_load, s);
     bdrv_set_dev_ops(s->bs, &virtio_block_ops, s);
-    bdrv_set_buffer_alignment(s->bs, s->conf->logical_block_size);
+    bdrv_set_guest_block_size(s->bs, s->conf->logical_block_size);
 
     bdrv_iostatus_enable(s->bs);
 
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 276641436e..a327d71fb1 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -393,6 +393,10 @@ static QEMUMachine pc_i440fx_machine_v1_7 = {
     PC_I440FX_1_7_MACHINE_OPTIONS,
     .name = "pc-i440fx-1.7",
     .init = pc_init_pci_1_7,
+    .compat_props = (GlobalProperty[]) {
+        PC_COMPAT_1_7,
+        { /* end of list */ }
+    },
 };
 
 #define PC_I440FX_1_6_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS
diff --git a/hw/ide/core.c b/hw/ide/core.c
index e1f4c33fb8..036cd4a6d1 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -2103,7 +2103,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
     s->smart_selftest_count = 0;
     if (kind == IDE_CD) {
         bdrv_set_dev_ops(bs, &ide_cd_block_ops, s);
-        bdrv_set_buffer_alignment(bs, 2048);
+        bdrv_set_guest_block_size(bs, 2048);
     } else {
         if (!bdrv_is_inserted(s->bs)) {
             error_report("Device needs media, but drive is empty");
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 3496c0bbd8..50b89ad4aa 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -469,6 +469,8 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
             r->req.dev->sense_is_ua = false;
         }
         break;
+    case TEST_UNIT_READY:
+        break;
     default:
         scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED));
         scsi_req_complete(req, CHECK_CONDITION);
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index bce617cb93..a8d0f15ebe 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2254,7 +2254,7 @@ static int scsi_initfn(SCSIDevice *dev)
     } else {
         bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_block_ops, s);
     }
-    bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
+    bdrv_set_guest_block_size(s->qdev.conf.bs, s->qdev.blocksize);
 
     bdrv_iostatus_enable(s->qdev.conf.bs);
     add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL);
@@ -2306,6 +2306,7 @@ static const SCSIReqOps scsi_disk_emulate_reqops = {
     .send_command = scsi_disk_emulate_command,
     .read_data    = scsi_disk_emulate_read_data,
     .write_data   = scsi_disk_emulate_write_data,
+    .cancel_io    = scsi_cancel_io,
     .get_buf      = scsi_get_buf,
 };
 
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 8f195bec00..f08b64e177 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -210,7 +210,7 @@ static void scsi_read_complete(void * opaque, int ret)
             s->blocksize = ldl_be_p(&r->buf[8]);
             s->max_lba = ldq_be_p(&r->buf[0]);
         }
-        bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
+        bdrv_set_guest_block_size(s->conf.bs, s->blocksize);
 
         scsi_req_data(&r->req, len);
         if (!r->req.io_canceled) {
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 6dcdd1b91c..6610b3aab3 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -306,6 +306,10 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
     VirtIOSCSIReq *req = r->hba_private;
     uint32_t sense_len;
 
+    if (r->io_canceled) {
+        return;
+    }
+
     req->resp.cmd->response = VIRTIO_SCSI_S_OK;
     req->resp.cmd->status = status;
     if (req->resp.cmd->status == GOOD) {
@@ -516,7 +520,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
     evt->event = event;
     evt->reason = reason;
     if (!dev) {
-        assert(event == VIRTIO_SCSI_T_NO_EVENT);
+        assert(event == VIRTIO_SCSI_T_EVENTS_MISSED);
     } else {
         evt->lun[0] = 1;
         evt->lun[1] = dev->id;
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index a3eac3e5c1..97b457541f 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -1,5 +1,5 @@
 # usb subsystem core
-common-obj-y += core.o combined-packet.o bus.o desc.o
+common-obj-y += core.o combined-packet.o bus.o desc.o desc-msos.o
 common-obj-y += libhw.o
 
 # usb host adapters
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 09848c6320..fe70429304 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -16,6 +16,8 @@ static Property usb_props[] = {
     DEFINE_PROP_STRING("serial", USBDevice, serial),
     DEFINE_PROP_BIT("full-path", USBDevice, flags,
                     USB_DEV_FLAG_FULL_PATH, true),
+    DEFINE_PROP_BIT("msos-desc", USBDevice, flags,
+                    USB_DEV_FLAG_MSOS_DESC_ENABLE, true),
     DEFINE_PROP_END_OF_LIST()
 };
 
diff --git a/hw/usb/desc-msos.c b/hw/usb/desc-msos.c
new file mode 100644
index 0000000000..ed8d62cab8
--- /dev/null
+++ b/hw/usb/desc-msos.c
@@ -0,0 +1,234 @@
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+
+/*
+ * Microsoft OS Descriptors
+ *
+ * Windows tries to fetch some special descriptors with informations
+ * specifically for windows.  Presence is indicated using a special
+ * string @ index 0xee.  There are two kinds of descriptors:
+ *
+ * compatid descriptor
+ *   Used to bind drivers, if usb class isn't specific enougth.
+ *   Used for PTP/MTP for example (both share the same usb class).
+ *
+ * properties descriptor
+ *   Does carry registry entries.  They show up in
+ *   HLM\SYSTEM\CurrentControlSet\Enum\USB\<devid>\<serial>\Device Parameters
+ *
+ * Note that Windows caches the stuff it got in the registry, so when
+ * playing with this you have to delete registry subtrees to make
+ * windows query the device again:
+ *   HLM\SYSTEM\CurrentControlSet\Control\usbflags
+ *   HLM\SYSTEM\CurrentControlSet\Enum\USB
+ * Windows will complain it can't delete entries on the second one.
+ * It has deleted everything it had permissions too, which is enouth
+ * as this includes "Device Parameters".
+ *
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff537430.aspx
+ *
+ */
+
+/* ------------------------------------------------------------------ */
+
+typedef struct msos_compat_hdr {
+    uint32_t dwLength;
+    uint8_t  bcdVersion_lo;
+    uint8_t  bcdVersion_hi;
+    uint8_t  wIndex_lo;
+    uint8_t  wIndex_hi;
+    uint8_t  bCount;
+    uint8_t  reserved[7];
+} QEMU_PACKED msos_compat_hdr;
+
+typedef struct msos_compat_func {
+    uint8_t  bFirstInterfaceNumber;
+    uint8_t  reserved_1;
+    uint8_t  compatibleId[8];
+    uint8_t  subCompatibleId[8];
+    uint8_t  reserved_2[6];
+} QEMU_PACKED msos_compat_func;
+
+static int usb_desc_msos_compat(const USBDesc *desc, uint8_t *dest)
+{
+    msos_compat_hdr *hdr = (void *)dest;
+    msos_compat_func *func;
+    int length = sizeof(*hdr);
+    int count = 0;
+
+    func = (void *)(dest + length);
+    func->bFirstInterfaceNumber = 0;
+    func->reserved_1 = 0x01;
+    length += sizeof(*func);
+    count++;
+
+    hdr->dwLength      = cpu_to_le32(length);
+    hdr->bcdVersion_lo = 0x00;
+    hdr->bcdVersion_hi = 0x01;
+    hdr->wIndex_lo     = 0x04;
+    hdr->wIndex_hi     = 0x00;
+    hdr->bCount        = count;
+    return length;
+}
+
+/* ------------------------------------------------------------------ */
+
+typedef struct msos_prop_hdr {
+    uint32_t dwLength;
+    uint8_t  bcdVersion_lo;
+    uint8_t  bcdVersion_hi;
+    uint8_t  wIndex_lo;
+    uint8_t  wIndex_hi;
+    uint8_t  wCount_lo;
+    uint8_t  wCount_hi;
+} QEMU_PACKED msos_prop_hdr;
+
+typedef struct msos_prop {
+    uint32_t dwLength;
+    uint32_t dwPropertyDataType;
+    uint8_t  dwPropertyNameLength_lo;
+    uint8_t  dwPropertyNameLength_hi;
+    uint8_t  bPropertyName[];
+} QEMU_PACKED msos_prop;
+
+typedef struct msos_prop_data {
+    uint32_t dwPropertyDataLength;
+    uint8_t  bPropertyData[];
+} QEMU_PACKED msos_prop_data;
+
+typedef enum msos_prop_type {
+    MSOS_REG_SZ        = 1,
+    MSOS_REG_EXPAND_SZ = 2,
+    MSOS_REG_BINARY    = 3,
+    MSOS_REG_DWORD_LE  = 4,
+    MSOS_REG_DWORD_BE  = 5,
+    MSOS_REG_LINK      = 6,
+    MSOS_REG_MULTI_SZ  = 7,
+} msos_prop_type;
+
+static int usb_desc_msos_prop_name(struct msos_prop *prop,
+                                   const wchar_t *name)
+{
+    int length = wcslen(name) + 1;
+    int i;
+
+    prop->dwPropertyNameLength_lo = usb_lo(length*2);
+    prop->dwPropertyNameLength_hi = usb_hi(length*2);
+    for (i = 0; i < length; i++) {
+        prop->bPropertyName[i*2]   = usb_lo(name[i]);
+        prop->bPropertyName[i*2+1] = usb_hi(name[i]);
+    }
+    return length*2;
+}
+
+static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type,
+                                  const wchar_t *name, const wchar_t *value)
+{
+    struct msos_prop *prop = (void *)dest;
+    struct msos_prop_data *data;
+    int length = sizeof(*prop);
+    int i, vlen = wcslen(value) + 1;
+
+    prop->dwPropertyDataType = cpu_to_le32(type);
+    length += usb_desc_msos_prop_name(prop, name);
+    data = (void *)(dest + length);
+
+    data->dwPropertyDataLength = cpu_to_le32(vlen*2);
+    length += sizeof(*prop);
+
+    for (i = 0; i < vlen; i++) {
+        data->bPropertyData[i*2]   = usb_lo(value[i]);
+        data->bPropertyData[i*2+1] = usb_hi(value[i]);
+    }
+    length += vlen*2;
+
+    prop->dwLength = cpu_to_le32(length);
+    return length;
+}
+
+static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name,
+                                    uint32_t value)
+{
+    struct msos_prop *prop = (void *)dest;
+    struct msos_prop_data *data;
+    int length = sizeof(*prop);
+
+    prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE);
+    length += usb_desc_msos_prop_name(prop, name);
+    data = (void *)(dest + length);
+
+    data->dwPropertyDataLength = cpu_to_le32(4);
+    data->bPropertyData[0] = (value)       & 0xff;
+    data->bPropertyData[1] = (value >>  8) & 0xff;
+    data->bPropertyData[2] = (value >> 16) & 0xff;
+    data->bPropertyData[3] = (value >> 24) & 0xff;
+    length += sizeof(*prop) + 4;
+
+    prop->dwLength = cpu_to_le32(length);
+    return length;
+}
+
+static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest)
+{
+    msos_prop_hdr *hdr = (void *)dest;
+    int length = sizeof(*hdr);
+    int count = 0;
+
+    if (desc->msos->Label) {
+        /*
+         * Given as example in the specs.  Havn't figured yet where
+         * this label shows up in the windows gui.
+         */
+        length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ,
+                                         L"Label", desc->msos->Label);
+        count++;
+    }
+
+    if (desc->msos->SelectiveSuspendEnabled) {
+        /*
+         * Signaling remote wakeup capability in the standard usb
+         * descriptors isn't enouth to make windows actually use it.
+         * This is the "Yes, we really mean it" registy entry to flip
+         * the switch in the windows drivers.
+         */
+        length += usb_desc_msos_prop_dword(dest+length,
+                                           L"SelectiveSuspendEnabled", 1);
+        count++;
+    }
+
+    hdr->dwLength      = cpu_to_le32(length);
+    hdr->bcdVersion_lo = 0x00;
+    hdr->bcdVersion_hi = 0x01;
+    hdr->wIndex_lo     = 0x05;
+    hdr->wIndex_hi     = 0x00;
+    hdr->wCount_lo     = usb_lo(count);
+    hdr->wCount_hi     = usb_hi(count);
+    return length;
+}
+
+/* ------------------------------------------------------------------ */
+
+int usb_desc_msos(const USBDesc *desc,  USBPacket *p,
+                  int index, uint8_t *dest, size_t len)
+{
+    void *buf = g_malloc0(4096);
+    int length = 0;
+
+    switch (index) {
+    case 0x0004:
+        length = usb_desc_msos_compat(desc, buf);
+        break;
+    case 0x0005:
+        length = usb_desc_msos_prop(desc, buf);
+        break;
+    }
+
+    if (length > len) {
+        length = len;
+    }
+    memcpy(dest, buf, length);
+    free(buf);
+
+    p->actual_length = length;
+    return 0;
+}
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index f18a043500..f133ddb9db 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -7,7 +7,7 @@
 /* ------------------------------------------------------------------ */
 
 int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
-                    uint8_t *dest, size_t len)
+                    bool msos, uint8_t *dest, size_t len)
 {
     uint8_t bLength = 0x12;
     USBDescriptor *d = (void *)dest;
@@ -19,8 +19,18 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
     d->bLength                     = bLength;
     d->bDescriptorType             = USB_DT_DEVICE;
 
-    d->u.device.bcdUSB_lo          = usb_lo(dev->bcdUSB);
-    d->u.device.bcdUSB_hi          = usb_hi(dev->bcdUSB);
+    if (msos && dev->bcdUSB < 0x0200) {
+        /*
+         * Version 2.0+ required for microsoft os descriptors to work.
+         * Done this way so msos-desc compat property will handle both
+         * the version and the new descriptors being present.
+         */
+        d->u.device.bcdUSB_lo          = usb_lo(0x0200);
+        d->u.device.bcdUSB_hi          = usb_hi(0x0200);
+    } else {
+        d->u.device.bcdUSB_lo          = usb_lo(dev->bcdUSB);
+        d->u.device.bcdUSB_hi          = usb_hi(dev->bcdUSB);
+    }
     d->u.device.bDeviceClass       = dev->bDeviceClass;
     d->u.device.bDeviceSubClass    = dev->bDeviceSubClass;
     d->u.device.bDeviceProtocol    = dev->bDeviceProtocol;
@@ -499,6 +509,10 @@ void usb_desc_init(USBDevice *dev)
     if (desc->super) {
         dev->speedmask |= USB_SPEED_MASK_SUPER;
     }
+    if (desc->msos && (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_ENABLE))) {
+        dev->flags |= (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE);
+        usb_desc_set_string(dev, 0xee, "MSFT100Q");
+    }
     usb_desc_setdefaults(dev);
 }
 
@@ -626,6 +640,7 @@ int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
 int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
                             int value, uint8_t *dest, size_t len)
 {
+    bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
     const USBDesc *desc = usb_device_get_usb_desc(dev);
     const USBDescDevice *other_dev;
     uint8_t buf[256];
@@ -646,7 +661,7 @@ int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
 
     switch(type) {
     case USB_DT_DEVICE:
-        ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
+        ret = usb_desc_device(&desc->id, dev->device, msos, buf, sizeof(buf));
         trace_usb_desc_device(dev->addr, len, ret);
         break;
     case USB_DT_CONFIG:
@@ -703,6 +718,7 @@ int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
 int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         int request, int value, int index, int length, uint8_t *data)
 {
+    bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
     const USBDesc *desc = usb_device_get_usb_desc(dev);
     int ret = -1;
 
@@ -782,6 +798,19 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         trace_usb_set_interface(dev->addr, index, value, ret);
         break;
 
+    case VendorDeviceRequest | 'Q':
+        if (msos) {
+            ret = usb_desc_msos(desc, p, index, data, length);
+            trace_usb_desc_msos(dev->addr, index, length, ret);
+        }
+        break;
+    case VendorInterfaceRequest | 'Q':
+        if (msos) {
+            ret = usb_desc_msos(desc, p, index, data, length);
+            trace_usb_desc_msos(dev->addr, index, length, ret);
+        }
+        break;
+
     }
     return ret;
 }
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
index 81327b0e74..2b4fcdae76 100644
--- a/hw/usb/desc.h
+++ b/hw/usb/desc.h
@@ -2,6 +2,7 @@
 #define QEMU_HW_USB_DESC_H
 
 #include <inttypes.h>
+#include <wchar.h>
 
 /* binary representation */
 typedef struct USBDescriptor {
@@ -182,6 +183,11 @@ struct USBDescOther {
     const uint8_t             *data;
 };
 
+struct USBDescMSOS {
+    const wchar_t             *Label;
+    bool                      SelectiveSuspendEnabled;
+};
+
 typedef const char *USBDescStrings[256];
 
 struct USBDesc {
@@ -190,6 +196,7 @@ struct USBDesc {
     const USBDescDevice       *high;
     const USBDescDevice       *super;
     const char* const         *str;
+    const USBDescMSOS         *msos;
 };
 
 #define USB_DESC_FLAG_SUPER (1 << 1)
@@ -207,7 +214,7 @@ static inline uint8_t usb_hi(uint16_t val)
 
 /* generate usb packages from structs */
 int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
-                    uint8_t *dest, size_t len);
+                    bool msos, 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, int flags,
@@ -219,6 +226,8 @@ int usb_desc_iface(const USBDescIface *iface, int flags,
 int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
                       uint8_t *dest, size_t len);
 int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
+int usb_desc_msos(const USBDesc *desc, USBPacket *p,
+                  int index, uint8_t *dest, size_t len);
 
 /* control message emulation helpers */
 void usb_desc_init(USBDevice *dev);
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index 5e667f0199..2966066682 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -261,6 +261,10 @@ static const USBDescDevice desc_device_keyboard = {
     },
 };
 
+static const USBDescMSOS desc_msos_suspend = {
+    .SelectiveSuspendEnabled = true,
+};
+
 static const USBDesc desc_mouse = {
     .id = {
         .idVendor          = 0x0627,
@@ -272,6 +276,7 @@ static const USBDesc desc_mouse = {
     },
     .full = &desc_device_mouse,
     .str  = desc_strings,
+    .msos = &desc_msos_suspend,
 };
 
 static const USBDesc desc_tablet = {
@@ -285,6 +290,7 @@ static const USBDesc desc_tablet = {
     },
     .full = &desc_device_tablet,
     .str  = desc_strings,
+    .msos = &desc_msos_suspend,
 };
 
 static const USBDesc desc_tablet2 = {
@@ -299,6 +305,7 @@ static const USBDesc desc_tablet2 = {
     .full = &desc_device_tablet,
     .high = &desc_device_tablet2,
     .str  = desc_strings,
+    .msos = &desc_msos_suspend,
 };
 
 static const USBDesc desc_keyboard = {
@@ -312,6 +319,7 @@ static const USBDesc desc_keyboard = {
     },
     .full = &desc_device_keyboard,
     .str  = desc_strings,
+    .msos = &desc_msos_suspend,
 };
 
 static const uint8_t qemu_mouse_hid_report_descriptor[] = {
diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c
index 250d45ec3d..665a1ffcb3 100644
--- a/hw/virtio/dataplane/vring.c
+++ b/hw/virtio/dataplane/vring.c
@@ -376,7 +376,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
         barrier();
 
         if (desc.flags & VRING_DESC_F_INDIRECT) {
-            int ret = get_indirect(vring, elem, &desc);
+            ret = get_indirect(vring, elem, &desc);
             if (ret < 0) {
                 goto out;
             }