Browse Source

[xhci] Support arbitrarily large transfers

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
4a7d691697
1 changed files with 45 additions and 11 deletions
  1. 45
    11
      src/drivers/usb/xhci.c

+ 45
- 11
src/drivers/usb/xhci.c View File

@@ -2541,6 +2541,26 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
2541 2541
 	return 0;
2542 2542
 }
2543 2543
 
2544
+/**
2545
+ * Calculate number of TRBs
2546
+ *
2547
+ * @v len		Length of data
2548
+ * @v zlp		Append a zero-length packet
2549
+ * @ret count		Number of transfer descriptors
2550
+ */
2551
+static unsigned int xhci_endpoint_count ( size_t len, int zlp ) {
2552
+	unsigned int count;
2553
+
2554
+	/* Split into 64kB TRBs */
2555
+	count = ( ( len + XHCI_MTU - 1 ) / XHCI_MTU );
2556
+
2557
+	/* Append a zero-length TRB if applicable */
2558
+	if ( zlp || ( count == 0 ) )
2559
+		count++;
2560
+
2561
+	return count;
2562
+}
2563
+
2544 2564
 /**
2545 2565
  * Enqueue stream transfer
2546 2566
  *
@@ -2552,10 +2572,14 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
2552 2572
 static int xhci_endpoint_stream ( struct usb_endpoint *ep,
2553 2573
 				  struct io_buffer *iobuf, int zlp ) {
2554 2574
 	struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
2555
-	union xhci_trb trbs[ 1 /* Normal */ + 1 /* Possible zero-length */ ];
2575
+	void *data = iobuf->data;
2576
+	size_t len = iob_len ( iobuf );
2577
+	unsigned int count = xhci_endpoint_count ( len, zlp );
2578
+	union xhci_trb trbs[count];
2556 2579
 	union xhci_trb *trb = trbs;
2557 2580
 	struct xhci_trb_normal *normal;
2558
-	size_t len = iob_len ( iobuf );
2581
+	unsigned int i;
2582
+	size_t trb_len;
2559 2583
 	int rc;
2560 2584
 
2561 2585
 	/* Profile stream transfers */
@@ -2563,20 +2587,30 @@ static int xhci_endpoint_stream ( struct usb_endpoint *ep,
2563 2587
 
2564 2588
 	/* Construct normal TRBs */
2565 2589
 	memset ( &trbs, 0, sizeof ( trbs ) );
2566
-	normal = &(trb++)->normal;
2567
-	normal->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) );
2568
-	normal->len = cpu_to_le32 ( len );
2569
-	normal->type = XHCI_TRB_NORMAL;
2570
-	if ( zlp ) {
2571
-		normal->flags = XHCI_TRB_CH;
2572
-		normal = &(trb++)->normal;
2590
+	for ( i = 0 ; i < count ; i ++ ) {
2591
+
2592
+		/* Calculate TRB length */
2593
+		trb_len = XHCI_MTU;
2594
+		if ( trb_len > len )
2595
+			trb_len = len;
2596
+
2597
+		/* Construct normal TRB */
2598
+		normal = &trb->normal;
2599
+		normal->data = cpu_to_le64 ( virt_to_phys ( data ) );
2600
+		normal->len = cpu_to_le32 ( trb_len );
2573 2601
 		normal->type = XHCI_TRB_NORMAL;
2602
+		normal->flags = XHCI_TRB_CH;
2603
+
2604
+		/* Move to next TRB */
2605
+		data += trb_len;
2606
+		len -= trb_len;
2607
+		trb++;
2574 2608
 	}
2575
-	normal->flags = XHCI_TRB_IOC;
2609
+	trb[-1].normal.flags = XHCI_TRB_IOC;
2576 2610
 
2577 2611
 	/* Enqueue TRBs */
2578 2612
 	if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs,
2579
-					 ( trb - trbs ) ) ) != 0 )
2613
+					 count ) ) != 0 )
2580 2614
 		return rc;
2581 2615
 
2582 2616
 	/* Ring the doorbell */

Loading…
Cancel
Save