diff --git a/arch/arm/src/lpc17xx/lpc17_usbhost.c b/arch/arm/src/lpc17xx/lpc17_usbhost.c index ee4b14191e1..567e9aafbe0 100644 --- a/arch/arm/src/lpc17xx/lpc17_usbhost.c +++ b/arch/arm/src/lpc17xx/lpc17_usbhost.c @@ -1464,6 +1464,9 @@ static int lpc17_wdhwait(struct lpc17_usbhost_s *priv, struct lpc17_ed_s *ed) */ ed->wdhwait = true; +#ifdef CONFIG_USBHOST_ASYNCH + ed->asynch = NULL; +#endif ret = OK; } @@ -2602,9 +2605,12 @@ static int lpc17_transfer_common(struct lpc17_usbhost_s *priv, * TDs on the Bulk list. */ - regval = lpc17_getreg(LPC17_USBHOST_CMDST); - regval |= OHCI_CMDST_BLF; - lpc17_putreg(regval, LPC17_USBHOST_CMDST); + if (ed->xfrtype == USB_EP_ATTR_XFER_BULK) + { + regval = lpc17_getreg(LPC17_USBHOST_CMDST); + regval |= OHCI_CMDST_BLF; + lpc17_putreg(regval, LPC17_USBHOST_CMDST); + } } return ret; @@ -2829,10 +2835,6 @@ static int lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, goto errout_with_buffers; } -#ifdef CONFIG_USBHOST_ASYNCH - ed->asynch = NULL; -#endif - /* Set up the transfer */ ret = lpc17_transfer_common(priv, ed, buffer, buflen); @@ -3140,6 +3142,8 @@ static int lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) lpc17_freeasynch(asynch); } + /* Determine the return value */ + ret = ed->wdhwait ? -EINVAL : OK; irqrestore(flags); return ret; diff --git a/arch/arm/src/sama5/sam_ohci.c b/arch/arm/src/sama5/sam_ohci.c index 27ccd7a62e8..1bd442fdbe2 100644 --- a/arch/arm/src/sama5/sam_ohci.c +++ b/arch/arm/src/sama5/sam_ohci.c @@ -211,6 +211,12 @@ struct sam_eplist_s { volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */ sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ + uint8_t *buffer; /* Buffer being transferred */ + uint16_t buflen; /* Length of the buffer */ +#endif struct sam_ed_s *ed; /* Endpoint descriptor (ED) */ struct sam_gtd_s *tail; /* Tail transfer descriptor (TD) */ }; @@ -368,11 +374,11 @@ static unsigned int sam_getinterval(uint8_t interval); static void sam_setinttab(uint32_t value, unsigned int interval, unsigned int offset); #endif -static inline int sam_addinted(const FAR struct usbhost_epdesc_s *epdesc, +static inline int sam_addinted(const struct usbhost_epdesc_s *epdesc, struct sam_ed_s *ed); static inline int sam_reminted(struct sam_ed_s *ed); -static inline int sam_addisoced(const FAR struct usbhost_epdesc_s *epdesc, +static inline int sam_addisoced(const struct usbhost_epdesc_s *epdesc, struct sam_ed_s *ed); static inline int sam_remisoced(struct sam_ed_s *ed); @@ -384,6 +390,11 @@ static int sam_enqueuetd(struct sam_rhport_s *rhport, struct sam_eplist_s *eplis static int sam_ep0enqueue(struct sam_rhport_s *rhport); static void sam_ep0dequeue(struct sam_rhport_s *rhport); static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed); +#ifdef CONFIG_USBHOST_ASYNCH +static int sam_wdhasynch(struct sam_rhport_s *rhport, struct sam_ed_s *ed, + usbhost_asynch_t callback, void *arg, + uint8_t *buffer, uint16_t buflen); +#endif static int sam_ctrltd(struct sam_rhport_s *rhport, struct sam_eplist_s *ep0, uint32_t dirpid, uint8_t *buffer, size_t buflen); @@ -395,45 +406,49 @@ static void sam_ohci_bottomhalf(void *arg); /* USB host controller operations **********************************************/ -static int sam_wait(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s **hport); -static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport); -static int sam_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport); +static int sam_wait(struct usbhost_connection_s *conn, + struct usbhost_hubport_s **hport); +static int sam_rh_enumerate(struct usbhost_connection_s *conn, + struct usbhost_hubport_s *hport); +static int sam_enumerate(struct usbhost_connection_s *conn, + struct usbhost_hubport_s *hport); -static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, +static int sam_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, uint16_t maxpacketsize); -static int sam_epalloc(FAR struct usbhost_driver_s *drvr, - const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep); -static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); -static int sam_alloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, FAR size_t *maxlen); -static int sam_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); -static int sam_ioalloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, size_t buflen); -static int sam_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); -static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, - FAR uint8_t *buffer); -static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, - FAR const uint8_t *buffer); -static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen); +static int sam_epalloc(struct usbhost_driver_s *drvr, + const struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep); +static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep); +static int sam_alloc(struct usbhost_driver_s *drvr, + uint8_t **buffer, size_t *maxlen); +static int sam_free(struct usbhost_driver_s *drvr, uint8_t *buffer); +static int sam_ioalloc(struct usbhost_driver_s *drvr, + uint8_t **buffer, size_t buflen); +static int sam_iofree(struct usbhost_driver_s *drvr, uint8_t *buffer); +static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, + uint8_t *buffer); +static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, + const uint8_t *buffer); +static int sam_transfer_common(struct sam_rhport_s *rhport, + struct sam_eplist_s *eplist, + uint8_t *buffer, size_t buflen); +static int sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, + uint8_t *buffer, size_t buflen); #ifdef CONFIG_USBHOST_ASYNCH -static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen, - usbhost_asynch_t callback, FAR void *arg); -static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +static void sam_asynch_completion(struct sam_eplist_s *eplist); +static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep, + uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, void *arg); +static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep); #endif #ifdef CONFIG_USBHOST_HUB -static int sam_connect(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_hubport_s *hport, +static int sam_connect(struct usbhost_driver_s *drvr, + struct usbhost_hubport_s *hport, bool connected); #endif -static void sam_disconnect(FAR struct usbhost_driver_s *drvr); +static void sam_disconnect(struct usbhost_driver_s *drvr); /******************************************************************************* * Private Data @@ -1125,7 +1140,7 @@ static void sam_setinttab(uint32_t value, unsigned int interval, unsigned int of * *******************************************************************************/ -static inline int sam_addinted(const FAR struct usbhost_epdesc_s *epdesc, +static inline int sam_addinted(const struct usbhost_epdesc_s *epdesc, struct sam_ed_s *ed) { #ifndef CONFIG_USBHOST_INT_DISABLE @@ -1387,7 +1402,7 @@ static inline int sam_reminted(struct sam_ed_s *ed) * *******************************************************************************/ -static inline int sam_addisoced(const FAR struct usbhost_epdesc_s *epdesc, +static inline int sam_addisoced(const struct usbhost_epdesc_s *epdesc, struct sam_ed_s *ed) { #ifndef CONFIG_USBHOST_ISOC_DISABLE @@ -1702,9 +1717,9 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport) * * Description: * Set the request for the Writeback Done Head event well BEFORE enabling the - * transfer (as soon as we are absolutely committed to the to avoid transfer). - * We do this to minimize race conditions. This logic would have to be expanded - * if we want to have more than one packet in flight at a time! + * transfer (as soon as we are absolutely committed to perform the transfer). + * We do this to minimize race conditions. This logic would have to be + * expanded if we want to have more than one packet in flight at a time! * *******************************************************************************/ @@ -1728,14 +1743,67 @@ static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed) * completed. */ - eplist->wdhwait = true; - ret = OK; + eplist->wdhwait = true; +#ifdef CONFIG_USBHOST_ASYNCH + eplist->callback = NULL; + eplist->arg = NULL; + eplist->buffer = NULL; + eplist->buflen = 0; +#endif + ret = OK; } irqrestore(flags); return ret; } +/******************************************************************************* + * Name: sam_wdhasynch + * + * Description: + * Set the request for the Writeback Done Head callback well BEFORE enabling + * the transfer (as soon as we are absolutely committed to perform the + * transfer). We do this to minimize race conditions. This logic would have + * to be expanded if we want to have more than one packet in flight at a time! + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int sam_wdhasynch(struct sam_rhport_s *rhport, struct sam_ed_s *ed, + usbhost_asynch_t callback, void *arg, + uint8_t *buffer, uint16_t buflen) +{ + struct sam_eplist_s *eplist; + irqstate_t flags = irqsave(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (rhport->connected) + { + /* Yes.. Get the endpoint list associated with the ED */ + + eplist = ed->eplist; + DEBUGASSERT(eplist); + + /* Then set wdhwait to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer + * completed. + */ + + eplist->wdhwait = false; + eplist->callback = callback; + eplist->arg = arg; + eplist->buffer = buffer; + eplist->buflen = buflen; + ret = OK; + } + + irqrestore(flags); + return ret; +} +#endif + /******************************************************************************* * Name: sam_ctrltd * @@ -1816,7 +1884,7 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, struct sam_eplist_s *eplist, sam_takesem(&eplist->wdhsem); } - /* Re-aquire the ECHI semaphore. The caller expects to be holding + /* Re-acquire the ECHI semaphore. The caller expects to be holding * this upon return. */ @@ -2080,6 +2148,17 @@ static void sam_wdh_bottomhalf(void) sam_givesem(&eplist->wdhsem); eplist->wdhwait = false; } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No thread waiting. Is there a callback scheduled? */ + + else if (eplist->callback) + { + /* Yes.. perform the callback */ + + sam_asynch_completion(eplist); + } +#endif } } @@ -2189,8 +2268,8 @@ static void sam_ohci_bottomhalf(void *arg) * *******************************************************************************/ -static int sam_wait(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s **hport) +static int sam_wait(struct usbhost_connection_s *conn, + struct usbhost_hubport_s **hport) { irqstate_t flags; int rhpndx; @@ -2247,11 +2326,11 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, if (g_ohci.hport) { - FAR struct usbhost_hubport_s *connport; + struct usbhost_hubport_s *connport; /* Yes.. return the external hub port */ - connport = (FAR struct usbhost_hubport_s *)g_ohci.hport; + connport = (struct usbhost_hubport_s *)g_ohci.hport; g_ohci.hport = NULL; *hport = connport; @@ -2303,8 +2382,8 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, * *******************************************************************************/ -static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport) +static int sam_rh_enumerate(struct usbhost_connection_s *conn, + struct usbhost_hubport_s *hport) { struct sam_rhport_s *rhport; uint32_t regaddr; @@ -2368,8 +2447,8 @@ static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, return OK; } -static int sam_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport) +static int sam_enumerate(struct usbhost_connection_s *conn, + struct usbhost_hubport_s *hport) { int ret; @@ -2493,8 +2572,8 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, * ************************************************************************************/ -static int sam_epalloc(FAR struct usbhost_driver_s *drvr, - const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep) +static int sam_epalloc(struct usbhost_driver_s *drvr, + const struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep) { struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_eplist_s *eplist; @@ -2692,7 +2771,7 @@ errout: * ************************************************************************************/ -static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep) { #ifdef CONFIG_DEBUG struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; @@ -2784,8 +2863,8 @@ static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) * *******************************************************************************/ -static int sam_alloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, FAR size_t *maxlen) +static int sam_alloc(struct usbhost_driver_s *drvr, + uint8_t **buffer, size_t *maxlen) { int ret = -ENOMEM; DEBUGASSERT(drvr && buffer && maxlen); @@ -2828,7 +2907,7 @@ static int sam_alloc(FAR struct usbhost_driver_s *drvr, * *******************************************************************************/ -static int sam_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +static int sam_free(struct usbhost_driver_s *drvr, uint8_t *buffer) { DEBUGASSERT(drvr && buffer); @@ -2867,7 +2946,7 @@ static int sam_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * ************************************************************************************/ -static int sam_ioalloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer, +static int sam_ioalloc(struct usbhost_driver_s *drvr, uint8_t **buffer, size_t buflen) { DEBUGASSERT(drvr && buffer); @@ -2901,7 +2980,7 @@ static int sam_ioalloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer, * ************************************************************************************/ -static int sam_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +static int sam_iofree(struct usbhost_driver_s *drvr, uint8_t *buffer) { DEBUGASSERT(drvr && buffer); @@ -2947,9 +3026,9 @@ static int sam_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * *******************************************************************************/ -static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, - FAR uint8_t *buffer) +static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, + uint8_t *buffer) { struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep0; @@ -2996,9 +3075,9 @@ static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, return ret; } -static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, - FAR const uint8_t *buffer) +static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, + const uint8_t *buffer) { struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep0; @@ -3041,6 +3120,90 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, return ret; } +/******************************************************************************* + * Name: sam_transfer_common + * + * Description: + * Initiate a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately + * + * Input Parameters: + * rhport - Internal driver root hub port state structure. + * eplist - The internal representation of the device endpoint on which + * to perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or received + * (IN endpoint). buffer must have been allocated using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int sam_transfer_common(struct sam_rhport_s *rhport, + struct sam_eplist_s *eplist, + uint8_t *buffer, size_t buflen) +{ + struct sam_ed_s *ed; + uint32_t dirpid; + uint32_t regval; + bool in; + int ret; + + ed = eplist->ed; + in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN; + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(OHCI_VTRACE2_TRANSFER, + (ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT, + (uint16_t)buflen); +#else + uvdbg("EP%d %s toggle: %d maxpacket: %d buflen: %d\n", + (ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT, + in ? "IN" : "OUT", + (ed->hw.headp & ED_HEADP_C) != 0 ? 1 : 0, + (ed->hw.ctrl & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT, + buflen); +#endif + + /* Get the direction of the endpoint */ + + if (in) + { + dirpid = GTD_STATUS_DP_IN; + } + else + { + dirpid = GTD_STATUS_DP_OUT; + } + + /* Then enqueue the transfer */ + + ed->tdstatus = TD_CC_NOERROR; + ret = sam_enqueuetd(rhport, eplist, ed, dirpid, GTD_STATUS_T_TOGGLE, + buffer, buflen); + if (ret == OK) + { + /* BulkListFilled. This bit is used to indicate whether there are any + * TDs on the Bulk list. + */ + + if (ed->xfrtype == USB_EP_ATTR_XFER_BULK) + { + regval = sam_getreg(SAM_USBHOST_CMDST); + regval |= OHCI_CMDST_BLF; + sam_putreg(regval, SAM_USBHOST_CMDST); + } + } + + return ret; +} /******************************************************************************* * Name: sam_transfer * @@ -3078,35 +3241,19 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, * *******************************************************************************/ -static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen) +static int sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, + uint8_t *buffer, size_t buflen) { struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep; struct sam_ed_s *ed; - uint32_t dirpid; - uint32_t regval; bool in; int ret; DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail && buffer && buflen > 0); - ed = eplist->ed; - in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN; - -#ifdef CONFIG_USBHOST_TRACE - usbhost_vtrace2(OHCI_VTRACE2_TRANSFER, - (ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT, - (uint16_t)buflen); -#else - uvdbg("EP%d %s toggle: %d maxpacket: %d buflen: %d\n", - (ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT, - in ? "IN" : "OUT", - (ed->hw.headp & ED_HEADP_C) != 0 ? 1 : 0, - (ed->hw.ctrl & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT, - buflen); -#endif + in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN; /* We must have exclusive access to the endpoint, the TD pool, the I/O buffer * pool, the bulk and interrupt lists, and the HCCA interrupt table. @@ -3125,89 +3272,68 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, goto errout; } - /* Get the direction of the endpoint */ + /* Set up the transfer */ - if (in) + ret = sam_transfer_common(rhport, eplist, buffer, buflen); + if (ret < 0) { - dirpid = GTD_STATUS_DP_IN; + udbg("ERROR: sam_transfer_common failed: %d\n", ret); + goto errout; + } + + /* Release the OHCI semaphore while we wait. Other threads need the + * opportunity to access the OHCI resources while we wait. + * + * REVISIT: Is this safe? NO. This is a bug and needs rethinking. + * We need to lock all of the port-resources (not OHCI common) until + * the transfer is complete. But we can't use the common OHCI exclsem + * or we will deadlock while waiting (because the working thread that + * wakes this thread up needs the exclsem). + */ +#warning REVISIT + sam_givesem(&g_ohci.exclsem); + + /* Wait for the Writeback Done Head interrupt Loop to handle any false + * alarm semaphore counts. + */ + + while (eplist->wdhwait) + { + sam_takesem(&eplist->wdhsem); + } + + /* Re-acquire the OCHI semaphore. The caller expects to be holding + * this upon return. + */ + + sam_takesem(&g_ohci.exclsem); + + /* Invalidate the D cache to force the ED to be reloaded from RAM */ + + arch_invalidate_dcache((uintptr_t)ed, + (uintptr_t)ed + sizeof(struct ohci_ed_s)); + + /* Check the TD completion status bits */ + + if (ed->tdstatus == TD_CC_NOERROR) + { + /* On an IN transaction, we also need to invalidate the buffer + * contents to force it to be reloaded from RAM. + */ + + if (in) + { + arch_invalidate_dcache((uintptr_t)buffer, + (uintptr_t)buffer + buflen); + } + + ret = OK; } else { - dirpid = GTD_STATUS_DP_OUT; - } - - /* Then enqueue the transfer */ - - ed->tdstatus = TD_CC_NOERROR; - ret = sam_enqueuetd(rhport, eplist, ed, dirpid, GTD_STATUS_T_TOGGLE, - buffer, buflen); - if (ret == OK) - { - /* BulkListFilled. This bit is used to indicate whether there are any - * TDs on the Bulk list. - */ - - if (ed->xfrtype == USB_EP_ATTR_XFER_BULK) - { - regval = sam_getreg(SAM_USBHOST_CMDST); - regval |= OHCI_CMDST_BLF; - sam_putreg(regval, SAM_USBHOST_CMDST); - } - - /* Release the OHCI semaphore while we wait. Other threads need the - * opportunity to access the OHCI resources while we wait. - * - * REVISIT: Is this safe? NO. This is a bug and needs rethinking. - * We need to lock all of the port-resources (not OHCI common) until - * the transfer is complete. But we can't use the common OHCI exclsem - * or we will deadlock while waiting (because the working thread that - * wakes this thread up needs the exclsem). - */ -#warning REVISIT - sam_givesem(&g_ohci.exclsem); - - /* Wait for the Writeback Done Head interrupt Loop to handle any false - * alarm semaphore counts. - */ - - while (eplist->wdhwait) - { - sam_takesem(&eplist->wdhsem); - } - - /* Re-aquire the ECHI semaphore. The caller expects to be holding - * this upon return. - */ - - sam_takesem(&g_ohci.exclsem); - - /* Invalidate the D cache to force the ED to be reloaded from RAM */ - - arch_invalidate_dcache((uintptr_t)ed, - (uintptr_t)ed + sizeof(struct ohci_ed_s)); - - /* Check the TD completion status bits */ - - if (ed->tdstatus == TD_CC_NOERROR) - { - /* On an IN transaction, we also need to invalidate the buffer - * contents to force it to be reloaded from RAM. - */ - - if (in) - { - arch_invalidate_dcache((uintptr_t)buffer, - (uintptr_t)buffer + buflen); - } - - ret = OK; - } - else - { - usbhost_trace2(OHCI_TRACE2_BADTDSTATUS, RHPORT(rhport), - ed->tdstatus); - ret = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO; - } + usbhost_trace2(OHCI_TRACE2_BADTDSTATUS, RHPORT(rhport), + ed->tdstatus); + ret = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO; } errout: @@ -3219,7 +3345,85 @@ errout: } /******************************************************************************* - * Name: lcp17_asynch + * Name: sam_asynch_completion + * + * Description: + * This function is called at the interrupt level when an asynchronous + * transfer completes. It performs the pending callback. + * + * Input Parameters: + * rhport - Internal driver root hub port state structure. + * eplist - The internal representation of the device endpoint on which + * to perform the transfer. + * + * Returned Values: + * None + * + * Assumptions: + * - Called from the interrupt level + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void sam_asynch_completion(struct sam_eplist_s *eplist) +{ + struct sam_ed_s *ed; + usbhost_asynch_t callback; + void *arg; + int result; + + DEBUGASSERT(eplist->ed && eplist->tail && eplist->callback != NULL && + eplist->buffer != NULL && eplist->buflen > 0); + ed = eplist->ed; + + /* Invalidate the D cache to force the ED to be reloaded from RAM */ + + arch_invalidate_dcache((uintptr_t)ed, + (uintptr_t)ed + sizeof(struct ohci_ed_s)); + + /* Check the TD completion status bits */ + + if (ed->tdstatus == TD_CC_NOERROR) + { + /* On an IN transaction, we also need to invalidate the buffer + * contents to force it to be reloaded from RAM. + */ + + if ((ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN) + { + uintptr_t buffaddr = (uintptr_t)eplist->buffer; + arch_invalidate_dcache(buffaddr, buffaddr + eplist->buflen); + } + + result = OK; + } + else + { + usbhost_trace1(OHCI_TRACE1_BADTDSTATUS, ed->tdstatus); + result = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO; + } + + /* Extract the callback information before freeing the buffer */ + + callback = eplist->callback; + arg = eplist->arg; + + /* Clear any pending transfer indicators */ + + eplist->wdhwait = false; + eplist->callback = NULL; + eplist->arg = NULL; + eplist->buffer = NULL; + eplist->buflen = 0; + + /* Then perform the callback */ + + callback(arg, result); +} +#endif + +/******************************************************************************* + * Name: sam_asynch * * Description: * Process a request to handle a transfer descriptor. This method will @@ -3254,12 +3458,59 @@ errout: *******************************************************************************/ #ifdef CONFIG_USBHOST_ASYNCH -static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen, - usbhost_asynch_t callback, FAR void *arg) +static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep, + uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, void *arg) { -# error Not implemented - return -ENOSYS; + struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; + struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep; + struct sam_ed_s *ed; + int ret; + + DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail && + buffer && buflen > 0 && buflen <= UINT16_MAX); + ed = eplist->ed; + + /* We must have exclusive access to the endpoint, the TD pool, the I/O buffer + * pool, the bulk and interrupt lists, and the HCCA interrupt table. + */ + + sam_takesem(&g_ohci.exclsem); + + /* Set the request for the Writeback Done Head callback well BEFORE + * enabling thetransfer. + */ + + ret = sam_wdhasynch(rhport, ed, callback, arg, buffer, buflen); + if (ret != OK) + { + usbhost_trace1(OHCI_TRACE1_DEVDISCONN, RHPORT(rhport)); + goto errout; + } + + /* Set up the transfer */ + + ret = sam_transfer_common(rhport, eplist, buffer, buflen); + if (ret < 0) + { + udbg("ERROR: sam_transfer_common failed: %d\n", ret); + goto errout; + } + + /* Then just return. The callback will be performed asynchronously + * when the transfer completes. + */ + + sam_givesem(&g_ohci.exclsem); + return OK; + +errout: + /* Make sure that there is no outstanding request on this endpoint */ + + eplist->callback = NULL; + eplist->arg = NULL; + sam_givesem(&g_ohci.exclsem); + return ret; } #endif /* CONFIG_USBHOST_ASYNCH */ @@ -3282,10 +3533,66 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, ************************************************************************************/ #ifdef CONFIG_USBHOST_ASYNCH -static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep) { -# error Not implemented - return -ENOSYS; + struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; + struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep; + struct sam_ed_s *ed; + struct sam_gtd_s *td; + struct sam_gtd_s *next; + uintptr_t paddr; + irqstate_t flags; + + DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail); + ed = eplist->ed; + + /* These first steps must be atomic as possible */ + + flags = irqsave(); + + /* It might be possible for no transfer to be in progress (callback == NULL), + * but it would be an usage error to use the interface to try to cancel a + * synchronous transfer (wdhwait == true). + */ + + DEBUGASSERT(eplist->wdhwait == false); + if (eplist->wdhwait) + { + return -EINVAL; + } + + if (eplist->callback) + { + /* We really need some kind of atomic test and set to do this right */ + + paddr = ed->hw.headp & ED_HEADP_ADDR_MASK; + td = (struct sam_gtd_s *)sam_virtramaddr(paddr); + + paddr = sam_physramaddr((uintptr_t)eplist->tail); + ed->hw.headp = paddr; + + /* Free all transfer descriptors that were connected to the ED */ + + DEBUGASSERT(td != (struct sam_gtd_s *)eplist->tail); + while (td != (struct sam_gtd_s *)eplist->tail) + { + paddr = (uintptr_t)td->hw.nexttd; + next = (struct sam_gtd_s *)sam_virtramaddr(paddr); + sam_tdfree(td); + td = next; + } + } + + /* Reset any pending activity indications */ + + eplist->wdhwait = false; + eplist->callback = NULL; + eplist->arg = NULL; + eplist->buffer = NULL; + eplist->buflen = 0; + + irqrestore(flags); + return OK; } #endif /* CONFIG_USBHOST_ASYNCH */ @@ -3311,8 +3618,8 @@ static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) ************************************************************************************/ #ifdef CONFIG_USBHOST_HUB -static int sam_connect(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_hubport_s *hport, +static int sam_connect(struct usbhost_driver_s *drvr, + struct usbhost_hubport_s *hport, bool connected) { irqstate_t flags; @@ -3362,7 +3669,7 @@ static int sam_connect(FAR struct usbhost_driver_s *drvr, * *******************************************************************************/ -static void sam_disconnect(FAR struct usbhost_driver_s *drvr) +static void sam_disconnect(struct usbhost_driver_s *drvr) { struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; DEBUGASSERT(rhport); @@ -3406,9 +3713,9 @@ static void sam_disconnect(FAR struct usbhost_driver_s *drvr) * *******************************************************************************/ -FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) +struct usbhost_connection_s *sam_ohci_initialize(int controller) { - FAR struct usbhost_hubport_s *hport; + struct usbhost_hubport_s *hport; uintptr_t physaddr; uint32_t regval; uint8_t *buffer; @@ -3685,7 +3992,7 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) * *******************************************************************************/ -int sam_ohci_tophalf(int irq, FAR void *context) +int sam_ohci_tophalf(int irq, void *context) { uint32_t intst; uint32_t inten; @@ -3721,7 +4028,7 @@ int sam_ohci_tophalf(int irq, FAR void *context) DEBUGASSERT(work_available(&g_ohci.work)); DEBUGVERIFY(work_queue(HPWORK, &g_ohci.work, sam_ohci_bottomhalf, - (FAR void *)pending, 0)); + (void *)pending, 0)); /* Disable further OHCI interrupts so that we do not overrun the * work queue. diff --git a/arch/arm/src/sama5/sam_usbhost.c b/arch/arm/src/sama5/sam_usbhost.c index db8ff89abb8..ce508baffb8 100644 --- a/arch/arm/src/sama5/sam_usbhost.c +++ b/arch/arm/src/sama5/sam_usbhost.c @@ -93,6 +93,9 @@ static const struct sam_usbhost_trace_s g_trace1[TRACE1_NSTRINGS] = TRENTRY(OHCI_TRACE1_EDALLOC_FAILED, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to allocate ED\n"), TRENTRY(OHCI_TRACE1_TDALLOC_FAILED, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to allocate TD\n"), TRENTRY(OHCI_TRACE1_IRQATTACH, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to attach IRQ%d\n"), +#ifdef CONFIG_USBHOST_ASYNCH + TRENTRY(OHCI_TRACE1_BADTDSTATUS, TR_OHCI, TR_FMT1, "OHCI ERROR: Bad asynch TD completion status: %d\n"), +#endif #ifdef HAVE_USBHOST_TRACE_VERBOSE TRENTRY(OHCI_VTRACE1_PHYSED, TR_OHCI, TR_FMT1, "OHCI physed: %06x\n"), diff --git a/arch/arm/src/sama5/sam_usbhost.h b/arch/arm/src/sama5/sam_usbhost.h index d19600fd4c6..b04aa83eb51 100644 --- a/arch/arm/src/sama5/sam_usbhost.h +++ b/arch/arm/src/sama5/sam_usbhost.h @@ -80,6 +80,9 @@ enum usbhost_trace1codes_e OHCI_TRACE1_EDALLOC_FAILED, /* OHCI ERROR: Failed to allocate ED */ OHCI_TRACE1_TDALLOC_FAILED, /* OHCI ERROR: Failed to allocate TD */ OHCI_TRACE1_IRQATTACH, /* OHCI ERROR: Failed to attach IRQ */ +#ifdef CONFIG_USBHOST_ASYNCH + OHCI_TRACE1_BADTDSTATUS, /* OHCI ERROR: Bad asynch TD completion status */ +#endif #ifdef HAVE_USBHOST_TRACE_VERBOSE OHCI_VTRACE1_PHYSED, /* OHCI physed */ diff --git a/configs/sama5d3x-ek/README.txt b/configs/sama5d3x-ek/README.txt index f0d0df6d6a8..a3d77520ece 100644 --- a/configs/sama5d3x-ek/README.txt +++ b/configs/sama5d3x-ek/README.txt @@ -1667,6 +1667,9 @@ USB High-Speed Host 2. Stack usage make increase when USB hub support is enabled because the nesting depth of certain USB host class logic can increase. + STATUS: + Hub support has not been verified on this board. + Mass Storage Device Usage -------------------------