aboutsummaryrefslogtreecommitdiff
path: root/usb-linux.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2010-11-26 19:11:03 +0100
committerGerd Hoffmann <kraxel@redhat.com>2011-05-04 12:25:24 +0200
commitbb6d5498c6756eba3d0779c7753fc8830a8a9078 (patch)
treed9350cc7b21c3bee00aea06fb935a163457a838b /usb-linux.c
parent3a4854b372e0ca1a619d33531efa02477674e93f (diff)
usb-linux: Add support for buffering iso out usb packets
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>
Diffstat (limited to 'usb-linux.c')
-rw-r--r--usb-linux.c152
1 files changed, 101 insertions, 51 deletions
diff --git a/usb-linux.c b/usb-linux.c
index 4498b16b07..730eac2ca6 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);