| From b907cc56cbbb6b979d97aa0c64eb08c1fe7b6f1e Mon Sep 17 00:00:00 2001 |
| From: Gerd Hoffmann <kraxel@redhat.com> |
| Date: Mon, 3 Feb 2014 16:30:30 +0100 |
| Subject: [PATCH 06/28] usb: add support for microsoft os descriptors |
| |
| RH-Author: Gerd Hoffmann <kraxel@redhat.com> |
| Message-id: <1391445032-5540-4-git-send-email-kraxel@redhat.com> |
| Patchwork-id: 57075 |
| O-Subject: [RHEL-7 qemu-kvm PATCH 3/5] usb: add support for microsoft os descriptors |
| Bugzilla: 1039530 |
| RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> |
| RH-Acked-by: Juan Quintela <quintela@redhat.com> |
| RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com> |
| |
| 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> |
| (cherry picked from commit 5319dc7b42610575cbd3a33f4340c1fb4f19b939) |
| |
| Conflicts: |
| hw/i386/pc_piix.c [ 1.7 compat properties ] |
| include/hw/i386/pc.h [ likewise ] |
| |
| hw/usb/Makefile.objs | 2 +- |
| hw/usb/bus.c | 2 + |
| hw/usb/desc-msos.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++ |
| hw/usb/desc.c | 37 +++++++- |
| hw/usb/desc.h | 11 ++- |
| include/hw/usb.h | 3 + |
| trace-events | 1 + |
| 7 files changed, 284 insertions(+), 6 deletions(-) |
| create mode 100644 hw/usb/desc-msos.c |
| |
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> |
| |
| hw/usb/Makefile.objs | 2 +- |
| hw/usb/bus.c | 2 + |
| hw/usb/desc-msos.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++ |
| hw/usb/desc.c | 37 +++++++- |
| hw/usb/desc.h | 11 ++- |
| include/hw/usb.h | 3 + |
| trace-events | 1 + |
| 7 files changed, 284 insertions(+), 6 deletions(-) |
| create mode 100644 hw/usb/desc-msos.c |
| |
| diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs |
| index a3eac3e..97b4575 100644 |
| |
| |
| @@ -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 ade9abc..fe6bd13 100644 |
| |
| |
| @@ -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 0000000..ed8d62c |
| |
| |
| @@ -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 3e560cd..5a954c6 100644 |
| |
| |
| @@ -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; |
| @@ -497,6 +507,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); |
| } |
| |
| @@ -624,6 +638,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]; |
| @@ -644,7 +659,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: |
| @@ -701,6 +716,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; |
| |
| @@ -780,6 +796,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 81327b0..2b4fcda 100644 |
| |
| |
| @@ -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/include/hw/usb.h b/include/hw/usb.h |
| index a910456..b111be0 100644 |
| |
| |
| @@ -182,6 +182,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; |
| @@ -207,6 +208,8 @@ struct USBEndpoint { |
| enum USBDeviceFlags { |
| USB_DEV_FLAG_FULL_PATH, |
| USB_DEV_FLAG_IS_HOST, |
| + USB_DEV_FLAG_MSOS_DESC_ENABLE, |
| + USB_DEV_FLAG_MSOS_DESC_IN_USE, |
| }; |
| |
| /* definition of a USB device */ |
| diff --git a/trace-events b/trace-events |
| index dbbd25d..5d86cf3 100644 |
| |
| |
| @@ -393,6 +393,7 @@ usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, |
| usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" |
| usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d" |
| usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d" |
| +usb_desc_msos(int addr, int index, int len, int ret) "dev %d msos, index 0x%x, len %d, ret %d" |
| usb_set_addr(int addr) "dev %d" |
| usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" |
| usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d" |
| -- |
| 1.7.1 |
| |