Skip to content

Commit

Permalink
Merge remote-tracking branch 'kraxel/tags/pull-usb-2' into staging
Browse files Browse the repository at this point in the history
usb core+hid: add support for microsoft os descriptors

# gpg: Signature made Tue 21 Jan 2014 02:21:29 AM PST using RSA key ID D3E87138
# gpg: Can't check signature: public key not found

* kraxel/tags/pull-usb-2:
  usb-hid: add microsoft os descriptor support
  usb: add support for microsoft os descriptors

Message-id: 1390299772-5368-1-git-send-email-kraxel@redhat.com
Signed-off-by: Anthony Liguori <aliguori@amazon.com>
  • Loading branch information
Anthony Liguori committed Jan 24, 2014
2 parents e9f526a + 88678fb commit f4b2779
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 6 deletions.
4 changes: 4 additions & 0 deletions hw/i386/pc_piix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion hw/usb/Makefile.objs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 2 additions & 0 deletions hw/usb/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()
};

Expand Down
234 changes: 234 additions & 0 deletions hw/usb/desc-msos.c
Original file line number Diff line number Diff line change
@@ -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;
}
37 changes: 33 additions & 4 deletions hw/usb/desc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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];
Expand All @@ -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:
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
11 changes: 10 additions & 1 deletion hw/usb/desc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define QEMU_HW_USB_DESC_H

#include <inttypes.h>
#include <wchar.h>

/* binary representation */
typedef struct USBDescriptor {
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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,
Expand All @@ -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);
Expand Down
Loading

0 comments on commit f4b2779

Please sign in to comment.