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