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 informations |
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 enougth. |
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 enouth |
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. Havn'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 enouth to make windows actually use it. |
196 | * This is the "Yes, we really mean it" registy 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 | } |
240 | |