xref: /qemu/hw/usb/desc-msos.c (revision ae420c95)
1e532b2e0SPeter Maydell #include "qemu/osdep.h"
25319dc7bSGerd Hoffmann #include "hw/usb.h"
3463581a8SMichael S. Tsirkin #include "desc.h"
45319dc7bSGerd Hoffmann 
55319dc7bSGerd Hoffmann /*
65319dc7bSGerd Hoffmann  * Microsoft OS Descriptors
75319dc7bSGerd Hoffmann  *
8*ae420c95SCai Huoqing  * Windows tries to fetch some special descriptors with information
95319dc7bSGerd Hoffmann  * specifically for windows.  Presence is indicated using a special
105319dc7bSGerd Hoffmann  * string @ index 0xee.  There are two kinds of descriptors:
115319dc7bSGerd Hoffmann  *
125319dc7bSGerd Hoffmann  * compatid descriptor
13*ae420c95SCai Huoqing  *   Used to bind drivers, if usb class isn't specific enough.
145319dc7bSGerd Hoffmann  *   Used for PTP/MTP for example (both share the same usb class).
155319dc7bSGerd Hoffmann  *
165319dc7bSGerd Hoffmann  * properties descriptor
175319dc7bSGerd Hoffmann  *   Does carry registry entries.  They show up in
185319dc7bSGerd Hoffmann  *   HLM\SYSTEM\CurrentControlSet\Enum\USB\<devid>\<serial>\Device Parameters
195319dc7bSGerd Hoffmann  *
205319dc7bSGerd Hoffmann  * Note that Windows caches the stuff it got in the registry, so when
215319dc7bSGerd Hoffmann  * playing with this you have to delete registry subtrees to make
225319dc7bSGerd Hoffmann  * windows query the device again:
235319dc7bSGerd Hoffmann  *   HLM\SYSTEM\CurrentControlSet\Control\usbflags
245319dc7bSGerd Hoffmann  *   HLM\SYSTEM\CurrentControlSet\Enum\USB
255319dc7bSGerd Hoffmann  * Windows will complain it can't delete entries on the second one.
26*ae420c95SCai Huoqing  * It has deleted everything it had permissions too, which is enough
275319dc7bSGerd Hoffmann  * as this includes "Device Parameters".
285319dc7bSGerd Hoffmann  *
295319dc7bSGerd Hoffmann  * http://msdn.microsoft.com/en-us/library/windows/hardware/ff537430.aspx
305319dc7bSGerd Hoffmann  *
315319dc7bSGerd Hoffmann  */
325319dc7bSGerd Hoffmann 
335319dc7bSGerd Hoffmann /* ------------------------------------------------------------------ */
345319dc7bSGerd Hoffmann 
355319dc7bSGerd Hoffmann typedef struct msos_compat_hdr {
365319dc7bSGerd Hoffmann     uint32_t dwLength;
375319dc7bSGerd Hoffmann     uint8_t  bcdVersion_lo;
385319dc7bSGerd Hoffmann     uint8_t  bcdVersion_hi;
395319dc7bSGerd Hoffmann     uint8_t  wIndex_lo;
405319dc7bSGerd Hoffmann     uint8_t  wIndex_hi;
415319dc7bSGerd Hoffmann     uint8_t  bCount;
425319dc7bSGerd Hoffmann     uint8_t  reserved[7];
435319dc7bSGerd Hoffmann } QEMU_PACKED msos_compat_hdr;
445319dc7bSGerd Hoffmann 
455319dc7bSGerd Hoffmann typedef struct msos_compat_func {
465319dc7bSGerd Hoffmann     uint8_t  bFirstInterfaceNumber;
475319dc7bSGerd Hoffmann     uint8_t  reserved_1;
48409951f5SGerd Hoffmann     char     compatibleId[8];
495319dc7bSGerd Hoffmann     uint8_t  subCompatibleId[8];
505319dc7bSGerd Hoffmann     uint8_t  reserved_2[6];
515319dc7bSGerd Hoffmann } QEMU_PACKED msos_compat_func;
525319dc7bSGerd Hoffmann 
usb_desc_msos_compat(const USBDesc * desc,uint8_t * dest)535319dc7bSGerd Hoffmann static int usb_desc_msos_compat(const USBDesc *desc, uint8_t *dest)
545319dc7bSGerd Hoffmann {
555319dc7bSGerd Hoffmann     msos_compat_hdr *hdr = (void *)dest;
565319dc7bSGerd Hoffmann     msos_compat_func *func;
575319dc7bSGerd Hoffmann     int length = sizeof(*hdr);
585319dc7bSGerd Hoffmann     int count = 0;
595319dc7bSGerd Hoffmann 
605319dc7bSGerd Hoffmann     func = (void *)(dest + length);
615319dc7bSGerd Hoffmann     func->bFirstInterfaceNumber = 0;
625319dc7bSGerd Hoffmann     func->reserved_1 = 0x01;
63409951f5SGerd Hoffmann     if (desc->msos->CompatibleID) {
64409951f5SGerd Hoffmann         snprintf(func->compatibleId, sizeof(func->compatibleId),
65409951f5SGerd Hoffmann                  "%s", desc->msos->CompatibleID);
66409951f5SGerd Hoffmann     }
675319dc7bSGerd Hoffmann     length += sizeof(*func);
685319dc7bSGerd Hoffmann     count++;
695319dc7bSGerd Hoffmann 
705319dc7bSGerd Hoffmann     hdr->dwLength      = cpu_to_le32(length);
715319dc7bSGerd Hoffmann     hdr->bcdVersion_lo = 0x00;
725319dc7bSGerd Hoffmann     hdr->bcdVersion_hi = 0x01;
735319dc7bSGerd Hoffmann     hdr->wIndex_lo     = 0x04;
745319dc7bSGerd Hoffmann     hdr->wIndex_hi     = 0x00;
755319dc7bSGerd Hoffmann     hdr->bCount        = count;
765319dc7bSGerd Hoffmann     return length;
775319dc7bSGerd Hoffmann }
785319dc7bSGerd Hoffmann 
795319dc7bSGerd Hoffmann /* ------------------------------------------------------------------ */
805319dc7bSGerd Hoffmann 
815319dc7bSGerd Hoffmann typedef struct msos_prop_hdr {
825319dc7bSGerd Hoffmann     uint32_t dwLength;
835319dc7bSGerd Hoffmann     uint8_t  bcdVersion_lo;
845319dc7bSGerd Hoffmann     uint8_t  bcdVersion_hi;
855319dc7bSGerd Hoffmann     uint8_t  wIndex_lo;
865319dc7bSGerd Hoffmann     uint8_t  wIndex_hi;
875319dc7bSGerd Hoffmann     uint8_t  wCount_lo;
885319dc7bSGerd Hoffmann     uint8_t  wCount_hi;
895319dc7bSGerd Hoffmann } QEMU_PACKED msos_prop_hdr;
905319dc7bSGerd Hoffmann 
915319dc7bSGerd Hoffmann typedef struct msos_prop {
925319dc7bSGerd Hoffmann     uint32_t dwLength;
935319dc7bSGerd Hoffmann     uint32_t dwPropertyDataType;
945319dc7bSGerd Hoffmann     uint8_t  dwPropertyNameLength_lo;
955319dc7bSGerd Hoffmann     uint8_t  dwPropertyNameLength_hi;
965319dc7bSGerd Hoffmann     uint8_t  bPropertyName[];
975319dc7bSGerd Hoffmann } QEMU_PACKED msos_prop;
985319dc7bSGerd Hoffmann 
995319dc7bSGerd Hoffmann typedef struct msos_prop_data {
1005319dc7bSGerd Hoffmann     uint32_t dwPropertyDataLength;
1015319dc7bSGerd Hoffmann     uint8_t  bPropertyData[];
1025319dc7bSGerd Hoffmann } QEMU_PACKED msos_prop_data;
1035319dc7bSGerd Hoffmann 
1045319dc7bSGerd Hoffmann typedef enum msos_prop_type {
1055319dc7bSGerd Hoffmann     MSOS_REG_SZ        = 1,
1065319dc7bSGerd Hoffmann     MSOS_REG_EXPAND_SZ = 2,
1075319dc7bSGerd Hoffmann     MSOS_REG_BINARY    = 3,
1085319dc7bSGerd Hoffmann     MSOS_REG_DWORD_LE  = 4,
1095319dc7bSGerd Hoffmann     MSOS_REG_DWORD_BE  = 5,
1105319dc7bSGerd Hoffmann     MSOS_REG_LINK      = 6,
1115319dc7bSGerd Hoffmann     MSOS_REG_MULTI_SZ  = 7,
1125319dc7bSGerd Hoffmann } msos_prop_type;
1135319dc7bSGerd Hoffmann 
usb_desc_msos_prop_name(struct msos_prop * prop,const wchar_t * name)1145319dc7bSGerd Hoffmann static int usb_desc_msos_prop_name(struct msos_prop *prop,
1155319dc7bSGerd Hoffmann                                    const wchar_t *name)
1165319dc7bSGerd Hoffmann {
1175319dc7bSGerd Hoffmann     int length = wcslen(name) + 1;
1185319dc7bSGerd Hoffmann     int i;
1195319dc7bSGerd Hoffmann 
1205319dc7bSGerd Hoffmann     prop->dwPropertyNameLength_lo = usb_lo(length*2);
1215319dc7bSGerd Hoffmann     prop->dwPropertyNameLength_hi = usb_hi(length*2);
1225319dc7bSGerd Hoffmann     for (i = 0; i < length; i++) {
1235319dc7bSGerd Hoffmann         prop->bPropertyName[i*2]   = usb_lo(name[i]);
1245319dc7bSGerd Hoffmann         prop->bPropertyName[i*2+1] = usb_hi(name[i]);
1255319dc7bSGerd Hoffmann     }
1265319dc7bSGerd Hoffmann     return length*2;
1275319dc7bSGerd Hoffmann }
1285319dc7bSGerd Hoffmann 
usb_desc_msos_prop_str(uint8_t * dest,msos_prop_type type,const wchar_t * name,const wchar_t * value)1295319dc7bSGerd Hoffmann static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type,
1305319dc7bSGerd Hoffmann                                   const wchar_t *name, const wchar_t *value)
1315319dc7bSGerd Hoffmann {
1325319dc7bSGerd Hoffmann     struct msos_prop *prop = (void *)dest;
1335319dc7bSGerd Hoffmann     struct msos_prop_data *data;
1345319dc7bSGerd Hoffmann     int length = sizeof(*prop);
1355319dc7bSGerd Hoffmann     int i, vlen = wcslen(value) + 1;
1365319dc7bSGerd Hoffmann 
1375319dc7bSGerd Hoffmann     prop->dwPropertyDataType = cpu_to_le32(type);
1385319dc7bSGerd Hoffmann     length += usb_desc_msos_prop_name(prop, name);
1395319dc7bSGerd Hoffmann     data = (void *)(dest + length);
1405319dc7bSGerd Hoffmann 
1415319dc7bSGerd Hoffmann     data->dwPropertyDataLength = cpu_to_le32(vlen*2);
1425319dc7bSGerd Hoffmann     length += sizeof(*prop);
1435319dc7bSGerd Hoffmann 
1445319dc7bSGerd Hoffmann     for (i = 0; i < vlen; i++) {
1455319dc7bSGerd Hoffmann         data->bPropertyData[i*2]   = usb_lo(value[i]);
1465319dc7bSGerd Hoffmann         data->bPropertyData[i*2+1] = usb_hi(value[i]);
1475319dc7bSGerd Hoffmann     }
1485319dc7bSGerd Hoffmann     length += vlen*2;
1495319dc7bSGerd Hoffmann 
1505319dc7bSGerd Hoffmann     prop->dwLength = cpu_to_le32(length);
1515319dc7bSGerd Hoffmann     return length;
1525319dc7bSGerd Hoffmann }
1535319dc7bSGerd Hoffmann 
usb_desc_msos_prop_dword(uint8_t * dest,const wchar_t * name,uint32_t value)1545319dc7bSGerd Hoffmann static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name,
1555319dc7bSGerd Hoffmann                                     uint32_t value)
1565319dc7bSGerd Hoffmann {
1575319dc7bSGerd Hoffmann     struct msos_prop *prop = (void *)dest;
1585319dc7bSGerd Hoffmann     struct msos_prop_data *data;
1595319dc7bSGerd Hoffmann     int length = sizeof(*prop);
1605319dc7bSGerd Hoffmann 
1615319dc7bSGerd Hoffmann     prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE);
1625319dc7bSGerd Hoffmann     length += usb_desc_msos_prop_name(prop, name);
1635319dc7bSGerd Hoffmann     data = (void *)(dest + length);
1645319dc7bSGerd Hoffmann 
1655319dc7bSGerd Hoffmann     data->dwPropertyDataLength = cpu_to_le32(4);
1665319dc7bSGerd Hoffmann     data->bPropertyData[0] = (value)       & 0xff;
1675319dc7bSGerd Hoffmann     data->bPropertyData[1] = (value >>  8) & 0xff;
1685319dc7bSGerd Hoffmann     data->bPropertyData[2] = (value >> 16) & 0xff;
1695319dc7bSGerd Hoffmann     data->bPropertyData[3] = (value >> 24) & 0xff;
1705319dc7bSGerd Hoffmann     length += sizeof(*prop) + 4;
1715319dc7bSGerd Hoffmann 
1725319dc7bSGerd Hoffmann     prop->dwLength = cpu_to_le32(length);
1735319dc7bSGerd Hoffmann     return length;
1745319dc7bSGerd Hoffmann }
1755319dc7bSGerd Hoffmann 
usb_desc_msos_prop(const USBDesc * desc,uint8_t * dest)1765319dc7bSGerd Hoffmann static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest)
1775319dc7bSGerd Hoffmann {
1785319dc7bSGerd Hoffmann     msos_prop_hdr *hdr = (void *)dest;
1795319dc7bSGerd Hoffmann     int length = sizeof(*hdr);
1805319dc7bSGerd Hoffmann     int count = 0;
1815319dc7bSGerd Hoffmann 
1825319dc7bSGerd Hoffmann     if (desc->msos->Label) {
1835319dc7bSGerd Hoffmann         /*
184a4761232SPhilippe Mathieu-Daudé          * Given as example in the specs.  Haven't figured yet where
1855319dc7bSGerd Hoffmann          * this label shows up in the windows gui.
1865319dc7bSGerd Hoffmann          */
1875319dc7bSGerd Hoffmann         length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ,
1885319dc7bSGerd Hoffmann                                          L"Label", desc->msos->Label);
1895319dc7bSGerd Hoffmann         count++;
1905319dc7bSGerd Hoffmann     }
1915319dc7bSGerd Hoffmann 
1925319dc7bSGerd Hoffmann     if (desc->msos->SelectiveSuspendEnabled) {
1935319dc7bSGerd Hoffmann         /*
1945319dc7bSGerd Hoffmann          * Signaling remote wakeup capability in the standard usb
195*ae420c95SCai Huoqing          * descriptors isn't enough to make windows actually use it.
196*ae420c95SCai Huoqing          * This is the "Yes, we really mean it" registry entry to flip
1975319dc7bSGerd Hoffmann          * the switch in the windows drivers.
1985319dc7bSGerd Hoffmann          */
1995319dc7bSGerd Hoffmann         length += usb_desc_msos_prop_dword(dest+length,
2005319dc7bSGerd Hoffmann                                            L"SelectiveSuspendEnabled", 1);
2015319dc7bSGerd Hoffmann         count++;
2025319dc7bSGerd Hoffmann     }
2035319dc7bSGerd Hoffmann 
2045319dc7bSGerd Hoffmann     hdr->dwLength      = cpu_to_le32(length);
2055319dc7bSGerd Hoffmann     hdr->bcdVersion_lo = 0x00;
2065319dc7bSGerd Hoffmann     hdr->bcdVersion_hi = 0x01;
2075319dc7bSGerd Hoffmann     hdr->wIndex_lo     = 0x05;
2085319dc7bSGerd Hoffmann     hdr->wIndex_hi     = 0x00;
2095319dc7bSGerd Hoffmann     hdr->wCount_lo     = usb_lo(count);
2105319dc7bSGerd Hoffmann     hdr->wCount_hi     = usb_hi(count);
2115319dc7bSGerd Hoffmann     return length;
2125319dc7bSGerd Hoffmann }
2135319dc7bSGerd Hoffmann 
2145319dc7bSGerd Hoffmann /* ------------------------------------------------------------------ */
2155319dc7bSGerd Hoffmann 
usb_desc_msos(const USBDesc * desc,USBPacket * p,int index,uint8_t * dest,size_t len)2165319dc7bSGerd Hoffmann int usb_desc_msos(const USBDesc *desc,  USBPacket *p,
2175319dc7bSGerd Hoffmann                   int index, uint8_t *dest, size_t len)
2185319dc7bSGerd Hoffmann {
2195319dc7bSGerd Hoffmann     void *buf = g_malloc0(4096);
2205319dc7bSGerd Hoffmann     int length = 0;
2215319dc7bSGerd Hoffmann 
2225319dc7bSGerd Hoffmann     switch (index) {
2235319dc7bSGerd Hoffmann     case 0x0004:
2245319dc7bSGerd Hoffmann         length = usb_desc_msos_compat(desc, buf);
2255319dc7bSGerd Hoffmann         break;
2265319dc7bSGerd Hoffmann     case 0x0005:
2275319dc7bSGerd Hoffmann         length = usb_desc_msos_prop(desc, buf);
2285319dc7bSGerd Hoffmann         break;
2295319dc7bSGerd Hoffmann     }
2305319dc7bSGerd Hoffmann 
2315319dc7bSGerd Hoffmann     if (length > len) {
2325319dc7bSGerd Hoffmann         length = len;
2335319dc7bSGerd Hoffmann     }
2345319dc7bSGerd Hoffmann     memcpy(dest, buf, length);
2350c6f807fSMarkus Armbruster     g_free(buf);
2365319dc7bSGerd Hoffmann 
2375319dc7bSGerd Hoffmann     p->actual_length = length;
2385319dc7bSGerd Hoffmann     return 0;
2395319dc7bSGerd Hoffmann }
240