xref: /openbmc/qemu/hw/usb/desc-msos.c (revision 6016b7b4)
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 
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.  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 
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