| #include "qemu/osdep.h" |
| #include "hw/usb.h" |
| #include "desc.h" |
| |
| /* |
| * Microsoft OS Descriptors |
| * |
| * Windows tries to fetch some special descriptors with information |
| * 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 enough. |
| * 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 enough |
| * 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. Haven'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 enough to make windows actually use it. |
| * This is the "Yes, we really mean it" registry 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; |
| } |