summaryrefslogtreecommitdiff
path: root/hw/usb
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2013-11-20 07:32:31 +0100
committerGerd Hoffmann <kraxel@redhat.com>2014-01-16 12:59:59 +0100
commit5319dc7b42610575cbd3a33f4340c1fb4f19b939 (patch)
tree5f43fc075e28b242bee3696cfa245c468feb26f1 /hw/usb
parent1cf892ca2689c84960b4ce4d2723b6bee453711c (diff)
downloadqemu-5319dc7b42610575cbd3a33f4340c1fb4f19b939.tar.gz
usb: add support for microsoft os descriptors
This patch adds support for special usb descriptors used by microsoft windows. They allow more fine-grained control over driver binding and adding entries to the registry for configuration. As this is a guest-visible change the "msos-desc" compat property has been added to turn this off for 1.7 + older Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb')
-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
5 files changed, 280 insertions, 6 deletions
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);