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