|
@@ -819,8 +819,8 @@ static void usbredir_handle_interrupt_in_data(USBRedirDevice *dev,
|
|
|
USBPacket *p, uint8_t ep)
|
|
|
{
|
|
|
/* Input interrupt endpoint, buffered packet input */
|
|
|
- struct buf_packet *intp;
|
|
|
- int status, len;
|
|
|
+ struct buf_packet *intp, *intp_to_free;
|
|
|
+ int status, len, sum;
|
|
|
|
|
|
if (!dev->endpoint[EP2I(ep)].interrupt_started &&
|
|
|
!dev->endpoint[EP2I(ep)].interrupt_error) {
|
|
@@ -839,9 +839,17 @@ static void usbredir_handle_interrupt_in_data(USBRedirDevice *dev,
|
|
|
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
|
|
|
}
|
|
|
|
|
|
- intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
|
|
|
+ /* check for completed interrupt message (with all fragments) */
|
|
|
+ sum = 0;
|
|
|
+ QTAILQ_FOREACH(intp, &dev->endpoint[EP2I(ep)].bufpq, next) {
|
|
|
+ sum += intp->len;
|
|
|
+ if (intp->len < dev->endpoint[EP2I(ep)].max_packet_size ||
|
|
|
+ sum >= p->iov.size)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
if (intp == NULL) {
|
|
|
- DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
|
|
|
+ DPRINTF2("interrupt-token-in ep %02X, no intp, buffered %d\n", ep, sum);
|
|
|
/* Check interrupt_error for stream errors */
|
|
|
status = dev->endpoint[EP2I(ep)].interrupt_error;
|
|
|
dev->endpoint[EP2I(ep)].interrupt_error = 0;
|
|
@@ -852,18 +860,42 @@ static void usbredir_handle_interrupt_in_data(USBRedirDevice *dev,
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
- DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
|
|
|
- intp->status, intp->len);
|
|
|
|
|
|
- status = intp->status;
|
|
|
- len = intp->len;
|
|
|
- if (len > p->iov.size) {
|
|
|
- ERROR("received int data is larger then packet ep %02X\n", ep);
|
|
|
- len = p->iov.size;
|
|
|
- status = usb_redir_babble;
|
|
|
+ /* copy of completed interrupt message */
|
|
|
+ sum = 0;
|
|
|
+ status = usb_redir_success;
|
|
|
+ intp_to_free = NULL;
|
|
|
+ QTAILQ_FOREACH(intp, &dev->endpoint[EP2I(ep)].bufpq, next) {
|
|
|
+ if (intp_to_free) {
|
|
|
+ bufp_free(dev, intp_to_free, ep);
|
|
|
+ }
|
|
|
+ DPRINTF("interrupt-token-in ep %02X fragment status %d len %d\n", ep,
|
|
|
+ intp->status, intp->len);
|
|
|
+
|
|
|
+ sum += intp->len;
|
|
|
+ len = intp->len;
|
|
|
+ if (status == usb_redir_success) {
|
|
|
+ status = intp->status;
|
|
|
+ }
|
|
|
+ if (sum > p->iov.size) {
|
|
|
+ ERROR("received int data is larger then packet ep %02X\n", ep);
|
|
|
+ len -= (sum - p->iov.size);
|
|
|
+ sum = p->iov.size;
|
|
|
+ status = usb_redir_babble;
|
|
|
+ }
|
|
|
+
|
|
|
+ usb_packet_copy(p, intp->data, len);
|
|
|
+
|
|
|
+ intp_to_free = intp;
|
|
|
+ if (intp->len < dev->endpoint[EP2I(ep)].max_packet_size ||
|
|
|
+ sum >= p->iov.size)
|
|
|
+ break;
|
|
|
}
|
|
|
- usb_packet_copy(p, intp->data, len);
|
|
|
- bufp_free(dev, intp, ep);
|
|
|
+ if (intp_to_free) {
|
|
|
+ bufp_free(dev, intp_to_free, ep);
|
|
|
+ }
|
|
|
+ DPRINTF("interrupt-token-in ep %02X summary status %d len %d\n", ep,
|
|
|
+ status, sum);
|
|
|
usbredir_handle_status(dev, p, status);
|
|
|
}
|
|
|
|
|
@@ -2041,22 +2073,17 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
|
|
|
}
|
|
|
|
|
|
if (ep & USB_DIR_IN) {
|
|
|
- bool q_was_empty;
|
|
|
-
|
|
|
if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
|
|
|
DPRINTF("received int packet while not started ep %02X\n", ep);
|
|
|
free(data);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- q_was_empty = QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq);
|
|
|
-
|
|
|
/* bufp_alloc also adds the packet to the ep queue */
|
|
|
bufp_alloc(dev, data, data_len, interrupt_packet->status, ep, data);
|
|
|
|
|
|
- if (q_was_empty) {
|
|
|
- usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0);
|
|
|
- }
|
|
|
+ /* insufficient data solved with USB_RET_NAK */
|
|
|
+ usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0);
|
|
|
} else {
|
|
|
/*
|
|
|
* We report output interrupt packets as completed directly upon
|