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 char 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 if (desc->msos->CompatibleID) { 63 snprintf(func->compatibleId, sizeof(func->compatibleId), 64 "%s", desc->msos->CompatibleID); 65 } 66 length += sizeof(*func); 67 count++; 68 69 hdr->dwLength = cpu_to_le32(length); 70 hdr->bcdVersion_lo = 0x00; 71 hdr->bcdVersion_hi = 0x01; 72 hdr->wIndex_lo = 0x04; 73 hdr->wIndex_hi = 0x00; 74 hdr->bCount = count; 75 return length; 76 } 77 78 /* ------------------------------------------------------------------ */ 79 80 typedef struct msos_prop_hdr { 81 uint32_t dwLength; 82 uint8_t bcdVersion_lo; 83 uint8_t bcdVersion_hi; 84 uint8_t wIndex_lo; 85 uint8_t wIndex_hi; 86 uint8_t wCount_lo; 87 uint8_t wCount_hi; 88 } QEMU_PACKED msos_prop_hdr; 89 90 typedef struct msos_prop { 91 uint32_t dwLength; 92 uint32_t dwPropertyDataType; 93 uint8_t dwPropertyNameLength_lo; 94 uint8_t dwPropertyNameLength_hi; 95 uint8_t bPropertyName[]; 96 } QEMU_PACKED msos_prop; 97 98 typedef struct msos_prop_data { 99 uint32_t dwPropertyDataLength; 100 uint8_t bPropertyData[]; 101 } QEMU_PACKED msos_prop_data; 102 103 typedef enum msos_prop_type { 104 MSOS_REG_SZ = 1, 105 MSOS_REG_EXPAND_SZ = 2, 106 MSOS_REG_BINARY = 3, 107 MSOS_REG_DWORD_LE = 4, 108 MSOS_REG_DWORD_BE = 5, 109 MSOS_REG_LINK = 6, 110 MSOS_REG_MULTI_SZ = 7, 111 } msos_prop_type; 112 113 static int usb_desc_msos_prop_name(struct msos_prop *prop, 114 const wchar_t *name) 115 { 116 int length = wcslen(name) + 1; 117 int i; 118 119 prop->dwPropertyNameLength_lo = usb_lo(length*2); 120 prop->dwPropertyNameLength_hi = usb_hi(length*2); 121 for (i = 0; i < length; i++) { 122 prop->bPropertyName[i*2] = usb_lo(name[i]); 123 prop->bPropertyName[i*2+1] = usb_hi(name[i]); 124 } 125 return length*2; 126 } 127 128 static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type, 129 const wchar_t *name, const wchar_t *value) 130 { 131 struct msos_prop *prop = (void *)dest; 132 struct msos_prop_data *data; 133 int length = sizeof(*prop); 134 int i, vlen = wcslen(value) + 1; 135 136 prop->dwPropertyDataType = cpu_to_le32(type); 137 length += usb_desc_msos_prop_name(prop, name); 138 data = (void *)(dest + length); 139 140 data->dwPropertyDataLength = cpu_to_le32(vlen*2); 141 length += sizeof(*prop); 142 143 for (i = 0; i < vlen; i++) { 144 data->bPropertyData[i*2] = usb_lo(value[i]); 145 data->bPropertyData[i*2+1] = usb_hi(value[i]); 146 } 147 length += vlen*2; 148 149 prop->dwLength = cpu_to_le32(length); 150 return length; 151 } 152 153 static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name, 154 uint32_t value) 155 { 156 struct msos_prop *prop = (void *)dest; 157 struct msos_prop_data *data; 158 int length = sizeof(*prop); 159 160 prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE); 161 length += usb_desc_msos_prop_name(prop, name); 162 data = (void *)(dest + length); 163 164 data->dwPropertyDataLength = cpu_to_le32(4); 165 data->bPropertyData[0] = (value) & 0xff; 166 data->bPropertyData[1] = (value >> 8) & 0xff; 167 data->bPropertyData[2] = (value >> 16) & 0xff; 168 data->bPropertyData[3] = (value >> 24) & 0xff; 169 length += sizeof(*prop) + 4; 170 171 prop->dwLength = cpu_to_le32(length); 172 return length; 173 } 174 175 static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest) 176 { 177 msos_prop_hdr *hdr = (void *)dest; 178 int length = sizeof(*hdr); 179 int count = 0; 180 181 if (desc->msos->Label) { 182 /* 183 * Given as example in the specs. Havn't figured yet where 184 * this label shows up in the windows gui. 185 */ 186 length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ, 187 L"Label", desc->msos->Label); 188 count++; 189 } 190 191 if (desc->msos->SelectiveSuspendEnabled) { 192 /* 193 * Signaling remote wakeup capability in the standard usb 194 * descriptors isn't enouth to make windows actually use it. 195 * This is the "Yes, we really mean it" registy entry to flip 196 * the switch in the windows drivers. 197 */ 198 length += usb_desc_msos_prop_dword(dest+length, 199 L"SelectiveSuspendEnabled", 1); 200 count++; 201 } 202 203 hdr->dwLength = cpu_to_le32(length); 204 hdr->bcdVersion_lo = 0x00; 205 hdr->bcdVersion_hi = 0x01; 206 hdr->wIndex_lo = 0x05; 207 hdr->wIndex_hi = 0x00; 208 hdr->wCount_lo = usb_lo(count); 209 hdr->wCount_hi = usb_hi(count); 210 return length; 211 } 212 213 /* ------------------------------------------------------------------ */ 214 215 int usb_desc_msos(const USBDesc *desc, USBPacket *p, 216 int index, uint8_t *dest, size_t len) 217 { 218 void *buf = g_malloc0(4096); 219 int length = 0; 220 221 switch (index) { 222 case 0x0004: 223 length = usb_desc_msos_compat(desc, buf); 224 break; 225 case 0x0005: 226 length = usb_desc_msos_prop(desc, buf); 227 break; 228 } 229 230 if (length > len) { 231 length = len; 232 } 233 memcpy(dest, buf, length); 234 free(buf); 235 236 p->actual_length = length; 237 return 0; 238 } 239