All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support.
@ 2011-05-23  9:43 Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 01/18] usb: Add Interface Association Descriptor descriptor type Gerd Hoffmann
                   ` (18 more replies)
  0 siblings, 19 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

Here is the usb patch queue, with EHCI support being the outstanding
new feature.  Most patches are unmodified.  Patch #5 got a better commit
message.  The EHCI patch now lists all contributes in the commit message
too (they where listed in the source code only before), I hope everybody
is happy with that now.

please pull,
  Gerd

The following changes since commit dcfd14b3741983c466ad92fa2ae91eeafce3e5d5:

  Delete unused tb_invalidate_page_range (2011-05-22 10:47:28 +0000)

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

Brad Hards (4):
      usb: Add Interface Association Descriptor descriptor type
      usb: update config descriptors to identify number of interfaces
      usb: remove fallback to bNumInterfaces if no .nif
      usb: add support for "grouped" interfaces and the Interface Association Descriptor

Gerd Hoffmann (11):
      usb-linux: fix device path aka physical port handling
      usb-linux: add hostport property
      usb-linux: track aurbs in list
      usb-linux: walk async urb list in cancel
      usb-linux: split large xfers
      usb-linux: fix max_packet_size for highspeed.
      usb-storage: don't call usb_packet_complete twice
      usb: add usb_handle_packet
      usb: keep track of packet owner.
      usb: move cancel callback to USBDeviceInfo
      usb: add ehci adapter

Hans de Goede (2):
      usb: Pass the packet to the device's handle_control callback
      usb-linux: use usb_generic_handle_packet()

Jan Vesely (1):
      Bug #757654: UHCI fails to signal stall response patch

 Makefile.objs           |    1 +
 default-configs/pci.mak |    1 +
 docs/usb2.txt           |   38 +
 hw/bt-hid.c             |    6 +-
 hw/pci_ids.h            |    1 +
 hw/usb-bt.c             |    6 +-
 hw/usb-ccid.c           |    4 +-
 hw/usb-desc.c           |   56 ++-
 hw/usb-desc.h           |   24 +-
 hw/usb-ehci.c           | 2038 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb-hid.c            |    9 +-
 hw/usb-hub.c            |    9 +-
 hw/usb-msd.c            |   18 +-
 hw/usb-musb.c           |    2 +-
 hw/usb-net.c            |    6 +-
 hw/usb-ohci.c           |    4 +-
 hw/usb-serial.c         |    7 +-
 hw/usb-uhci.c           |    6 +-
 hw/usb-wacom.c          |    7 +-
 hw/usb.c                |  101 +++-
 hw/usb.h                |   40 +-
 usb-bsd.c               |    1 +
 usb-linux.c             |  444 ++++-------
 23 files changed, 2442 insertions(+), 387 deletions(-)
 create mode 100644 docs/usb2.txt
 create mode 100644 hw/usb-ehci.c

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

* [Qemu-devel] [PATCH 01/18] usb: Add Interface Association Descriptor descriptor type
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 02/18] usb: update config descriptors to identify number of interfaces Gerd Hoffmann
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Brad Hards

From: Brad Hards <bradh@frogmouth.net>

Signed-off-by: Brad Hards <bradh@frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/hw/usb.h b/hw/usb.h
index 7e46141..ca06bf8 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -124,6 +124,7 @@
 #define USB_DT_ENDPOINT			0x05
 #define USB_DT_DEVICE_QUALIFIER         0x06
 #define USB_DT_OTHER_SPEED_CONFIG       0x07
+#define USB_DT_INTERFACE_ASSOC          0x0B
 
 #define USB_ENDPOINT_XFER_CONTROL	0
 #define USB_ENDPOINT_XFER_ISOC		1
-- 
1.7.1

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

* [Qemu-devel] [PATCH 02/18] usb: update config descriptors to identify number of interfaces
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 01/18] usb: Add Interface Association Descriptor descriptor type Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 03/18] usb: remove fallback to bNumInterfaces if no .nif Gerd Hoffmann
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Brad Hards

From: Brad Hards <bradh@frogmouth.net>

Previously we relied on the .bNumInterfaces, but that won't always be
accurate after the introduction of grouped interfaces.

Signed-off-by: Brad Hards <bradh@frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-hid.c    |    3 +++
 hw/usb-hub.c    |    1 +
 hw/usb-msd.c    |    2 ++
 hw/usb-serial.c |    1 +
 hw/usb-wacom.c  |    1 +
 5 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 89c293c..bf59a7d 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -211,6 +211,7 @@ static const USBDescDevice desc_device_mouse = {
             .iConfiguration        = STR_CONFIG_MOUSE,
             .bmAttributes          = 0xa0,
             .bMaxPower             = 50,
+            .nif = 1,
             .ifs = &desc_iface_mouse,
         },
     },
@@ -227,6 +228,7 @@ static const USBDescDevice desc_device_tablet = {
             .iConfiguration        = STR_CONFIG_TABLET,
             .bmAttributes          = 0xa0,
             .bMaxPower             = 50,
+            .nif = 1,
             .ifs = &desc_iface_tablet,
         },
     },
@@ -243,6 +245,7 @@ static const USBDescDevice desc_device_keyboard = {
             .iConfiguration        = STR_CONFIG_KEYBOARD,
             .bmAttributes          = 0xa0,
             .bMaxPower             = 50,
+            .nif = 1,
             .ifs = &desc_iface_keyboard,
         },
     },
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index e0588f8..7c1f159 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -119,6 +119,7 @@ static const USBDescDevice desc_device_hub = {
             .bNumInterfaces        = 1,
             .bConfigurationValue   = 1,
             .bmAttributes          = 0xe0,
+            .nif = 1,
             .ifs = &desc_iface_hub,
         },
     },
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index bd1c3a4..040ea7a 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -119,6 +119,7 @@ static const USBDescDevice desc_device_full = {
             .bConfigurationValue   = 1,
             .iConfiguration        = STR_CONFIG_FULL,
             .bmAttributes          = 0xc0,
+            .nif = 1,
             .ifs = &desc_iface_full,
         },
     },
@@ -153,6 +154,7 @@ static const USBDescDevice desc_device_high = {
             .bConfigurationValue   = 1,
             .iConfiguration        = STR_CONFIG_HIGH,
             .bmAttributes          = 0xc0,
+            .nif = 1,
             .ifs = &desc_iface_high,
         },
     },
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 6763d52..48ea0d8 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -146,6 +146,7 @@ static const USBDescDevice desc_device = {
             .bConfigurationValue   = 1,
             .bmAttributes          = 0x80,
             .bMaxPower             = 50,
+            .nif = 1,
             .ifs = &desc_iface0,
         },
     },
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index 16be7a2..57041a1 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -108,6 +108,7 @@ static const USBDescDevice desc_device_wacom = {
             .bConfigurationValue   = 1,
             .bmAttributes          = 0x80,
             .bMaxPower             = 40,
+            .nif = 1,
             .ifs = &desc_iface_wacom,
         },
     },
-- 
1.7.1

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

* [Qemu-devel] [PATCH 03/18] usb: remove fallback to bNumInterfaces if no .nif
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 01/18] usb: Add Interface Association Descriptor descriptor type Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 02/18] usb: update config descriptors to identify number of interfaces Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 04/18] usb: add support for "grouped" interfaces and the Interface Association Descriptor Gerd Hoffmann
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Brad Hards

From: Brad Hards <bradh@frogmouth.net>

All callers have been updated.

Signed-off-by: Brad Hards <bradh@frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-desc.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 62591f2..a784155 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -76,7 +76,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
 {
     uint8_t  bLength = 0x09;
     uint16_t wTotalLength = 0;
-    int i, rc, count;
+    int i, rc;
 
     if (len < bLength) {
         return -1;
@@ -91,8 +91,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
     dest[0x08] = conf->bMaxPower;
     wTotalLength += bLength;
 
-    count = conf->nif ? conf->nif : conf->bNumInterfaces;
-    for (i = 0; i < count; i++) {
+    for (i = 0; i < conf->nif; i++) {
         rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
         if (rc < 0) {
             return rc;
-- 
1.7.1

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

* [Qemu-devel] [PATCH 04/18] usb: add support for "grouped" interfaces and the Interface Association Descriptor
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 03/18] usb: remove fallback to bNumInterfaces if no .nif Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 05/18] Bug #757654: UHCI fails to signal stall response patch Gerd Hoffmann
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Brad Hards

From: Brad Hards <bradh@frogmouth.net>

This is used for some devices that have multiple interfaces that form a logic
device. An example is Video Class, which has a Control interface and a
Streaming interface. There can be additional interfaces on the same (physical)
devices (e.g. a microphone), and Interface Association Descriptor handles this
case.

Signed-off-by: Brad Hards <bradh@frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-desc.c |   47 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb-desc.h |   20 ++++++++++++++++++++
 hw/usb.h      |    1 +
 3 files changed, 68 insertions(+), 0 deletions(-)

diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index a784155..8367c45 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -91,6 +91,18 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
     dest[0x08] = conf->bMaxPower;
     wTotalLength += bLength;
 
+    /* handle grouped interfaces if any*/
+    for (i = 0; i < conf->nif_groups; i++) {
+        rc = usb_desc_iface_group(&(conf->if_groups[i]),
+                                  dest + wTotalLength,
+                                  len - wTotalLength);
+        if (rc < 0) {
+            return rc;
+        }
+        wTotalLength += rc;
+    }
+
+    /* handle normal (ungrouped / no IAD) interfaces if any */
     for (i = 0; i < conf->nif; i++) {
         rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
         if (rc < 0) {
@@ -104,6 +116,41 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
     return wTotalLength;
 }
 
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+                         size_t len)
+{
+    int pos = 0;
+    int i = 0;
+
+    /* handle interface association descriptor */
+    uint8_t bLength = 0x08;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_INTERFACE_ASSOC;
+    dest[0x02] = iad->bFirstInterface;
+    dest[0x03] = iad->bInterfaceCount;
+    dest[0x04] = iad->bFunctionClass;
+    dest[0x05] = iad->bFunctionSubClass;
+    dest[0x06] = iad->bFunctionProtocol;
+    dest[0x07] = iad->iFunction;
+    pos += bLength;
+
+    /* handle associated interfaces in this group */
+    for (i = 0; i < iad->nif; i++) {
+        int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
+        if (rc < 0) {
+            return rc;
+        }
+        pos += rc;
+    }
+
+    return pos;
+}
+
 int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
 {
     uint8_t bLength = 0x09;
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
index ac734ab..a612515 100644
--- a/hw/usb-desc.h
+++ b/hw/usb-desc.h
@@ -30,6 +30,24 @@ struct USBDescConfig {
     uint8_t                   bmAttributes;
     uint8_t                   bMaxPower;
 
+    /* grouped interfaces */
+    uint8_t                   nif_groups;
+    const USBDescIfaceAssoc   *if_groups;
+
+    /* "normal" interfaces */
+    uint8_t                   nif;
+    const USBDescIface        *ifs;
+};
+
+/* conceptually an Interface Association Descriptor, and releated interfaces */
+struct USBDescIfaceAssoc {
+    uint8_t                   bFirstInterface;
+    uint8_t                   bInterfaceCount;
+    uint8_t                   bFunctionClass;
+    uint8_t                   bFunctionSubClass;
+    uint8_t                   bFunctionProtocol;
+    uint8_t                   iFunction;
+
     uint8_t                   nif;
     const USBDescIface        *ifs;
 };
@@ -75,6 +93,8 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
 int usb_desc_device_qualifier(const USBDescDevice *dev,
                               uint8_t *dest, size_t len);
 int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+                         size_t len);
 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);
 int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
diff --git a/hw/usb.h b/hw/usb.h
index ca06bf8..e0961ac 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -141,6 +141,7 @@ typedef struct USBDesc USBDesc;
 typedef struct USBDescID USBDescID;
 typedef struct USBDescDevice USBDescDevice;
 typedef struct USBDescConfig USBDescConfig;
+typedef struct USBDescIfaceAssoc USBDescIfaceAssoc;
 typedef struct USBDescIface USBDescIface;
 typedef struct USBDescEndpoint USBDescEndpoint;
 typedef struct USBDescOther USBDescOther;
-- 
1.7.1

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

* [Qemu-devel] [PATCH 05/18] Bug #757654: UHCI fails to signal stall response patch
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (3 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 04/18] usb: add support for "grouped" interfaces and the Interface Association Descriptor Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 06/18] usb: Pass the packet to the device's handle_control callback Gerd Hoffmann
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Jan Vesely, Gerd Hoffmann

From: Jan Vesely <jano.vesely@gmail.com>

UHCI host controller status register indicates error and
an interrupt is triggered on BABBLE and STALL errors.

Signed-off-by: Jan Vesely <jano.vesely@gmail.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-uhci.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index a65e0b3..1e9c1e7 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -702,11 +702,15 @@ out:
     case USB_RET_STALL:
         td->ctrl |= TD_CTRL_STALL;
         td->ctrl &= ~TD_CTRL_ACTIVE;
+        s->status |= UHCI_STS_USBERR;
+        uhci_update_irq(s);
         return 1;
 
     case USB_RET_BABBLE:
         td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
         td->ctrl &= ~TD_CTRL_ACTIVE;
+        s->status |= UHCI_STS_USBERR;
+        uhci_update_irq(s);
         /* frame interrupted */
         return -1;
 
-- 
1.7.1

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

* [Qemu-devel] [PATCH 06/18] usb: Pass the packet to the device's handle_control callback
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (4 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 05/18] Bug #757654: UHCI fails to signal stall response patch Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 07/18] usb-linux: use usb_generic_handle_packet() Gerd Hoffmann
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede

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

This allows using the generic usb_generic_handle_packet function from
device code which does ASYNC control requests (such as the linux host
pass through code).

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 hw/bt-hid.c     |    6 +++---
 hw/usb-bt.c     |    6 +++---
 hw/usb-ccid.c   |    4 ++--
 hw/usb-desc.c   |    4 ++--
 hw/usb-desc.h   |    4 ++--
 hw/usb-hid.c    |    6 +++---
 hw/usb-hub.c    |    6 +++---
 hw/usb-msd.c    |    6 +++---
 hw/usb-net.c    |    6 +++---
 hw/usb-serial.c |    6 +++---
 hw/usb-wacom.c  |    6 +++---
 hw/usb.c        |   11 +++++++----
 hw/usb.h        |    2 +-
 usb-bsd.c       |    1 +
 14 files changed, 39 insertions(+), 35 deletions(-)

diff --git a/hw/bt-hid.c b/hw/bt-hid.c
index abdfd35..09120af 100644
--- a/hw/bt-hid.c
+++ b/hw/bt-hid.c
@@ -323,7 +323,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
             break;
         }
         s->proto = parameter;
-        s->usbdev->info->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0,
+        s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0,
                                         NULL);
         ret = BT_HS_SUCCESSFUL;
         break;
@@ -333,7 +333,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
             ret = BT_HS_ERR_INVALID_PARAMETER;
             break;
         }
-        s->usbdev->info->handle_control(s->usbdev, GET_IDLE, 0, 0, 1,
+        s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1,
                         s->control->sdu_out(s->control, 1));
         s->control->sdu_submit(s->control);
         break;
@@ -346,7 +346,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
 
         /* We don't need to know about the Idle Rate here really,
          * so just pass it on to the device.  */
-        ret = s->usbdev->info->handle_control(s->usbdev,
+        ret = s->usbdev->info->handle_control(s->usbdev, NULL,
                         SET_IDLE, data[1], 0, 0, NULL) ?
                 BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
         /* XXX: Does this generate a handshake? */
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
index 22e6845..baae487 100644
--- a/hw/usb-bt.c
+++ b/hw/usb-bt.c
@@ -372,13 +372,13 @@ static void usb_bt_handle_reset(USBDevice *dev)
     s->altsetting = 0;
 }
 
-static int usb_bt_handle_control(USBDevice *dev, int request, int value,
-                int index, int length, uint8_t *data)
+static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
     struct USBBtState *s = (struct USBBtState *) dev->opaque;
     int ret;
 
-    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         switch (request) {
         case DeviceRequest | USB_REQ_GET_CONFIGURATION:
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index 079b4a2..5b6878b 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -602,8 +602,8 @@ static void ccid_handle_reset(USBDevice *dev)
     ccid_reset(s);
 }
 
-static int ccid_handle_control(USBDevice *dev, int request, int value,
-                                  int index, int length, uint8_t *data)
+static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
+                               int value, int index, int length, uint8_t *data)
 {
     USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
     int ret = 0;
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 8367c45..e4a4680 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -390,8 +390,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
     return ret;
 }
 
-int usb_desc_handle_control(USBDevice *dev, int request, int value,
-                            int index, int length, uint8_t *data)
+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;
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
index a612515..9d7ed59 100644
--- a/hw/usb-desc.h
+++ b/hw/usb-desc.h
@@ -106,7 +106,7 @@ void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
 const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
 int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
 int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
-int usb_desc_handle_control(USBDevice *dev, int request, int value,
-                            int index, int length, uint8_t *data);
+int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
+        int request, int value, int index, int length, uint8_t *data);
 
 #endif /* QEMU_HW_USB_DESC_H */
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index bf59a7d..53b261c 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -727,13 +727,13 @@ static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime)
     s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000;
 }
 
-static int usb_hid_handle_control(USBDevice *dev, int request, int value,
-                                  int index, int length, uint8_t *data)
+static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
     USBHIDState *s = (USBHIDState *)dev;
     int ret;
 
-    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         return ret;
     }
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 7c1f159..477927b 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -285,13 +285,13 @@ static void usb_hub_handle_reset(USBDevice *dev)
     /* XXX: do it */
 }
 
-static int usb_hub_handle_control(USBDevice *dev, int request, int value,
-                                  int index, int length, uint8_t *data)
+static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
     USBHubState *s = (USBHubState *)dev;
     int ret;
 
-    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         return ret;
     }
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 040ea7a..c3a197a 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -272,13 +272,13 @@ static void usb_msd_handle_reset(USBDevice *dev)
     s->mode = USB_MSDM_CBW;
 }
 
-static int usb_msd_handle_control(USBDevice *dev, int request, int value,
-                                  int index, int length, uint8_t *data)
+static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
     MSDState *s = (MSDState *)dev;
     int ret;
 
-    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         return ret;
     }
diff --git a/hw/usb-net.c b/hw/usb-net.c
index bf51bb3..9be709f 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1042,13 +1042,13 @@ static void usb_net_handle_reset(USBDevice *dev)
 {
 }
 
-static int usb_net_handle_control(USBDevice *dev, int request, int value,
-                int index, int length, uint8_t *data)
+static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
     USBNetState *s = (USBNetState *) dev;
     int ret;
 
-    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         return ret;
     }
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 48ea0d8..59cb0fb 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -219,14 +219,14 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
     return ret;
 }
 
-static int usb_serial_handle_control(USBDevice *dev, int request, int value,
-                                  int index, int length, uint8_t *data)
+static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
     USBSerialState *s = (USBSerialState *)dev;
     int ret;
 
     DPRINTF("got control %x, value %x\n",request, value);
-    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         return ret;
     }
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index 57041a1..9d348e1 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -250,13 +250,13 @@ static void usb_wacom_handle_reset(USBDevice *dev)
     s->mode = WACOM_MODE_HID;
 }
 
-static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
-                                    int index, int length, uint8_t *data)
+static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
     USBWacomState *s = (USBWacomState *) dev;
     int ret;
 
-    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         return ret;
     }
diff --git a/hw/usb.c b/hw/usb.c
index d8c0a75..f503b7a 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -82,9 +82,9 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
     request = (s->setup_buf[0] << 8) | s->setup_buf[1];
     value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
     index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
- 
+
     if (s->setup_buf[0] & USB_DIR_IN) {
-        ret = s->info->handle_control(s, request, value, index, 
+        ret = s->info->handle_control(s, p, request, value, index,
                                       s->setup_len, s->data_buf);
         if (ret < 0)
             return ret;
@@ -123,9 +123,12 @@ static int do_token_in(USBDevice *s, USBPacket *p)
     switch(s->setup_state) {
     case SETUP_STATE_ACK:
         if (!(s->setup_buf[0] & USB_DIR_IN)) {
-            s->setup_state = SETUP_STATE_IDLE;
-            ret = s->info->handle_control(s, request, value, index,
+            ret = s->info->handle_control(s, p, request, value, index,
                                           s->setup_len, s->data_buf);
+            if (ret == USB_RET_ASYNC) {
+                return USB_RET_ASYNC;
+            }
+            s->setup_state = SETUP_STATE_IDLE;
             if (ret > 0)
                 return 0;
             return ret;
diff --git a/hw/usb.h b/hw/usb.h
index e0961ac..b52fa34 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -214,7 +214,7 @@ struct USBDeviceInfo {
      *
      * Returns length or one of the USB_RET_ codes.
      */
-    int (*handle_control)(USBDevice *dev, int request, int value,
+    int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
                           int index, int length, uint8_t *data);
 
     /*
diff --git a/usb-bsd.c b/usb-bsd.c
index 50ccd48..9bab6e3 100644
--- a/usb-bsd.c
+++ b/usb-bsd.c
@@ -126,6 +126,7 @@ static void usb_host_handle_reset(USBDevice *dev)
  *  and return appropriate response
  */
 static int usb_host_handle_control(USBDevice *dev,
+                                   USBPacket *p,
                                    int request,
                                    int value,
                                    int index,
-- 
1.7.1

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

* [Qemu-devel] [PATCH 07/18] usb-linux: use usb_generic_handle_packet()
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (5 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 06/18] usb: Pass the packet to the device's handle_control callback Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 08/18] usb-linux: fix device path aka physical port handling Gerd Hoffmann
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede

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

Make the linux usb host passthrough code use the usb_generic_handle_packet()
function, rather then the curent DYI code. This removes 200 lines of almost
identical code.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 hw/usb.c    |   41 +++++++++-
 hw/usb.h    |    1 +
 usb-linux.c |  269 ++++++-----------------------------------------------------
 3 files changed, 66 insertions(+), 245 deletions(-)

diff --git a/hw/usb.c b/hw/usb.c
index f503b7a..60027c6 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -63,9 +63,10 @@ void usb_wakeup(USBDevice *dev)
    protocol)
 */
 
-#define SETUP_STATE_IDLE 0
-#define SETUP_STATE_DATA 1
-#define SETUP_STATE_ACK  2
+#define SETUP_STATE_IDLE  0
+#define SETUP_STATE_SETUP 1
+#define SETUP_STATE_DATA  2
+#define SETUP_STATE_ACK   3
 
 static int do_token_setup(USBDevice *s, USBPacket *p)
 {
@@ -86,6 +87,10 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
     if (s->setup_buf[0] & USB_DIR_IN) {
         ret = s->info->handle_control(s, p, request, value, index,
                                       s->setup_len, s->data_buf);
+        if (ret == USB_RET_ASYNC) {
+             s->setup_state = SETUP_STATE_SETUP;
+             return USB_RET_ASYNC;
+        }
         if (ret < 0)
             return ret;
 
@@ -241,6 +246,36 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
     }
 }
 
+/* ctrl complete function for devices which use usb_generic_handle_packet and
+   may return USB_RET_ASYNC from their handle_control callback. Device code
+   which does this *must* call this function instead of the normal
+   usb_packet_complete to complete their async control packets. */
+void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
+{
+    if (p->len < 0) {
+        s->setup_state = SETUP_STATE_IDLE;
+    }
+
+    switch (s->setup_state) {
+    case SETUP_STATE_SETUP:
+        if (p->len < s->setup_len) {
+            s->setup_len = p->len;
+        }
+        s->setup_state = SETUP_STATE_DATA;
+        p->len = 8;
+        break;
+
+    case SETUP_STATE_ACK:
+        s->setup_state = SETUP_STATE_IDLE;
+        p->len = 0;
+        break;
+
+    default:
+        break;
+    }
+    usb_packet_complete(s, p);
+}
+
 /* XXX: fix overflow */
 int set_usb_string(uint8_t *buf, const char *str)
 {
diff --git a/hw/usb.h b/hw/usb.h
index b52fa34..c1d1014 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -294,6 +294,7 @@ static inline void usb_cancel_packet(USBPacket * p)
 void usb_attach(USBPort *port, USBDevice *dev);
 void usb_wakeup(USBDevice *dev);
 int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
+void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
 int set_usb_string(uint8_t *buf, const char *str);
 void usb_send_msg(USBDevice *dev, int msg);
 
diff --git a/usb-linux.c b/usb-linux.c
index 0ef1d26..84d3a8b 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -54,14 +54,6 @@ struct usb_ctrltransfer {
     void *data;
 };
 
-struct usb_ctrlrequest {
-    uint8_t bRequestType;
-    uint8_t bRequest;
-    uint16_t wValue;
-    uint16_t wIndex;
-    uint16_t wLength;
-};
-
 typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
                         int class_id, int vendor_id, int product_id,
                         const char *product_name, int speed);
@@ -108,26 +100,6 @@ struct endp_data {
     int max_packet_size;
 };
 
-enum {
-    CTRL_STATE_IDLE = 0,
-    CTRL_STATE_SETUP,
-    CTRL_STATE_DATA,
-    CTRL_STATE_ACK
-};
-
-/*
- * Control transfer state.
- * Note that 'buffer' _must_ follow 'req' field because
- * we need contiguous buffer when we submit control URB.
- */
-struct ctrl_struct {
-    uint16_t len;
-    uint16_t offset;
-    uint8_t  state;
-    struct   usb_ctrlrequest req;
-    uint8_t  buffer[8192];
-};
-
 struct USBAutoFilter {
     uint32_t bus_num;
     uint32_t addr;
@@ -146,7 +118,6 @@ typedef struct USBHostDevice {
     int       closing;
     Notifier  exit;
 
-    struct ctrl_struct ctrl;
     struct endp_data endp_table[MAX_ENDPOINTS];
 
     /* Host side address */
@@ -269,26 +240,6 @@ static void async_free(AsyncURB *aurb)
     qemu_free(aurb);
 }
 
-static void async_complete_ctrl(USBHostDevice *s, USBPacket *p)
-{
-    switch(s->ctrl.state) {
-    case CTRL_STATE_SETUP:
-        if (p->len < s->ctrl.len)
-            s->ctrl.len = p->len;
-        s->ctrl.state = CTRL_STATE_DATA;
-        p->len = 8;
-        break;
-
-    case CTRL_STATE_ACK:
-        s->ctrl.state = CTRL_STATE_IDLE;
-        p->len = 0;
-        break;
-
-    default:
-        break;
-    }
-}
-
 static void async_complete(void *opaque)
 {
     USBHostDevice *s = opaque;
@@ -333,9 +284,6 @@ static void async_complete(void *opaque)
             switch (aurb->urb.status) {
             case 0:
                 p->len = aurb->urb.actual_length;
-                if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
-                    async_complete_ctrl(s, p);
-                }
                 break;
 
             case -EPIPE:
@@ -348,7 +296,11 @@ static void async_complete(void *opaque)
                 break;
             }
 
-            usb_packet_complete(&s->dev, p);
+            if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
+                usb_generic_async_ctrl_complete(&s->dev, p);
+            } else {
+                usb_packet_complete(&s->dev, p);
+            }
         }
 
         async_free(aurb);
@@ -675,8 +627,9 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
     return len;
 }
 
-static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
 {
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
     struct usbdevfs_urb *urb;
     AsyncURB *aurb;
     int ret;
@@ -796,45 +749,39 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
     return 0;
 }
 
-static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
+static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
 {
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
     struct usbdevfs_urb *urb;
     AsyncURB *aurb;
-    int ret, value, index;
-    int buffer_len;
+    int ret;
 
     /*
      * Process certain standard device requests.
      * These are infrequent and are processed synchronously.
      */
-    value = le16_to_cpu(s->ctrl.req.wValue);
-    index = le16_to_cpu(s->ctrl.req.wIndex);
 
+    /* Note request is (bRequestType << 8) | bRequest */
     DPRINTF("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n",
-            s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index,
-            s->ctrl.len);
+            request >> 8, request & 0xff, value, index, length);
 
-    if (s->ctrl.req.bRequestType == 0) {
-        switch (s->ctrl.req.bRequest) {
-        case USB_REQ_SET_ADDRESS:
-            return usb_host_set_address(s, value);
+    switch (request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        return usb_host_set_address(s, value);
 
-        case USB_REQ_SET_CONFIGURATION:
-            return usb_host_set_config(s, value & 0xff);
-        }
-    }
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        return usb_host_set_config(s, value & 0xff);
 
-    if (s->ctrl.req.bRequestType == 1 &&
-                  s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE) {
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
         return usb_host_set_interface(s, index, value);
     }
 
     /* The rest are asynchronous */
 
-    buffer_len = 8 + s->ctrl.len;
-    if (buffer_len > sizeof(s->ctrl.buffer)) {
-        fprintf(stderr, "husb: ctrl buffer too small (%u > %zu)\n",
-                buffer_len, sizeof(s->ctrl.buffer));
+    if (length > sizeof(dev->data_buf)) {
+        fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
+                length, sizeof(dev->data_buf));
         return USB_RET_STALL;
     }
 
@@ -853,8 +800,8 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
     urb->type     = USBDEVFS_URB_TYPE_CONTROL;
     urb->endpoint = p->devep;
 
-    urb->buffer        = &s->ctrl.req;
-    urb->buffer_length = buffer_len;
+    urb->buffer        = &dev->setup_buf;
+    urb->buffer_length = length + 8;
 
     urb->usercontext = s;
 
@@ -879,170 +826,6 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
     return USB_RET_ASYNC;
 }
 
-static int do_token_setup(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = (USBHostDevice *) dev;
-    int ret = 0;
-
-    if (p->len != 8) {
-        return USB_RET_STALL;
-    }
-
-    memcpy(&s->ctrl.req, p->data, 8);
-    s->ctrl.len    = le16_to_cpu(s->ctrl.req.wLength);
-    s->ctrl.offset = 0;
-    s->ctrl.state  = CTRL_STATE_SETUP;
-
-    if (s->ctrl.req.bRequestType & USB_DIR_IN) {
-        ret = usb_host_handle_control(s, p);
-        if (ret < 0) {
-            return ret;
-        }
-
-        if (ret < s->ctrl.len) {
-            s->ctrl.len = ret;
-        }
-        s->ctrl.state = CTRL_STATE_DATA;
-    } else {
-        if (s->ctrl.len == 0) {
-            s->ctrl.state = CTRL_STATE_ACK;
-        } else {
-            s->ctrl.state = CTRL_STATE_DATA;
-        }
-    }
-
-    return ret;
-}
-
-static int do_token_in(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = (USBHostDevice *) dev;
-    int ret = 0;
-
-    if (p->devep != 0) {
-        return usb_host_handle_data(s, p);
-    }
-
-    switch(s->ctrl.state) {
-    case CTRL_STATE_ACK:
-        if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
-            ret = usb_host_handle_control(s, p);
-            if (ret == USB_RET_ASYNC) {
-                return USB_RET_ASYNC;
-            }
-            s->ctrl.state = CTRL_STATE_IDLE;
-            return ret > 0 ? 0 : ret;
-        }
-
-        return 0;
-
-    case CTRL_STATE_DATA:
-        if (s->ctrl.req.bRequestType & USB_DIR_IN) {
-            int len = s->ctrl.len - s->ctrl.offset;
-            if (len > p->len) {
-                len = p->len;
-            }
-            memcpy(p->data, s->ctrl.buffer + s->ctrl.offset, len);
-            s->ctrl.offset += len;
-            if (s->ctrl.offset >= s->ctrl.len) {
-                s->ctrl.state = CTRL_STATE_ACK;
-            }
-            return len;
-        }
-
-        s->ctrl.state = CTRL_STATE_IDLE;
-        return USB_RET_STALL;
-
-    default:
-        return USB_RET_STALL;
-    }
-}
-
-static int do_token_out(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = (USBHostDevice *) dev;
-
-    if (p->devep != 0) {
-        return usb_host_handle_data(s, p);
-    }
-
-    switch(s->ctrl.state) {
-    case CTRL_STATE_ACK:
-        if (s->ctrl.req.bRequestType & USB_DIR_IN) {
-            s->ctrl.state = CTRL_STATE_IDLE;
-            /* transfer OK */
-        } else {
-            /* ignore additional output */
-        }
-        return 0;
-
-    case CTRL_STATE_DATA:
-        if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
-            int len = s->ctrl.len - s->ctrl.offset;
-            if (len > p->len) {
-                len = p->len;
-            }
-            memcpy(s->ctrl.buffer + s->ctrl.offset, p->data, len);
-            s->ctrl.offset += len;
-            if (s->ctrl.offset >= s->ctrl.len) {
-                s->ctrl.state = CTRL_STATE_ACK;
-            }
-            return len;
-        }
-
-        s->ctrl.state = CTRL_STATE_IDLE;
-        return USB_RET_STALL;
-
-    default:
-        return USB_RET_STALL;
-    }
-}
-
-/*
- * Packet handler.
- * Called by the HC (host controller).
- *
- * Returns length of the transaction or one of the USB_RET_XXX codes.
- */
-static int usb_host_handle_packet(USBDevice *s, USBPacket *p)
-{
-    switch(p->pid) {
-    case USB_MSG_ATTACH:
-        s->state = USB_STATE_ATTACHED;
-        return 0;
-
-    case USB_MSG_DETACH:
-        s->state = USB_STATE_NOTATTACHED;
-        return 0;
-
-    case USB_MSG_RESET:
-        s->remote_wakeup = 0;
-        s->addr = 0;
-        s->state = USB_STATE_DEFAULT;
-        s->info->handle_reset(s);
-        return 0;
-    }
-
-    /* Rest of the PIDs must match our address */
-    if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) {
-        return USB_RET_NODEV;
-    }
-
-    switch (p->pid) {
-    case USB_TOKEN_SETUP:
-        return do_token_setup(s, p);
-
-    case USB_TOKEN_IN:
-        return do_token_in(s, p);
-
-    case USB_TOKEN_OUT:
-        return do_token_out(s, p);
-
-    default:
-        return USB_RET_STALL;
-    }
-}
-
 static int usb_linux_get_configuration(USBHostDevice *s)
 {
     uint8_t configuration;
@@ -1368,7 +1151,9 @@ static struct USBDeviceInfo usb_host_dev_info = {
     .qdev.name      = "usb-host",
     .qdev.size      = sizeof(USBHostDevice),
     .init           = usb_host_initfn,
-    .handle_packet  = usb_host_handle_packet,
+    .handle_packet  = usb_generic_handle_packet,
+    .handle_data    = usb_host_handle_data,
+    .handle_control = usb_host_handle_control,
     .handle_reset   = usb_host_handle_reset,
     .handle_destroy = usb_host_handle_destroy,
     .usbdevice_name = "host",
-- 
1.7.1

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

* [Qemu-devel] [PATCH 08/18] usb-linux: fix device path aka physical port handling
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (6 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 07/18] usb-linux: use usb_generic_handle_packet() Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 09/18] usb-linux: add hostport property Gerd Hoffmann
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

The device path isn't just a number.  It specifies the physical port
the device is connected to and in case the device is connected via
usb hub you'll have two numbers there, like this: "5.1".  The first
specifies the root port where the hub is plugged into, the second
specifies the port number of the hub where the device is plugged in.
With multiple hubs chained the string can become longer.

This patch renames devpath to port and makes it a string.   It also
adapts the sysfs parsing code accordingly.  The parser code is also more
strict now and skips the root hubs (which can't be assigned anyway).

The "info usbhost" monitor command now prints bus number, (os-assigned)
device address and physical port for each device.

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

diff --git a/usb-linux.c b/usb-linux.c
index 84d3a8b..2c6e249 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -54,7 +54,7 @@ struct usb_ctrltransfer {
     void *data;
 };
 
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port,
                         int class_id, int vendor_id, int product_id,
                         const char *product_name, int speed);
 
@@ -71,6 +71,7 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
 #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"
 
@@ -123,7 +124,7 @@ typedef struct USBHostDevice {
     /* Host side address */
     int bus_num;
     int addr;
-    int devpath;
+    char port[MAX_PORTLEN];
     struct USBAutoFilter match;
 
     QTAILQ_ENTRY(USBHostDevice) next;
@@ -836,7 +837,7 @@ static int usb_linux_get_configuration(USBHostDevice *s)
         char device_name[32], line[1024];
         int configuration;
 
-        sprintf(device_name, "%d-%d", s->bus_num, s->devpath);
+        sprintf(device_name, "%d-%s", s->bus_num, s->port);
 
         if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue",
                                 device_name)) {
@@ -882,7 +883,7 @@ static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
         char device_name[64], line[1024];
         int alt_setting;
 
-        sprintf(device_name, "%d-%d:%d.%d", s->bus_num, s->devpath,
+        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",
@@ -1001,7 +1002,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
 }
 
 static int usb_host_open(USBHostDevice *dev, int bus_num,
-                         int addr, int devpath, const char *prod_name)
+                         int addr, char *port, const char *prod_name)
 {
     int fd = -1, ret;
     struct usbdevfs_connectinfo ci;
@@ -1027,7 +1028,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
 
     dev->bus_num = bus_num;
     dev->addr = addr;
-    dev->devpath = devpath;
+    strcpy(dev->port, port);
     dev->fd = fd;
 
     /* read the device description */
@@ -1401,8 +1402,9 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
 {
     DIR *dir = NULL;
     char line[1024];
-    int bus_num, addr, devpath, speed, class_id, product_id, vendor_id;
+    int bus_num, addr, speed, class_id, product_id, vendor_id;
     int ret = 0;
+    char port[MAX_PORTLEN];
     char product_name[512];
     struct dirent *de;
 
@@ -1414,12 +1416,8 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
 
     while ((de = readdir(dir))) {
         if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
-            char *tmpstr = de->d_name;
-            if (!strncmp(de->d_name, "usb", 3)) {
-                tmpstr += 3;
-            }
-            if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) {
-                goto the_end;
+            if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
+                continue;
             }
 
             if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
@@ -1471,7 +1469,7 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
                 speed = USB_SPEED_FULL;
             }
 
-            ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id,
+            ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
                        product_id, product_name, speed);
             if (ret) {
                 goto the_end;
@@ -1562,7 +1560,7 @@ static int usb_host_scan(void *opaque, USBScanFunc *func)
 
 static QEMUTimer *usb_auto_timer;
 
-static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
+static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
                               int class_id, int vendor_id, int product_id,
                               const char *product_name, int speed)
 {
@@ -1598,7 +1596,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
         }
         DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
 
-        usb_host_open(s, bus_num, addr, devpath, product_name);
+        usb_host_open(s, bus_num, addr, port, product_name);
     }
 
     return 0;
@@ -1720,8 +1718,8 @@ static const char *usb_class_str(uint8_t class)
     return p->class_name;
 }
 
-static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
-                            int vendor_id, int product_id,
+static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port,
+                            int class_id, int vendor_id, int product_id,
                             const char *product_name,
                             int speed)
 {
@@ -1742,8 +1740,8 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
         break;
     }
 
-    monitor_printf(mon, "  Device %d.%d, speed %s Mb/s\n",
-                bus_num, addr, speed_str);
+    monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+                   bus_num, addr, port, speed_str);
     class_str = usb_class_str(class_id);
     if (class_str) {
         monitor_printf(mon, "    %s:", class_str);
@@ -1758,14 +1756,14 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
 }
 
 static int usb_host_info_device(void *opaque, int bus_num, int addr,
-                                int devpath, int class_id,
+                                char *path, int class_id,
                                 int vendor_id, int product_id,
                                 const char *product_name,
                                 int speed)
 {
     Monitor *mon = opaque;
 
-    usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
+    usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
                     product_name, speed);
     return 0;
 }
-- 
1.7.1

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

* [Qemu-devel] [PATCH 09/18] usb-linux: add hostport property
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (7 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 08/18] usb-linux: fix device path aka physical port handling Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 10/18] usb-linux: track aurbs in list Gerd Hoffmann
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This patch adds a hostport property which allows to specify the host usb
devices to pass through by bus number and physical port.  This means you
can basically hand over one (or more) of the usb plugs on your host to
the guest and whatever device is plugged in there will show up in the
guest.

Usage:

  -device usb-host,hostbus=1,hostport=1

You can figure the port numbers by plugging in some usb device, then
find it in "info usbhost" and pick bus and port specified there.

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

diff --git a/usb-linux.c b/usb-linux.c
index 2c6e249..55d914d 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -104,6 +104,7 @@ struct endp_data {
 struct USBAutoFilter {
     uint32_t bus_num;
     uint32_t addr;
+    char     *port;
     uint32_t vendor_id;
     uint32_t product_id;
 };
@@ -1162,6 +1163,7 @@ static struct USBDeviceInfo usb_host_dev_info = {
     .qdev.props     = (Property[]) {
         DEFINE_PROP_UINT32("hostbus",  USBHostDevice, match.bus_num,    0),
         DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr,       0),
+        DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
         DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
         DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
         DEFINE_PROP_END_OF_LIST(),
@@ -1580,6 +1582,9 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
         if (f->addr > 0 && f->addr != addr) {
             continue;
         }
+        if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) {
+            continue;
+        }
 
         if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
             continue;
@@ -1805,7 +1810,7 @@ void usb_host_info(Monitor *mon)
         dec2str(f->addr, addr, sizeof(addr));
         hex2str(f->vendor_id, vid, sizeof(vid));
         hex2str(f->product_id, pid, sizeof(pid));
-        monitor_printf(mon, "    Device %s.%s ID %s:%s\n",
-                       bus, addr, vid, pid);
+        monitor_printf(mon, "    Bus %s, Addr %s, Port %s, ID %s:%s\n",
+                       bus, addr, f->port ? f->port : "*", vid, pid);
     }
 }
-- 
1.7.1

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

* [Qemu-devel] [PATCH 10/18] usb-linux: track aurbs in list
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (8 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 09/18] usb-linux: add hostport property Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 11/18] usb-linux: walk async urb list in cancel Gerd Hoffmann
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This patch adds code to track all async urbs in a linked list,
so we can find them without having to pass around a opaque
pointer to them.  Prerequisite for the cleanups.

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

diff --git a/usb-linux.c b/usb-linux.c
index 55d914d..3213215 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -121,6 +121,7 @@ typedef struct USBHostDevice {
     Notifier  exit;
 
     struct endp_data endp_table[MAX_ENDPOINTS];
+    QLIST_HEAD(, AsyncURB) aurbs;
 
     /* Host side address */
     int bus_num;
@@ -223,22 +224,27 @@ struct AsyncURB
 {
     struct usbdevfs_urb urb;
     struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
+    USBHostDevice *hdev;
+    QLIST_ENTRY(AsyncURB) next;
 
     /* For regular async urbs */
     USBPacket     *packet;
-    USBHostDevice *hdev;
 
     /* For buffered iso handling */
     int iso_frame_idx; /* -1 means in flight */
 };
 
-static AsyncURB *async_alloc(void)
+static AsyncURB *async_alloc(USBHostDevice *s)
 {
-    return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+    AsyncURB *aurb = qemu_mallocz(sizeof(AsyncURB));
+    aurb->hdev = s;
+    QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
+    return aurb;
 }
 
 static void async_free(AsyncURB *aurb)
 {
+    QLIST_REMOVE(aurb, next);
     qemu_free(aurb);
 }
 
@@ -661,8 +667,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
     }
 
-    aurb = async_alloc();
-    aurb->hdev   = s;
+    aurb = async_alloc(s);
     aurb->packet = p;
 
     urb = &aurb->urb;
@@ -787,8 +792,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
         return USB_RET_STALL;
     }
 
-    aurb = async_alloc();
-    aurb->hdev   = s;
+    aurb = async_alloc(s);
     aurb->packet = p;
 
     /*
-- 
1.7.1

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

* [Qemu-devel] [PATCH 11/18] usb-linux: walk async urb list in cancel
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (9 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 10/18] usb-linux: track aurbs in list Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 12/18] usb-linux: split large xfers Gerd Hoffmann
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Lookup async urbs which are to be canceled using the linked list
instead of the direct opaque pointer.  There are two reasons we
are doing that:  First, to avoid the opaque poiner to the callback,
which is needed for upcoming cleanups.  Second, because we might
need multiple urbs per request for highspeed support, so a single
opaque pointer doesn't cut it any more anyway.

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

diff --git a/usb-linux.c b/usb-linux.c
index 3213215..5e9c5e4 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -315,19 +315,25 @@ static void async_complete(void *opaque)
     }
 }
 
-static void async_cancel(USBPacket *unused, void *opaque)
+static void async_cancel(USBPacket *p, void *opaque)
 {
-    AsyncURB *aurb = opaque;
-    USBHostDevice *s = aurb->hdev;
+    USBHostDevice *s = opaque;
+    AsyncURB *aurb;
 
-    DPRINTF("husb: async cancel. aurb %p\n", aurb);
+    QLIST_FOREACH(aurb, &s->aurbs, next) {
+        if (p != aurb->packet) {
+            continue;
+        }
 
-    /* Mark it as dead (see async_complete above) */
-    aurb->packet = NULL;
+        DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
 
-    int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
-    if (r < 0) {
-        DPRINTF("husb: async. discard urb failed errno %d\n", errno);
+        /* Mark it as dead (see async_complete above) */
+        aurb->packet = NULL;
+
+        int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+        if (r < 0) {
+            DPRINTF("husb: async. discard urb failed errno %d\n", errno);
+        }
     }
 }
 
@@ -696,7 +702,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         }
     }
 
-    usb_defer_packet(p, async_cancel, aurb);
+    usb_defer_packet(p, async_cancel, s);
     return USB_RET_ASYNC;
 }
 
@@ -828,7 +834,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
         }
     }
 
-    usb_defer_packet(p, async_cancel, aurb);
+    usb_defer_packet(p, async_cancel, s);
     return USB_RET_ASYNC;
 }
 
-- 
1.7.1

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

* [Qemu-devel] [PATCH 12/18] usb-linux: split large xfers
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (10 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 11/18] usb-linux: walk async urb list in cancel Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 13/18] usb-linux: fix max_packet_size for highspeed Gerd Hoffmann
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, David Ahern

Add support for splitting large transfers into multiple smaller ones.
This is needed for the upcoming EHCI emulation which allows guests
to submit requests up to 20k in size.  The linux kernel allows 16k
max size though.

Based on a patch from David Ahern, see
http://www.mail-archive.com/qemu-devel@nongnu.org/msg30337.html

Cc: David Ahern <daahern@cisco.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-linux.c |   68 +++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 43 insertions(+), 25 deletions(-)

diff --git a/usb-linux.c b/usb-linux.c
index 5e9c5e4..4edcdc4 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -89,6 +89,9 @@ static int usb_fs_type;
 #define ISO_URB_COUNT 3
 #define INVALID_EP_TYPE 255
 
+/* devio.c limits single requests to 16k */
+#define MAX_USBFS_BUFFER_SIZE 16384
+
 typedef struct AsyncURB AsyncURB;
 
 struct endp_data {
@@ -229,6 +232,7 @@ struct AsyncURB
 
     /* For regular async urbs */
     USBPacket     *packet;
+    int more; /* large transfer, more urbs follow */
 
     /* For buffered iso handling */
     int iso_frame_idx; /* -1 means in flight */
@@ -291,7 +295,7 @@ static void async_complete(void *opaque)
         if (p) {
             switch (aurb->urb.status) {
             case 0:
-                p->len = aurb->urb.actual_length;
+                p->len += aurb->urb.actual_length;
                 break;
 
             case -EPIPE:
@@ -306,7 +310,7 @@ static void async_complete(void *opaque)
 
             if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
                 usb_generic_async_ctrl_complete(&s->dev, p);
-            } else {
+            } else if (!aurb->more) {
                 usb_packet_complete(&s->dev, p);
             }
         }
@@ -646,7 +650,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
     USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
     struct usbdevfs_urb *urb;
     AsyncURB *aurb;
-    int ret;
+    int ret, rem;
+    uint8_t *pbuf;
     uint8_t ep;
 
     if (!is_valid(s, p->devep)) {
@@ -673,32 +678,45 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
     }
 
-    aurb = async_alloc(s);
-    aurb->packet = p;
+    rem = p->len;
+    pbuf = p->data;
+    p->len = 0;
+    while (rem) {
+        aurb = async_alloc(s);
+        aurb->packet = p;
 
-    urb = &aurb->urb;
+        urb = &aurb->urb;
+        urb->endpoint      = ep;
+        urb->type          = USBDEVFS_URB_TYPE_BULK;
+        urb->usercontext   = s;
+        urb->buffer        = pbuf;
 
-    urb->endpoint      = ep;
-    urb->buffer        = p->data;
-    urb->buffer_length = p->len;
-    urb->type          = USBDEVFS_URB_TYPE_BULK;
-    urb->usercontext   = s;
+        if (rem > MAX_USBFS_BUFFER_SIZE) {
+            urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
+            aurb->more         = 1;
+        } else {
+            urb->buffer_length = rem;
+            aurb->more         = 0;
+        }
+        pbuf += urb->buffer_length;
+        rem  -= urb->buffer_length;
 
-    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
 
-    DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
-            urb->endpoint, p->len, aurb);
+        DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
+                urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
 
-    if (ret < 0) {
-        DPRINTF("husb: submit failed. errno %d\n", errno);
-        async_free(aurb);
+        if (ret < 0) {
+            DPRINTF("husb: submit failed. errno %d\n", errno);
+            async_free(aurb);
 
-        switch(errno) {
-        case ETIMEDOUT:
-            return USB_RET_NAK;
-        case EPIPE:
-        default:
-            return USB_RET_STALL;
+            switch(errno) {
+            case ETIMEDOUT:
+                return USB_RET_NAK;
+            case EPIPE:
+            default:
+                return USB_RET_STALL;
+            }
         }
     }
 
-- 
1.7.1

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

* [Qemu-devel] [PATCH 13/18] usb-linux: fix max_packet_size for highspeed.
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (11 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 12/18] usb-linux: split large xfers Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 14/18] usb-storage: don't call usb_packet_complete twice Gerd Hoffmann
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Calculate the max packet size correctly.  Only bits 0..11 specify the size,
bits 11+12 specify the number of (highspeed) microframes the endpoint wants
to use.

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

diff --git a/usb-linux.c b/usb-linux.c
index 4edcdc4..c7e96c3 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -213,6 +213,22 @@ static int get_iso_buffer_used(USBHostDevice *s, int ep)
     return s->endp_table[ep - 1].iso_buffer_used;
 }
 
+static void set_max_packet_size(USBHostDevice *s, 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;
+    }
+    DPRINTF("husb: max packet size: 0x%x -> %d x %d\n",
+            raw, microframes, size);
+    s->endp_table[ep - 1].max_packet_size = size * microframes;
+}
+
 static int get_max_packet_size(USBHostDevice *s, int ep)
 {
     return s->endp_table[ep - 1].max_packet_size;
@@ -1008,8 +1024,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
                 break;
             case 0x01:
                 type = USBDEVFS_URB_TYPE_ISO;
-                s->endp_table[(devep & 0xf) - 1].max_packet_size =
-                    descriptors[i + 4] + (descriptors[i + 5] << 8);
+                set_max_packet_size(s, (devep & 0xf), descriptors + i);
                 break;
             case 0x02:
                 type = USBDEVFS_URB_TYPE_BULK;
-- 
1.7.1

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

* [Qemu-devel] [PATCH 14/18] usb-storage: don't call usb_packet_complete twice
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (12 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 13/18] usb-linux: fix max_packet_size for highspeed Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 15/18] usb: add usb_handle_packet Gerd Hoffmann
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

usb_msd_copy_data() may cause a recursive call to
usb_msd_command_complete() which in turn may complete
the packet, setting s->packet to NULL in case it does.
Recheck s->packet before calling usb_packet_complete()
to fix the double call.

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

diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index c3a197a..1064920 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -253,7 +253,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
     s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
     if (p) {
         usb_msd_copy_data(s);
-        if (s->usb_len == 0) {
+        if (s->packet && s->usb_len == 0) {
             /* Set s->packet to NULL before calling usb_packet_complete
                because another request may be issued before
                usb_packet_complete returns.  */
-- 
1.7.1

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

* [Qemu-devel] [PATCH 15/18] usb: add usb_handle_packet
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (13 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 14/18] usb-storage: don't call usb_packet_complete twice Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 16/18] usb: keep track of packet owner Gerd Hoffmann
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Add a usb_handle_packet function, put it into use everywhere.
Right now it just calls dev->info->handle_packet(), that will
change in future patches though.

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

diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 477927b..6e2a358 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -495,7 +495,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
         port = &s->ports[i];
         dev = port->port.dev;
         if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
-            ret = dev->info->handle_packet(dev, p);
+            ret = usb_handle_packet(dev, p);
             if (ret != USB_RET_NODEV) {
                 return ret;
             }
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
index 38986d3..6037193 100644
--- a/hw/usb-musb.c
+++ b/hw/usb-musb.c
@@ -601,7 +601,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
     ep->packey[dir].dir = dir;
 
     if (s->port.dev)
-        ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir].p);
+        ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p);
     else
         ret = USB_RET_NODEV;
 
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 32913eb..8b966f7 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -748,7 +748,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
             ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
             ohci->usb_packet.data = ohci->usb_buf;
             ohci->usb_packet.len = len;
-            ret = dev->info->handle_packet(dev, &ohci->usb_packet);
+            ret = usb_handle_packet(dev, &ohci->usb_packet);
             if (ret != USB_RET_NODEV)
                 break;
         }
@@ -944,7 +944,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
             ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
             ohci->usb_packet.data = ohci->usb_buf;
             ohci->usb_packet.len = len;
-            ret = dev->info->handle_packet(dev, &ohci->usb_packet);
+            ret = usb_handle_packet(dev, &ohci->usb_packet);
             if (ret != USB_RET_NODEV)
                 break;
         }
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 1e9c1e7..c0de05b 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -632,7 +632,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
         USBDevice *dev = port->port.dev;
 
         if (dev && (port->ctrl & UHCI_PORT_EN))
-            ret = dev->info->handle_packet(dev, p);
+            ret = usb_handle_packet(dev, p);
     }
 
     DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len);
diff --git a/hw/usb.c b/hw/usb.c
index 60027c6..966cb0f 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -297,9 +297,22 @@ int set_usb_string(uint8_t *buf, const char *str)
 void usb_send_msg(USBDevice *dev, int msg)
 {
     USBPacket p;
+    int ret;
+
     memset(&p, 0, sizeof(p));
     p.pid = msg;
-    dev->info->handle_packet(dev, &p);
-
+    ret = usb_handle_packet(dev, &p);
     /* This _must_ be synchronous */
+    assert(ret != USB_RET_ASYNC);
+}
+
+/* Hand over a packet to a device for processing.  Return value
+   USB_RET_ASYNC indicates the processing isn't finished yet, the
+   driver will call usb_packet_complete() when done processing it. */
+int usb_handle_packet(USBDevice *dev, USBPacket *p)
+{
+    int ret;
+
+    ret = dev->info->handle_packet(dev, p);
+    return ret;
 }
diff --git a/hw/usb.h b/hw/usb.h
index c1d1014..6889467 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -266,6 +266,8 @@ struct USBPacket {
     void *cancel_opaque;
 };
 
+int usb_handle_packet(USBDevice *dev, USBPacket *p);
+
 /* Defer completion of a USB packet.  The hadle_packet routine should then
    return USB_RET_ASYNC.  Packets that complete immediately (before
    handle_packet returns) should not call this method.  */
-- 
1.7.1

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

* [Qemu-devel] [PATCH 16/18] usb: keep track of packet owner.
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (14 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 15/18] usb: add usb_handle_packet Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo Gerd Hoffmann
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Keep track of the device which owns the usb packet for async processing.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb.c |   32 ++++++++++++++++++++++++++++++++
 hw/usb.h |   18 +++---------------
 2 files changed, 35 insertions(+), 15 deletions(-)

diff --git a/hw/usb.c b/hw/usb.c
index 966cb0f..8a9a7fc 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -313,6 +313,38 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
 {
     int ret;
 
+    assert(p->owner == NULL);
     ret = dev->info->handle_packet(dev, p);
+    if (ret == USB_RET_ASYNC) {
+        if (p->owner == NULL) {
+            p->owner = dev;
+        } else {
+            /* We'll end up here when usb_handle_packet is called
+             * recursively due to a hub being in the chain.  Nothing
+             * to do.  Leave p->owner pointing to the device, not the
+             * hub. */;
+        }
+    }
     return ret;
 }
+
+/* Notify the controller that an async packet is complete.  This should only
+   be called for packets previously deferred by returning USB_RET_ASYNC from
+   handle_packet. */
+void usb_packet_complete(USBDevice *dev, USBPacket *p)
+{
+    /* Note: p->owner != dev is possible in case dev is a hub */
+    assert(p->owner != NULL);
+    dev->port->ops->complete(dev, p);
+    p->owner = NULL;
+}
+
+/* Cancel an active packet.  The packed must have been deferred by
+   returning USB_RET_ASYNC from handle_packet, and not yet
+   completed.  */
+void usb_cancel_packet(USBPacket * p)
+{
+    assert(p->owner != NULL);
+    p->cancel_cb(p, p->cancel_opaque);
+    p->owner = NULL;
+}
diff --git a/hw/usb.h b/hw/usb.h
index 6889467..80e8e90 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -262,11 +262,14 @@ struct USBPacket {
     uint8_t *data;
     int len;
     /* Internal use by the USB layer.  */
+    USBDevice *owner;
     USBCallback *cancel_cb;
     void *cancel_opaque;
 };
 
 int usb_handle_packet(USBDevice *dev, USBPacket *p);
+void usb_packet_complete(USBDevice *dev, USBPacket *p);
+void usb_cancel_packet(USBPacket * p);
 
 /* Defer completion of a USB packet.  The hadle_packet routine should then
    return USB_RET_ASYNC.  Packets that complete immediately (before
@@ -278,21 +281,6 @@ static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
     p->cancel_opaque = opaque;
 }
 
-/* Notify the controller that an async packet is complete.  This should only
-   be called for packets previously deferred with usb_defer_packet, and
-   should never be called from within handle_packet.  */
-static inline void usb_packet_complete(USBDevice *dev, USBPacket *p)
-{
-    dev->port->ops->complete(dev, p);
-}
-
-/* Cancel an active packet.  The packed must have been deferred with
-   usb_defer_packet,  and not yet completed.  */
-static inline void usb_cancel_packet(USBPacket * p)
-{
-    p->cancel_cb(p, p->cancel_opaque);
-}
-
 void usb_attach(USBPort *port, USBDevice *dev);
 void usb_wakeup(USBDevice *dev);
 int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
-- 
1.7.1

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

* [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (15 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 16/18] usb: keep track of packet owner Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23 14:04   ` Hans de Goede
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 18/18] usb: add ehci adapter Gerd Hoffmann
  2011-05-26 10:13 ` [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
  18 siblings, 1 reply; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Remove the cancel callback from the USBPacket struct, move it over
to USBDeviceInfo.  Zap usb_defer_packet() which is obsolete now.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb-msd.c |    8 +++-----
 hw/usb.c     |    2 +-
 hw/usb.h     |   17 +++++------------
 usb-linux.c  |    7 +++----
 4 files changed, 12 insertions(+), 22 deletions(-)

diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 1064920..141da2c 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -315,9 +315,9 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
     return ret;
 }
 
-static void usb_msd_cancel_io(USBPacket *p, void *opaque)
+static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
 {
-    MSDState *s = opaque;
+    MSDState *s = DO_UPCAST(MSDState, dev, dev);
     s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
     s->packet = NULL;
     s->scsi_len = 0;
@@ -398,7 +398,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             }
             if (s->usb_len) {
                 DPRINTF("Deferring packet %p\n", p);
-                usb_defer_packet(p, usb_msd_cancel_io, s);
                 s->packet = p;
                 ret = USB_RET_ASYNC;
             } else {
@@ -421,7 +420,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             if (s->data_len != 0 || len < 13)
                 goto fail;
             /* Waiting for SCSI write to complete.  */
-            usb_defer_packet(p, usb_msd_cancel_io, s);
             s->packet = p;
             ret = USB_RET_ASYNC;
             break;
@@ -455,7 +453,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             }
             if (s->usb_len) {
                 DPRINTF("Deferring packet %p\n", p);
-                usb_defer_packet(p, usb_msd_cancel_io, s);
                 s->packet = p;
                 ret = USB_RET_ASYNC;
             } else {
@@ -604,6 +601,7 @@ static struct USBDeviceInfo msd_info = {
     .usb_desc       = &desc,
     .init           = usb_msd_initfn,
     .handle_packet  = usb_generic_handle_packet,
+    .cancel_packet  = usb_msd_cancel_io,
     .handle_attach  = usb_desc_attach,
     .handle_reset   = usb_msd_handle_reset,
     .handle_control = usb_msd_handle_control,
diff --git a/hw/usb.c b/hw/usb.c
index 8a9a7fc..4a39cbc 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -345,6 +345,6 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
 void usb_cancel_packet(USBPacket * p)
 {
     assert(p->owner != NULL);
-    p->cancel_cb(p, p->cancel_opaque);
+    p->owner->info->cancel_packet(p->owner, p);
     p->owner = NULL;
 }
diff --git a/hw/usb.h b/hw/usb.h
index 80e8e90..9882400 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -194,6 +194,11 @@ struct USBDeviceInfo {
     int (*handle_packet)(USBDevice *dev, USBPacket *p);
 
     /*
+     * Called when a packet is canceled.
+     */
+    void (*cancel_packet)(USBDevice *dev, USBPacket *p);
+
+    /*
      * Called when device is destroyed.
      */
     void (*handle_destroy)(USBDevice *dev);
@@ -263,24 +268,12 @@ struct USBPacket {
     int len;
     /* Internal use by the USB layer.  */
     USBDevice *owner;
-    USBCallback *cancel_cb;
-    void *cancel_opaque;
 };
 
 int usb_handle_packet(USBDevice *dev, USBPacket *p);
 void usb_packet_complete(USBDevice *dev, USBPacket *p);
 void usb_cancel_packet(USBPacket * p);
 
-/* Defer completion of a USB packet.  The hadle_packet routine should then
-   return USB_RET_ASYNC.  Packets that complete immediately (before
-   handle_packet returns) should not call this method.  */
-static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
-                                    void * opaque)
-{
-    p->cancel_cb = cancel;
-    p->cancel_opaque = opaque;
-}
-
 void usb_attach(USBPort *port, USBDevice *dev);
 void usb_wakeup(USBDevice *dev);
 int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
diff --git a/usb-linux.c b/usb-linux.c
index c7e96c3..baa6574 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -335,9 +335,9 @@ static void async_complete(void *opaque)
     }
 }
 
-static void async_cancel(USBPacket *p, void *opaque)
+static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
 {
-    USBHostDevice *s = opaque;
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
     AsyncURB *aurb;
 
     QLIST_FOREACH(aurb, &s->aurbs, next) {
@@ -736,7 +736,6 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         }
     }
 
-    usb_defer_packet(p, async_cancel, s);
     return USB_RET_ASYNC;
 }
 
@@ -868,7 +867,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
         }
     }
 
-    usb_defer_packet(p, async_cancel, s);
     return USB_RET_ASYNC;
 }
 
@@ -1197,6 +1195,7 @@ static struct USBDeviceInfo usb_host_dev_info = {
     .qdev.size      = sizeof(USBHostDevice),
     .init           = usb_host_initfn,
     .handle_packet  = usb_generic_handle_packet,
+    .cancel_packet  = usb_host_async_cancel,
     .handle_data    = usb_host_handle_data,
     .handle_control = usb_host_handle_control,
     .handle_reset   = usb_host_handle_reset,
-- 
1.7.1

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

* [Qemu-devel] [PATCH 18/18] usb: add ehci adapter
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (16 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo Gerd Hoffmann
@ 2011-05-23  9:43 ` Gerd Hoffmann
  2011-05-23 19:25   ` Blue Swirl
  2011-05-24 15:45   ` Erik Rull
  2011-05-26 10:13 ` [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
  18 siblings, 2 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23  9:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: Vincent Palatin, Kevin Wolf, Jan Kiszka, Gerd Hoffmann, David S. Ahern

This patch finally merges the EHCI host adapter aka USB 2.0 support.

Based on the ehci bits collected @ git://git.kiszka.org/qemu.git ehci

EHCI has a long out-of-tree history.  Project was started by Mark
Burkley, with contributions by Niels de Vos.  David S. Ahern continued
working on it.  Kevin Wolf, Jan Kiszka and Vincent Palatin contributed
bugfixes.

/me (Gerd Hoffmann) picked it up where it left off, prepared the code
for merge, fixed a few bugs and added basic user docs.

Cc: David S. Ahern <daahern@cisco.com>
Cc: Jan Kiszka <jan.kiszka@web.de>
Cc: Kevin Wolf <mail@kevin-wolf.de>
Cc: Vincent Palatin <vincent.palatin_qemu@m4x.org>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.objs           |    1 +
 default-configs/pci.mak |    1 +
 docs/usb2.txt           |   38 +
 hw/pci_ids.h            |    1 +
 hw/usb-ehci.c           | 2038 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 2079 insertions(+), 0 deletions(-)
 create mode 100644 docs/usb2.txt
 create mode 100644 hw/usb-ehci.c

diff --git a/Makefile.objs b/Makefile.objs
index 4478c61..90838f6 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -193,6 +193,7 @@ hw-obj-$(CONFIG_PCSPK) += pcspk.o
 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_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 0471efb..22bd350 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -3,6 +3,7 @@ CONFIG_VIRTIO_PCI=y
 CONFIG_VIRTIO=y
 CONFIG_USB_UHCI=y
 CONFIG_USB_OHCI=y
+CONFIG_USB_EHCI=y
 CONFIG_NE2000_PCI=y
 CONFIG_EEPRO100_PCI=y
 CONFIG_PCNET_PCI=y
diff --git a/docs/usb2.txt b/docs/usb2.txt
new file mode 100644
index 0000000..b283c13
--- /dev/null
+++ b/docs/usb2.txt
@@ -0,0 +1,38 @@
+
+USB 2.0 Quick Start
+===================
+
+The QEMU EHCI Adapter does *not* support companion controllers.  That
+implies there are two completely separate USB busses: One USB 1.1 bus
+driven by the UHCI controller and one USB 2.0 bus driven by the EHCI
+controller.  Devices must be attached to the correct controller
+manually.
+
+The '-usb' switch will make qemu create the UHCI controller as part of
+the PIIX3 chipset.  The USB 1.1 bus will carry the name "usb.0".
+
+You can use the standard -device switch to add a EHCI controller to
+your virtual machine.  It is strongly recommended to specify an ID for
+the controller so the USB 2.0 bus gets a individual name, for example
+'-device usb-ehci,id=ehci".  This will give you a USB 2.0 bus named
+"ehci.0".
+
+I strongly recomment to also use -device to attach usb devices because
+you can specify the bus they should be attached to this way.  Here is
+a complete example:
+
+    qemu -M pc ${otheroptions}                           \
+        -drive if=none,id=usbstick,file=/path/to/image   \
+        -usb                                             \
+        -device usb-ehci,id=ehci                         \
+        -device usb-tablet,bus=usb.0                     \
+        -device usb-storage,bus=ehci.0,drive=usbstick
+
+This attaches a usb tablet to the UHCI adapter and a usb mass storage
+device to the EHCI adapter.
+
+enjoy,
+  Gerd
+
+--
+Gerd Hoffmann <kraxel@redhat.com>
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index ea3418c..d9457ed 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -100,6 +100,7 @@
 #define PCI_VENDOR_ID_INTEL              0x8086
 #define PCI_DEVICE_ID_INTEL_82441        0x1237
 #define PCI_DEVICE_ID_INTEL_82801AA_5    0x2415
+#define PCI_DEVICE_ID_INTEL_82801D       0x24CD
 #define PCI_DEVICE_ID_INTEL_ESB_9        0x25ab
 #define PCI_DEVICE_ID_INTEL_82371SB_0    0x7000
 #define PCI_DEVICE_ID_INTEL_82371SB_1    0x7010
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
new file mode 100644
index 0000000..545c590
--- /dev/null
+++ b/hw/usb-ehci.c
@@ -0,0 +1,2038 @@
+/*
+ * QEMU USB EHCI Emulation
+ *
+ * Copyright(c) 2008  Emutex Ltd. (address@hidden)
+ *
+ * EHCI project was started by Mark Burkley, with contributions by
+ * Niels de Vos.  David S. Ahern continued working on it.  Kevin Wolf,
+ * Jan Kiszka and Vincent Palatin contributed bugfixes.
+ *
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * TODO:
+ *  o Downstream port handoff
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "pci.h"
+#include "monitor.h"
+
+#define EHCI_DEBUG   0
+#define STATE_DEBUG  0       /* state transitions  */
+
+#if EHCI_DEBUG || STATE_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#if STATE_DEBUG
+#define DPRINTF_ST DPRINTF
+#else
+#define DPRINTF_ST(...)
+#endif
+
+/* internal processing - reset HC to try and recover */
+#define USB_RET_PROCERR   (-99)
+
+#define MMIO_SIZE        0x1000
+
+/* Capability Registers Base Address - section 2.2 */
+#define CAPREGBASE       0x0000
+#define CAPLENGTH        CAPREGBASE + 0x0000  // 1-byte, 0x0001 reserved
+#define HCIVERSION       CAPREGBASE + 0x0002  // 2-bytes, i/f version #
+#define HCSPARAMS        CAPREGBASE + 0x0004  // 4-bytes, structural params
+#define HCCPARAMS        CAPREGBASE + 0x0008  // 4-bytes, capability params
+#define EECP             HCCPARAMS + 1
+#define HCSPPORTROUTE1   CAPREGBASE + 0x000c
+#define HCSPPORTROUTE2   CAPREGBASE + 0x0010
+
+#define OPREGBASE        0x0020        // Operational Registers Base Address
+
+#define USBCMD           OPREGBASE + 0x0000
+#define USBCMD_RUNSTOP   (1 << 0)      // run / Stop
+#define USBCMD_HCRESET   (1 << 1)      // HC Reset
+#define USBCMD_FLS       (3 << 2)      // Frame List Size
+#define USBCMD_FLS_SH    2             // Frame List Size Shift
+#define USBCMD_PSE       (1 << 4)      // Periodic Schedule Enable
+#define USBCMD_ASE       (1 << 5)      // Asynch Schedule Enable
+#define USBCMD_IAAD      (1 << 6)      // Int Asynch Advance Doorbell
+#define USBCMD_LHCR      (1 << 7)      // Light Host Controller Reset
+#define USBCMD_ASPMC     (3 << 8)      // Async Sched Park Mode Count
+#define USBCMD_ASPME     (1 << 11)     // Async Sched Park Mode Enable
+#define USBCMD_ITC       (0x7f << 16)  // Int Threshold Control
+#define USBCMD_ITC_SH    16            // Int Threshold Control Shift
+
+#define USBSTS           OPREGBASE + 0x0004
+#define USBSTS_RO_MASK   0x0000003f
+#define USBSTS_INT       (1 << 0)      // USB Interrupt
+#define USBSTS_ERRINT    (1 << 1)      // Error Interrupt
+#define USBSTS_PCD       (1 << 2)      // Port Change Detect
+#define USBSTS_FLR       (1 << 3)      // Frame List Rollover
+#define USBSTS_HSE       (1 << 4)      // Host System Error
+#define USBSTS_IAA       (1 << 5)      // Interrupt on Async Advance
+#define USBSTS_HALT      (1 << 12)     // HC Halted
+#define USBSTS_REC       (1 << 13)     // Reclamation
+#define USBSTS_PSS       (1 << 14)     // Periodic Schedule Status
+#define USBSTS_ASS       (1 << 15)     // Asynchronous Schedule Status
+
+/*
+ *  Interrupt enable bits correspond to the interrupt active bits in USBSTS
+ *  so no need to redefine here.
+ */
+#define USBINTR              OPREGBASE + 0x0008
+#define USBINTR_MASK         0x0000003f
+
+#define FRINDEX              OPREGBASE + 0x000c
+#define CTRLDSSEGMENT        OPREGBASE + 0x0010
+#define PERIODICLISTBASE     OPREGBASE + 0x0014
+#define ASYNCLISTADDR        OPREGBASE + 0x0018
+#define ASYNCLISTADDR_MASK   0xffffffe0
+
+#define CONFIGFLAG           OPREGBASE + 0x0040
+
+#define PORTSC               (OPREGBASE + 0x0044)
+#define PORTSC_BEGIN         PORTSC
+#define PORTSC_END           (PORTSC + 4 * NB_PORTS)
+/*
+ * Bits that are reserverd or are read-only are masked out of values
+ * written to us by software
+ */
+#define PORTSC_RO_MASK       0x007021c5
+#define PORTSC_RWC_MASK      0x0000002a
+#define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
+#define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
+#define PORTSC_WKCN_E        (1 << 20)    // Wake on Connect Enable
+#define PORTSC_PTC           (15 << 16)   // Port Test Control
+#define PORTSC_PTC_SH        16           // Port Test Control shift
+#define PORTSC_PIC           (3 << 14)    // Port Indicator Control
+#define PORTSC_PIC_SH        14           // Port Indicator Control Shift
+#define PORTSC_POWNER        (1 << 13)    // Port Owner
+#define PORTSC_PPOWER        (1 << 12)    // Port Power
+#define PORTSC_LINESTAT      (3 << 10)    // Port Line Status
+#define PORTSC_LINESTAT_SH   10           // Port Line Status Shift
+#define PORTSC_PRESET        (1 << 8)     // Port Reset
+#define PORTSC_SUSPEND       (1 << 7)     // Port Suspend
+#define PORTSC_FPRES         (1 << 6)     // Force Port Resume
+#define PORTSC_OCC           (1 << 5)     // Over Current Change
+#define PORTSC_OCA           (1 << 4)     // Over Current Active
+#define PORTSC_PEDC          (1 << 3)     // Port Enable/Disable Change
+#define PORTSC_PED           (1 << 2)     // Port Enable/Disable
+#define PORTSC_CSC           (1 << 1)     // Connect Status Change
+#define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
+
+#define FRAME_TIMER_FREQ 1000
+#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ)
+
+#define NB_MAXINTRATE    8        // Max rate at which controller issues ints
+#define NB_PORTS         4        // Number of downstream ports
+#define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction
+#define MAX_ITERATIONS   20       // Max number of QH before we break the loop
+#define MAX_QH           100      // Max allowable queue heads in a chain
+
+/*  Internal periodic / asynchronous schedule state machine states
+ */
+typedef enum {
+    EST_INACTIVE = 1000,
+    EST_ACTIVE,
+    EST_EXECUTING,
+    EST_SLEEPING,
+    /*  The following states are internal to the state machine function
+    */
+    EST_WAITLISTHEAD,
+    EST_FETCHENTRY,
+    EST_FETCHQH,
+    EST_FETCHITD,
+    EST_ADVANCEQUEUE,
+    EST_FETCHQTD,
+    EST_EXECUTE,
+    EST_WRITEBACK,
+    EST_HORIZONTALQH
+} EHCI_STATES;
+
+/* macros for accessing fields within next link pointer entry */
+#define NLPTR_GET(x)             ((x) & 0xffffffe0)
+#define NLPTR_TYPE_GET(x)        (((x) >> 1) & 3)
+#define NLPTR_TBIT(x)            ((x) & 1)  // 1=invalid, 0=valid
+
+/* link pointer types */
+#define NLPTR_TYPE_ITD           0     // isoc xfer descriptor
+#define NLPTR_TYPE_QH            1     // queue head
+#define NLPTR_TYPE_STITD         2     // split xaction, isoc xfer descriptor
+#define NLPTR_TYPE_FSTN          3     // frame span traversal node
+
+
+/*  EHCI spec version 1.0 Section 3.3
+ */
+typedef struct EHCIitd {
+    uint32_t next;
+
+    uint32_t transact[8];
+#define ITD_XACT_ACTIVE          (1 << 31)
+#define ITD_XACT_DBERROR         (1 << 30)
+#define ITD_XACT_BABBLE          (1 << 29)
+#define ITD_XACT_XACTERR         (1 << 28)
+#define ITD_XACT_LENGTH_MASK     0x0fff0000
+#define ITD_XACT_LENGTH_SH       16
+#define ITD_XACT_IOC             (1 << 15)
+#define ITD_XACT_PGSEL_MASK      0x00007000
+#define ITD_XACT_PGSEL_SH        12
+#define ITD_XACT_OFFSET_MASK     0x00000fff
+
+    uint32_t bufptr[7];
+#define ITD_BUFPTR_MASK          0xfffff000
+#define ITD_BUFPTR_SH            12
+#define ITD_BUFPTR_EP_MASK       0x00000f00
+#define ITD_BUFPTR_EP_SH         8
+#define ITD_BUFPTR_DEVADDR_MASK  0x0000007f
+#define ITD_BUFPTR_DEVADDR_SH    0
+#define ITD_BUFPTR_DIRECTION     (1 << 11)
+#define ITD_BUFPTR_MAXPKT_MASK   0x000007ff
+#define ITD_BUFPTR_MAXPKT_SH     0
+#define ITD_BUFPTR_MULT_MASK     0x00000003
+} EHCIitd;
+
+/*  EHCI spec version 1.0 Section 3.4
+ */
+typedef struct EHCIsitd {
+    uint32_t next;                  // Standard next link pointer
+    uint32_t epchar;
+#define SITD_EPCHAR_IO              (1 << 31)
+#define SITD_EPCHAR_PORTNUM_MASK    0x7f000000
+#define SITD_EPCHAR_PORTNUM_SH      24
+#define SITD_EPCHAR_HUBADD_MASK     0x007f0000
+#define SITD_EPCHAR_HUBADDR_SH      16
+#define SITD_EPCHAR_EPNUM_MASK      0x00000f00
+#define SITD_EPCHAR_EPNUM_SH        8
+#define SITD_EPCHAR_DEVADDR_MASK    0x0000007f
+
+    uint32_t uframe;
+#define SITD_UFRAME_CMASK_MASK      0x0000ff00
+#define SITD_UFRAME_CMASK_SH        8
+#define SITD_UFRAME_SMASK_MASK      0x000000ff
+
+    uint32_t results;
+#define SITD_RESULTS_IOC              (1 << 31)
+#define SITD_RESULTS_PGSEL            (1 << 30)
+#define SITD_RESULTS_TBYTES_MASK      0x03ff0000
+#define SITD_RESULTS_TYBYTES_SH       16
+#define SITD_RESULTS_CPROGMASK_MASK   0x0000ff00
+#define SITD_RESULTS_CPROGMASK_SH     8
+#define SITD_RESULTS_ACTIVE           (1 << 7)
+#define SITD_RESULTS_ERR              (1 << 6)
+#define SITD_RESULTS_DBERR            (1 << 5)
+#define SITD_RESULTS_BABBLE           (1 << 4)
+#define SITD_RESULTS_XACTERR          (1 << 3)
+#define SITD_RESULTS_MISSEDUF         (1 << 2)
+#define SITD_RESULTS_SPLITXSTATE      (1 << 1)
+
+    uint32_t bufptr[2];
+#define SITD_BUFPTR_MASK              0xfffff000
+#define SITD_BUFPTR_CURROFF_MASK      0x00000fff
+#define SITD_BUFPTR_TPOS_MASK         0x00000018
+#define SITD_BUFPTR_TPOS_SH           3
+#define SITD_BUFPTR_TCNT_MASK         0x00000007
+
+    uint32_t backptr;                 // Standard next link pointer
+} EHCIsitd;
+
+/*  EHCI spec version 1.0 Section 3.5
+ */
+typedef struct EHCIqtd {
+    uint32_t next;                    // Standard next link pointer
+    uint32_t altnext;                 // Standard next link pointer
+    uint32_t token;
+#define QTD_TOKEN_DTOGGLE             (1 << 31)
+#define QTD_TOKEN_TBYTES_MASK         0x7fff0000
+#define QTD_TOKEN_TBYTES_SH           16
+#define QTD_TOKEN_IOC                 (1 << 15)
+#define QTD_TOKEN_CPAGE_MASK          0x00007000
+#define QTD_TOKEN_CPAGE_SH            12
+#define QTD_TOKEN_CERR_MASK           0x00000c00
+#define QTD_TOKEN_CERR_SH             10
+#define QTD_TOKEN_PID_MASK            0x00000300
+#define QTD_TOKEN_PID_SH              8
+#define QTD_TOKEN_ACTIVE              (1 << 7)
+#define QTD_TOKEN_HALT                (1 << 6)
+#define QTD_TOKEN_DBERR               (1 << 5)
+#define QTD_TOKEN_BABBLE              (1 << 4)
+#define QTD_TOKEN_XACTERR             (1 << 3)
+#define QTD_TOKEN_MISSEDUF            (1 << 2)
+#define QTD_TOKEN_SPLITXSTATE         (1 << 1)
+#define QTD_TOKEN_PING                (1 << 0)
+
+    uint32_t bufptr[5];               // Standard buffer pointer
+#define QTD_BUFPTR_MASK               0xfffff000
+} EHCIqtd;
+
+/*  EHCI spec version 1.0 Section 3.6
+ */
+typedef struct EHCIqh {
+    uint32_t next;                    // Standard next link pointer
+
+    /* endpoint characteristics */
+    uint32_t epchar;
+#define QH_EPCHAR_RL_MASK             0xf0000000
+#define QH_EPCHAR_RL_SH               28
+#define QH_EPCHAR_C                   (1 << 27)
+#define QH_EPCHAR_MPLEN_MASK          0x07FF0000
+#define QH_EPCHAR_MPLEN_SH            16
+#define QH_EPCHAR_H                   (1 << 15)
+#define QH_EPCHAR_DTC                 (1 << 14)
+#define QH_EPCHAR_EPS_MASK            0x00003000
+#define QH_EPCHAR_EPS_SH              12
+#define EHCI_QH_EPS_FULL              0
+#define EHCI_QH_EPS_LOW               1
+#define EHCI_QH_EPS_HIGH              2
+#define EHCI_QH_EPS_RESERVED          3
+
+#define QH_EPCHAR_EP_MASK             0x00000f00
+#define QH_EPCHAR_EP_SH               8
+#define QH_EPCHAR_I                   (1 << 7)
+#define QH_EPCHAR_DEVADDR_MASK        0x0000007f
+#define QH_EPCHAR_DEVADDR_SH          0
+
+    /* endpoint capabilities */
+    uint32_t epcap;
+#define QH_EPCAP_MULT_MASK            0xc0000000
+#define QH_EPCAP_MULT_SH              30
+#define QH_EPCAP_PORTNUM_MASK         0x3f800000
+#define QH_EPCAP_PORTNUM_SH           23
+#define QH_EPCAP_HUBADDR_MASK         0x007f0000
+#define QH_EPCAP_HUBADDR_SH           16
+#define QH_EPCAP_CMASK_MASK           0x0000ff00
+#define QH_EPCAP_CMASK_SH             8
+#define QH_EPCAP_SMASK_MASK           0x000000ff
+#define QH_EPCAP_SMASK_SH             0
+
+    uint32_t current_qtd;             // Standard next link pointer
+    uint32_t next_qtd;                // Standard next link pointer
+    uint32_t altnext_qtd;
+#define QH_ALTNEXT_NAKCNT_MASK        0x0000001e
+#define QH_ALTNEXT_NAKCNT_SH          1
+
+    uint32_t token;                   // Same as QTD token
+    uint32_t bufptr[5];               // Standard buffer pointer
+#define BUFPTR_CPROGMASK_MASK         0x000000ff
+#define BUFPTR_FRAMETAG_MASK          0x0000001f
+#define BUFPTR_SBYTES_MASK            0x00000fe0
+#define BUFPTR_SBYTES_SH              5
+} EHCIqh;
+
+/*  EHCI spec version 1.0 Section 3.7
+ */
+typedef struct EHCIfstn {
+    uint32_t next;                    // Standard next link pointer
+    uint32_t backptr;                 // Standard next link pointer
+} EHCIfstn;
+
+typedef struct {
+    PCIDevice dev;
+    qemu_irq irq;
+    target_phys_addr_t mem_base;
+    int mem;
+    int num_ports;
+    /*
+     *  EHCI spec version 1.0 Section 2.3
+     *  Host Controller Operational Registers
+     */
+    union {
+        uint8_t mmio[MMIO_SIZE];
+        struct {
+            uint8_t cap[OPREGBASE];
+            uint32_t usbcmd;
+            uint32_t usbsts;
+            uint32_t usbintr;
+            uint32_t frindex;
+            uint32_t ctrldssegment;
+            uint32_t periodiclistbase;
+            uint32_t asynclistaddr;
+            uint32_t notused[9];
+            uint32_t configflag;
+            uint32_t portsc[NB_PORTS];
+        };
+    };
+    /*
+     *  Internal states, shadow registers, etc
+     */
+    uint32_t sofv;
+    QEMUTimer *frame_timer;
+    int attach_poll_counter;
+    int astate;                        // Current state in asynchronous schedule
+    int pstate;                        // Current state in periodic schedule
+    USBPort ports[NB_PORTS];
+    uint8_t buffer[BUFF_SIZE];
+    uint32_t usbsts_pending;
+
+    /* cached data from guest - needs to be flushed
+     * when guest removes an entry (doorbell, handshake sequence)
+     */
+    EHCIqh qh;             // copy of current QH (being worked on)
+    uint32_t qhaddr;       // address QH read from
+
+    EHCIqtd qtd;           // copy of current QTD (being worked on)
+    uint32_t qtdaddr;      // address QTD read from
+
+    uint32_t itdaddr;      // current ITD
+
+    uint32_t fetch_addr;   // which address to look at next
+
+    USBBus bus;
+    USBPacket usb_packet;
+    int async_complete;
+    uint32_t tbytes;
+    int pid;
+    int exec_status;
+    int isoch_pause;
+    uint32_t last_run_usec;
+    uint32_t frame_end_usec;
+} EHCIState;
+
+#define SET_LAST_RUN_CLOCK(s) \
+    (s)->last_run_usec = qemu_get_clock_ns(vm_clock) / 1000;
+
+/* nifty macros from Arnon's EHCI version  */
+#define get_field(data, field) \
+    (((data) & field##_MASK) >> field##_SH)
+
+#define set_field(data, newval, field) do { \
+    uint32_t val = *data; \
+    val &= ~ field##_MASK; \
+    val |= ((newval) << field##_SH) & field##_MASK; \
+    *data = val; \
+    } while(0)
+
+
+#if EHCI_DEBUG
+static const char *addr2str(unsigned addr)
+{
+    const char *r            = "   unknown";
+    const char *n[] = {
+        [ CAPLENGTH ]        = " CAPLENGTH",
+        [ HCIVERSION ]       = "HCIVERSION",
+        [ HCSPARAMS ]        = " HCSPARAMS",
+        [ HCCPARAMS ]        = " HCCPARAMS",
+        [ USBCMD ]           = "   COMMAND",
+        [ USBSTS ]           = "    STATUS",
+        [ USBINTR ]          = " INTERRUPT",
+        [ FRINDEX ]          = " FRAME IDX",
+        [ PERIODICLISTBASE ] = "P-LIST BASE",
+        [ ASYNCLISTADDR ]    = "A-LIST ADDR",
+        [ PORTSC_BEGIN ...
+          PORTSC_END ]       = "PORT STATUS",
+        [ CONFIGFLAG ]       = "CONFIG FLAG",
+    };
+
+    if (addr < ARRAY_SIZE(n) && n[addr] != NULL) {
+        return n[addr];
+    } else {
+        return r;
+    }
+}
+#endif
+
+
+static inline void ehci_set_interrupt(EHCIState *s, int intr)
+{
+    int level = 0;
+
+    // TODO honour interrupt threshold requests
+
+    s->usbsts |= intr;
+
+    if ((s->usbsts & USBINTR_MASK) & s->usbintr) {
+        level = 1;
+    }
+
+    qemu_set_irq(s->irq, level);
+}
+
+static inline void ehci_record_interrupt(EHCIState *s, int intr)
+{
+    s->usbsts_pending |= intr;
+}
+
+static inline void ehci_commit_interrupt(EHCIState *s)
+{
+    if (!s->usbsts_pending) {
+        return;
+    }
+    ehci_set_interrupt(s, s->usbsts_pending);
+    s->usbsts_pending = 0;
+}
+
+/* Attach or detach a device on root hub */
+
+static void ehci_attach(USBPort *port)
+{
+    EHCIState *s = port->opaque;
+    uint32_t *portsc = &s->portsc[port->index];
+
+    DPRINTF("ehci_attach invoked for index %d, portsc 0x%x, desc %s\n",
+           port->index, *portsc, port->dev->product_desc);
+
+    *portsc |= PORTSC_CONNECT;
+    *portsc |= PORTSC_CSC;
+
+    /*
+     *  If a high speed device is attached then we own this port(indicated
+     *  by zero in the PORTSC_POWNER bit field) so set the status bit
+     *  and set an interrupt if enabled.
+     */
+    if ( !(*portsc & PORTSC_POWNER)) {
+        ehci_set_interrupt(s, USBSTS_PCD);
+    }
+}
+
+static void ehci_detach(USBPort *port)
+{
+    EHCIState *s = port->opaque;
+    uint32_t *portsc = &s->portsc[port->index];
+
+    DPRINTF("ehci_attach invoked for index %d, portsc 0x%x\n",
+           port->index, *portsc);
+
+    *portsc &= ~PORTSC_CONNECT;
+    *portsc |= PORTSC_CSC;
+
+    /*
+     *  If a high speed device is attached then we own this port(indicated
+     *  by zero in the PORTSC_POWNER bit field) so set the status bit
+     *  and set an interrupt if enabled.
+     */
+    if ( !(*portsc & PORTSC_POWNER)) {
+        ehci_set_interrupt(s, USBSTS_PCD);
+    }
+}
+
+/* 4.1 host controller initialization */
+static void ehci_reset(void *opaque)
+{
+    EHCIState *s = opaque;
+    uint8_t *pci_conf;
+    int i;
+
+    pci_conf = s->dev.config;
+
+    memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
+
+    s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
+    s->usbsts = USBSTS_HALT;
+
+    s->astate = EST_INACTIVE;
+    s->pstate = EST_INACTIVE;
+    s->async_complete = 0;
+    s->isoch_pause = -1;
+    s->attach_poll_counter = 0;
+
+    for(i = 0; i < NB_PORTS; i++) {
+        s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
+
+        if (s->ports[i].dev) {
+            usb_attach(&s->ports[i], s->ports[i].dev);
+        }
+    }
+}
+
+static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr];
+
+    return val;
+}
+
+static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr] | (s->mmio[addr+1] << 8);
+
+    return val;
+}
+
+static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
+          (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
+
+    return val;
+}
+
+static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
+    exit(1);
+}
+
+static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
+    exit(1);
+}
+
+static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
+{
+    uint32_t *portsc = &s->portsc[port];
+    int rwc;
+    USBDevice *dev = s->ports[port].dev;
+
+    DPRINTF("port_status_write: "
+            "PORTSC (port %d) curr %08X new %08X rw-clear %08X rw %08X\n",
+            port, *portsc, val, (val & PORTSC_RWC_MASK), val & PORTSC_RO_MASK);
+
+    rwc = val & PORTSC_RWC_MASK;
+    val &= PORTSC_RO_MASK;
+
+    // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC);
+
+    *portsc &= ~rwc;
+
+    if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
+        DPRINTF("port_status_write: USBTRAN Port %d reset begin\n", port);
+    }
+
+    if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
+        DPRINTF("port_status_write: USBTRAN Port %d reset done\n", port);
+        usb_attach(&s->ports[port], dev);
+
+        // TODO how to handle reset of ports with no device
+        if (dev) {
+            usb_send_msg(dev, USB_MSG_RESET);
+        }
+
+        if (s->ports[port].dev) {
+            DPRINTF("port_status_write: "
+                    "Device was connected before reset, clearing CSC bit\n");
+            *portsc &= ~PORTSC_CSC;
+        }
+
+        /*  Table 2.16 Set the enable bit(and enable bit change) to indicate
+         *  to SW that this port has a high speed device attached
+         *
+         *  TODO - when to disable?
+         */
+        val |= PORTSC_PED;
+        val |= PORTSC_PEDC;
+    }
+
+    *portsc &= ~PORTSC_RO_MASK;
+    *portsc |= val;
+    DPRINTF("port_status_write: Port %d status set to 0x%08x\n", port, *portsc);
+}
+
+static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    EHCIState *s = ptr;
+    int i;
+#if EHCI_DEBUG
+    const char *str;
+#endif
+
+    /* Only aligned reads are allowed on OHCI */
+    if (addr & 3) {
+        fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
+                TARGET_FMT_plx "\n", addr);
+        return;
+    }
+
+    if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
+        handle_port_status_write(s, (addr-PORTSC)/4, val);
+        return;
+    }
+
+    if (addr < OPREGBASE) {
+        fprintf(stderr, "usb-ehci: write attempt to read-only register"
+                TARGET_FMT_plx "\n", addr);
+        return;
+    }
+
+
+    /* Do any register specific pre-write processing here.  */
+#if EHCI_DEBUG
+    str = addr2str((unsigned) addr);
+#endif
+    switch(addr) {
+    case USBCMD:
+        DPRINTF("ehci_mem_writel: USBCMD val=0x%08X, current cmd=0x%08X\n",
+                val, s->usbcmd);
+
+        if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
+            DPRINTF("ehci_mem_writel: %s run, clear halt\n", str);
+            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
+            SET_LAST_RUN_CLOCK(s);
+            s->usbsts &= ~USBSTS_HALT;
+        }
+
+        if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            DPRINTF("                         ** STOP **\n");
+            qemu_del_timer(s->frame_timer);
+            // TODO - should finish out some stuff before setting halt
+            s->usbsts |= USBSTS_HALT;
+        }
+
+        if (val & USBCMD_HCRESET) {
+            DPRINTF("ehci_mem_writel: %s run, resetting\n", str);
+            ehci_reset(s);
+            val &= ~USBCMD_HCRESET;
+        }
+
+        /* not supporting dynamic frame list size at the moment */
+        if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
+            fprintf(stderr, "attempt to set frame list size -- value %d\n",
+                    val & USBCMD_FLS);
+            val &= ~USBCMD_FLS;
+        }
+#if EHCI_DEBUG
+        if ((val & USBCMD_PSE) && !(s->usbcmd & USBCMD_PSE)) {
+            DPRINTF("periodic scheduling enabled\n");
+        }
+        if (!(val & USBCMD_PSE) && (s->usbcmd & USBCMD_PSE)) {
+            DPRINTF("periodic scheduling disabled\n");
+        }
+        if ((val & USBCMD_ASE) && !(s->usbcmd & USBCMD_ASE)) {
+            DPRINTF("asynchronous scheduling enabled\n");
+        }
+        if (!(val & USBCMD_ASE) && (s->usbcmd & USBCMD_ASE)) {
+            DPRINTF("asynchronous scheduling disabled\n");
+        }
+        if ((val & USBCMD_IAAD) && !(s->usbcmd & USBCMD_IAAD)) {
+            DPRINTF("doorbell request received\n");
+        }
+        if ((val & USBCMD_LHCR) && !(s->usbcmd & USBCMD_LHCR)) {
+            DPRINTF("light host controller reset received\n");
+        }
+        if ((val & USBCMD_ITC) != (s->usbcmd & USBCMD_ITC)) {
+            DPRINTF("interrupt threshold control set to %x\n",
+                    (val & USBCMD_ITC)>>USBCMD_ITC_SH);
+        }
+#endif
+        break;
+
+
+    case USBSTS:
+        val &= USBSTS_RO_MASK;              // bits 6 thru 31 are RO
+        DPRINTF("ehci_mem_writel: %s RWC set to 0x%08X\n", str, val);
+
+        val = (s->usbsts &= ~val);         // bits 0 thru 5 are R/WC
+
+        DPRINTF("ehci_mem_writel: %s updating interrupt condition\n", str);
+        ehci_set_interrupt(s, 0);
+        break;
+
+
+    case USBINTR:
+        val &= USBINTR_MASK;
+        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+        break;
+
+    case FRINDEX:
+        s->sofv = val >> 3;
+        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+        break;
+
+    case CONFIGFLAG:
+        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+        val &= 0x1;
+        if (val) {
+            for(i = 0; i < NB_PORTS; i++)
+                s->portsc[i] &= ~PORTSC_POWNER;
+        }
+        break;
+
+    case PERIODICLISTBASE:
+        if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            fprintf(stderr,
+              "ehci: PERIODIC list base register set while periodic schedule\n"
+              "      is enabled and HC is enabled\n");
+        }
+        DPRINTF("ehci_mem_writel: P-LIST BASE set to 0x%08X\n", val);
+        break;
+
+    case ASYNCLISTADDR:
+        if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            fprintf(stderr,
+              "ehci: ASYNC list address register set while async schedule\n"
+              "      is enabled and HC is enabled\n");
+        }
+        DPRINTF("ehci_mem_writel: A-LIST ADDR set to 0x%08X\n", val);
+        break;
+    }
+
+    *(uint32_t *)(&s->mmio[addr]) = val;
+}
+
+
+// TODO : Put in common header file, duplication from usb-ohci.c
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        cpu_physical_memory_rw(addr,(uint8_t *)buf, sizeof(*buf), 0);
+        *buf = le32_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint32_t tmp = cpu_to_le32(*buf);
+        cpu_physical_memory_rw(addr,(uint8_t *)&tmp, sizeof(tmp), 1);
+    }
+
+    return 1;
+}
+
+// 4.10.2
+
+static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
+{
+    int i;
+    int dtoggle;
+    int ping;
+    int eps;
+    int reload;
+
+    // remember values in fields to preserve in qh after overlay
+
+    dtoggle = qh->token & QTD_TOKEN_DTOGGLE;
+    ping    = qh->token & QTD_TOKEN_PING;
+
+    DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->current_qtd,
+            ehci->qtdaddr);
+    qh->current_qtd = ehci->qtdaddr;
+    qh->next_qtd    = qtd->next;
+    qh->altnext_qtd = qtd->altnext;
+    qh->token       = qtd->token;
+
+
+    eps = get_field(qh->epchar, QH_EPCHAR_EPS);
+    if (eps == EHCI_QH_EPS_HIGH) {
+        qh->token &= ~QTD_TOKEN_PING;
+        qh->token |= ping;
+    }
+
+    reload = get_field(qh->epchar, QH_EPCHAR_RL);
+    set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+
+    for (i = 0; i < 5; i++) {
+        qh->bufptr[i] = qtd->bufptr[i];
+    }
+
+    if (!(qh->epchar & QH_EPCHAR_DTC)) {
+        // preserve QH DT bit
+        qh->token &= ~QTD_TOKEN_DTOGGLE;
+        qh->token |= dtoggle;
+    }
+
+    qh->bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
+    qh->bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
+
+    put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+    return 0;
+}
+
+static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw)
+{
+    int bufpos = 0;
+    int cpage, offset;
+    uint32_t head;
+    uint32_t tail;
+
+
+    if (!bytes) {
+        return 0;
+    }
+
+    cpage = get_field(qh->token, QTD_TOKEN_CPAGE);
+    if (cpage > 4) {
+        fprintf(stderr, "cpage out of range (%d)\n", cpage);
+        return USB_RET_PROCERR;
+    }
+
+    offset = qh->bufptr[0] & ~QTD_BUFPTR_MASK;
+    DPRINTF("ehci_buffer_rw: %sing %d bytes %08x cpage %d offset %d\n",
+           rw ? "writ" : "read", bytes, qh->bufptr[0], cpage, offset);
+
+    do {
+        /* start and end of this page */
+        head = qh->bufptr[cpage] & QTD_BUFPTR_MASK;
+        tail = head + ~QTD_BUFPTR_MASK + 1;
+        /* add offset into page */
+        head |= offset;
+
+        if (bytes <= (tail - head)) {
+            tail = head + bytes;
+        }
+
+        DPRINTF("DATA %s cpage:%d head:%08X tail:%08X target:%08X\n",
+                rw ? "WRITE" : "READ ", cpage, head, tail, bufpos);
+
+        cpu_physical_memory_rw(head, &buffer[bufpos], tail - head, rw);
+
+        bufpos += (tail - head);
+        bytes -= (tail - head);
+
+        if (bytes > 0) {
+            cpage++;
+            offset = 0;
+        }
+    } while (bytes > 0);
+
+    /* save cpage */
+    set_field(&qh->token, cpage, QTD_TOKEN_CPAGE);
+
+    /* save offset into cpage */
+    offset = tail - head;
+    qh->bufptr[0] &= ~QTD_BUFPTR_MASK;
+    qh->bufptr[0] |= offset;
+
+    return 0;
+}
+
+static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet)
+{
+    EHCIState *ehci = container_of(packet, EHCIState, usb_packet);
+
+    DPRINTF("Async packet complete\n");
+    ehci->async_complete = 1;
+    ehci->exec_status = packet->len;
+}
+
+static int ehci_execute_complete(EHCIState *ehci, EHCIqh *qh, int ret)
+{
+    int c_err, reload;
+
+    if (ret == USB_RET_ASYNC && !ehci->async_complete) {
+        DPRINTF("not done yet\n");
+        return ret;
+    }
+
+    ehci->async_complete = 0;
+
+    DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
+            ehci->qhaddr, qh->next, ehci->qtdaddr, ret);
+
+    if (ret < 0) {
+err:
+        /* TO-DO: put this is in a function that can be invoked below as well */
+        c_err = get_field(qh->token, QTD_TOKEN_CERR);
+        c_err--;
+        set_field(&qh->token, c_err, QTD_TOKEN_CERR);
+
+        switch(ret) {
+        case USB_RET_NODEV:
+            fprintf(stderr, "USB no device\n");
+            break;
+        case USB_RET_STALL:
+            fprintf(stderr, "USB stall\n");
+            qh->token |= QTD_TOKEN_HALT;
+            ehci_record_interrupt(ehci, USBSTS_ERRINT);
+            break;
+        case USB_RET_NAK:
+            /* 4.10.3 */
+            reload = get_field(qh->epchar, QH_EPCHAR_RL);
+            if ((ehci->pid == USB_TOKEN_IN) && reload) {
+                int nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+                nakcnt--;
+                set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+            } else if (!reload) {
+                return USB_RET_NAK;
+            }
+            break;
+        case USB_RET_BABBLE:
+            fprintf(stderr, "USB babble TODO\n");
+            qh->token |= QTD_TOKEN_BABBLE;
+            ehci_record_interrupt(ehci, USBSTS_ERRINT);
+            break;
+        default:
+            fprintf(stderr, "USB invalid response %d to handle\n", ret);
+            /* TO-DO: transaction error */
+            ret = USB_RET_PROCERR;
+            break;
+        }
+    } else {
+        // DPRINTF("Short packet condition\n");
+        // TODO check 4.12 for splits
+
+        if ((ret > ehci->tbytes) && (ehci->pid == USB_TOKEN_IN)) {
+            ret = USB_RET_BABBLE;
+            goto err;
+        }
+
+        if (ehci->tbytes && ehci->pid == USB_TOKEN_IN) {
+            if (ehci_buffer_rw(ehci->buffer, qh, ret, 1) != 0) {
+                return USB_RET_PROCERR;
+            }
+            ehci->tbytes -= ret;
+        } else {
+            ehci->tbytes = 0;
+        }
+
+        DPRINTF("updating tbytes to %d\n", ehci->tbytes);
+        set_field(&qh->token, ehci->tbytes, QTD_TOKEN_TBYTES);
+    }
+
+    qh->token ^= QTD_TOKEN_DTOGGLE;
+    qh->token &= ~QTD_TOKEN_ACTIVE;
+
+    if ((ret >= 0) && (qh->token & QTD_TOKEN_IOC)) {
+        ehci_record_interrupt(ehci, USBSTS_INT);
+    }
+
+    return ret;
+}
+
+// 4.10.3
+
+static int ehci_execute(EHCIState *ehci, EHCIqh *qh)
+{
+    USBPort *port;
+    USBDevice *dev;
+    int ret;
+    int i;
+    int endp;
+    int devadr;
+
+    if ( !(qh->token & QTD_TOKEN_ACTIVE)) {
+        fprintf(stderr, "Attempting to execute inactive QH\n");
+        return USB_RET_PROCERR;
+    }
+
+    ehci->tbytes = (qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+    if (ehci->tbytes > BUFF_SIZE) {
+        fprintf(stderr, "Request for more bytes than allowed\n");
+        return USB_RET_PROCERR;
+    }
+
+    ehci->pid = (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
+    switch(ehci->pid) {
+        case 0: ehci->pid = USB_TOKEN_OUT; break;
+        case 1: ehci->pid = USB_TOKEN_IN; break;
+        case 2: ehci->pid = USB_TOKEN_SETUP; break;
+        default: fprintf(stderr, "bad token\n"); break;
+    }
+
+    if ((ehci->tbytes && ehci->pid != USB_TOKEN_IN) &&
+        (ehci_buffer_rw(ehci->buffer, qh, ehci->tbytes, 0) != 0)) {
+        return USB_RET_PROCERR;
+    }
+
+    endp = get_field(qh->epchar, QH_EPCHAR_EP);
+    devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
+
+    ret = USB_RET_NODEV;
+
+    // TO-DO: associating device with ehci port
+    for(i = 0; i < NB_PORTS; i++) {
+        port = &ehci->ports[i];
+        dev = port->dev;
+
+        // TODO sometime we will also need to check if we are the port owner
+
+        if (!(ehci->portsc[i] &(PORTSC_CONNECT))) {
+            DPRINTF("Port %d, no exec, not connected(%08X)\n",
+                    i, ehci->portsc[i]);
+            continue;
+        }
+
+        ehci->usb_packet.pid = ehci->pid;
+        ehci->usb_packet.devaddr = devadr;
+        ehci->usb_packet.devep = endp;
+        ehci->usb_packet.data = ehci->buffer;
+        ehci->usb_packet.len = ehci->tbytes;
+
+        ret = usb_handle_packet(dev, &ehci->usb_packet);
+
+        DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
+                ehci->qhaddr, qh->next, ehci->qtdaddr, ehci->pid,
+                ehci->usb_packet.len, ehci->tbytes, endp, ret);
+
+        if (ret != USB_RET_NODEV) {
+            break;
+        }
+    }
+
+    if (ret > BUFF_SIZE) {
+        fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
+        return USB_RET_PROCERR;
+    }
+
+    if (ret == USB_RET_ASYNC) {
+        ehci->async_complete = 0;
+    }
+
+    return ret;
+}
+
+/*  4.7.2
+ */
+
+static int ehci_process_itd(EHCIState *ehci,
+                            EHCIitd *itd)
+{
+    USBPort *port;
+    USBDevice *dev;
+    int ret;
+    int i, j;
+    int ptr;
+    int pid;
+    int pg;
+    int len;
+    int dir;
+    int devadr;
+    int endp;
+    int maxpkt;
+
+    dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
+    devadr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
+    endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
+    maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
+
+    for(i = 0; i < 8; i++) {
+        if (itd->transact[i] & ITD_XACT_ACTIVE) {
+            DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n",
+                    ehci->frindex >> 3, i);
+
+            pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
+            ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) |
+                (itd->transact[i] & ITD_XACT_OFFSET_MASK);
+            len = get_field(itd->transact[i], ITD_XACT_LENGTH);
+
+            if (len > BUFF_SIZE) {
+                return USB_RET_PROCERR;
+            }
+
+            DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len);
+
+            if (!dir) {
+                cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0);
+                pid = USB_TOKEN_OUT;
+            } else
+                pid = USB_TOKEN_IN;
+
+            ret = USB_RET_NODEV;
+
+            for (j = 0; j < NB_PORTS; j++) {
+                port = &ehci->ports[j];
+                dev = port->dev;
+
+                // TODO sometime we will also need to check if we are the port owner
+
+                if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
+                    DPRINTF("Port %d, no exec, not connected(%08X)\n",
+                            j, ehci->portsc[j]);
+                    continue;
+                }
+
+                ehci->usb_packet.pid = ehci->pid;
+                ehci->usb_packet.devaddr = devadr;
+                ehci->usb_packet.devep = endp;
+                ehci->usb_packet.data = ehci->buffer;
+                ehci->usb_packet.len = len;
+
+                DPRINTF("calling usb_handle_packet\n");
+                ret = usb_handle_packet(dev, &ehci->usb_packet);
+
+                if (ret != USB_RET_NODEV) {
+                    break;
+                }
+            }
+
+            /*  In isoch, there is no facility to indicate a NAK so let's
+             *  instead just complete a zero-byte transaction.  Setting
+             *  DBERR seems too draconian.
+             */
+
+            if (ret == USB_RET_NAK) {
+                if (ehci->isoch_pause > 0) {
+                    DPRINTF("ISOCH: received a NAK but paused so returning\n");
+                    ehci->isoch_pause--;
+                    return 0;
+                } else if (ehci->isoch_pause == -1) {
+                    DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
+                    // Pause frindex for up to 50 msec waiting for data from
+                    // remote
+                    ehci->isoch_pause = 50;
+                    return 0;
+                } else {
+                    DPRINTF("ISOCH: isoch pause timeout! return 0\n");
+                    ret = 0;
+                }
+            } else {
+                DPRINTF("ISOCH: received ACK, clearing pause\n");
+                ehci->isoch_pause = -1;
+            }
+
+            if (ret >= 0) {
+                itd->transact[i] &= ~ITD_XACT_ACTIVE;
+
+                if (itd->transact[i] & ITD_XACT_IOC) {
+                    ehci_record_interrupt(ehci, USBSTS_INT);
+                }
+            }
+
+            if (ret >= 0 && dir) {
+                cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1);
+
+                if (ret != len) {
+                    DPRINTF("ISOCH IN expected %d, got %d\n",
+                            len, ret);
+                    set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+/*  This state is the entry point for asynchronous schedule
+ *  processing.  Entry here consitutes a EHCI start event state (4.8.5)
+ */
+static int ehci_state_waitlisthead(EHCIState *ehci,  int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int i = 0;
+    int again = 0;
+    uint32_t entry = ehci->asynclistaddr;
+
+    /* set reclamation flag at start event (4.8.6) */
+    if (async) {
+        ehci->usbsts |= USBSTS_REC;
+    }
+
+    /*  Find the head of the list (4.9.1.1) */
+    for(i = 0; i < MAX_QH; i++) {
+        get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+        if (qh->epchar & QH_EPCHAR_H) {
+            DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n",
+                       entry);
+            if (async) {
+                entry |= (NLPTR_TYPE_QH << 1);
+            }
+
+            ehci->fetch_addr = entry;
+            *state = EST_FETCHENTRY;
+            again = 1;
+            goto out;
+        }
+
+        DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n",
+                   entry);
+        entry = qh->next;
+        if (entry == ehci->asynclistaddr) {
+            DPRINTF("WAITLISTHEAD: reached beginning of QH list\n");
+            break;
+        }
+    }
+
+    /* no head found for list. */
+
+    *state = EST_ACTIVE;
+
+out:
+    return again;
+}
+
+
+/*  This state is the entry point for periodic schedule processing as
+ *  well as being a continuation state for async processing.
+ */
+static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
+{
+    int again = 0;
+    uint32_t entry = ehci->fetch_addr;
+
+#if EHCI_DEBUG == 0
+    if (qemu_get_clock_ns(vm_clock) / 1000 >= ehci->frame_end_usec) {
+        if (async) {
+            DPRINTF("FETCHENTRY: FRAME timer elapsed, exit state machine\n");
+            goto out;
+        } else {
+            DPRINTF("FETCHENTRY: WARNING "
+                    "- frame timer elapsed during periodic\n");
+        }
+    }
+#endif
+    if (entry < 0x1000) {
+        DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry);
+        *state = EST_ACTIVE;
+        goto out;
+    }
+
+    /* section 4.8, only QH in async schedule */
+    if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) {
+        fprintf(stderr, "non queue head request in async schedule\n");
+        return -1;
+    }
+
+    switch (NLPTR_TYPE_GET(entry)) {
+    case NLPTR_TYPE_QH:
+        DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", entry);
+        *state = EST_FETCHQH;
+        ehci->qhaddr = entry;
+        again = 1;
+        break;
+
+    case NLPTR_TYPE_ITD:
+        DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", entry);
+        *state = EST_FETCHITD;
+        ehci->itdaddr = entry;
+        again = 1;
+        break;
+
+    default:
+        // TODO: handle siTD and FSTN types
+        fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
+                "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
+        return -1;
+    }
+
+out:
+    return again;
+}
+
+static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int reload;
+    int again = 0;
+
+    get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+    if (async && (qh->epchar & QH_EPCHAR_H)) {
+
+        /*  EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
+        if (ehci->usbsts & USBSTS_REC) {
+            ehci->usbsts &= ~USBSTS_REC;
+        } else {
+            DPRINTF("FETCHQH:  QH 0x%08x. H-bit set, reclamation status reset"
+                       " - done processing\n", ehci->qhaddr);
+            *state = EST_ACTIVE;
+            goto out;
+        }
+    }
+
+#if EHCI_DEBUG
+    if (ehci->qhaddr != qh->next) {
+    DPRINTF("FETCHQH:  QH 0x%08x (h %x halt %x active %x) next 0x%08x\n",
+               ehci->qhaddr,
+               qh->epchar & QH_EPCHAR_H,
+               qh->token & QTD_TOKEN_HALT,
+               qh->token & QTD_TOKEN_ACTIVE,
+               qh->next);
+    }
+#endif
+
+    reload = get_field(qh->epchar, QH_EPCHAR_RL);
+    if (reload) {
+        DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload);
+        set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+    }
+
+    if (qh->token & QTD_TOKEN_HALT) {
+        DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n");
+        *state = EST_HORIZONTALQH;
+        again = 1;
+
+    } else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) {
+        DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n");
+        ehci->qtdaddr = qh->current_qtd;
+        *state = EST_FETCHQTD;
+        again = 1;
+
+    } else {
+        /*  EHCI spec version 1.0 Section 4.10.2 */
+        DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n");
+        *state = EST_ADVANCEQUEUE;
+        again = 1;
+    }
+
+out:
+    return again;
+}
+
+static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state)
+{
+    EHCIitd itd;
+
+    get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
+               sizeof(EHCIitd) >> 2);
+    DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n",
+               ehci->itdaddr, itd.next);
+
+    if (ehci_process_itd(ehci, &itd) != 0) {
+        return -1;
+    }
+
+    put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd,
+                sizeof(EHCIitd) >> 2);
+    ehci->fetch_addr = itd.next;
+    *state = EST_FETCHENTRY;
+
+    return 1;
+}
+
+/* Section 4.10.2 - paragraph 3 */
+static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
+{
+#if 0
+    /* TO-DO: 4.10.2 - paragraph 2
+     * if I-bit is set to 1 and QH is not active
+     * go to horizontal QH
+     */
+    if (I-bit set) {
+        *state = EST_HORIZONTALQH;
+        goto out;
+    }
+#endif
+
+    /*
+     * want data and alt-next qTD is valid
+     */
+    if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
+        (ehci->qh.altnext_qtd > 0x1000) &&
+        (NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) {
+        DPRINTF_ST("ADVQUEUE: goto alt next qTD. "
+                   "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
+                   ehci->qh.current_qtd, ehci->qh.altnext_qtd,
+                   ehci->qh.next_qtd, ehci->qh.next);
+        ehci->qtdaddr = ehci->qh.altnext_qtd;
+        *state = EST_FETCHQTD;
+
+    /*
+     *  next qTD is valid
+     */
+    } else if ((ehci->qh.next_qtd > 0x1000) &&
+               (NLPTR_TBIT(ehci->qh.next_qtd) == 0)) {
+        DPRINTF_ST("ADVQUEUE: next qTD. "
+                   "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
+                   ehci->qh.current_qtd, ehci->qh.altnext_qtd,
+                   ehci->qh.next_qtd, ehci->qh.next);
+        ehci->qtdaddr = ehci->qh.next_qtd;
+        *state = EST_FETCHQTD;
+
+    /*
+     *  no valid qTD, try next QH
+     */
+    } else {
+        DPRINTF_ST("ADVQUEUE: go to horizontal QH\n");
+        *state = EST_HORIZONTALQH;
+    }
+
+    return 1;
+}
+
+/* Section 4.10.2 - paragraph 4 */
+static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state)
+{
+    EHCIqtd *qtd = &ehci->qtd;
+    int again = 0;
+
+    get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2);
+
+    if (qtd->token & QTD_TOKEN_ACTIVE) {
+        *state = EST_EXECUTE;
+        again = 1;
+    } else {
+        *state = EST_HORIZONTALQH;
+        again = 1;
+    }
+
+    return again;
+}
+
+static int ehci_state_horizqh(EHCIState *ehci, int async, int *state)
+{
+    int again = 0;
+
+    if (ehci->fetch_addr != ehci->qh.next) {
+        ehci->fetch_addr = ehci->qh.next;
+        *state = EST_FETCHENTRY;
+        again = 1;
+    } else {
+        *state = EST_ACTIVE;
+    }
+
+    return again;
+}
+
+static int ehci_state_execute(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    EHCIqtd *qtd = &ehci->qtd;
+    int again = 0;
+    int reload, nakcnt;
+    int smask;
+
+    if (async) {
+        DPRINTF_ST(">>>>> ASYNC STATE MACHINE execute QH 0x%08x, QTD 0x%08x\n",
+                  ehci->qhaddr, ehci->qtdaddr);
+    } else {
+        DPRINTF_ST(">>>>> PERIODIC STATE MACHINE execute\n");
+    }
+
+    if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) {
+        return -1;
+    }
+
+    smask = get_field(qh->epcap, QH_EPCAP_SMASK);
+
+    if (!smask) {
+        reload = get_field(qh->epchar, QH_EPCHAR_RL);
+        nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+        if (reload && !nakcnt) {
+            DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n");
+            *state = EST_HORIZONTALQH;
+            again = 1;
+            goto out;
+        }
+    }
+
+    // TODO verify enough time remains in the uframe as in 4.4.1.1
+    // TODO write back ptr to async list when done or out of time
+    // TODO Windows does not seem to ever set the MULT field
+
+    if (!async) {
+        int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+        if (!transactCtr) {
+            DPRINTF("ZERO transactctr for int qh, go HORIZ\n");
+            *state = EST_HORIZONTALQH;
+            again = 1;
+            goto out;
+        }
+    }
+
+    if (async) {
+        ehci->usbsts |= USBSTS_REC;
+    }
+
+    ehci->exec_status = ehci_execute(ehci, qh);
+    if (ehci->exec_status == USB_RET_PROCERR) {
+        again = -1;
+        goto out;
+    }
+    *state = EST_EXECUTING;
+
+    if (ehci->exec_status != USB_RET_ASYNC) {
+        again = 1;
+    }
+
+out:
+    return again;
+}
+
+static int ehci_state_executing(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int again = 0;
+    int reload, nakcnt;
+
+    ehci->exec_status = ehci_execute_complete(ehci, qh, ehci->exec_status);
+    if (ehci->exec_status == USB_RET_ASYNC) {
+        goto out;
+    }
+    if (ehci->exec_status == USB_RET_PROCERR) {
+        again = -1;
+        goto out;
+    }
+
+    // 4.10.3
+    if (!async) {
+        int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+        transactCtr--;
+        set_field(&qh->epcap, transactCtr, QH_EPCAP_MULT);
+        // 4.10.3, bottom of page 82, should exit this state when transaction
+        // counter decrements to 0
+    }
+
+
+    reload = get_field(qh->epchar, QH_EPCHAR_RL);
+    if (reload) {
+        nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+        if (ehci->exec_status == USB_RET_NAK) {
+            if (nakcnt) {
+                nakcnt--;
+            }
+            DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n",
+                    nakcnt);
+        } else {
+            nakcnt = reload;
+            DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n",
+                       nakcnt);
+        }
+        set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+    }
+
+    /*
+     *  Write the qh back to guest physical memory.  This step isn't
+     *  in the EHCI spec but we need to do it since we don't share
+     *  physical memory with our guest VM.
+     */
+
+    DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, next 0x%x\n",
+              ehci->qhaddr, qh->next);
+    put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+    /* 4.10.5 */
+    if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) {
+        *state = EST_HORIZONTALQH;
+    } else {
+        *state = EST_WRITEBACK;
+    }
+
+    again = 1;
+
+out:
+    return again;
+}
+
+
+static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int again = 0;
+
+    /*  Write back the QTD from the QH area */
+    DPRINTF_ST("WRITEBACK: write QTD to VM memory\n");
+    put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd,
+                sizeof(EHCIqtd) >> 2);
+
+    /* TODO confirm next state.  For now, keep going if async
+     * but stop after one qtd if periodic
+     */
+    //if (async) {
+        *state = EST_ADVANCEQUEUE;
+        again = 1;
+    //} else {
+    //    *state = EST_ACTIVE;
+    //}
+    return again;
+}
+
+/*
+ * This is the state machine that is common to both async and periodic
+ */
+
+static int ehci_advance_state(EHCIState *ehci,
+                              int async,
+                              int state)
+{
+    int again;
+    int iter = 0;
+
+    do {
+        if (state == EST_FETCHQH) {
+            iter++;
+            /* if we are roaming a lot of QH without executing a qTD
+             * something is wrong with the linked list. TO-DO: why is
+             * this hack needed?
+             */
+            if (iter > MAX_ITERATIONS) {
+                DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n");
+                state = EST_ACTIVE;
+                break;
+            }
+        }
+        switch(state) {
+        case EST_WAITLISTHEAD:
+            again = ehci_state_waitlisthead(ehci, async, &state);
+            break;
+
+        case EST_FETCHENTRY:
+            again = ehci_state_fetchentry(ehci, async, &state);
+            break;
+
+        case EST_FETCHQH:
+            again = ehci_state_fetchqh(ehci, async, &state);
+            break;
+
+        case EST_FETCHITD:
+            again = ehci_state_fetchitd(ehci, async, &state);
+            break;
+
+        case EST_ADVANCEQUEUE:
+            again = ehci_state_advqueue(ehci, async, &state);
+            break;
+
+        case EST_FETCHQTD:
+            again = ehci_state_fetchqtd(ehci, async, &state);
+            break;
+
+        case EST_HORIZONTALQH:
+            again = ehci_state_horizqh(ehci, async, &state);
+            break;
+
+        case EST_EXECUTE:
+            iter = 0;
+            again = ehci_state_execute(ehci, async, &state);
+            break;
+
+        case EST_EXECUTING:
+            again = ehci_state_executing(ehci, async, &state);
+            break;
+
+        case EST_WRITEBACK:
+            again = ehci_state_writeback(ehci, async, &state);
+            break;
+
+        default:
+            fprintf(stderr, "Bad state!\n");
+            again = -1;
+            break;
+        }
+
+        if (again < 0) {
+            fprintf(stderr, "processing error - resetting ehci HC\n");
+            ehci_reset(ehci);
+            again = 0;
+        }
+    }
+    while (again);
+
+    ehci_commit_interrupt(ehci);
+    return state;
+}
+
+static void ehci_advance_async_state(EHCIState *ehci)
+{
+    EHCIqh qh;
+    int state = ehci->astate;
+
+    switch(state) {
+    case EST_INACTIVE:
+        if (!(ehci->usbcmd & USBCMD_ASE)) {
+            break;
+        }
+        ehci->usbsts |= USBSTS_ASS;
+        ehci->astate = EST_ACTIVE;
+        // No break, fall through to ACTIVE
+
+    case EST_ACTIVE:
+        if ( !(ehci->usbcmd & USBCMD_ASE)) {
+            ehci->usbsts &= ~USBSTS_ASS;
+            ehci->astate = EST_INACTIVE;
+            break;
+        }
+
+        /* If the doorbell is set, the guest wants to make a change to the
+         * schedule. The host controller needs to release cached data.
+         * (section 4.8.2)
+         */
+        if (ehci->usbcmd & USBCMD_IAAD) {
+            DPRINTF("ASYNC: doorbell request acknowledged\n");
+            ehci->usbcmd &= ~USBCMD_IAAD;
+            ehci_set_interrupt(ehci, USBSTS_IAA);
+            break;
+        }
+
+        /* make sure guest has acknowledged */
+        /* TO-DO: is this really needed? */
+        if (ehci->usbsts & USBSTS_IAA) {
+            DPRINTF("IAA status bit still set.\n");
+            break;
+        }
+
+        DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n",
+                ehci->asynclistaddr);
+        /* check that address register has been set */
+        if (ehci->asynclistaddr == 0) {
+            break;
+        }
+
+        state = EST_WAITLISTHEAD;
+        /* fall through */
+
+    case EST_FETCHENTRY:
+        /* fall through */
+
+    case EST_EXECUTING:
+        get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh,
+                   sizeof(EHCIqh) >> 2);
+        ehci->astate = ehci_advance_state(ehci, 1, state);
+        break;
+
+    default:
+        /* this should only be due to a developer mistake */
+        fprintf(stderr, "ehci: Bad asynchronous state %d. "
+                "Resetting to active\n", ehci->astate);
+        ehci->astate = EST_ACTIVE;
+    }
+}
+
+static void ehci_advance_periodic_state(EHCIState *ehci)
+{
+    uint32_t entry;
+    uint32_t list;
+
+    // 4.6
+
+    switch(ehci->pstate) {
+    case EST_INACTIVE:
+        if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
+            DPRINTF("PERIODIC going active\n");
+            ehci->usbsts |= USBSTS_PSS;
+            ehci->pstate = EST_ACTIVE;
+            // No break, fall through to ACTIVE
+        } else
+            break;
+
+    case EST_ACTIVE:
+        if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
+            DPRINTF("PERIODIC going inactive\n");
+            ehci->usbsts &= ~USBSTS_PSS;
+            ehci->pstate = EST_INACTIVE;
+            break;
+        }
+
+        list = ehci->periodiclistbase & 0xfffff000;
+        /* check that register has been set */
+        if (list == 0) {
+            break;
+        }
+        list |= ((ehci->frindex & 0x1ff8) >> 1);
+
+        cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0);
+        entry = le32_to_cpu(entry);
+
+        DPRINTF("PERIODIC state adv fr=%d.  [%08X] -> %08X\n",
+                ehci->frindex / 8, list, entry);
+        ehci->fetch_addr = entry;
+        ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY);
+        break;
+
+    case EST_EXECUTING:
+        DPRINTF("PERIODIC state adv for executing\n");
+        ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING);
+        break;
+
+    default:
+        /* this should only be due to a developer mistake */
+        fprintf(stderr, "ehci: Bad periodic state %d. "
+                "Resetting to active\n", ehci->pstate);
+        ehci->pstate = EST_ACTIVE;
+    }
+}
+
+static void ehci_frame_timer(void *opaque)
+{
+    EHCIState *ehci = opaque;
+    int64_t expire_time, t_now;
+    int usec_elapsed;
+    int frames;
+    int usec_now;
+    int i;
+    int skipped_frames = 0;
+
+
+    t_now = qemu_get_clock_ns(vm_clock);
+    expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+    if (expire_time == t_now) {
+        expire_time++;
+    }
+
+    usec_now = t_now / 1000;
+    usec_elapsed = usec_now - ehci->last_run_usec;
+    frames = usec_elapsed / FRAME_TIMER_USEC;
+    ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC - 10;
+
+    for (i = 0; i < frames; i++) {
+        if ( !(ehci->usbsts & USBSTS_HALT)) {
+            if (ehci->isoch_pause <= 0) {
+                ehci->frindex += 8;
+            }
+
+            if (ehci->frindex > 0x00001fff) {
+                ehci->frindex = 0;
+                ehci_set_interrupt(ehci, USBSTS_FLR);
+            }
+
+            ehci->sofv = (ehci->frindex - 1) >> 3;
+            ehci->sofv &= 0x000003ff;
+        }
+
+        if (frames - i > 10) {
+            skipped_frames++;
+        } else {
+            // TODO could this cause periodic frames to get skipped if async
+            // active?
+            if (ehci->astate != EST_EXECUTING) {
+                ehci_advance_periodic_state(ehci);
+            }
+        }
+
+        ehci->last_run_usec += FRAME_TIMER_USEC;
+    }
+
+#if 0
+    if (skipped_frames) {
+        DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
+    }
+#endif
+
+    /*  Async is not inside loop since it executes everything it can once
+     *  called
+     */
+    if (ehci->pstate != EST_EXECUTING) {
+        ehci_advance_async_state(ehci);
+    }
+
+    qemu_mod_timer(ehci->frame_timer, expire_time);
+}
+
+static CPUReadMemoryFunc *ehci_readfn[3]={
+    ehci_mem_readb,
+    ehci_mem_readw,
+    ehci_mem_readl
+};
+
+static CPUWriteMemoryFunc *ehci_writefn[3]={
+    ehci_mem_writeb,
+    ehci_mem_writew,
+    ehci_mem_writel
+};
+
+static void ehci_map(PCIDevice *pci_dev, int region_num,
+                     pcibus_t addr, pcibus_t size, int type)
+{
+    EHCIState *s =(EHCIState *)pci_dev;
+
+    DPRINTF("ehci_map: region %d, addr %08" PRIx64 ", size %" PRId64 ", s->mem %08X\n",
+            region_num, addr, size, s->mem);
+    s->mem_base = addr;
+    cpu_register_physical_memory(addr, size, s->mem);
+}
+
+static int usb_ehci_initfn(PCIDevice *dev);
+
+static USBPortOps ehci_port_ops = {
+    .attach = ehci_attach,
+    .detach = ehci_detach,
+    .complete = ehci_async_complete_packet,
+};
+
+static PCIDeviceInfo ehci_info = {
+    .qdev.name    = "usb-ehci",
+    .qdev.size    = sizeof(EHCIState),
+    .init         = usb_ehci_initfn,
+};
+
+static int usb_ehci_initfn(PCIDevice *dev)
+{
+    EHCIState *s = DO_UPCAST(EHCIState, dev, dev);
+    uint8_t *pci_conf = s->dev.config;
+    int i;
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82801D);
+    pci_set_byte(&pci_conf[PCI_REVISION_ID], 0x10);
+    pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
+    pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
+    pci_set_byte(&pci_conf[PCI_HEADER_TYPE], PCI_HEADER_TYPE_NORMAL);
+
+    /* capabilities pointer */
+    pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
+    //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
+
+    pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrupt pin 3
+    pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
+    pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
+
+    // pci_conf[0x50] = 0x01; // power management caps
+
+    pci_set_byte(&pci_conf[0x60], 0x20);  // spec release number (2.1.4)
+    pci_set_byte(&pci_conf[0x61], 0x20);  // frame length adjustment (2.1.5)
+    pci_set_word(&pci_conf[0x62], 0x00);  // port wake up capability (2.1.6)
+
+    pci_conf[0x64] = 0x00;
+    pci_conf[0x65] = 0x00;
+    pci_conf[0x66] = 0x00;
+    pci_conf[0x67] = 0x00;
+    pci_conf[0x68] = 0x01;
+    pci_conf[0x69] = 0x00;
+    pci_conf[0x6a] = 0x00;
+    pci_conf[0x6b] = 0x00;  // USBLEGSUP
+    pci_conf[0x6c] = 0x00;
+    pci_conf[0x6d] = 0x00;
+    pci_conf[0x6e] = 0x00;
+    pci_conf[0x6f] = 0xc0;  // USBLEFCTLSTS
+
+    // 2.2 host controller interface version
+    s->mmio[0x00] = (uint8_t) OPREGBASE;
+    s->mmio[0x01] = 0x00;
+    s->mmio[0x02] = 0x00;
+    s->mmio[0x03] = 0x01;        // HC version
+    s->mmio[0x04] = NB_PORTS;    // Number of downstream ports
+    s->mmio[0x05] = 0x00;        // No companion ports at present
+    s->mmio[0x06] = 0x00;
+    s->mmio[0x07] = 0x00;
+    s->mmio[0x08] = 0x80;        // We can cache whole frame, not 64-bit capable
+    s->mmio[0x09] = 0x68;        // EECP
+    s->mmio[0x0a] = 0x00;
+    s->mmio[0x0b] = 0x00;
+
+    s->irq = s->dev.irq[3];
+
+    usb_bus_new(&s->bus, &s->dev.qdev);
+    for(i = 0; i < NB_PORTS; i++) {
+        usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
+                          USB_SPEED_MASK_HIGH);
+        usb_port_location(&s->ports[i], NULL, i+1);
+        s->ports[i].dev = 0;
+    }
+
+    s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
+
+    qemu_register_reset(ehci_reset, s);
+
+    s->mem = cpu_register_io_memory(ehci_readfn, ehci_writefn, s,
+                                    DEVICE_LITTLE_ENDIAN);
+
+    pci_register_bar(&s->dev, 0, MMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY,
+                                                            ehci_map);
+
+    fprintf(stderr, "*** EHCI support is under development ***\n");
+
+    return 0;
+}
+
+static void ehci_register(void)
+{
+    pci_qdev_register(&ehci_info);
+}
+device_init(ehci_register);
+
+/*
+ * vim: expandtab ts=4
+ */
-- 
1.7.1

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

* Re: [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo Gerd Hoffmann
@ 2011-05-23 14:04   ` Hans de Goede
  2011-05-23 14:34     ` Gerd Hoffmann
  0 siblings, 1 reply; 28+ messages in thread
From: Hans de Goede @ 2011-05-23 14:04 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Hi,

Gerd, this is more or less a copy of a mail I send you directly earlier
before I saw this pull request.

NACK for this one, rational:

While working on re-basing / re-doing my usb network redirection code for qemu, on top of
your usb.12 I've hit a problem caused by the move of the async cancel callback from
USBPacket to USBDevice, and I think I know now why it used to be in USBPacket
(and we may need to move it back).

The problem is that the USBDevice lifetime may be shorter then the USBPacket lifetime,
USBPackets are created by uhci.c (for example), where as the device is managed
from the monitor (for example), doing a usb_del in the monitor using the guest bus:addr
will call usb_device_delete_addr, which will call qdev_free. At this time the
USBDevice struct is gone, and at a later time the uhci code will cancel any still
outstanding async packets, who's owner pointer will now point to free-ed memory.

Note that doing a usb_del on a (direct or net) redirected device used to work
before.

We could ref-count the USBDevice, but would make the usb_del not do anything
as long as some async requests are active, which seems wrong.

Another solution would be moving the async_cancel function pointer + an opaque
ptr back to the USBPacket, which seems best to me...

Note the above is all theory I've not yet tried this yet (still need to finish
up re-basing my code first).

Regards,

Hans


On 05/23/2011 11:43 AM, Gerd Hoffmann wrote:
> Remove the cancel callback from the USBPacket struct, move it over
> to USBDeviceInfo.  Zap usb_defer_packet() which is obsolete now.
>
> Signed-off-by: Gerd Hoffmann<kraxel@redhat.com>
> ---
>   hw/usb-msd.c |    8 +++-----
>   hw/usb.c     |    2 +-
>   hw/usb.h     |   17 +++++------------
>   usb-linux.c  |    7 +++----
>   4 files changed, 12 insertions(+), 22 deletions(-)
>
> diff --git a/hw/usb-msd.c b/hw/usb-msd.c
> index 1064920..141da2c 100644
> --- a/hw/usb-msd.c
> +++ b/hw/usb-msd.c
> @@ -315,9 +315,9 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
>       return ret;
>   }
>
> -static void usb_msd_cancel_io(USBPacket *p, void *opaque)
> +static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
>   {
> -    MSDState *s = opaque;
> +    MSDState *s = DO_UPCAST(MSDState, dev, dev);
>       s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
>       s->packet = NULL;
>       s->scsi_len = 0;
> @@ -398,7 +398,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
>               }
>               if (s->usb_len) {
>                   DPRINTF("Deferring packet %p\n", p);
> -                usb_defer_packet(p, usb_msd_cancel_io, s);
>                   s->packet = p;
>                   ret = USB_RET_ASYNC;
>               } else {
> @@ -421,7 +420,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
>               if (s->data_len != 0 || len<  13)
>                   goto fail;
>               /* Waiting for SCSI write to complete.  */
> -            usb_defer_packet(p, usb_msd_cancel_io, s);
>               s->packet = p;
>               ret = USB_RET_ASYNC;
>               break;
> @@ -455,7 +453,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
>               }
>               if (s->usb_len) {
>                   DPRINTF("Deferring packet %p\n", p);
> -                usb_defer_packet(p, usb_msd_cancel_io, s);
>                   s->packet = p;
>                   ret = USB_RET_ASYNC;
>               } else {
> @@ -604,6 +601,7 @@ static struct USBDeviceInfo msd_info = {
>       .usb_desc       =&desc,
>       .init           = usb_msd_initfn,
>       .handle_packet  = usb_generic_handle_packet,
> +    .cancel_packet  = usb_msd_cancel_io,
>       .handle_attach  = usb_desc_attach,
>       .handle_reset   = usb_msd_handle_reset,
>       .handle_control = usb_msd_handle_control,
> diff --git a/hw/usb.c b/hw/usb.c
> index 8a9a7fc..4a39cbc 100644
> --- a/hw/usb.c
> +++ b/hw/usb.c
> @@ -345,6 +345,6 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
>   void usb_cancel_packet(USBPacket * p)
>   {
>       assert(p->owner != NULL);
> -    p->cancel_cb(p, p->cancel_opaque);
> +    p->owner->info->cancel_packet(p->owner, p);
>       p->owner = NULL;
>   }
> diff --git a/hw/usb.h b/hw/usb.h
> index 80e8e90..9882400 100644
> --- a/hw/usb.h
> +++ b/hw/usb.h
> @@ -194,6 +194,11 @@ struct USBDeviceInfo {
>       int (*handle_packet)(USBDevice *dev, USBPacket *p);
>
>       /*
> +     * Called when a packet is canceled.
> +     */
> +    void (*cancel_packet)(USBDevice *dev, USBPacket *p);
> +
> +    /*
>        * Called when device is destroyed.
>        */
>       void (*handle_destroy)(USBDevice *dev);
> @@ -263,24 +268,12 @@ struct USBPacket {
>       int len;
>       /* Internal use by the USB layer.  */
>       USBDevice *owner;
> -    USBCallback *cancel_cb;
> -    void *cancel_opaque;
>   };
>
>   int usb_handle_packet(USBDevice *dev, USBPacket *p);
>   void usb_packet_complete(USBDevice *dev, USBPacket *p);
>   void usb_cancel_packet(USBPacket * p);
>
> -/* Defer completion of a USB packet.  The hadle_packet routine should then
> -   return USB_RET_ASYNC.  Packets that complete immediately (before
> -   handle_packet returns) should not call this method.  */
> -static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
> -                                    void * opaque)
> -{
> -    p->cancel_cb = cancel;
> -    p->cancel_opaque = opaque;
> -}
> -
>   void usb_attach(USBPort *port, USBDevice *dev);
>   void usb_wakeup(USBDevice *dev);
>   int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
> diff --git a/usb-linux.c b/usb-linux.c
> index c7e96c3..baa6574 100644
> --- a/usb-linux.c
> +++ b/usb-linux.c
> @@ -335,9 +335,9 @@ static void async_complete(void *opaque)
>       }
>   }
>
> -static void async_cancel(USBPacket *p, void *opaque)
> +static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
>   {
> -    USBHostDevice *s = opaque;
> +    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
>       AsyncURB *aurb;
>
>       QLIST_FOREACH(aurb,&s->aurbs, next) {
> @@ -736,7 +736,6 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
>           }
>       }
>
> -    usb_defer_packet(p, async_cancel, s);
>       return USB_RET_ASYNC;
>   }
>
> @@ -868,7 +867,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
>           }
>       }
>
> -    usb_defer_packet(p, async_cancel, s);
>       return USB_RET_ASYNC;
>   }
>
> @@ -1197,6 +1195,7 @@ static struct USBDeviceInfo usb_host_dev_info = {
>       .qdev.size      = sizeof(USBHostDevice),
>       .init           = usb_host_initfn,
>       .handle_packet  = usb_generic_handle_packet,
> +    .cancel_packet  = usb_host_async_cancel,
>       .handle_data    = usb_host_handle_data,
>       .handle_control = usb_host_handle_control,
>       .handle_reset   = usb_host_handle_reset,

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

* Re: [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo
  2011-05-23 14:04   ` Hans de Goede
