From 17b097bd0c30c3338787fd0901e237d8e6ae64ca Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 20 May 2014 10:37:34 +0200 Subject: [PATCH 06/26] usb: add support for microsoft os descriptors RH-Author: Gerd Hoffmann Message-id: <1400582257-1744-4-git-send-email-kraxel@redhat.com> Patchwork-id: 58963 O-Subject: [RHEL-6.6 qemu-kvm PATCH 3/6] usb: add support for microsoft os descriptors Bugzilla: 949547 RH-Acked-by: Laszlo Ersek RH-Acked-by: Paolo Bonzini RH-Acked-by: Markus Armbruster 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 (cherry picked from commit 5319dc7b42610575cbd3a33f4340c1fb4f19b939) Conflicts: hw/i386/pc_piix.c hw/usb-bus.c hw/usb-desc.c hw/usb/Makefile.objs include/hw/i386/pc.h include/hw/usb.h trace-events --- Makefile.objs | 2 +- hw/usb-bus.c | 2 + hw/usb-desc-msos.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb-desc.c | 38 ++++++++- hw/usb-desc.h | 11 ++- hw/usb.h | 7 ++ trace-events | 1 + 7 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 hw/usb-desc-msos.c Signed-off-by: Miroslav Rezanina --- Makefile.objs | 2 +- hw/usb-bus.c | 2 + hw/usb-desc-msos.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb-desc.c | 38 ++++++++- hw/usb-desc.h | 11 ++- hw/usb.h | 7 ++ trace-events | 1 + 7 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 hw/usb-desc-msos.c diff --git a/Makefile.objs b/Makefile.objs index 397be26..f28fd83 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -92,7 +92,7 @@ common-obj-y += i2c.o smbus.o smbus_eeprom.o common-obj-y += eeprom93xx.o common-obj-y += cdrom.o common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o -common-obj-y += usb-bus.o usb-desc.o +common-obj-y += usb-bus.o usb-desc.o usb-desc-msos.o common-obj-y += usb-msd.o scsi-bus.o scsi-disk.o scsi-generic.o common-obj-$(CONFIG_SSI) += ssi.o common-obj-$(CONFIG_SSI_SD) += ssi-sd.o diff --git a/hw/usb-bus.c b/hw/usb-bus.c index b0cb0e8..812eb5a 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -20,6 +20,8 @@ static struct BusInfo usb_bus_info = { .props = (Property[]) { DEFINE_PROP_STRING("port", USBDevice, port_path), DEFINE_PROP_UINT32("create_unique_serial", USBDevice, create_unique_serial, 1), + 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 0000000..2eee06b --- /dev/null +++ b/hw/usb-desc-msos.c @@ -0,0 +1,233 @@ +#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\\\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); + + return length; +} diff --git a/hw/usb-desc.c b/hw/usb-desc.c index 08c0863..a076aa2 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; @@ -253,6 +263,10 @@ void usb_desc_init(USBDevice *dev) if (desc->high) { dev->speedmask |= USB_SPEED_MASK_HIGH; } + 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); } @@ -377,6 +391,7 @@ 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) { const USBDesc *desc = dev->info->usb_desc; + bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE)); const USBDescDevice *other_dev; uint8_t buf[256]; uint8_t type = value >> 8; @@ -391,7 +406,7 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len 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: @@ -442,6 +457,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data) { const USBDesc *desc = dev->info->usb_desc; + bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE)); int i, ret = -1; assert(desc != NULL); @@ -495,6 +511,20 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, } trace_usb_set_device_feature(dev->addr, 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 8dcdf0c..150f088 100644 --- a/hw/usb-desc.h +++ b/hw/usb-desc.h @@ -2,6 +2,7 @@ #define QEMU_HW_USB_DESC_H #include +#include /* binary representation */ typedef struct USBDescriptor { @@ -141,6 +142,11 @@ struct USBDescOther { uint8_t *data; }; +struct USBDescMSOS { + const wchar_t *Label; + bool SelectiveSuspendEnabled; +}; + typedef const char *USBDescStrings[256]; struct USBDesc { @@ -148,6 +154,7 @@ struct USBDesc { const USBDescDevice *full; const USBDescDevice *high; const char* const *str; + const USBDescMSOS *msos; }; /* little helpers */ @@ -163,7 +170,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, uint8_t *dest, size_t len); @@ -172,6 +179,8 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, 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); +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.h b/hw/usb.h index bfe39fe..43cdd25 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -158,6 +158,7 @@ typedef struct USBDescIface USBDescIface; typedef struct USBDescEndpoint USBDescEndpoint; typedef struct USBDescOther USBDescOther; typedef struct USBDescString USBDescString; +typedef struct USBDescMSOS USBDescMSOS; struct USBDescString { uint8_t index; @@ -165,6 +166,11 @@ struct USBDescString { QLIST_ENTRY(USBDescString) next; }; +enum USBDeviceFlags { + USB_DEV_FLAG_MSOS_DESC_ENABLE, + USB_DEV_FLAG_MSOS_DESC_IN_USE, +}; + /* definition of a USB device */ struct USBDevice { DeviceState qdev; @@ -173,6 +179,7 @@ struct USBDevice { char *port_path; uint32_t create_unique_serial; void *opaque; + uint32_t flags; /* Actual connected speed */ int speed; diff --git a/trace-events b/trace-events index b7e02f7..f08a150 100644 --- a/trace-events +++ b/trace-events @@ -154,6 +154,7 @@ disable usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query devi disable usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" disable usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" disable usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d" +disable usb_desc_msos(int addr, int index, int len, int ret) "dev %d msos, index 0x%x, len %d, ret %d" disable usb_set_addr(int addr) "dev %d" disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" -- 1.7.1