| 1 | #include "qemu/osdep.h" |
| 2 | #include "hw/usb/usb.h" |
| 3 | #include "desc.h" |
| 4 | |
| 5 | /* |
| 6 | * Microsoft OS Descriptors |
| 7 | * |
| 8 | * Windows tries to fetch some special descriptors with information |
| 9 | * specifically for windows. Presence is indicated using a special |
| 10 | * string @ index 0xee. There are two kinds of descriptors: |
| 11 | * |
| 12 | * compatid descriptor |
| 13 | * Used to bind drivers, if usb class isn't specific enough. |
| 14 | * Used for PTP/MTP for example (both share the same usb class). |
| 15 | * |
| 16 | * properties descriptor |
| 17 | * Does carry registry entries. They show up in |
| 18 | * HLM\SYSTEM\CurrentControlSet\Enum\USB\<devid>\<serial>\Device Parameters |
| 19 | * |
| 20 | * Note that Windows caches the stuff it got in the registry, so when |
| 21 | * playing with this you have to delete registry subtrees to make |
| 22 | * windows query the device again: |
| 23 | * HLM\SYSTEM\CurrentControlSet\Control\usbflags |
| 24 | * HLM\SYSTEM\CurrentControlSet\Enum\USB |
| 25 | * Windows will complain it can't delete entries on the second one. |
| 26 | * It has deleted everything it had permissions too, which is enough |
| 27 | * as this includes "Device Parameters". |
| 28 | * |
| 29 | * http://msdn.microsoft.com/en-us/library/windows/hardware/ff537430.aspx |
| 30 | * |
| 31 | */ |
| 32 | |
| 33 | /* ------------------------------------------------------------------ */ |
| 34 | |
| 35 | typedef struct msos_compat_hdr { |
| 36 | uint32_t dwLength; |
| 37 | uint8_t bcdVersion_lo; |
| 38 | uint8_t bcdVersion_hi; |
| 39 | uint8_t wIndex_lo; |
| 40 | uint8_t wIndex_hi; |
| 41 | uint8_t bCount; |
| 42 | uint8_t reserved[7]; |
| 43 | } QEMU_PACKED msos_compat_hdr; |
| 44 | |
| 45 | typedef struct msos_compat_func { |
| 46 | uint8_t bFirstInterfaceNumber; |
| 47 | uint8_t reserved_1; |
| 48 | char compatibleId[8]; |
| 49 | uint8_t subCompatibleId[8]; |
| 50 | uint8_t reserved_2[6]; |
| 51 | } QEMU_PACKED msos_compat_func; |
| 52 | |
| 53 | static int usb_desc_msos_compat(const USBDesc *desc, uint8_t *dest) |
| 54 | { |
| 55 | msos_compat_hdr *hdr = (void *)dest; |
| 56 | msos_compat_func *func; |
| 57 | int length = sizeof(*hdr); |
| 58 | int count = 0; |
| 59 | |
| 60 | func = (void *)(dest + length); |
| 61 | func->bFirstInterfaceNumber = 0; |
| 62 | func->reserved_1 = 0x01; |
| 63 | if (desc->msos->CompatibleID) { |
| 64 | snprintf(func->compatibleId, sizeof(func->compatibleId), |
| 65 | "%s", desc->msos->CompatibleID); |
| 66 | } |
| 67 | length += sizeof(*func); |
| 68 | count++; |
| 69 | |
| 70 | hdr->dwLength = cpu_to_le32(length); |
| 71 | hdr->bcdVersion_lo = 0x00; |
| 72 | hdr->bcdVersion_hi = 0x01; |
| 73 | hdr->wIndex_lo = 0x04; |
| 74 | hdr->wIndex_hi = 0x00; |
| 75 | hdr->bCount = count; |
| 76 | return length; |
| 77 | } |
| 78 | |
| 79 | /* ------------------------------------------------------------------ */ |
| 80 | |
| 81 | typedef struct msos_prop_hdr { |
| 82 | uint32_t dwLength; |
| 83 | uint8_t bcdVersion_lo; |
| 84 | uint8_t bcdVersion_hi; |
| 85 | uint8_t wIndex_lo; |
| 86 | uint8_t wIndex_hi; |
| 87 | uint8_t wCount_lo; |
| 88 | uint8_t wCount_hi; |
| 89 | } QEMU_PACKED msos_prop_hdr; |
| 90 | |
| 91 | typedef struct msos_prop { |
| 92 | uint32_t dwLength; |
| 93 | uint32_t dwPropertyDataType; |
| 94 | uint8_t dwPropertyNameLength_lo; |
| 95 | uint8_t dwPropertyNameLength_hi; |
| 96 | uint8_t bPropertyName[]; |
| 97 | } QEMU_PACKED msos_prop; |
| 98 | |
| 99 | typedef struct msos_prop_data { |
| 100 | uint32_t dwPropertyDataLength; |
| 101 | uint8_t bPropertyData[]; |
| 102 | } QEMU_PACKED msos_prop_data; |
| 103 | |
| 104 | typedef enum msos_prop_type { |
| 105 | MSOS_REG_SZ = 1, |
| 106 | MSOS_REG_EXPAND_SZ = 2, |
| 107 | MSOS_REG_BINARY = 3, |
| 108 | MSOS_REG_DWORD_LE = 4, |
| 109 | MSOS_REG_DWORD_BE = 5, |
| 110 | MSOS_REG_LINK = 6, |
| 111 | MSOS_REG_MULTI_SZ = 7, |
| 112 | } msos_prop_type; |
| 113 | |
| 114 | static int usb_desc_msos_prop_name(struct msos_prop *prop, |
| 115 | const wchar_t *name) |
| 116 | { |
| 117 | int length = wcslen(name) + 1; |
| 118 | int i; |
| 119 | |
| 120 | prop->dwPropertyNameLength_lo = usb_lo(length*2); |
| 121 | prop->dwPropertyNameLength_hi = usb_hi(length*2); |
| 122 | for (i = 0; i < length; i++) { |
| 123 | prop->bPropertyName[i*2] = usb_lo(name[i]); |
| 124 | prop->bPropertyName[i*2+1] = usb_hi(name[i]); |
| 125 | } |
| 126 | return length*2; |
| 127 | } |
| 128 | |
| 129 | static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type, |
| 130 | const wchar_t *name, const wchar_t *value) |
| 131 | { |
| 132 | struct msos_prop *prop = (void *)dest; |
| 133 | struct msos_prop_data *data; |
| 134 | int length = sizeof(*prop); |
| 135 | int i, vlen = wcslen(value) + 1; |
| 136 | |
| 137 | prop->dwPropertyDataType = cpu_to_le32(type); |
| 138 | length += usb_desc_msos_prop_name(prop, name); |
| 139 | data = (void *)(dest + length); |
| 140 | |
| 141 | data->dwPropertyDataLength = cpu_to_le32(vlen*2); |
| 142 | length += sizeof(*prop); |
| 143 | |
| 144 | for (i = 0; i < vlen; i++) { |
| 145 | data->bPropertyData[i*2] = usb_lo(value[i]); |
| 146 | data->bPropertyData[i*2+1] = usb_hi(value[i]); |
| 147 | } |
| 148 | length += vlen*2; |
| 149 | |
| 150 | prop->dwLength = cpu_to_le32(length); |
| 151 | return length; |
| 152 | } |
| 153 | |
| 154 | static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name, |
| 155 | uint32_t value) |
| 156 | { |
| 157 | struct msos_prop *prop = (void *)dest; |
| 158 | struct msos_prop_data *data; |
| 159 | int length = sizeof(*prop); |
| 160 | |
| 161 | prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE); |
| 162 | length += usb_desc_msos_prop_name(prop, name); |
| 163 | data = (void *)(dest + length); |
| 164 | |
| 165 | data->dwPropertyDataLength = cpu_to_le32(4); |
| 166 | data->bPropertyData[0] = (value) & 0xff; |
| 167 | data->bPropertyData[1] = (value >> 8) & 0xff; |
| 168 | data->bPropertyData[2] = (value >> 16) & 0xff; |
| 169 | data->bPropertyData[3] = (value >> 24) & 0xff; |
| 170 | length += sizeof(*prop) + 4; |
| 171 | |
| 172 | prop->dwLength = cpu_to_le32(length); |
| 173 | return length; |
| 174 | } |
| 175 | |
| 176 | static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest) |
| 177 | { |
| 178 | msos_prop_hdr *hdr = (void *)dest; |
| 179 | int length = sizeof(*hdr); |
| 180 | int count = 0; |
| 181 | |
| 182 | if (desc->msos->Label) { |
| 183 | /* |
| 184 | * Given as example in the specs. Haven't figured yet where |
| 185 | * this label shows up in the windows gui. |
| 186 | */ |
| 187 | length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ, |
| 188 | L"Label", desc->msos->Label); |
| 189 | count++; |
| 190 | } |
| 191 | |
| 192 | if (desc->msos->SelectiveSuspendEnabled) { |
| 193 | /* |
| 194 | * Signaling remote wakeup capability in the standard usb |
| 195 | * descriptors isn't enough to make windows actually use it. |
| 196 | * This is the "Yes, we really mean it" registry entry to flip |
| 197 | * the switch in the windows drivers. |
| 198 | */ |
| 199 | length += usb_desc_msos_prop_dword(dest+length, |
| 200 | L"SelectiveSuspendEnabled", 1); |
| 201 | count++; |
| 202 | } |
| 203 | |
| 204 | hdr->dwLength = cpu_to_le32(length); |
| 205 | hdr->bcdVersion_lo = 0x00; |
| 206 | hdr->bcdVersion_hi = 0x01; |
| 207 | hdr->wIndex_lo = 0x05; |
| 208 | hdr->wIndex_hi = 0x00; |
| 209 | hdr->wCount_lo = usb_lo(count); |
| 210 | hdr->wCount_hi = usb_hi(count); |
| 211 | return length; |
| 212 | } |
| 213 | |
| 214 | /* ------------------------------------------------------------------ */ |
| 215 | |
| 216 | int usb_desc_msos(const USBDesc *desc, USBPacket *p, |
| 217 | int index, uint8_t *dest, size_t len) |
| 218 | { |
| 219 | void *buf = g_malloc0(4096); |
| 220 | int length = 0; |
| 221 | |
| 222 | switch (index) { |
| 223 | case 0x0004: |
| 224 | length = usb_desc_msos_compat(desc, buf); |
| 225 | break; |
| 226 | case 0x0005: |
| 227 | length = usb_desc_msos_prop(desc, buf); |
| 228 | break; |
| 229 | } |
| 230 | |
| 231 | if (length > len) { |
| 232 | length = len; |
| 233 | } |
| 234 | memcpy(dest, buf, length); |
| 235 | g_free(buf); |
| 236 | |
| 237 | p->actual_length = length; |
| 238 | return 0; |
| 239 | } |