From c641b8fed450b5142ad16cd6df8fec02910cd7e1 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 28 Apr 2015 11:29:16 -0600 Subject: [PATCH] USB ECHI: Fix a bug when trying to traverse an empty asynchronous queue --- arch/arm/src/lpc31xx/lpc31_ehci.c | 47 ++++++++++++++++++++++++++++--- arch/arm/src/sama5/sam_ehci.c | 47 ++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/arch/arm/src/lpc31xx/lpc31_ehci.c b/arch/arm/src/lpc31xx/lpc31_ehci.c index e9598aee0f..49dc9a615d 100644 --- a/arch/arm/src/lpc31xx/lpc31_ehci.c +++ b/arch/arm/src/lpc31xx/lpc31_ehci.c @@ -2995,13 +2995,18 @@ static inline void lpc31_ioc_bottomhalf(void) cp15_invalidate_dcache((uintptr_t)&g_asynchead.hw, (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); - /* Set the back pointer to the forward qTD pointer of the asynchronous + /* Set the back pointer to the forward QH pointer of the asynchronous * queue head. */ bp = (uint32_t *)&g_asynchead.hw.hlp; qh = (struct lpc31_qh_s *)lpc31_virtramaddr(lpc31_swap32(*bp) & QH_HLP_MASK); - if (qh) + + /* If the asynchronous queue is empty, then the forward point in the + * asynchronous queue head will point back to the the queue head. + */ + + if (qh && qh != &g_asynchead) { /* Then traverse and operate on every QH and qTD in the asynchronous * queue @@ -4491,12 +4496,46 @@ static int lpc31_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) { case USB_EP_ATTR_XFER_CONTROL: case USB_EP_ATTR_XFER_BULK: - qh = &g_asynchead; + { + /* Get the horizontal pointer from the head of the asynchronous + * queue. + */ + + bp = (uint32_t *)&g_asynchead.hw.hlp; + qh = (struct lpc31_qh_s *)lpc31_virtramaddr(lpc31_swap32(*bp) & QH_HLP_MASK); + + /* If the asynchronous queue is empty, then the forward point in + * the asynchronous queue head will point back to the the queue + * head. + */ + + if (qh && qh != &g_asynchead) + { + /* Claim that we successfully cancelled the transfer */ + + return OK; + } + } break; #ifndef CONFIG_USBHOST_INT_DISABLE case USB_EP_ATTR_XFER_INT: - qh = &g_intrhead; + { + /* Get the horizontal pointer from the head of the interrupt + * queue. + */ + + bp = (uint32_t *)&g_intrhead.hw.hlp; + qh = (struct lpc31_qh_s *)lpc31_virtramaddr(lpc31_swap32(*bp) & QH_HLP_MASK); + if (qh) + { + /* if the queue is empty, then just claim that we successfully + * cancelled the transfer. + */ + + return OK; + } + } break; #endif diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c index 3e3bb37dd8..22a12645f8 100644 --- a/arch/arm/src/sama5/sam_ehci.c +++ b/arch/arm/src/sama5/sam_ehci.c @@ -2814,13 +2814,18 @@ static inline void sam_ioc_bottomhalf(void) arch_invalidate_dcache((uintptr_t)&g_asynchead.hw, (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); - /* Set the back pointer to the forward qTD pointer of the asynchronous + /* Set the back pointer to the forward QH pointer of the asynchronous * queue head. */ bp = (uint32_t *)&g_asynchead.hw.hlp; qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK); - if (qh) + + /* If the asynchronous queue is empty, then the forward point in the + * asynchronous queue head will point back to the the queue head. + */ + + if (qh && qh != &g_asynchead) { /* Then traverse and operate on every QH and qTD in the asynchronous * queue @@ -4317,12 +4322,46 @@ static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) { case USB_EP_ATTR_XFER_CONTROL: case USB_EP_ATTR_XFER_BULK: - qh = &g_asynchead; + { + /* Get the horizontal pointer from the head of the asynchronous + * queue. + */ + + bp = (uint32_t *)&g_asynchead.hw.hlp; + qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK); + + /* If the asynchronous queue is empty, then the forward point in + * the asynchronous queue head will point back to the the queue + * head. + */ + + if (qh && qh != &g_asynchead) + { + /* Claim that we successfully cancelled the transfer */ + + return OK; + } + } break; #ifndef CONFIG_USBHOST_INT_DISABLE case USB_EP_ATTR_XFER_INT: - qh = &g_intrhead; + { + /* Get the horizontal pointer from the head of the interrupt + * queue. + */ + + bp = (uint32_t *)&g_intrhead.hw.hlp; + qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK); + if (qh) + { + /* if the queue is empty, then just claim that we successfully + * cancelled the transfer. + */ + + return OK; + } + } break; #endif