summaryrefslogtreecommitdiffstats
path: root/src/drivers/usb
diff options
context:
space:
mode:
authorMichael Brown2015-09-13 13:31:18 +0200
committerMichael Brown2015-09-13 13:54:31 +0200
commit4a7d69169788a65d3d4bb4d3a00497bb1a45d51d (patch)
treeb00c11e8cabeaac3316787b703822830bc08a6b2 /src/drivers/usb
parent[ehci] Support arbitrarily large transfers (diff)
downloadipxe-4a7d69169788a65d3d4bb4d3a00497bb1a45d51d.tar.gz
ipxe-4a7d69169788a65d3d4bb4d3a00497bb1a45d51d.tar.xz
ipxe-4a7d69169788a65d3d4bb4d3a00497bb1a45d51d.zip
[xhci] Support arbitrarily large transfers
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers/usb')
-rw-r--r--src/drivers/usb/xhci.c56
1 files changed, 45 insertions, 11 deletions
diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c
index f0f8eb1d..110b4328 100644
--- a/src/drivers/usb/xhci.c
+++ b/src/drivers/usb/xhci.c
@@ -2542,6 +2542,26 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
}
/**
+ * Calculate number of TRBs
+ *
+ * @v len Length of data
+ * @v zlp Append a zero-length packet
+ * @ret count Number of transfer descriptors
+ */
+static unsigned int xhci_endpoint_count ( size_t len, int zlp ) {
+ unsigned int count;
+
+ /* Split into 64kB TRBs */
+ count = ( ( len + XHCI_MTU - 1 ) / XHCI_MTU );
+
+ /* Append a zero-length TRB if applicable */
+ if ( zlp || ( count == 0 ) )
+ count++;
+
+ return count;
+}
+
+/**
* Enqueue stream transfer
*
* @v ep USB endpoint
@@ -2552,10 +2572,14 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
static int xhci_endpoint_stream ( struct usb_endpoint *ep,
struct io_buffer *iobuf, int zlp ) {
struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- union xhci_trb trbs[ 1 /* Normal */ + 1 /* Possible zero-length */ ];
+ void *data = iobuf->data;
+ size_t len = iob_len ( iobuf );
+ unsigned int count = xhci_endpoint_count ( len, zlp );
+ union xhci_trb trbs[count];
union xhci_trb *trb = trbs;
struct xhci_trb_normal *normal;
- size_t len = iob_len ( iobuf );
+ unsigned int i;
+ size_t trb_len;
int rc;
/* Profile stream transfers */
@@ -2563,20 +2587,30 @@ static int xhci_endpoint_stream ( struct usb_endpoint *ep,
/* Construct normal TRBs */
memset ( &trbs, 0, sizeof ( trbs ) );
- normal = &(trb++)->normal;
- normal->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) );
- normal->len = cpu_to_le32 ( len );
- normal->type = XHCI_TRB_NORMAL;
- if ( zlp ) {
- normal->flags = XHCI_TRB_CH;
- normal = &(trb++)->normal;
+ for ( i = 0 ; i < count ; i ++ ) {
+
+ /* Calculate TRB length */
+ trb_len = XHCI_MTU;
+ if ( trb_len > len )
+ trb_len = len;
+
+ /* Construct normal TRB */
+ normal = &trb->normal;
+ normal->data = cpu_to_le64 ( virt_to_phys ( data ) );
+ normal->len = cpu_to_le32 ( trb_len );
normal->type = XHCI_TRB_NORMAL;
+ normal->flags = XHCI_TRB_CH;
+
+ /* Move to next TRB */
+ data += trb_len;
+ len -= trb_len;
+ trb++;
}
- normal->flags = XHCI_TRB_IOC;
+ trb[-1].normal.flags = XHCI_TRB_IOC;
/* Enqueue TRBs */
if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs,
- ( trb - trbs ) ) ) != 0 )
+ count ) ) != 0 )
return rc;
/* Ring the doorbell */