@ 2011-05-23 14:34     ` Gerd Hoffmann
  2011-05-23 14:53       ` Gerd Hoffmann
  2011-05-23 17:30       ` Hans de Goede
  0 siblings, 2 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23 14:34 UTC (permalink / raw)
  To: Hans de Goede; +Cc: qemu-devel

   Hi,

> The problem is that the USBDevice lifetime may be shorter then the
> USBPacket lifetime, USBPackets are created by uhci.c (for example),
> where as the device is managed from the monitor (for example), doing
> a usb_del in the monitor using the guest bus:addr will call
> usb_device_delete_addr, which will call qdev_free. At this time the
> USBDevice struct is gone, and at a later time the uhci code will
> cancel any still outstanding async packets, who's owner pointer will
> now point to free-ed memory.

Good spotting, this is indeed a issue which needs fixing.  It isn't 
introduced by this patch though, it exists even without the usb patch queue.

usb-msd.c passes a USBDevice pointer directly as opaque.  The 
usb-linux.c callback function assumes it can dereference aurb->hdev just 
fine.  Both will hit free'ed memory in case the device is unplugged 
while a async packet is in flight.

cheers,
   Gerd

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

* Re: [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo
  2011-05-23 14:34     ` Gerd Hoffmann
@ 2011-05-23 14:53       ` Gerd Hoffmann
  2011-05-23 17:31         ` Hans de Goede
  2011-05-23 17:30       ` Hans de Goede
  1 sibling, 1 reply; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-23 14:53 UTC (permalink / raw)
  To: Hans de Goede; +Cc: qemu-devel

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

   Hi,

> Good spotting, this is indeed a issue which needs fixing. It isn't
> introduced by this patch though, it exists even without the usb patch
> queue.

Something like the attached patch should fix it.

cheers,
   Gerd

[-- Attachment #2: fix --]
[-- Type: text/plain, Size: 2672 bytes --]

diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index bc31850..e9993a7 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -234,6 +234,19 @@ static void uhci_async_validate_end(UHCIState *s)
     }
 }
 
+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) {
+            continue;
+        }
+        uhci_async_unlink(s, curr);
+        uhci_async_cancel(s, curr);
+    }
+}
+
 static void uhci_async_cancel_all(UHCIState *s)
 {
     UHCIAsync *curr, *n;
@@ -1097,6 +1110,13 @@ static void uhci_map(PCIDevice *pci_dev, int region_num,
     register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
 }
 
+static void uhci_device_destroy(USBBus *bus, USBDevice *dev)
+{
+    UHCIState *s = container_of(bus, UHCIState, bus);
+
+    uhci_async_cancel_device(s, dev);
+}
+
 static USBPortOps uhci_port_ops = {
     .attach = uhci_attach,
     .detach = uhci_detach,
@@ -1104,6 +1124,10 @@ static USBPortOps uhci_port_ops = {
     .complete = uhci_async_complete,
 };
 
+static USBBusOps uhci_bus_ops = {
+    .device_destroy = uhci_device_destroy,
+};
+
 static int usb_uhci_common_initfn(UHCIState *s)
 {
     uint8_t *pci_conf = s->dev.config;
@@ -1116,7 +1140,7 @@ static int usb_uhci_common_initfn(UHCIState *s)
     pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3
     pci_conf[0x60] = 0x10; // release number
 
-    usb_bus_new(&s->bus, &s->dev.qdev);
+    usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
     for(i = 0; i < NB_PORTS; i++) {
         usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
                           USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
diff --git a/hw/usb.h b/hw/usb.h
index 0fa86a3..097d789 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -139,6 +139,7 @@
 #define USB_ENDPOINT_XFER_INT		3
 
 typedef struct USBBus USBBus;
+typedef struct USBBusOps USBBusOps;
 typedef struct USBPort USBPort;
 typedef struct USBDevice USBDevice;
 typedef struct USBDeviceInfo USBDeviceInfo;
@@ -330,6 +331,7 @@ void musb_set_size(MUSBState *s, int epnum, int size, int is_tx);
 
 struct USBBus {
     BusState qbus;
+    USBBusOps *ops;
     int busnr;
     int nfree;
     int nused;
@@ -338,7 +340,11 @@ struct USBBus {
     QTAILQ_ENTRY(USBBus) next;
 };
 
-void usb_bus_new(USBBus *bus, DeviceState *host);
+struct USBBusOps {
+    void (*device_destroy)(USBBus *bus, USBDevice *dev);
+};
+
+void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
 USBBus *usb_bus_find(int busnr);
 void usb_qdev_register(USBDeviceInfo *info);
 void usb_qdev_register_many(USBDeviceInfo *info);

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

* Re: [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo
  2011-05-23 14:34     ` Gerd Hoffmann
  2011-05-23 14:53       ` Gerd Hoffmann
@ 2011-05-23 17:30       ` Hans de Goede
  1 sibling, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2011-05-23 17:30 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Hi,

