All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir
@ 2012-01-13 10:18 Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 01/17] usb-host: rip out legacy procfs support Gerd Hoffmann
                   ` (17 more replies)
  0 siblings, 18 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

Here comes the usb patch queue.  It features the patches posted a week
ago for review with some minor tweaks according to the review comments:
The comment in usb-audio was fixed and xhci got a codestyle cleanup.  No
actual code changes.  I've also included the usb-redir patches posted by
Hans earlier this week.

please pull,
  Gerd

The following changes since commit 515aa3c57986b3e26558d72ecaeb7545ecd30510:

  check-qstring: remove check.h include (2012-01-12 11:33:22 -0600)

are available in the git repository at:
  git://git.kraxel.org/qemu usb.36

Gerd Hoffmann (10):
      usb-host: rip out legacy procfs support
      usb: track configuration and interface count in USBDevice.
      usb: track altsetting in USBDevice
      usb-desc: audio endpoint support
      usb: add USBEndpoint
      usb: add ifnum to USBEndpoint
      usb-desc: USBEndpoint support
      usb/debug: add usb_ep_dump
      usb: add max_packet_size to USBEndpoint
      usb: link packets to endpoints not devices

H. Peter Anvin (1):
      usb: add audio device model

Hans de Goede (5):
      usb-redir: Clear iso / irq error when stopping the stream
      usb-redir: Dynamically adjust iso buffering size based on ep interval
      usb-redir: Pre-fill our isoc input buffer before sending pkts to the host
      usb-redir: Try to keep our buffer size near the target size
      usb-redir: Improve some debugging messages

Hector Martin (1):
      xhci: Initial xHCI implementation

 Makefile.objs           |    3 +-
 default-configs/pci.mak |    1 +
 hw/pci_ids.h            |    3 +
 hw/usb-audio.c          |  704 ++++++++++++
 hw/usb-bt.c             |   22 -
 hw/usb-bus.c            |    1 +
 hw/usb-ccid.c           |    8 -
 hw/usb-desc.c           |  143 +++-
 hw/usb-desc.h           |    5 +
 hw/usb-ehci.c           |    3 +-
 hw/usb-hid.c            |    7 -
 hw/usb-hub.c            |    7 -
 hw/usb-msd.c            |   10 -
 hw/usb-musb.c           |    3 +-
 hw/usb-net.c            |   14 -
 hw/usb-ohci.c           |    4 +-
 hw/usb-serial.c         |    7 -
 hw/usb-uhci.c           |    3 +-
 hw/usb-wacom.c          |    7 -
 hw/usb-xhci.c           | 2748 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb.c                |  125 +++-
 hw/usb.h                |   44 +-
 trace-events            |    1 +
 usb-linux.c             |  452 ++------
 usb-redir.c             |  118 ++-
 25 files changed, 3972 insertions(+), 471 deletions(-)
 create mode 100644 hw/usb-audio.c
 create mode 100644 hw/usb-xhci.c

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 01/17] usb-host: rip out legacy procfs support
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 02/17] usb: track configuration and interface count in USBDevice Gerd Hoffmann
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This patch removes support for parsing /proc/bus/usb/devices for device
discovery.  The code lacks a few features compared to the sysfs code and
is also bitrotting as everybody has sysfs these days.

This implies having sysfs mounted is mandatory now to use the usb-host
driver.  udev isn't required though.  qemu will prefer the udev-managed
device nodes below /dev/bus/usb, but in case this directory isn't preset
qemu will use the device nodes below /proc/bus/usb (default usbfs mount
point).

Bottom line: make sure you have both sysfs and usbfs mounted properly,
and everything should continue to work as it did before.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-linux.c |  327 ++++++++---------------------------------------------------
 1 files changed, 42 insertions(+), 285 deletions(-)

diff --git a/usb-linux.c b/usb-linux.c
index 749ce71..c68e194 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -66,23 +66,9 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
 #define DPRINTF(...)
 #endif
 
-#define USBDBG_DEVOPENED "husb: opened %s/devices\n"
-
-#define USBPROCBUS_PATH "/proc/bus/usb"
 #define PRODUCT_NAME_SZ 32
 #define MAX_ENDPOINTS 15
 #define MAX_PORTLEN 16
-#define USBDEVBUS_PATH "/dev/bus/usb"
-#define USBSYSBUS_PATH "/sys/bus/usb"
-
-static char *usb_host_device_path;
-
-#define USB_FS_NONE 0
-#define USB_FS_PROC 1
-#define USB_FS_DEV 2
-#define USB_FS_SYS 3
-
-static int usb_fs_type;
 
 /* endpoint association data */
 #define ISO_FRAME_DESC_PER_URB 32
@@ -431,6 +417,31 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
     }
 }
 
+static int usb_host_open_device(int bus, int addr)
+{
+    const char *usbfs = NULL;
+    char filename[32];
+    struct stat st;
+    int fd, rc;
+
+    rc = stat("/dev/bus/usb", &st);
+    if (rc == 0 && S_ISDIR(st.st_mode)) {
+        /* udev-created device nodes available */
+        usbfs = "/dev/bus/usb";
+    } else {
+        /* fallback: usbfs mounted below /proc */
+        usbfs = "/proc/bus/usb";
+    }
+
+    snprintf(filename, sizeof(filename), "%s/%03d/%03d",
+             usbfs, bus, addr);
+    fd = open(filename, O_RDWR | O_NONBLOCK);
+    if (fd < 0) {
+        fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno));
+    }
+    return fd;
+}
+
 static int usb_host_claim_port(USBHostDevice *s)
 {
 #ifdef USBDEVFS_CLAIM_PORT
@@ -460,12 +471,7 @@ static int usb_host_claim_port(USBHostDevice *s)
         return -1;
     }
 
-    if (!usb_host_device_path) {
-        return -1;
-    }
-    snprintf(line, sizeof(line), "%s/%03d/%03d",
-             usb_host_device_path, s->match.bus_num, hub_addr);
-    s->hub_fd = open(line, O_RDWR | O_NONBLOCK);
+    s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
     if (s->hub_fd < 0) {
         return -1;
     }
@@ -522,10 +528,6 @@ static int usb_linux_get_num_interfaces(USBHostDevice *s)
     char device_name[64], line[1024];
     int num_interfaces = 0;
 
-    if (usb_fs_type != USB_FS_SYS) {
-        return -1;
-    }
-
     sprintf(device_name, "%d-%s", s->bus_num, s->port);
     if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
                             device_name)) {
@@ -1090,41 +1092,21 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
 static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
     uint8_t configuration, uint8_t interface)
 {
-    uint8_t alt_setting;
-    struct usb_ctrltransfer ct;
-    int ret;
-
-    if (usb_fs_type == USB_FS_SYS) {
-        char device_name[64], line[1024];
-        int alt_setting;
+    char device_name[64], line[1024];
+    int alt_setting;
 
-        sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
-                (int)configuration, (int)interface);
+    sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
+            (int)configuration, (int)interface);
 
-        if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
-                                device_name)) {
-            goto usbdevfs;
-        }
-        if (sscanf(line, "%d", &alt_setting) != 1) {
-            goto usbdevfs;
-        }
-        return alt_setting;
-    }
-
-usbdevfs:
-    ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE;
-    ct.bRequest = USB_REQ_GET_INTERFACE;
-    ct.wValue = 0;
-    ct.wIndex = interface;
-    ct.wLength = 1;
-    ct.data = &alt_setting;
-    ct.timeout = 50;
-    ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
-    if (ret < 0) {
+    if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
+                            device_name)) {
+        /* Assume alt 0 on error */
+        return 0;
+    }
+    if (sscanf(line, "%d", &alt_setting) != 1) {
         /* Assume alt 0 on error */
         return 0;
     }
-
     return alt_setting;
 }
 
@@ -1273,7 +1255,6 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
                          const char *prod_name, int speed)
 {
     int fd = -1, ret;
-    char buf[1024];
 
     trace_usb_host_open_started(bus_num, addr);
 
@@ -1281,15 +1262,8 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
         goto fail;
     }
 
-    if (!usb_host_device_path) {
-        perror("husb: USB Host Device Path not set");
-        goto fail;
-    }
-    snprintf(buf, sizeof(buf), "%s/%03d/%03d", usb_host_device_path,
-             bus_num, addr);
-    fd = open(buf, O_RDWR | O_NONBLOCK);
+    fd = usb_host_open_device(bus_num, addr);
     if (fd < 0) {
-        perror(buf);
         goto fail;
     }
     DPRINTF("husb: opened %s\n", buf);
@@ -1538,149 +1512,6 @@ int usb_host_device_close(const char *devname)
     return -1;
 }
 
-static int get_tag_value(char *buf, int buf_size,
-                         const char *str, const char *tag,
-                         const char *stopchars)
-{
-    const char *p;
-    char *q;
-    p = strstr(str, tag);
-    if (!p) {
-        return -1;
-    }
-    p += strlen(tag);
-    while (qemu_isspace(*p)) {
-        p++;
-    }
-    q = buf;
-    while (*p != '\0' && !strchr(stopchars, *p)) {
-        if ((q - buf) < (buf_size - 1)) {
-            *q++ = *p;
-        }
-        p++;
-    }
-    *q = '\0';
-    return q - buf;
-}
-
-/*
- * Use /proc/bus/usb/devices or /dev/bus/usb/devices file to determine
- * host's USB devices. This is legacy support since many distributions
- * are moving to /sys/bus/usb
- */
-static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
-{
-    FILE *f = NULL;
-    char line[1024];
-    char buf[1024];
-    int bus_num, addr, speed, device_count;
-    int class_id, product_id, vendor_id, port;
-    char product_name[512];
-    int ret = 0;
-
-    if (!usb_host_device_path) {
-        perror("husb: USB Host Device Path not set");
-        goto the_end;
-    }
-    snprintf(line, sizeof(line), "%s/devices", usb_host_device_path);
-    f = fopen(line, "r");
-    if (!f) {
-        perror("husb: cannot open devices file");
-        goto the_end;
-    }
-
-    device_count = 0;
-    bus_num = addr = class_id = product_id = vendor_id = port = 0;
-    speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */
-    for(;;) {
-        if (fgets(line, sizeof(line), f) == NULL) {
-            break;
-        }
-        if (strlen(line) > 0) {
-            line[strlen(line) - 1] = '\0';
-        }
-        if (line[0] == 'T' && line[1] == ':') {
-            if (device_count && (vendor_id || product_id)) {
-                /* New device.  Add the previously discovered device.  */
-                if (port > 0) {
-                    snprintf(buf, sizeof(buf), "%d", port);
-                } else {
-                    snprintf(buf, sizeof(buf), "?");
-                }
-                ret = func(opaque, bus_num, addr, buf, class_id, vendor_id,
-                           product_id, product_name, speed);
-                if (ret) {
-                    goto the_end;
-                }
-            }
-            if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0) {
-                goto fail;
-            }
-            bus_num = atoi(buf);
-            if (get_tag_value(buf, sizeof(buf), line, "Port=", " ") < 0) {
-                goto fail;
-            }
-            port = atoi(buf);
-            if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) {
-                goto fail;
-            }
-            addr = atoi(buf);
-            if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) {
-                goto fail;
-            }
-            if (!strcmp(buf, "5000")) {
-                speed = USB_SPEED_SUPER;
-            } else if (!strcmp(buf, "480")) {
-                speed = USB_SPEED_HIGH;
-            } else if (!strcmp(buf, "1.5")) {
-                speed = USB_SPEED_LOW;
-            } else {
-                speed = USB_SPEED_FULL;
-            }
-            product_name[0] = '\0';
-            class_id = 0xff;
-            device_count++;
-            product_id = 0;
-            vendor_id = 0;
-        } else if (line[0] == 'P' && line[1] == ':') {
-            if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0) {
-                goto fail;
-            }
-            vendor_id = strtoul(buf, NULL, 16);
-            if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0) {
-                goto fail;
-            }
-            product_id = strtoul(buf, NULL, 16);
-        } else if (line[0] == 'S' && line[1] == ':') {
-            if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0) {
-                goto fail;
-            }
-            pstrcpy(product_name, sizeof(product_name), buf);
-        } else if (line[0] == 'D' && line[1] == ':') {
-            if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0) {
-                goto fail;
-            }
-            class_id = strtoul(buf, NULL, 16);
-        }
-    fail: ;
-    }
-    if (device_count && (vendor_id || product_id)) {
-        /* Add the last device.  */
-        if (port > 0) {
-            snprintf(buf, sizeof(buf), "%d", port);
-        } else {
-            snprintf(buf, sizeof(buf), "?");
-        }
-        ret = func(opaque, bus_num, addr, buf, class_id, vendor_id,
-                   product_id, product_name, speed);
-    }
- the_end:
-    if (f) {
-        fclose(f);
-    }
-    return ret;
-}
-
 /*
  * Read sys file-system device file
  *
@@ -1698,7 +1529,7 @@ static int usb_host_read_file(char *line, size_t line_size,
     int ret = 0;
     char filename[PATH_MAX];
 
-    snprintf(filename, PATH_MAX, USBSYSBUS_PATH "/devices/%s/%s", device_name,
+    snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
              device_file);
     f = fopen(filename, "r");
     if (f) {
@@ -1716,7 +1547,7 @@ static int usb_host_read_file(char *line, size_t line_size,
  * This code is based on Robert Schiele's original patches posted to
  * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
  */
-static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
+static int usb_host_scan(void *opaque, USBScanFunc *func)
 {
     DIR *dir = NULL;
     char line[1024];
@@ -1726,9 +1557,10 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
     char product_name[512];
     struct dirent *de;
 
-    dir = opendir(USBSYSBUS_PATH "/devices");
+    dir = opendir("/sys/bus/usb/devices");
     if (!dir) {
-        perror("husb: cannot open devices directory");
+        perror("husb: opendir /sys/bus/usb/devices");
+        fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
         goto the_end;
     }
 
@@ -1803,81 +1635,6 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
     return ret;
 }
 
-/*
- * Determine how to access the host's USB devices and call the
- * specific support function.
- */
-static int usb_host_scan(void *opaque, USBScanFunc *func)
-{
-    Monitor *mon = cur_mon;
-    FILE *f = NULL;
-    DIR *dir = NULL;
-    int ret = 0;
-    const char *fs_type[] = {"unknown", "proc", "dev", "sys"};
-    char devpath[PATH_MAX];
-
-    /* only check the host once */
-    if (!usb_fs_type) {
-        dir = opendir(USBSYSBUS_PATH "/devices");
-        if (dir) {
-            /* devices found in /dev/bus/usb/ (yes - not a mistake!) */
-            strcpy(devpath, USBDEVBUS_PATH);
-            usb_fs_type = USB_FS_SYS;
-            closedir(dir);
-            DPRINTF(USBDBG_DEVOPENED, USBSYSBUS_PATH);
-            goto found_devices;
-        }
-        f = fopen(USBPROCBUS_PATH "/devices", "r");
-        if (f) {
-            /* devices found in /proc/bus/usb/ */
-            strcpy(devpath, USBPROCBUS_PATH);
-            usb_fs_type = USB_FS_PROC;
-            fclose(f);
-            DPRINTF(USBDBG_DEVOPENED, USBPROCBUS_PATH);
-            goto found_devices;
-        }
-        /* try additional methods if an access method hasn't been found yet */
-        f = fopen(USBDEVBUS_PATH "/devices", "r");
-        if (f) {
-            /* devices found in /dev/bus/usb/ */
-            strcpy(devpath, USBDEVBUS_PATH);
-            usb_fs_type = USB_FS_DEV;
-            fclose(f);
-            DPRINTF(USBDBG_DEVOPENED, USBDEVBUS_PATH);
-            goto found_devices;
-        }
-    found_devices:
-        if (!usb_fs_type) {
-            if (mon) {
-                monitor_printf(mon, "husb: unable to access USB devices\n");
-            }
-            return -ENOENT;
-        }
-
-        /* the module setting (used later for opening devices) */
-        usb_host_device_path = g_malloc0(strlen(devpath)+1);
-        strcpy(usb_host_device_path, devpath);
-        if (mon) {
-            monitor_printf(mon, "husb: using %s file-system with %s\n",
-                           fs_type[usb_fs_type], usb_host_device_path);
-        }
-    }
-
-    switch (usb_fs_type) {
-    case USB_FS_PROC:
-    case USB_FS_DEV:
-        ret = usb_host_scan_dev(opaque, func);
-        break;
-    case USB_FS_SYS:
-        ret = usb_host_scan_sys(opaque, func);
-        break;
-    default:
-        ret = -EINVAL;
-        break;
-    }
-    return ret;
-}
-
 static QEMUTimer *usb_auto_timer;
 
 static int usb_host_auto_scan(void *opaque, int bus_num,
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 02/17] usb: track configuration and interface count in USBDevice.
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 01/17] usb-host: rip out legacy procfs support Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 03/17] usb: track altsetting " Gerd Hoffmann
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Move fields from USBHostDevice to USBDevice.
Add bits to usb-desc.c to fill them for emulated devices too.
Also allow to set configuration 0 (== None) for emulated devices.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-desc.c |   34 ++++++++++++++++++++++++++--------
 hw/usb.h      |    3 +++
 usb-linux.c   |   21 +++++++++------------
 3 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index ae2d384..b52561a 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -223,6 +223,29 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
 
 /* ------------------------------------------------------------------ */
 
+static int usb_desc_set_config(USBDevice *dev, int value)
+{
+    int i;
+
+    if (value == 0) {
+        dev->configuration = 0;
+        dev->ninterfaces   = 0;
+        dev->config = NULL;
+    } else {
+        for (i = 0; i < dev->device->bNumConfigurations; i++) {
+            if (dev->device->confs[i].bConfigurationValue == value) {
+                dev->configuration = value;
+                dev->ninterfaces   = dev->device->confs[i].bNumInterfaces;
+                dev->config = dev->device->confs + i;
+            }
+        }
+        if (i < dev->device->bNumConfigurations) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
 static void usb_desc_setdefaults(USBDevice *dev)
 {
     const USBDesc *desc = dev->info->usb_desc;
@@ -237,7 +260,7 @@ static void usb_desc_setdefaults(USBDevice *dev)
         dev->device = desc->high;
         break;
     }
-    dev->config = dev->device->confs;
+    usb_desc_set_config(dev, 0);
 }
 
 void usb_desc_init(USBDevice *dev)
@@ -408,7 +431,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         int request, int value, int index, int length, uint8_t *data)
 {
     const USBDesc *desc = dev->info->usb_desc;
-    int i, ret = -1;
+    int ret = -1;
 
     assert(desc != NULL);
     switch(request) {
@@ -427,12 +450,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         ret = 1;
         break;
     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        for (i = 0; i < dev->device->bNumConfigurations; i++) {
-            if (dev->device->confs[i].bConfigurationValue == value) {
-                dev->config = dev->device->confs + i;
-                ret = 0;
-            }
-        }
+        ret = usb_desc_set_config(dev, value);
         trace_usb_set_config(dev->addr, value, ret);
         break;
 
diff --git a/hw/usb.h b/hw/usb.h
index c6e1870..1ef53a1 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -188,6 +188,9 @@ struct USBDevice {
 
     QLIST_HEAD(, USBDescString) strings;
     const USBDescDevice *device;
+
+    int configuration;
+    int ninterfaces;
     const USBDescConfig *config;
 };
 
diff --git a/usb-linux.c b/usb-linux.c
index c68e194..3aaa93b 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -106,8 +106,6 @@ typedef struct USBHostDevice {
 
     uint8_t   descr[8192];
     int       descr_len;
-    int       configuration;
-    int       ninterfaces;
     int       closing;
     uint32_t  iso_urb_count;
     Notifier  exit;
@@ -547,8 +545,8 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
     int ret, i;
 
     if (configuration == 0) { /* address state - ignore */
-        dev->ninterfaces   = 0;
-        dev->configuration = 0;
+        dev->dev.ninterfaces   = 0;
+        dev->dev.configuration = 0;
         return 1;
     }
 
@@ -606,8 +604,8 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
     trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
                                     nb_interfaces, configuration);
 
-    dev->ninterfaces   = nb_interfaces;
-    dev->configuration = configuration;
+    dev->dev.ninterfaces   = nb_interfaces;
+    dev->dev.configuration = configuration;
     return 1;
 
 fail:
@@ -624,7 +622,7 @@ static int usb_host_release_interfaces(USBHostDevice *s)
 
     trace_usb_host_release_interfaces(s->bus_num, s->addr);
 
-    for (i = 0; i < s->ninterfaces; i++) {
+    for (i = 0; i < s->dev.ninterfaces; i++) {
         ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
         if (ret < 0) {
             perror("USBDEVFS_RELEASEINTERFACE");
@@ -1123,7 +1121,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
         s->ep_out[i].type = INVALID_EP_TYPE;
     }
 
-    if (s->configuration == 0) {
+    if (s->dev.configuration == 0) {
         /* not configured yet -- leave all endpoints disabled */
         return 0;
     }
@@ -1138,12 +1136,11 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
         if (descriptors[i + 1] != USB_DT_CONFIG) {
             fprintf(stderr, "invalid descriptor data\n");
             return 1;
-        } else if (descriptors[i + 5] != s->configuration) {
-            DPRINTF("not requested configuration %d\n", s->configuration);
+        } else if (descriptors[i + 5] != s->dev.configuration) {
+            DPRINTF("not requested configuration %d\n", s->dev.configuration);
             i += (descriptors[i + 3] << 8) + descriptors[i + 2];
             continue;
         }
-
         i += descriptors[i];
 
         if (descriptors[i + 1] != USB_DT_INTERFACE ||
@@ -1154,7 +1151,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
         }
 
         interface = descriptors[i + 2];
-        alt_interface = usb_linux_get_alt_setting(s, s->configuration,
+        alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
                                                   interface);
 
         /* the current interface descriptor is the active interface
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 03/17] usb: track altsetting in USBDevice
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 01/17] usb-host: rip out legacy procfs support Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 02/17] usb: track configuration and interface count in USBDevice Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 04/17] usb-desc: audio endpoint support Gerd Hoffmann
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Also handle {GET,SET}_INTERFACE in common code (usb-desc.c).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-bt.c     |   22 -----------------
 hw/usb-ccid.c   |    8 ------
 hw/usb-desc.c   |   71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb-hid.c    |    7 -----
 hw/usb-hub.c    |    7 -----
 hw/usb-msd.c    |   10 -------
 hw/usb-net.c    |   11 --------
 hw/usb-serial.c |    7 -----
 hw/usb-wacom.c  |    7 -----
 hw/usb.h        |    8 ++++++
 trace-events    |    1 +
 usb-linux.c     |   10 +++++++
 12 files changed, 90 insertions(+), 79 deletions(-)

diff --git a/hw/usb-bt.c b/hw/usb-bt.c
index f30eec1..0c1270b 100644
--- a/hw/usb-bt.c
+++ b/hw/usb-bt.c
@@ -28,7 +28,6 @@ struct USBBtState {
     USBDevice dev;
     struct HCIInfo *hci;
 
-    int altsetting;
     int config;
 
 #define CFIFO_LEN_MASK	255
@@ -362,7 +361,6 @@ static void usb_bt_handle_reset(USBDevice *dev)
     s->outcmd.len = 0;
     s->outacl.len = 0;
     s->outsco.len = 0;
-    s->altsetting = 0;
 }
 
 static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
@@ -402,26 +400,6 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
     case EndpointOutRequest | USB_REQ_SET_FEATURE:
         goto fail;
         break;
-    case InterfaceRequest | USB_REQ_GET_INTERFACE:
-        if (value != 0 || (index & ~1) || length != 1)
-            goto fail;
-        if (index == 1)
-            data[0] = s->altsetting;
-        else
-            data[0] = 0;
-        ret = 1;
-        break;
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        if ((index & ~1) || length != 0 ||
-                        (index == 1 && (value < 0 || value > 4)) ||
-                        (index == 0 && value != 0)) {
-            printf("%s: Wrong SET_INTERFACE request (%i, %i)\n",
-                            __FUNCTION__, index, value);
-            goto fail;
-        }
-        s->altsetting = value;
-        ret = 0;
-        break;
     case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
         if (s->config)
             usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index cd349f3..e9935ad 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -611,14 +611,6 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
     }
 
     switch (request) {
-    case DeviceRequest | USB_REQ_GET_INTERFACE:
-        data[0] = 0;
-        ret = 1;
-        break;
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
-
         /* Class specific requests.  */
     case InterfaceOutClass | CCID_CONTROL_ABORT:
         DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index b52561a..b9996a1 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -223,6 +223,54 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
 
 /* ------------------------------------------------------------------ */
 
+static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
+                                                   int nif, int alt)
+{
+    const USBDescIface *iface;
+    int g, i;
+
+    if (!dev->config) {
+        return NULL;
+    }
+    for (g = 0; g < dev->config->nif_groups; g++) {
+        for (i = 0; i < dev->config->if_groups[g].nif; i++) {
+            iface = &dev->config->if_groups[g].ifs[i];
+            if (iface->bInterfaceNumber == nif &&
+                iface->bAlternateSetting == alt) {
+                return iface;
+            }
+        }
+    }
+    for (i = 0; i < dev->config->nif; i++) {
+        iface = &dev->config->ifs[i];
+        if (iface->bInterfaceNumber == nif &&
+            iface->bAlternateSetting == alt) {
+            return iface;
+        }
+    }
+    return NULL;
+}
+
+static int usb_desc_set_interface(USBDevice *dev, int index, int value)
+{
+    const USBDescIface *iface;
+    int old;
+
+    iface = usb_desc_find_interface(dev, index, value);
+    if (iface == NULL) {
+        return -1;
+    }
+
+    old = dev->altsetting[index];
+    dev->altsetting[index] = value;
+    dev->ifaces[index] = iface;
+
+    if (dev->info->set_interface && old != value) {
+        dev->info->set_interface(dev, index, old, value);
+    }
+    return 0;
+}
+
 static int usb_desc_set_config(USBDevice *dev, int value)
 {
     int i;
@@ -237,12 +285,22 @@ static int usb_desc_set_config(USBDevice *dev, int value)
                 dev->configuration = value;
                 dev->ninterfaces   = dev->device->confs[i].bNumInterfaces;
                 dev->config = dev->device->confs + i;
+                assert(dev->ninterfaces <= USB_MAX_INTERFACES);
             }
         }
         if (i < dev->device->bNumConfigurations) {
             return -1;
         }
     }
+
+    for (i = 0; i < dev->ninterfaces; i++) {
+        usb_desc_set_interface(dev, i, 0);
+    }
+    for (; i < USB_MAX_INTERFACES; i++) {
+        dev->altsetting[i] = 0;
+        dev->ifaces[i] = NULL;
+    }
+
     return 0;
 }
 
@@ -479,6 +537,19 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         }
         trace_usb_set_device_feature(dev->addr, value, ret);
         break;
+
+    case InterfaceRequest | USB_REQ_GET_INTERFACE:
+        if (index < 0 || index >= dev->ninterfaces) {
+            break;
+        }
+        data[0] = dev->altsetting[index];
+        ret = 1;
+        break;
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = usb_desc_set_interface(dev, index, value);
+        trace_usb_set_interface(dev->addr, index, value, ret);
+        break;
+
     }
     return ret;
 }
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index a110c74..997f828 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -384,13 +384,6 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = 0;
     switch (request) {
-    case DeviceRequest | USB_REQ_GET_INTERFACE:
-        data[0] = 0;
-        ret = 1;
-        break;
-    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
         /* hid specific requests */
     case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
         switch (value >> 8) {
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index e195937..069611b 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -258,13 +258,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
         }
         ret = 0;
         break;
-    case DeviceRequest | USB_REQ_GET_INTERFACE:
-        data[0] = 0;
-        ret = 1;
-        break;
-    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
         /* usb specific requests */
     case GetHubStatus:
         data[0] = 0;
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index e427296..186831d 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -306,19 +306,9 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = 0;
     switch (request) {
-    case DeviceRequest | USB_REQ_GET_INTERFACE:
-        data[0] = 0;
-        ret = 1;
-        break;
-    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
         ret = 0;
         break;
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
         /* Class specific requests.  */
     case ClassInterfaceOutRequest | MassStorageReset:
         /* Reset state ready for the next CBW.  */
diff --git a/hw/usb-net.c b/hw/usb-net.c
index f91fa32..5622bed 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1098,17 +1098,6 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
 #endif
         break;
 
-    case DeviceRequest | USB_REQ_GET_INTERFACE:
-    case InterfaceRequest | USB_REQ_GET_INTERFACE:
-        data[0] = 0;
-        ret = 1;
-        break;
-
-    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
-
     default:
     fail:
         fprintf(stderr, "usbnet: failed control transaction: "
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 7dbf6df..e3c8238 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -233,13 +233,6 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = 0;
     switch (request) {
-    case DeviceRequest | USB_REQ_GET_INTERFACE:
-        data[0] = 0;
-        ret = 1;
-        break;
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
         ret = 0;
         break;
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index 2558006..61d5b18 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -263,13 +263,6 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = 0;
     switch (request) {
-    case DeviceRequest | USB_REQ_GET_INTERFACE:
-        data[0] = 0;
-        ret = 1;
-        break;
-    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = 0;
-        break;
     case WACOM_SET_REPORT:
         if (s->mouse_grabbed) {
             qemu_remove_mouse_event_handler(s->eh_entry);
diff --git a/hw/usb.h b/hw/usb.h
index 1ef53a1..1496f76 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -161,6 +161,9 @@ struct USBDescString {
     QLIST_ENTRY(USBDescString) next;
 };
 
+#define USB_MAX_ENDPOINTS  15
+#define USB_MAX_INTERFACES 16
+
 /* definition of a USB device */
 struct USBDevice {
     DeviceState qdev;
@@ -191,7 +194,9 @@ struct USBDevice {
 
     int configuration;
     int ninterfaces;
+    int altsetting[USB_MAX_INTERFACES];
     const USBDescConfig *config;
+    const USBDescIface  *ifaces[USB_MAX_INTERFACES];
 };
 
 struct USBDeviceInfo {
@@ -244,6 +249,9 @@ struct USBDeviceInfo {
      */
     int (*handle_data)(USBDevice *dev, USBPacket *p);
 
+    void (*set_interface)(USBDevice *dev, int interface,
+                          int alt_old, int alt_new);
+
     const char *product_desc;
     const USBDesc *usb_desc;
 
diff --git a/trace-events b/trace-events
index c18435b..834eb7f 100644
--- a/trace-events
+++ b/trace-events
@@ -246,6 +246,7 @@ usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query
 usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
 usb_set_addr(int addr) "dev %d"
 usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
+usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d"
 usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
 usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
 
diff --git a/usb-linux.c b/usb-linux.c
index 3aaa93b..ded0726 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -544,6 +544,10 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
     int interface, nb_interfaces;
     int ret, i;
 
+    for (i = 0; i < USB_MAX_INTERFACES; i++) {
+        dev->dev.altsetting[i] = 0;
+    }
+
     if (configuration == 0) { /* address state - ignore */
         dev->dev.ninterfaces   = 0;
         dev->dev.configuration = 0;
@@ -997,6 +1001,10 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
         }
     }
 
+    if (iface >= USB_MAX_INTERFACES) {
+        return USB_RET_STALL;
+    }
+
     si.interface  = iface;
     si.altsetting = alt;
     ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
@@ -1007,6 +1015,8 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
     if (ret < 0) {
         return ctrl_error();
     }
+
+    s->dev.altsetting[iface] = alt;
     usb_linux_update_endp_table(s);
     return 0;
 }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 04/17] usb-desc: audio endpoint support
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 03/17] usb: track altsetting " Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 05/17] usb: add audio device model Gerd Hoffmann
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Add support for audio endpoints which have two more fields in the
descriptor.  Also add support for extra class specific endpoint
descriptors.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-desc.c |   14 +++++++++++---
 hw/usb-desc.h |    5 +++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index b9996a1..9c38661 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -192,9 +192,10 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
 
 int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
 {
-    uint8_t bLength = 0x07;
+    uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
+    uint8_t extralen = ep->extra ? ep->extra[0] : 0;
 
-    if (len < bLength) {
+    if (len < bLength + extralen) {
         return -1;
     }
 
@@ -205,8 +206,15 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
     dest[0x04] = usb_lo(ep->wMaxPacketSize);
     dest[0x05] = usb_hi(ep->wMaxPacketSize);
     dest[0x06] = ep->bInterval;
+    if (ep->is_audio) {
+        dest[0x07] = ep->bRefresh;
+        dest[0x08] = ep->bSynchAddress;
+    }
+    if (ep->extra) {
+        memcpy(dest + bLength, ep->extra, extralen);
+    }
 
-    return bLength;
+    return bLength + extralen;
 }
 
 int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
index 5c14e4a..d6e07ea 100644
--- a/hw/usb-desc.h
+++ b/hw/usb-desc.h
@@ -71,6 +71,11 @@ struct USBDescEndpoint {
     uint8_t                   bmAttributes;
     uint16_t                  wMaxPacketSize;
     uint8_t                   bInterval;
+    uint8_t                   bRefresh;
+    uint8_t                   bSynchAddress;
+
+    uint8_t                   is_audio; /* has bRefresh + bSynchAddress */
+    uint8_t                   *extra;
 };
 
 struct USBDescOther {
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 05/17] usb: add audio device model
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (3 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 04/17] usb-desc: audio endpoint support Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 06/17] xhci: Initial xHCI implementation Gerd Hoffmann
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: H. Peter Anvin, Gerd Hoffmann

From: H. Peter Anvin <hpa@linux.intel.com>

This brings a usb audio device to qemu.  Output only, fixed at
16bit stereo @ 480000 Hz.  Based on a patch from
H. Peter Anvin <hpa@linux.intel.com>

Usage: add '-device usb-audio' to your qemu command line.

Works sorta ok on a idle machine.  Known issues:

 * Is *very* sensitive to latencies.
 * Burns quite some CPU due to usb polling.

In short:  It brings the qemu usb emulation to its limits.  Enjoy!

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.objs  |    2 +-
 hw/usb-audio.c |  704 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb-net.c   |    3 -
 hw/usb.h       |    7 +
 4 files changed, 712 insertions(+), 4 deletions(-)
 create mode 100644 hw/usb-audio.c

diff --git a/Makefile.objs b/Makefile.objs
index 4f6d26c..8956cb9 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -102,7 +102,7 @@ common-obj-y += scsi-disk.o cdrom.o
 common-obj-y += scsi-generic.o scsi-bus.o
 common-obj-y += hid.o
 common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
-common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
+common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o usb-audio.o
 common-obj-$(CONFIG_SSI) += ssi.o
 common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
 common-obj-$(CONFIG_SD) += sd.o
diff --git a/hw/usb-audio.c b/hw/usb-audio.c
new file mode 100644
index 0000000..b22d578
--- /dev/null
+++ b/hw/usb-audio.c
@@ -0,0 +1,704 @@
+/*
+ * QEMU USB audio device
+ *
+ * written by:
+ *  H. Peter Anvin <hpa@linux.intel.com>
+ *  Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * lousely based on usb net device code which is:
+ *
+ * Copyright (c) 2006 Thomas Sailer
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "usb-desc.h"
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+
+#define USBAUDIO_VENDOR_NUM     0x46f4 /* CRC16() of "QEMU" */
+#define USBAUDIO_PRODUCT_NUM    0x0002
+
+#define DEV_CONFIG_VALUE        1 /* The one and only */
+
+/* Descriptor subtypes for AC interfaces */
+#define DST_AC_HEADER           1
+#define DST_AC_INPUT_TERMINAL   2
+#define DST_AC_OUTPUT_TERMINAL  3
+#define DST_AC_FEATURE_UNIT     6
+/* Descriptor subtypes for AS interfaces */
+#define DST_AS_GENERAL          1
+#define DST_AS_FORMAT_TYPE      2
+/* Descriptor subtypes for endpoints */
+#define DST_EP_GENERAL          1
+
+enum usb_audio_strings {
+    STRING_NULL,
+    STRING_MANUFACTURER,
+    STRING_PRODUCT,
+    STRING_SERIALNUMBER,
+    STRING_CONFIG,
+    STRING_USBAUDIO_CONTROL,
+    STRING_INPUT_TERMINAL,
+    STRING_FEATURE_UNIT,
+    STRING_OUTPUT_TERMINAL,
+    STRING_NULL_STREAM,
+    STRING_REAL_STREAM,
+};
+
+static const USBDescStrings usb_audio_stringtable = {
+    [STRING_MANUFACTURER]       = "QEMU",
+    [STRING_PRODUCT]            = "QEMU USB Audio",
+    [STRING_SERIALNUMBER]       = "1",
+    [STRING_CONFIG]             = "Audio Configuration",
+    [STRING_USBAUDIO_CONTROL]   = "Audio Device",
+    [STRING_INPUT_TERMINAL]     = "Audio Output Pipe",
+    [STRING_FEATURE_UNIT]       = "Audio Output Volume Control",
+    [STRING_OUTPUT_TERMINAL]    = "Audio Output Terminal",
+    [STRING_NULL_STREAM]        = "Audio Output - Disabled",
+    [STRING_REAL_STREAM]        = "Audio Output - 48 kHz Stereo",
+};
+
+#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff)
+#define U24(x) U16(x), (((x) >> 16) & 0xff)
+#define U32(x) U24(x), (((x) >> 24) & 0xff)
+
+/*
+ * A Basic Audio Device uses these specific values
+ */
+#define USBAUDIO_PACKET_SIZE     192
+#define USBAUDIO_SAMPLE_RATE     48000
+#define USBAUDIO_PACKET_INTERVAL 1
+
+static const USBDescIface desc_iface[] = {
+    {
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_CONTROL,
+        .bInterfaceProtocol            = 0x04,
+        .iInterface                    = STRING_USBAUDIO_CONTROL,
+        .ndesc                         = 4,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-Specific AC Interface Header Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_HEADER,              /*  u8  bDescriptorSubtype */
+                    U16(0x0100),                /* u16  bcdADC */
+                    U16(0x2b),                  /* u16  wTotalLength */
+                    0x01,                       /*  u8  bInCollection */
+                    0x01,                       /*  u8  baInterfaceNr */
+                }
+            },{
+                /* Generic Stereo Input Terminal ID1 Descriptor */
+                .data = (uint8_t[]) {
+                    0x0c,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_INPUT_TERMINAL,      /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalID */
+                    U16(0x0101),                /* u16  wTerminalType */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x02,                       /* u16  bNrChannels */
+                    U16(0x0003),                /* u16  wChannelConfig */
+                    0x00,                       /*  u8  iChannelNames */
+                    STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
+                }
+            },{
+                /* Generic Stereo Feature Unit ID2 Descriptor */
+                .data = (uint8_t[]) {
+                    0x0d,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_FEATURE_UNIT,        /*  u8  bDescriptorSubtype */
+                    0x02,                       /*  u8  bUnitID */
+                    0x01,                       /*  u8  bSourceID */
+                    0x02,                       /*  u8  bControlSize */
+                    U16(0x0001),                /* u16  bmaControls(0) */
+                    U16(0x0002),                /* u16  bmaControls(1) */
+                    U16(0x0002),                /* u16  bmaControls(2) */
+                    STRING_FEATURE_UNIT,        /*  u8  iFeature */
+                }
+            },{
+                /* Headphone Ouptut Terminal ID3 Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_OUTPUT_TERMINAL,     /*  u8  bDescriptorSubtype */
+                    0x03,                       /*  u8  bUnitID */
+                    U16(0x0301),                /* u16  wTerminalType (SPK) */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x02,                       /*  u8  bSourceID */
+                    STRING_OUTPUT_TERMINAL,     /*  u8  iTerminal */
+                }
+            }
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_NULL_STREAM,
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 1,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_REAL_STREAM,
+        .ndesc                         = 2,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-specific AS General Interface Descriptor */
+                .data = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalLink */
+                    0x00,                       /*  u8  bDelay */
+                    0x01, 0x00,                 /* u16  wFormatTag */
+                }
+            },{
+                /* Headphone Type I Format Type Descriptor */
+                .data = (uint8_t[]) {
+                    0x0b,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bFormatType */
+                    0x02,                       /*  u8  bNrChannels */
+                    0x02,                       /*  u8  bSubFrameSize */
+                    0x10,                       /*  u8  bBitResolution */
+                    0x01,                       /*  u8  bSamFreqType */
+                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
+                }
+            }
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | 0x01,
+                .bmAttributes          = 0x0d,
+                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE,
+                .bInterval             = 1,
+                .is_audio              = 1,
+                /* Stereo Headphone Class-specific
+                   AS Audio Data Endpoint Descriptor */
+                .extra = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
+                    DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x00,                       /*  u8  bmAttributes */
+                    0x00,                       /*  u8  bLockDelayUnits */
+                    U16(0x0000),                /* u16  wLockDelay */
+                },
+            },
+        }
+    }
+};
+
+static const USBDescDevice desc_device = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = DEV_CONFIG_VALUE,
+            .iConfiguration        = STRING_CONFIG,
+            .bmAttributes          = 0xc0,
+            .bMaxPower             = 0x32,
+            .nif = ARRAY_SIZE(desc_iface),
+            .ifs = desc_iface,
+        },
+    },
+};
+
+static const USBDesc desc_audio = {
+    .id = {
+        .idVendor          = USBAUDIO_VENDOR_NUM,
+        .idProduct         = USBAUDIO_PRODUCT_NUM,
+        .bcdDevice         = 0,
+        .iManufacturer     = STRING_MANUFACTURER,
+        .iProduct          = STRING_PRODUCT,
+        .iSerialNumber     = STRING_SERIALNUMBER,
+    },
+    .full = &desc_device,
+    .str  = usb_audio_stringtable,
+};
+
+/*
+ * A USB audio device supports an arbitrary number of alternate
+ * interface settings for each interface.  Each corresponds to a block
+ * diagram of parameterized blocks.  This can thus refer to things like
+ * number of channels, data rates, or in fact completely different
+ * block diagrams.  Alternative setting 0 is always the null block diagram,
+ * which is used by a disabled device.
+ */
+enum usb_audio_altset {
+    ALTSET_OFF  = 0x00,         /* No endpoint */
+    ALTSET_ON   = 0x01,         /* Single endpoint */
+};
+
+/*
+ * Class-specific control requests
+ */
+#define CR_SET_CUR      0x01
+#define CR_GET_CUR      0x81
+#define CR_SET_MIN      0x02
+#define CR_GET_MIN      0x82
+#define CR_SET_MAX      0x03
+#define CR_GET_MAX      0x83
+#define CR_SET_RES      0x04
+#define CR_GET_RES      0x84
+#define CR_SET_MEM      0x05
+#define CR_GET_MEM      0x85
+#define CR_GET_STAT     0xff
+
+/*
+ * Feature Unit Control Selectors
+ */
+#define MUTE_CONTROL                    0x01
+#define VOLUME_CONTROL                  0x02
+#define BASS_CONTROL                    0x03
+#define MID_CONTROL                     0x04
+#define TREBLE_CONTROL                  0x05
+#define GRAPHIC_EQUALIZER_CONTROL       0x06
+#define AUTOMATIC_GAIN_CONTROL          0x07
+#define DELAY_CONTROL                   0x08
+#define BASS_BOOST_CONTROL              0x09
+#define LOUDNESS_CONTROL                0x0a
+
+/*
+ * buffering
+ */
+
+struct streambuf {
+    uint8_t *data;
+    uint32_t size;
+    uint32_t prod;
+    uint32_t cons;
+};
+
+static void streambuf_init(struct streambuf *buf, uint32_t size)
+{
+    g_free(buf->data);
+    buf->size = size - (size % USBAUDIO_PACKET_SIZE);
+    buf->data = g_malloc(buf->size);
+    buf->prod = 0;
+    buf->cons = 0;
+}
+
+static void streambuf_fini(struct streambuf *buf)
+{
+    g_free(buf->data);
+    buf->data = NULL;
+}
+
+static int streambuf_put(struct streambuf *buf, USBPacket *p)
+{
+    uint32_t free = buf->size - (buf->prod - buf->cons);
+
+    if (!free) {
+        return 0;
+    }
+    assert(free >= USBAUDIO_PACKET_SIZE);
+    usb_packet_copy(p, buf->data + (buf->prod % buf->size),
+                    USBAUDIO_PACKET_SIZE);
+    buf->prod += USBAUDIO_PACKET_SIZE;
+    return USBAUDIO_PACKET_SIZE;
+}
+
+static uint8_t *streambuf_get(struct streambuf *buf)
+{
+    uint32_t used = buf->prod - buf->cons;
+    uint8_t *data;
+
+    if (!used) {
+        return NULL;
+    }
+    assert(used >= USBAUDIO_PACKET_SIZE);
+    data = buf->data + (buf->cons % buf->size);
+    buf->cons += USBAUDIO_PACKET_SIZE;
+    return data;
+}
+
+typedef struct USBAudioState {
+    /* qemu interfaces */
+    USBDevice dev;
+    QEMUSoundCard card;
+
+    /* state */
+    struct {
+        enum usb_audio_altset altset;
+        struct audsettings as;
+        SWVoiceOut *voice;
+        bool mute;
+        uint8_t vol[2];
+        struct streambuf buf;
+    } out;
+
+    /* properties */
+    uint32_t debug;
+    uint32_t buffer;
+} USBAudioState;
+
+static void output_callback(void *opaque, int avail)
+{
+    USBAudioState *s = opaque;
+    uint8_t *data;
+
+    for (;;) {
+        if (avail < USBAUDIO_PACKET_SIZE) {
+            return;
+        }
+        data = streambuf_get(&s->out.buf);
+        if (NULL == data) {
+            return;
+        }
+        AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE);
+        avail -= USBAUDIO_PACKET_SIZE;
+    }
+}
+
+static int usb_audio_set_output_altset(USBAudioState *s, int altset)
+{
+    switch (altset) {
+    case ALTSET_OFF:
+        streambuf_init(&s->out.buf, s->buffer);
+        AUD_set_active_out(s->out.voice, false);
+        break;
+    case ALTSET_ON:
+        AUD_set_active_out(s->out.voice, true);
+        break;
+    default:
+        return -1;
+    }
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: set interface %d\n", altset);
+    }
+    s->out.altset = altset;
+    return 0;
+}
+
+/*
+ * Note: we arbitrarily map the volume control range onto -inf..+8 dB
+ */
+#define ATTRIB_ID(cs, attrib, idif)     \
+    (((cs) << 24) | ((attrib) << 16) | (idif))
+
+static int usb_audio_get_control(USBAudioState *s, uint8_t attrib,
+                                 uint16_t cscn, uint16_t idif,
+                                 int length, uint8_t *data)
+{
+    uint8_t cs = cscn >> 8;
+    uint8_t cn = cscn - 1;      /* -1 for the non-present master control */
+    uint32_t aid = ATTRIB_ID(cs, attrib, idif);
+    int ret = USB_RET_STALL;
+
+    switch (aid) {
+    case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200):
+        data[0] = s->out.mute;
+        ret = 1;
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
+        if (cn < 2) {
+            uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
+            data[0] = vol;
+            data[1] = vol >> 8;
+            ret = 2;
+        }
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200):
+        if (cn < 2) {
+            data[0] = 0x01;
+            data[1] = 0x80;
+            ret = 2;
+        }
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200):
+        if (cn < 2) {
+            data[0] = 0x00;
+            data[1] = 0x08;
+            ret = 2;
+        }
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200):
+        if (cn < 2) {
+            data[0] = 0x88;
+            data[1] = 0x00;
+            ret = 2;
+        }
+        break;
+    }
+
+    return ret;
+}
+static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
+                                 uint16_t cscn, uint16_t idif,
+                                 int length, uint8_t *data)
+{
+    uint8_t cs = cscn >> 8;
+    uint8_t cn = cscn - 1;      /* -1 for the non-present master control */
+    uint32_t aid = ATTRIB_ID(cs, attrib, idif);
+    int ret = USB_RET_STALL;
+    bool set_vol = false;
+
+    switch (aid) {
+    case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200):
+        s->out.mute = data[0] & 1;
+        set_vol = true;
+        ret = 0;
+        break;
+    case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200):
+        if (cn < 2) {
+            uint16_t vol = data[0] + (data[1] << 8);
+
+            if (s->debug) {
+                fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
+            }
+
+            vol -= 0x8000;
+            vol = (vol * 255 + 0x4400) / 0x8800;
+            if (vol > 255) {
+                vol = 255;
+            }
+
+            s->out.vol[cn] = vol;
+            set_vol = true;
+            ret = 0;
+        }
+        break;
+    }
+
+    if (set_vol) {
+        if (s->debug) {
+            fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
+                    s->out.mute, s->out.vol[0], s->out.vol[1]);
+        }
+        AUD_set_volume_out(s->out.voice, s->out.mute,
+                           s->out.vol[0], s->out.vol[1]);
+    }
+
+    return ret;
+}
+
+static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
+                                    int request, int value, int index,
+                                    int length, uint8_t *data)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+    int ret = 0;
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: control transaction: "
+                "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
+                request, value, index, length);
+    }
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
+    switch (request) {
+    case ClassInterfaceRequest | CR_GET_CUR:
+    case ClassInterfaceRequest | CR_GET_MIN:
+    case ClassInterfaceRequest | CR_GET_MAX:
+    case ClassInterfaceRequest | CR_GET_RES:
+        ret = usb_audio_get_control(s, request & 0xff, value, index,
+                                    length, data);
+        if (ret < 0) {
+            if (s->debug) {
+                fprintf(stderr, "usb-audio: fail: get control\n");
+            }
+            goto fail;
+        }
+        break;
+
+    case ClassInterfaceOutRequest | CR_SET_CUR:
+    case ClassInterfaceOutRequest | CR_SET_MIN:
+    case ClassInterfaceOutRequest | CR_SET_MAX:
+    case ClassInterfaceOutRequest | CR_SET_RES:
+        ret = usb_audio_set_control(s, request & 0xff, value, index,
+                                    length, data);
+        if (ret < 0) {
+            if (s->debug) {
+                fprintf(stderr, "usb-audio: fail: set control\n");
+            }
+            goto fail;
+        }
+        break;
+
+    default:
+fail:
+        if (s->debug) {
+            fprintf(stderr, "usb-audio: failed control transaction: "
+                    "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
+                    request, value, index, length);
+        }
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_audio_set_interface(USBDevice *dev, int iface,
+                                    int old, int value)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    if (iface == 1) {
+        usb_audio_set_output_altset(s, value);
+    }
+}
+
+static void usb_audio_handle_reset(USBDevice *dev)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: reset\n");
+    }
+    usb_audio_set_output_altset(s, ALTSET_OFF);
+}
+
+static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
+{
+    int rc;
+
+    if (s->out.altset == ALTSET_OFF) {
+        return USB_RET_STALL;
+    }
+
+    rc = streambuf_put(&s->out.buf, p);
+    if (rc < p->iov.size && s->debug > 1) {
+        fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
+                p->iov.size - rc);
+    }
+
+    return 0;
+}
+
+static int usb_audio_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBAudioState *s = (USBAudioState *) dev;
+    int ret = 0;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        switch (p->devep) {
+        case 1:
+            ret = usb_audio_handle_dataout(s, p);
+            break;
+        default:
+            goto fail;
+        }
+        break;
+
+    default:
+fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    if (ret == USB_RET_STALL && s->debug) {
+        fprintf(stderr, "usb-audio: failed data transaction: "
+                        "pid 0x%x ep 0x%x len 0x%zx\n",
+                        p->pid, p->devep, p->iov.size);
+    }
+    return ret;
+}
+
+static void usb_audio_handle_destroy(USBDevice *dev)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    if (s->debug) {
+        fprintf(stderr, "usb-audio: destroy\n");
+    }
+
+    usb_audio_set_output_altset(s, ALTSET_OFF);
+    AUD_close_out(&s->card, s->out.voice);
+    AUD_remove_card(&s->card);
+
+    streambuf_fini(&s->out.buf);
+}
+
+static int usb_audio_initfn(USBDevice *dev)
+{
+    USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+    usb_desc_init(dev);
+    s->dev.opaque = s;
+    AUD_register_card("usb-audio", &s->card);
+
+    s->out.altset        = ALTSET_OFF;
+    s->out.mute          = false;
+    s->out.vol[0]        = 240; /* 0 dB */
+    s->out.vol[1]        = 240; /* 0 dB */
+    s->out.as.freq       = USBAUDIO_SAMPLE_RATE;
+    s->out.as.nchannels  = 2;
+    s->out.as.fmt        = AUD_FMT_S16;
+    s->out.as.endianness = 0;
+    streambuf_init(&s->out.buf, s->buffer);
+
+    s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio",
+                                s, output_callback, &s->out.as);
+    AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
+    AUD_set_active_out(s->out.voice, 0);
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_audio = {
+    .name = "usb-audio",
+    .unmigratable = 1,
+};
+
+static struct USBDeviceInfo usb_audio_info = {
+    .product_desc   = "QEMU USB Audio Interface",
+    .usbdevice_name = "audio",
+    .qdev.name      = "usb-audio",
+    .qdev.size      = sizeof(USBAudioState),
+    .qdev.vmsd      = &vmstate_usb_audio,
+    .usb_desc       = &desc_audio,
+    .init           = usb_audio_initfn,
+    .handle_packet  = usb_generic_handle_packet,
+    .handle_reset   = usb_audio_handle_reset,
+    .handle_control = usb_audio_handle_control,
+    .handle_data    = usb_audio_handle_data,
+    .handle_destroy = usb_audio_handle_destroy,
+    .set_interface  = usb_audio_set_interface,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
+        DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
+                           8 * USBAUDIO_PACKET_SIZE),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void usb_audio_register_devices(void)
+{
+    usb_qdev_register(&usb_audio_info);
+}
+
+device_init(usb_audio_register_devices)
diff --git a/hw/usb-net.c b/hw/usb-net.c
index 5622bed..2f527a8 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -71,9 +71,6 @@ enum usbstring_idx {
 #define USB_CDC_UNION_TYPE		0x06	/* union_desc */
 #define USB_CDC_ETHERNET_TYPE		0x0f	/* ether_desc */
 
-#define USB_DT_CS_INTERFACE		0x24
-#define USB_DT_CS_ENDPOINT		0x25
-
 #define USB_CDC_SEND_ENCAPSULATED_COMMAND	0x00
 #define USB_CDC_GET_ENCAPSULATED_RESPONSE	0x01
 #define USB_CDC_REQ_SET_LINE_CODING		0x20
diff --git a/hw/usb.h b/hw/usb.h
index 1496f76..b2c9479 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -79,6 +79,11 @@
 #define USB_CLASS_APP_SPEC		0xfe
 #define USB_CLASS_VENDOR_SPEC		0xff
 
+#define USB_SUBCLASS_UNDEFINED          0
+#define USB_SUBCLASS_AUDIO_CONTROL      1
+#define USB_SUBCLASS_AUDIO_STREAMING    2
+#define USB_SUBCLASS_AUDIO_MIDISTREAMING 3
+
 #define USB_DIR_OUT			0
 #define USB_DIR_IN			0x80
 
@@ -132,6 +137,8 @@
 #define USB_DT_OTHER_SPEED_CONFIG       0x07
 #define USB_DT_DEBUG                    0x0A
 #define USB_DT_INTERFACE_ASSOC          0x0B
+#define USB_DT_CS_INTERFACE             0x24
+#define USB_DT_CS_ENDPOINT              0x25
 
 #define USB_ENDPOINT_XFER_CONTROL	0
 #define USB_ENDPOINT_XFER_ISOC		1
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 06/17] xhci: Initial xHCI implementation
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (4 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 05/17] usb: add audio device model Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 07/17] usb: add USBEndpoint Gerd Hoffmann
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hector Martin, Gerd Hoffmann

From: Hector Martin <hector@marcansoft.com>

Based on the implementation from Hector Martin <hector@marcansoft.com>

Hectors's implementation completely sidestepped the qemu usb system and
used libusb directly for usb device pass through.  So I've ripped out
the libusb bits (or left them in disabled, as reference for further
coding) and hooked up the qemu subsystem instead.  That work is not
complete yet though, partly due to limitations of the qemu usb
subsystem.  Nevertheless I think it is better to continue development
in-tree, especially as the qemu usb bits need a bunch of improvements
too for decent usb 3.0 support.

Current state:
  - usb-storage emulation should work ok.
  - Devices which need constant polling (HID emulation like usb-tablet)
    are known to not work.
  - ISO xfers are not implemented yet.
  - superspeed ports are not implemented yet.
  - usb pass-through is completely untested so far.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

xhci: code style fixups
---
 Makefile.objs           |    1 +
 default-configs/pci.mak |    1 +
 hw/pci_ids.h            |    3 +
 hw/usb-xhci.c           | 2748 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 2753 insertions(+), 0 deletions(-)
 create mode 100644 hw/usb-xhci.c

diff --git a/Makefile.objs b/Makefile.objs
index 8956cb9..f94c881 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -211,6 +211,7 @@ hw-obj-$(CONFIG_PCKBD) += pckbd.o
 hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
 hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
 hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
+hw-obj-$(CONFIG_USB_XHCI) += usb-xhci.o
 hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index 22bd350..9d3e1db 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -4,6 +4,7 @@ CONFIG_VIRTIO=y
 CONFIG_USB_UHCI=y
 CONFIG_USB_OHCI=y
 CONFIG_USB_EHCI=y
+CONFIG_USB_XHCI=y
 CONFIG_NE2000_PCI=y
 CONFIG_EEPRO100_PCI=y
 CONFIG_PCNET_PCI=y
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 83f3893..85e5372 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -120,3 +120,6 @@
 
 #define PCI_VENDOR_ID_XEN               0x5853
 #define PCI_DEVICE_ID_XEN_PLATFORM      0x0001
+
+#define PCI_VENDOR_ID_NEC                0x1033
+#define PCI_DEVICE_ID_NEC_UPD720200      0x0194
diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c
new file mode 100644
index 0000000..0c5007b
--- /dev/null
+++ b/hw/usb-xhci.c
@@ -0,0 +1,2748 @@
+/*
+ * USB xHCI controller emulation
+ *
+ * Copyright (c) 2011 Securiforest
+ * Date: 2011-05-11 ;  Author: Hector Martin <hector@marcansoft.com>
+ * Based on usb-ohci.c, emulates Renesas NEC USB 3.0
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "pci.h"
+#include "qdev-addr.h"
+#include "msi.h"
+
+//#define DEBUG_XHCI
+//#define DEBUG_DATA
+
+#ifdef DEBUG_XHCI
+#define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINTF(...) do {} while (0)
+#endif
+#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
+                             __func__, __LINE__); abort(); } while (0)
+
+#define MAXSLOTS 8
+#define MAXINTRS 1
+
+#define USB2_PORTS 4
+#define USB3_PORTS 4
+
+#define MAXPORTS (USB2_PORTS+USB3_PORTS)
+
+#define TD_QUEUE 24
+#define BG_XFERS 8
+#define BG_PKTS 8
+
+/* Very pessimistic, let's hope it's enough for all cases */
+#define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS)
+/* Do not deliver ER Full events. NEC's driver does some things not bound
+ * to the specs when it gets them */
+#define ER_FULL_HACK
+
+#define LEN_CAP         0x40
+#define OFF_OPER        LEN_CAP
+#define LEN_OPER        (0x400 + 0x10 * MAXPORTS)
+#define OFF_RUNTIME     ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f)
+#define LEN_RUNTIME     (0x20 + MAXINTRS * 0x20)
+#define OFF_DOORBELL    (OFF_RUNTIME + LEN_RUNTIME)
+#define LEN_DOORBELL    ((MAXSLOTS + 1) * 0x20)
+
+/* must be power of 2 */
+#define LEN_REGS        0x2000
+
+#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS
+# error Increase LEN_REGS
+#endif
+
+#if MAXINTRS > 1
+# error TODO: only one interrupter supported
+#endif
+
+/* bit definitions */
+#define USBCMD_RS       (1<<0)
+#define USBCMD_HCRST    (1<<1)
+#define USBCMD_INTE     (1<<2)
+#define USBCMD_HSEE     (1<<3)
+#define USBCMD_LHCRST   (1<<7)
+#define USBCMD_CSS      (1<<8)
+#define USBCMD_CRS      (1<<9)
+#define USBCMD_EWE      (1<<10)
+#define USBCMD_EU3S     (1<<11)
+
+#define USBSTS_HCH      (1<<0)
+#define USBSTS_HSE      (1<<2)
+#define USBSTS_EINT     (1<<3)
+#define USBSTS_PCD      (1<<4)
+#define USBSTS_SSS      (1<<8)
+#define USBSTS_RSS      (1<<9)
+#define USBSTS_SRE      (1<<10)
+#define USBSTS_CNR      (1<<11)
+#define USBSTS_HCE      (1<<12)
+
+
+#define PORTSC_CCS          (1<<0)
+#define PORTSC_PED          (1<<1)
+#define PORTSC_OCA          (1<<3)
+#define PORTSC_PR           (1<<4)
+#define PORTSC_PLS_SHIFT        5
+#define PORTSC_PLS_MASK     0xf
+#define PORTSC_PP           (1<<9)
+#define PORTSC_SPEED_SHIFT      10
+#define PORTSC_SPEED_MASK   0xf
+#define PORTSC_SPEED_FULL   (1<<10)
+#define PORTSC_SPEED_LOW    (2<<10)
+#define PORTSC_SPEED_HIGH   (3<<10)
+#define PORTSC_SPEED_SUPER  (4<<10)
+#define PORTSC_PIC_SHIFT        14
+#define PORTSC_PIC_MASK     0x3
+#define PORTSC_LWS          (1<<16)
+#define PORTSC_CSC          (1<<17)
+#define PORTSC_PEC          (1<<18)
+#define PORTSC_WRC          (1<<19)
+#define PORTSC_OCC          (1<<20)
+#define PORTSC_PRC          (1<<21)
+#define PORTSC_PLC          (1<<22)
+#define PORTSC_CEC          (1<<23)
+#define PORTSC_CAS          (1<<24)
+#define PORTSC_WCE          (1<<25)
+#define PORTSC_WDE          (1<<26)
+#define PORTSC_WOE          (1<<27)
+#define PORTSC_DR           (1<<30)
+#define PORTSC_WPR          (1<<31)
+
+#define CRCR_RCS        (1<<0)
+#define CRCR_CS         (1<<1)
+#define CRCR_CA         (1<<2)
+#define CRCR_CRR        (1<<3)
+
+#define IMAN_IP         (1<<0)
+#define IMAN_IE         (1<<1)
+
+#define ERDP_EHB        (1<<3)
+
+#define TRB_SIZE 16
+typedef struct XHCITRB {
+    uint64_t parameter;
+    uint32_t status;
+    uint32_t control;
+    target_phys_addr_t addr;
+    bool ccs;
+} XHCITRB;
+
+
+typedef enum TRBType {
+    TRB_RESERVED = 0,
+    TR_NORMAL,
+    TR_SETUP,
+    TR_DATA,
+    TR_STATUS,
+    TR_ISOCH,
+    TR_LINK,
+    TR_EVDATA,
+    TR_NOOP,
+    CR_ENABLE_SLOT,
+    CR_DISABLE_SLOT,
+    CR_ADDRESS_DEVICE,
+    CR_CONFIGURE_ENDPOINT,
+    CR_EVALUATE_CONTEXT,
+    CR_RESET_ENDPOINT,
+    CR_STOP_ENDPOINT,
+    CR_SET_TR_DEQUEUE,
+    CR_RESET_DEVICE,
+    CR_FORCE_EVENT,
+    CR_NEGOTIATE_BW,
+    CR_SET_LATENCY_TOLERANCE,
+    CR_GET_PORT_BANDWIDTH,
+    CR_FORCE_HEADER,
+    CR_NOOP,
+    ER_TRANSFER = 32,
+    ER_COMMAND_COMPLETE,
+    ER_PORT_STATUS_CHANGE,
+    ER_BANDWIDTH_REQUEST,
+    ER_DOORBELL,
+    ER_HOST_CONTROLLER,
+    ER_DEVICE_NOTIFICATION,
+    ER_MFINDEX_WRAP
+} TRBType;
+
+#define CR_LINK TR_LINK
+
+typedef enum TRBCCode {
+    CC_INVALID = 0,
+    CC_SUCCESS,
+    CC_DATA_BUFFER_ERROR,
+    CC_BABBLE_DETECTED,
+    CC_USB_TRANSACTION_ERROR,
+    CC_TRB_ERROR,
+    CC_STALL_ERROR,
+    CC_RESOURCE_ERROR,
+    CC_BANDWIDTH_ERROR,
+    CC_NO_SLOTS_ERROR,
+    CC_INVALID_STREAM_TYPE_ERROR,
+    CC_SLOT_NOT_ENABLED_ERROR,
+    CC_EP_NOT_ENABLED_ERROR,
+    CC_SHORT_PACKET,
+    CC_RING_UNDERRUN,
+    CC_RING_OVERRUN,
+    CC_VF_ER_FULL,
+    CC_PARAMETER_ERROR,
+    CC_BANDWIDTH_OVERRUN,
+    CC_CONTEXT_STATE_ERROR,
+    CC_NO_PING_RESPONSE_ERROR,
+    CC_EVENT_RING_FULL_ERROR,
+    CC_INCOMPATIBLE_DEVICE_ERROR,
+    CC_MISSED_SERVICE_ERROR,
+    CC_COMMAND_RING_STOPPED,
+    CC_COMMAND_ABORTED,
+    CC_STOPPED,
+    CC_STOPPED_LENGTH_INVALID,
+    CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29,
+    CC_ISOCH_BUFFER_OVERRUN = 31,
+    CC_EVENT_LOST_ERROR,
+    CC_UNDEFINED_ERROR,
+    CC_INVALID_STREAM_ID_ERROR,
+    CC_SECONDARY_BANDWIDTH_ERROR,
+    CC_SPLIT_TRANSACTION_ERROR
+} TRBCCode;
+
+#define TRB_C               (1<<0)
+#define TRB_TYPE_SHIFT          10
+#define TRB_TYPE_MASK       0x3f
+#define TRB_TYPE(t)         (((t).control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
+
+#define TRB_EV_ED           (1<<2)
+
+#define TRB_TR_ENT          (1<<1)
+#define TRB_TR_ISP          (1<<2)
+#define TRB_TR_NS           (1<<3)
+#define TRB_TR_CH           (1<<4)
+#define TRB_TR_IOC          (1<<5)
+#define TRB_TR_IDT          (1<<6)
+#define TRB_TR_TBC_SHIFT        7
+#define TRB_TR_TBC_MASK     0x3
+#define TRB_TR_BEI          (1<<9)
+#define TRB_TR_TLBPC_SHIFT      16
+#define TRB_TR_TLBPC_MASK   0xf
+#define TRB_TR_FRAMEID_SHIFT    20
+#define TRB_TR_FRAMEID_MASK 0x7ff
+#define TRB_TR_SIA          (1<<31)
+
+#define TRB_TR_DIR          (1<<16)
+
+#define TRB_CR_SLOTID_SHIFT     24
+#define TRB_CR_SLOTID_MASK  0xff
+#define TRB_CR_EPID_SHIFT       16
+#define TRB_CR_EPID_MASK    0x1f
+
+#define TRB_CR_BSR          (1<<9)
+#define TRB_CR_DC           (1<<9)
+
+#define TRB_LK_TC           (1<<1)
+
+#define EP_TYPE_MASK        0x7
+#define EP_TYPE_SHIFT           3
+
+#define EP_STATE_MASK       0x7
+#define EP_DISABLED         (0<<0)
+#define EP_RUNNING          (1<<0)
+#define EP_HALTED           (2<<0)
+#define EP_STOPPED          (3<<0)
+#define EP_ERROR            (4<<0)
+
+#define SLOT_STATE_MASK     0x1f
+#define SLOT_STATE_SHIFT        27
+#define SLOT_STATE(s)       (((s)>>SLOT_STATE_SHIFT)&SLOT_STATE_MASK)
+#define SLOT_ENABLED        0
+#define SLOT_DEFAULT        1
+#define SLOT_ADDRESSED      2
+#define SLOT_CONFIGURED     3
+
+#define SLOT_CONTEXT_ENTRIES_MASK 0x1f
+#define SLOT_CONTEXT_ENTRIES_SHIFT 27
+
+typedef enum EPType {
+    ET_INVALID = 0,
+    ET_ISO_OUT,
+    ET_BULK_OUT,
+    ET_INTR_OUT,
+    ET_CONTROL,
+    ET_ISO_IN,
+    ET_BULK_IN,
+    ET_INTR_IN,
+} EPType;
+
+typedef struct XHCIRing {
+    target_phys_addr_t base;
+    target_phys_addr_t dequeue;
+    bool ccs;
+} XHCIRing;
+
+typedef struct XHCIPort {
+    USBPort port;
+    uint32_t portsc;
+} XHCIPort;
+
+struct XHCIState;
+typedef struct XHCIState XHCIState;
+
+typedef struct XHCITransfer {
+    XHCIState *xhci;
+    USBPacket packet;
+    bool running;
+    bool cancelled;
+    bool complete;
+    bool backgrounded;
+    unsigned int iso_pkts;
+    unsigned int slotid;
+    unsigned int epid;
+    bool in_xfer;
+    bool iso_xfer;
+    bool bg_xfer;
+
+    unsigned int trb_count;
+    unsigned int trb_alloced;
+    XHCITRB *trbs;
+
+    unsigned int data_length;
+    unsigned int data_alloced;
+    uint8_t *data;
+
+    TRBCCode status;
+
+    unsigned int pkts;
+    unsigned int pktsize;
+    unsigned int cur_pkt;
+} XHCITransfer;
+
+typedef struct XHCIEPContext {
+    XHCIRing ring;
+    unsigned int next_xfer;
+    unsigned int comp_xfer;
+    XHCITransfer transfers[TD_QUEUE];
+    bool bg_running;
+    bool bg_updating;
+    unsigned int next_bg;
+    XHCITransfer bg_transfers[BG_XFERS];
+    EPType type;
+    target_phys_addr_t pctx;
+    unsigned int max_psize;
+    bool has_bg;
+    uint32_t state;
+} XHCIEPContext;
+
+typedef struct XHCISlot {
+    bool enabled;
+    target_phys_addr_t ctx;
+    unsigned int port;
+    unsigned int devaddr;
+    XHCIEPContext * eps[31];
+} XHCISlot;
+
+typedef struct XHCIEvent {
+    TRBType type;
+    TRBCCode ccode;
+    uint64_t ptr;
+    uint32_t length;
+    uint32_t flags;
+    uint8_t slotid;
+    uint8_t epid;
+} XHCIEvent;
+
+struct XHCIState {
+    PCIDevice pci_dev;
+    USBBus bus;
+    qemu_irq irq;
+    MemoryRegion mem;
+    const char *name;
+    uint32_t msi;
+    unsigned int devaddr;
+
+    /* Operational Registers */
+    uint32_t usbcmd;
+    uint32_t usbsts;
+    uint32_t dnctrl;
+    uint32_t crcr_low;
+    uint32_t crcr_high;
+    uint32_t dcbaap_low;
+    uint32_t dcbaap_high;
+    uint32_t config;
+
+    XHCIPort ports[MAXPORTS];
+    XHCISlot slots[MAXSLOTS];
+
+    /* Runtime Registers */
+    uint32_t mfindex;
+    /* note: we only support one interrupter */
+    uint32_t iman;
+    uint32_t imod;
+    uint32_t erstsz;
+    uint32_t erstba_low;
+    uint32_t erstba_high;
+    uint32_t erdp_low;
+    uint32_t erdp_high;
+
+    target_phys_addr_t er_start;
+    uint32_t er_size;
+    bool er_pcs;
+    unsigned int er_ep_idx;
+    bool er_full;
+
+    XHCIEvent ev_buffer[EV_QUEUE];
+    unsigned int ev_buffer_put;
+    unsigned int ev_buffer_get;
+
+    XHCIRing cmd_ring;
+};
+
+typedef struct XHCIEvRingSeg {
+    uint32_t addr_low;
+    uint32_t addr_high;
+    uint32_t size;
+    uint32_t rsvd;
+} XHCIEvRingSeg;
+
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+                         unsigned int epid);
+
+static inline target_phys_addr_t xhci_addr64(uint32_t low, uint32_t high)
+{
+#if TARGET_PHYS_ADDR_BITS > 32
+    return low | ((target_phys_addr_t)high << 32);
+#else
+    return low;
+#endif
+}
+
+static inline target_phys_addr_t xhci_mask64(uint64_t addr)
+{
+#if TARGET_PHYS_ADDR_BITS > 32
+    return addr;
+#else
+    return addr & 0xffffffff;
+#endif
+}
+
+static void xhci_irq_update(XHCIState *xhci)
+{
+    int level = 0;
+
+    if (xhci->iman & IMAN_IP && xhci->iman & IMAN_IE &&
+        xhci->usbcmd && USBCMD_INTE) {
+        level = 1;
+    }
+
+    DPRINTF("xhci_irq_update(): %d\n", level);
+
+    if (xhci->msi && msi_enabled(&xhci->pci_dev)) {
+        if (level) {
+            DPRINTF("xhci_irq_update(): MSI signal\n");
+            msi_notify(&xhci->pci_dev, 0);
+        }
+    } else {
+        qemu_set_irq(xhci->irq, level);
+    }
+}
+
+static inline int xhci_running(XHCIState *xhci)
+{
+    return !(xhci->usbsts & USBSTS_HCH) && !xhci->er_full;
+}
+
+static void xhci_die(XHCIState *xhci)
+{
+    xhci->usbsts |= USBSTS_HCE;
+    fprintf(stderr, "xhci: asserted controller error\n");
+}
+
+static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
+{
+    XHCITRB ev_trb;
+    target_phys_addr_t addr;
+
+    ev_trb.parameter = cpu_to_le64(event->ptr);
+    ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24));
+    ev_trb.control = (event->slotid << 24) | (event->epid << 16) |
+                     event->flags | (event->type << TRB_TYPE_SHIFT);
+    if (xhci->er_pcs) {
+        ev_trb.control |= TRB_C;
+    }
+    ev_trb.control = cpu_to_le32(ev_trb.control);
+
+    DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x\n",
+            xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control);
+
+    addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
+    cpu_physical_memory_write(addr, (uint8_t *) &ev_trb, TRB_SIZE);
+
+    xhci->er_ep_idx++;
+    if (xhci->er_ep_idx >= xhci->er_size) {
+        xhci->er_ep_idx = 0;
+        xhci->er_pcs = !xhci->er_pcs;
+    }
+}
+
+static void xhci_events_update(XHCIState *xhci)
+{
+    target_phys_addr_t erdp;
+    unsigned int dp_idx;
+    bool do_irq = 0;
+
+    if (xhci->usbsts & USBSTS_HCH) {
+        return;
+    }
+
+    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
+    if (erdp < xhci->er_start ||
+        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
+        fprintf(stderr, "xhci: ERDP out of bounds: "TARGET_FMT_plx"\n", erdp);
+        fprintf(stderr, "xhci: ER at "TARGET_FMT_plx" len %d\n",
+                xhci->er_start, xhci->er_size);
+        xhci_die(xhci);
+        return;
+    }
+    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
+    assert(dp_idx < xhci->er_size);
+
+    /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus
+     * deadlocks when the ER is full. Hack it by holding off events until
+     * the driver decides to free at least half of the ring */
+    if (xhci->er_full) {
+        int er_free = dp_idx - xhci->er_ep_idx;
+        if (er_free <= 0) {
+            er_free += xhci->er_size;
+        }
+        if (er_free < (xhci->er_size/2)) {
+            DPRINTF("xhci_events_update(): event ring still "
+                    "more than half full (hack)\n");
+            return;
+        }
+    }
+
+    while (xhci->ev_buffer_put != xhci->ev_buffer_get) {
+        assert(xhci->er_full);
+        if (((xhci->er_ep_idx+1) % xhci->er_size) == dp_idx) {
+            DPRINTF("xhci_events_update(): event ring full again\n");
+#ifndef ER_FULL_HACK
+            XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
+            xhci_write_event(xhci, &full);
+#endif
+            do_irq = 1;
+            break;
+        }
+        XHCIEvent *event = &xhci->ev_buffer[xhci->ev_buffer_get];
+        xhci_write_event(xhci, event);
+        xhci->ev_buffer_get++;
+        do_irq = 1;
+        if (xhci->ev_buffer_get == EV_QUEUE) {
+            xhci->ev_buffer_get = 0;
+        }
+    }
+
+    if (do_irq) {
+        xhci->erdp_low |= ERDP_EHB;
+        xhci->iman |= IMAN_IP;
+        xhci->usbsts |= USBSTS_EINT;
+        xhci_irq_update(xhci);
+    }
+
+    if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) {
+        DPRINTF("xhci_events_update(): event ring no longer full\n");
+        xhci->er_full = 0;
+    }
+    return;
+}
+
+static void xhci_event(XHCIState *xhci, XHCIEvent *event)
+{
+    target_phys_addr_t erdp;
+    unsigned int dp_idx;
+
+    if (xhci->er_full) {
+        DPRINTF("xhci_event(): ER full, queueing\n");
+        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
+            fprintf(stderr, "xhci: event queue full, dropping event!\n");
+            return;
+        }
+        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
+        if (xhci->ev_buffer_put == EV_QUEUE) {
+            xhci->ev_buffer_put = 0;
+        }
+        return;
+    }
+
+    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
+    if (erdp < xhci->er_start ||
+        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
+        fprintf(stderr, "xhci: ERDP out of bounds: "TARGET_FMT_plx"\n", erdp);
+        fprintf(stderr, "xhci: ER at "TARGET_FMT_plx" len %d\n",
+                xhci->er_start, xhci->er_size);
+        xhci_die(xhci);
+        return;
+    }
+
+    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
+    assert(dp_idx < xhci->er_size);
+
+    if ((xhci->er_ep_idx+1) % xhci->er_size == dp_idx) {
+        DPRINTF("xhci_event(): ER full, queueing\n");
+#ifndef ER_FULL_HACK
+        XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
+        xhci_write_event(xhci, &full);
+#endif
+        xhci->er_full = 1;
+        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
+            fprintf(stderr, "xhci: event queue full, dropping event!\n");
+            return;
+        }
+        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
+        if (xhci->ev_buffer_put == EV_QUEUE) {
+            xhci->ev_buffer_put = 0;
+        }
+    } else {
+        xhci_write_event(xhci, event);
+    }
+
+    xhci->erdp_low |= ERDP_EHB;
+    xhci->iman |= IMAN_IP;
+    xhci->usbsts |= USBSTS_EINT;
+
+    xhci_irq_update(xhci);
+}
+
+static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
+                           target_phys_addr_t base)
+{
+    ring->base = base;
+    ring->dequeue = base;
+    ring->ccs = 1;
+}
+
+static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
+                               target_phys_addr_t *addr)
+{
+    while (1) {
+        TRBType type;
+        cpu_physical_memory_read(ring->dequeue, (uint8_t *) trb, TRB_SIZE);
+        trb->addr = ring->dequeue;
+        trb->ccs = ring->ccs;
+        le64_to_cpus(&trb->parameter);
+        le32_to_cpus(&trb->status);
+        le32_to_cpus(&trb->control);
+
+        DPRINTF("xhci: TRB fetched [" TARGET_FMT_plx "]: "
+                "%016" PRIx64 " %08x %08x\n",
+                ring->dequeue, trb->parameter, trb->status, trb->control);
+
+        if ((trb->control & TRB_C) != ring->ccs) {
+            return 0;
+        }
+
+        type = TRB_TYPE(*trb);
+
+        if (type != TR_LINK) {
+            if (addr) {
+                *addr = ring->dequeue;
+            }
+            ring->dequeue += TRB_SIZE;
+            return type;
+        } else {
+            ring->dequeue = xhci_mask64(trb->parameter);
+            if (trb->control & TRB_LK_TC) {
+                ring->ccs = !ring->ccs;
+            }
+        }
+    }
+}
+
+static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
+{
+    XHCITRB trb;
+    int length = 0;
+    target_phys_addr_t dequeue = ring->dequeue;
+    bool ccs = ring->ccs;
+    /* hack to bundle together the two/three TDs that make a setup transfer */
+    bool control_td_set = 0;
+
+    while (1) {
+        TRBType type;
+        cpu_physical_memory_read(dequeue, (uint8_t *) &trb, TRB_SIZE);
+        le64_to_cpus(&trb.parameter);
+        le32_to_cpus(&trb.status);
+        le32_to_cpus(&trb.control);
+
+        DPRINTF("xhci: TRB peeked [" TARGET_FMT_plx "]: "
+                "%016" PRIx64 " %08x %08x\n",
+                dequeue, trb.parameter, trb.status, trb.control);
+
+        if ((trb.control & TRB_C) != ccs) {
+            return -length;
+        }
+
+        type = TRB_TYPE(trb);
+
+        if (type == TR_LINK) {
+            dequeue = xhci_mask64(trb.parameter);
+            if (trb.control & TRB_LK_TC) {
+                ccs = !ccs;
+            }
+            continue;
+        }
+
+        length += 1;
+        dequeue += TRB_SIZE;
+
+        if (type == TR_SETUP) {
+            control_td_set = 1;
+        } else if (type == TR_STATUS) {
+            control_td_set = 0;
+        }
+
+        if (!control_td_set && !(trb.control & TRB_TR_CH)) {
+            return length;
+        }
+    }
+}
+
+static void xhci_er_reset(XHCIState *xhci)
+{
+    XHCIEvRingSeg seg;
+
+    /* cache the (sole) event ring segment location */
+    if (xhci->erstsz != 1) {
+        fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", xhci->erstsz);
+        xhci_die(xhci);
+        return;
+    }
+    target_phys_addr_t erstba = xhci_addr64(xhci->erstba_low, xhci->erstba_high);
+    cpu_physical_memory_read(erstba, (uint8_t *) &seg, sizeof(seg));
+    le32_to_cpus(&seg.addr_low);
+    le32_to_cpus(&seg.addr_high);
+    le32_to_cpus(&seg.size);
+    if (seg.size < 16 || seg.size > 4096) {
+        fprintf(stderr, "xhci: invalid value for segment size: %d\n", seg.size);
+        xhci_die(xhci);
+        return;
+    }
+    xhci->er_start = xhci_addr64(seg.addr_low, seg.addr_high);
+    xhci->er_size = seg.size;
+
+    xhci->er_ep_idx = 0;
+    xhci->er_pcs = 1;
+    xhci->er_full = 0;
+
+    DPRINTF("xhci: event ring:" TARGET_FMT_plx " [%d]\n",
+            xhci->er_start, xhci->er_size);
+}
+
+static void xhci_run(XHCIState *xhci)
+{
+    DPRINTF("xhci_run()\n");
+
+    xhci->usbsts &= ~USBSTS_HCH;
+}
+
+static void xhci_stop(XHCIState *xhci)
+{
+    DPRINTF("xhci_stop()\n");
+    xhci->usbsts |= USBSTS_HCH;
+    xhci->crcr_low &= ~CRCR_CRR;
+}
+
+static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
+                              uint32_t state)
+{
+    uint32_t ctx[5];
+    if (epctx->state == state) {
+        return;
+    }
+
+    cpu_physical_memory_read(epctx->pctx, (uint8_t *) ctx, sizeof(ctx));
+    ctx[0] &= ~EP_STATE_MASK;
+    ctx[0] |= state;
+    ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
+    ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
+    DPRINTF("xhci: set epctx: " TARGET_FMT_plx " state=%d dequeue=%08x%08x\n",
+            epctx->pctx, state, ctx[3], ctx[2]);
+    cpu_physical_memory_write(epctx->pctx, (uint8_t *) ctx, sizeof(ctx));
+    epctx->state = state;
+}
+
+static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
+                               unsigned int epid, target_phys_addr_t pctx,
+                               uint32_t *ctx)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    target_phys_addr_t dequeue;
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+
+    DPRINTF("xhci_enable_ep(%d, %d)\n", slotid, epid);
+
+    slot = &xhci->slots[slotid-1];
+    if (slot->eps[epid-1]) {
+        fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid);
+        return CC_TRB_ERROR;
+    }
+
+    epctx = g_malloc(sizeof(XHCIEPContext));
+    memset(epctx, 0, sizeof(XHCIEPContext));
+
+    slot->eps[epid-1] = epctx;
+
+    dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
+    xhci_ring_init(xhci, &epctx->ring, dequeue);
+    epctx->ring.ccs = ctx[2] & 1;
+
+    epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK;
+    DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type);
+    epctx->pctx = pctx;
+    epctx->max_psize = ctx[1]>>16;
+    epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
+    epctx->has_bg = false;
+    if (epctx->type == ET_ISO_IN) {
+        epctx->has_bg = true;
+    }
+    DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
+            epid/2, epid%2, epctx->max_psize);
+    for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
+        usb_packet_init(&epctx->transfers[i].packet);
+    }
+
+    epctx->state = EP_RUNNING;
+    ctx[0] &= ~EP_STATE_MASK;
+    ctx[0] |= EP_RUNNING;
+
+    return CC_SUCCESS;
+}
+
+static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
+                               unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    int i, xferi, killed = 0;
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+
+    DPRINTF("xhci_ep_nuke_xfers(%d, %d)\n", slotid, epid);
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        return 0;
+    }
+
+    epctx = slot->eps[epid-1];
+
+    xferi = epctx->next_xfer;
+    for (i = 0; i < TD_QUEUE; i++) {
+        XHCITransfer *t = &epctx->transfers[xferi];
+        if (t->running) {
+            t->cancelled = 1;
+            /* libusb_cancel_transfer(t->usbxfer) */
+            DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i);
+            killed++;
+        }
+        if (t->backgrounded) {
+            t->backgrounded = 0;
+        }
+        if (t->trbs) {
+            g_free(t->trbs);
+        }
+        if (t->data) {
+            g_free(t->data);
+        }
+
+        t->trbs = NULL;
+        t->data = NULL;
+        t->trb_count = t->trb_alloced = 0;
+        t->data_length = t->data_alloced = 0;
+        xferi = (xferi + 1) % TD_QUEUE;
+    }
+    if (epctx->has_bg) {
+        xferi = epctx->next_bg;
+        for (i = 0; i < BG_XFERS; i++) {
+            XHCITransfer *t = &epctx->bg_transfers[xferi];
+            if (t->running) {
+                t->cancelled = 1;
+                /* libusb_cancel_transfer(t->usbxfer); */
+                DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i);
+                killed++;
+            }
+            if (t->data) {
+                g_free(t->data);
+            }
+
+            t->data = NULL;
+            xferi = (xferi + 1) % BG_XFERS;
+        }
+    }
+    return killed;
+}
+
+static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
+                               unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+
+    DPRINTF("xhci_disable_ep(%d, %d)\n", slotid, epid);
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d already disabled\n", slotid, epid);
+        return CC_SUCCESS;
+    }
+
+    xhci_ep_nuke_xfers(xhci, slotid, epid);
+
+    epctx = slot->eps[epid-1];
+
+    xhci_set_ep_state(xhci, epctx, EP_DISABLED);
+
+    g_free(epctx);
+    slot->eps[epid-1] = NULL;
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
+                             unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+
+    DPRINTF("xhci_stop_ep(%d, %d)\n", slotid, epid);
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+
+    if (epid < 1 || epid > 31) {
+        fprintf(stderr, "xhci: bad ep %d\n", epid);
+        return CC_TRB_ERROR;
+    }
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
+        return CC_EP_NOT_ENABLED_ERROR;
+    }
+
+    if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) {
+        fprintf(stderr, "xhci: FIXME: endpoint stopped w/ xfers running, "
+                "data might be lost\n");
+    }
+
+    epctx = slot->eps[epid-1];
+
+    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
+                              unsigned int epid)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    USBDevice *dev;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+
+    DPRINTF("xhci_reset_ep(%d, %d)\n", slotid, epid);
+
+    if (epid < 1 || epid > 31) {
+        fprintf(stderr, "xhci: bad ep %d\n", epid);
+        return CC_TRB_ERROR;
+    }
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
+        return CC_EP_NOT_ENABLED_ERROR;
+    }
+
+    epctx = slot->eps[epid-1];
+
+    if (epctx->state != EP_HALTED) {
+        fprintf(stderr, "xhci: reset EP while EP %d not halted (%d)\n",
+                epid, epctx->state);
+        return CC_CONTEXT_STATE_ERROR;
+    }
+
+    if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) {
+        fprintf(stderr, "xhci: FIXME: endpoint reset w/ xfers running, "
+                "data might be lost\n");
+    }
+
+    uint8_t ep = epid>>1;
+
+    if (epid & 1) {
+        ep |= 0x80;
+    }
+
+    dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev;
+    if (!dev) {
+        return CC_USB_TRANSACTION_ERROR;
+    }
+
+    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
+                                    unsigned int epid, uint64_t pdequeue)
+{
+    XHCISlot *slot;
+    XHCIEPContext *epctx;
+    target_phys_addr_t dequeue;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+
+    if (epid < 1 || epid > 31) {
+        fprintf(stderr, "xhci: bad ep %d\n", epid);
+        return CC_TRB_ERROR;
+    }
+
+    DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue);
+    dequeue = xhci_mask64(pdequeue);
+
+    slot = &xhci->slots[slotid-1];
+
+    if (!slot->eps[epid-1]) {
+        DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid);
+        return CC_EP_NOT_ENABLED_ERROR;
+    }
+
+    epctx = slot->eps[epid-1];
+
+
+    if (epctx->state != EP_STOPPED) {
+        fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
+        return CC_CONTEXT_STATE_ERROR;
+    }
+
+    xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
+    epctx->ring.ccs = dequeue & 1;
+
+    xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+
+    return CC_SUCCESS;
+}
+
+static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
+                          unsigned int length, bool in_xfer, bool out_xfer,
+                          bool report)
+{
+    int i;
+    uint32_t edtla = 0;
+    unsigned int transferred = 0;
+    unsigned int left = length;
+    bool reported = 0;
+    bool shortpkt = 0;
+    XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
+    XHCIState *xhci = xfer->xhci;
+
+    DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n",
+            length, in_xfer, out_xfer, report);
+
+    assert(!(in_xfer && out_xfer));
+
+    for (i = 0; i < xfer->trb_count; i++) {
+        XHCITRB *trb = &xfer->trbs[i];
+        target_phys_addr_t addr;
+        unsigned int chunk = 0;
+
+        switch (TRB_TYPE(*trb)) {
+        case TR_DATA:
+            if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
+                fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n");
+                xhci_die(xhci);
+                return transferred;
+            }
+            /* fallthrough */
+        case TR_NORMAL:
+        case TR_ISOCH:
+            addr = xhci_mask64(trb->parameter);
+            chunk = trb->status & 0x1ffff;
+            if (chunk > left) {
+                chunk = left;
+                shortpkt = 1;
+            }
+            if (in_xfer || out_xfer) {
+                if (trb->control & TRB_TR_IDT) {
+                    uint64_t idata;
+                    if (chunk > 8 || in_xfer) {
+                        fprintf(stderr, "xhci: invalid immediate data TRB\n");
+                        xhci_die(xhci);
+                        return transferred;
+                    }
+                    idata = le64_to_cpu(trb->parameter);
+                    memcpy(data, &idata, chunk);
+                } else {
+                    DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at "
+                            TARGET_FMT_plx "\n", in_xfer, chunk, addr);
+                    if (in_xfer) {
+                        cpu_physical_memory_write(addr, data, chunk);
+                    } else {
+                        cpu_physical_memory_read(addr, data, chunk);
+                    }
+#ifdef DEBUG_DATA
+                    unsigned int count = chunk;
+                    int i;
+                    if (count > 16) {
+                        count = 16;
+                    }
+                    DPRINTF(" ::");
+                    for (i = 0; i < count; i++) {
+                        DPRINTF(" %02x", data[i]);
+                    }
+                    DPRINTF("\n");
+#endif
+                }
+            }
+            left -= chunk;
+            data += chunk;
+            edtla += chunk;
+            transferred += chunk;
+            break;
+        case TR_STATUS:
+            reported = 0;
+            shortpkt = 0;
+            break;
+        }
+
+        if (report && !reported && (trb->control & TRB_TR_IOC ||
+            (shortpkt && (trb->control & TRB_TR_ISP)))) {
+            event.slotid = xfer->slotid;
+            event.epid = xfer->epid;
+            event.length = (trb->status & 0x1ffff) - chunk;
+            event.flags = 0;
+            event.ptr = trb->addr;
+            if (xfer->status == CC_SUCCESS) {
+                event.ccode = shortpkt ? CC_SHORT_PACKET : CC_SUCCESS;
+            } else {
+                event.ccode = xfer->status;
+            }
+            if (TRB_TYPE(*trb) == TR_EVDATA) {
+                event.ptr = trb->parameter;
+                event.flags |= TRB_EV_ED;
+                event.length = edtla & 0xffffff;
+                DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length);
+                edtla = 0;
+            }
+            xhci_event(xhci, &event);
+            reported = 1;
+        }
+    }
+    return transferred;
+}
+
+static void xhci_stall_ep(XHCITransfer *xfer)
+{
+    XHCIState *xhci = xfer->xhci;
+    XHCISlot *slot = &xhci->slots[xfer->slotid-1];
+    XHCIEPContext *epctx = slot->eps[xfer->epid-1];
+
+    epctx->ring.dequeue = xfer->trbs[0].addr;
+    epctx->ring.ccs = xfer->trbs[0].ccs;
+    xhci_set_ep_state(xhci, epctx, EP_HALTED);
+    DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid);
+    DPRINTF("xhci: will continue at "TARGET_FMT_plx"\n", epctx->ring.dequeue);
+}
+
+static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
+                       XHCIEPContext *epctx);
+
+static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx)
+{
+    if (epctx->bg_updating) {
+        return;
+    }
+    DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx);
+    assert(epctx->has_bg);
+    DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg);
+    epctx->bg_updating = 1;
+    while (epctx->transfers[epctx->comp_xfer].backgrounded &&
+           epctx->bg_transfers[epctx->next_bg].complete) {
+        XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer];
+        XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg];
+#if 0
+        DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n",
+                epctx->comp_xfer, epctx->next_bg, bg->cur_pkt,
+                bg->usbxfer->iso_packet_desc[bg->cur_pkt].status
+               );
+#endif
+        assert(epctx->type == ET_ISO_IN);
+        assert(bg->iso_xfer);
+        assert(bg->in_xfer);
+        uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize;
+#if 0
+        int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length;
+        fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status);
+#else
+        int len = 0;
+        FIXME();
+#endif
+        fg->complete = 1;
+        fg->backgrounded = 0;
+
+        if (fg->status == CC_STALL_ERROR) {
+            xhci_stall_ep(fg);
+        }
+
+        xhci_xfer_data(fg, p, len, 1, 0, 1);
+
+        epctx->comp_xfer++;
+        if (epctx->comp_xfer == TD_QUEUE) {
+            epctx->comp_xfer = 0;
+        }
+        DPRINTF("next fg xfer: %d\n", epctx->comp_xfer);
+        bg->cur_pkt++;
+        if (bg->cur_pkt == bg->pkts) {
+            bg->complete = 0;
+            if (xhci_submit(xhci, bg, epctx) < 0) {
+                fprintf(stderr, "xhci: bg resubmit failed\n");
+            }
+            epctx->next_bg++;
+            if (epctx->next_bg == BG_XFERS) {
+                epctx->next_bg = 0;
+            }
+            DPRINTF("next bg xfer: %d\n", epctx->next_bg);
+
+        xhci_kick_ep(xhci, fg->slotid, fg->epid);
+        }
+    }
+    epctx->bg_updating = 0;
+}
+
+#if 0
+static void xhci_xfer_cb(struct libusb_transfer *transfer)
+{
+    XHCIState *xhci;
+    XHCITransfer *xfer;
+
+    xfer = (XHCITransfer *)transfer->user_data;
+    xhci = xfer->xhci;
+
+    DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid,
+            xfer->epid, transfer->status);
+
+    assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS);
+    assert(xfer->epid >= 1 && xfer->epid <= 31);
+
+    if (xfer->cancelled) {
+        DPRINTF("xhci: transfer cancelled, not reporting anything\n");
+        xfer->running = 0;
+        return;
+    }
+
+    XHCIEPContext *epctx;
+    XHCISlot *slot;
+    slot = &xhci->slots[xfer->slotid-1];
+    assert(slot->eps[xfer->epid-1]);
+    epctx = slot->eps[xfer->epid-1];
+
+    if (xfer->bg_xfer) {
+        DPRINTF("xhci: background transfer, updating\n");
+        xfer->complete = 1;
+        xfer->running = 0;
+        xhci_bg_update(xhci, epctx);
+        return;
+    }
+
+    if (xfer->iso_xfer) {
+        transfer->status = transfer->iso_packet_desc[0].status;
+        transfer->actual_length = transfer->iso_packet_desc[0].actual_length;
+    }
+
+    xfer->status = libusb_to_ccode(transfer->status);
+
+    xfer->complete = 1;
+    xfer->running = 0;
+
+    if (transfer->status == LIBUSB_TRANSFER_STALL)
+        xhci_stall_ep(xhci, epctx, xfer);
+
+    DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length);
+
+    if (xfer->in_xfer) {
+        if (xfer->epid == 1) {
+            xhci_xfer_data(xhci, xfer, xfer->data + 8,
+                           transfer->actual_length, 1, 0, 1);
+        } else {
+            xhci_xfer_data(xhci, xfer, xfer->data,
+                           transfer->actual_length, 1, 0, 1);
+        }
+    } else {
+        xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1);
+    }
+
+    xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+}
+
+static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer,
+                            uint8_t bmRequestType, uint8_t bRequest,
+                            uint16_t wValue, uint16_t wIndex, uint16_t wLength)
+{
+    uint16_t type_req = (bmRequestType << 8) | bRequest;
+
+    switch (type_req) {
+        case 0x0000 | USB_REQ_SET_CONFIGURATION:
+            DPRINTF("xhci: HLE switch configuration\n");
+            return xhci_switch_config(xhci, xfer->slotid, wValue) == 0;
+        case 0x0100 | USB_REQ_SET_INTERFACE:
+            DPRINTF("xhci: HLE set interface altsetting\n");
+            return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0;
+        case 0x0200 | USB_REQ_CLEAR_FEATURE:
+            if (wValue == 0) { // endpoint halt
+                DPRINTF("xhci: HLE clear halt\n");
+                return xhci_clear_halt(xhci, xfer->slotid, wIndex);
+            }
+        case 0x0000 | USB_REQ_SET_ADDRESS:
+            fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n");
+            return 0;
+        default:
+            return 0;
+    }
+}
+#endif
+
+static int xhci_setup_packet(XHCITransfer *xfer, XHCIPort *port, int ep)
+{
+    usb_packet_setup(&xfer->packet,
+                     xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT,
+                     xfer->xhci->slots[xfer->slotid-1].devaddr,
+                     ep & 0x7f);
+    usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
+    DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
+            xfer->packet.pid, xfer->packet.devaddr, xfer->packet.devep);
+    return 0;
+}
+
+static int xhci_complete_packet(XHCITransfer *xfer, int ret)
+{
+    if (ret == USB_RET_ASYNC) {
+        xfer->running = 1;
+        xfer->complete = 0;
+        xfer->cancelled = 0;
+        return 0;
+    } else {
+        xfer->running = 0;
+        xfer->complete = 1;
+    }
+
+    if (ret >= 0) {
+        xfer->status = CC_SUCCESS;
+        xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
+        return 0;
+    }
+
+    /* error */
+    switch (ret) {
+    case USB_RET_NODEV:
+        xfer->status = CC_USB_TRANSACTION_ERROR;
+        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+        xhci_stall_ep(xfer);
+        break;
+    case USB_RET_STALL:
+        xfer->status = CC_STALL_ERROR;
+        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+        xhci_stall_ep(xfer);
+        break;
+    default:
+        fprintf(stderr, "%s: FIXME: ret = %d\n", __FUNCTION__, ret);
+        FIXME();
+    }
+    return 0;
+}
+
+static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+{
+    XHCITRB *trb_setup, *trb_status;
+    uint8_t bmRequestType, bRequest;
+    uint16_t wValue, wLength, wIndex;
+    XHCIPort *port;
+    USBDevice *dev;
+    int ret;
+
+    DPRINTF("xhci_fire_ctl_transfer(slot=%d)\n", xfer->slotid);
+
+    trb_setup = &xfer->trbs[0];
+    trb_status = &xfer->trbs[xfer->trb_count-1];
+
+    /* at most one Event Data TRB allowed after STATUS */
+    if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
+        trb_status--;
+    }
+
+    /* do some sanity checks */
+    if (TRB_TYPE(*trb_setup) != TR_SETUP) {
+        fprintf(stderr, "xhci: ep0 first TD not SETUP: %d\n",
+                TRB_TYPE(*trb_setup));
+        return -1;
+    }
+    if (TRB_TYPE(*trb_status) != TR_STATUS) {
+        fprintf(stderr, "xhci: ep0 last TD not STATUS: %d\n",
+                TRB_TYPE(*trb_status));
+        return -1;
+    }
+    if (!(trb_setup->control & TRB_TR_IDT)) {
+        fprintf(stderr, "xhci: Setup TRB doesn't have IDT set\n");
+        return -1;
+    }
+    if ((trb_setup->status & 0x1ffff) != 8) {
+        fprintf(stderr, "xhci: Setup TRB has bad length (%d)\n",
+                (trb_setup->status & 0x1ffff));
+        return -1;
+    }
+
+    bmRequestType = trb_setup->parameter;
+    bRequest = trb_setup->parameter >> 8;
+    wValue = trb_setup->parameter >> 16;
+    wIndex = trb_setup->parameter >> 32;
+    wLength = trb_setup->parameter >> 48;
+
+    if (xfer->data && xfer->data_alloced < wLength) {
+        xfer->data_alloced = 0;
+        g_free(xfer->data);
+        xfer->data = NULL;
+    }
+    if (!xfer->data) {
+        DPRINTF("xhci: alloc %d bytes data\n", wLength);
+        xfer->data = g_malloc(wLength+1);
+        xfer->data_alloced = wLength;
+    }
+    xfer->data_length = wLength;
+
+    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+    dev = port->port.dev;
+    if (!dev) {
+        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
+                xhci->slots[xfer->slotid-1].port);
+        return -1;
+    }
+
+    xfer->in_xfer = bmRequestType & USB_DIR_IN;
+    xfer->iso_xfer = false;
+
+    xhci_setup_packet(xfer, port, 0);
+    if (!xfer->in_xfer) {
+        xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
+    }
+    ret = dev->info->handle_control(dev, &xfer->packet,
+                                    (bmRequestType << 8) | bRequest,
+                                    wValue, wIndex, wLength, xfer->data);
+
+    xhci_complete_packet(xfer, ret);
+    if (!xfer->running) {
+        xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+    }
+    return 0;
+}
+
+static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+{
+    XHCIPort *port;
+    USBDevice *dev;
+    int ret;
+
+    DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
+    uint8_t ep = xfer->epid>>1;
+
+    xfer->in_xfer = epctx->type>>2;
+    if (xfer->in_xfer) {
+        ep |= 0x80;
+    }
+
+    if (xfer->data && xfer->data_alloced < xfer->data_length) {
+        xfer->data_alloced = 0;
+        g_free(xfer->data);
+        xfer->data = NULL;
+    }
+    if (!xfer->data && xfer->data_length) {
+        DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length);
+        xfer->data = g_malloc(xfer->data_length);
+        xfer->data_alloced = xfer->data_length;
+    }
+    if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
+        if (!xfer->bg_xfer) {
+            xfer->pkts = 1;
+        }
+    } else {
+        xfer->pkts = 0;
+    }
+
+    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+    dev = port->port.dev;
+    if (!dev) {
+        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
+                xhci->slots[xfer->slotid-1].port);
+        return -1;
+    }
+
+    xhci_setup_packet(xfer, port, ep);
+
+    switch(epctx->type) {
+    case ET_INTR_OUT:
+    case ET_INTR_IN:
+    case ET_BULK_OUT:
+    case ET_BULK_IN:
+        break;
+    case ET_ISO_OUT:
+    case ET_ISO_IN:
+        FIXME();
+        break;
+    default:
+        fprintf(stderr, "xhci: unknown or unhandled EP type %d (ep %02x)\n",
+                epctx->type, ep);
+        return -1;
+    }
+
+    if (!xfer->in_xfer) {
+        xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0);
+    }
+    ret = usb_handle_packet(dev, &xfer->packet);
+
+    xhci_complete_packet(xfer, ret);
+    if (!xfer->running) {
+        xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+    }
+    return 0;
+}
+
+static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+{
+    int i;
+    unsigned int length = 0;
+    XHCITRB *trb;
+
+    DPRINTF("xhci_fire_transfer(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
+
+    for (i = 0; i < xfer->trb_count; i++) {
+        trb = &xfer->trbs[i];
+        if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
+            length += trb->status & 0x1ffff;
+        }
+    }
+    DPRINTF("xhci: total TD length=%d\n", length);
+
+    if (!epctx->has_bg) {
+        xfer->data_length = length;
+        xfer->backgrounded = 0;
+        return xhci_submit(xhci, xfer, epctx);
+    } else {
+        if (!epctx->bg_running) {
+            for (i = 0; i < BG_XFERS; i++) {
+                XHCITransfer *t = &epctx->bg_transfers[i];
+                t->xhci = xhci;
+                t->epid = xfer->epid;
+                t->slotid = xfer->slotid;
+                t->pkts = BG_PKTS;
+                t->pktsize = epctx->max_psize;
+                t->data_length = t->pkts * t->pktsize;
+                t->bg_xfer = 1;
+                if (xhci_submit(xhci, t, epctx) < 0) {
+                    fprintf(stderr, "xhci: bg submit failed\n");
+                    return -1;
+                }
+            }
+            epctx->bg_running = 1;
+        }
+        xfer->backgrounded = 1;
+        xhci_bg_update(xhci, epctx);
+        return 0;
+    }
+}
+
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
+{
+    XHCIEPContext *epctx;
+    int length;
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    assert(epid >= 1 && epid <= 31);
+    DPRINTF("xhci_kick_ep(%d, %d)\n", slotid, epid);
+
+    if (!xhci->slots[slotid-1].enabled) {
+        fprintf(stderr, "xhci: xhci_kick_ep for disabled slot %d\n", slotid);
+        return;
+    }
+    epctx = xhci->slots[slotid-1].eps[epid-1];
+    if (!epctx) {
+        fprintf(stderr, "xhci: xhci_kick_ep for disabled endpoint %d,%d\n",
+                epid, slotid);
+        return;
+    }
+
+    if (epctx->state == EP_HALTED) {
+        DPRINTF("xhci: ep halted, not running schedule\n");
+        return;
+    }
+
+    xhci_set_ep_state(xhci, epctx, EP_RUNNING);
+
+    while (1) {
+        XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
+        if (xfer->running || xfer->backgrounded) {
+            DPRINTF("xhci: ep is busy\n");
+            break;
+        }
+        length = xhci_ring_chain_length(xhci, &epctx->ring);
+        if (length < 0) {
+            DPRINTF("xhci: incomplete TD (%d TRBs)\n", -length);
+            break;
+        } else if (length == 0) {
+            break;
+        }
+        DPRINTF("xhci: fetching %d-TRB TD\n", length);
+        if (xfer->trbs && xfer->trb_alloced < length) {
+            xfer->trb_count = 0;
+            xfer->trb_alloced = 0;
+            g_free(xfer->trbs);
+            xfer->trbs = NULL;
+        }
+        if (!xfer->trbs) {
+            xfer->trbs = g_malloc(sizeof(XHCITRB) * length);
+            xfer->trb_alloced = length;
+        }
+        xfer->trb_count = length;
+
+        for (i = 0; i < length; i++) {
+            assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL));
+        }
+        xfer->xhci = xhci;
+        xfer->epid = epid;
+        xfer->slotid = slotid;
+
+        if (epid == 1) {
+            if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
+                epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
+            } else {
+                fprintf(stderr, "xhci: error firing CTL transfer\n");
+            }
+        } else {
+            if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
+                epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
+            } else {
+                fprintf(stderr, "xhci: error firing data transfer\n");
+            }
+        }
+
+        /*
+         * Qemu usb can't handle multiple in-flight xfers.
+         * Also xfers might be finished here already,
+         * possibly with an error.  Stop here for now.
+         */
+        break;
+    }
+}
+
+static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
+{
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_enable_slot(%d)\n", slotid);
+    xhci->slots[slotid-1].enabled = 1;
+    xhci->slots[slotid-1].port = 0;
+    memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
+{
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_disable_slot(%d)\n", slotid);
+
+    for (i = 1; i <= 31; i++) {
+        if (xhci->slots[slotid-1].eps[i-1]) {
+            xhci_disable_ep(xhci, slotid, i);
+        }
+    }
+
+    xhci->slots[slotid-1].enabled = 0;
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
+                                  uint64_t pictx, bool bsr)
+{
+    XHCISlot *slot;
+    USBDevice *dev;
+    target_phys_addr_t ictx, octx, dcbaap;
+    uint64_t poctx;
+    uint32_t ictl_ctx[2];
+    uint32_t slot_ctx[4];
+    uint32_t ep0_ctx[5];
+    unsigned int port;
+    int i;
+    TRBCCode res;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_address_slot(%d)\n", slotid);
+
+    dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
+    cpu_physical_memory_read(dcbaap + 8*slotid,
+                             (uint8_t *) &poctx, sizeof(poctx));
+    ictx = xhci_mask64(pictx);
+    octx = xhci_mask64(le64_to_cpu(poctx));
+
+    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
+
+    if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) {
+        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
+                ictl_ctx[0], ictl_ctx[1]);
+        return CC_TRB_ERROR;
+    }
+
+    cpu_physical_memory_read(ictx+32, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    cpu_physical_memory_read(ictx+64, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+
+    DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+
+    DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
+            ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
+
+    port = (slot_ctx[1]>>16) & 0xFF;
+    dev = xhci->ports[port-1].port.dev;
+
+    if (port < 1 || port > MAXPORTS) {
+        fprintf(stderr, "xhci: bad port %d\n", port);
+        return CC_TRB_ERROR;
+    } else if (!dev) {
+        fprintf(stderr, "xhci: port %d not connected\n", port);
+        return CC_USB_TRANSACTION_ERROR;
+    }
+
+    for (i = 0; i < MAXSLOTS; i++) {
+        if (xhci->slots[i].port == port) {
+            fprintf(stderr, "xhci: port %d already assigned to slot %d\n",
+                    port, i+1);
+            return CC_TRB_ERROR;
+        }
+    }
+
+    slot = &xhci->slots[slotid-1];
+    slot->port = port;
+    slot->ctx = octx;
+
+    if (bsr) {
+        slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
+    } else {
+        slot->devaddr = xhci->devaddr++;
+        slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
+        DPRINTF("xhci: device address is %d\n", slot->devaddr);
+        dev->info->handle_control(dev, NULL,
+                                  DeviceOutRequest | USB_REQ_SET_ADDRESS,
+                                  slot->devaddr, 0, 0, NULL);
+    }
+
+    res = xhci_enable_ep(xhci, slotid, 1, octx+32, ep0_ctx);
+
+    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+    DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
+            ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
+
+    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    cpu_physical_memory_write(octx+32, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+
+    return res;
+}
+
+
+static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
+                                  uint64_t pictx, bool dc)
+{
+    target_phys_addr_t ictx, octx;
+    uint32_t ictl_ctx[2];
+    uint32_t slot_ctx[4];
+    uint32_t islot_ctx[4];
+    uint32_t ep_ctx[5];
+    int i;
+    TRBCCode res;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_configure_slot(%d)\n", slotid);
+
+    ictx = xhci_mask64(pictx);
+    octx = xhci->slots[slotid-1].ctx;
+
+    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    if (dc) {
+        for (i = 2; i <= 31; i++) {
+            if (xhci->slots[slotid-1].eps[i-1]) {
+                xhci_disable_ep(xhci, slotid, i);
+            }
+        }
+
+        cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+        slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
+        slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT;
+        DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+                slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+        cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+        return CC_SUCCESS;
+    }
+
+    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
+
+    if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) {
+        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
+                ictl_ctx[0], ictl_ctx[1]);
+        return CC_TRB_ERROR;
+    }
+
+    cpu_physical_memory_read(ictx+32, (uint8_t *) islot_ctx, sizeof(islot_ctx));
+    cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+    if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) {
+        fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]);
+        return CC_CONTEXT_STATE_ERROR;
+    }
+
+    for (i = 2; i <= 31; i++) {
+        if (ictl_ctx[0] & (1<<i)) {
+            xhci_disable_ep(xhci, slotid, i);
+        }
+        if (ictl_ctx[1] & (1<<i)) {
+            cpu_physical_memory_read(ictx+32+(32*i),
+                                     (uint8_t *) ep_ctx, sizeof(ep_ctx));
+            DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n",
+                    i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
+                    ep_ctx[3], ep_ctx[4]);
+            xhci_disable_ep(xhci, slotid, i);
+            res = xhci_enable_ep(xhci, slotid, i, octx+(32*i), ep_ctx);
+            if (res != CC_SUCCESS) {
+                return res;
+            }
+            DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n",
+                    i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
+                    ep_ctx[3], ep_ctx[4]);
+            cpu_physical_memory_write(octx+(32*i),
+                                      (uint8_t *) ep_ctx, sizeof(ep_ctx));
+        }
+    }
+
+    slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
+    slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT;
+    slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT);
+    slot_ctx[0] |= islot_ctx[0] & (SLOT_CONTEXT_ENTRIES_MASK <<
+                                   SLOT_CONTEXT_ENTRIES_SHIFT);
+    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+
+    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+    return CC_SUCCESS;
+}
+
+
+static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
+                                   uint64_t pictx)
+{
+    target_phys_addr_t ictx, octx;
+    uint32_t ictl_ctx[2];
+    uint32_t iep0_ctx[5];
+    uint32_t ep0_ctx[5];
+    uint32_t islot_ctx[4];
+    uint32_t slot_ctx[4];
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_evaluate_slot(%d)\n", slotid);
+
+    ictx = xhci_mask64(pictx);
+    octx = xhci->slots[slotid-1].ctx;
+
+    DPRINTF("xhci: input context at "TARGET_FMT_plx"\n", ictx);
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    cpu_physical_memory_read(ictx, (uint8_t *) ictl_ctx, sizeof(ictl_ctx));
+
+    if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) {
+        fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
+                ictl_ctx[0], ictl_ctx[1]);
+        return CC_TRB_ERROR;
+    }
+
+    if (ictl_ctx[1] & 0x1) {
+        cpu_physical_memory_read(ictx+32,
+                                 (uint8_t *) islot_ctx, sizeof(islot_ctx));
+
+        DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
+                islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]);
+
+        cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+        slot_ctx[1] &= ~0xFFFF; /* max exit latency */
+        slot_ctx[1] |= islot_ctx[1] & 0xFFFF;
+        slot_ctx[2] &= ~0xFF00000; /* interrupter target */
+        slot_ctx[2] |= islot_ctx[2] & 0xFF000000;
+
+        DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+                slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+
+        cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    }
+
+    if (ictl_ctx[1] & 0x2) {
+        cpu_physical_memory_read(ictx+64,
+                                 (uint8_t *) iep0_ctx, sizeof(iep0_ctx));
+
+        DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
+                iep0_ctx[0], iep0_ctx[1], iep0_ctx[2],
+                iep0_ctx[3], iep0_ctx[4]);
+
+        cpu_physical_memory_read(octx+32, (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+
+        ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/
+        ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000;
+
+        DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
+                ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
+
+        cpu_physical_memory_write(octx+32,
+                                  (uint8_t *) ep0_ctx, sizeof(ep0_ctx));
+    }
+
+    return CC_SUCCESS;
+}
+
+static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
+{
+    uint32_t slot_ctx[4];
+    target_phys_addr_t octx;
+    int i;
+
+    assert(slotid >= 1 && slotid <= MAXSLOTS);
+    DPRINTF("xhci_reset_slot(%d)\n", slotid);
+
+    octx = xhci->slots[slotid-1].ctx;
+
+    DPRINTF("xhci: output context at "TARGET_FMT_plx"\n", octx);
+
+    for (i = 2; i <= 31; i++) {
+        if (xhci->slots[slotid-1].eps[i-1]) {
+            xhci_disable_ep(xhci, slotid, i);
+        }
+    }
+
+    cpu_physical_memory_read(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+    slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
+    slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT;
+    DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
+            slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
+    cpu_physical_memory_write(octx, (uint8_t *) slot_ctx, sizeof(slot_ctx));
+
+    return CC_SUCCESS;
+}
+
+static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *trb)
+{
+    unsigned int slotid;
+    slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK;
+    if (slotid < 1 || slotid > MAXSLOTS) {
+        fprintf(stderr, "xhci: bad slot id %d\n", slotid);
+        event->ccode = CC_TRB_ERROR;
+        return 0;
+    } else if (!xhci->slots[slotid-1].enabled) {
+        fprintf(stderr, "xhci: slot id %d not enabled\n", slotid);
+        event->ccode = CC_SLOT_NOT_ENABLED_ERROR;
+        return 0;
+    }
+    return slotid;
+}
+
+static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
+{
+    target_phys_addr_t ctx;
+    uint8_t bw_ctx[MAXPORTS+1];
+
+    DPRINTF("xhci_get_port_bandwidth()\n");
+
+    ctx = xhci_mask64(pctx);
+
+    DPRINTF("xhci: bandwidth context at "TARGET_FMT_plx"\n", ctx);
+
+    /* TODO: actually implement real values here */
+    bw_ctx[0] = 0;
+    memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */
+    cpu_physical_memory_write(ctx, bw_ctx, sizeof(bw_ctx));
+
+    return CC_SUCCESS;
+}
+
+static uint32_t rotl(uint32_t v, unsigned count)
+{
+    count &= 31;
+    return (v << count) | (v >> (32 - count));
+}
+
+
+static uint32_t xhci_nec_challenge(uint32_t hi, uint32_t lo)
+{
+    uint32_t val;
+    val = rotl(lo - 0x49434878, 32 - ((hi>>8) & 0x1F));
+    val += rotl(lo + 0x49434878, hi & 0x1F);
+    val -= rotl(hi ^ 0x49434878, (lo >> 16) & 0x1F);
+    return ~val;
+}
+
+static void xhci_via_challenge(uint64_t addr)
+{
+    uint32_t buf[8];
+    uint32_t obuf[8];
+    target_phys_addr_t paddr = xhci_mask64(addr);
+
+    cpu_physical_memory_read(paddr, (uint8_t *) &buf, 32);
+
+    memcpy(obuf, buf, sizeof(obuf));
+
+    if ((buf[0] & 0xff) == 2) {
+        obuf[0] = 0x49932000 + 0x54dc200 * buf[2] + 0x7429b578 * buf[3];
+        obuf[0] |=  (buf[2] * buf[3]) & 0xff;
+        obuf[1] = 0x0132bb37 + 0xe89 * buf[2] + 0xf09 * buf[3];
+        obuf[2] = 0x0066c2e9 + 0x2091 * buf[2] + 0x19bd * buf[3];
+        obuf[3] = 0xd5281342 + 0x2cc9691 * buf[2] + 0x2367662 * buf[3];
+        obuf[4] = 0x0123c75c + 0x1595 * buf[2] + 0x19ec * buf[3];
+        obuf[5] = 0x00f695de + 0x26fd * buf[2] + 0x3e9 * buf[3];
+        obuf[6] = obuf[2] ^ obuf[3] ^ 0x29472956;
+        obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593;
+    }
+
+    cpu_physical_memory_write(paddr, (uint8_t *) &obuf, 32);
+}
+
+static void xhci_process_commands(XHCIState *xhci)
+{
+    XHCITRB trb;
+    TRBType type;
+    XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS};
+    target_phys_addr_t addr;
+    unsigned int i, slotid = 0;
+
+    DPRINTF("xhci_process_commands()\n");
+    if (!xhci_running(xhci)) {
+        DPRINTF("xhci_process_commands() called while xHC stopped or paused\n");
+        return;
+    }
+
+    xhci->crcr_low |= CRCR_CRR;
+
+    while ((type = xhci_ring_fetch(xhci, &xhci->cmd_ring, &trb, &addr))) {
+        event.ptr = addr;
+        switch (type) {
+        case CR_ENABLE_SLOT:
+            for (i = 0; i < MAXSLOTS; i++) {
+                if (!xhci->slots[i].enabled) {
+                    break;
+                }
+            }
+            if (i >= MAXSLOTS) {
+                fprintf(stderr, "xhci: no device slots available\n");
+                event.ccode = CC_NO_SLOTS_ERROR;
+            } else {
+                slotid = i+1;
+                event.ccode = xhci_enable_slot(xhci, slotid);
+            }
+            break;
+        case CR_DISABLE_SLOT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_disable_slot(xhci, slotid);
+            }
+            break;
+        case CR_ADDRESS_DEVICE:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_address_slot(xhci, slotid, trb.parameter,
+                                                trb.control & TRB_CR_BSR);
+            }
+            break;
+        case CR_CONFIGURE_ENDPOINT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_configure_slot(xhci, slotid, trb.parameter,
+                                                  trb.control & TRB_CR_DC);
+            }
+            break;
+        case CR_EVALUATE_CONTEXT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_evaluate_slot(xhci, slotid, trb.parameter);
+            }
+            break;
+        case CR_STOP_ENDPOINT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
+                    & TRB_CR_EPID_MASK;
+                event.ccode = xhci_stop_ep(xhci, slotid, epid);
+            }
+            break;
+        case CR_RESET_ENDPOINT:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
+                    & TRB_CR_EPID_MASK;
+                event.ccode = xhci_reset_ep(xhci, slotid, epid);
+            }
+            break;
+        case CR_SET_TR_DEQUEUE:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
+                    & TRB_CR_EPID_MASK;
+                event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid,
+                                                  trb.parameter);
+            }
+            break;
+        case CR_RESET_DEVICE:
+            slotid = xhci_get_slot(xhci, &event, &trb);
+            if (slotid) {
+                event.ccode = xhci_reset_slot(xhci, slotid);
+            }
+            break;
+        case CR_GET_PORT_BANDWIDTH:
+            event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter);
+            break;
+        case 48:
+            /* VIA challenge response */
+            xhci_via_challenge(trb.parameter);
+            break;
+        case 49:
+            /* NEC get FW revision */
+            event.type = 48; /* NEC reply */
+            event.length = 0x3025;
+            break;
+        case 50:
+        {
+            /* NEC challenge response */
+            uint32_t chi = trb.parameter >> 32;
+            uint32_t clo = trb.parameter;
+            uint32_t val = xhci_nec_challenge(chi, clo);
+            event.length = val & 0xFFFF;
+            event.epid = val >> 16;
+            slotid = val >> 24;
+            event.type = 48; /* NEC reply */
+        }
+        break;
+        default:
+            fprintf(stderr, "xhci: unimplemented command %d\n", type);
+            event.ccode = CC_TRB_ERROR;
+            break;
+        }
+        event.slotid = slotid;
+        xhci_event(xhci, &event);
+    }
+}
+
+static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
+{
+    int nr = port->port.index + 1;
+
+    port->portsc = PORTSC_PP;
+    if (port->port.dev && !is_detach) {
+        port->portsc |= PORTSC_CCS;
+        switch (port->port.dev->speed) {
+        case USB_SPEED_LOW:
+            port->portsc |= PORTSC_SPEED_LOW;
+            break;
+        case USB_SPEED_FULL:
+            port->portsc |= PORTSC_SPEED_FULL;
+            break;
+        case USB_SPEED_HIGH:
+            port->portsc |= PORTSC_SPEED_HIGH;
+            break;
+        }
+    }
+
+    if (xhci_running(xhci)) {
+        port->portsc |= PORTSC_CSC;
+        XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
+        xhci_event(xhci, &ev);
+        DPRINTF("xhci: port change event for port %d\n", nr);
+    }
+}
+
+static void xhci_reset(void *opaque)
+{
+    XHCIState *xhci = opaque;
+    int i;
+
+    DPRINTF("xhci: full reset\n");
+    if (!(xhci->usbsts & USBSTS_HCH)) {
+        fprintf(stderr, "xhci: reset while running!\n");
+    }
+
+    xhci->usbcmd = 0;
+    xhci->usbsts = USBSTS_HCH;
+    xhci->dnctrl = 0;
+    xhci->crcr_low = 0;
+    xhci->crcr_high = 0;
+    xhci->dcbaap_low = 0;
+    xhci->dcbaap_high = 0;
+    xhci->config = 0;
+    xhci->devaddr = 2;
+
+    for (i = 0; i < MAXSLOTS; i++) {
+        xhci_disable_slot(xhci, i+1);
+    }
+
+    for (i = 0; i < MAXPORTS; i++) {
+        xhci_update_port(xhci, xhci->ports + i, 0);
+    }
+
+    xhci->mfindex = 0;
+    xhci->iman = 0;
+    xhci->imod = 0;
+    xhci->erstsz = 0;
+    xhci->erstba_low = 0;
+    xhci->erstba_high = 0;
+    xhci->erdp_low = 0;
+    xhci->erdp_high = 0;
+
+    xhci->er_ep_idx = 0;
+    xhci->er_pcs = 1;
+    xhci->er_full = 0;
+    xhci->ev_buffer_put = 0;
+    xhci->ev_buffer_get = 0;
+}
+
+static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_cap_read(0x%x)\n", reg);
+
+    switch (reg) {
+    case 0x00: /* HCIVERSION, CAPLENGTH */
+        return 0x01000000 | LEN_CAP;
+    case 0x04: /* HCSPARAMS 1 */
+        return (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+    case 0x08: /* HCSPARAMS 2 */
+        return 0x0000000f;
+    case 0x0c: /* HCSPARAMS 3 */
+        return 0x00000000;
+    case 0x10: /* HCCPARAMS */
+#if TARGET_PHYS_ADDR_BITS > 32
+        return 0x00081001;
+#else
+        return 0x00081000;
+#endif
+    case 0x14: /* DBOFF */
+        return OFF_DOORBELL;
+    case 0x18: /* RTSOFF */
+        return OFF_RUNTIME;
+
+    /* extended capabilities */
+    case 0x20: /* Supported Protocol:00 */
+#if USB3_PORTS > 0
+        return 0x02000402; /* USB 2.0 */
+#else
+        return 0x02000002; /* USB 2.0 */
+#endif
+    case 0x24: /* Supported Protocol:04 */
+        return 0x20425455; /* "USB " */
+    case 0x28: /* Supported Protocol:08 */
+        return 0x00000001 | (USB2_PORTS<<8);
+    case 0x2c: /* Supported Protocol:0c */
+        return 0x00000000; /* reserved */
+#if USB3_PORTS > 0
+    case 0x30: /* Supported Protocol:00 */
+        return 0x03000002; /* USB 3.0 */
+    case 0x34: /* Supported Protocol:04 */
+        return 0x20425455; /* "USB " */
+    case 0x38: /* Supported Protocol:08 */
+        return 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+    case 0x3c: /* Supported Protocol:0c */
+        return 0x00000000; /* reserved */
+#endif
+    default:
+        fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg);
+    }
+    return 0;
+}
+
+static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
+{
+    uint32_t port = reg >> 4;
+    if (port >= MAXPORTS) {
+        fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
+        return 0;
+    }
+
+    switch (reg & 0xf) {
+    case 0x00: /* PORTSC */
+        return xhci->ports[port].portsc;
+    case 0x04: /* PORTPMSC */
+    case 0x08: /* PORTLI */
+        return 0;
+    case 0x0c: /* reserved */
+    default:
+        fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
+                port, reg);
+        return 0;
+    }
+}
+
+static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    uint32_t port = reg >> 4;
+    uint32_t portsc;
+
+    if (port >= MAXPORTS) {
+        fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
+        return;
+    }
+
+    switch (reg & 0xf) {
+    case 0x00: /* PORTSC */
+        portsc = xhci->ports[port].portsc;
+        /* write-1-to-clear bits*/
+        portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
+                           PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
+        if (val & PORTSC_LWS) {
+            /* overwrite PLS only when LWS=1 */
+            portsc &= ~(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
+            portsc |= val & (PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
+        }
+        /* read/write bits */
+        portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
+        portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
+        /* write-1-to-start bits */
+        if (val & PORTSC_PR) {
+            DPRINTF("xhci: port %d reset\n", port);
+            if (xhci->ports[port].port.dev) {
+                usb_send_msg(xhci->ports[port].port.dev, USB_MSG_RESET);
+            }
+            portsc |= PORTSC_PRC | PORTSC_PED;
+        }
+        xhci->ports[port].portsc = portsc;
+        break;
+    case 0x04: /* PORTPMSC */
+    case 0x08: /* PORTLI */
+    default:
+        fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n",
+                port, reg);
+    }
+}
+
+static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_oper_read(0x%x)\n", reg);
+
+    if (reg >= 0x400) {
+        return xhci_port_read(xhci, reg - 0x400);
+    }
+
+    switch (reg) {
+    case 0x00: /* USBCMD */
+        return xhci->usbcmd;
+    case 0x04: /* USBSTS */
+        return xhci->usbsts;
+    case 0x08: /* PAGESIZE */
+        return 1; /* 4KiB */
+    case 0x14: /* DNCTRL */
+        return xhci->dnctrl;
+    case 0x18: /* CRCR low */
+        return xhci->crcr_low & ~0xe;
+    case 0x1c: /* CRCR high */
+        return xhci->crcr_high;
+    case 0x30: /* DCBAAP low */
+        return xhci->dcbaap_low;
+    case 0x34: /* DCBAAP high */
+        return xhci->dcbaap_high;
+    case 0x38: /* CONFIG */
+        return xhci->config;
+    default:
+        fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg);
+    }
+    return 0;
+}
+
+static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    DPRINTF("xhci_oper_write(0x%x, 0x%08x)\n", reg, val);
+
+    if (reg >= 0x400) {
+        xhci_port_write(xhci, reg - 0x400, val);
+        return;
+    }
+
+    switch (reg) {
+    case 0x00: /* USBCMD */
+        if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) {
+            xhci_run(xhci);
+        } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) {
+            xhci_stop(xhci);
+        }
+        xhci->usbcmd = val & 0xc0f;
+        if (val & USBCMD_HCRST) {
+            xhci_reset(xhci);
+        }
+        xhci_irq_update(xhci);
+        break;
+
+    case 0x04: /* USBSTS */
+        /* these bits are write-1-to-clear */
+        xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
+        xhci_irq_update(xhci);
+        break;
+
+    case 0x14: /* DNCTRL */
+        xhci->dnctrl = val & 0xffff;
+        break;
+    case 0x18: /* CRCR low */
+        xhci->crcr_low = (val & 0xffffffcf) | (xhci->crcr_low & CRCR_CRR);
+        break;
+    case 0x1c: /* CRCR high */
+        xhci->crcr_high = val;
+        if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) {
+            XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED};
+            xhci->crcr_low &= ~CRCR_CRR;
+            xhci_event(xhci, &event);
+            DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
+        } else {
+            target_phys_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
+            xhci_ring_init(xhci, &xhci->cmd_ring, base);
+        }
+        xhci->crcr_low &= ~(CRCR_CA | CRCR_CS);
+        break;
+    case 0x30: /* DCBAAP low */
+        xhci->dcbaap_low = val & 0xffffffc0;
+        break;
+    case 0x34: /* DCBAAP high */
+        xhci->dcbaap_high = val;
+        break;
+    case 0x38: /* CONFIG */
+        xhci->config = val & 0xff;
+        break;
+    default:
+        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+    }
+}
+
+static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_runtime_read(0x%x)\n", reg);
+
+    switch (reg) {
+    case 0x00: /* MFINDEX */
+        fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
+        return xhci->mfindex;
+    case 0x20: /* IMAN */
+        return xhci->iman;
+    case 0x24: /* IMOD */
+        return xhci->imod;
+    case 0x28: /* ERSTSZ */
+        return xhci->erstsz;
+    case 0x30: /* ERSTBA low */
+        return xhci->erstba_low;
+    case 0x34: /* ERSTBA high */
+        return xhci->erstba_high;
+    case 0x38: /* ERDP low */
+        return xhci->erdp_low;
+    case 0x3c: /* ERDP high */
+        return xhci->erdp_high;
+    default:
+        fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
+    }
+    return 0;
+}
+
+static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    DPRINTF("xhci_runtime_write(0x%x, 0x%08x)\n", reg, val);
+
+    switch (reg) {
+    case 0x20: /* IMAN */
+        if (val & IMAN_IP) {
+            xhci->iman &= ~IMAN_IP;
+        }
+        xhci->iman &= ~IMAN_IE;
+        xhci->iman |= val & IMAN_IE;
+        xhci_irq_update(xhci);
+        break;
+    case 0x24: /* IMOD */
+        xhci->imod = val;
+        break;
+    case 0x28: /* ERSTSZ */
+        xhci->erstsz = val & 0xffff;
+        break;
+    case 0x30: /* ERSTBA low */
+        /* XXX NEC driver bug: it doesn't align this to 64 bytes
+        xhci->erstba_low = val & 0xffffffc0; */
+        xhci->erstba_low = val & 0xfffffff0;
+        break;
+    case 0x34: /* ERSTBA high */
+        xhci->erstba_high = val;
+        xhci_er_reset(xhci);
+        break;
+    case 0x38: /* ERDP low */
+        if (val & ERDP_EHB) {
+            xhci->erdp_low &= ~ERDP_EHB;
+        }
+        xhci->erdp_low = (val & ~ERDP_EHB) | (xhci->erdp_low & ERDP_EHB);
+        break;
+    case 0x3c: /* ERDP high */
+        xhci->erdp_high = val;
+        xhci_events_update(xhci);
+        break;
+    default:
+        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+    }
+}
+
+static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg)
+{
+    DPRINTF("xhci_doorbell_read(0x%x)\n", reg);
+    /* doorbells always read as 0 */
+    return 0;
+}
+
+static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+{
+    DPRINTF("xhci_doorbell_write(0x%x, 0x%08x)\n", reg, val);
+
+    if (!xhci_running(xhci)) {
+        fprintf(stderr, "xhci: wrote doorbell while xHC stopped or paused\n");
+        return;
+    }
+
+    reg >>= 2;
+
+    if (reg == 0) {
+        if (val == 0) {
+            xhci_process_commands(xhci);
+        } else {
+            fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", val);
+        }
+    } else {
+        if (reg > MAXSLOTS) {
+            fprintf(stderr, "xhci: bad doorbell %d\n", reg);
+        } else if (val > 31) {
+            fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", reg, val);
+        } else {
+            xhci_kick_ep(xhci, reg, val);
+        }
+    }
+}
+
+static uint64_t xhci_mem_read(void *ptr, target_phys_addr_t addr,
+                              unsigned size)
+{
+    XHCIState *xhci = ptr;
+
+    /* Only aligned reads are allowed on xHCI */
+    if (addr & 3) {
+        fprintf(stderr, "xhci_mem_read: Mis-aligned read\n");
+        return 0;
+    }
+
+    if (addr < LEN_CAP) {
+        return xhci_cap_read(xhci, addr);
+    } else if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
+        return xhci_oper_read(xhci, addr - OFF_OPER);
+    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
+        return xhci_runtime_read(xhci, addr - OFF_RUNTIME);
+    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
+        return xhci_doorbell_read(xhci, addr - OFF_DOORBELL);
+    } else {
+        fprintf(stderr, "xhci_mem_read: Bad offset %x\n", (int)addr);
+        return 0;
+    }
+}
+
+static void xhci_mem_write(void *ptr, target_phys_addr_t addr,
+                           uint64_t val, unsigned size)
+{
+    XHCIState *xhci = ptr;
+
+    /* Only aligned writes are allowed on xHCI */
+    if (addr & 3) {
+        fprintf(stderr, "xhci_mem_write: Mis-aligned write\n");
+        return;
+    }
+
+    if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
+        xhci_oper_write(xhci, addr - OFF_OPER, val);
+    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
+        xhci_runtime_write(xhci, addr - OFF_RUNTIME, val);
+    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
+        xhci_doorbell_write(xhci, addr - OFF_DOORBELL, val);
+    } else {
+        fprintf(stderr, "xhci_mem_write: Bad offset %x\n", (int)addr);
+    }
+}
+
+static const MemoryRegionOps xhci_mem_ops = {
+    .read = xhci_mem_read,
+    .write = xhci_mem_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void xhci_attach(USBPort *usbport)
+{
+    XHCIState *xhci = usbport->opaque;
+    XHCIPort *port = &xhci->ports[usbport->index];
+
+    xhci_update_port(xhci, port, 0);
+}
+
+static void xhci_detach(USBPort *usbport)
+{
+    XHCIState *xhci = usbport->opaque;
+    XHCIPort *port = &xhci->ports[usbport->index];
+
+    xhci_update_port(xhci, port, 1);
+}
+
+static void xhci_complete(USBPort *port, USBPacket *packet)
+{
+    XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
+
+    xhci_complete_packet(xfer, packet->result);
+    xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
+}
+
+static void xhci_child_detach(USBPort *port, USBDevice *child)
+{
+    FIXME();
+}
+
+static USBPortOps xhci_port_ops = {
+    .attach   = xhci_attach,
+    .detach   = xhci_detach,
+    .complete = xhci_complete,
+    .child_detach = xhci_child_detach,
+};
+
+static USBBusOps xhci_bus_ops = {
+};
+
+static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
+{
+    int i;
+
+    xhci->usbsts = USBSTS_HCH;
+
+    usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev);
+
+    for (i = 0; i < MAXPORTS; i++) {
+        memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
+        usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
+                          &xhci_port_ops, USB_SPEED_MASK_HIGH);
+    }
+    for (i = 0; i < MAXSLOTS; i++) {
+        xhci->slots[i].enabled = 0;
+    }
+
+    qemu_register_reset(xhci_reset, xhci);
+}
+
+static int usb_xhci_initfn(struct PCIDevice *dev)
+{
+    int ret;
+
+    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
+
+    xhci->pci_dev.config[PCI_CLASS_PROG] = 0x30;    /* xHCI */
+    xhci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
+    xhci->pci_dev.config[PCI_CACHE_LINE_SIZE] = 0x10;
+    xhci->pci_dev.config[0x60] = 0x30; /* release number */
+
+    usb_xhci_init(xhci, &dev->qdev);
+
+    xhci->irq = xhci->pci_dev.irq[0];
+
+    memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci,
+                          "xhci", LEN_REGS);
+    pci_register_bar(&xhci->pci_dev, 0,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
+                     &xhci->mem);
+
+    ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0);
+    assert(ret >= 0);
+
+    if (xhci->msi) {
+        ret = msi_init(&xhci->pci_dev, 0x70, 1, true, false);
+        assert(ret >= 0);
+    }
+
+    return 0;
+}
+
+static void xhci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val,
+                              int len)
+{
+    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
+
+    pci_default_write_config(dev, addr, val, len);
+    if (xhci->msi) {
+        msi_write_config(dev, addr, val, len);
+    }
+}
+
+static const VMStateDescription vmstate_xhci = {
+    .name = "xhci",
+    .unmigratable = 1,
+};
+
+static PCIDeviceInfo xhci_info = {
+    .qdev.name    = "nec-usb-xhci",
+    .qdev.alias   = "xhci",
+    .qdev.size    = sizeof(XHCIState),
+    .qdev.vmsd    = &vmstate_xhci,
+    .init         = usb_xhci_initfn,
+    .vendor_id    = PCI_VENDOR_ID_NEC,
+    .device_id    = PCI_DEVICE_ID_NEC_UPD720200,
+    .class_id     = PCI_CLASS_SERIAL_USB,
+    .revision     = 0x03,
+    .is_express   = 1,
+    .config_write = xhci_write_config,
+    .qdev.props   = (Property[]) {
+        DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void xhci_register(void)
+{
+    pci_qdev_register(&xhci_info);
+}
+device_init(xhci_register);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 07/17] usb: add USBEndpoint
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (5 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 06/17] xhci: Initial xHCI implementation Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 08/17] usb: add ifnum to USBEndpoint Gerd Hoffmann
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Start maintaining endpoint state at USBDevice level.  Add USBEndpoint
struct and some helper functions to deal with it.  For now it contains
the endpoint type only.  Moved over some bits from usb-linux.c

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-bus.c |    1 +
 hw/usb.c     |   30 +++++++++++++++++++++++++++
 hw/usb.h     |   14 ++++++++++++
 usb-linux.c  |   63 ++++++++++++++++++++++++++-------------------------------
 4 files changed, 74 insertions(+), 34 deletions(-)

diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index bd4afa7..016a3f2 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -75,6 +75,7 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
     dev->info = info;
     dev->auto_attach = 1;
     QLIST_INIT(&dev->strings);
+    usb_ep_init(dev);
     rc = usb_claim_port(dev);
     if (rc != 0) {
         return rc;
diff --git a/hw/usb.c b/hw/usb.c
index 2216efe..5d6baaf 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -414,3 +414,33 @@ void usb_packet_cleanup(USBPacket *p)
 {
     qemu_iovec_destroy(&p->iov);
 }
+
+void usb_ep_init(USBDevice *dev)
+{
+    int ep;
+
+    for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
+        dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
+        dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
+    }
+}
+
+struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *eps = pid == USB_TOKEN_IN ? dev->ep_in : dev->ep_out;
+    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
+    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
+    return eps + ep - 1;
+}
+
+uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    return uep->type;
+}
+
+void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    uep->type = type;
+}
diff --git a/hw/usb.h b/hw/usb.h
index b2c9479..85cbe71 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -144,6 +144,7 @@
 #define USB_ENDPOINT_XFER_ISOC		1
 #define USB_ENDPOINT_XFER_BULK		2
 #define USB_ENDPOINT_XFER_INT		3
+#define USB_ENDPOINT_XFER_INVALID     255
 
 typedef struct USBBus USBBus;
 typedef struct USBBusOps USBBusOps;
@@ -151,6 +152,7 @@ typedef struct USBPort USBPort;
 typedef struct USBDevice USBDevice;
 typedef struct USBDeviceInfo USBDeviceInfo;
 typedef struct USBPacket USBPacket;
+typedef struct USBEndpoint USBEndpoint;
 
 typedef struct USBDesc USBDesc;
 typedef struct USBDescID USBDescID;
@@ -171,6 +173,10 @@ struct USBDescString {
 #define USB_MAX_ENDPOINTS  15
 #define USB_MAX_INTERFACES 16
 
+struct USBEndpoint {
+    uint8_t type;
+};
+
 /* definition of a USB device */
 struct USBDevice {
     DeviceState qdev;
@@ -196,6 +202,9 @@ struct USBDevice {
     int32_t setup_len;
     int32_t setup_index;
 
+    USBEndpoint ep_in[USB_MAX_ENDPOINTS];
+    USBEndpoint ep_out[USB_MAX_ENDPOINTS];
+
     QLIST_HEAD(, USBDescString) strings;
     const USBDescDevice *device;
 
@@ -322,6 +331,11 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p);
 void usb_packet_complete(USBDevice *dev, USBPacket *p);
 void usb_cancel_packet(USBPacket * p);
 
+void usb_ep_init(USBDevice *dev);
+struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep);
+uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep);
+void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type);
+
 void usb_attach(USBPort *port);
 void usb_detach(USBPort *port);
 void usb_reset(USBPort *port);
diff --git a/usb-linux.c b/usb-linux.c
index ded0726..9967975 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -67,12 +67,10 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
 #endif
 
 #define PRODUCT_NAME_SZ 32
-#define MAX_ENDPOINTS 15
 #define MAX_PORTLEN 16
 
 /* endpoint association data */
 #define ISO_FRAME_DESC_PER_URB 32
-#define INVALID_EP_TYPE 255
 
 /* devio.c limits single requests to 16k */
 #define MAX_USBFS_BUFFER_SIZE 16384
@@ -80,7 +78,6 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
 typedef struct AsyncURB AsyncURB;
 
 struct endp_data {
-    uint8_t type;
     uint8_t halted;
     uint8_t iso_started;
     AsyncURB *iso_urb;
@@ -110,8 +107,8 @@ typedef struct USBHostDevice {
     uint32_t  iso_urb_count;
     Notifier  exit;
 
-    struct endp_data ep_in[MAX_ENDPOINTS];
-    struct endp_data ep_out[MAX_ENDPOINTS];
+    struct endp_data ep_in[USB_MAX_ENDPOINTS];
+    struct endp_data ep_out[USB_MAX_ENDPOINTS];
     QLIST_HEAD(, AsyncURB) aurbs;
 
     /* Host side address */
@@ -133,6 +130,19 @@ static int usb_host_read_file(char *line, size_t line_size,
                             const char *device_file, const char *device_name);
 static int usb_linux_update_endp_table(USBHostDevice *s);
 
+static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p)
+{
+    static const int usbfs[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL,
+        [USB_ENDPOINT_XFER_ISOC]    = USBDEVFS_URB_TYPE_ISO,
+        [USB_ENDPOINT_XFER_BULK]    = USBDEVFS_URB_TYPE_BULK,
+        [USB_ENDPOINT_XFER_INT]     = USBDEVFS_URB_TYPE_INTERRUPT,
+    };
+    uint8_t type = usb_ep_get_type(&s->dev, p->pid, p->devep);
+    assert(type < ARRAY_SIZE(usbfs));
+    return usbfs[type];
+}
+
 static int usb_host_do_reset(USBHostDevice *dev)
 {
     struct timeval s, e;
@@ -156,18 +166,18 @@ static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
 {
     struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
     assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
-    assert(ep > 0 && ep <= MAX_ENDPOINTS);
+    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
     return eps + ep - 1;
 }
 
 static int is_isoc(USBHostDevice *s, int pid, int ep)
 {
-    return get_endp(s, pid, ep)->type == USBDEVFS_URB_TYPE_ISO;
+    return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
 }
 
 static int is_valid(USBHostDevice *s, int pid, int ep)
 {
-    return get_endp(s, pid, ep)->type != INVALID_EP_TYPE;
+    return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
 }
 
 static int is_halted(USBHostDevice *s, int pid, int ep)
@@ -896,7 +906,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
 
         urb = &aurb->urb;
         urb->endpoint      = ep;
-        urb->type          = USBDEVFS_URB_TYPE_BULK;
+        urb->type          = usb_host_usbfs_type(s, p);
         urb->usercontext   = s;
         urb->buffer        = pbuf;
         urb->buffer_length = prem;
@@ -992,7 +1002,7 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
 
     trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
 
-    for (i = 1; i <= MAX_ENDPOINTS; i++) {
+    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
         if (is_isoc(s, USB_TOKEN_IN, i)) {
             usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
         }
@@ -1126,10 +1136,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
     int interface, length, i, ep, pid;
     struct endp_data *epd;
 
-    for (i = 0; i < MAX_ENDPOINTS; i++) {
-        s->ep_in[i].type = INVALID_EP_TYPE;
-        s->ep_out[i].type = INVALID_EP_TYPE;
-    }
+    usb_ep_init(&s->dev);
 
     if (s->dev.configuration == 0) {
         /* not configured yet -- leave all endpoints disabled */
@@ -1192,27 +1199,15 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
                 return 1;
             }
 
-            switch (descriptors[i + 3] & 0x3) {
-            case 0x00:
-                type = USBDEVFS_URB_TYPE_CONTROL;
-                break;
-            case 0x01:
-                type = USBDEVFS_URB_TYPE_ISO;
+            type = descriptors[i + 3] & 0x3;
+            if (type == USB_ENDPOINT_XFER_ISOC) {
                 set_max_packet_size(s, pid, ep, descriptors + i);
-                break;
-            case 0x02:
-                type = USBDEVFS_URB_TYPE_BULK;
-                break;
-            case 0x03:
-                type = USBDEVFS_URB_TYPE_INTERRUPT;
-                break;
-            default:
-                DPRINTF("usb_host: malformed endpoint type\n");
-                type = USBDEVFS_URB_TYPE_BULK;
-            }
+            };
+            assert(usb_ep_get_type(&s->dev, pid, ep) ==
+                   USB_ENDPOINT_XFER_INVALID);
+            usb_ep_set_type(&s->dev, pid, ep, type);
+
             epd = get_endp(s, pid, ep);
-            assert(epd->type == INVALID_EP_TYPE);
-            epd->type = type;
             epd->halted = 0;
 
             i += descriptors[i];
@@ -1371,7 +1366,7 @@ static int usb_host_close(USBHostDevice *dev)
 
     qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
     dev->closing = 1;
-    for (i = 1; i <= MAX_ENDPOINTS; i++) {
+    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
         if (is_isoc(dev, USB_TOKEN_IN, i)) {
             usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
         }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 08/17] usb: add ifnum to USBEndpoint
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (6 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 07/17] usb: add USBEndpoint Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 09/17] usb-desc: USBEndpoint support Gerd Hoffmann
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb.c    |   14 ++++++++++++++
 hw/usb.h    |    3 +++
 usb-linux.c |    1 +
 3 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/hw/usb.c b/hw/usb.c
index 5d6baaf..6ba063a 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -422,6 +422,8 @@ void usb_ep_init(USBDevice *dev)
     for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
         dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
         dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
+        dev->ep_in[ep].ifnum = 0;
+        dev->ep_out[ep].ifnum = 0;
     }
 }
 
@@ -444,3 +446,15 @@ void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type)
     struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
     uep->type = type;
 }
+
+uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    return uep->ifnum;
+}
+
+void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    uep->ifnum = ifnum;
+}
diff --git a/hw/usb.h b/hw/usb.h
index 85cbe71..60e8858 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -175,6 +175,7 @@ struct USBDescString {
 
 struct USBEndpoint {
     uint8_t type;
+    uint8_t ifnum;
 };
 
 /* definition of a USB device */
@@ -334,7 +335,9 @@ void usb_cancel_packet(USBPacket * p);
 void usb_ep_init(USBDevice *dev);
 struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep);
 uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep);
+uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep);
 void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type);
+void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum);
 
 void usb_attach(USBPort *port);
 void usb_detach(USBPort *port);
diff --git a/usb-linux.c b/usb-linux.c
index 9967975..cb66ef3 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -1206,6 +1206,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
             assert(usb_ep_get_type(&s->dev, pid, ep) ==
                    USB_ENDPOINT_XFER_INVALID);
             usb_ep_set_type(&s->dev, pid, ep, type);
+            usb_ep_set_ifnum(&s->dev, pid, ep, interface);
 
             epd = get_endp(s, pid, ep);
             epd->halted = 0;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 09/17] usb-desc: USBEndpoint support
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (7 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 08/17] usb: add ifnum to USBEndpoint Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 10/17] usb/debug: add usb_ep_dump Gerd Hoffmann
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Initialize USBEndpoint structs from USBDesc* data.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-desc.c |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 9c38661..0768334 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -231,6 +231,27 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
 
 /* ------------------------------------------------------------------ */
 
+static void usb_desc_ep_init(USBDevice *dev)
+{
+    const USBDescIface *iface;
+    int i, e, pid, ep;
+
+    usb_ep_init(dev);
+    for (i = 0; i < dev->ninterfaces; i++) {
+        iface = dev->ifaces[i];
+        if (iface == NULL) {
+            continue;
+        }
+        for (e = 0; e < iface->bNumEndpoints; e++) {
+            pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
+                USB_TOKEN_IN : USB_TOKEN_OUT;
+            ep = iface->eps[e].bEndpointAddress & 0x0f;
+            usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
+            usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
+        }
+    }
+}
+
 static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
                                                    int nif, int alt)
 {
@@ -272,6 +293,7 @@ static int usb_desc_set_interface(USBDevice *dev, int index, int value)
     old = dev->altsetting[index];
     dev->altsetting[index] = value;
     dev->ifaces[index] = iface;
+    usb_desc_ep_init(dev);
 
     if (dev->info->set_interface && old != value) {
         dev->info->set_interface(dev, index, old, value);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 10/17] usb/debug: add usb_ep_dump
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (8 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 09/17] usb-desc: USBEndpoint support Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 11/17] usb: add max_packet_size to USBEndpoint Gerd Hoffmann
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Add function to dump endpoint data, for debugging purposes.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb.c    |   40 ++++++++++++++++++++++++++++++++++++++++
 hw/usb.h    |    1 +
 usb-linux.c |    3 +++
 3 files changed, 44 insertions(+), 0 deletions(-)

diff --git a/hw/usb.c b/hw/usb.c
index 6ba063a..f07cb9d 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -427,6 +427,46 @@ void usb_ep_init(USBDevice *dev)
     }
 }
 
+void usb_ep_dump(USBDevice *dev)
+{
+    static const char *tname[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = "control",
+        [USB_ENDPOINT_XFER_ISOC]    = "isoc",
+        [USB_ENDPOINT_XFER_BULK]    = "bulk",
+        [USB_ENDPOINT_XFER_INT]     = "int",
+    };
+    int ifnum, ep, first;
+
+    fprintf(stderr, "Device \"%s\", config %d\n",
+            dev->product_desc, dev->configuration);
+    for (ifnum = 0; ifnum < 16; ifnum++) {
+        first = 1;
+        for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
+            if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID &&
+                dev->ep_in[ep].ifnum == ifnum) {
+                if (first) {
+                    first = 0;
+                    fprintf(stderr, "  Interface %d, alternative %d\n",
+                            ifnum, dev->altsetting[ifnum]);
+                }
+                fprintf(stderr, "    Endpoint %d, IN, %s\n", ep,
+                        tname[dev->ep_in[ep].type]);
+            }
+            if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID &&
+                dev->ep_out[ep].ifnum == ifnum) {
+                if (first) {
+                    first = 0;
+                    fprintf(stderr, "  Interface %d, alternative %d\n",
+                            ifnum, dev->altsetting[ifnum]);
+                }
+                fprintf(stderr, "    Endpoint %d, OUT, %s\n", ep,
+                        tname[dev->ep_out[ep].type]);
+            }
+        }
+    }
+    fprintf(stderr, "--\n");
+}
+
 struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
 {
     struct USBEndpoint *eps = pid == USB_TOKEN_IN ? dev->ep_in : dev->ep_out;
diff --git a/hw/usb.h b/hw/usb.h
index 60e8858..c35ff74 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -333,6 +333,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p);
 void usb_cancel_packet(USBPacket * p);
 
 void usb_ep_init(USBDevice *dev);
+void usb_ep_dump(USBDevice *dev);
 struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep);
 uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep);
 uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep);
diff --git a/usb-linux.c b/usb-linux.c
index cb66ef3..2a7b748 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -1214,6 +1214,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
             i += descriptors[i];
         }
     }
+#ifdef DEBUG
+    usb_ep_dump(&s->dev);
+#endif
     return 0;
 }
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 11/17] usb: add max_packet_size to USBEndpoint
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (9 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 10/17] usb/debug: add usb_ep_dump Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 12/17] usb: link packets to endpoints not devices Gerd Hoffmann
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-desc.c |    2 ++
 hw/usb.c      |   37 +++++++++++++++++++++++++++++++++----
 hw/usb.h      |    4 ++++
 usb-linux.c   |   31 +++++--------------------------
 4 files changed, 44 insertions(+), 30 deletions(-)

diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 0768334..b3eb97b 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -248,6 +248,8 @@ static void usb_desc_ep_init(USBDevice *dev)
             ep = iface->eps[e].bEndpointAddress & 0x0f;
             usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
             usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
+            usb_ep_set_max_packet_size(dev, pid, ep,
+                                       iface->eps[e].wMaxPacketSize);
         }
     }
 }
diff --git a/hw/usb.c b/hw/usb.c
index f07cb9d..0f163b4 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -449,8 +449,9 @@ void usb_ep_dump(USBDevice *dev)
                     fprintf(stderr, "  Interface %d, alternative %d\n",
                             ifnum, dev->altsetting[ifnum]);
                 }
-                fprintf(stderr, "    Endpoint %d, IN, %s\n", ep,
-                        tname[dev->ep_in[ep].type]);
+                fprintf(stderr, "    Endpoint %d, IN, %s, %d max\n", ep,
+                        tname[dev->ep_in[ep].type],
+                        dev->ep_in[ep].max_packet_size);
             }
             if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID &&
                 dev->ep_out[ep].ifnum == ifnum) {
@@ -459,8 +460,9 @@ void usb_ep_dump(USBDevice *dev)
                     fprintf(stderr, "  Interface %d, alternative %d\n",
                             ifnum, dev->altsetting[ifnum]);
                 }
-                fprintf(stderr, "    Endpoint %d, OUT, %s\n", ep,
-                        tname[dev->ep_out[ep].type]);
+                fprintf(stderr, "    Endpoint %d, OUT, %s, %d max\n", ep,
+                        tname[dev->ep_out[ep].type],
+                        dev->ep_out[ep].max_packet_size);
             }
         }
     }
@@ -498,3 +500,30 @@ void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum)
     struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
     uep->ifnum = ifnum;
 }
+
+void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
+                                uint16_t raw)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    int size, microframes;
+
+    size = raw & 0x7ff;
+    switch ((raw >> 11) & 3) {
+    case 1:
+        microframes = 2;
+        break;
+    case 2:
+        microframes = 3;
+        break;
+    default:
+        microframes = 1;
+        break;
+    }
+    uep->max_packet_size = size * microframes;
+}
+
+int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    return uep->max_packet_size;
+}
diff --git a/hw/usb.h b/hw/usb.h
index c35ff74..5ea984c 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -176,6 +176,7 @@ struct USBDescString {
 struct USBEndpoint {
     uint8_t type;
     uint8_t ifnum;
+    int max_packet_size;
 };
 
 /* definition of a USB device */
@@ -339,6 +340,9 @@ uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep);
 uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep);
 void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type);
 void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum);
+void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
+                                uint16_t raw);
+int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
 
 void usb_attach(USBPort *port);
 void usb_detach(USBPort *port);
diff --git a/usb-linux.c b/usb-linux.c
index 2a7b748..56898dd 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -83,7 +83,6 @@ struct endp_data {
     AsyncURB *iso_urb;
     int iso_urb_idx;
     int iso_buffer_used;
-    int max_packet_size;
     int inflight;
 };
 
@@ -259,26 +258,6 @@ static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
     return get_endp(s, pid, ep)->iso_buffer_used;
 }
 
-static void set_max_packet_size(USBHostDevice *s, int pid, int ep,
-                                uint8_t *descriptor)
-{
-    int raw = descriptor[4] + (descriptor[5] << 8);
-    int size, microframes;
-
-    size = raw & 0x7ff;
-    switch ((raw >> 11) & 3) {
-    case 1:  microframes = 2; break;
-    case 2:  microframes = 3; break;
-    default: microframes = 1; break;
-    }
-    get_endp(s, pid, ep)->max_packet_size = size * microframes;
-}
-
-static int get_max_packet_size(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->max_packet_size;
-}
-
 /*
  * Async URB state.
  * We always allocate iso packet descriptors even for bulk transfers
@@ -674,7 +653,7 @@ static void usb_host_handle_destroy(USBDevice *dev)
 static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
 {
     AsyncURB *aurb;
-    int i, j, len = get_max_packet_size(s, pid, ep);
+    int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
 
     aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
     for (i = 0; i < s->iso_urb_count; i++) {
@@ -754,7 +733,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
     int i, j, ret, max_packet_size, offset, len = 0;
     uint8_t *buf;
 
-    max_packet_size = get_max_packet_size(s, p->pid, p->devep);
+    max_packet_size = usb_ep_get_max_packet_size(&s->dev, p->pid, p->devep);
     if (max_packet_size == 0)
         return USB_RET_NAK;
 
@@ -1133,6 +1112,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
 {
     uint8_t *descriptors;
     uint8_t devep, type, alt_interface;
+    uint16_t raw;
     int interface, length, i, ep, pid;
     struct endp_data *epd;
 
@@ -1200,9 +1180,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
             }
 
             type = descriptors[i + 3] & 0x3;
-            if (type == USB_ENDPOINT_XFER_ISOC) {
-                set_max_packet_size(s, pid, ep, descriptors + i);
-            };
+            raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
+            usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
             assert(usb_ep_get_type(&s->dev, pid, ep) ==
                    USB_ENDPOINT_XFER_INVALID);
             usb_ep_set_type(&s->dev, pid, ep, type);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 12/17] usb: link packets to endpoints not devices
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (10 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 11/17] usb: add max_packet_size to USBEndpoint Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 13/17] usb-redir: Clear iso / irq error when stopping the stream Gerd Hoffmann
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Add USBEndpoint for the control endpoint to USBDevices.  Link async
packets to the USBEndpoint instead of the USBDevice.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-ehci.c |    3 ++-
 hw/usb-musb.c |    3 ++-
 hw/usb-ohci.c |    4 +++-
 hw/usb-uhci.c |    3 ++-
 hw/usb.c      |   12 ++++++++++--
 hw/usb.h      |    4 +++-
 6 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 7c926c0..a305661 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -715,7 +715,8 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev)
     EHCIQueue *q, *tmp;
 
     QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) {
-        if (q->packet.owner != dev) {
+        if (q->packet.owner == NULL ||
+            q->packet.owner->dev != dev) {
             continue;
         }
         ehci_free_queue(q);
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
index 01e2e7c..4f528d2 100644
--- a/hw/usb-musb.c
+++ b/hw/usb-musb.c
@@ -812,7 +812,8 @@ static void musb_async_cancel_device(MUSBState *s, USBDevice *dev)
 
     for (ep = 0; ep < 16; ep++) {
         for (dir = 0; dir < 2; dir++) {
-            if (s->ep[ep].packey[dir].p.owner != dev) {
+            if (s->ep[ep].packey[dir].p.owner == NULL ||
+                s->ep[ep].packey[dir].p.owner->dev != dev) {
                 continue;
             }
             usb_cancel_packet(&s->ep[ep].packey[dir].p);
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 81488c4..69463d2 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -1707,7 +1707,9 @@ static void ohci_mem_write(void *opaque,
 
 static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
 {
-    if (ohci->async_td && ohci->usb_packet.owner == dev) {
+    if (ohci->async_td &&
+        ohci->usb_packet.owner != NULL &&
+        ohci->usb_packet.owner->dev == dev) {
         usb_cancel_packet(&ohci->usb_packet);
         ohci->async_td = 0;
     }
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index f8912e2..25d4e8c 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -245,7 +245,8 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
     UHCIAsync *curr, *n;
 
     QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
-        if (curr->packet.owner != dev) {
+        if (curr->packet.owner == NULL ||
+            curr->packet.owner->dev != dev) {
             continue;
         }
         uhci_async_unlink(s, curr);
diff --git a/hw/usb.c b/hw/usb.c
index 0f163b4..860538a 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -329,7 +329,7 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
     ret = dev->info->handle_packet(dev, p);
     if (ret == USB_RET_ASYNC) {
         if (p->owner == NULL) {
-            p->owner = dev;
+            p->owner = usb_ep_get(dev, p->pid, p->devep);
         } else {
             /* We'll end up here when usb_handle_packet is called
              * recursively due to a hub being in the chain.  Nothing
@@ -357,7 +357,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
 void usb_cancel_packet(USBPacket * p)
 {
     assert(p->owner != NULL);
-    p->owner->info->cancel_packet(p->owner, p);
+    p->owner->dev->info->cancel_packet(p->owner->dev, p);
     p->owner = NULL;
 }
 
@@ -419,11 +419,16 @@ void usb_ep_init(USBDevice *dev)
 {
     int ep;
 
+    dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
+    dev->ep_ctl.ifnum = 0;
+    dev->ep_ctl.dev = dev;
     for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
         dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
         dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
         dev->ep_in[ep].ifnum = 0;
         dev->ep_out[ep].ifnum = 0;
+        dev->ep_in[ep].dev = dev;
+        dev->ep_out[ep].dev = dev;
     }
 }
 
@@ -472,6 +477,9 @@ void usb_ep_dump(USBDevice *dev)
 struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
 {
     struct USBEndpoint *eps = pid == USB_TOKEN_IN ? dev->ep_in : dev->ep_out;
+    if (ep == 0) {
+        return &dev->ep_ctl;
+    }
     assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
     assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
     return eps + ep - 1;
diff --git a/hw/usb.h b/hw/usb.h
index 5ea984c..0776463 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -177,6 +177,7 @@ struct USBEndpoint {
     uint8_t type;
     uint8_t ifnum;
     int max_packet_size;
+    USBDevice *dev;
 };
 
 /* definition of a USB device */
@@ -204,6 +205,7 @@ struct USBDevice {
     int32_t setup_len;
     int32_t setup_index;
 
+    USBEndpoint ep_ctl;
     USBEndpoint ep_in[USB_MAX_ENDPOINTS];
     USBEndpoint ep_out[USB_MAX_ENDPOINTS];
 
@@ -317,7 +319,7 @@ struct USBPacket {
     QEMUIOVector iov;
     int result; /* transfer length or USB_RET_* status code */
     /* Internal use by the USB layer.  */
-    USBDevice *owner;
+    USBEndpoint *owner;
 };
 
 void usb_packet_init(USBPacket *p);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 13/17] usb-redir: Clear iso / irq error when stopping the stream
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (11 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 12/17] usb: link packets to endpoints not devices Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 14/17] usb-redir: Dynamically adjust iso buffering size based on ep interval Gerd Hoffmann
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede, Gerd Hoffmann

From: Hans de Goede <hdegoede@redhat.com>

And ignore status messages from the client which arrive after stream
stop (the stream stop send to the client and an error status reported by
the client my cross each other due to network latency).

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-redir.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/usb-redir.c b/usb-redir.c
index 2b53cf3..81a35c6 100644
--- a/usb-redir.c
+++ b/usb-redir.c
@@ -410,6 +410,7 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
         DPRINTF("iso stream stopped ep %02X\n", ep);
         dev->endpoint[EP2I(ep)].iso_started = 0;
     }
+    dev->endpoint[EP2I(ep)].iso_error = 0;
     usbredir_free_bufpq(dev, ep);
 }
 
@@ -522,6 +523,7 @@ static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
         DPRINTF("interrupt recv stopped ep %02X\n", ep);
         dev->endpoint[EP2I(ep)].interrupt_started = 0;
     }
+    dev->endpoint[EP2I(ep)].interrupt_error = 0;
     usbredir_free_bufpq(dev, ep);
 }
 
@@ -1029,7 +1031,7 @@ static void usbredir_iso_stream_status(void *priv, uint32_t id,
     DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
             ep, id);
 
-    if (!dev->dev.attached) {
+    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
         return;
     }
 
@@ -1050,7 +1052,7 @@ static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
     DPRINTF("interrupt recv status %d ep %02X id %u\n",
             interrupt_receiving_status->status, ep, id);
 
-    if (!dev->dev.attached) {
+    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
         return;
     }
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 14/17] usb-redir: Dynamically adjust iso buffering size based on ep interval
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (12 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 13/17] usb-redir: Clear iso / irq error when stopping the stream Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 15/17] usb-redir: Pre-fill our isoc input buffer before sending pkts to the host Gerd Hoffmann
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede, Gerd Hoffmann

From: Hans de Goede <hdegoede@redhat.com>

Note the bufpq_target_size id stored in the endpoint info struct,
even though it only used once. This is done because it will be
referenced from other code in a follow up patch.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-redir.c |   53 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/usb-redir.c b/usb-redir.c
index 81a35c6..99a12d5 100644
--- a/usb-redir.c
+++ b/usb-redir.c
@@ -61,6 +61,7 @@ struct endp_data {
     uint8_t interrupt_started;
     uint8_t interrupt_error;
     QTAILQ_HEAD(, buf_packet) bufpq;
+    int bufpq_target_size;
 };
 
 struct USBRedirDevice {
@@ -332,15 +333,42 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
                                      uint8_t ep)
 {
     int status, len;
-
     if (!dev->endpoint[EP2I(ep)].iso_started &&
             !dev->endpoint[EP2I(ep)].iso_error) {
         struct usb_redir_start_iso_stream_header start_iso = {
             .endpoint = ep,
-            /* TODO maybe do something with these depending on ep interval? */
-            .pkts_per_urb = 32,
-            .no_urbs = 3,
         };
+        int pkts_per_sec;
+
+        if (dev->dev.speed == USB_SPEED_HIGH) {
+            pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval;
+        } else {
+            pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval;
+        }
+        /* Testing has shown that we need circa 60 ms buffer */
+        dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000;
+
+        /* Aim for approx 100 interrupts / second on the client to
+           balance latency and interrupt load */
+        start_iso.pkts_per_urb = pkts_per_sec / 100;
+        if (start_iso.pkts_per_urb < 1) {
+            start_iso.pkts_per_urb = 1;
+        } else if (start_iso.pkts_per_urb > 32) {
+            start_iso.pkts_per_urb = 32;
+        }
+
+        start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size +
+                             start_iso.pkts_per_urb - 1) /
+                            start_iso.pkts_per_urb;
+        /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest
+           as overflow buffer. Also see the usbredir protocol documentation */
+        if (!(ep & USB_DIR_IN)) {
+            start_iso.no_urbs *= 2;
+        }
+        if (start_iso.no_urbs > 16) {
+            start_iso.no_urbs = 16;
+        }
+
         /* No id, we look at the ep when receiving a status back */
         usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
         usbredirparser_do_write(dev->parser);
@@ -961,9 +989,24 @@ static void usbredir_ep_info(void *priv,
         dev->endpoint[i].type = ep_info->type[i];
         dev->endpoint[i].interval = ep_info->interval[i];
         dev->endpoint[i].interface = ep_info->interface[i];
-        if (dev->endpoint[i].type != usb_redir_type_invalid) {
+        switch (dev->endpoint[i].type) {
+        case usb_redir_type_invalid:
+            break;
+        case usb_redir_type_iso:
+        case usb_redir_type_interrupt:
+            if (dev->endpoint[i].interval == 0) {
+                ERROR("Received 0 interval for isoc or irq endpoint\n");
+                usbredir_device_disconnect(dev);
+            }
+            /* Fall through */
+        case usb_redir_type_control:
+        case usb_redir_type_bulk:
             DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
                     dev->endpoint[i].type, dev->endpoint[i].interface);
+            break;
+        default:
+            ERROR("Received invalid endpoint type\n");
+            usbredir_device_disconnect(dev);
         }
     }
 }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 15/17] usb-redir: Pre-fill our isoc input buffer before sending pkts to the host
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (13 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 14/17] usb-redir: Dynamically adjust iso buffering size based on ep interval Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 16/17] usb-redir: Try to keep our buffer size near the target size Gerd Hoffmann
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede, Gerd Hoffmann

From: Hans de Goede <hdegoede@redhat.com>

This is something which should have been done from the first version of
usb-redir, but wasn't.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-redir.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/usb-redir.c b/usb-redir.c
index 99a12d5..cdd929a 100644
--- a/usb-redir.c
+++ b/usb-redir.c
@@ -60,7 +60,9 @@ struct endp_data {
     uint8_t iso_error; /* For reporting iso errors to the HC */
     uint8_t interrupt_started;
     uint8_t interrupt_error;
+    uint8_t bufpq_prefilled;
     QTAILQ_HEAD(, buf_packet) bufpq;
+    int bufpq_size;
     int bufpq_target_size;
 };
 
@@ -296,6 +298,7 @@ static struct buf_packet *bufp_alloc(USBRedirDevice *dev,
     bufp->len    = len;
     bufp->status = status;
     QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+    dev->endpoint[EP2I(ep)].bufpq_size++;
     return bufp;
 }
 
@@ -303,6 +306,7 @@ static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
     uint8_t ep)
 {
     QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+    dev->endpoint[EP2I(ep)].bufpq_size--;
     free(bufp->data);
     g_free(bufp);
 }
@@ -374,14 +378,26 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
         usbredirparser_do_write(dev->parser);
         DPRINTF("iso stream started ep %02X\n", ep);
         dev->endpoint[EP2I(ep)].iso_started = 1;
+        dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
     }
 
     if (ep & USB_DIR_IN) {
         struct buf_packet *isop;
 
+        if (dev->endpoint[EP2I(ep)].iso_started &&
+                !dev->endpoint[EP2I(ep)].bufpq_prefilled) {
+            if (dev->endpoint[EP2I(ep)].bufpq_size <
+                    dev->endpoint[EP2I(ep)].bufpq_target_size) {
+                return usbredir_handle_status(dev, 0, 0);
+            }
+            dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
+        }
+
         isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
         if (isop == NULL) {
             DPRINTF2("iso-token-in ep %02X, no isop\n", ep);
+            /* Re-fill the buffer */
+            dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
             /* Check iso_error for stream errors, otherwise its an underrun */
             status = dev->endpoint[EP2I(ep)].iso_error;
             dev->endpoint[EP2I(ep)].iso_error = 0;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 16/17] usb-redir: Try to keep our buffer size near the target size
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (14 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 15/17] usb-redir: Pre-fill our isoc input buffer before sending pkts to the host Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 17/17] usb-redir: Improve some debugging messages Gerd Hoffmann
  2012-01-13 15:19 ` [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Anthony Liguori
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede, Gerd Hoffmann

From: Hans de Goede <hdegoede@redhat.com>

Before this patch we would allow the (iso) buffer to grow unlimited
(and it would under certain circumstances) leading to way too high
latencies for iso data streams.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-redir.c |   30 +++++++++++++++++++++++++++---
 1 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/usb-redir.c b/usb-redir.c
index cdd929a..147e237 100644
--- a/usb-redir.c
+++ b/usb-redir.c
@@ -61,6 +61,7 @@ struct endp_data {
     uint8_t interrupt_started;
     uint8_t interrupt_error;
     uint8_t bufpq_prefilled;
+    uint8_t bufpq_dropping_packets;
     QTAILQ_HEAD(, buf_packet) bufpq;
     int bufpq_size;
     int bufpq_target_size;
@@ -290,16 +291,34 @@ static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
     }
 }
 
