From 08e3e98d28e32852e43bf25fb3e64bb3f5e6af4d Mon Sep 17 00:00:00 2001
From: P33M <P33M@github.com>
Date: Fri, 15 Feb 2013 22:36:47 +0000
Subject: [PATCH 049/174] dwc_otg: Fix unsafe access of QTD during URB enqueue

In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the
transaction could complete almost immediately after the qtd was assigned
to a host channel during URB enqueue, which meant the qtd pointer was no
longer valid having been completed and removed. Usually, this resulted in
an OOPS during URB submission. By predetermining whether transactions
need to be queued or not, this unsafe pointer access is avoided.

This bug was only evident on the Pi model A where a device was attached
that had no periodic endpoints (e.g. USB pendrive or some wlan devices).
---
 drivers/usb/host/dwc_otg/dwc_otg_hcd.c       | 23 ++++++++++++-----------
 drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c |  2 +-
 2 files changed, 13 insertions(+), 12 deletions(-)

--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
@@ -462,6 +462,8 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
 {
 	dwc_irqflags_t flags;
 	int retval = 0;
+	uint8_t needs_scheduling = 0;
+	dwc_otg_transaction_type_e tr_type;
 	dwc_otg_qtd_t *qtd;
 	gintmsk_data_t intr_mask = {.d32 = 0 };
 
@@ -493,22 +495,22 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
 		return -DWC_E_NO_MEMORY;
 	}
 #endif
-	retval =
-	    dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
+	intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
+	if(!intr_mask.b.sofintr) needs_scheduling = 1;
+	if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP))
+		/* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
+		needs_scheduling = 0;
+
+	retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
             // creates a new queue in ep_handle if it doesn't exist already
 	if (retval < 0) {
 		DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. "
 			  "Error status %d\n", retval);
 		dwc_otg_hcd_qtd_free(qtd);
+		return retval;
 	}
-	intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
-	if (!intr_mask.b.sofintr && retval == 0) {
-		dwc_otg_transaction_type_e tr_type;
-		if ((qtd->qh->ep_type == UE_BULK)
-		    && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) {
-			/* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
-			return 0;
-		}
+
+	if(needs_scheduling) {
 		DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
 		tr_type = dwc_otg_hcd_select_transactions(hcd);
 		if (tr_type != DWC_OTG_TRANSACTION_NONE) {
@@ -516,7 +518,6 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
 		}
 		DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
 	}
-
 	return retval;
 }
 
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
@@ -937,7 +937,7 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *
 	if (*qh == NULL) {
 		*qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc);
 		if (*qh == NULL) {
-			retval = -1;
+			retval = -DWC_E_NO_MEMORY;
 			goto done;
 		}
 	}