On 05/23/2011 04:34 PM, Gerd Hoffmann wrote:
> Hi,
>
>> The problem is that the USBDevice lifetime may be shorter then the
>> USBPacket lifetime, USBPackets are created by uhci.c (for example),
>> where as the device is managed from the monitor (for example), doing
>> a usb_del in the monitor using the guest bus:addr will call
>> usb_device_delete_addr, which will call qdev_free. At this time the
>> USBDevice struct is gone, and at a later time the uhci code will
>> cancel any still outstanding async packets, who's owner pointer will
>> now point to free-ed memory.
>
> Good spotting, this is indeed a issue which needs fixing. It isn't introduced by this patch though, it exists even without the usb patch queue.
>
> usb-msd.c passes a USBDevice pointer directly as opaque. The usb-linux.c callback function assumes it can dereference aurb->hdev just fine.

Ah, that is no good, my usb network redir device code uses aurb's similar too linux.c, but on
device-destroy walks its list of pending aurbs, sends a cancel to the host-os, and
sets aurb->hdev to null, and the async cancel checks for aurb->hdev being NULL and
in that case only frees the aurb and does nothing else.

 > Both will hit free'ed memory in case the device is unplugged while a async packet is in flight.

Yep, linux.c could be fixed the same way as my usb net redir device code. But I like
the patch you just send better. It looks incomplete though, I'll give more details
in a reply to the patch it self.

