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 {
|
struct endp_data {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t halted;
|
uint8_t halted;
|
||||||
|
uint8_t iso_started;
|
||||||
AsyncURB *iso_urb;
|
AsyncURB *iso_urb;
|
||||||
int iso_urb_idx;
|
int iso_urb_idx;
|
||||||
|
int iso_buffer_used;
|
||||||
int max_packet_size;
|
int max_packet_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -189,6 +191,21 @@ static void set_halt(USBHostDevice *s, int ep)
|
|||||||
s->endp_table[ep - 1].halted = 1;
|
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)
|
static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb)
|
||||||
{
|
{
|
||||||
s->endp_table[ep - 1].iso_urb = 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;
|
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)
|
static int get_max_packet_size(USBHostDevice *s, int ep)
|
||||||
{
|
{
|
||||||
return s->endp_table[ep - 1].max_packet_size;
|
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
|
else
|
||||||
printf("husb: leaking iso urbs because of discard failure\n");
|
printf("husb: leaking iso urbs because of discard failure\n");
|
||||||
set_iso_urb(s, ep, NULL);
|
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)
|
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;
|
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);
|
max_packet_size = get_max_packet_size(s, p->devep);
|
||||||
if (max_packet_size == 0)
|
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);
|
aurb = get_iso_urb(s, p->devep);
|
||||||
if (!aurb) {
|
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);
|
i = get_iso_urb_idx(s, p->devep);
|
||||||
j = aurb[i].iso_frame_idx;
|
j = aurb[i].iso_frame_idx;
|
||||||
if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
|
if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
|
||||||
/* Check urb status */
|
if (in) {
|
||||||
if (aurb[i].urb.status) {
|
/* Check urb status */
|
||||||
len = urb_status_to_usb_ret(aurb[i].urb.status);
|
if (aurb[i].urb.status) {
|
||||||
/* Move to the next urb */
|
len = urb_status_to_usb_ret(aurb[i].urb.status);
|
||||||
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
|
/* Move to the next urb */
|
||||||
/* Check frame status */
|
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
|
||||||
} else if (aurb[i].urb.iso_frame_desc[j].status) {
|
/* Check frame status */
|
||||||
len = urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status);
|
} else if (aurb[i].urb.iso_frame_desc[j].status) {
|
||||||
/* Check the frame fits */
|
len = urb_status_to_usb_ret(
|
||||||
} else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) {
|
aurb[i].urb.iso_frame_desc[j].status);
|
||||||
printf("husb: error received isoc data is larger then packet\n");
|
/* Check the frame fits */
|
||||||
len = USB_RET_NAK;
|
} else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) {
|
||||||
/* All good copy data over */
|
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 {
|
} else {
|
||||||
len = aurb[i].urb.iso_frame_desc[j].actual_length;
|
len = p->len;
|
||||||
memcpy(p->data,
|
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep);
|
||||||
aurb[i].urb.buffer +
|
|
||||||
j * aurb[i].urb.iso_frame_desc[0].length,
|
/* Check the frame fits */
|
||||||
len);
|
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++;
|
aurb[i].iso_frame_idx++;
|
||||||
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
||||||
i = (i + 1) % ISO_URB_COUNT;
|
i = (i + 1) % ISO_URB_COUNT;
|
||||||
set_iso_urb_idx(s, p->devep, i);
|
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 */
|
if (is_iso_started(s, p->devep)) {
|
||||||
for (i = 0; i < ISO_URB_COUNT; i++) {
|
/* (Re)-submit all fully consumed / filled urbs */
|
||||||
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
for (i = 0; i < ISO_URB_COUNT; i++) {
|
||||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
|
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
||||||
if (ret < 0) {
|
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
|
||||||
printf("husb error submitting isoc urb %d: %d\n", i, errno);
|
if (ret < 0) {
|
||||||
if (len == 0) {
|
printf("husb error submitting iso urb %d: %d\n", i, errno);
|
||||||
switch(errno) {
|
if (!in || len == 0) {
|
||||||
case ETIMEDOUT:
|
switch(errno) {
|
||||||
len = USB_RET_NAK;
|
case ETIMEDOUT:
|
||||||
case EPIPE:
|
len = USB_RET_NAK;
|
||||||
default:
|
case EPIPE:
|
||||||
len = USB_RET_STALL;
|
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);
|
clear_halt(s, p->devep);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_isoc(s, p->devep) && p->pid == USB_TOKEN_IN)
|
if (is_isoc(s, p->devep)) {
|
||||||
return usb_host_handle_iso_data(s, p);
|
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
|
||||||
|
}
|
||||||
|
|
||||||
aurb = async_alloc();
|
aurb = async_alloc();
|
||||||
aurb->hdev = s;
|
aurb->hdev = s;
|
||||||
@ -653,19 +714,8 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
|
|||||||
urb->endpoint = ep;
|
urb->endpoint = ep;
|
||||||
urb->buffer = p->data;
|
urb->buffer = p->data;
|
||||||
urb->buffer_length = p->len;
|
urb->buffer_length = p->len;
|
||||||
|
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||||
if (is_isoc(s, p->devep)) {
|
urb->usercontext = s;
|
||||||
/* 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;
|
|
||||||
|
|
||||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user