diff options
| author | Andiry Xu <andiry.xu@amd.com> | 2010-07-22 15:23:31 -0700 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-10 14:35:41 -0700 | 
| commit | 8e51adccd4c4b9ffcd509d7f2afce0a906139f75 (patch) | |
| tree | ddd035c4775db7f504878574d3925f5bf4c87ccd /drivers/usb/host/xhci.c | |
| parent | d18240db797ed749b511b8dc910c5dcf08be46d6 (diff) | |
USB: 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@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/xhci.c')
| -rw-r--r-- | drivers/usb/host/xhci.c | 45 | 
1 files changed, 39 insertions, 6 deletions
| diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3106d22ae053..295a0a2063a6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -804,7 +804,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; @@ -824,6 +825,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 @@ -877,6 +902,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); @@ -918,9 +945,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; @@ -935,12 +963,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) { @@ -968,9 +996,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.  	 */ | 