-static struct buf_packet *bufp_alloc(USBRedirDevice *dev,
+static void bufp_alloc(USBRedirDevice *dev,
     uint8_t *data, int len, int status, uint8_t ep)
 {
-    struct buf_packet *bufp = g_malloc(sizeof(struct buf_packet));
+    struct buf_packet *bufp;
+
+    if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets &&
+        dev->endpoint[EP2I(ep)].bufpq_size >
+            2 * dev->endpoint[EP2I(ep)].bufpq_target_size) {
+        DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1;
+    }
+    /* Since we're interupting the stream anyways, drop enough packets to get
+       back to our target buffer size */
+    if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) {
+        if (dev->endpoint[EP2I(ep)].bufpq_size >
+                dev->endpoint[EP2I(ep)].bufpq_target_size) {
+            free(data);
+            return;
+        }
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
+    }
+
+    bufp = g_malloc(sizeof(struct buf_packet));
     bufp->data   = data;
     bufp->len    = len;
     bufp->status = status;
     QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
     dev->endpoint[EP2I(ep)].bufpq_size++;
-    return bufp;
 }
 
 static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
@@ -379,6 +398,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
         DPRINTF("iso stream started ep %02X\n", ep);
         dev->endpoint[EP2I(ep)].iso_started = 1;
         dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
     }
 
     if (ep & USB_DIR_IN) {
@@ -505,6 +525,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
             usbredirparser_do_write(dev->parser);
             DPRINTF("interrupt recv started ep %02X\n", ep);
             dev->endpoint[EP2I(ep)].interrupt_started = 1;
+            /* We don't really want to drop interrupt packets ever, but
+               having some upper limit to how much we buffer is good. */
+            dev->endpoint[EP2I(ep)].bufpq_target_size = 1000;
+            dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
         }
 
         intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [Qemu-devel] [PATCH 17/17] usb-redir: Improve some debugging messages
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (15 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 16/17] usb-redir: Try to keep our buffer size near the target size Gerd Hoffmann
@ 2012-01-13 10:18 ` Gerd Hoffmann
  2012-01-13 15:19 ` [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Anthony Liguori
  17 siblings, 0 replies; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-13 10:18 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede, Gerd Hoffmann

From: Hans de Goede <hdegoede@redhat.com>

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-redir.c |   13 ++++++++-----
 1 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/usb-redir.c b/usb-redir.c
index 147e237..79d29ec 100644
--- a/usb-redir.c
+++ b/usb-redir.c
@@ -395,7 +395,8 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
         /* No id, we look at the ep when receiving a status back */
         usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
         usbredirparser_do_write(dev->parser);
-        DPRINTF("iso stream started ep %02X\n", ep);
+        DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n",
+                pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep);
         dev->endpoint[EP2I(ep)].iso_started = 1;
         dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
         dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
