qemu/hw/usb/desc-msos.c
<<
>>
Prefs
   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
  34typedef 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
  44typedef 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
  52static 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
  80typedef 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
  90typedef 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
  98typedef struct msos_prop_data {
  99    uint32_t dwPropertyDataLength;
 100    uint8_t  bPropertyData[];
 101} QEMU_PACKED msos_prop_data;
 102
 103typedef 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
 113static 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
 128static 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
 153static 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
 175static 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
 215int 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    g_free(buf);
 235
 236    p->actual_length = length;
 237    return 0;
 238}
 239