Re: [PATCH 07/10 v8] xHCI: Introduce urb_priv structure

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

 



On Tue, Jul 20, 2010 at 04:49:28PM +0800, Andiry Xu wrote:
> >From 22d37c3a7d7463d060efb48ef19df7882c53017c Mon Sep 17 00:00:00 2001
> From: Andiry Xu <andiry.xu@xxxxxxx>
> Date: Fri, 16 Jul 2010 10:57:28 +0800
> Subject: [PATCH 07/10] xHCI: Introduce urb_priv structure
> 
> Add urb_priv data structure to xHCI driver. This structure allows multiple
> xhci TDs to be linked to one urb, which is essential for isochronous
> transfer. For non-isochronous urb, only one TD is needed for one urb;
> for isochronous urb, the TD number for the urb is equal to
> urb->number_of_packets.
> 
> The length field of urb_priv indicates the number of TDs in the urb.
> The td_cnt field indicates the number of TDs already processed by xHC.
> When td_cnt matches length, the urb can be given back to usbcore.
> 
> When an urb is dequeued or cancelled, add all the unprocessed TDs to the
> endpoint's cancelled_td_list. When process a cancelled TD, increase
> td_cnt field. When td_cnt equals urb_priv->length, giveback the
> cancelled urb.
> 
> Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>

