qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 19/32] usb: Add an usb_device_ep_stopped USBDevice m


From: Gerd Hoffmann
Subject: [Qemu-devel] [PATCH 19/32] usb: Add an usb_device_ep_stopped USBDevice method
Date: Tue, 8 Jan 2013 14:14:41 +0100

From: Hans de Goede <address@hidden>

Some usb devices (host or network redirection) can benefit from knowing when
the guest stops using an endpoint. Redirection may involve submitting packets
independently from the guest (in combination with a fifo buffer between the
redirection code and the guest), to ensure that buffers of the real usb device
are timely emptied. This is done for example for isoc traffic and for interrupt
input endpoints. But when the (re)submission of packets is done by the device
code, then how does it know when to stop this?

For isoc endpoints this is handled by detecting a set interface (change alt
setting) command, which works well for isoc endpoints. But for interrupt
endpoints currently the redirection code never stops receiving data from
the device, which is less then ideal.

However the controller emulation is aware when a guest looses interest, as
then the qh for the endpoint gets unlinked (ehci, ohci, uhci) or the endpoint
is explicitly stopped (xhci). This patch adds a new ep_stopped USBDevice
method and modifies the hcd code to call this on queue unlink / ep stop.

This makes it possible for the redirection code to properly stop receiving
interrupt input (*) data when the guest no longer has interest in it.

*) And in the future also buffered bulk input.

Signed-off-by: Hans de Goede <address@hidden>
Signed-off-by: Gerd Hoffmann <address@hidden>
---
 hw/usb.h          |    8 ++++++++
 hw/usb/bus.c      |    8 ++++++++
 hw/usb/hcd-ehci.c |   19 ++++++++++++++++++-
 hw/usb/hcd-ohci.c |   30 ++++++++++++++++++++++++++----
 hw/usb/hcd-uhci.c |    1 +
 hw/usb/hcd-xhci.c |    7 +++++++
 6 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/hw/usb.h b/hw/usb.h
index 81e265c..aca8ff6 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -307,6 +307,12 @@ typedef struct USBDeviceClass {
      */
     void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep);
 
+    /*
+     * Called by the hcd to let the device know the queue for an endpoint
+     * has been unlinked / stopped. Optional may be NULL.
+     */
+    void (*ep_stopped)(USBDevice *dev, USBEndpoint *ep);
+
     const char *product_desc;
     const USBDesc *usb_desc;
 } USBDeviceClass;
@@ -539,6 +545,8 @@ void usb_device_set_interface(USBDevice *dev, int interface,
 
 void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep);
 
+void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep);
+
 const char *usb_device_get_product_desc(USBDevice *dev);
 
 const USBDesc *usb_device_get_usb_desc(USBDevice *dev);
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 10260a1..180d1d7 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -189,6 +189,14 @@ void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint 
*ep)
     }
 }
 
+void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep)
+{
+    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+    if (klass->ep_stopped) {
+        klass->ep_stopped(dev, ep);
+    }
+}
+
 static int usb_qdev_init(DeviceState *qdev)
 {
     USBDevice *dev = USB_DEVICE(qdev);
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 1713394..320b7e7 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -622,6 +622,17 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, 
uint32_t addr, int async)
     return q;
 }
 
+static void ehci_queue_stopped(EHCIQueue *q)
+{
+    int endp  = get_field(q->qh.epchar, QH_EPCHAR_EP);
+
+    if (!q->last_pid || !q->dev) {
+        return;
+    }
+
+    usb_device_ep_stopped(q->dev, usb_ep_get(q->dev, q->last_pid, endp));
+}
+
 static int ehci_cancel_queue(EHCIQueue *q)
 {
     EHCIPacket *p;
@@ -629,7 +640,7 @@ static int ehci_cancel_queue(EHCIQueue *q)
 
     p = QTAILQ_FIRST(&q->packets);
     if (p == NULL) {
-        return 0;
+        goto leave;
     }
 
     trace_usb_ehci_queue_action(q, "cancel");
@@ -637,6 +648,9 @@ static int ehci_cancel_queue(EHCIQueue *q)
         ehci_free_packet(p);
         packets++;
     } while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
