240 lines
6.9 KiB
C
240 lines
6.9 KiB
C
#include "qemu/osdep.h"
|
|
#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;
|
|
char 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;
|
|
if (desc->msos->CompatibleID) {
|
|
snprintf(func->compatibleId, sizeof(func->compatibleId),
|
|
"%s", desc->msos->CompatibleID);
|
|
}
|
|
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);
|
|
g_free(buf);
|
|
|
|
p->actual_length = length;
|
|
return 0;
|
|
}
|