@@ -415,7 +416,8 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
 
         isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
         if (isop == NULL) {
-            DPRINTF2("iso-token-in ep %02X, no isop\n", ep);
+            DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n",
+                    ep, dev->endpoint[EP2I(ep)].iso_error);
             /* Re-fill the buffer */
             dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
             /* Check iso_error for stream errors, otherwise its an underrun */
@@ -423,8 +425,8 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
             dev->endpoint[EP2I(ep)].iso_error = 0;
             return usbredir_handle_status(dev, status, 0);
         }
-        DPRINTF2("iso-token-in ep %02X status %d len %d\n", ep, isop->status,
-                 isop->len);
+        DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
+                 isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
 
         status = isop->status;
         if (status != usb_redir_success) {
@@ -434,7 +436,8 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
 
         len = isop->len;
         if (len > p->iov.size) {
-            ERROR("received iso data is larger then packet ep %02X\n", ep);
+            ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
+                  ep, len, (int)p->iov.size);
             bufp_free(dev, isop, ep);
             return USB_RET_NAK;
         }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir
  2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
                   ` (16 preceding siblings ...)
  2012-01-13 10:18 ` [Qemu-devel] [PATCH 17/17] usb-redir: Improve some debugging messages Gerd Hoffmann
@ 2012-01-13 15:19 ` Anthony Liguori
  2012-01-17  9:06   ` Gerd Hoffmann
  17 siblings, 1 reply; 21+ messages in thread
