Re: [PATCH v2] usb: xhci: Add support for URB_ZERO_PACKET

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

 



On Wed, Nov 13, 2013 at 12:28:09PM -0000, David Laight wrote:
> If URB_ZERO_PACKET is set on a transfer that is an integral number
> of maximum length packets (1k for USB3 bulk) then an additional
> zero length fragment must be sent.
> 
> Merge together the functions that setup single buffer and scatter-gather
> transfers (they aren't that different) simplifying the logic as well.
> 
> In particular we note that the number of TRB passed to prepare_ring()
> need only be an upper limit on the number required. However we do try
> to only request 1 TRB when we know one is sufficient.
> 
> Signed-off-by: David Laight <david.laight@xxxxxxxxxx>
> ---
> Change for v2:
> - Ensure we check there is space for 1 TRB for zero length transfer requests.
>   (The extra TRB is added in the same path that checks URB_ZERO_PACKET).
> 
> I hope I've edited the patch correctly.
> I've NFI how to get git to generate modified patches.

git commit --amend

I'll look over this patch in a couple days.  ISTR that someone else
submitted a zero-length packet fix patch, so I need to coordinate the
two.

Sarah Sharp

> 
>  drivers/usb/host/xhci-ring.c | 390 +++++++++++--------------------------------
>  drivers/usb/host/xhci.h      |   1 +
>  2 files changed, 102 insertions(+), 290 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index ace586c..7211223 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -3082,55 +3082,6 @@ static int prepare_transfer(struct xhci_hcd *xhci,
>  	return 0;
>  }
>  
> -static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
> -{
> -	int num_sgs, num_trbs, running_total, temp, i;
> -	struct scatterlist *sg;
> -
> -	sg = NULL;
> -	num_sgs = urb->num_mapped_sgs;
> -	temp = urb->transfer_buffer_length;
> -
> -	num_trbs = 0;
> -	for_each_sg(urb->sg, sg, num_sgs, i) {
> -		unsigned int len = sg_dma_len(sg);
> -
> -		/* Scatter gather list entries may cross 64KB boundaries */
> -		running_total = TRB_MAX_BUFF_SIZE -
> -			(sg_dma_address(sg) & (TRB_MAX_BUFF_SIZE - 1));
> -		running_total &= TRB_MAX_BUFF_SIZE - 1;
> -		if (running_total != 0)
> -			num_trbs++;
> -
> -		/* How many more 64KB chunks to transfer, how many more TRBs? */
> -		while (running_total < sg_dma_len(sg) && running_total < temp) {
> -			num_trbs++;
> -			running_total += TRB_MAX_BUFF_SIZE;
> -		}
> -		len = min_t(int, len, temp);
> -		temp -= len;
> -		if (temp == 0)
> -			break;
> -	}
> -	return num_trbs;
> -}
> -
> -static void check_trb_math(struct urb *urb, int num_trbs, int running_total)
> -{
> -	if (num_trbs != 0)
> -		dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated number of "
> -				"TRBs, %d left\n", __func__,
> -				urb->ep->desc.bEndpointAddress, num_trbs);
> -	if (running_total != urb->transfer_buffer_length)
> -		dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated tx length, "
> -				"queued %#x (%d), asked for %#x (%d)\n",
> -				__func__,
> -				urb->ep->desc.bEndpointAddress,
> -				running_total, running_total,
> -				urb->transfer_buffer_length,
> -				urb->transfer_buffer_length);
> -}
> -
>  static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
>  		unsigned int ep_index, unsigned int stream_id,
>  		struct xhci_generic_trb *start_trb)
> @@ -3232,285 +3183,145 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
>  	return (total_packet_count - packets_transferred) << 17;
>  }
>  
> -static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
> +int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  		struct urb *urb, int slot_id, unsigned int ep_index)
>  {
>  	struct xhci_ring *ep_ring;
>  	unsigned int num_trbs;
>  	struct urb_priv *urb_priv;
> -	struct xhci_td *td;
> +	u32 trb_buff_len, this_sg_len;
>  	struct scatterlist *sg;
> -	int num_sgs;
> -	int trb_buff_len, this_sg_len, running_total;
> -	unsigned int total_packet_count;
> -	u32 trb_cycle_invert;
> +	u32 trb_type_flags;
> +	u32 max_packet, td_residue, len_left;
> +	u32 td_size;
>  	u64 addr;
> -	bool more_trbs_coming;
> -
> -	struct xhci_generic_trb *start_trb;
> +	int ret;
>  
>  	ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
> -	if (!ep_ring)
> +	if (unlikely(!ep_ring))
>  		return -EINVAL;
>  
> -	num_trbs = count_sg_trbs_needed(xhci, urb);
> -	num_sgs = urb->num_mapped_sgs;
> -	total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
> -			usb_endpoint_maxp(&urb->ep->desc));
> -
> -	trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
> -			ep_index, urb->stream_id,
> -			num_trbs, urb, 0, mem_flags);
> -	if (trb_buff_len < 0)
> -		return trb_buff_len;
> -
> -	urb_priv = urb->hcpriv;
> -	td = &urb_priv->td[0];
> -
> -	/*
> -	 * Don't give the first TRB to the hardware (by toggling the cycle bit)
> -	 * until we've finished creating all the other TRBs.  The ring's cycle
> -	 * state may change as we enqueue the other TRBs, so save it too.
> -	 */
> -	start_trb = &ep_ring->enqueue->generic;
> -
> -	running_total = 0;
> -	/*
> -	 * How much data is in the first TRB?
> -	 *
> -	 * There are three forces at work for TRB buffer pointers and lengths:
> -	 * 1. We don't want to walk off the end of this sg-list entry buffer.
> -	 * 2. The transfer length that the driver requested may be smaller than
> -	 *    the amount of memory allocated for this scatter-gather list.
> -	 * 3. TRBs buffers can't cross 64KB boundaries.
> -	 */
> -	sg = urb->sg;
> -	addr = (u64) sg_dma_address(sg);
> -	this_sg_len = sg_dma_len(sg);
> -	trb_buff_len = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1));
> -	trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
> -	if (trb_buff_len > urb->transfer_buffer_length)
> -		trb_buff_len = urb->transfer_buffer_length;
> -
> -	trb_cycle_invert = TRB_CYCLE;
> -	/* Queue the first TRB, even if it's zero-length */
> -	do {
> -		u32 field = 0;
> -		u32 length_field = 0;
> -		u32 remainder = 0;
> -
> -		field |= ep_ring->cycle_state;
> -		/* Don't change the cycle bit of the first TRB until later */
> -		field ^= trb_cycle_invert;
> -		trb_cycle_invert = 0;
> -
> -		/* Chain all the TRBs together; clear the chain bit in the last
> -		 * TRB to indicate it's the last TRB in the chain.
> -		 */
> -		if (num_trbs > 1) {
> -			field |= TRB_CHAIN;
> -		} else {
> -			/* FIXME - add check for ZERO_PACKET flag before this */
> -			td->last_trb = ep_ring->enqueue;
> -			field |= TRB_IOC;
> -		}
> -
> -		/* Only set interrupt on short packet for IN endpoints */
> -		if (usb_urb_dir_in(urb))
> -			field |= TRB_ISP;
> -
> -		if (TRB_MAX_BUFF_SIZE -
> -				(addr & (TRB_MAX_BUFF_SIZE - 1)) < trb_buff_len) {
> -			xhci_warn(xhci, "WARN: sg dma xfer crosses 64KB boundaries!\n");
> -			xhci_dbg(xhci, "Next boundary at %#x, end dma = %#x\n",
> -					(unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
> -					(unsigned int) addr + trb_buff_len);
> -		}
> -
> -		/* Set the TRB length, TD size, and interrupter fields. */
> -		if (xhci->hci_version < 0x100) {
> -			remainder = xhci_td_remainder(
> -					urb->transfer_buffer_length -
> -					running_total);
> -		} else {
> -			remainder = xhci_v1_0_td_remainder(running_total,
> -					trb_buff_len, total_packet_count, urb,
> -					num_trbs - 1);
> -		}
> -		length_field = TRB_LEN(trb_buff_len) |
> -			remainder |
> -			TRB_INTR_TARGET(0);
> -
> -		if (num_trbs > 1)
> -			more_trbs_coming = true;
> -		else
> -			more_trbs_coming = false;
> -		queue_trb(xhci, ep_ring, more_trbs_coming,
> -				lower_32_bits(addr),
> -				upper_32_bits(addr),
> -				length_field,
> -				field | TRB_TYPE(TRB_NORMAL));
> -		--num_trbs;
> -		running_total += trb_buff_len;
> -
> -		/* Calculate length for next transfer --
> -		 * Are we done queueing all the TRBs for this sg entry?
> +	if (urb->num_sgs == 0) {
> +		sg = NULL;
> +		addr = (u64)urb->transfer_dma;
> +		this_sg_len = urb->transfer_buffer_length;
> +		num_trbs = 0;
> +	} else {
> +		sg = urb->sg;
> +		addr = (u64)sg_dma_address(sg);
> +		this_sg_len = sg_dma_len(sg);
> +		/*
> +		 * We only need an upper bound for the number of TRBs.
> +		 * Add in two extra TRB for each subsequent segment.
>  		 */
> -		this_sg_len -= trb_buff_len;
> -		if (this_sg_len == 0) {
> -			--num_sgs;
> -			if (num_sgs == 0)
> -				break;
> -			sg = sg_next(sg);
> -			addr = (u64) sg_dma_address(sg);
> -			this_sg_len = sg_dma_len(sg);
> -		} else {
> -			addr += trb_buff_len;
> -		}
> -
> -		trb_buff_len = TRB_MAX_BUFF_SIZE -
> -			(addr & (TRB_MAX_BUFF_SIZE - 1));
> -		trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
> -		if (running_total + trb_buff_len > urb->transfer_buffer_length)
> -			trb_buff_len =
> -				urb->transfer_buffer_length - running_total;
> -	} while (running_total < urb->transfer_buffer_length);
> -
> -	check_trb_math(urb, num_trbs, running_total);
> -	giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, start_trb);
> -	return 0;
> -}
> -
> -/* This is very similar to what ehci-q.c qtd_fill() does */
> -int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
> -		struct urb *urb, int slot_id, unsigned int ep_index)
> -{
> -	struct xhci_ring *ep_ring;
> -	struct urb_priv *urb_priv;
> -	struct xhci_td *td;
> -	int num_trbs;
> -	struct xhci_generic_trb *start_trb;
> -	u32 trb_cycle_invert;
> -	bool more_trbs_coming;
> -	u32 field, length_field;
> -
> -	int running_total, trb_buff_len, ret;
> -	unsigned int total_packet_count;
> -	u64 addr;
> -
> -	if (urb->num_sgs)
> -		return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index);
> +		num_trbs = (urb->num_mapped_sgs - 1) * 2;
> +	}
>  
> -	ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
> -	if (!ep_ring)
> -		return -EINVAL;
> +	/* One TRB for each 64k 'page' that contains data */
> +	num_trbs += DIV_ROUND_UP((addr & (TRB_MAX_BUFF_SIZE - 1)) +
> +			urb->transfer_buffer_length, TRB_MAX_BUFF_SIZE);
>  
> -	num_trbs = 0;
> -	/* How much data is (potentially) left before the 64KB boundary? */
> -	running_total = TRB_MAX_BUFF_SIZE -
> -		(urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
> -	running_total &= TRB_MAX_BUFF_SIZE - 1;
> +	max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
>  
> -	/* If there's some data on this 64KB chunk, or we have to send a
> -	 * zero-length transfer, we need at least one TRB
> +	/*
> +	 * For v 1.0 we need to calculate the number of TD needed to
> +	 * complete the transfer.
> +	 * The code here is equivalent to 4.11.2.4.
>  	 */
> -	if (running_total != 0 || urb->transfer_buffer_length == 0)
> -		num_trbs++;
> -	/* How many more 64KB chunks to transfer, how many more TRBs? */
> -	while (running_total < urb->transfer_buffer_length) {
> +	td_residue = urb->transfer_buffer_length;
> +	if (unlikely(urb->transfer_flags & URB_ZERO_PACKET)
> +			|| unlikely(urb->transfer_buffer_length == 0)) {
> +		/* We need an extra zero length TD */
>  		num_trbs++;
> -		running_total += TRB_MAX_BUFF_SIZE;
> +		td_residue++;
>  	}
> -	/* FIXME: this doesn't deal with URB_ZERO_PACKET - need one more */
> +	td_residue = ALIGN(td_residue, max_packet) + max_packet - 1;
>  
>  	ret = prepare_transfer(xhci, xhci->devs[slot_id],
>  			ep_index, urb->stream_id,
>  			num_trbs, urb, 0, mem_flags);
> -	if (ret < 0)
> +	if (unlikely(ret < 0))
>  		return ret;
>  
> -	urb_priv = urb->hcpriv;
> -	td = &urb_priv->td[0];
> +	trb_type_flags = TRB_CYCLE | TRB_CHAIN | TRB_TYPE(TRB_NORMAL);
>  
> -	/*
> -	 * Don't give the first TRB to the hardware (by toggling the cycle bit)
> -	 * until we've finished creating all the other TRBs.  The ring's cycle
> -	 * state may change as we enqueue the other TRBs, so save it too.
> -	 */
> -	start_trb = &ep_ring->enqueue->generic;
> -
> -	running_total = 0;
> -	total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
> -			usb_endpoint_maxp(&urb->ep->desc));
> -	/* How much data is in the first TRB? */
> -	addr = (u64) urb->transfer_dma;
> -	trb_buff_len = TRB_MAX_BUFF_SIZE -
> -		(urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
> -	if (trb_buff_len > urb->transfer_buffer_length)
> -		trb_buff_len = urb->transfer_buffer_length;
> -
> -	trb_cycle_invert = TRB_CYCLE;
> +	/* Set interrupt on short packet for IN endpoints */
> +	if (usb_urb_dir_in(urb))
> +		trb_type_flags |= TRB_ISP;
>  
>  	/* Queue the first TRB, even if it's zero-length */
> -	do {
> -		u32 remainder = 0;
> -		field = 0;
> +	len_left = urb->transfer_buffer_length;
> +	for (;;) {
> +		/*
> +		 * How much data is in the TRB?
> +		 *
> +		 * 1. TRBs buffers can't cross 64KB boundaries.
> +		 * 2. We don't want to walk off the end of this sg-list buffer.
> +		 * 2. Don't exceed the driver-supplied transfer length.
> +		 *    (May be smaller than the sg list.)
> +		 */
> +		trb_buff_len = (~addr & (TRB_MAX_BUFF_SIZE - 1)) + 1;
> +		trb_buff_len = min_t(u32, trb_buff_len, this_sg_len);
> +		trb_buff_len = min_t(u32, trb_buff_len, len_left);
> +		td_residue -= trb_buff_len;
>  
> -		field |= ep_ring->cycle_state;
> -		/* Don't change the cycle bit of the first TRB until later */
> -		field ^= trb_cycle_invert;
> -		trb_cycle_invert = 0;
> +		/* TRB_CYCLE is inverted in the first TRB */
> +		trb_type_flags ^= ep_ring->cycle_state;
>  
> -		/* Chain all the TRBs together; clear the chain bit in the last
> +		/*
> +		 * Chain all the TRBs together; clear the chain bit in the last
>  		 * TRB to indicate it's the last TRB in the chain.
> +		 * The td_residue can only be 2 * max_packet - 1 if
> +		 * URB_ZERO_PACKET was set, this forces a zero length packet.
> +		 * For version 1.0 we could send the last fragment with
> +		 * td_size set to 1 - but that is just extra logic.
>  		 */
> -		if (num_trbs > 1) {
> -			field |= TRB_CHAIN;
> +		if (trb_buff_len == len_left && (trb_buff_len == 0 ||
> +				td_residue < 2 * max_packet - 1)) {
> +			/* The last buffer fragment */
> +			urb_priv = urb->hcpriv;
> +			urb_priv->td[0].last_trb = ep_ring->enqueue;
> +			/* Set TRB_IOC and clear TRB_CHAIN */
> +			trb_type_flags ^= TRB_IOC | TRB_CHAIN;
> +			td_size = 0;
>  		} else {
> -			/* FIXME - add check for ZERO_PACKET flag before this */
> -			td->last_trb = ep_ring->enqueue;
> -			field |= TRB_IOC;
> +			if (xhci->hci_version < 0x100)
> +				td_size = xhci_td_remainder(len_left);
> +			else
> +				td_size = TRB_TD_SIZE(td_residue / max_packet);
>  		}
>  
> -		/* Only set interrupt on short packet for IN endpoints */
> -		if (usb_urb_dir_in(urb))
> -			field |= TRB_ISP;
> +		/* No need to specify 'more_trbs_coming', implied by CHAIN */
> +		queue_trb(xhci, ep_ring, false,
> +				lower_32_bits(addr),
> +				upper_32_bits(addr),
> +				TRB_LEN(trb_buff_len) | td_size |
> +					TRB_INTR_TARGET(0),
> +				trb_type_flags);
> +
> +		if (!(trb_type_flags & TRB_CHAIN))
> +			break;
> +
> +		/* We want the current TRB_CYCLE in subsequent TRB */
> +		trb_type_flags &= ~TRB_CYCLE;
>  
> -		/* Set the TRB length, TD size, and interrupter fields. */
> -		if (xhci->hci_version < 0x100) {
> -			remainder = xhci_td_remainder(
> -					urb->transfer_buffer_length -
> -					running_total);
> +		/* Advance everything past this segment */
> +		len_left -= trb_buff_len;
> +		this_sg_len -= trb_buff_len;
> +		if (this_sg_len == 0 && len_left != 0) {
> +			sg = sg_next(sg);
> +			addr = (u64)sg_dma_address(sg);
> +			this_sg_len = sg_dma_len(sg);
>  		} else {
> -			remainder = xhci_v1_0_td_remainder(running_total,
> -					trb_buff_len, total_packet_count, urb,
> -					num_trbs - 1);
> +			/* Buffer was split on 64k boundary.
> +			 * Or we need to send a zero length packet. */
> +			addr += trb_buff_len;
>  		}
> -		length_field = TRB_LEN(trb_buff_len) |
> -			remainder |
> -			TRB_INTR_TARGET(0);
> +	}
>  
> -		if (num_trbs > 1)
> -			more_trbs_coming = true;
> -		else
> -			more_trbs_coming = false;
> -		queue_trb(xhci, ep_ring, more_trbs_coming,
> -				lower_32_bits(addr),
> -				upper_32_bits(addr),
> -				length_field,
> -				field | TRB_TYPE(TRB_NORMAL));
> -		--num_trbs;
> -		running_total += trb_buff_len;
> -
> -		/* Calculate length for next transfer */
> -		addr += trb_buff_len;
> -		trb_buff_len = urb->transfer_buffer_length - running_total;
> -		if (trb_buff_len > TRB_MAX_BUFF_SIZE)
> -			trb_buff_len = TRB_MAX_BUFF_SIZE;
> -	} while (running_total < urb->transfer_buffer_length);
> -
> -	check_trb_math(urb, num_trbs, running_total);
> -	giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, start_trb);
> +	urb_priv = urb->hcpriv;
> +	giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
> +			&urb_priv->td[0].first_trb->generic);
>  	return 0;
>  }
>  
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 118f90b..bbdbfed 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1123,6 +1123,7 @@ struct xhci_event_cmd {
>  /* Normal TRB fields */
>  /* transfer_len bitmasks - bits 0:16 */
>  #define	TRB_LEN(p)		((p) & 0x1ffff)
> +#define	TRB_TD_SIZE(p)		(min_t(u32, (p), 31) << 17)
>  /* Interrupter Target - which MSI-X vector to target the completion event at */
>  #define TRB_INTR_TARGET(p)	(((p) & 0x3ff) << 22)
>  #define GET_INTR_TARGET(p)	(((p) >> 22) & 0x3ff)
> -- 
> 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