Patchwork [07/14] usb-linux: Add support for buffering iso out usb packets

login
register
mail settings
Submitter Gerd Hoffmann
Date May 4, 2011, 3:41 p.m.
Message ID <1304523708-9556-8-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/94063/
State New
Headers show

Comments

Gerd Hoffmann - May 4, 2011, 3:41 p.m.
From: Hans de Goede <hdegoede@redhat.com>

Extend the iso buffering code to also buffer iso out packets, this
fixes for example using usb speakers with usb redirection.

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

Patch

diff --git a/usb-linux.c b/usb-linux.c
index 4498b16..730eac2 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -101,8 +101,10 @@  typedef struct AsyncURB AsyncURB;
 struct endp_data {
     uint8_t type;
     uint8_t halted;
+    uint8_t iso_started;
     AsyncURB *iso_urb;
     int iso_urb_idx;
+    int iso_buffer_used;
     int max_packet_size;
 };
 
@@ -189,6 +191,21 @@  static void set_halt(USBHostDevice *s, int ep)
     s->endp_table[ep - 1].halted = 1;
 }
 
+static int is_iso_started(USBHostDevice *s, int ep)
+{
+    return s->endp_table[ep - 1].iso_started;
+}
+
+static void clear_iso_started(USBHostDevice *s, int ep)
+{
+    s->endp_table[ep - 1].iso_started = 0;
+}
+
+static void set_iso_started(USBHostDevice *s, int ep)
+{
+    s->endp_table[ep - 1].iso_started = 1;
+}
+
 static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb)
 {
     s->endp_table[ep - 1].iso_urb = iso_urb;
@@ -209,6 +226,16 @@  static int get_iso_urb_idx(USBHostDevice *s, int ep)
     return s->endp_table[ep - 1].iso_urb_idx;
 }
 
+static void set_iso_buffer_used(USBHostDevice *s, int ep, int i)
+{
+    s->endp_table[ep - 1].iso_buffer_used = i;
+}
+
+static int get_iso_buffer_used(USBHostDevice *s, int ep)
+{
+    return s->endp_table[ep - 1].iso_buffer_used;
+}
+
 static int get_max_packet_size(USBHostDevice *s, int ep)
 {
     return s->endp_table[ep - 1].max_packet_size;
@@ -534,6 +561,8 @@  static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep)
     else
         printf("husb: leaking iso urbs because of discard failure\n");
     set_iso_urb(s, ep, NULL);
+    set_iso_urb_idx(s, ep, 0);
+    clear_iso_started(s, ep);
 }
 
 static int urb_status_to_usb_ret(int status)
@@ -546,10 +575,10 @@  static int urb_status_to_usb_ret(int status)
     }
 }
 
-static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p)
+static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
 {
     AsyncURB *aurb;
-    int i, j, ret, max_packet_size, len = 0;
+    int i, j, ret, max_packet_size, offset, len = 0;
 
     max_packet_size = get_max_packet_size(s, p->devep);
     if (max_packet_size == 0)
@@ -557,57 +586,88 @@  static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p)
 
     aurb = get_iso_urb(s, p->devep);
     if (!aurb) {
-        aurb = usb_host_alloc_iso(s, p->devep, 1);
+        aurb = usb_host_alloc_iso(s, p->devep, in);
     }
 
     i = get_iso_urb_idx(s, p->devep);
     j = aurb[i].iso_frame_idx;
     if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
