]>
Commit | Line | Data |
---|---|---|
5319dc7b GH |
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; | |
409951f5 | 47 | char compatibleId[8]; |
5319dc7b GH |
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; | |
409951f5 GH |
62 | if (desc->msos->CompatibleID) { |
63 | snprintf(func->compatibleId, sizeof(func->compatibleId), | |
64 | "%s", desc->msos->CompatibleID); | |
65 | } | |
5319dc7b GH |
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 | } |