Regards,

Hans

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

* Re: [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo
  2011-05-23 14:53       ` Gerd Hoffmann
@ 2011-05-23 17:31         ` Hans de Goede
  0 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2011-05-23 17:31 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Hi,

On 05/23/2011 04:53 PM, Gerd Hoffmann wrote:
> Hi,
>
>> Good spotting, this is indeed a issue which needs fixing. It isn't
>> introduced by this patch though, it exists even without the usb patch
>> queue.
>
> Something like the attached patch should fix it.

The diff looks incomplete (missing changes to usbbus.c), but I
see you send a more complete patch in a separate mail, I'll
take a look at that one instead.

Regards,

Hans

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

* Re: [Qemu-devel] [PATCH 18/18] usb: add ehci adapter
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 18/18] usb: add ehci adapter Gerd Hoffmann
@ 2011-05-23 19:25   ` Blue Swirl
  2011-05-24 15:45   ` Erik Rull
  1 sibling, 0 replies; 28+ messages in thread
From: Blue Swirl @ 2011-05-23 19:25 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Vincent Palatin, Jan Kiszka, Kevin Wolf, qemu-devel, David S. Ahern

On Mon, May 23, 2011 at 12:43 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> This patch finally merges the EHCI host adapter aka USB 2.0 support.
>
> Based on the ehci bits collected @ git://git.kiszka.org/qemu.git ehci
>
> EHCI has a long out-of-tree history.  Project was started by Mark
> Burkley, with contributions by Niels de Vos.  David S. Ahern continued
> working on it.  Kevin Wolf, Jan Kiszka and Vincent Palatin contributed
> bugfixes.
>
> /me (Gerd Hoffmann) picked it up where it left off, prepared the code
> for merge, fixed a few bugs and added basic user docs.
>
> Cc: David S. Ahern <daahern@cisco.com>
> Cc: Jan Kiszka <jan.kiszka@web.de>
> Cc: Kevin Wolf <mail@kevin-wolf.de>
> Cc: Vincent Palatin <vincent.palatin_qemu@m4x.org>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  Makefile.objs           |    1 +
>  default-configs/pci.mak |    1 +
>  docs/usb2.txt           |   38 +
>  hw/pci_ids.h            |    1 +
>  hw/usb-ehci.c           | 2038 +++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 2079 insertions(+), 0 deletions(-)
>  create mode 100644 docs/usb2.txt
>  create mode 100644 hw/usb-ehci.c
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 4478c61..90838f6 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -193,6 +193,7 @@ hw-obj-$(CONFIG_PCSPK) += pcspk.o
>  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_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 0471efb..22bd350 100644
> --- a/default-configs/pci.mak
> +++ b/default-configs/pci.mak
> @@ -3,6 +3,7 @@ CONFIG_VIRTIO_PCI=y
>  CONFIG_VIRTIO=y
>  CONFIG_USB_UHCI=y
>  CONFIG_USB_OHCI=y
> +CONFIG_USB_EHCI=y
>  CONFIG_NE2000_PCI=y
>  CONFIG_EEPRO100_PCI=y
>  CONFIG_PCNET_PCI=y
> diff --git a/docs/usb2.txt b/docs/usb2.txt
> new file mode 100644
> index 0000000..b283c13
> --- /dev/null
> +++ b/docs/usb2.txt
> @@ -0,0 +1,38 @@
> +
> +USB 2.0 Quick Start
> +===================
> +
> +The QEMU EHCI Adapter does *not* support companion controllers.  That
> +implies there are two completely separate USB busses: One USB 1.1 bus
> +driven by the UHCI controller and one USB 2.0 bus driven by the EHCI
> +controller.  Devices must be attached to the correct controller
> +manually.
> +
> +The '-usb' switch will make qemu create the UHCI controller as part of
> +the PIIX3 chipset.  The USB 1.1 bus will carry the name "usb.0".
> +
> +You can use the standard -device switch to add a EHCI controller to
> +your virtual machine.  It is strongly recommended to specify an ID for
> +the controller so the USB 2.0 bus gets a individual name, for example
> +'-device usb-ehci,id=ehci".  This will give you a USB 2.0 bus named
> +"ehci.0".
> +
> +I strongly recomment to also use -device to attach usb devices because
> +you can specify the bus they should be attached to this way.  Here is
> +a complete example:
> +
> +    qemu -M pc ${otheroptions}                           \
> +        -drive if=none,id=usbstick,file=/path/to/image   \
> +        -usb                                             \
> +        -device usb-ehci,id=ehci                         \
> +        -device usb-tablet,bus=usb.0                     \
> +        -device usb-storage,bus=ehci.0,drive=usbstick
> +
> +This attaches a usb tablet to the UHCI adapter and a usb mass storage
> +device to the EHCI adapter.
> +
> +enjoy,
> +  Gerd
> +
> +--
> +Gerd Hoffmann <kraxel@redhat.com>
> diff --git a/hw/pci_ids.h b/hw/pci_ids.h
> index ea3418c..d9457ed 100644
> --- a/hw/pci_ids.h
> +++ b/hw/pci_ids.h
> @@ -100,6 +100,7 @@
>  #define PCI_VENDOR_ID_INTEL              0x8086
>  #define PCI_DEVICE_ID_INTEL_82441        0x1237
>  #define PCI_DEVICE_ID_INTEL_82801AA_5    0x2415
> +#define PCI_DEVICE_ID_INTEL_82801D       0x24CD
>  #define PCI_DEVICE_ID_INTEL_ESB_9        0x25ab
>  #define PCI_DEVICE_ID_INTEL_82371SB_0    0x7000
>  #define PCI_DEVICE_ID_INTEL_82371SB_1    0x7010
> diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
> new file mode 100644
> index 0000000..545c590
> --- /dev/null
> +++ b/hw/usb-ehci.c
> @@ -0,0 +1,2038 @@
> +/*
> + * QEMU USB EHCI Emulation
> + *
> + * Copyright(c) 2008  Emutex Ltd. (address@hidden)
> + *
> + * EHCI project was started by Mark Burkley, with contributions by
> + * Niels de Vos.  David S. Ahern continued working on it.  Kevin Wolf,
> + * Jan Kiszka and Vincent Palatin contributed bugfixes.
> + *
> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


FSF's current address is:

51 Franklin Street, Fifth Floor
Boston, MA 02110-1301
USA

But please use the interweb address.

> + *
> + * TODO:
> + *  o Downstream port handoff
> + */
> +
> +#include "hw.h"
> +#include "qemu-timer.h"
> +#include "usb.h"
> +#include "pci.h"
> +#include "monitor.h"
> +
> +#define EHCI_DEBUG   0
> +#define STATE_DEBUG  0       /* state transitions  */
> +
> +#if EHCI_DEBUG || STATE_DEBUG
> +#define DPRINTF printf
> +#else
> +#define DPRINTF(...)