-        /* Check urb status  */
-        if (aurb[i].urb.status) {
-            len = urb_status_to_usb_ret(aurb[i].urb.status);
-            /* Move to the next urb */
-            aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
-        /* Check frame status */
-        } else if (aurb[i].urb.iso_frame_desc[j].status) {
-            len = urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status);
-        /* Check the frame fits */
-        } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) {
-            printf("husb: error received isoc data is larger then packet\n");
-            len = USB_RET_NAK;
-        /* All good copy data over */
+        if (in) {
+            /* Check urb status  */
+            if (aurb[i].urb.status) {
+                len = urb_status_to_usb_ret(aurb[i].urb.status);
+                /* Move to the next urb */
+                aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
+            /* Check frame status */
+            } else if (aurb[i].urb.iso_frame_desc[j].status) {
+                len = urb_status_to_usb_ret(
+                                        aurb[i].urb.iso_frame_desc[j].status);
+            /* Check the frame fits */
+            } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) {
+                printf("husb: received iso data is larger then packet\n");
+                len = USB_RET_NAK;
+            /* All good copy data over */
+            } else {
+                len = aurb[i].urb.iso_frame_desc[j].actual_length;
+                memcpy(p->data,
+                       aurb[i].urb.buffer +
+                           j * aurb[i].urb.iso_frame_desc[0].length,
+                       len);
+            }
         } else {
-            len = aurb[i].urb.iso_frame_desc[j].actual_length;
-            memcpy(p->data,
-                   aurb[i].urb.buffer +
-                       j * aurb[i].urb.iso_frame_desc[0].length,
-                   len);
+            len = p->len;
+            offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep);
+
+            /* Check the frame fits */
+            if (len > max_packet_size) {
+                printf("husb: send iso data is larger then max packet size\n");
+                return USB_RET_NAK;
+            }
+
+            /* All good copy data over */
+            memcpy(aurb[i].urb.buffer + offset, p->data, len);
+            aurb[i].urb.iso_frame_desc[j].length = len;
+            offset += len;
+            set_iso_buffer_used(s, p->devep, offset);
+
+            /* Start the stream once we have buffered enough data */
+            if (!is_iso_started(s, p->devep) && i == 1 && j == 8) {
+                set_iso_started(s, p->devep);
+            }
         }
         aurb[i].iso_frame_idx++;
         if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
             i = (i + 1) % ISO_URB_COUNT;
             set_iso_urb_idx(s, p->devep, i);
         }
+    } else {
+        if (in) {
+            set_iso_started(s, p->devep);
+        } else {
+            DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
+        }
     }
 
-    /* (Re)-submit all fully consumed urbs */
-    for (i = 0; i < ISO_URB_COUNT; i++) {
-        if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
-            ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
-            if (ret < 0) {
-                printf("husb error submitting isoc urb %d: %d\n", i, errno);
-                if (len == 0) {
-                    switch(errno) {
-                    case ETIMEDOUT:
-                        len = USB_RET_NAK;
-                    case EPIPE:
-                    default:
-                        len = USB_RET_STALL;
+    if (is_iso_started(s, p->devep)) {
+        /* (Re)-submit all fully consumed / filled urbs */
+        for (i = 0; i < ISO_URB_COUNT; i++) {
+            if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
+                ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
+                if (ret < 0) {
+                    printf("husb error submitting iso urb %d: %d\n", i, errno);
+                    if (!in || len == 0) {
+                        switch(errno) {
+                        case ETIMEDOUT:
+                            len = USB_RET_NAK;
+                        case EPIPE:
+                        default:
+                            len = USB_RET_STALL;
+                        }
                     }
+                    break;
                 }
-                break;
+                aurb[i].iso_frame_idx = -1;
             }
-            aurb[i].iso_frame_idx = -1;
         }
     }
 
@@ -641,8 +701,9 @@  static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
         clear_halt(s, p->devep);
     }
 
-    if (is_isoc(s, p->devep) && p->pid == USB_TOKEN_IN)
-        return usb_host_handle_iso_data(s, p);
+    if (is_isoc(s, p->devep)) {
+        return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
+    }
 
     aurb = async_alloc();
     aurb->hdev   = s;
@@ -653,19 +714,8 @@  static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
     urb->endpoint      = ep;
     urb->buffer        = p->data;
     urb->buffer_length = p->len;
-
-    if (is_isoc(s, p->devep)) {
-        /* Setup ISOC transfer */
-        urb->type     = USBDEVFS_URB_TYPE_ISO;
-        urb->flags    = USBDEVFS_URB_ISO_ASAP;
-        urb->number_of_packets = 1;
-        urb->iso_frame_desc[0].length = p->len;
-    } else {
-        /* Setup bulk transfer */
-        urb->type     = USBDEVFS_URB_TYPE_BULK;
-    }
-
-    urb->usercontext = s;
+    urb->type          = USBDEVFS_URB_TYPE_BULK;
+    urb->usercontext   = s;
 
     ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);