Add qemu 2.4.0
[kvmfornfv.git] / qemu / hw / usb / desc-msos.c
diff --git a/qemu/hw/usb/desc-msos.c b/qemu/hw/usb/desc-msos.c
new file mode 100644 (file)
index 0000000..32c3600
--- /dev/null
@@ -0,0 +1,238 @@
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+
+/*
+ * Microsoft OS Descriptors
+ *
+ * Windows tries to fetch some special descriptors with informations
+ * specifically for windows.  Presence is indicated using a special
+ * string @ index 0xee.  There are two kinds of descriptors:
+ *
+ * compatid descriptor
+ *   Used to bind drivers, if usb class isn't specific enougth.
+ *   Used for PTP/MTP for example (both share the same usb class).
+ *
+ * properties descriptor
+ *   Does carry registry entries.  They show up in
+ *   HLM\SYSTEM\CurrentControlSet\Enum\USB\<devid>\<serial>\Device Parameters
+ *
+ * Note that Windows caches the stuff it got in the registry, so when
+ * playing with this you have to delete registry subtrees to make
+ * windows query the device again:
+ *   HLM\SYSTEM\CurrentControlSet\Control\usbflags
+ *   HLM\SYSTEM\CurrentControlSet\Enum\USB
+ * Windows will complain it can't delete entries on the second one.
+ * It has deleted everything it had permissions too, which is enouth
+ * as this includes "Device Parameters".
+ *
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff537430.aspx
+ *
+ */
+
+/* ------------------------------------------------------------------ */
+
+typedef struct msos_compat_hdr {
+    uint32_t dwLength;
+    uint8_t  bcdVersion_lo;
+    uint8_t  bcdVersion_hi;
+    uint8_t  wIndex_lo;
+    uint8_t  wIndex_hi;
+    uint8_t  bCount;
+    uint8_t  reserved[7];
+} QEMU_PACKED msos_compat_hdr;
+
+typedef struct msos_compat_func {
+    uint8_t  bFirstInterfaceNumber;
+    uint8_t  reserved_1;
+    char     compatibleId[8];
+    uint8_t  subCompatibleId[8];
+    uint8_t  reserved_2[6];
+} QEMU_PACKED msos_compat_func;
+
+static int usb_desc_msos_compat(const USBDesc *desc, uint8_t *dest)
+{
+    msos_compat_hdr *hdr = (void *)dest;
+    msos_compat_func *func;
+    int length = sizeof(*hdr);
+    int count = 0;
+
+    func = (void *)(dest + length);
+    func->bFirstInterfaceNumber = 0;
+    func->reserved_1 = 0x01;
+    if (desc->msos->CompatibleID) {
+        snprintf(func->compatibleId, sizeof(func->compatibleId),
+                 "%s", desc->msos->CompatibleID);
+    }
+    length += sizeof(*func);
+    count++;
+
+    hdr->dwLength      = cpu_to_le32(length);
+    hdr->bcdVersion_lo = 0x00;
+    hdr->bcdVersion_hi = 0x01;
+    hdr->wIndex_lo     = 0x04;
+    hdr->wIndex_hi     = 0x00;
+    hdr->bCount        = count;
+    return length;
+}
+
+/* ------------------------------------------------------------------ */
+
+typedef struct msos_prop_hdr {
+    uint32_t dwLength;
+    uint8_t  bcdVersion_lo;
+    uint8_t  bcdVersion_hi;
+    uint8_t  wIndex_lo;
+    uint8_t  wIndex_hi;
+    uint8_t  wCount_lo;
+    uint8_t  wCount_hi;
+} QEMU_PACKED msos_prop_hdr;
+
+typedef struct msos_prop {
+    uint32_t dwLength;
+    uint32_t dwPropertyDataType;
+    uint8_t  dwPropertyNameLength_lo;
+    uint8_t  dwPropertyNameLength_hi;
+    uint8_t  bPropertyName[];
+} QEMU_PACKED msos_prop;
+
+typedef struct msos_prop_data {
+    uint32_t dwPropertyDataLength;
+    uint8_t  bPropertyData[];
+} QEMU_PACKED msos_prop_data;
+
+typedef enum msos_prop_type {
+    MSOS_REG_SZ        = 1,
+    MSOS_REG_EXPAND_SZ = 2,
+    MSOS_REG_BINARY    = 3,
+    MSOS_REG_DWORD_LE  = 4,
+    MSOS_REG_DWORD_BE  = 5,
+    MSOS_REG_LINK      = 6,
+    MSOS_REG_MULTI_SZ  = 7,
+} msos_prop_type;
+
+static int usb_desc_msos_prop_name(struct msos_prop *prop,
+                                   const wchar_t *name)
+{
+    int length = wcslen(name) + 1;
+    int i;
+
+    prop->dwPropertyNameLength_lo = usb_lo(length*2);
+    prop->dwPropertyNameLength_hi = usb_hi(length*2);
+    for (i = 0; i < length; i++) {
+        prop->bPropertyName[i*2]   = usb_lo(name[i]);
+        prop->bPropertyName[i*2+1] = usb_hi(name[i]);
+    }
+    return length*2;
+}
+
+static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type,
+                                  const wchar_t *name, const wchar_t *value)
+{
+    struct msos_prop *prop = (void *)dest;
+    struct msos_prop_data *data;
+    int length = sizeof(*prop);
+    int i, vlen = wcslen(value) + 1;
+
+    prop->dwPropertyDataType = cpu_to_le32(type);
+    length += usb_desc_msos_prop_name(prop, name);
+    data = (void *)(dest + length);
+
+    data->dwPropertyDataLength = cpu_to_le32(vlen*2);
+    length += sizeof(*prop);
+
+    for (i = 0; i < vlen; i++) {
+        data->bPropertyData[i*2]   = usb_lo(value[i]);
+        data->bPropertyData[i*2+1] = usb_hi(value[i]);
+    }
+    length += vlen*2;
+
+    prop->dwLength = cpu_to_le32(length);
+    return length;
+}
+
+static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name,
+                                    uint32_t value)
+{
+    struct msos_prop *prop = (void *)dest;
+    struct msos_prop_data *data;
+    int length = sizeof(*prop);
+
+    prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE);
+    length += usb_desc_msos_prop_name(prop, name);
+    data = (void *)(dest + length);
+
+    data->dwPropertyDataLength = cpu_to_le32(4);
+    data->bPropertyData[0] = (value)       & 0xff;
+    data->bPropertyData[1] = (value >>  8) & 0xff;
+    data->bPropertyData[2] = (value >> 16) & 0xff;
+    data->bPropertyData[3] = (value >> 24) & 0xff;
+    length += sizeof(*prop) + 4;
+
+    prop->dwLength = cpu_to_le32(length);
+    return length;
+}
+
+static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest)
+{
+    msos_prop_hdr *hdr = (void *)dest;
+    int length = sizeof(*hdr);
+    int count = 0;
+
+    if (desc->msos->Label) {
+        /*
+         * Given as example in the specs.  Havn't figured yet where
+         * this label shows up in the windows gui.
+         */
+        length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ,
+                                         L"Label", desc->msos->Label);
+        count++;
+    }
+
+    if (desc->msos->SelectiveSuspendEnabled) {
+        /*
+         * Signaling remote wakeup capability in the standard usb
+         * descriptors isn't enouth to make windows actually use it.
+         * This is the "Yes, we really mean it" registy entry to flip
+         * the switch in the windows drivers.
+         */
+        length += usb_desc_msos_prop_dword(dest+length,
+                                           L"SelectiveSuspendEnabled", 1);
+        count++;
+    }
+
+    hdr->dwLength      = cpu_to_le32(length);
+    hdr->bcdVersion_lo = 0x00;
+    hdr->bcdVersion_hi = 0x01;
+    hdr->wIndex_lo     = 0x05;
+    hdr->wIndex_hi     = 0x00;
+    hdr->wCount_lo     = usb_lo(count);
+    hdr->wCount_hi     = usb_hi(count);
+    return length;
+}
+
+/* ------------------------------------------------------------------ */
+
+int usb_desc_msos(const USBDesc *desc,  USBPacket *p,
+                  int index, uint8_t *dest, size_t len)
+{
+    void *buf = g_malloc0(4096);
+    int length = 0;
+
+    switch (index) {
+    case 0x0004:
+        length = usb_desc_msos_compat(desc, buf);
+        break;
+    case 0x0005:
+        length = usb_desc_msos_prop(desc, buf);
+        break;
+    }
+
+    if (length > len) {
+        length = len;
+    }
+    memcpy(dest, buf, length);
+    g_free(buf);
+
+    p->actual_length = length;
+    return 0;
+}