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>
This commit is contained in:
parent
3a4854b372
commit
bb6d5498c6
152
usb-linux.c
152
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user