desc-msos.c (7038B)
1#include "qemu/osdep.h" 2#include "hw/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 35typedef 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 45typedef 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 53static 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 81typedef 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 91typedef 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 99typedef struct msos_prop_data { 100 uint32_t dwPropertyDataLength; 101 uint8_t bPropertyData[]; 102} QEMU_PACKED msos_prop_data; 103 104typedef 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 114static 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 129static 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 154static 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 176static 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 216int 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}