diff --git a/arch/arm/src/lpc31xx/lpc31_ehci.c b/arch/arm/src/lpc31xx/lpc31_ehci.c index 9729fa5c4d7..76956623ee4 100644 --- a/arch/arm/src/lpc31xx/lpc31_ehci.c +++ b/arch/arm/src/lpc31xx/lpc31_ehci.c @@ -246,6 +246,10 @@ struct lpc31_epinfo_s int result; /* The result of the transfer */ uint32_t xfrd; /* On completion, will hold the number of bytes transferred */ sem_t iocsem; /* Semaphore used to wait for transfer completion */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ +#endif }; /* This structure retains the state of one root hub port */ @@ -483,6 +487,12 @@ static int lpc31_intr_setup(struct lpc31_rhport_s *rhport, struct lpc31_epinfo_s *epinfo, uint8_t *buffer, size_t buflen); #endif static ssize_t lpc31_transfer_wait(struct lpc31_epinfo_s *epinfo); +#ifdef CONFIG_USBHOST_ASYNCH +static inline int lpc31_asynch_setup(struct lpc31_rhport_s *rhport, + struct lpc31_epinfo_s *epinfo, usbhost_asynch_t callback, + FAR void *arg); +static void lpc31_asynch_completion(struct lpc31_epinfo_s *epinfo); +#endif /* Interrupt Handling **********************************************************/ @@ -1533,22 +1543,29 @@ static int lpc31_ioc_setup(struct lpc31_rhport_s *rhport, struct lpc31_epinfo_s int ret = -ENODEV; DEBUGASSERT(rhport && epinfo && !epinfo->iocwait); +#ifdef CONFIG_USBHOST_ASYNCH + DEBUGASSERT(epinfo->callback == NULL); +#endif /* Is the device still connected? */ flags = irqsave(); if (rhport->connected) { - /* Then set wdhwait to indicate that we expect to be informed when + /* Then set iocwait to indicate that we expect to be informed when * either (1) the device is disconnected, or (2) the transfer * completed. */ - epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ - epinfo->status = 0; /* No status yet */ - epinfo->xfrd = 0; /* Nothing transferred yet */ - epinfo->result = -EBUSY; /* Transfer in progress */ - ret = OK; /* We are good to go */ + epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ +#ifdef CONFIG_USBHOST_ASYNCH + epinfo->callback = NULL; /* No asynchronous callback */ + epinfo->arg = NULL; +#endif + ret = OK; /* We are good to go */ } irqrestore(flags); @@ -2479,6 +2496,106 @@ static ssize_t lpc31_transfer_wait(struct lpc31_epinfo_s *epinfo) return epinfo->xfrd; } +/******************************************************************************* + * Name: lpc31_asynch_setup + * + * Description: + * Setup to receive an asynchronous notification when a transfer completes. + * + * Input Parameters: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer will be performed. + * callback - The function to be called when the completes + * arg - An arbitrary argument that will be provided with the callback. + * + * Returned Values: + * None + * + * Assumptions: + * - Called from the interrupt level + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static inline int lpc31_asynch_setup(struct lpc31_rhport_s *rhport, + struct lpc31_epinfo_s *epinfo, + usbhost_asynch_t callback, FAR void *arg) +{ + irqstate_t flags; + int ret = -ENODEV; + + DEBUGASSERT(rhport && epinfo && !epinfo->iocwait && + epinfo->callback == NULL); + + /* Is the device still connected? */ + + flags = irqsave(); + if (rhport->connected) + { + /* Then save callback information to used when either (1) the + * device is disconnected, or (2) the transfer completes. + */ + + epinfo->iocwait = false; /* No synchronous wakeup */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ + epinfo->callback = callback; /* Asynchronous callback */ + epinfo->arg = arg; /* Argument that accompanies the callback */ + ret = OK; /* We are good to go */ + } + + irqrestore(flags); + return ret; +} +#endif + +/******************************************************************************* + * Name: lpc31_asynch_completion + * + * Description: + * This function is called at the interrupt level when an asynchronous + * transfer completes. It performs the pending callback. + * + * Input Parameters: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer was performed. + * + * Returned Values: + * None + * + * Assumptions: + * - Called from the interrupt level + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void lpc31_asynch_completion(struct lpc31_epinfo_s *epinfo) +{ + usbhost_asynch_t callback; + void *arg; + int result; + + DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false && + epinfo->callback != NULL); + + /* Extract and reset the callback info */ + + callback = epinfo->callback; + arg = epinfo->arg; + result = epinfo->result; + + epinfo->callback = NULL; + epinfo->arg = NULL; + epinfo->result = OK; + epinfo->iocwait = false; + + /* Then perform the callback */ + + callback(arg, result); +} +#endif + /******************************************************************************* * EHCI Interrupt Handling *******************************************************************************/ @@ -2663,6 +2780,17 @@ static int lpc31_qh_ioccheck(struct lpc31_qh_s *qh, uint32_t **bp, void *arg) epinfo->iocwait = 0; } +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. Is there a pending asynchronous transfer? */ + + else if (epinfo->callback != NULL) + { + /* Yes.. perform the callback */ + + lpc31_asynch_completion(epinfo); + } +#endif + /* Then release this QH by returning it to the free list */ lpc31_qh_free(qh); @@ -4077,8 +4205,68 @@ static int lpc31_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) { -# error Not implemented - return -ENOSYS; + struct lpc31_rhport_s *rhport = (struct lpc31_rhport_s *)drvr; + struct lpc31_epinfo_s *epinfo = (struct lpc31_epinfo_s *)ep; + int ret; + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* We must have exclusive access to the EHCI hardware and data structures. */ + + lpc31_takesem(&g_ehci.exclsem); + + /* Set the request for the callback well BEFORE initiating the transfer. */ + + ret = lpc31_asynch_setup(rhport, epinfo, callback, arg); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Initiate the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_BULK: + ret = lpc31_async_setup(rhport, epinfo, NULL, buffer, buflen); + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + ret = lpc31_intr_setup(rhport, epinfo, buffer, buflen); + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + case USB_EP_ATTR_XFER_CONTROL: + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + break; + } + + /* Check for errors in the setup of the transfer */ + + if (ret < 0) + { + goto errout_with_callback; + } + + /* The transfer is in progress */ + + lpc31_givesem(&g_ehci.exclsem); + return OK; + +errout_with_callback: + epinfo->callback = NULL; + epinfo->arg = NULL; +errout_with_sem: + lpc31_givesem(&g_ehci.exclsem); + return ret; } #endif /* CONFIG_USBHOST_ASYNCH */ diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c index 845769f77be..762319e8683 100644 --- a/arch/arm/src/sama5/sam_ehci.c +++ b/arch/arm/src/sama5/sam_ehci.c @@ -214,6 +214,10 @@ struct sam_epinfo_s int result; /* The result of the transfer */ uint32_t xfrd; /* On completion, will hold the number of bytes transferred */ sem_t iocsem; /* Semaphore used to wait for transfer completion */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ +#endif }; /* This structure retains the state of one root hub port */ @@ -360,6 +364,12 @@ static int sam_intr_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinfo, uint8_t *buffer, size_t buflen); #endif static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo); +#ifdef CONFIG_USBHOST_ASYNCH +static inline int sam_asynch_setup(struct sam_rhport_s *rhport, + struct sam_epinfo_s *epinfo, usbhost_asynch_t callback, + FAR void *arg); +static void sam_asynch_completion(struct sam_epinfo_s *epinfo); +#endif /* Interrupt Handling **********************************************************/ @@ -1348,22 +1358,29 @@ static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinf int ret = -ENODEV; DEBUGASSERT(rhport && epinfo && !epinfo->iocwait); +#ifdef CONFIG_USBHOST_ASYNCH + DEBUGASSERT(epinfo->callback == NULL); +#endif /* Is the device still connected? */ flags = irqsave(); if (rhport->connected) { - /* Then set wdhwait to indicate that we expect to be informed when + /* Then set iocwait to indicate that we expect to be informed when * either (1) the device is disconnected, or (2) the transfer * completed. */ - epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ - epinfo->status = 0; /* No status yet */ - epinfo->xfrd = 0; /* Nothing transferred yet */ - epinfo->result = -EBUSY; /* Transfer in progress */ - ret = OK; /* We are good to go */ + epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ +#ifdef CONFIG_USBHOST_ASYNCH + epinfo->callback = NULL; /* No asynchronous callback */ + epinfo->arg = NULL; +#endif + ret = OK; /* We are good to go */ } irqrestore(flags); @@ -2298,6 +2315,106 @@ static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo) return epinfo->xfrd; } +/******************************************************************************* + * Name: sam_asynch_setup + * + * Description: + * Setup to receive an asynchronous notification when a transfer completes. + * + * Input Parameters: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer will be performed. + * callback - The function to be called when the completes + * arg - An arbitrary argument that will be provided with the callback. + * + * Returned Values: + * None + * + * Assumptions: + * - Called from the interrupt level + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static inline int sam_asynch_setup(struct sam_rhport_s *rhport, + struct sam_epinfo_s *epinfo, + usbhost_asynch_t callback, FAR void *arg) +{ + irqstate_t flags; + int ret = -ENODEV; + + DEBUGASSERT(rhport && epinfo && !epinfo->iocwait && + epinfo->callback == NULL); + + /* Is the device still connected? */ + + flags = irqsave(); + if (rhport->connected) + { + /* Then save callback information to used when either (1) the + * device is disconnected, or (2) the transfer completes. + */ + + epinfo->iocwait = false; /* No synchronous wakeup */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ + epinfo->callback = callback; /* Asynchronous callback */ + epinfo->arg = arg; /* Argument that accompanies the callback */ + ret = OK; /* We are good to go */ + } + + irqrestore(flags); + return ret; +} +#endif + +/******************************************************************************* + * 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: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer was performed. + * + * Returned Values: + * None + * + * Assumptions: + * - Called from the interrupt level + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void sam_asynch_completion(struct sam_epinfo_s *epinfo) +{ + usbhost_asynch_t callback; + void *arg; + int result; + + DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false && + epinfo->callback != NULL); + + /* Extract and reset the callback info */ + + callback = epinfo->callback; + arg = epinfo->arg; + result = epinfo->result; + + epinfo->callback = NULL; + epinfo->arg = NULL; + epinfo->result = OK; + epinfo->iocwait = false; + + /* Then perform the callback */ + + callback(arg, result); +} +#endif + /******************************************************************************* * EHCI Interrupt Handling *******************************************************************************/ @@ -2482,6 +2599,17 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg) epinfo->iocwait = 0; } +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. Is there a pending asynchronous transfer? */ + + else if (epinfo->callback != NULL) + { + /* Yes.. perform the callback */ + + sam_asynch_completion(epinfo); + } +#endif + /* Then release this QH by returning it to the free list */ sam_qh_free(qh); @@ -3864,7 +3992,7 @@ errout_with_sem: } /******************************************************************************* - * Name: lpc17_asynch + * Name: sam_asynch * * Description: * Process a request to handle a transfer descriptor. This method will @@ -3903,8 +4031,68 @@ 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) { -# error Not implemented - return -ENOSYS; + struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; + struct sam_epinfo_s *epinfo = (struct sam_epinfo_s *)ep; + int ret; + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* We must have exclusive access to the EHCI hardware and data structures. */ + + sam_takesem(&g_ehci.exclsem); + + /* Set the request for the callback well BEFORE initiating the transfer. */ + + ret = sam_asynch_setup(rhport, epinfo, callback, arg); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Initiate the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_BULK: + ret = sam_async_setup(rhport, epinfo, NULL, buffer, buflen); + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + ret = sam_intr_setup(rhport, epinfo, buffer, buflen); + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + case USB_EP_ATTR_XFER_CONTROL: + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + break; + } + + /* Check for errors in the setup of the transfer */ + + if (ret < 0) + { + goto errout_with_callback; + } + + /* The transfer is in progress */ + + sam_givesem(&g_ehci.exclsem); + return OK; + +errout_with_callback: + epinfo->callback = NULL; + epinfo->arg = NULL; +errout_with_sem: + sam_givesem(&g_ehci.exclsem); + return ret; } #endif /* CONFIG_USBHOST_ASYNCH */