+
+leave:
+    ehci_queue_stopped(q);
     return packets;
 }
 
@@ -1392,6 +1406,9 @@ static int ehci_execute(EHCIPacket *p, const char *action)
         return -1;
     }
 
+    if (!ehci_verify_pid(p->queue, &p->qtd)) {
+        ehci_queue_stopped(p->queue); /* Mark the ep in the prev dir stopped */
+    }
     p->pid = ehci_get_pid(&p->qtd);
     p->queue->last_pid = p->pid;
     endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 052c4a3..29bafa6 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -430,6 +430,23 @@ static USBDevice *ohci_find_device(OHCIState *ohci, 
uint8_t addr)
     return NULL;
 }
 
+static void ohci_stop_endpoints(OHCIState *ohci)
+{
+    USBDevice *dev;
+    int i, j;
+
+    for (i = 0; i < ohci->num_ports; i++) {
+        dev = ohci->rhport[i].port.dev;
+        if (dev && dev->attached) {
+            usb_device_ep_stopped(dev, &dev->ep_ctl);
+            for (j = 0; j < USB_MAX_ENDPOINTS; j++) {
+                usb_device_ep_stopped(dev, &dev->ep_in[j]);
+                usb_device_ep_stopped(dev, &dev->ep_out[j]);
+            }
+        }
+    }
+}
+
 /* Reset the controller */
 static void ohci_reset(void *opaque)
 {
@@ -478,6 +495,7 @@ static void ohci_reset(void *opaque)
         usb_cancel_packet(&ohci->usb_packet);
         ohci->async_td = 0;
     }
+    ohci_stop_endpoints(ohci);
     DPRINTF("usb-ohci: Reset %s\n", ohci->name);
 }
 
@@ -1147,6 +1165,8 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t 
head, int completion)
             if (ohci->async_td && addr == ohci->async_td) {
                 usb_cancel_packet(&ohci->usb_packet);
                 ohci->async_td = 0;
+                usb_device_ep_stopped(ohci->usb_packet.ep->dev,
+                                      ohci->usb_packet.ep);
             }
             continue;
         }
@@ -1227,10 +1247,12 @@ static void ohci_frame_boundary(void *opaque)
     }
 
     /* Cancel all pending packets if either of the lists has been disabled.  */
-    if (ohci->async_td &&
-        ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
-        usb_cancel_packet(&ohci->usb_packet);
-        ohci->async_td = 0;
+    if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
+        if (ohci->async_td) {
+            usb_cancel_packet(&ohci->usb_packet);
+            ohci->async_td = 0;
+        }
+        ohci_stop_endpoints(ohci);
     }
     ohci->old_ctl = ohci->ctl;
     ohci_process_lists(ohci, 0);
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index bd3377e..0cd68cf 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -226,6 +226,7 @@ static void uhci_queue_free(UHCIQueue *queue, const char 
*reason)
         async = QTAILQ_FIRST(&queue->asyncs);
         uhci_async_cancel(async);
     }
+    usb_device_ep_stopped(queue->ep->dev, queue->ep);
 
     trace_usb_uhci_queue_del(queue->token, reason);
     QTAILQ_REMOVE(&s->queues, queue, next);
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index e2de71e..40542b8 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1177,6 +1177,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned 
int slotid,
     XHCISlot *slot;
     XHCIEPContext *epctx;
     int i, xferi, killed = 0;
+    USBEndpoint *ep = NULL;
     assert(slotid >= 1 && slotid <= xhci->numslots);
     assert(epid >= 1 && epid <= 31);
 
@@ -1192,9 +1193,15 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned 
int slotid,
 
     xferi = epctx->next_xfer;
     for (i = 0; i < TD_QUEUE; i++) {
+        if (epctx->transfers[xferi].packet.ep) {
+            ep = epctx->transfers[xferi].packet.ep;
+        }
         killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]);
         xferi = (xferi + 1) % TD_QUEUE;
     }
+    if (ep) {
+        usb_device_ep_stopped(ep->dev, ep);
+    }
     return killed;
 }
 
-- 
1.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]