Unusual debug macros. Using tracepoints would be nice.

> +#endif
> +
> +#if STATE_DEBUG
> +#define DPRINTF_ST DPRINTF
> +#else
> +#define DPRINTF_ST(...)
> +#endif
> +
> +/* internal processing - reset HC to try and recover */
> +#define USB_RET_PROCERR   (-99)
> +
> +#define MMIO_SIZE        0x1000
> +
> +/* Capability Registers Base Address - section 2.2 */
> +#define CAPREGBASE       0x0000
> +#define CAPLENGTH        CAPREGBASE + 0x0000  // 1-byte, 0x0001 reserved

C99 comments. The expression should be enclosed in parentheses.

> +#define HCIVERSION       CAPREGBASE + 0x0002  // 2-bytes, i/f version #
> +#define HCSPARAMS        CAPREGBASE + 0x0004  // 4-bytes, structural params
> +#define HCCPARAMS        CAPREGBASE + 0x0008  // 4-bytes, capability params
> +#define EECP             HCCPARAMS + 1
> +#define HCSPPORTROUTE1   CAPREGBASE + 0x000c
> +#define HCSPPORTROUTE2   CAPREGBASE + 0x0010
> +
> +#define OPREGBASE        0x0020        // Operational Registers Base Address
> +
> +#define USBCMD           OPREGBASE + 0x0000
> +#define USBCMD_RUNSTOP   (1 << 0)      // run / Stop
> +#define USBCMD_HCRESET   (1 << 1)      // HC Reset
> +#define USBCMD_FLS       (3 << 2)      // Frame List Size
> +#define USBCMD_FLS_SH    2             // Frame List Size Shift
> +#define USBCMD_PSE       (1 << 4)      // Periodic Schedule Enable
> +#define USBCMD_ASE       (1 << 5)      // Asynch Schedule Enable
> +#define USBCMD_IAAD      (1 << 6)      // Int Asynch Advance Doorbell
> +#define USBCMD_LHCR      (1 << 7)      // Light Host Controller Reset
> +#define USBCMD_ASPMC     (3 << 8)      // Async Sched Park Mode Count
> +#define USBCMD_ASPME     (1 << 11)     // Async Sched Park Mode Enable
> +#define USBCMD_ITC       (0x7f << 16)  // Int Threshold Control
> +#define USBCMD_ITC_SH    16            // Int Threshold Control Shift
> +
> +#define USBSTS           OPREGBASE + 0x0004
> +#define USBSTS_RO_MASK   0x0000003f
> +#define USBSTS_INT       (1 << 0)      // USB Interrupt
> +#define USBSTS_ERRINT    (1 << 1)      // Error Interrupt
> +#define USBSTS_PCD       (1 << 2)      // Port Change Detect
> +#define USBSTS_FLR       (1 << 3)      // Frame List Rollover
> +#define USBSTS_HSE       (1 << 4)      // Host System Error
> +#define USBSTS_IAA       (1 << 5)      // Interrupt on Async Advance
> +#define USBSTS_HALT      (1 << 12)     // HC Halted
> +#define USBSTS_REC       (1 << 13)     // Reclamation
> +#define USBSTS_PSS       (1 << 14)     // Periodic Schedule Status
> +#define USBSTS_ASS       (1 << 15)     // Asynchronous Schedule Status
> +
> +/*
> + *  Interrupt enable bits correspond to the interrupt active bits in USBSTS
> + *  so no need to redefine here.
> + */
> +#define USBINTR              OPREGBASE + 0x0008
> +#define USBINTR_MASK         0x0000003f
> +
> +#define FRINDEX              OPREGBASE + 0x000c
> +#define CTRLDSSEGMENT        OPREGBASE + 0x0010
> +#define PERIODICLISTBASE     OPREGBASE + 0x0014
> +#define ASYNCLISTADDR        OPREGBASE + 0x0018
> +#define ASYNCLISTADDR_MASK   0xffffffe0
> +
> +#define CONFIGFLAG           OPREGBASE + 0x0040
> +
> +#define PORTSC               (OPREGBASE + 0x0044)
> +#define PORTSC_BEGIN         PORTSC
> +#define PORTSC_END           (PORTSC + 4 * NB_PORTS)
> +/*
> + * Bits that are reserverd or are read-only are masked out of values
> + * written to us by software
> + */
> +#define PORTSC_RO_MASK       0x007021c5
> +#define PORTSC_RWC_MASK      0x0000002a
> +#define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
> +#define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
> +#define PORTSC_WKCN_E        (1 << 20)    // Wake on Connect Enable
> +#define PORTSC_PTC           (15 << 16)   // Port Test Control
> +#define PORTSC_PTC_SH        16           // Port Test Control shift
> +#define PORTSC_PIC           (3 << 14)    // Port Indicator Control
> +#define PORTSC_PIC_SH        14           // Port Indicator Control Shift
> +#define PORTSC_POWNER        (1 << 13)    // Port Owner
> +#define PORTSC_PPOWER        (1 << 12)    // Port Power
> +#define PORTSC_LINESTAT      (3 << 10)    // Port Line Status
> +#define PORTSC_LINESTAT_SH   10           // Port Line Status Shift
> +#define PORTSC_PRESET        (1 << 8)     // Port Reset
> +#define PORTSC_SUSPEND       (1 << 7)     // Port Suspend
> +#define PORTSC_FPRES         (1 << 6)     // Force Port Resume
> +#define PORTSC_OCC           (1 << 5)     // Over Current Change
> +#define PORTSC_OCA           (1 << 4)     // Over Current Active
> +#define PORTSC_PEDC          (1 << 3)     // Port Enable/Disable Change
> +#define PORTSC_PED           (1 << 2)     // Port Enable/Disable
> +#define PORTSC_CSC           (1 << 1)     // Connect Status Change
> +#define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
> +
> +#define FRAME_TIMER_FREQ 1000
> +#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ)
> +
> +#define NB_MAXINTRATE    8        // Max rate at which controller issues ints
> +#define NB_PORTS         4        // Number of downstream ports
> +#define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction

Also here.

> +#define MAX_ITERATIONS   20       // Max number of QH before we break the loop
> +#define MAX_QH           100      // Max allowable queue heads in a chain
> +
> +/*  Internal periodic / asynchronous schedule state machine states
> + */
> +typedef enum {
> +    EST_INACTIVE = 1000,
> +    EST_ACTIVE,
> +    EST_EXECUTING,
> +    EST_SLEEPING,
> +    /*  The following states are internal to the state machine function
> +    */
> +    EST_WAITLISTHEAD,
> +    EST_FETCHENTRY,
> +    EST_FETCHQH,
> +    EST_FETCHITD,
> +    EST_ADVANCEQUEUE,
> +    EST_FETCHQTD,
> +    EST_EXECUTE,
> +    EST_WRITEBACK,
> +    EST_HORIZONTALQH
> +} EHCI_STATES;
> +
> +/* macros for accessing fields within next link pointer entry */
> +#define NLPTR_GET(x)             ((x) & 0xffffffe0)
> +#define NLPTR_TYPE_GET(x)        (((x) >> 1) & 3)
> +#define NLPTR_TBIT(x)            ((x) & 1)  // 1=invalid, 0=valid
> +
> +/* link pointer types */
> +#define NLPTR_TYPE_ITD           0     // isoc xfer descriptor
> +#define NLPTR_TYPE_QH            1     // queue head
> +#define NLPTR_TYPE_STITD         2     // split xaction, isoc xfer descriptor
> +#define NLPTR_TYPE_FSTN          3     // frame span traversal node
> +
> +
> +/*  EHCI spec version 1.0 Section 3.3
> + */
> +typedef struct EHCIitd {
> +    uint32_t next;
> +
> +    uint32_t transact[8];
> +#define ITD_XACT_ACTIVE          (1 << 31)
> +#define ITD_XACT_DBERROR         (1 << 30)
> +#define ITD_XACT_BABBLE          (1 << 29)
> +#define ITD_XACT_XACTERR         (1 << 28)
> +#define ITD_XACT_LENGTH_MASK     0x0fff0000
> +#define ITD_XACT_LENGTH_SH       16
> +#define ITD_XACT_IOC             (1 << 15)
> +#define ITD_XACT_PGSEL_MASK      0x00007000
> +#define ITD_XACT_PGSEL_SH        12
> +#define ITD_XACT_OFFSET_MASK     0x00000fff
> +
> +    uint32_t bufptr[7];
> +#define ITD_BUFPTR_MASK          0xfffff000
> +#define ITD_BUFPTR_SH            12
> +#define ITD_BUFPTR_EP_MASK       0x00000f00
> +#define ITD_BUFPTR_EP_SH         8
> +#define ITD_BUFPTR_DEVADDR_MASK  0x0000007f
> +#define ITD_BUFPTR_DEVADDR_SH    0
> +#define ITD_BUFPTR_DIRECTION     (1 << 11)
> +#define ITD_BUFPTR_MAXPKT_MASK   0x000007ff
> +#define ITD_BUFPTR_MAXPKT_SH     0
> +#define ITD_BUFPTR_MULT_MASK     0x00000003
> +} EHCIitd;
> +
> +/*  EHCI spec version 1.0 Section 3.4
> + */
> +typedef struct EHCIsitd {
> +    uint32_t next;                  // Standard next link pointer
> +    uint32_t epchar;
> +#define SITD_EPCHAR_IO              (1 << 31)
> +#define SITD_EPCHAR_PORTNUM_MASK    0x7f000000
> +#define SITD_EPCHAR_PORTNUM_SH      24
> +#define SITD_EPCHAR_HUBADD_MASK     0x007f0000
> +#define SITD_EPCHAR_HUBADDR_SH      16
> +#define SITD_EPCHAR_EPNUM_MASK      0x00000f00
> +#define SITD_EPCHAR_EPNUM_SH        8
> +#define SITD_EPCHAR_DEVADDR_MASK    0x0000007f
> +
> +    uint32_t uframe;
> +#define SITD_UFRAME_CMASK_MASK      0x0000ff00
> +#define SITD_UFRAME_CMASK_SH        8
> +#define SITD_UFRAME_SMASK_MASK      0x000000ff
> +
> +    uint32_t results;
> +#define SITD_RESULTS_IOC              (1 << 31)
> +#define SITD_RESULTS_PGSEL            (1 << 30)
> +#define SITD_RESULTS_TBYTES_MASK      0x03ff0000
> +#define SITD_RESULTS_TYBYTES_SH       16
> +#define SITD_RESULTS_CPROGMASK_MASK   0x0000ff00
> +#define SITD_RESULTS_CPROGMASK_SH     8
> +#define SITD_RESULTS_ACTIVE           (1 << 7)
> +#define SITD_RESULTS_ERR              (1 << 6)
> +#define SITD_RESULTS_DBERR            (1 << 5)
> +#define SITD_RESULTS_BABBLE           (1 << 4)
> +#define SITD_RESULTS_XACTERR          (1 << 3)
> +#define SITD_RESULTS_MISSEDUF         (1 << 2)
> +#define SITD_RESULTS_SPLITXSTATE      (1 << 1)
> +
> +    uint32_t bufptr[2];
> +#define SITD_BUFPTR_MASK              0xfffff000
> +#define SITD_BUFPTR_CURROFF_MASK      0x00000fff
> +#define SITD_BUFPTR_TPOS_MASK         0x00000018
> +#define SITD_BUFPTR_TPOS_SH           3
> +#define SITD_BUFPTR_TCNT_MASK         0x00000007
> +
> +    uint32_t backptr;                 // Standard next link pointer
> +} EHCIsitd;
> +
> +/*  EHCI spec version 1.0 Section 3.5
> + */
> +typedef struct EHCIqtd {
> +    uint32_t next;                    // Standard next link pointer
> +    uint32_t altnext;                 // Standard next link pointer
> +    uint32_t token;
> +#define QTD_TOKEN_DTOGGLE             (1 << 31)
> +#define QTD_TOKEN_TBYTES_MASK         0x7fff0000
> +#define QTD_TOKEN_TBYTES_SH           16
> +#define QTD_TOKEN_IOC                 (1 << 15)
> +#define QTD_TOKEN_CPAGE_MASK          0x00007000
> +#define QTD_TOKEN_CPAGE_SH            12
> +#define QTD_TOKEN_CERR_MASK           0x00000c00
> +#define QTD_TOKEN_CERR_SH             10
> +#define QTD_TOKEN_PID_MASK            0x00000300
> +#define QTD_TOKEN_PID_SH              8
> +#define QTD_TOKEN_ACTIVE              (1 << 7)
> +#define QTD_TOKEN_HALT                (1 << 6)
> +#define QTD_TOKEN_DBERR               (1 << 5)
> +#define QTD_TOKEN_BABBLE              (1 << 4)
> +#define QTD_TOKEN_XACTERR             (1 << 3)
> +#define QTD_TOKEN_MISSEDUF            (1 << 2)
> +#define QTD_TOKEN_SPLITXSTATE         (1 << 1)
> +#define QTD_TOKEN_PING                (1 << 0)
> +
> +    uint32_t bufptr[5];               // Standard buffer pointer
> +#define QTD_BUFPTR_MASK               0xfffff000
> +} EHCIqtd;
> +
> +/*  EHCI spec version 1.0 Section 3.6
> + */
> +typedef struct EHCIqh {
> +    uint32_t next;                    // Standard next link pointer
> +
> +    /* endpoint characteristics */
> +    uint32_t epchar;
> +#define QH_EPCHAR_RL_MASK             0xf0000000
> +#define QH_EPCHAR_RL_SH               28
> +#define QH_EPCHAR_C                   (1 << 27)
> +#define QH_EPCHAR_MPLEN_MASK          0x07FF0000
> +#define QH_EPCHAR_MPLEN_SH            16
> +#define QH_EPCHAR_H                   (1 << 15)
> +#define QH_EPCHAR_DTC                 (1 << 14)
> +#define QH_EPCHAR_EPS_MASK            0x00003000
> +#define QH_EPCHAR_EPS_SH              12
> +#define EHCI_QH_EPS_FULL              0
> +#define EHCI_QH_EPS_LOW               1
> +#define EHCI_QH_EPS_HIGH              2
> +#define EHCI_QH_EPS_RESERVED          3
> +
> +#define QH_EPCHAR_EP_MASK             0x00000f00
> +#define QH_EPCHAR_EP_SH               8
> +#define QH_EPCHAR_I                   (1 << 7)
> +#define QH_EPCHAR_DEVADDR_MASK        0x0000007f
> +#define QH_EPCHAR_DEVADDR_SH          0
> +
> +    /* endpoint capabilities */
> +    uint32_t epcap;
> +#define QH_EPCAP_MULT_MASK            0xc0000000
> +#define QH_EPCAP_MULT_SH              30
> +#define QH_EPCAP_PORTNUM_MASK         0x3f800000
> +#define QH_EPCAP_PORTNUM_SH           23
> +#define QH_EPCAP_HUBADDR_MASK         0x007f0000
> +#define QH_EPCAP_HUBADDR_SH           16
> +#define QH_EPCAP_CMASK_MASK           0x0000ff00
> +#define QH_EPCAP_CMASK_SH             8
> +#define QH_EPCAP_SMASK_MASK           0x000000ff
> +#define QH_EPCAP_SMASK_SH             0
> +
> +    uint32_t current_qtd;             // Standard next link pointer
> +    uint32_t next_qtd;                // Standard next link pointer
> +    uint32_t altnext_qtd;
> +#define QH_ALTNEXT_NAKCNT_MASK        0x0000001e
> +#define QH_ALTNEXT_NAKCNT_SH          1
> +
> +    uint32_t token;                   // Same as QTD token
> +    uint32_t bufptr[5];               // Standard buffer pointer
> +#define BUFPTR_CPROGMASK_MASK         0x000000ff
> +#define BUFPTR_FRAMETAG_MASK          0x0000001f
> +#define BUFPTR_SBYTES_MASK            0x00000fe0
> +#define BUFPTR_SBYTES_SH              5
> +} EHCIqh;
> +
> +/*  EHCI spec version 1.0 Section 3.7
> + */
> +typedef struct EHCIfstn {
> +    uint32_t next;                    // Standard next link pointer
> +    uint32_t backptr;                 // Standard next link pointer
> +} EHCIfstn;
> +
> +typedef struct {
> +    PCIDevice dev;
> +    qemu_irq irq;
> +    target_phys_addr_t mem_base;

Is this needed?

> +    int mem;
> +    int num_ports;
> +    /*
> +     *  EHCI spec version 1.0 Section 2.3
> +     *  Host Controller Operational Registers
> +     */
> +    union {
> +        uint8_t mmio[MMIO_SIZE];
> +        struct {
> +            uint8_t cap[OPREGBASE];
> +            uint32_t usbcmd;
> +            uint32_t usbsts;
> +            uint32_t usbintr;
> +            uint32_t frindex;
> +            uint32_t ctrldssegment;
> +            uint32_t periodiclistbase;
> +            uint32_t asynclistaddr;
> +            uint32_t notused[9];
> +            uint32_t configflag;
> +            uint32_t portsc[NB_PORTS];
> +        };

This union smells of endianness troubles.

> +    };
> +    /*
> +     *  Internal states, shadow registers, etc
> +     */
> +    uint32_t sofv;
> +    QEMUTimer *frame_timer;
> +    int attach_poll_counter;
> +    int astate;                        // Current state in asynchronous schedule
> +    int pstate;                        // Current state in periodic schedule
> +    USBPort ports[NB_PORTS];
> +    uint8_t buffer[BUFF_SIZE];
> +    uint32_t usbsts_pending;
> +
> +    /* cached data from guest - needs to be flushed
> +     * when guest removes an entry (doorbell, handshake sequence)
> +     */
> +    EHCIqh qh;             // copy of current QH (being worked on)
> +    uint32_t qhaddr;       // address QH read from
> +
> +    EHCIqtd qtd;           // copy of current QTD (being worked on)
> +    uint32_t qtdaddr;      // address QTD read from
> +
> +    uint32_t itdaddr;      // current ITD
> +
> +    uint32_t fetch_addr;   // which address to look at next
> +
> +    USBBus bus;
> +    USBPacket usb_packet;
> +    int async_complete;
> +    uint32_t tbytes;
> +    int pid;
> +    int exec_status;
> +    int isoch_pause;
> +    uint32_t last_run_usec;
> +    uint32_t frame_end_usec;
> +} EHCIState;
> +
> +#define SET_LAST_RUN_CLOCK(s) \
> +    (s)->last_run_usec = qemu_get_clock_ns(vm_clock) / 1000;

Inline function, please. The correct way for a macro would be to
enclose it within 'do { } while(0)'.

