This allows to hcd code to slow down its timer, rather then having to
poll us every ms.

Signed-off-by: Hans de Goede <hdego...@redhat.com>
---
 hw/usb/redirect.c | 64 ++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 47 insertions(+), 17 deletions(-)

diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index ad601d8..5367876 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -68,6 +68,7 @@ struct endp_data {
     QTAILQ_HEAD(, buf_packet) bufpq;
     int32_t bufpq_size;
     int32_t bufpq_target_size;
+    USBPacket *pending_async_packet;
 };
 
 struct PacketIdQueueEntry {
@@ -323,12 +324,23 @@ static void packet_id_queue_empty(struct PacketIdQueue *q)
 static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
 {
     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    int ep_idx;
 
     if (p->combined) {
         usb_combined_packet_cancel(udev, p);
         return;
     }
 
+    ep_idx = p->ep->nr;
+    if (p->pid == USB_TOKEN_IN) {
+        ep_idx += 16;
+    }
+    if (dev->endpoint[ep_idx].pending_async_packet) {
+        assert(dev->endpoint[ep_idx].pending_async_packet == p);
+        dev->endpoint[ep_idx].pending_async_packet = NULL;
+        return;
+    }
+
     packet_id_queue_add(&dev->cancelled, p->id);
     usbredirparser_send_cancel_data_packet(dev->parser, p->id);
     usbredirparser_do_write(dev->parser);
@@ -366,8 +378,12 @@ static void usbredir_fill_already_in_flight(USBRedirDevice 
*dev)
     usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl);
 
     for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
-        usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]);
-        usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]);
+        if (udev->ep_in[ep].type == USB_ENDPOINT_XFER_BULK) {
+            usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]);
+        }
+        if (udev->ep_out[ep].type == USB_ENDPOINT_XFER_BULK) {
+            usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]);
+        }
     }
 }
 
@@ -620,12 +636,25 @@ static void usbredir_handle_bulk_data(USBRedirDevice 
*dev, USBPacket *p,
     p->status = USB_RET_ASYNC;
 }
 
+static void usbredir_interrupt_in_complete(USBRedirDevice *dev, USBPacket *p,
+    uint8_t ep, int status, uint8_t *data, int len)
+{
+    DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, status, len);
+
+    if (len > p->iov.size) {
+        ERROR("received int data is larger then packet ep %02X\n", ep);
+        len = p->iov.size;
+        status = usb_redir_babble;
+    }
+    usb_packet_copy(p, data, len);
+    usbredir_handle_status(dev, p, status);
+}
+
 static void usbredir_handle_interrupt_in_data(USBRedirDevice *dev,
                                               USBPacket *p, uint8_t ep)
 {
     /* Input interrupt endpoint, buffered packet input */
     struct buf_packet *intp;
-    int status, len;
 
     if (!dev->endpoint[EP2I(ep)].interrupt_started &&
             QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) {
@@ -647,22 +676,15 @@ static void 
usbredir_handle_interrupt_in_data(USBRedirDevice *dev,
     intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
     if (intp == NULL) {
         DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
-        p->status = USB_RET_NAK;
+        assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL);
+        dev->endpoint[EP2I(ep)].pending_async_packet = p;
+        p->status = USB_RET_ASYNC;
         return;
     }
-    DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
-            intp->status, intp->len);
 
-    status = intp->status;
-    len = intp->len;
-    if (len > p->iov.size) {
-        ERROR("received int data is larger then packet ep %02X\n", ep);
-        len = p->iov.size;
-        status = usb_redir_babble;
-    }
-    usb_packet_copy(p, intp->data, len);
+    usbredir_interrupt_in_complete(dev, p, ep, intp->status,
+                                   intp->data, intp->len);
     bufp_free(dev, intp, ep);
-    usbredir_handle_status(dev, p, status);
 }
 
 static void usbredir_handle_interrupt_out_data(USBRedirDevice *dev,
@@ -1661,8 +1683,16 @@ static void usbredir_interrupt_packet(void *priv, 
uint64_t id,
             return;
         }
 
-        /* bufp_alloc also adds the packet to the ep queue */
-        bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
+        if (dev->endpoint[EP2I(ep)].pending_async_packet) {
+            USBPacket *p = dev->endpoint[EP2I(ep)].pending_async_packet;
+            usbredir_interrupt_in_complete(dev, p, ep,
+                                    interrupt_packet->status, data, data_len);
+            usb_packet_complete(&dev->dev, p);
+            dev->endpoint[EP2I(ep)].pending_async_packet = NULL;
+        } else {
+            /* bufp_alloc also adds the packet to the ep queue */
+            bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
+        }
     } else {
         USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
         if (p) {
-- 
1.7.12.1


Reply via email to