aboutsummaryrefslogtreecommitdiff
path: root/hw/usb/host-libusb.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/usb/host-libusb.c')
-rw-r--r--hw/usb/host-libusb.c76
1 files changed, 63 insertions, 13 deletions
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 8007d1d156..afbf1563f4 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -111,6 +111,7 @@ struct USBHostRequest {
unsigned char *buffer;
unsigned char *cbuf;
unsigned int clen;
+ bool usb3ep0quirk;
QTAILQ_ENTRY(USBHostRequest) next;
};
@@ -146,6 +147,10 @@ static void usb_host_attach_kernel(USBHostDevice *s);
#define BULK_TIMEOUT 0 /* unlimited */
#define INTR_TIMEOUT 0 /* unlimited */
+#if LIBUSBX_API_VERSION >= 0x01000103
+# define HAVE_STREAMS 1
+#endif
+
static const char *speed_name[] = {
[LIBUSB_SPEED_UNKNOWN] = "?",
[LIBUSB_SPEED_LOW] = "1.5",
@@ -346,6 +351,13 @@ static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
r->p->actual_length = xfer->actual_length;
if (r->in && xfer->actual_length) {
memcpy(r->cbuf, r->buffer + 8, xfer->actual_length);
+
+ /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
+ * to work redirected to a not superspeed capable hcd */
+ if (r->usb3ep0quirk && xfer->actual_length >= 18 &&
+ r->cbuf[7] == 9) {
+ r->cbuf[7] = 64;
+ }
}
trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
r->p->status, r->p->actual_length);
@@ -672,11 +684,17 @@ static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
/* ------------------------------------------------------------------------ */
-static bool usb_host_full_speed_compat(USBHostDevice *s)
+static void usb_host_speed_compat(USBHostDevice *s)
{
+ USBDevice *udev = USB_DEVICE(s);
struct libusb_config_descriptor *conf;
const struct libusb_interface_descriptor *intf;
const struct libusb_endpoint_descriptor *endp;
+#ifdef HAVE_STREAMS
+ struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
+#endif
+ bool compat_high = true;
+ bool compat_full = true;
uint8_t type;
int rc, c, i, a, e;
@@ -693,10 +711,27 @@ static bool usb_host_full_speed_compat(USBHostDevice *s)
type = endp->bmAttributes & 0x3;
switch (type) {
case 0x01: /* ISO */
- return false;
+ compat_full = false;
+ compat_high = false;
+ break;
+ case 0x02: /* BULK */
+#ifdef HAVE_STREAMS
+ rc = libusb_get_ss_endpoint_companion_descriptor
+ (ctx, endp, &endp_ss_comp);
+ if (rc == LIBUSB_SUCCESS) {
+ libusb_free_ss_endpoint_companion_descriptor
+ (endp_ss_comp);
+ compat_full = false;
+ compat_high = false;
+ }
+#endif
+ break;
case 0x03: /* INTERRUPT */
if (endp->wMaxPacketSize > 64) {
- return false;
+ compat_full = false;
+ }
+ if (endp->wMaxPacketSize > 1024) {
+ compat_high = false;
}
break;
}
@@ -705,7 +740,17 @@ static bool usb_host_full_speed_compat(USBHostDevice *s)
}
libusb_free_config_descriptor(conf);
}
- return true;
+
+ udev->speedmask = (1 << udev->speed);
+ if (udev->speed == USB_SPEED_SUPER && compat_high) {
+ udev->speedmask |= USB_SPEED_HIGH;
+ }
+ if (udev->speed == USB_SPEED_SUPER && compat_full) {
+ udev->speedmask |= USB_SPEED_FULL;
+ }
+ if (udev->speed == USB_SPEED_HIGH && compat_full) {
+ udev->speedmask |= USB_SPEED_FULL;
+ }
}
static void usb_host_ep_update(USBHostDevice *s)
@@ -720,7 +765,7 @@ static void usb_host_ep_update(USBHostDevice *s)
struct libusb_config_descriptor *conf;
const struct libusb_interface_descriptor *intf;
const struct libusb_endpoint_descriptor *endp;
-#if LIBUSBX_API_VERSION >= 0x01000103
+#ifdef HAVE_STREAMS
struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
#endif
uint8_t devep, type;
@@ -768,7 +813,7 @@ static void usb_host_ep_update(USBHostDevice *s)
usb_ep_set_type(udev, pid, ep, type);
usb_ep_set_ifnum(udev, pid, ep, i);
usb_ep_set_halted(udev, pid, ep, 0);
-#if LIBUSBX_API_VERSION >= 0x01000103
+#ifdef HAVE_STREAMS
if (type == LIBUSB_TRANSFER_TYPE_BULK &&
libusb_get_ss_endpoint_companion_descriptor(ctx, endp,
&endp_ss_comp) == LIBUSB_SUCCESS) {
@@ -813,10 +858,7 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev)
usb_host_ep_update(s);
udev->speed = speed_map[libusb_get_device_speed(dev)];
- udev->speedmask = (1 << udev->speed);
- if (udev->speed == USB_SPEED_HIGH && usb_host_full_speed_compat(s)) {
- udev->speedmask |= USB_SPEED_MASK_FULL;
- }
+ usb_host_speed_compat(s);
if (s->ddesc.iProduct) {
libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
@@ -1162,6 +1204,14 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
memcpy(r->buffer + 8, r->cbuf, r->clen);
}
+ /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
+ * to work redirected to a not superspeed capable hcd */
+ if (udev->speed == USB_SPEED_SUPER &&
+ !((udev->port->speedmask & USB_SPEED_MASK_SUPER)) &&
+ request == 0x8006 && value == 0x100 && index == 0) {
+ r->usb3ep0quirk = true;
+ }
+
libusb_fill_control_transfer(r->xfer, s->dh, r->buffer,
usb_host_req_complete_ctrl, r,
CONTROL_TIMEOUT);
@@ -1215,7 +1265,7 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
}
ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
if (p->stream) {
-#if LIBUSBX_API_VERSION >= 0x01000103
+#ifdef HAVE_STREAMS
libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream,
r->buffer, size,
usb_host_req_complete_data, r,
@@ -1296,7 +1346,7 @@ static void usb_host_handle_reset(USBDevice *udev)
static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
int nr_eps, int streams)
{
-#if LIBUSBX_API_VERSION >= 0x01000103
+#ifdef HAVE_STREAMS
USBHostDevice *s = USB_HOST_DEVICE(udev);
unsigned char endpoints[30];
int i, rc;
@@ -1326,7 +1376,7 @@ static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
int nr_eps)
{
-#if LIBUSBX_API_VERSION >= 0x01000103
+#ifdef HAVE_STREAMS
USBHostDevice *s = USB_HOST_DEVICE(udev);
unsigned char endpoints[30];
int i;