From: Anthony Liguori @ 2012-01-13 15:19 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On 01/13/2012 04:18 AM, Gerd Hoffmann wrote:
>    Hi,
>
> Here comes the usb patch queue.  It features the patches posted a week
> ago for review with some minor tweaks according to the review comments:
> The comment in usb-audio was fixed and xhci got a codestyle cleanup.  No
> actual code changes.  I've also included the usb-redir patches posted by
> Hans earlier this week.
>

cc1: warnings being treated as errors
/home/anthony/git/qemu/hw/usb-xhci.c: In function ‘xhci_process_commands’:
/home/anthony/git/qemu/hw/usb-xhci.c:2143:9: error: case value ‘48’ not in 
enumerated type ‘TRBType’
/home/anthony/git/qemu/hw/usb-xhci.c:2147:9: error: case value ‘49’ not in 
enumerated type ‘TRBType’
/home/anthony/git/qemu/hw/usb-xhci.c:2152:9: error: case value ‘50’ not in 
enumerated type ‘TRBType’
make[1]: *** [usb-xhci.o] Error 1

Regards,

Anthony Liguori


> please pull,
>    Gerd
>
> The following changes since commit 515aa3c57986b3e26558d72ecaeb7545ecd30510:
>
>    check-qstring: remove check.h include (2012-01-12 11:33:22 -0600)
>
> are available in the git repository at:
>    git://git.kraxel.org/qemu usb.36
>
> Gerd Hoffmann (10):
>        usb-host: rip out legacy procfs support
>        usb: track configuration and interface count in USBDevice.
>        usb: track altsetting in USBDevice
>        usb-desc: audio endpoint support
>        usb: add USBEndpoint
>        usb: add ifnum to USBEndpoint
>        usb-desc: USBEndpoint support
>        usb/debug: add usb_ep_dump
>        usb: add max_packet_size to USBEndpoint
>        usb: link packets to endpoints not devices
>
> H. Peter Anvin (1):
>        usb: add audio device model
>
> Hans de Goede (5):
>        usb-redir: Clear iso / irq error when stopping the stream
>        usb-redir: Dynamically adjust iso buffering size based on ep interval
>        usb-redir: Pre-fill our isoc input buffer before sending pkts to the host
>        usb-redir: Try to keep our buffer size near the target size
>        usb-redir: Improve some debugging messages
>
> Hector Martin (1):
>        xhci: Initial xHCI implementation
>
>   Makefile.objs           |    3 +-
>   default-configs/pci.mak |    1 +
>   hw/pci_ids.h            |    3 +
>   hw/usb-audio.c          |  704 ++++++++++++
>   hw/usb-bt.c             |   22 -
>   hw/usb-bus.c            |    1 +
>   hw/usb-ccid.c           |    8 -
>   hw/usb-desc.c           |  143 +++-
>   hw/usb-desc.h           |    5 +
>   hw/usb-ehci.c           |    3 +-
>   hw/usb-hid.c            |    7 -
>   hw/usb-hub.c            |    7 -
>   hw/usb-msd.c            |   10 -
>   hw/usb-musb.c           |    3 +-
>   hw/usb-net.c            |   14 -
>   hw/usb-ohci.c           |    4 +-
>   hw/usb-serial.c         |    7 -
>   hw/usb-uhci.c           |    3 +-
>   hw/usb-wacom.c          |    7 -
>   hw/usb-xhci.c           | 2748 +++++++++++++++++++++++++++++++++++++++++++++++
>   hw/usb.c                |  125 +++-
>   hw/usb.h                |   44 +-
>   trace-events            |    1 +
>   usb-linux.c             |  452 ++------
>   usb-redir.c             |  118 ++-
>   25 files changed, 3972 insertions(+), 471 deletions(-)
>   create mode 100644 hw/usb-audio.c
>   create mode 100644 hw/usb-xhci.c
>
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir
  2012-01-13 15:19 ` [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Anthony Liguori
@ 2012-01-17  9:06   ` Gerd Hoffmann
  2012-01-19 18:48     ` Anthony Liguori
  0 siblings, 1 reply; 21+ messages in thread
From: Gerd Hoffmann @ 2012-01-17  9:06 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1057 bytes --]

On 01/13/12 16:19, Anthony Liguori wrote:
> On 01/13/2012 04:18 AM, Gerd Hoffmann wrote:
>>    Hi,
>>
>> Here comes the usb patch queue.  It features the patches posted a week
>> ago for review with some minor tweaks according to the review comments:
>> The comment in usb-audio was fixed and xhci got a codestyle cleanup.  No
>> actual code changes.  I've also included the usb-redir patches posted by
>> Hans earlier this week.
>>
> 
> cc1: warnings being treated as errors
> /home/anthony/git/qemu/hw/usb-xhci.c: In function ‘xhci_process_commands’:
> /home/anthony/git/qemu/hw/usb-xhci.c:2143:9: error: case value ‘48’ not
> in enumerated type ‘TRBType’
> /home/anthony/git/qemu/hw/usb-xhci.c:2147:9: error: case value ‘49’ not
> in enumerated type ‘TRBType’
> /home/anthony/git/qemu/hw/usb-xhci.c:2152:9: error: case value ‘50’ not
> in enumerated type ‘TRBType’
> make[1]: *** [usb-xhci.o] Error 1

Fixed & pushed to git://git.kraxel.org/qemu usb.37

diff between usb.36 and usb.37 attached for reference.

cheers,
  Gerd


[-- Attachment #2: xhci-warnfix.diff --]
[-- Type: text/plain, Size: 1331 bytes --]

diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c
index 0c5007b..28fe9de 100644
--- a/hw/usb-xhci.c
+++ b/hw/usb-xhci.c
@@ -177,7 +177,11 @@ typedef enum TRBType {
     ER_DOORBELL,
     ER_HOST_CONTROLLER,
     ER_DEVICE_NOTIFICATION,
-    ER_MFINDEX_WRAP
+    ER_MFINDEX_WRAP,
+    /* vendor specific bits */
+    CR_VENDOR_VIA_CHALLENGE_RESPONSE = 48,
+    CR_VENDOR_NEC_FIRMWARE_REVISION  = 49,
+    CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50,
 } TRBType;
 
 #define CR_LINK TR_LINK
@@ -2140,18 +2144,15 @@ static void xhci_process_commands(XHCIState *xhci)
         case CR_GET_PORT_BANDWIDTH:
             event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter);
             break;
-        case 48:
-            /* VIA challenge response */
+        case CR_VENDOR_VIA_CHALLENGE_RESPONSE:
             xhci_via_challenge(trb.parameter);
             break;
-        case 49:
-            /* NEC get FW revision */
+        case CR_VENDOR_NEC_FIRMWARE_REVISION:
             event.type = 48; /* NEC reply */
             event.length = 0x3025;
             break;
-        case 50:
+        case CR_VENDOR_NEC_CHALLENGE_RESPONSE:
         {
-            /* NEC challenge response */
             uint32_t chi = trb.parameter >> 32;
             uint32_t clo = trb.parameter;
             uint32_t val = xhci_nec_challenge(chi, clo);

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir
  2012-01-17  9:06   ` Gerd Hoffmann
