]>
Commit | Line | Data |
---|---|---|
e532b2e0 | 1 | #include "qemu/osdep.h" |
5319dc7b | 2 | #include "hw/usb.h" |
463581a8 | 3 | #include "desc.h" |
5319dc7b GH |
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; | |
409951f5 | 48 | char compatibleId[8]; |
5319dc7b GH |
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; | |
409951f5 GH |
63 | if (desc->msos->CompatibleID) { |
64 | snprintf(func->compatibleId, sizeof(func->compatibleId), | |
65 | "%s", desc->msos->CompatibleID); | |
66 | } | |
5319dc7b GH |
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); | |
0c6f807f | 235 | g_free(buf); |
5319dc7b GH |
236 | |
237 | p->actual_length = length; | |
238 | return 0; | |
239 | } |