Re: [PATCH RFC 2/7] xHCI: Isoc transfer URB enqueue

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

 



On Tue, Mar 02, 2010 at 06:36:48PM +0800, Libin wrote:
> >From 050df8768e10e58d01bbc24d8ee256d864cc6fff Mon Sep 17 00:00:00 2001
> From: Andiry Xu <andiry.xu@xxxxxxx>
> Date: Fri, 26 Feb 2010 17:38:15 +0800
> Subject: [PATCH 2/7] xHCI: Isoc transfer URB enqueue
> 
> This patch implements isoc transfer URB enqueue part.
> 
> Split the isoc URB into isoc TDs and insert them to the isoc endpoint
> transfer ring. A isoc TD can be made up of one isoc TRB and several normal
> TRBs (if needed).

Can you combine patches 2 and 3 into one patch?  This is very confusing
to review.

Also, please add more description to your commit about what it means to
have multiple TDs for one URB added to the endpoint list.  Specifically,
I want to know what your plan is for when an isochronous URB should be
given back, and what happens to hcpriv, the isochronous TDs, and the
endpoint's TD lists when an isochronous URB is canceled (dequeued) or
the hardware dies.  I can probably figure it out from the code, but I
would rather have a statement of your intentions in case there is a bug
in the code. :)

Sarah Sharp

> Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>
> Signed-off-by: Libin Yang <libin.yang@xxxxxxx>
> ---
>  drivers/usb/host/xhci-hcd.c  |    7 ++-
>  drivers/usb/host/xhci-ring.c |  148 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/host/xhci.h      |    2 +
>  3 files changed, 156 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
> index 36b7fa1..acd620b 100644
> --- a/drivers/usb/host/xhci-hcd.c
> +++ b/drivers/usb/host/xhci-hcd.c
> @@ -768,7 +768,12 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
>  				slot_id, ep_index);
>  		spin_unlock_irqrestore(&xhci->lock, flags);
>  	} else {
> -		ret = -EINVAL;
> +		spin_lock_irqsave(&xhci->lock, flags);
> +		if (xhci->xhc_state & XHCI_STATE_DYING)
> +			goto dying;
> +		ret = xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb,
> +				slot_id, ep_index);
> +		spin_unlock_irqrestore(&xhci->lock, flags);
>  	}
>  exit:
>  	return ret;
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index 79041b9..8815210 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -2148,6 +2148,154 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  	return 0;
>  }
>  
> +static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
> +		struct urb *urb, int i)
> +{
> +	int num_trbs = 0;
> +	u64 addr, td_len, running_total;
> +
> +	addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
> +	td_len = urb->iso_frame_desc[i].length;
> +
> +	running_total = TRB_MAX_BUFF_SIZE -
> +			(addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> +	if (running_total != 0)
> +		num_trbs++;
> +
> +	while (running_total < td_len) {
> +		num_trbs++;
> +		running_total += TRB_MAX_BUFF_SIZE;
> +	}
> +
> +	return num_trbs;
> +}
> +
> +/* This is for isoc transfer */
> +int xhci_queue_isoc_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_tds, trbs_per_td;
> +	struct xhci_generic_trb *start_trb;
> +	bool first_trb;
> +	int start_cycle;
> +	u32 field, length_field;
> +
> +	int running_total, trb_buff_len, td_len, td_remain_len, ret;
> +	u64 start_addr, addr;
> +	int i, j;
> +
> +	ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
> +
> +	num_tds = urb->number_of_packets;
> +	if (num_tds < 1) {
> +		xhci_dbg(xhci, "ISOC URB with zero packets?\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!in_interrupt())
> +		dev_dbg(&urb->dev->dev, "ep %#x - urb len = %#x (%d),"
> +				" addr = %#llx, num_trbs = %d\n",
> +				urb->ep->desc.bEndpointAddress,
> +				urb->transfer_buffer_length,
> +				urb->transfer_buffer_length,
> +				(unsigned long long)urb->transfer_dma,
> +				num_tds);
> +
> +	start_addr = (u64) urb->transfer_dma;
> +
> +	/* Queue the first TRB, even if it's zero-length */
> +	for (i = 0; i < num_tds; i++) {
> +		first_trb = true;
> +
> +		running_total = 0;
> +		start_trb = &ep_ring->enqueue->generic;
> +		start_cycle = ep_ring->cycle_state;
> +
> +		addr = start_addr + urb->iso_frame_desc[i].offset;
> +		td_len = urb->iso_frame_desc[i].length;
> +		td_remain_len = td_len;
> +
> +		trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);
> +
> +		ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
> +				trbs_per_td, urb, i, mem_flags);
> +		if (ret < 0)
> +			return ret;
> +
> +		urb_priv = urb->hcpriv;
> +		td = urb_priv->td[i];
> +
> +		for (j = 0; j < trbs_per_td; j++) {
> +			u32 remainder = 0;
> +			field = 0;
> +
> +			if (first_trb) {
> +				/* Queue the isoc TRB */
> +				field |= TRB_TYPE(TRB_ISOC);
> +				/* Assume URB_ISO_ASAP is set */
> +				field |= TRB_SIA;
> +				first_trb = false;
> +			} else {
> +				/* Queue other normal TRBs */
> +				field |= TRB_TYPE(TRB_NORMAL);
> +				field |= ep_ring->cycle_state;
> +			}
> +
> +			/* Chain all the TRBs together; clear the chain bit in
> +			 * the last TRB to indicate it's the last TRB in the
> +			 * chain.
> +			 */
> +
> +			if (j < trbs_per_td - 1) {
> +				field |= TRB_CHAIN;
> +			} else {
> +				td->last_trb = ep_ring->enqueue;
> +				field |= TRB_IOC;
> +			}
> +
> +			/* Calculate TRB length */
> +			trb_buff_len = TRB_MAX_BUFF_SIZE -
> +				(addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> +			if (trb_buff_len > td_remain_len)
> +				trb_buff_len = td_remain_len;
> +
> +			remainder = xhci_td_remainder(td_len - running_total);
> +			length_field = TRB_LEN(trb_buff_len) |
> +				remainder |
> +				TRB_INTR_TARGET(0);
> +			queue_trb(xhci, ep_ring, false,
> +				lower_32_bits(addr),
> +				upper_32_bits(addr),
> +				length_field,
> +				/* We always want to know if the TRB was short,
> +				 * or we won't get an event when it completes.
> +				 * (Unless we use event data TRBs, which are a
> +				 * waste of space and HC resources.)
> +				 */
> +				field | TRB_ISP);
> +			running_total += trb_buff_len;
> +
> +			addr += trb_buff_len;
> +			td_remain_len -= trb_buff_len;
> +		}
> +
> +		/* Check TD length */
> +		if (running_total != td_len) {
> +			xhci_dbg(xhci, "ISOC TD length unmatch\n");
> +			return -EINVAL;
> +		}
> +
> +		wmb();
> +		start_trb->field[3] |= start_cycle;
> +	}
> +
> +	ring_ep_doorbell(xhci, slot_id, ep_index);
> +	return 0;
> +}
> +
>  /****		Command Ring Operations		****/
>  
>  /* Generic function for queueing a command TRB on the command ring.
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 6d4b21c..980f08b 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1305,6 +1305,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
>  		int slot_id, unsigned int ep_index);
>  int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
>  		int slot_id, unsigned int ep_index);
> +int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
> +		int slot_id, unsigned int ep_index);
>  int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
>  		u32 slot_id, bool command_must_succeed);
>  int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
> -- 
> 1.6.0.4
> 
> 
> 
--
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