Fix typo: *informations ==> information *enougth ==> enough *enouth ==> enough *registy ==> registry *releated ==> related *Ouptut ==> Output *manualy ==> manually *Attemping ==> Attempting *contine ==> continue *tranceiver ==> transceiver *Tranceiver ==> Transceiver Signed-off-by: Cai Huoqing <caihuoqing@baidu.com> Message-Id: <20210730012720.2246-1-caihuoqing@baidu.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
		
			
				
	
	
		
			240 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "qemu/osdep.h"
 | |
| #include "hw/usb.h"
 | |
| #include "desc.h"
 | |
| 
 | |
| /*
 | |
|  * Microsoft OS Descriptors
 | |
|  *
 | |
|  * Windows tries to fetch some special descriptors with information
 | |
|  * 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 enough.
 | |
|  *   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 enough
 | |
|  * 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.  Haven'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 enough to make windows actually use it.
 | |
|          * This is the "Yes, we really mean it" registry 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;
 | |
| }
 |