qemu/hw/usb/desc-msos.c
<<
>>
Prefs
   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
  35typedef 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
  45typedef 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
  53static 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
  81typedef 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
  91typedef 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
  99typedef struct msos_prop_data {
 100    uint32_t dwPropertyDataLength;
 101    uint8_t  bPropertyData[];
 102} QEMU_PACKED msos_prop_data;
 103
 104typedef 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
 114static 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
 129static 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
 154static 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
 176static 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
 216int 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