xref: /openbmc/qemu/hw/usb/desc-msos.c (revision 52f91c37)
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;
47     char     compatibleId[8];
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;
62     if (desc->msos->CompatibleID) {
63         snprintf(func->compatibleId, sizeof(func->compatibleId),
64                  "%s", desc->msos->CompatibleID);
65     }
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 }
239