2010-11-17 11:03:53 +01:00
|
|
|
#ifndef QEMU_HW_USB_DESC_H
|
|
|
|
#define QEMU_HW_USB_DESC_H
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
2013-11-20 07:32:31 +01:00
|
|
|
#include <wchar.h>
|
2010-11-17 11:03:53 +01:00
|
|
|
|
2012-03-29 12:04:54 +02:00
|
|
|
/* binary representation */
|
|
|
|
typedef struct USBDescriptor {
|
|
|
|
uint8_t bLength;
|
|
|
|
uint8_t bDescriptorType;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
uint8_t bcdUSB_lo;
|
|
|
|
uint8_t bcdUSB_hi;
|
|
|
|
uint8_t bDeviceClass;
|
|
|
|
uint8_t bDeviceSubClass;
|
|
|
|
uint8_t bDeviceProtocol;
|
|
|
|
uint8_t bMaxPacketSize0;
|
|
|
|
uint8_t idVendor_lo;
|
|
|
|
uint8_t idVendor_hi;
|
|
|
|
uint8_t idProduct_lo;
|
|
|
|
uint8_t idProduct_hi;
|
|
|
|
uint8_t bcdDevice_lo;
|
|
|
|
uint8_t bcdDevice_hi;
|
|
|
|
uint8_t iManufacturer;
|
|
|
|
uint8_t iProduct;
|
|
|
|
uint8_t iSerialNumber;
|
|
|
|
uint8_t bNumConfigurations;
|
|
|
|
} device;
|
2012-03-29 12:15:01 +02:00
|
|
|
struct {
|
|
|
|
uint8_t bcdUSB_lo;
|
|
|
|
uint8_t bcdUSB_hi;
|
|
|
|
uint8_t bDeviceClass;
|
|
|
|
uint8_t bDeviceSubClass;
|
|
|
|
uint8_t bDeviceProtocol;
|
|
|
|
uint8_t bMaxPacketSize0;
|
|
|
|
uint8_t bNumConfigurations;
|
|
|
|
uint8_t bReserved;
|
|
|
|
} device_qualifier;
|
2012-03-29 12:24:08 +02:00
|
|
|
struct {
|
|
|
|
uint8_t wTotalLength_lo;
|
|
|
|
uint8_t wTotalLength_hi;
|
|
|
|
uint8_t bNumInterfaces;
|
|
|
|
uint8_t bConfigurationValue;
|
|
|
|
uint8_t iConfiguration;
|
|
|
|
uint8_t bmAttributes;
|
|
|
|
uint8_t bMaxPower;
|
|
|
|
} config;
|
2012-03-29 12:30:33 +02:00
|
|
|
struct {
|
|
|
|
uint8_t bInterfaceNumber;
|
|
|
|
uint8_t bAlternateSetting;
|
|
|
|
uint8_t bNumEndpoints;
|
|
|
|
uint8_t bInterfaceClass;
|
|
|
|
uint8_t bInterfaceSubClass;
|
|
|
|
uint8_t bInterfaceProtocol;
|
|
|
|
uint8_t iInterface;
|
|
|
|
} interface;
|
2012-03-29 16:01:21 +02:00
|
|
|
struct {
|
|
|
|
uint8_t bEndpointAddress;
|
|
|
|
uint8_t bmAttributes;
|
|
|
|
uint8_t wMaxPacketSize_lo;
|
|
|
|
uint8_t wMaxPacketSize_hi;
|
|
|
|
uint8_t bInterval;
|
|
|
|
uint8_t bRefresh; /* only audio ep */
|
|
|
|
uint8_t bSynchAddress; /* only audio ep */
|
|
|
|
} endpoint;
|
2012-08-28 17:28:03 +02:00
|
|
|
struct {
|
|
|
|
uint8_t bMaxBurst;
|
|
|
|
uint8_t bmAttributes;
|
|
|
|
uint8_t wBytesPerInterval_lo;
|
|
|
|
uint8_t wBytesPerInterval_hi;
|
|
|
|
} super_endpoint;
|
2012-08-28 17:46:29 +02:00
|
|
|
struct {
|
|
|
|
uint8_t wTotalLength_lo;
|
|
|
|
uint8_t wTotalLength_hi;
|
|
|
|
uint8_t bNumDeviceCaps;
|
|
|
|
} bos;
|
|
|
|
struct {
|
|
|
|
uint8_t bDevCapabilityType;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
uint8_t bmAttributes_1;
|
|
|
|
uint8_t bmAttributes_2;
|
|
|
|
uint8_t bmAttributes_3;
|
|
|
|
uint8_t bmAttributes_4;
|
|
|
|
} usb2_ext;
|
|
|
|
struct {
|
|
|
|
uint8_t bmAttributes;
|
|
|
|
uint8_t wSpeedsSupported_lo;
|
|
|
|
uint8_t wSpeedsSupported_hi;
|
|
|
|
uint8_t bFunctionalitySupport;
|
|
|
|
uint8_t bU1DevExitLat;
|
|
|
|
uint8_t wU2DevExitLat_lo;
|
|
|
|
uint8_t wU2DevExitLat_hi;
|
|
|
|
} super;
|
|
|
|
} u;
|
|
|
|
} cap;
|
2012-03-29 12:04:54 +02:00
|
|
|
} u;
|
|
|
|
} QEMU_PACKED USBDescriptor;
|
|
|
|
|
2010-11-17 11:03:53 +01:00
|
|
|
struct USBDescID {
|
|
|
|
uint16_t idVendor;
|
|
|
|
uint16_t idProduct;
|
|
|
|
uint16_t bcdDevice;
|
|
|
|
uint8_t iManufacturer;
|
|
|
|
uint8_t iProduct;
|
|
|
|
uint8_t iSerialNumber;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBDescDevice {
|
|
|
|
uint16_t bcdUSB;
|
|
|
|
uint8_t bDeviceClass;
|
|
|
|
uint8_t bDeviceSubClass;
|
|
|
|
uint8_t bDeviceProtocol;
|
|
|
|
uint8_t bMaxPacketSize0;
|
|
|
|
uint8_t bNumConfigurations;
|
|
|
|
|
|
|
|
const USBDescConfig *confs;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBDescConfig {
|
|
|
|
uint8_t bNumInterfaces;
|
|
|
|
uint8_t bConfigurationValue;
|
|
|
|
uint8_t iConfiguration;
|
|
|
|
uint8_t bmAttributes;
|
|
|
|
uint8_t bMaxPower;
|
|
|
|
|
2011-04-03 07:33:21 +02:00
|
|
|
/* grouped interfaces */
|
|
|
|
uint8_t nif_groups;
|
|
|
|
const USBDescIfaceAssoc *if_groups;
|
|
|
|
|
|
|
|
/* "normal" interfaces */
|
|
|
|
uint8_t nif;
|
|
|
|
const USBDescIface *ifs;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* conceptually an Interface Association Descriptor, and releated interfaces */
|
|
|
|
struct USBDescIfaceAssoc {
|
|
|
|
uint8_t bFirstInterface;
|
|
|
|
uint8_t bInterfaceCount;
|
|
|
|
uint8_t bFunctionClass;
|
|
|
|
uint8_t bFunctionSubClass;
|
|
|
|
uint8_t bFunctionProtocol;
|
|
|
|
uint8_t iFunction;
|
|
|
|
|
2010-11-17 11:03:53 +01:00
|
|
|
uint8_t nif;
|
|
|
|
const USBDescIface *ifs;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBDescIface {
|
|
|
|
uint8_t bInterfaceNumber;
|
|
|
|
uint8_t bAlternateSetting;
|
|
|
|
uint8_t bNumEndpoints;
|
|
|
|
uint8_t bInterfaceClass;
|
|
|
|
uint8_t bInterfaceSubClass;
|
|
|
|
uint8_t bInterfaceProtocol;
|
|
|
|
uint8_t iInterface;
|
|
|
|
|
|
|
|
uint8_t ndesc;
|
|
|
|
USBDescOther *descs;
|
|
|
|
USBDescEndpoint *eps;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBDescEndpoint {
|
|
|
|
uint8_t bEndpointAddress;
|
|
|
|
uint8_t bmAttributes;
|
|
|
|
uint16_t wMaxPacketSize;
|
|
|
|
uint8_t bInterval;
|
2011-08-10 14:10:04 +02:00
|
|
|
uint8_t bRefresh;
|
|
|
|
uint8_t bSynchAddress;
|
|
|
|
|
|
|
|
uint8_t is_audio; /* has bRefresh + bSynchAddress */
|
|
|
|
uint8_t *extra;
|
2012-08-28 17:28:03 +02:00
|
|
|
|
|
|
|
/* superspeed endpoint companion */
|
|
|
|
uint8_t bMaxBurst;
|
|
|
|
uint8_t bmAttributes_super;
|
|
|
|
uint16_t wBytesPerInterval;
|
2010-11-17 11:03:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct USBDescOther {
|
|
|
|
uint8_t length;
|
2011-08-30 11:49:05 +02:00
|
|
|
const uint8_t *data;
|
2010-11-17 11:03:53 +01:00
|
|
|
};
|
|
|
|
|
2013-11-20 07:32:31 +01:00
|
|
|
struct USBDescMSOS {
|
2014-03-18 11:01:50 +01:00
|
|
|
const char *CompatibleID;
|
2013-11-20 07:32:31 +01:00
|
|
|
const wchar_t *Label;
|
|
|
|
bool SelectiveSuspendEnabled;
|
|
|
|
};
|
|
|
|
|
2010-11-17 11:03:53 +01:00
|
|
|
typedef const char *USBDescStrings[256];
|
|
|
|
|
|
|
|
struct USBDesc {
|
|
|
|
USBDescID id;
|
|
|
|
const USBDescDevice *full;
|
|
|
|
const USBDescDevice *high;
|
2012-08-28 17:28:50 +02:00
|
|
|
const USBDescDevice *super;
|
2010-11-17 11:03:53 +01:00
|
|
|
const char* const *str;
|
2013-11-20 07:32:31 +01:00
|
|
|
const USBDescMSOS *msos;
|
2010-11-17 11:03:53 +01:00
|
|
|
};
|
|
|
|
|
2012-08-28 17:28:03 +02:00
|
|
|
#define USB_DESC_FLAG_SUPER (1 << 1)
|
|
|
|
|
2013-11-20 07:33:28 +01:00
|
|
|
/* little helpers */
|
|
|
|
static inline uint8_t usb_lo(uint16_t val)
|
|
|
|
{
|
|
|
|
return val & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint8_t usb_hi(uint16_t val)
|
|
|
|
{
|
|
|
|
return (val >> 8) & 0xff;
|
|
|
|
}
|
|
|
|
|
2010-11-17 11:03:53 +01:00
|
|
|
/* generate usb packages from structs */
|
|
|
|
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
|
2013-11-20 07:32:31 +01:00
|
|
|
bool msos, uint8_t *dest, size_t len);
|
2010-12-08 17:35:22 +01:00
|
|
|
int usb_desc_device_qualifier(const USBDescDevice *dev,
|
|
|
|
uint8_t *dest, size_t len);
|
2012-08-28 17:28:03 +02:00
|
|
|
int usb_desc_config(const USBDescConfig *conf, int flags,
|
|
|
|
uint8_t *dest, size_t len);
|
|
|
|
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
|
|
|
|
uint8_t *dest, size_t len);
|
|
|
|
int usb_desc_iface(const USBDescIface *iface, int flags,
|
|
|
|
uint8_t *dest, size_t len);
|
|
|
|
int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
|
|
|
|
uint8_t *dest, size_t len);
|
2010-11-17 11:03:53 +01:00
|
|
|
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
|
2013-11-20 07:32:31 +01:00
|
|
|
int usb_desc_msos(const USBDesc *desc, USBPacket *p,
|
|
|
|
int index, uint8_t *dest, size_t len);
|
2010-11-17 11:03:53 +01:00
|
|
|
|
|
|
|
/* control message emulation helpers */
|
2010-11-26 20:20:41 +01:00
|
|
|
void usb_desc_init(USBDevice *dev);
|
2010-12-03 18:07:20 +01:00
|
|
|
void usb_desc_attach(USBDevice *dev);
|
2010-11-26 12:25:32 +01:00
|
|
|
void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
|
2012-04-20 12:33:30 +02:00
|
|
|
void usb_desc_create_serial(USBDevice *dev);
|
2010-11-26 12:25:32 +01:00
|
|
|
const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
|
|
|
|
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
|
|
|
|
int value, uint8_t *dest, size_t len);
|
2011-02-02 16:33:13 +01:00
|
|
|
int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
|
|
|
int request, int value, int index, int length, uint8_t *data);
|
2010-11-17 11:03:53 +01:00
|
|
|
|
|
|
|
#endif /* QEMU_HW_USB_DESC_H */
|