@ 2012-01-19 18:48     ` Anthony Liguori
  0 siblings, 0 replies; 21+ messages in thread
From: Anthony Liguori @ 2012-01-19 18:48 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On 01/17/2012 03:06 AM, Gerd Hoffmann wrote:
> On 01/13/12 16:19, Anthony Liguori wrote:
>> On 01/13/2012 04:18 AM, Gerd Hoffmann wrote:
>>>     Hi,
>>>
>>> Here comes the usb patch queue.  It features the patches posted a week
>>> ago for review with some minor tweaks according to the review comments:
>>> The comment in usb-audio was fixed and xhci got a codestyle cleanup.  No
>>> actual code changes.  I've also included the usb-redir patches posted by
>>> Hans earlier this week.
>>>
>>
>> cc1: warnings being treated as errors
>> /home/anthony/git/qemu/hw/usb-xhci.c: In function ‘xhci_process_commands’:
>> /home/anthony/git/qemu/hw/usb-xhci.c:2143:9: error: case value ‘48’ not
>> in enumerated type ‘TRBType’
>> /home/anthony/git/qemu/hw/usb-xhci.c:2147:9: error: case value ‘49’ not
>> in enumerated type ‘TRBType’
>> /home/anthony/git/qemu/hw/usb-xhci.c:2152:9: error: case value ‘50’ not
>> in enumerated type ‘TRBType’
>> make[1]: *** [usb-xhci.o] Error 1
>
> Fixed&  pushed to git://git.kraxel.org/qemu usb.37
>
> diff between usb.36 and usb.37 attached for reference.

Pulled.  Thanks.

Regards,

Anthony Liguori


> cheers,
>    Gerd
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2012-01-19 18:48 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-13 10:18 [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 01/17] usb-host: rip out legacy procfs support Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 02/17] usb: track configuration and interface count in USBDevice Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 03/17] usb: track altsetting " Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 04/17] usb-desc: audio endpoint support Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 05/17] usb: add audio device model Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 06/17] xhci: Initial xHCI implementation Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 07/17] usb: add USBEndpoint Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 08/17] usb: add ifnum to USBEndpoint Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 09/17] usb-desc: USBEndpoint support Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 10/17] usb/debug: add usb_ep_dump Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 11/17] usb: add max_packet_size to USBEndpoint Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 12/17] usb: link packets to endpoints not devices Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 13/17] usb-redir: Clear iso / irq error when stopping the stream Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 14/17] usb-redir: Dynamically adjust iso buffering size based on ep interval Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 15/17] usb-redir: Pre-fill our isoc input buffer before sending pkts to the host Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 16/17] usb-redir: Try to keep our buffer size near the target size Gerd Hoffmann
2012-01-13 10:18 ` [Qemu-devel] [PATCH 17/17] usb-redir: Improve some debugging messages Gerd Hoffmann
2012-01-13 15:19 ` [Qemu-devel] [PULL 00/17] usb patch queue: audio, xhci, usbredir Anthony Liguori
2012-01-17  9:06   ` Gerd Hoffmann
2012-01-19 18:48     ` Anthony Liguori

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.