#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); p->actual_length = length; return 0; }