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
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
usb_desc_msos_compat(const USBDesc * desc,uint8_t * dest)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
usb_desc_msos_prop_name(struct msos_prop * prop,const wchar_t * name)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
usb_desc_msos_prop_str(uint8_t * dest,msos_prop_type type,const wchar_t * name,const wchar_t * value)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
usb_desc_msos_prop_dword(uint8_t * dest,const wchar_t * name,uint32_t value)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
usb_desc_msos_prop(const USBDesc * desc,uint8_t * dest)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
usb_desc_msos(const USBDesc * desc,USBPacket * p,int index,uint8_t * dest,size_t len)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 }
240