diff options
author | Michael Brown <mcb30@ipxe.org> | 2015-09-13 12:31:18 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2015-09-13 12:54:31 +0100 |
commit | 4a7d69169788a65d3d4bb4d3a00497bb1a45d51d (patch) | |
tree | b00c11e8cabeaac3316787b703822830bc08a6b2 /src/drivers | |
parent | 8f418ee477e17fdfe40870f94c03ddd088cc7a2b (diff) | |
download | ipxe-4a7d69169788a65d3d4bb4d3a00497bb1a45d51d.zip ipxe-4a7d69169788a65d3d4bb4d3a00497bb1a45d51d.tar.gz ipxe-4a7d69169788a65d3d4bb4d3a00497bb1a45d51d.tar.bz2 |
[xhci] Support arbitrarily large transfers
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers')
-rw-r--r-- | src/drivers/usb/xhci.c | 56 |
1 files changed, 45 insertions, 11 deletions
diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index f0f8eb1..110b432 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 */ |