david/ipxe
Archived
1
0

[xhci] Support arbitrarily large transfers

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2015-09-13 12:31:18 +01:00
parent 8f418ee477
commit 4a7d691697

View File

@ -2541,6 +2541,26 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
return 0; return 0;
} }
/**
* 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 * Enqueue stream transfer
* *
@ -2552,10 +2572,14 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
static int xhci_endpoint_stream ( struct usb_endpoint *ep, static int xhci_endpoint_stream ( struct usb_endpoint *ep,
struct io_buffer *iobuf, int zlp ) { struct io_buffer *iobuf, int zlp ) {
struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); 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; union xhci_trb *trb = trbs;
struct xhci_trb_normal *normal; struct xhci_trb_normal *normal;
size_t len = iob_len ( iobuf ); unsigned int i;
size_t trb_len;
int rc; int rc;
/* Profile stream transfers */ /* Profile stream transfers */
@ -2563,20 +2587,30 @@ static int xhci_endpoint_stream ( struct usb_endpoint *ep,
/* Construct normal TRBs */ /* Construct normal TRBs */
memset ( &trbs, 0, sizeof ( trbs ) ); memset ( &trbs, 0, sizeof ( trbs ) );
normal = &(trb++)->normal; for ( i = 0 ; i < count ; i ++ ) {
normal->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) );
normal->len = cpu_to_le32 ( len ); /* 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->type = XHCI_TRB_NORMAL;
if ( zlp ) {
normal->flags = XHCI_TRB_CH; normal->flags = XHCI_TRB_CH;
normal = &(trb++)->normal;
normal->type = XHCI_TRB_NORMAL; /* 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 */ /* Enqueue TRBs */
if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs, if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs,
( trb - trbs ) ) ) != 0 ) count ) ) != 0 )
return rc; return rc;
/* Ring the doorbell */ /* Ring the doorbell */