diff options
Diffstat (limited to 'drivers/usb/dwc2/hcd.c')
| -rw-r--r-- | drivers/usb/dwc2/hcd.c | 55 | 
1 files changed, 42 insertions, 13 deletions
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index b10377c65064..f845c41fe9e5 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -359,10 +359,9 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)  /* Caller must hold driver lock */  static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, -				struct dwc2_hcd_urb *urb, void **ep_handle, -				gfp_t mem_flags) +				struct dwc2_hcd_urb *urb, struct dwc2_qh *qh, +				struct dwc2_qtd *qtd)  { -	struct dwc2_qtd *qtd;  	u32 intr_mask;  	int retval;  	int dev_speed; @@ -386,18 +385,15 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,  			return -ENODEV;  	} -	qtd = kzalloc(sizeof(*qtd), mem_flags);  	if (!qtd) -		return -ENOMEM; +		return -EINVAL;  	dwc2_hcd_qtd_init(qtd, urb); -	retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle, -				  mem_flags); +	retval = dwc2_hcd_qtd_add(hsotg, qtd, qh);  	if (retval) {  		dev_err(hsotg->dev,  			"DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n",  			retval); -		kfree(qtd);  		return retval;  	} @@ -2445,6 +2441,9 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,  	u32 tflags = 0;  	void *buf;  	unsigned long flags; +	struct dwc2_qh *qh; +	bool qh_allocated = false; +	struct dwc2_qtd *qtd;  	if (dbg_urb(urb)) {  		dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); @@ -2523,15 +2522,32 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,  						 urb->iso_frame_desc[i].length);  	urb->hcpriv = dwc2_urb; +	qh = (struct dwc2_qh *) ep->hcpriv; +	/* Create QH for the endpoint if it doesn't exist */ +	if (!qh) { +		qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, mem_flags); +		if (!qh) { +			retval = -ENOMEM; +			goto fail0; +		} +		ep->hcpriv = qh; +		qh_allocated = true; +	} + +	qtd = kzalloc(sizeof(*qtd), mem_flags); +	if (!qtd) { +		retval = -ENOMEM; +		goto fail1; +	}  	spin_lock_irqsave(&hsotg->lock, flags);  	retval = usb_hcd_link_urb_to_ep(hcd, urb);  	if (retval) -		goto fail1; +		goto fail2; -	retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, &ep->hcpriv, mem_flags); +	retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, qtd);  	if (retval) -		goto fail2; +		goto fail3;  	if (alloc_bandwidth) {  		dwc2_allocate_bus_bandwidth(hcd, @@ -2543,12 +2559,25 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,  	return 0; -fail2: +fail3:  	dwc2_urb->priv = NULL;  	usb_hcd_unlink_urb_from_ep(hcd, urb); -fail1: +fail2:  	spin_unlock_irqrestore(&hsotg->lock, flags);  	urb->hcpriv = NULL; +	kfree(qtd); +fail1: +	if (qh_allocated) { +		struct dwc2_qtd *qtd2, *qtd2_tmp; + +		ep->hcpriv = NULL; +		dwc2_hcd_qh_unlink(hsotg, qh); +		/* Free each QTD in the QH's QTD list */ +		list_for_each_entry_safe(qtd2, qtd2_tmp, &qh->qtd_list, +							 qtd_list_entry) +			dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh); +		dwc2_hcd_qh_free(hsotg, qh); +	}  fail0:  	kfree(dwc2_urb);  |