summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongwoo Lee <dwoo08.lee@samsung.com>2016-11-17 13:21:55 +0900
committerPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>2017-10-02 11:20:57 +0200
commitc0547807acefd639fa7b7ea7b00a99dc4e527f03 (patch)
tree10584bd135e25eb7ddfa34abfc48bb481e2ad6b8
parent893eccd3a9ff72e62564ce09becff04b4253a021 (diff)
usb: xhci: Limit transfer length in a single TD
The transfer request exceeding 4032KB (the maximum number of TRBs per TD * the maximum size of transfer buffer on TRB) fails on xhci host with timed out error or babble error state. This failure occurs when accessing large files on USB mass-storage. Currently with xhci as well as ehci host, the driver requests maximum 30MB (65536 blks * 512 byte) to storage at once. However, xhci cannot handle this request because of the reason mentioned above, even though ehci can handle this. Thus, transfer request larger than this size should be splitted in order to limit the length of data in a single TD. Even though the single request is splitted into multiple requests, the transfer speed has affected insignificantly in comparison with ehci host: 22.6 MB/s on ehci and 22.3 MB/s on xhci for 100MB tranfer. Reported-by: Jaehoon Chung <jh80.chung@samsung.com> Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
-rw-r--r--drivers/usb/host/xhci.c30
1 files changed, 29 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 4673738d1e..efb075b006 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1138,12 +1138,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;
}
/**