> ---
>  drivers/usb/host/xhci-mem.c  |   16 +++++++
>  drivers/usb/host/xhci-ring.c |   91 +++++++++++++++++++++++++++++-------------
>  drivers/usb/host/xhci.c      |   45 ++++++++++++++++++---
>  drivers/usb/host/xhci.h      |    7 +++
>  4 files changed, 125 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
> index 5763dd3..4e51e36 100644
> --- a/drivers/usb/host/xhci-mem.c
> +++ b/drivers/usb/host/xhci-mem.c
> @@ -1389,6 +1389,22 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
>  	return command;
>  }
>  
> +void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv)
> +{
> +	int last;
> +
> +	if (!urb_priv)
> +		return;
> +
> +	last = urb_priv->length - 1;
> +	if (last >= 0) {
> +		int	i;
> +		for (i = 0; i <= last; i++)
> +			kfree(urb_priv->td[i]);
> +	}
> +	kfree(urb_priv);
> +}
> +
>  void xhci_free_command(struct xhci_hcd *xhci,
>  		struct xhci_command *command)
>  {
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index 4c35010..fa8c935 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -578,16 +578,24 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
>  		struct xhci_td *cur_td, int status, char *adjective)
>  {
>  	struct usb_hcd *hcd = xhci_to_hcd(xhci);
> +	struct urb	*urb;
> +	struct urb_priv	*urb_priv;
>  
> -	cur_td->urb->hcpriv = NULL;
> -	usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb);
> -	xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb);
> +	urb = cur_td->urb;
> +	urb_priv = urb->hcpriv;
> +	urb_priv->td_cnt++;
>  
> -	spin_unlock(&xhci->lock);
> -	usb_hcd_giveback_urb(hcd, cur_td->urb, status);
> -	kfree(cur_td);
> -	spin_lock(&xhci->lock);
> -	xhci_dbg(xhci, "%s URB given back\n", adjective);
> +	/* Only giveback urb when this is the last td in urb */
> +	if (urb_priv->td_cnt == urb_priv->length) {
> +		usb_hcd_unlink_urb_from_ep(hcd, urb);
> +		xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb);
> +
> +		spin_unlock(&xhci->lock);
> +		usb_hcd_giveback_urb(hcd, urb, status);
> +		xhci_urb_free_priv(xhci, urb_priv);
> +		spin_lock(&xhci->lock);
> +		xhci_dbg(xhci, "%s URB given back\n", adjective);
> +	}
>  }
>  
>  /*
> @@ -1272,6 +1280,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
>  	struct urb *urb = NULL;
>  	struct xhci_ep_ctx *ep_ctx;
>  	int ret = 0;
> +	struct urb_priv	*urb_priv;
>  	u32 trb_comp_code;
>  
>  	slot_id = TRB_TO_SLOT_ID(event->flags);
> @@ -1325,6 +1334,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
>  td_cleanup:
>  		/* Clean up the endpoint's TD list */
>  		urb = td->urb;
> +		urb_priv = urb->hcpriv;
>  
>  		/* Do one last check of the actual transfer length.
>  		 * If the host controller said we transferred more data than
> @@ -1349,7 +1359,10 @@ td_cleanup:
>  		if (!list_empty(&td->cancelled_td_list))
>  			list_del(&td->cancelled_td_list);
>  
> -		ret = 1;
> +		urb_priv->td_cnt++;
> +		/* Giveback the urb when all the tds are completed */
> +		if (urb_priv->td_cnt == urb_priv->length)
> +			ret = 1;
>  	}
>  
>  	return ret;
> @@ -1588,6 +1601,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
>  	union xhci_trb *event_trb;
>  	struct urb *urb = NULL;
>  	int status = -EINPROGRESS;
> +	struct urb_priv *urb_priv;
>  	struct xhci_ep_ctx *ep_ctx;
>  	u32 trb_comp_code;
>  	int ret = 0;
> @@ -1770,6 +1784,7 @@ cleanup:
>  
>  		if (ret) {
>  			urb = td->urb;
> +			urb_priv = urb->hcpriv;
>  			/* Leave the TD around for the reset endpoint function
>  			 * to use(but only if it's not a control endpoint,
>  			 * since we already queued the Set TR dequeue pointer
> @@ -1778,7 +1793,7 @@ cleanup:
>  			if (usb_endpoint_xfer_control(&urb->ep->desc) ||
>  				(trb_comp_code != COMP_STALL &&
>  					trb_comp_code != COMP_BABBLE))
> -				kfree(td);
> +				xhci_urb_free_priv(xhci, urb_priv);
>  
>  			usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
>  			xhci_dbg(xhci, "Giveback URB %p, len = %d, "
> @@ -1979,10 +1994,12 @@ static int prepare_transfer(struct xhci_hcd *xhci,
>  		unsigned int stream_id,
>  		unsigned int num_trbs,
>  		struct urb *urb,
> -		struct xhci_td **td,
> +		unsigned int td_index,
>  		gfp_t mem_flags)
>  {
>  	int ret;
> +	struct urb_priv *urb_priv;
> +	struct xhci_td	*td;
>  	struct xhci_ring *ep_ring;
>  	struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
>  
> @@ -1998,24 +2015,29 @@ static int prepare_transfer(struct xhci_hcd *xhci,
>  			num_trbs, mem_flags);
>  	if (ret)
>  		return ret;
> -	*td = kzalloc(sizeof(struct xhci_td), mem_flags);
> -	if (!*td)
> -		return -ENOMEM;
> -	INIT_LIST_HEAD(&(*td)->td_list);
> -	INIT_LIST_HEAD(&(*td)->cancelled_td_list);
>  
> -	ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
> -	if (unlikely(ret)) {
> -		kfree(*td);
> -		return ret;
> +	urb_priv = urb->hcpriv;
> +	td = urb_priv->td[td_index];
> +
> +	INIT_LIST_HEAD(&td->td_list);
> +	INIT_LIST_HEAD(&td->cancelled_td_list);
> +
> +	if (td_index == 0) {
> +		ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
> +		if (unlikely(ret)) {
> +			xhci_urb_free_priv(xhci, urb_priv);
> +			urb->hcpriv = NULL;
> +			return ret;
> +		}
>  	}
>  
> -	(*td)->urb = urb;
> -	urb->hcpriv = (void *) (*td);
> +	td->urb = urb;
>  	/* Add this TD to the tail of the endpoint ring's TD list */
> -	list_add_tail(&(*td)->td_list, &ep_ring->td_list);
> -	(*td)->start_seg = ep_ring->enq_seg;
> -	(*td)->first_trb = ep_ring->enqueue;
> +	list_add_tail(&td->td_list, &ep_ring->td_list);
> +	td->start_seg = ep_ring->enq_seg;
> +	td->first_trb = ep_ring->enqueue;
> +
> +	urb_priv->td[td_index] = td;
>  
>  	return 0;
>  }
> @@ -2154,6 +2176,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  {
>  	struct xhci_ring *ep_ring;
>  	unsigned int num_trbs;
> +	struct urb_priv *urb_priv;
>  	struct xhci_td *td;
>  	struct scatterlist *sg;
>  	int num_sgs;
> @@ -2174,9 +2197,13 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  
>  	trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
>  			ep_index, urb->stream_id,
> -			num_trbs, urb, &td, mem_flags);
> +			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
> @@ -2297,6 +2324,7 @@ 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;
> @@ -2342,10 +2370,13 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  
>  	ret = prepare_transfer(xhci, xhci->devs[slot_id],
>  			ep_index, urb->stream_id,
> -			num_trbs, urb, &td, mem_flags);
> +			num_trbs, urb, 0, mem_flags);
>  	if (ret < 0)
>  		return ret;
>  
> +	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
> @@ -2431,6 +2462,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  	struct xhci_generic_trb *start_trb;
>  	int start_cycle;
>  	u32 field, length_field;
> +	struct urb_priv *urb_priv;
>  	struct xhci_td *td;
>  
>  	ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
> @@ -2458,10 +2490,13 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  		num_trbs++;
>  	ret = prepare_transfer(xhci, xhci->devs[slot_id],
>  			ep_index, urb->stream_id,
> -			num_trbs, urb, &td, mem_flags);
> +			num_trbs, urb, 0, mem_flags);
>  	if (ret < 0)
>  		return ret;
>  
> +	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
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 3998f72..448e000 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -720,7 +720,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
>  	unsigned long flags;
>  	int ret = 0;
>  	unsigned int slot_id, ep_index;
> -
> +	struct urb_priv	*urb_priv;
> +	int size, i;
>  
>  	if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0)
>  		return -EINVAL;
> @@ -740,6 +741,30 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
>  		ret = -ESHUTDOWN;
>  		goto exit;
>  	}
> +
> +	if (usb_endpoint_xfer_isoc(&urb->ep->desc))
> +		size = urb->number_of_packets;
> +	else
> +		size = 1;
> +
> +	urb_priv = kzalloc(sizeof(struct urb_priv) +
> +				  size * sizeof(struct xhci_td *), mem_flags);
> +	if (!urb_priv)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < size; i++) {
> +		urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags);
> +		if (!urb_priv->td[i]) {
> +			urb_priv->length = i;
> +			xhci_urb_free_priv(xhci, urb_priv);
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	urb_priv->length = size;
> +	urb_priv->td_cnt = 0;
> +	urb->hcpriv = urb_priv;
> +
>  	if (usb_endpoint_xfer_control(&urb->ep->desc)) {
>  		/* Check to see if the max packet size for the default control
>  		 * endpoint changed during FS device enumeration
> @@ -793,6 +818,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
>  exit:
>  	return ret;
>  dying:
> +	xhci_urb_free_priv(xhci, urb_priv);
> +	urb->hcpriv = NULL;
>  	xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
>  			"non-responsive xHCI host.\n",
>  			urb->ep->desc.bEndpointAddress, urb);
> @@ -834,9 +861,10 @@ dying:
>  int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
>  {
>  	unsigned long flags;
> -	int ret;
> +	int ret, i;
>  	u32 temp;
>  	struct xhci_hcd *xhci;
> +	struct urb_priv	*urb_priv;
>  	struct xhci_td *td;
>  	unsigned int ep_index;
>  	struct xhci_ring *ep_ring;
> @@ -851,12 +879,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
>  	temp = xhci_readl(xhci, &xhci->op_regs->status);
>  	if (temp == 0xffffffff) {
>  		xhci_dbg(xhci, "HW died, freeing TD.\n");
> -		td = (struct xhci_td *) urb->hcpriv;
> +		urb_priv = urb->hcpriv;
>  
>  		usb_hcd_unlink_urb_from_ep(hcd, urb);
>  		spin_unlock_irqrestore(&xhci->lock, flags);
>  		usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN);
> -		kfree(td);
> +		xhci_urb_free_priv(xhci, urb_priv);
>  		return ret;
>  	}
>  	if (xhci->xhc_state & XHCI_STATE_DYING) {
> @@ -884,9 +912,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
>  
>  	xhci_dbg(xhci, "Endpoint ring:\n");
>  	xhci_debug_ring(xhci, ep_ring);
> -	td = (struct xhci_td *) urb->hcpriv;
>  
> -	list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
> +	urb_priv = urb->hcpriv;
> +
> +	for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
> +		td = urb_priv->td[i];
> +		list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
> +	}
> +
>  	/* Queue a stop endpoint command, but only if this is
>  	 * the first cancellation to be handled.
>  	 */
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 01da844..8316c41 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1090,6 +1090,12 @@ struct xhci_scratchpad {
>  	dma_addr_t *sp_dma_buffers;
>  };
>  
> +struct urb_priv {
> +	int	length;
> +	int	td_cnt;
> +	struct	xhci_td	*td[0];
> +};
> +
>  /*
>   * Each segment table entry is 4*32bits long.  1K seems like an ok size:
>   * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
> @@ -1347,6 +1353,7 @@ struct xhci_ring *xhci_stream_id_to_ring(
>  struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
>  		bool allocate_in_ctx, bool allocate_completion,
>  		gfp_t mem_flags);
> +void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv);
>  void xhci_free_command(struct xhci_hcd *xhci,
>  		struct xhci_command *command);
>  
> -- 
> 1.7.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