[PATCH v3 6/9] xhci: Use a much simpler algorithm for td_size on 1.0 hosts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



v1.0 hosts need the td_size set to the number of messages (including
the final short one) that need to be sent to complete the transfer
after all the full-sized ones for the current TRB have been sent.

The code in xhci_v1_0_td_remainder() carefully copies the algorithm
from the documentation, but the value can be calculated much more
easily.

Signed-off-by: David Laight <david.laight@xxxxxxxxxx>
---
 drivers/usb/host/xhci-ring.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index bf9a20f..afd8eeb 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3272,9 +3272,9 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 	unsigned int num_trbs;
 	struct urb_priv *urb_priv;
 	struct scatterlist *sg;
-	u32 trb_buff_len, this_sg_len, running_total;
+	u32 trb_buff_len, this_sg_len;
 	u32 len_left;
-	unsigned int total_packet_count;
+	u32 td_residue, max_packet;
 	u32 trb_type_flags;
 	u64 addr;
 	int ret;
@@ -3287,8 +3287,19 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		return -EINVAL;
 
 	num_trbs = count_sg_trbs_needed(ep_ring, urb);
-	total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
-			usb_endpoint_maxp(&urb->ep->desc));
+
+	/*
+	 * For v1.0 endpoints we need to calculate the number of packets
+	 * required to complete the request.
+	 * The code here is equivalent to the calculations in section 4.11.2.4
+	 *
+	 * td_residue is set so that td_residue/max_packet is the number of
+	 * packets required to send the data after the current partial
+	 * transfer(s) have completed.
+	 */
+	max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
+	td_residue = urb->transfer_buffer_length;
+	td_residue = ALIGN(td_residue, max_packet) + max_packet - 1;
 
 	ret = prepare_transfer(xhci, xhci->devs[slot_id],
 			ep_index, urb->stream_id,
@@ -3313,7 +3324,6 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 	if (usb_urb_dir_in(urb))
 		trb_type_flags |= TRB_ISP;
 
-	running_total = 0;
 	len_left = urb->transfer_buffer_length;
 
 	sg = urb->sg;
@@ -3323,7 +3333,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 	/* Queue the first TRB, even if it's zero-length */
 	for (;;) {
 		u32 length_field = 0;
-		u32 remainder = 0;
+		u32 td_size = 0;
 		/*
 		 * How much data is in the TRB?
 		 *
@@ -3342,9 +3352,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 			urb_priv->td[0]->last_trb = ep_ring->enqueue;
 			/* Request interrupt and clear chain */
 			trb_type_flags ^= TRB_IOC | TRB_CHAIN;
+			td_residue = 0;
 		} else {
 			if (trb_buff_len == 0)
 				goto next_frag;
+			td_residue -= trb_buff_len;
 		}
 
 		/* Set the correct cycle bit (inverted for first TRB). */
@@ -3352,14 +3364,13 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 		/* Set the TRB length, TD size, and interrupter fields. */
 		if (xhci->hci_version < 0x100) {
-			remainder = xhci_td_remainder(len_left);
+			td_size = xhci_td_remainder(len_left);
 		} else {
-			remainder = xhci_v1_0_td_remainder(running_total,
-					trb_buff_len, total_packet_count, urb,
-					trb_buff_len != len_left);
+			td_size = min_t(u32, td_residue / max_packet, 31) << 17;
 		}
+
 		length_field = TRB_LEN(trb_buff_len) |
-			remainder |
+			td_size |
 			TRB_INTR_TARGET(0);
 
 		queue_trb(xhci, ep_ring, false,
@@ -3375,7 +3386,6 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		/* We want the current cycle in subsequent TRB */
 		trb_type_flags &= ~TRB_CYCLE;
 
-		running_total += trb_buff_len;
 		len_left -= trb_buff_len;
 
 		/* Advance source data pointer */
-- 
1.8.1.2



--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux