Spotted by Coverity with preview checker ALLOC_FREE_MISMATCH enabled and my "coverity: Model g_free() isn't necessarily free()" model patch applied. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
		
			
				
	
	
		
			239 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#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;
 | 
						|
}
 |