> +
> +/* nifty macros from Arnon's EHCI version  */
> +#define get_field(data, field) \
> +    (((data) & field##_MASK) >> field##_SH)
> +
> +#define set_field(data, newval, field) do { \
> +    uint32_t val = *data; \
> +    val &= ~ field##_MASK; \
> +    val |= ((newval) << field##_SH) & field##_MASK; \
> +    *data = val; \
> +    } while(0)

More macro argument uses without parentheses. Please convert to an
inline function.

> +
> +
> +#if EHCI_DEBUG
> +static const char *addr2str(unsigned addr)
> +{
> +    const char *r            = "   unknown";
> +    const char *n[] = {

'static' to both above. Otherwise a compiler might construct the full
array to stack.

> +        [ CAPLENGTH ]        = " CAPLENGTH",
> +        [ HCIVERSION ]       = "HCIVERSION",
> +        [ HCSPARAMS ]        = " HCSPARAMS",
> +        [ HCCPARAMS ]        = " HCCPARAMS",
> +        [ USBCMD ]           = "   COMMAND",
> +        [ USBSTS ]           = "    STATUS",
> +        [ USBINTR ]          = " INTERRUPT",
> +        [ FRINDEX ]          = " FRAME IDX",
> +        [ PERIODICLISTBASE ] = "P-LIST BASE",
> +        [ ASYNCLISTADDR ]    = "A-LIST ADDR",
> +        [ PORTSC_BEGIN ...
> +          PORTSC_END ]       = "PORT STATUS",
> +        [ CONFIGFLAG ]       = "CONFIG FLAG",
> +    };
> +
> +    if (addr < ARRAY_SIZE(n) && n[addr] != NULL) {
> +        return n[addr];
> +    } else {
> +        return r;
> +    }
> +}
> +#endif
> +
> +
> +static inline void ehci_set_interrupt(EHCIState *s, int intr)
> +{
> +    int level = 0;
> +
> +    // TODO honour interrupt threshold requests
> +
> +    s->usbsts |= intr;
> +
> +    if ((s->usbsts & USBINTR_MASK) & s->usbintr) {

Should the second '&' be '&&'?

> +        level = 1;
> +    }
> +
> +    qemu_set_irq(s->irq, level);
> +}
> +
> +static inline void ehci_record_interrupt(EHCIState *s, int intr)
> +{
> +    s->usbsts_pending |= intr;
> +}
> +
> +static inline void ehci_commit_interrupt(EHCIState *s)
> +{
> +    if (!s->usbsts_pending) {
> +        return;
> +    }
> +    ehci_set_interrupt(s, s->usbsts_pending);
> +    s->usbsts_pending = 0;
> +}
> +
> +/* Attach or detach a device on root hub */
> +
> +static void ehci_attach(USBPort *port)
> +{
> +    EHCIState *s = port->opaque;
> +    uint32_t *portsc = &s->portsc[port->index];
> +
> +    DPRINTF("ehci_attach invoked for index %d, portsc 0x%x, desc %s\n",
> +           port->index, *portsc, port->dev->product_desc);
> +
> +    *portsc |= PORTSC_CONNECT;
> +    *portsc |= PORTSC_CSC;
> +
> +    /*
> +     *  If a high speed device is attached then we own this port(indicated
> +     *  by zero in the PORTSC_POWNER bit field) so set the status bit
> +     *  and set an interrupt if enabled.
> +     */
> +    if ( !(*portsc & PORTSC_POWNER)) {
> +        ehci_set_interrupt(s, USBSTS_PCD);
> +    }
> +}
> +
> +static void ehci_detach(USBPort *port)
> +{
> +    EHCIState *s = port->opaque;
> +    uint32_t *portsc = &s->portsc[port->index];
> +
> +    DPRINTF("ehci_attach invoked for index %d, portsc 0x%x\n",
> +           port->index, *portsc);
> +
> +    *portsc &= ~PORTSC_CONNECT;
> +    *portsc |= PORTSC_CSC;
> +
> +    /*
> +     *  If a high speed device is attached then we own this port(indicated
> +     *  by zero in the PORTSC_POWNER bit field) so set the status bit
> +     *  and set an interrupt if enabled.
> +     */
> +    if ( !(*portsc & PORTSC_POWNER)) {
> +        ehci_set_interrupt(s, USBSTS_PCD);
> +    }
> +}
> +
> +/* 4.1 host controller initialization */
> +static void ehci_reset(void *opaque)
> +{
> +    EHCIState *s = opaque;
> +    uint8_t *pci_conf;
> +    int i;
> +
> +    pci_conf = s->dev.config;
> +
> +    memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
> +
> +    s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
> +    s->usbsts = USBSTS_HALT;
> +
> +    s->astate = EST_INACTIVE;
> +    s->pstate = EST_INACTIVE;
> +    s->async_complete = 0;
> +    s->isoch_pause = -1;
> +    s->attach_poll_counter = 0;
> +
> +    for(i = 0; i < NB_PORTS; i++) {
> +        s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
> +
> +        if (s->ports[i].dev) {
> +            usb_attach(&s->ports[i], s->ports[i].dev);
> +        }
> +    }
> +}
> +
> +static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
> +{
> +    EHCIState *s = ptr;
> +    uint32_t val;
> +
> +    val = s->mmio[addr];
> +
> +    return val;
> +}
> +
> +static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
> +{
> +    EHCIState *s = ptr;
> +    uint32_t val;
> +
> +    val = s->mmio[addr] | (s->mmio[addr+1] << 8);
> +
> +    return val;
> +}
> +
> +static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
> +{
> +    EHCIState *s = ptr;
> +    uint32_t val;
> +
> +    val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
> +          (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
> +
> +    return val;
> +}
> +
> +static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
> +{
> +    fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
> +    exit(1);
> +}
> +
> +static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
> +{
> +    fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
> +    exit(1);
> +}
> +
> +static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
> +{
> +    uint32_t *portsc = &s->portsc[port];
> +    int rwc;
> +    USBDevice *dev = s->ports[port].dev;
> +
> +    DPRINTF("port_status_write: "
> +            "PORTSC (port %d) curr %08X new %08X rw-clear %08X rw %08X\n",
> +            port, *portsc, val, (val & PORTSC_RWC_MASK), val & PORTSC_RO_MASK);
> +
> +    rwc = val & PORTSC_RWC_MASK;
> +    val &= PORTSC_RO_MASK;
> +
> +    // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC);
> +
> +    *portsc &= ~rwc;
> +
> +    if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
> +        DPRINTF("port_status_write: USBTRAN Port %d reset begin\n", port);
> +    }
> +
> +    if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
> +        DPRINTF("port_status_write: USBTRAN Port %d reset done\n", port);
> +        usb_attach(&s->ports[port], dev);
> +
> +        // TODO how to handle reset of ports with no device
> +        if (dev) {
> +            usb_send_msg(dev, USB_MSG_RESET);
> +        }
> +
> +        if (s->ports[port].dev) {
> +            DPRINTF("port_status_write: "
> +                    "Device was connected before reset, clearing CSC bit\n");
> +            *portsc &= ~PORTSC_CSC;
> +        }
> +
> +        /*  Table 2.16 Set the enable bit(and enable bit change) to indicate
> +         *  to SW that this port has a high speed device attached
> +         *
> +         *  TODO - when to disable?
> +         */
> +        val |= PORTSC_PED;
> +        val |= PORTSC_PEDC;
> +    }
> +
> +    *portsc &= ~PORTSC_RO_MASK;
> +    *portsc |= val;
> +    DPRINTF("port_status_write: Port %d status set to 0x%08x\n", port, *portsc);
> +}
> +
> +static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
> +{
> +    EHCIState *s = ptr;
> +    int i;
> +#if EHCI_DEBUG
> +    const char *str;
> +#endif
> +
> +    /* Only aligned reads are allowed on OHCI */
> +    if (addr & 3) {
> +        fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
> +                TARGET_FMT_plx "\n", addr);

This and below should become DPRINTFs.

> +        return;
> +    }
> +
> +    if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
> +        handle_port_status_write(s, (addr-PORTSC)/4, val);
> +        return;
> +    }
> +
> +    if (addr < OPREGBASE) {
> +        fprintf(stderr, "usb-ehci: write attempt to read-only register"
> +                TARGET_FMT_plx "\n", addr);
> +        return;
> +    }
> +
> +
> +    /* Do any register specific pre-write processing here.  */
> +#if EHCI_DEBUG
> +    str = addr2str((unsigned) addr);
> +#endif
> +    switch(addr) {
> +    case USBCMD:
> +        DPRINTF("ehci_mem_writel: USBCMD val=0x%08X, current cmd=0x%08X\n",
> +                val, s->usbcmd);
> +
> +        if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
> +            DPRINTF("ehci_mem_writel: %s run, clear halt\n", str);
> +            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
> +            SET_LAST_RUN_CLOCK(s);
> +            s->usbsts &= ~USBSTS_HALT;
> +        }
> +
> +        if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
> +            DPRINTF("                         ** STOP **\n");
> +            qemu_del_timer(s->frame_timer);
> +            // TODO - should finish out some stuff before setting halt
> +            s->usbsts |= USBSTS_HALT;
> +        }
> +
> +        if (val & USBCMD_HCRESET) {
> +            DPRINTF("ehci_mem_writel: %s run, resetting\n", str);
> +            ehci_reset(s);
> +            val &= ~USBCMD_HCRESET;
> +        }
> +
> +        /* not supporting dynamic frame list size at the moment */
> +        if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
> +            fprintf(stderr, "attempt to set frame list size -- value %d\n",
> +                    val & USBCMD_FLS);
> +            val &= ~USBCMD_FLS;
> +        }
> +#if EHCI_DEBUG
> +        if ((val & USBCMD_PSE) && !(s->usbcmd & USBCMD_PSE)) {
> +            DPRINTF("periodic scheduling enabled\n");
> +        }
> +        if (!(val & USBCMD_PSE) && (s->usbcmd & USBCMD_PSE)) {
> +            DPRINTF("periodic scheduling disabled\n");
> +        }
> +        if ((val & USBCMD_ASE) && !(s->usbcmd & USBCMD_ASE)) {
> +            DPRINTF("asynchronous scheduling enabled\n");
> +        }
> +        if (!(val & USBCMD_ASE) && (s->usbcmd & USBCMD_ASE)) {
> +            DPRINTF("asynchronous scheduling disabled\n");
> +        }
> +        if ((val & USBCMD_IAAD) && !(s->usbcmd & USBCMD_IAAD)) {
> +            DPRINTF("doorbell request received\n");
> +        }
> +        if ((val & USBCMD_LHCR) && !(s->usbcmd & USBCMD_LHCR)) {
> +            DPRINTF("light host controller reset received\n");
> +        }
> +        if ((val & USBCMD_ITC) != (s->usbcmd & USBCMD_ITC)) {
> +            DPRINTF("interrupt threshold control set to %x\n",
> +                    (val & USBCMD_ITC)>>USBCMD_ITC_SH);
> +        }
> +#endif
> +        break;
> +
> +
> +    case USBSTS:
> +        val &= USBSTS_RO_MASK;              // bits 6 thru 31 are RO
> +        DPRINTF("ehci_mem_writel: %s RWC set to 0x%08X\n", str, val);
> +
> +        val = (s->usbsts &= ~val);         // bits 0 thru 5 are R/WC
> +
> +        DPRINTF("ehci_mem_writel: %s updating interrupt condition\n", str);
> +        ehci_set_interrupt(s, 0);
> +        break;
> +
> +
> +    case USBINTR:
> +        val &= USBINTR_MASK;
> +        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
> +        break;
> +
> +    case FRINDEX:
> +        s->sofv = val >> 3;
> +        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
> +        break;
> +
> +    case CONFIGFLAG:
> +        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
> +        val &= 0x1;
> +        if (val) {
> +            for(i = 0; i < NB_PORTS; i++)

Braces.

> +                s->portsc[i] &= ~PORTSC_POWNER;
> +        }
> +        break;
> +
> +    case PERIODICLISTBASE:
> +        if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
> +            fprintf(stderr,
> +              "ehci: PERIODIC list base register set while periodic schedule\n"
> +              "      is enabled and HC is enabled\n");
> +        }
> +        DPRINTF("ehci_mem_writel: P-LIST BASE set to 0x%08X\n", val);
> +        break;
> +
> +    case ASYNCLISTADDR:
> +        if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
> +            fprintf(stderr,
> +              "ehci: ASYNC list address register set while async schedule\n"
> +              "      is enabled and HC is enabled\n");
> +        }
> +        DPRINTF("ehci_mem_writel: A-LIST ADDR set to 0x%08X\n", val);
> +        break;
> +    }
> +
> +    *(uint32_t *)(&s->mmio[addr]) = val;
> +}
> +
> +
> +// TODO : Put in common header file, duplication from usb-ohci.c
> +
> +/* Get an array of dwords from main memory */
> +static inline int get_dwords(uint32_t addr, uint32_t *buf, int num)

Type of addr should probably be target_phys_addr_t.

> +{
> +    int i;
> +
> +    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
> +        cpu_physical_memory_rw(addr,(uint8_t *)buf, sizeof(*buf), 0);
> +        *buf = le32_to_cpu(*buf);
> +    }
> +
> +    return 1;
> +}
> +
> +/* Put an array of dwords in to main memory */
> +static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
> +{
> +    int i;
> +
> +    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
> +        uint32_t tmp = cpu_to_le32(*buf);
> +        cpu_physical_memory_rw(addr,(uint8_t *)&tmp, sizeof(tmp), 1);
> +    }
> +
> +    return 1;
> +}
> +
> +// 4.10.2
> +
> +static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
> +{
> +    int i;
> +    int dtoggle;
> +    int ping;
> +    int eps;
> +    int reload;
> +
> +    // remember values in fields to preserve in qh after overlay
> +
> +    dtoggle = qh->token & QTD_TOKEN_DTOGGLE;
> +    ping    = qh->token & QTD_TOKEN_PING;
> +
> +    DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->current_qtd,
> +            ehci->qtdaddr);
> +    qh->current_qtd = ehci->qtdaddr;
> +    qh->next_qtd    = qtd->next;
> +    qh->altnext_qtd = qtd->altnext;
> +    qh->token       = qtd->token;
> +
> +
> +    eps = get_field(qh->epchar, QH_EPCHAR_EPS);
> +    if (eps == EHCI_QH_EPS_HIGH) {
> +        qh->token &= ~QTD_TOKEN_PING;
> +        qh->token |= ping;
> +    }
> +
> +    reload = get_field(qh->epchar, QH_EPCHAR_RL);
> +    set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
> +
> +    for (i = 0; i < 5; i++) {
> +        qh->bufptr[i] = qtd->bufptr[i];
> +    }
> +
> +    if (!(qh->epchar & QH_EPCHAR_DTC)) {
> +        // preserve QH DT bit
> +        qh->token &= ~QTD_TOKEN_DTOGGLE;
> +        qh->token |= dtoggle;
> +    }
> +
> +    qh->bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
> +    qh->bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
> +
> +    put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
> +
> +    return 0;
> +}
> +
> +static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw)
> +{
> +    int bufpos = 0;
> +    int cpage, offset;
> +    uint32_t head;
> +    uint32_t tail;

target_phys_addr_t

> +
> +
> +    if (!bytes) {
> +        return 0;
> +    }
> +
> +    cpage = get_field(qh->token, QTD_TOKEN_CPAGE);
> +    if (cpage > 4) {
> +        fprintf(stderr, "cpage out of range (%d)\n", cpage);
> +        return USB_RET_PROCERR;
> +    }
> +
> +    offset = qh->bufptr[0] & ~QTD_BUFPTR_MASK;
> +    DPRINTF("ehci_buffer_rw: %sing %d bytes %08x cpage %d offset %d\n",
> +           rw ? "writ" : "read", bytes, qh->bufptr[0], cpage, offset);
> +
> +    do {
> +        /* start and end of this page */
> +        head = qh->bufptr[cpage] & QTD_BUFPTR_MASK;
> +        tail = head + ~QTD_BUFPTR_MASK + 1;
> +        /* add offset into page */
> +        head |= offset;
> +
> +        if (bytes <= (tail - head)) {
> +            tail = head + bytes;
> +        }
> +
> +        DPRINTF("DATA %s cpage:%d head:%08X tail:%08X target:%08X\n",
> +                rw ? "WRITE" : "READ ", cpage, head, tail, bufpos);
> +
> +        cpu_physical_memory_rw(head, &buffer[bufpos], tail - head, rw);
> +
> +        bufpos += (tail - head);
> +        bytes -= (tail - head);
> +
> +        if (bytes > 0) {
> +            cpage++;
> +            offset = 0;
> +        }
> +    } while (bytes > 0);
> +
> +    /* save cpage */
> +    set_field(&qh->token, cpage, QTD_TOKEN_CPAGE);
> +
> +    /* save offset into cpage */
> +    offset = tail - head;
> +    qh->bufptr[0] &= ~QTD_BUFPTR_MASK;
> +    qh->bufptr[0] |= offset;
> +
> +    return 0;
> +}
> +
> +static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet)
> +{
> +    EHCIState *ehci = container_of(packet, EHCIState, usb_packet);
> +
> +    DPRINTF("Async packet complete\n");
> +    ehci->async_complete = 1;
> +    ehci->exec_status = packet->len;
> +}
> +
> +static int ehci_execute_complete(EHCIState *ehci, EHCIqh *qh, int ret)
> +{
> +    int c_err, reload;
> +
> +    if (ret == USB_RET_ASYNC && !ehci->async_complete) {
> +        DPRINTF("not done yet\n");
> +        return ret;
> +    }
> +
> +    ehci->async_complete = 0;
> +
> +    DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
> +            ehci->qhaddr, qh->next, ehci->qtdaddr, ret);
> +
> +    if (ret < 0) {
> +err:
> +        /* TO-DO: put this is in a function that can be invoked below as well */
> +        c_err = get_field(qh->token, QTD_TOKEN_CERR);
> +        c_err--;
> +        set_field(&qh->token, c_err, QTD_TOKEN_CERR);
> +
> +        switch(ret) {
> +        case USB_RET_NODEV:
> +            fprintf(stderr, "USB no device\n");
> +            break;
> +        case USB_RET_STALL:
> +            fprintf(stderr, "USB stall\n");
> +            qh->token |= QTD_TOKEN_HALT;
> +            ehci_record_interrupt(ehci, USBSTS_ERRINT);
> +            break;
> +        case USB_RET_NAK:
> +            /* 4.10.3 */
> +            reload = get_field(qh->epchar, QH_EPCHAR_RL);
> +            if ((ehci->pid == USB_TOKEN_IN) && reload) {
> +                int nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
> +                nakcnt--;
> +                set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
> +            } else if (!reload) {
> +                return USB_RET_NAK;
> +            }
> +            break;
> +        case USB_RET_BABBLE:
> +            fprintf(stderr, "USB babble TODO\n");
> +            qh->token |= QTD_TOKEN_BABBLE;
> +            ehci_record_interrupt(ehci, USBSTS_ERRINT);
> +            break;
> +        default:
> +            fprintf(stderr, "USB invalid response %d to handle\n", ret);
> +            /* TO-DO: transaction error */
> +            ret = USB_RET_PROCERR;
> +            break;
> +        }
> +    } else {
> +        // DPRINTF("Short packet condition\n");

Commented out debug statement?

> +        // TODO check 4.12 for splits
> +
> +        if ((ret > ehci->tbytes) && (ehci->pid == USB_TOKEN_IN)) {
> +            ret = USB_RET_BABBLE;
> +            goto err;
> +        }
> +
> +        if (ehci->tbytes && ehci->pid == USB_TOKEN_IN) {
> +            if (ehci_buffer_rw(ehci->buffer, qh, ret, 1) != 0) {
> +                return USB_RET_PROCERR;
> +            }
> +            ehci->tbytes -= ret;
> +        } else {
> +            ehci->tbytes = 0;
> +        }
> +
> +        DPRINTF("updating tbytes to %d\n", ehci->tbytes);
> +        set_field(&qh->token, ehci->tbytes, QTD_TOKEN_TBYTES);
> +    }
> +
> +    qh->token ^= QTD_TOKEN_DTOGGLE;
> +    qh->token &= ~QTD_TOKEN_ACTIVE;
> +
> +    if ((ret >= 0) && (qh->token & QTD_TOKEN_IOC)) {
> +        ehci_record_interrupt(ehci, USBSTS_INT);
> +    }
> +
> +    return ret;
> +}
> +
> +// 4.10.3
> +
> +static int ehci_execute(EHCIState *ehci, EHCIqh *qh)
> +{
> +    USBPort *port;
> +    USBDevice *dev;
> +    int ret;
> +    int i;
> +    int endp;
> +    int devadr;
> +
> +    if ( !(qh->token & QTD_TOKEN_ACTIVE)) {
> +        fprintf(stderr, "Attempting to execute inactive QH\n");
> +        return USB_RET_PROCERR;
> +    }
> +
> +    ehci->tbytes = (qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
> +    if (ehci->tbytes > BUFF_SIZE) {
> +        fprintf(stderr, "Request for more bytes than allowed\n");
> +        return USB_RET_PROCERR;
> +    }
> +
> +    ehci->pid = (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
> +    switch(ehci->pid) {
> +        case 0: ehci->pid = USB_TOKEN_OUT; break;

Statements on lines of their own, please.

> +        case 1: ehci->pid = USB_TOKEN_IN; break;
> +        case 2: ehci->pid = USB_TOKEN_SETUP; break;
> +        default: fprintf(stderr, "bad token\n"); break;
> +    }
> +
> +    if ((ehci->tbytes && ehci->pid != USB_TOKEN_IN) &&
> +        (ehci_buffer_rw(ehci->buffer, qh, ehci->tbytes, 0) != 0)) {
> +        return USB_RET_PROCERR;
> +    }
> +
> +    endp = get_field(qh->epchar, QH_EPCHAR_EP);
> +    devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
> +
> +    ret = USB_RET_NODEV;
> +
> +    // TO-DO: associating device with ehci port
> +    for(i = 0; i < NB_PORTS; i++) {
> +        port = &ehci->ports[i];
> +        dev = port->dev;
> +
> +        // TODO sometime we will also need to check if we are the port owner
> +
> +        if (!(ehci->portsc[i] &(PORTSC_CONNECT))) {
> +            DPRINTF("Port %d, no exec, not connected(%08X)\n",
> +                    i, ehci->portsc[i]);
> +            continue;
> +        }
> +
> +        ehci->usb_packet.pid = ehci->pid;
> +        ehci->usb_packet.devaddr = devadr;
> +        ehci->usb_packet.devep = endp;
> +        ehci->usb_packet.data = ehci->buffer;
> +        ehci->usb_packet.len = ehci->tbytes;
> +
> +        ret = usb_handle_packet(dev, &ehci->usb_packet);
> +
> +        DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
> +                ehci->qhaddr, qh->next, ehci->qtdaddr, ehci->pid,
> +                ehci->usb_packet.len, ehci->tbytes, endp, ret);
> +
> +        if (ret != USB_RET_NODEV) {
> +            break;
> +        }
> +    }
> +
> +    if (ret > BUFF_SIZE) {
> +        fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
> +        return USB_RET_PROCERR;
> +    }
> +
> +    if (ret == USB_RET_ASYNC) {
> +        ehci->async_complete = 0;
> +    }
> +
> +    return ret;
> +}
> +
> +/*  4.7.2
> + */
> +
> +static int ehci_process_itd(EHCIState *ehci,
> +                            EHCIitd *itd)
> +{
> +    USBPort *port;
> +    USBDevice *dev;
> +    int ret;
> +    int i, j;
> +    int ptr;
> +    int pid;
> +    int pg;
> +    int len;
> +    int dir;
> +    int devadr;
> +    int endp;
> +    int maxpkt;
> +
> +    dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
> +    devadr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
> +    endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
> +    maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
> +
> +    for(i = 0; i < 8; i++) {
> +        if (itd->transact[i] & ITD_XACT_ACTIVE) {
> +            DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n",
> +                    ehci->frindex >> 3, i);
> +
> +            pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
> +            ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) |
> +                (itd->transact[i] & ITD_XACT_OFFSET_MASK);
> +            len = get_field(itd->transact[i], ITD_XACT_LENGTH);
> +
> +            if (len > BUFF_SIZE) {
> +                return USB_RET_PROCERR;
> +            }
> +
> +            DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len);
> +
> +            if (!dir) {
> +                cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0);
> +                pid = USB_TOKEN_OUT;
> +            } else
> +                pid = USB_TOKEN_IN;

b-word

