usb: xhci: Limit transfer length of a single TD
authorDongwoo Lee <dwoo08.lee@samsung.com>
Mon, 7 Nov 2016 05:34:15 +0000 (14:34 +0900)
committerMarek Szyprowski <m.szyprowski@samsung.com>
Mon, 15 Nov 2021 10:19:26 +0000 (11:19 +0100)
This is workaround solution for timed out error and babble error while
transfering data exceeding 0x3F01FF bytes on xhci host. Actually, this
size equals to value that the maximum number of TRBs per TD times the
maximum size of transfer buffer on TRB. Thus, huge transfer request is
splitted in order to limit the size of data in a single TD.

Even though the single I/O request is splitted into multiple requests,
the transfer speed has affected insignificantly: 22.6 --> 22.3 MiB/s.

Change-Id: I85a17910587b70807f075dd40634a0da817cea2f
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
drivers/usb/host/xhci.c

index 126dabc11ba39f3f8ad54191504f58d9fd60b361..0b95efcb1b7f99c4d97a5609f82886ad59dcf07f 100644 (file)
@@ -1183,12 +1183,40 @@ static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
 static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe,
                                 void *buffer, int length)
 {
+       int ret;
+       int xfer_max_per_td, xfer_length, buf_pos;
+
        if (usb_pipetype(pipe) != PIPE_BULK) {
                printf("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
                return -EINVAL;
        }
 
-       return xhci_bulk_tx(udev, pipe, length, buffer);
+       /*
+        * When transfering data exceeding the maximum number of TRBs per
+        * TD (default 64) is requested, the transfer fails with babble
+        * error or time out.
+        *
+        * Thus, huge data transfer should be splitted into multiple TDs.
+        */
+       xfer_max_per_td = TRB_MAX_BUFF_SIZE * (TRBS_PER_SEGMENT - 1);
+
+       buf_pos = 0;
+       do {
+               if (length > xfer_max_per_td)
+                       xfer_length = xfer_max_per_td;
+               else
+                       xfer_length = length;
+
+               ret = xhci_bulk_tx(udev, pipe, xfer_length, buffer + buf_pos);
+               if (ret < 0)
+                       return ret;
+
+               buf_pos += xfer_length;
+               length -= xfer_length;
+
+       } while (length > 0);
+
+       return ret;
 }
 
 /**