From 7d7ab442e14034dfbede1840e37942d39ff9687b Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 24 Apr 2015 11:23:52 -0600 Subject: [PATCH] If asynchronous tranfers are supported, then there must also be a mechanism to cancel the pending transfer --- arch/arm/src/lpc17xx/lpc17_usbhost.c | 77 +++++++++++++++++++++++-- arch/arm/src/sama5/sam_ehci.c | 84 ++++++++++++++++++++++------ arch/arm/src/sama5/sam_ohci.c | 74 ++++++++++++++++++++++-- include/nuttx/usb/usbhost.h | 28 ++++++++++ 4 files changed, 237 insertions(+), 26 deletions(-) diff --git a/arch/arm/src/lpc17xx/lpc17_usbhost.c b/arch/arm/src/lpc17xx/lpc17_usbhost.c index 48c4b333c2..0cf06b8f77 100644 --- a/arch/arm/src/lpc17xx/lpc17_usbhost.c +++ b/arch/arm/src/lpc17xx/lpc17_usbhost.c @@ -379,6 +379,7 @@ static void lpc17_asynch_completion(struct lpc17_usbhost_s *priv, static int lpc17_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 lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); #endif #ifdef CONFIG_USBHOST_HUB static int lpc17_connect(FAR struct usbhost_driver_s *drvr, @@ -682,8 +683,8 @@ static void lpc17_tdfree(struct lpc17_gtd_s *td) if (tdfree != NULL && td != TDTAIL) { - tdfree->flink = g_tdfree; - g_tdfree = tdfree; + tdfree->flink = g_tdfree; + g_tdfree = tdfree; } } @@ -2943,7 +2944,74 @@ errout_with_sem: lpc17_givesem(&priv->exclsem); return ret; } -#endif +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: DRVR_CANCEL + * + * Description: + * Cancel a pending asynchronous transfer on an endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which an + * asynchronous transfer should be transferred. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + struct lpc17_ed_s *ed = (struct lpc17_ed_s *)ep; + struct lpc17_gtd_s *td; + struct lpc17_gtd_s *next; + struct lpc17_asynch_s *asynch; + irqstate_t flags; + int ret; + + /* These first steps must be atomic as possible */ + + flags = irqsave(); + + /* It might be possible for no transfer to be in progress (asynch == NULL), + * but it would be an usage error to use the interface to try to cancel a + * synchronous transfer (wdhwait == true). + */ + + DEBUGASSERT(ed != NULL && ed->wdhwait == false); + asynch = ed->asynch; + if (asynch) + { + /* We really need some kind of atomic test and set to do this right */ + + td = (struct lpc17_gtd_s *)ed->hw.headp; + ed->hw.headp = LPC17_TDTAIL_ADDR; + ed->asynch = NULL; + + /* Free all transfer descriptors that were connected to the ED */ + + DEBUGASSERT(td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR); + while (td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR) + { + next = (struct lpc17_gtd_s *)td->hw.nexttd; + lpc17_tdfree(td); + td = next; + } + + memset(asynch, 0, sizeof(struct lpc17_asynch_s)); + lpc17_freeasynch(asynch); + } + + ret = ed->wdhwait ? -EINVAL : OK; + irqrestore(flags); + return ret; +} +#endif /* CONFIG_USBHOST_ASYNCH */ /************************************************************************************ * Name: lpc17_connect @@ -3146,6 +3214,7 @@ struct usbhost_connection_s *lpc17_usbhost_initialize(int controller) drvr->transfer = lpc17_transfer; #ifdef CONFIG_USBHOST_ASYNCH drvr->asynch = lpc17_asynch; + drvr->cancel = lpc17_cancel; #endif #ifdef CONFIG_USBHOST_HUB drvr->connect = lpc17_connect; @@ -3269,7 +3338,7 @@ struct usbhost_connection_s *lpc17_usbhost_initialize(int controller) buffer = (uint8_t *)LPC17_TDFREE_BASE; for (i = 0; i < CONFIG_USBHOST_NTDS; i++) { - /* Put the ED in a free list */ + /* Put the TD in a free list */ lpc17_tdfree((struct lpc17_gtd_s *)buffer); buffer += LPC17_TD_SIZE; diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c index cbc14efd6b..75d147bde2 100644 --- a/arch/arm/src/sama5/sam_ehci.c +++ b/arch/arm/src/sama5/sam_ehci.c @@ -403,6 +403,7 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, 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); #endif #ifdef CONFIG_USBHOST_HUB static int sam_connect(FAR struct usbhost_driver_s *drvr, @@ -3766,23 +3767,17 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, } /******************************************************************************* - * Name: sam_transfer and sam_asynch + * Name: sam_transfer * * Description: * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request and return immediately. Only one transfer may be - * queued;. + * enqueue the transfer request, blocking until the transfer completes. Only + * one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until this function returns. * * This is a blocking method; this functions will not return until the * transfer has completed. * - * - 'transfer' is a blocking method; this method will not return until the - * transfer has completed. - * - 'asynch' will return immediately. When the transfer completes, the - * the callback will be invoked with the provided transfer. This method - * is useful for receiving interrupt transfers which may come - * infrequently. - * * Input Parameters: * drvr - The USB host driver instance obtained as a parameter from the call to * the class create() method. @@ -3791,10 +3786,6 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, * 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. - * callback - This function will be called when the transfer completes ('asynch' - * only). - * arg - The arbitrary parameter that will be passed to the callback function - * when the transfer completes ('asynch' only). * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is @@ -3807,7 +3798,6 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, * EPIPE - Overrun errors * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * @@ -3855,6 +3845,41 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, return nbytes >=0 ? OK : (int)nbytes; } +/******************************************************************************* + * Name: lcp17_asynch + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. When the transfer + * completes, the the callback will be invoked with the provided transfer. + * This method is useful for receiving interrupt transfers which may come + * infrequently. + * + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until the transfer completes. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for 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. + * callback - This function will be called when the transfer completes. + * arg - The arbitrary parameter that will be passed to the callback function + * when the transfer completes. + * + * 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. + * + *******************************************************************************/ + #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, @@ -3863,7 +3888,33 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, # error Not implemented return -ENOSYS; } -#endif +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: sam_cancel + * + * Description: + * Cancel a pending asynchronous transfer on an endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which an + * asynchronous transfer should be transferred. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ +# error Not implemented + return -ENOSYS; +} +#endif /* CONFIG_USBHOST_ASYNCH */ /************************************************************************************ * Name: sam_connect @@ -4216,6 +4267,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) rhport->drvr.transfer = sam_transfer; #ifdef CONFIG_USBHOST_ASYNCH rhport->drvr.asynch = sam_asynch; + rhport->drvr.cancel = sam_cancel; #endif #ifdef CONFIG_USBHOST_HUB rhport->drvr.connect = sam_connect; diff --git a/arch/arm/src/sama5/sam_ohci.c b/arch/arm/src/sama5/sam_ohci.c index cea1aea09c..ce9c03b285 100644 --- a/arch/arm/src/sama5/sam_ohci.c +++ b/arch/arm/src/sama5/sam_ohci.c @@ -420,6 +420,7 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, 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); #endif #ifdef CONFIG_USBHOST_HUB static int sam_connect(FAR struct usbhost_driver_s *drvr, @@ -2921,13 +2922,13 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, } /******************************************************************************* - * Name: sam_transfer and sam_asynch + * Name: sam_transfer * * Description: * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request and return immediately. Only one transfer may be - * queued; Neither this method nor the ctrlin or ctrlout methods can be called - * again until this function returns. + * enqueue the transfer request, blocking until the transfer completes. Only + * one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until this function returns. * * This is a blocking method; this functions will not return until the * transfer has completed. @@ -2952,7 +2953,6 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, * EPIPE - Overrun errors * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * @@ -3098,6 +3098,41 @@ errout: return ret; } +/******************************************************************************* + * Name: lcp17_asynch + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. When the transfer + * completes, the the callback will be invoked with the provided transfer. + * This method is useful for receiving interrupt transfers which may come + * infrequently. + * + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until the transfer completes. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for 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. + * callback - This function will be called when the transfer completes. + * arg - The arbitrary parameter that will be passed to the callback function + * when the transfer completes. + * + * 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. + * + *******************************************************************************/ + #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, @@ -3106,7 +3141,33 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, # error Not implemented return -ENOSYS; } -#endif +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: sam_cancel + * + * Description: + * Cancel a pending asynchronous transfer on an endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which an + * asynchronous transfer should be transferred. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ +# error Not implemented + return -ENOSYS; +} +#endif /* CONFIG_USBHOST_ASYNCH */ /************************************************************************************ * Name: sam_connect @@ -3365,6 +3426,7 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) rhport->drvr.transfer = sam_transfer; #ifdef CONFIG_USBHOST_ASYNCH rhport->drvr.asynch = sam_asynch; + rhport->drvr.cancel = sam_cancel; #endif #ifdef CONFIG_USBHOST_HUB rhport->drvr.connect = sam_connect; diff --git a/include/nuttx/usb/usbhost.h b/include/nuttx/usb/usbhost.h index 5f2231deb9..6347477edd 100644 --- a/include/nuttx/usb/usbhost.h +++ b/include/nuttx/usb/usbhost.h @@ -513,6 +513,28 @@ ((drvr)->asynch(drvr,ep,buffer,buflen,callback,arg)) #endif +/************************************************************************************ + * Name: DRVR_CANCEL + * + * Description: + * Cancel a pending asynchronous transfer on an endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which an + * asynchronous transfer should be transferred. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +# define DRVR_CANCEL(drvr,ep) ((drvr)->cancel(drvr,ep)) +#endif + /************************************************************************************ * Name: DRVR_CONNECT * @@ -823,6 +845,12 @@ struct usbhost_driver_s usbhost_asynch_t callback, FAR void *arg); #endif +#ifdef CONFIG_USBHOST_ASYNCH + /* Cancel any pending asynchronous transfer on an endpoint */ + + int (*cancel)(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +#endif + #ifdef CONFIG_USBHOST_HUB /* New connections may be detected by an attached hub. This method is the * mechanism that is used by the hub class to introduce a new connection