diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2011-06-15 18:22:35 +0100 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2011-06-23 11:28:07 +0200 |
commit | f3dc0051dcd68b395861b6f1f3d5a900d44bd767 (patch) | |
tree | a0db8bced1bb4764d114a4c76a726083e4dfdaf5 /hw | |
parent | bf5547f5365c1bfeba9d5c3e87653b9803a875a5 (diff) |
hw/usb-ohci.c: Fix handling of remote wakeup corner cases
Correct a number of minor errors in the OHCI wakeup implementation:
* when the port is suspended but the controller is not, raise RHSC
* when the controller is suspended but the port is not, raise RD
* when the controller is suspended, move it to resume state
These fix some edge cases where a USB device might not successfully get
the attention of the guest OS if it tried to do so at the wrong time.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/usb-ohci.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 5d2ae01235..1c29b9fa6c 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -373,14 +373,25 @@ static void ohci_wakeup(USBDevice *dev) OHCIState *s = container_of(bus, OHCIState, bus); int portnum = dev->port->index; OHCIPort *port = &s->rhport[portnum]; + uint32_t intr = 0; if (port->ctrl & OHCI_PORT_PSS) { DPRINTF("usb-ohci: port %d: wakeup\n", portnum); port->ctrl |= OHCI_PORT_PSSC; port->ctrl &= ~OHCI_PORT_PSS; - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - ohci_set_interrupt(s, OHCI_INTR_RD); - } + intr = OHCI_INTR_RHSC; + } + /* Note that the controller can be suspended even if this port is not */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + DPRINTF("usb-ohci: remote-wakeup: SUSPEND->RESUME\n"); + /* This is the one state transition the controller can do by itself */ + s->ctl &= ~OHCI_CTL_HCFS; + s->ctl |= OHCI_USB_RESUME; + /* In suspend mode only ResumeDetected is possible, not RHSC: + * see the OHCI spec 5.1.2.3. + */ + intr = OHCI_INTR_RD; } + ohci_set_interrupt(s, intr); } /* Reset the controller */ |