summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc2/hcd.c
diff options
context:
space:
mode:
authorMian Yousaf Kaukab <yousaf.kaukab@intel.com>2015-06-29 11:05:28 +0200
committerFelipe Balbi <balbi@ti.com>2015-07-06 12:34:08 -0500
commitb58e6ceef96f2fbcb1698b0a7c7df59fdea0aa32 (patch)
treeee2e0256ca9d9a743e2b22bcf1d108ad98f6e594 /drivers/usb/dwc2/hcd.c
parentbe9d39881fc4fa39a64b6eed6bab5d9ee5125344 (diff)
downloadlinux-b58e6ceef96f2fbcb1698b0a7c7df59fdea0aa32.tar.bz2
usb: dwc2: host: allocate qh before atomic enqueue
To avoid sleep while atomic bugs, allocate qh before calling dwc2_hcd_urb_enqueue. qh pointer can be used directly now instead of passing ep->hcpriv as double pointer. Acked-by: John Youn <johnyoun@synopsys.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Tested-by: Doug Anderson <dianders@chromium.org> Signed-off-by: Mian Yousaf Kaukab <yousaf.kaukab@intel.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc2/hcd.c')
-rw-r--r--drivers/usb/dwc2/hcd.c31
1 files changed, 27 insertions, 4 deletions
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index b10377c65064..80bce711bc14 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -359,7 +359,7 @@ 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,
+ struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
gfp_t mem_flags)
{
struct dwc2_qtd *qtd;
@@ -391,8 +391,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
return -ENOMEM;
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",
@@ -2445,6 +2444,8 @@ 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;
if (dbg_urb(urb)) {
dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n");
@@ -2523,13 +2524,24 @@ 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;
+ }
spin_lock_irqsave(&hsotg->lock, flags);
retval = usb_hcd_link_urb_to_ep(hcd, urb);
if (retval)
goto fail1;
- retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, &ep->hcpriv, mem_flags);
+ retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, mem_flags);
if (retval)
goto fail2;
@@ -2549,6 +2561,17 @@ fail2:
fail1:
spin_unlock_irqrestore(&hsotg->lock, flags);
urb->hcpriv = NULL;
+ 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);