From patchwork Fri Nov 26 18:13:23 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 73217 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id D7EDDB70DF for ; Sat, 27 Nov 2010 05:19:16 +1100 (EST) Received: from localhost ([127.0.0.1]:53434 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PM2t8-0002A2-3j for incoming@patchwork.ozlabs.org; Fri, 26 Nov 2010 13:19:14 -0500 Received: from [140.186.70.92] (port=35735 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PM2l2-0005sn-Ld for qemu-devel@nongnu.org; Fri, 26 Nov 2010 13:10:54 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PM2l1-0002Cd-9e for qemu-devel@nongnu.org; Fri, 26 Nov 2010 13:10:52 -0500 Received: from mx1.redhat.com ([209.132.183.28]:18302) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PM2l0-0002CT-Uz for qemu-devel@nongnu.org; Fri, 26 Nov 2010 13:10:51 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oAQIAnee018531 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 26 Nov 2010 13:10:49 -0500 Received: from shalem.localdomain (vpn1-7-82.ams2.redhat.com [10.36.7.82]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id oAQIAcbX009779; Fri, 26 Nov 2010 13:10:48 -0500 From: Hans de Goede To: qemu-devel@nongnu.org Date: Fri, 26 Nov 2010 19:13:23 +0100 Message-Id: <1290795203-2597-7-git-send-email-hdegoede@redhat.com> In-Reply-To: <1290795203-2597-1-git-send-email-hdegoede@redhat.com> References: <1290795203-2597-1-git-send-email-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. Cc: spice-devel@lists.freedesktop.org, Gerd Hoffmann , Hans de Goede Subject: [Qemu-devel] [PATCH 7/7] usb-linux: Add support for buffering iso out usb packets X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- usb-linux.c | 152 +++++++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 101 insertions(+), 51 deletions(-) diff --git a/usb-linux.c b/usb-linux.c index 9301084..3ef8198 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; @@ -533,6 +560,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) @@ -545,10 +574,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) @@ -556,57 +585,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; } } @@ -640,8 +700,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; @@ -652,19 +713,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);