> +
> +            ret = USB_RET_NODEV;
> +
> +            for (j = 0; j < NB_PORTS; j++) {
> +                port = &ehci->ports[j];
> +                dev = port->dev;
> +
> +                // TODO sometime we will also need to check if we are the port owner
> +
> +                if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
> +                    DPRINTF("Port %d, no exec, not connected(%08X)\n",
> +                            j, ehci->portsc[j]);
> +                    continue;
> +                }
> +
> +                ehci->usb_packet.pid = ehci->pid;
> +                ehci->usb_packet.devaddr = devadr;
> +                ehci->usb_packet.devep = endp;
> +                ehci->usb_packet.data = ehci->buffer;
> +                ehci->usb_packet.len = len;
> +
> +                DPRINTF("calling usb_handle_packet\n");
> +                ret = usb_handle_packet(dev, &ehci->usb_packet);
> +
> +                if (ret != USB_RET_NODEV) {
> +                    break;
> +                }
> +            }
> +
> +            /*  In isoch, there is no facility to indicate a NAK so let's
> +             *  instead just complete a zero-byte transaction.  Setting
> +             *  DBERR seems too draconian.
> +             */
> +
> +            if (ret == USB_RET_NAK) {
> +                if (ehci->isoch_pause > 0) {
> +                    DPRINTF("ISOCH: received a NAK but paused so returning\n");
> +                    ehci->isoch_pause--;
> +                    return 0;
> +                } else if (ehci->isoch_pause == -1) {
> +                    DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
> +                    // Pause frindex for up to 50 msec waiting for data from
> +                    // remote
> +                    ehci->isoch_pause = 50;
> +                    return 0;
> +                } else {
> +                    DPRINTF("ISOCH: isoch pause timeout! return 0\n");
> +                    ret = 0;
> +                }
> +            } else {
> +                DPRINTF("ISOCH: received ACK, clearing pause\n");
> +                ehci->isoch_pause = -1;
> +            }
> +
> +            if (ret >= 0) {
> +                itd->transact[i] &= ~ITD_XACT_ACTIVE;
> +
> +                if (itd->transact[i] & ITD_XACT_IOC) {
> +                    ehci_record_interrupt(ehci, USBSTS_INT);
> +                }
> +            }
> +
> +            if (ret >= 0 && dir) {
> +                cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1);
> +
> +                if (ret != len) {
> +                    DPRINTF("ISOCH IN expected %d, got %d\n",
> +                            len, ret);
> +                    set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
> +                }
> +            }
> +        }
> +    }
> +    return 0;
> +}
> +
> +/*  This state is the entry point for asynchronous schedule
> + *  processing.  Entry here consitutes a EHCI start event state (4.8.5)
> + */
> +static int ehci_state_waitlisthead(EHCIState *ehci,  int async, int *state)
> +{
> +    EHCIqh *qh = &ehci->qh;
> +    int i = 0;
> +    int again = 0;
> +    uint32_t entry = ehci->asynclistaddr;
> +
> +    /* set reclamation flag at start event (4.8.6) */
> +    if (async) {
> +        ehci->usbsts |= USBSTS_REC;
> +    }
> +
> +    /*  Find the head of the list (4.9.1.1) */
> +    for(i = 0; i < MAX_QH; i++) {
> +        get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
> +
> +        if (qh->epchar & QH_EPCHAR_H) {
> +            DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n",
> +                       entry);
> +            if (async) {
> +                entry |= (NLPTR_TYPE_QH << 1);
> +            }
> +
> +            ehci->fetch_addr = entry;
> +            *state = EST_FETCHENTRY;
> +            again = 1;
> +            goto out;
> +        }
> +
> +        DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n",
> +                   entry);
> +        entry = qh->next;
> +        if (entry == ehci->asynclistaddr) {
> +            DPRINTF("WAITLISTHEAD: reached beginning of QH list\n");
> +            break;
> +        }
> +    }
> +
> +    /* no head found for list. */
> +
> +    *state = EST_ACTIVE;
> +
> +out:
> +    return again;
> +}
> +
> +
> +/*  This state is the entry point for periodic schedule processing as
> + *  well as being a continuation state for async processing.
> + */
> +static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
> +{
> +    int again = 0;
> +    uint32_t entry = ehci->fetch_addr;
> +
> +#if EHCI_DEBUG == 0
> +    if (qemu_get_clock_ns(vm_clock) / 1000 >= ehci->frame_end_usec) {
> +        if (async) {
> +            DPRINTF("FETCHENTRY: FRAME timer elapsed, exit state machine\n");
> +            goto out;
> +        } else {
> +            DPRINTF("FETCHENTRY: WARNING "
> +                    "- frame timer elapsed during periodic\n");
> +        }
> +    }
> +#endif
> +    if (entry < 0x1000) {
> +        DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry);
> +        *state = EST_ACTIVE;
> +        goto out;
> +    }
> +
> +    /* section 4.8, only QH in async schedule */
> +    if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) {
> +        fprintf(stderr, "non queue head request in async schedule\n");
> +        return -1;
> +    }
> +
> +    switch (NLPTR_TYPE_GET(entry)) {
> +    case NLPTR_TYPE_QH:
> +        DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", entry);
> +        *state = EST_FETCHQH;
> +        ehci->qhaddr = entry;
> +        again = 1;
> +        break;
> +
> +    case NLPTR_TYPE_ITD:
> +        DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", entry);
> +        *state = EST_FETCHITD;
> +        ehci->itdaddr = entry;
> +        again = 1;
> +        break;
> +
> +    default:
> +        // TODO: handle siTD and FSTN types
> +        fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
> +                "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
> +        return -1;
> +    }
> +
> +out:
> +    return again;
> +}
> +
> +static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
> +{
> +    EHCIqh *qh = &ehci->qh;
> +    int reload;
> +    int again = 0;
> +
> +    get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
> +
> +    if (async && (qh->epchar & QH_EPCHAR_H)) {
> +
> +        /*  EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
> +        if (ehci->usbsts & USBSTS_REC) {
> +            ehci->usbsts &= ~USBSTS_REC;
> +        } else {
> +            DPRINTF("FETCHQH:  QH 0x%08x. H-bit set, reclamation status reset"
> +                       " - done processing\n", ehci->qhaddr);
> +            *state = EST_ACTIVE;
> +            goto out;
> +        }
> +    }
> +
> +#if EHCI_DEBUG
> +    if (ehci->qhaddr != qh->next) {
> +    DPRINTF("FETCHQH:  QH 0x%08x (h %x halt %x active %x) next 0x%08x\n",
> +               ehci->qhaddr,
> +               qh->epchar & QH_EPCHAR_H,
> +               qh->token & QTD_TOKEN_HALT,
> +               qh->token & QTD_TOKEN_ACTIVE,
> +               qh->next);
> +    }
> +#endif
> +
> +    reload = get_field(qh->epchar, QH_EPCHAR_RL);
> +    if (reload) {
> +        DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload);
> +        set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
> +    }
> +
> +    if (qh->token & QTD_TOKEN_HALT) {
> +        DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n");
> +        *state = EST_HORIZONTALQH;
> +        again = 1;
> +
> +    } else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) {
> +        DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n");
> +        ehci->qtdaddr = qh->current_qtd;
> +        *state = EST_FETCHQTD;
> +        again = 1;
> +
> +    } else {
> +        /*  EHCI spec version 1.0 Section 4.10.2 */
> +        DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n");
> +        *state = EST_ADVANCEQUEUE;
> +        again = 1;
> +    }
> +
> +out:
> +    return again;
> +}
> +
> +static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state)
> +{
> +    EHCIitd itd;
> +
> +    get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
> +               sizeof(EHCIitd) >> 2);
> +    DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n",
> +               ehci->itdaddr, itd.next);
> +
> +    if (ehci_process_itd(ehci, &itd) != 0) {
> +        return -1;
> +    }
> +
> +    put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd,
> +                sizeof(EHCIitd) >> 2);
> +    ehci->fetch_addr = itd.next;
> +    *state = EST_FETCHENTRY;
> +
> +    return 1;
> +}
> +
> +/* Section 4.10.2 - paragraph 3 */
> +static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
> +{
> +#if 0
> +    /* TO-DO: 4.10.2 - paragraph 2
> +     * if I-bit is set to 1 and QH is not active
> +     * go to horizontal QH
> +     */
> +    if (I-bit set) {
> +        *state = EST_HORIZONTALQH;
> +        goto out;
> +    }
> +#endif
> +
> +    /*
> +     * want data and alt-next qTD is valid
> +     */
> +    if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
> +        (ehci->qh.altnext_qtd > 0x1000) &&
> +        (NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) {
> +        DPRINTF_ST("ADVQUEUE: goto alt next qTD. "
> +                   "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
> +                   ehci->qh.current_qtd, ehci->qh.altnext_qtd,
> +                   ehci->qh.next_qtd, ehci->qh.next);
> +        ehci->qtdaddr = ehci->qh.altnext_qtd;
> +        *state = EST_FETCHQTD;
> +
> +    /*
> +     *  next qTD is valid
> +     */
> +    } else if ((ehci->qh.next_qtd > 0x1000) &&
> +               (NLPTR_TBIT(ehci->qh.next_qtd) == 0)) {
> +        DPRINTF_ST("ADVQUEUE: next qTD. "
> +                   "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
> +                   ehci->qh.current_qtd, ehci->qh.altnext_qtd,
> +                   ehci->qh.next_qtd, ehci->qh.next);
> +        ehci->qtdaddr = ehci->qh.next_qtd;
> +        *state = EST_FETCHQTD;
> +
> +    /*
> +     *  no valid qTD, try next QH
> +     */
> +    } else {
> +        DPRINTF_ST("ADVQUEUE: go to horizontal QH\n");
> +        *state = EST_HORIZONTALQH;
> +    }
> +
> +    return 1;
> +}
> +
> +/* Section 4.10.2 - paragraph 4 */
> +static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state)
> +{
> +    EHCIqtd *qtd = &ehci->qtd;
> +    int again = 0;
> +
> +    get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2);
> +
> +    if (qtd->token & QTD_TOKEN_ACTIVE) {
> +        *state = EST_EXECUTE;
> +        again = 1;
> +    } else {
> +        *state = EST_HORIZONTALQH;
> +        again = 1;
> +    }
> +
> +    return again;
> +}
> +
> +static int ehci_state_horizqh(EHCIState *ehci, int async, int *state)
> +{
> +    int again = 0;
> +
> +    if (ehci->fetch_addr != ehci->qh.next) {
> +        ehci->fetch_addr = ehci->qh.next;
> +        *state = EST_FETCHENTRY;
> +        again = 1;
> +    } else {
> +        *state = EST_ACTIVE;
> +    }
> +
> +    return again;
> +}
> +
> +static int ehci_state_execute(EHCIState *ehci, int async, int *state)
> +{
> +    EHCIqh *qh = &ehci->qh;
> +    EHCIqtd *qtd = &ehci->qtd;
> +    int again = 0;
> +    int reload, nakcnt;
> +    int smask;
> +
> +    if (async) {
> +        DPRINTF_ST(">>>>> ASYNC STATE MACHINE execute QH 0x%08x, QTD 0x%08x\n",
> +                  ehci->qhaddr, ehci->qtdaddr);
> +    } else {
> +        DPRINTF_ST(">>>>> PERIODIC STATE MACHINE execute\n");
> +    }
> +
> +    if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) {
> +        return -1;
> +    }
> +
> +    smask = get_field(qh->epcap, QH_EPCAP_SMASK);
> +
> +    if (!smask) {
> +        reload = get_field(qh->epchar, QH_EPCHAR_RL);
> +        nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
> +        if (reload && !nakcnt) {
> +            DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n");
> +            *state = EST_HORIZONTALQH;
> +            again = 1;
> +            goto out;
> +        }
> +    }
> +
> +    // TODO verify enough time remains in the uframe as in 4.4.1.1
> +    // TODO write back ptr to async list when done or out of time
> +    // TODO Windows does not seem to ever set the MULT field
> +
> +    if (!async) {
> +        int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
> +        if (!transactCtr) {
> +            DPRINTF("ZERO transactctr for int qh, go HORIZ\n");
> +            *state = EST_HORIZONTALQH;
> +            again = 1;
> +            goto out;
> +        }
> +    }
> +
> +    if (async) {
> +        ehci->usbsts |= USBSTS_REC;
> +    }
> +
> +    ehci->exec_status = ehci_execute(ehci, qh);
> +    if (ehci->exec_status == USB_RET_PROCERR) {
> +        again = -1;
> +        goto out;
> +    }
> +    *state = EST_EXECUTING;
> +
> +    if (ehci->exec_status != USB_RET_ASYNC) {
> +        again = 1;
> +    }
> +
> +out:
> +    return again;
> +}
> +
> +static int ehci_state_executing(EHCIState *ehci, int async, int *state)
> +{
> +    EHCIqh *qh = &ehci->qh;
> +    int again = 0;
> +    int reload, nakcnt;
> +
> +    ehci->exec_status = ehci_execute_complete(ehci, qh, ehci->exec_status);
> +    if (ehci->exec_status == USB_RET_ASYNC) {
> +        goto out;
> +    }
> +    if (ehci->exec_status == USB_RET_PROCERR) {
> +        again = -1;
> +        goto out;
> +    }
> +
> +    // 4.10.3
> +    if (!async) {
> +        int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
> +        transactCtr--;
> +        set_field(&qh->epcap, transactCtr, QH_EPCAP_MULT);
> +        // 4.10.3, bottom of page 82, should exit this state when transaction
> +        // counter decrements to 0
> +    }
> +
> +
> +    reload = get_field(qh->epchar, QH_EPCHAR_RL);
> +    if (reload) {
> +        nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
> +        if (ehci->exec_status == USB_RET_NAK) {
> +            if (nakcnt) {
> +                nakcnt--;
> +            }
> +            DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n",
> +                    nakcnt);
> +        } else {
> +            nakcnt = reload;
> +            DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n",
> +                       nakcnt);
> +        }
> +        set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
> +    }
> +
> +    /*
> +     *  Write the qh back to guest physical memory.  This step isn't
> +     *  in the EHCI spec but we need to do it since we don't share
> +     *  physical memory with our guest VM.
> +     */
> +
> +    DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, next 0x%x\n",
> +              ehci->qhaddr, qh->next);
> +    put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
> +
> +    /* 4.10.5 */
> +    if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) {
> +        *state = EST_HORIZONTALQH;
> +    } else {
> +        *state = EST_WRITEBACK;
> +    }
> +
> +    again = 1;
> +
> +out:
> +    return again;
> +}
> +
> +
> +static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
> +{
> +    EHCIqh *qh = &ehci->qh;
> +    int again = 0;
> +
> +    /*  Write back the QTD from the QH area */
> +    DPRINTF_ST("WRITEBACK: write QTD to VM memory\n");
> +    put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd,

Space after comma.

> +                sizeof(EHCIqtd) >> 2);
> +
> +    /* TODO confirm next state.  For now, keep going if async
> +     * but stop after one qtd if periodic
> +     */
> +    //if (async) {
> +        *state = EST_ADVANCEQUEUE;
> +        again = 1;
> +    //} else {
> +    //    *state = EST_ACTIVE;
> +    //}
> +    return again;
> +}
> +
> +/*
> + * This is the state machine that is common to both async and periodic
> + */
> +
> +static int ehci_advance_state(EHCIState *ehci,
> +                              int async,
> +                              int state)
> +{
> +    int again;
> +    int iter = 0;
> +
> +    do {
> +        if (state == EST_FETCHQH) {
> +            iter++;
> +            /* if we are roaming a lot of QH without executing a qTD
> +             * something is wrong with the linked list. TO-DO: why is
> +             * this hack needed?
> +             */
> +            if (iter > MAX_ITERATIONS) {
> +                DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n");
> +                state = EST_ACTIVE;
> +                break;
> +            }
> +        }
> +        switch(state) {
> +        case EST_WAITLISTHEAD:
> +            again = ehci_state_waitlisthead(ehci, async, &state);
> +            break;
> +
> +        case EST_FETCHENTRY:
> +            again = ehci_state_fetchentry(ehci, async, &state);
> +            break;
> +
> +        case EST_FETCHQH:
> +            again = ehci_state_fetchqh(ehci, async, &state);
> +            break;
> +
> +        case EST_FETCHITD:
> +            again = ehci_state_fetchitd(ehci, async, &state);
> +            break;
> +
> +        case EST_ADVANCEQUEUE:
> +            again = ehci_state_advqueue(ehci, async, &state);
> +            break;
> +
> +        case EST_FETCHQTD:
> +            again = ehci_state_fetchqtd(ehci, async, &state);
> +            break;
> +
> +        case EST_HORIZONTALQH:
> +            again = ehci_state_horizqh(ehci, async, &state);
> +            break;
> +
> +        case EST_EXECUTE:
> +            iter = 0;
> +            again = ehci_state_execute(ehci, async, &state);
> +            break;
> +
> +        case EST_EXECUTING:
> +            again = ehci_state_executing(ehci, async, &state);
> +            break;
> +
> +        case EST_WRITEBACK:
> +            again = ehci_state_writeback(ehci, async, &state);
> +            break;
> +
> +        default:
> +            fprintf(stderr, "Bad state!\n");
> +            again = -1;
> +            break;
> +        }
> +
> +        if (again < 0) {
> +            fprintf(stderr, "processing error - resetting ehci HC\n");
> +            ehci_reset(ehci);
> +            again = 0;
> +        }
> +    }
> +    while (again);
> +
> +    ehci_commit_interrupt(ehci);
> +    return state;
> +}
> +
> +static void ehci_advance_async_state(EHCIState *ehci)
> +{
> +    EHCIqh qh;
> +    int state = ehci->astate;
> +
> +    switch(state) {
> +    case EST_INACTIVE:
> +        if (!(ehci->usbcmd & USBCMD_ASE)) {
> +            break;
> +        }
> +        ehci->usbsts |= USBSTS_ASS;
> +        ehci->astate = EST_ACTIVE;
> +        // No break, fall through to ACTIVE
> +
> +    case EST_ACTIVE:
> +        if ( !(ehci->usbcmd & USBCMD_ASE)) {
> +            ehci->usbsts &= ~USBSTS_ASS;
> +            ehci->astate = EST_INACTIVE;
> +            break;
> +        }
> +
> +        /* If the doorbell is set, the guest wants to make a change to the
> +         * schedule. The host controller needs to release cached data.
> +         * (section 4.8.2)
> +         */
> +        if (ehci->usbcmd & USBCMD_IAAD) {
> +            DPRINTF("ASYNC: doorbell request acknowledged\n");
> +            ehci->usbcmd &= ~USBCMD_IAAD;
> +            ehci_set_interrupt(ehci, USBSTS_IAA);
> +            break;
> +        }
> +
> +        /* make sure guest has acknowledged */
> +        /* TO-DO: is this really needed? */
> +        if (ehci->usbsts & USBSTS_IAA) {
> +            DPRINTF("IAA status bit still set.\n");
> +            break;
> +        }
> +
> +        DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n",
> +                ehci->asynclistaddr);
> +        /* check that address register has been set */
> +        if (ehci->asynclistaddr == 0) {
> +            break;
> +        }
> +
> +        state = EST_WAITLISTHEAD;
> +        /* fall through */
> +
> +    case EST_FETCHENTRY:
> +        /* fall through */
> +
> +    case EST_EXECUTING:
> +        get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh,
> +                   sizeof(EHCIqh) >> 2);
> +        ehci->astate = ehci_advance_state(ehci, 1, state);
> +        break;
> +
> +    default:
> +        /* this should only be due to a developer mistake */
> +        fprintf(stderr, "ehci: Bad asynchronous state %d. "
> +                "Resetting to active\n", ehci->astate);
> +        ehci->astate = EST_ACTIVE;
> +    }
> +}
> +
> +static void ehci_advance_periodic_state(EHCIState *ehci)
> +{
> +    uint32_t entry;
> +    uint32_t list;
> +
> +    // 4.6
> +
> +    switch(ehci->pstate) {
> +    case EST_INACTIVE:
> +        if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {

Extra space here.

> +            DPRINTF("PERIODIC going active\n");
> +            ehci->usbsts |= USBSTS_PSS;
> +            ehci->pstate = EST_ACTIVE;
> +            // No break, fall through to ACTIVE
> +        } else
> +            break;
> +
> +    case EST_ACTIVE:
> +        if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
> +            DPRINTF("PERIODIC going inactive\n");
> +            ehci->usbsts &= ~USBSTS_PSS;
> +            ehci->pstate = EST_INACTIVE;
> +            break;
> +        }
> +
> +        list = ehci->periodiclistbase & 0xfffff000;
> +        /* check that register has been set */
> +        if (list == 0) {
> +            break;
> +        }
> +        list |= ((ehci->frindex & 0x1ff8) >> 1);
> +
> +        cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0);
> +        entry = le32_to_cpu(entry);
> +
> +        DPRINTF("PERIODIC state adv fr=%d.  [%08X] -> %08X\n",
> +                ehci->frindex / 8, list, entry);
> +        ehci->fetch_addr = entry;
> +        ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY);
> +        break;
> +
> +    case EST_EXECUTING:
> +        DPRINTF("PERIODIC state adv for executing\n");
> +        ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING);
> +        break;
> +
> +    default:
> +        /* this should only be due to a developer mistake */
> +        fprintf(stderr, "ehci: Bad periodic state %d. "
> +                "Resetting to active\n", ehci->pstate);
> +        ehci->pstate = EST_ACTIVE;
> +    }
> +}
> +
> +static void ehci_frame_timer(void *opaque)
> +{
> +    EHCIState *ehci = opaque;
> +    int64_t expire_time, t_now;
> +    int usec_elapsed;
> +    int frames;
> +    int usec_now;
> +    int i;
> +    int skipped_frames = 0;
> +
> +
> +    t_now = qemu_get_clock_ns(vm_clock);
> +    expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
> +    if (expire_time == t_now) {
> +        expire_time++;
> +    }
> +
> +    usec_now = t_now / 1000;
> +    usec_elapsed = usec_now - ehci->last_run_usec;
> +    frames = usec_elapsed / FRAME_TIMER_USEC;
> +    ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC - 10;
> +
> +    for (i = 0; i < frames; i++) {
> +        if ( !(ehci->usbsts & USBSTS_HALT)) {
> +            if (ehci->isoch_pause <= 0) {
> +                ehci->frindex += 8;
> +            }
> +
> +            if (ehci->frindex > 0x00001fff) {
> +                ehci->frindex = 0;
> +                ehci_set_interrupt(ehci, USBSTS_FLR);
> +            }
> +
> +            ehci->sofv = (ehci->frindex - 1) >> 3;
> +            ehci->sofv &= 0x000003ff;
> +        }
> +
> +        if (frames - i > 10) {
> +            skipped_frames++;
> +        } else {
> +            // TODO could this cause periodic frames to get skipped if async
> +            // active?
> +            if (ehci->astate != EST_EXECUTING) {
> +                ehci_advance_periodic_state(ehci);
> +            }
> +        }
> +
> +        ehci->last_run_usec += FRAME_TIMER_USEC;
> +    }
> +
> +#if 0
> +    if (skipped_frames) {
> +        DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
> +    }
> +#endif
> +
> +    /*  Async is not inside loop since it executes everything it can once
> +     *  called
> +     */
> +    if (ehci->pstate != EST_EXECUTING) {
> +        ehci_advance_async_state(ehci);
> +    }
> +
> +    qemu_mod_timer(ehci->frame_timer, expire_time);
> +}
> +
> +static CPUReadMemoryFunc *ehci_readfn[3]={

'const', spaces around '='

> +    ehci_mem_readb,
> +    ehci_mem_readw,
> +    ehci_mem_readl
> +};
> +
> +static CPUWriteMemoryFunc *ehci_writefn[3]={
> +    ehci_mem_writeb,
> +    ehci_mem_writew,
> +    ehci_mem_writel
> +};
> +
> +static void ehci_map(PCIDevice *pci_dev, int region_num,
> +                     pcibus_t addr, pcibus_t size, int type)
> +{
> +    EHCIState *s =(EHCIState *)pci_dev;
> +
> +    DPRINTF("ehci_map: region %d, addr %08" PRIx64 ", size %" PRId64 ", s->mem %08X\n",

Instead of PRIx64, please use FMT_PCIBUS.

> +            region_num, addr, size, s->mem);
> +    s->mem_base = addr;
> +    cpu_register_physical_memory(addr, size, s->mem);
> +}
> +
> +static int usb_ehci_initfn(PCIDevice *dev);
> +
> +static USBPortOps ehci_port_ops = {
> +    .attach = ehci_attach,
> +    .detach = ehci_detach,
> +    .complete = ehci_async_complete_packet,
> +};
> +
> +static PCIDeviceInfo ehci_info = {
> +    .qdev.name    = "usb-ehci",
> +    .qdev.size    = sizeof(EHCIState),
> +    .init         = usb_ehci_initfn,
> +};
> +
> +static int usb_ehci_initfn(PCIDevice *dev)
> +{
> +    EHCIState *s = DO_UPCAST(EHCIState, dev, dev);
> +    uint8_t *pci_conf = s->dev.config;
> +    int i;
> +
> +    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
> +    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82801D);
> +    pci_set_byte(&pci_conf[PCI_REVISION_ID], 0x10);
> +    pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
> +    pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
> +    pci_set_byte(&pci_conf[PCI_HEADER_TYPE], PCI_HEADER_TYPE_NORMAL);
> +
> +    /* capabilities pointer */
> +    pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
> +    //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
> +
> +    pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrupt pin 3
> +    pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
> +    pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
> +
> +    // pci_conf[0x50] = 0x01; // power management caps
> +
> +    pci_set_byte(&pci_conf[0x60], 0x20);  // spec release number (2.1.4)
> +    pci_set_byte(&pci_conf[0x61], 0x20);  // frame length adjustment (2.1.5)
> +    pci_set_word(&pci_conf[0x62], 0x00);  // port wake up capability (2.1.6)
> +
> +    pci_conf[0x64] = 0x00;
> +    pci_conf[0x65] = 0x00;
> +    pci_conf[0x66] = 0x00;
> +    pci_conf[0x67] = 0x00;
> +    pci_conf[0x68] = 0x01;
> +    pci_conf[0x69] = 0x00;
> +    pci_conf[0x6a] = 0x00;
> +    pci_conf[0x6b] = 0x00;  // USBLEGSUP
> +    pci_conf[0x6c] = 0x00;
> +    pci_conf[0x6d] = 0x00;
> +    pci_conf[0x6e] = 0x00;
> +    pci_conf[0x6f] = 0xc0;  // USBLEFCTLSTS
> +
> +    // 2.2 host controller interface version
> +    s->mmio[0x00] = (uint8_t) OPREGBASE;
> +    s->mmio[0x01] = 0x00;
> +    s->mmio[0x02] = 0x00;
> +    s->mmio[0x03] = 0x01;        // HC version
> +    s->mmio[0x04] = NB_PORTS;    // Number of downstream ports
> +    s->mmio[0x05] = 0x00;        // No companion ports at present
> +    s->mmio[0x06] = 0x00;
> +    s->mmio[0x07] = 0x00;
> +    s->mmio[0x08] = 0x80;        // We can cache whole frame, not 64-bit capable
> +    s->mmio[0x09] = 0x68;        // EECP
> +    s->mmio[0x0a] = 0x00;
> +    s->mmio[0x0b] = 0x00;
> +
> +    s->irq = s->dev.irq[3];
> +
> +    usb_bus_new(&s->bus, &s->dev.qdev);
> +    for(i = 0; i < NB_PORTS; i++) {
> +        usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
> +                          USB_SPEED_MASK_HIGH);
> +        usb_port_location(&s->ports[i], NULL, i+1);
> +        s->ports[i].dev = 0;
> +    }
> +
> +    s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
> +
> +    qemu_register_reset(ehci_reset, s);

qdev.reset

> +
> +    s->mem = cpu_register_io_memory(ehci_readfn, ehci_writefn, s,
> +                                    DEVICE_LITTLE_ENDIAN);
> +
> +    pci_register_bar(&s->dev, 0, MMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY,
> +                                                            ehci_map);
> +
> +    fprintf(stderr, "*** EHCI support is under development ***\n");

Right...

> +
> +    return 0;
> +}
> +
> +static void ehci_register(void)
> +{
> +    pci_qdev_register(&ehci_info);
> +}
> +device_init(ehci_register);
> +
> +/*
> + * vim: expandtab ts=4
> + */
> --
> 1.7.1
>
>
>

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

* Re: [Qemu-devel] [PATCH 18/18] usb: add ehci adapter
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 18/18] usb: add ehci adapter Gerd Hoffmann
  2011-05-23 19:25   ` Blue Swirl
@ 2011-05-24 15:45   ` Erik Rull
  1 sibling, 0 replies; 28+ messages in thread
From: Erik Rull @ 2011-05-24 15:45 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Vincent Palatin, Jan Kiszka, Kevin Wolf, qemu-devel, David S. Ahern

Hi Gerd,

could you provide a single patch file for the new USB files against the 
released qemu 0.14.0 or 0.14.1? I tried to create that file by assembling 
the patch emails but no real success.

Feedback on the patch is guaranteed :-)

Thanks a lot!

Best regards,

Erik

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

* Re: [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support.
  2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
                   ` (17 preceding siblings ...)
  2011-05-23  9:43 ` [Qemu-devel] [PATCH 18/18] usb: add ehci adapter Gerd Hoffmann
@ 2011-05-26 10:13 ` Gerd Hoffmann
  2011-05-31 13:37   ` Anthony Liguori
  18 siblings, 1 reply; 28+ messages in thread
From: Gerd Hoffmann @ 2011-05-26 10:13 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On 05/23/11 11:43, Gerd Hoffmann wrote:
>    Hi,
>
> Here is the usb patch queue, with EHCI support being the outstanding
> new feature.  Most patches are unmodified.  Patch #5 got a better commit
> message.  The EHCI patch now lists all contributes in the commit message
> too (they where listed in the source code only before), I hope everybody
> is happy with that now.

> are available in the git repository at:
>    git://git.kraxel.org/qemu usb.13.pull

Pushed a slightly updated usb.14.pull branch.  Rebased to latest master. 
  Fixed the FSF address as noted by blueswirl.  No other changes, so I 
don't spam the list again with the whole series.

The issue with async packets on unplug noticed by Hans (which isn't new) 
will be fixed with the next batch of usb patches.

please pull,
   Gerd

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

* Re: [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support.
  2011-05-26 10:13 ` [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
@ 2011-05-31 13:37   ` Anthony Liguori
  0 siblings, 0 replies; 28+ messages in thread
From: Anthony Liguori @ 2011-05-31 13:37 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On 05/26/2011 05:13 AM, Gerd Hoffmann wrote:
> On 05/23/11 11:43, Gerd Hoffmann wrote:
>> Hi,
>>
>> Here is the usb patch queue, with EHCI support being the outstanding
>> new feature. Most patches are unmodified. Patch #5 got a better commit
>> message. The EHCI patch now lists all contributes in the commit message
>> too (they where listed in the source code only before), I hope everybody
>> is happy with that now.
>
>> are available in the git repository at:
>> git://git.kraxel.org/qemu usb.13.pull
>
> Pushed a slightly updated usb.14.pull branch. Rebased to latest master.
> Fixed the FSF address as noted by blueswirl. No other changes, so I
> don't spam the list again with the whole series.
>
> The issue with async packets on unplug noticed by Hans (which isn't new)
> will be fixed with the next batch of usb patches.

Pulled.  Thanks.

Regards,

Anthony Liguori

>
> please pull,
> Gerd
>
>
>

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

end of thread, other threads:[~2011-05-31 13:37 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-23  9:43 [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 01/18] usb: Add Interface Association Descriptor descriptor type Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 02/18] usb: update config descriptors to identify number of interfaces Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 03/18] usb: remove fallback to bNumInterfaces if no .nif Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 04/18] usb: add support for "grouped" interfaces and the Interface Association Descriptor Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 05/18] Bug #757654: UHCI fails to signal stall response patch Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 06/18] usb: Pass the packet to the device's handle_control callback Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 07/18] usb-linux: use usb_generic_handle_packet() Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 08/18] usb-linux: fix device path aka physical port handling Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 09/18] usb-linux: add hostport property Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 10/18] usb-linux: track aurbs in list Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 11/18] usb-linux: walk async urb list in cancel Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 12/18] usb-linux: split large xfers Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 13/18] usb-linux: fix max_packet_size for highspeed Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 14/18] usb-storage: don't call usb_packet_complete twice Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 15/18] usb: add usb_handle_packet Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 16/18] usb: keep track of packet owner Gerd Hoffmann
2011-05-23  9:43 ` [Qemu-devel] [PATCH 17/18] usb: move cancel callback to USBDeviceInfo Gerd Hoffmann
2011-05-23 14:04   ` Hans de Goede
2011-05-23 14:34     ` Gerd Hoffmann
2011-05-23 14:53       ` Gerd Hoffmann
2011-05-23 17:31         ` Hans de Goede
2011-05-23 17:30       ` Hans de Goede
2011-05-23  9:43 ` [Qemu-devel] [PATCH 18/18] usb: add ehci adapter Gerd Hoffmann
2011-05-23 19:25   ` Blue Swirl
2011-05-24 15:45   ` Erik Rull
2011-05-26 10:13 ` [Qemu-devel] [PULL] usb patch queue: initial usb 2.0 support Gerd Hoffmann
2011-05-31 13:37   ` 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.