From dd56308ce7c169aac13008f2e13d498eaea7f23e Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 22 Apr 2015 15:03:25 -0600 Subject: [PATCH] USB Hub: Initial implementation asynchronous pipe I/O in the LPC17 HCD needed for hub support --- arch/arm/src/lpc17xx/lpc17_usbhost.c | 752 ++++++++++++++++++++++----- 1 file changed, 614 insertions(+), 138 deletions(-) diff --git a/arch/arm/src/lpc17xx/lpc17_usbhost.c b/arch/arm/src/lpc17xx/lpc17_usbhost.c index 3a76ba8f040..be149d3aa1e 100644 --- a/arch/arm/src/lpc17xx/lpc17_usbhost.c +++ b/arch/arm/src/lpc17xx/lpc17_usbhost.c @@ -84,6 +84,10 @@ # warning "No IO buffers allocated" #endif +#ifndef CONFIG_LPC17_USBHOST_NASYNCH +# define CONFIG_LPC17_USBHOST_NASYNCH 8 +#endif + /* OHCI Setup ******************************************************************/ /* Frame Interval / Periodic Start */ @@ -180,6 +184,21 @@ struct lpc17_usbhost_s #endif }; +#ifdef CONFIG_USBHOST_ASYNCH +/* This structure describes one asynchronous transfer */ + +struct lpc17_asynch_s +{ + uint16_t buflen; /* Buffer length */ + uint8_t *user; /* User buffer */ +#if LPC17_IOBUFFERS > 0 + uint8_t *alloc; /* Allocated buffer */ +#endif + usbhost_asynch_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ +}; +#endif + /* The OCHI expects the size of an endpoint descriptor to be 16 bytes. * However, the size allocated for an endpoint descriptor is 32 bytes in * lpc17_ohciram.h. This extra 16-bytes is used by the OHCI host driver in @@ -194,12 +213,17 @@ struct lpc17_ed_s /* Software specific fields */ - uint8_t xfrtype; /* Transfer type. See SB_EP_ATTR_XFER_* in usb.h */ - uint8_t interval; /* Periodic EP polling interval: 2, 4, 6, 16, or 32 */ - volatile uint8_t tdstatus; /* TD control status bits from last Writeback Done Head event */ - volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */ - sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */ - /* Unused bytes follow, depending on the size of sem_t */ + uint8_t xfrtype; /* 0: Transfer type. See SB_EP_ATTR_XFER_* in usb.h */ + uint8_t interval; /* 1: Periodic EP polling interval: 2, 4, 6, 16, or 32 */ + volatile uint8_t tdstatus; /* 2: TD control status bits from last Writeback Done Head event */ + volatile bool wdhwait; /* 3: TRUE: Thread is waiting for WDH interrupt */ + sem_t wdhsem; /* 4: Semaphore used to wait for Writeback Done Head event */ + /* Unused bytes may follow, depending on the size of sem_t */ +#ifdef CONFIG_USBHOST_ASYNCH + /* Pointer to structure that manages asynchronous transfers on this pipe */ + + struct lpc17_asynch_s *asynch; +#endif }; /* The OCHI expects the size of an transfer descriptor to be 16 bytes. @@ -267,6 +291,10 @@ static void lpc17_tbfree(uint8_t *buffer); static uint8_t *lpc17_allocio(void); static void lpc17_freeio(uint8_t *buffer); #endif +#ifdef CONFIG_USBHOST_ASYNCH +static struct lpc17_asynch_s *lpc17_allocasynch(void); +static void lpc17_freeasynch(struct lpc17_asynch_s *asynch); +#endif /* ED list helper functions ****************************************************/ @@ -332,9 +360,22 @@ static int lpc17_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, static int lpc17_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, const struct usb_ctrlreq_s *req, const uint8_t *buffer); +static int lpc17_transfer_common(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed, uint8_t *buffer, + size_t buflen); +#if LPC17_IOBUFFERS > 0 +static int lpc17_dma_alloc(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed, uint8_t *userbuffer, + size_t buflen, uint8_t **alloc); +static void lpc17_dma_free(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed, uint8_t *userbuffer, + size_t buflen, uint8_t *alloc); +#endif static int lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, uint8_t *buffer, size_t buflen); #ifdef CONFIG_USBHOST_ASYNCH +static void lpc17_asynch_completion(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed); 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); @@ -378,6 +419,13 @@ static struct lpc17_list_s *g_tbfree; /* List of unused transfer buffers */ static struct lpc17_list_s *g_iofree; /* List of unused I/O buffers */ #endif +#ifdef CONFIG_USBHOST_ASYNCH +/* Pool and freelist of asynchronous transfer structures */ + +static struct lpc17_list_s *g_asynchfree; +static struct lpc17_list_s g_asynchbuffers[CONFIG_LPC17_USBHOST_NASYNCH]; +#endif + /******************************************************************************* * Public Data *******************************************************************************/ @@ -695,11 +743,19 @@ static void lpc17_tbfree(uint8_t *buffer) #if LPC17_IOBUFFERS > 0 static uint8_t *lpc17_allocio(void) { - uint8_t *ret = (uint8_t *)g_iofree; + uint8_t *ret; + irqstate_t flags; + + /* lpc17_freeio() may be called from the interrupt level */ + + flags = irqsave(); + ret = (uint8_t *)g_iofree; if (ret) { g_iofree = ((struct lpc17_list_s*)ret)->flink; } + + irqrestore(flags); return ret; } #endif @@ -715,9 +771,72 @@ static uint8_t *lpc17_allocio(void) #if LPC17_IOBUFFERS > 0 static void lpc17_freeio(uint8_t *buffer) { - struct lpc17_list_s *iofree = (struct lpc17_list_s *)buffer; - iofree->flink = g_iofree; - g_iofree = iofree; + struct lpc17_list_s *iofree; + irqstate_t flags; + + /* Could be called from the interrupt level */ + + flags = irqsave(); + iofree = (struct lpc17_list_s *)buffer; + iofree->flink = g_iofree; + g_iofree = iofree; + irqrestore(flags); +} +#endif + +/******************************************************************************* + * Name: lpc17_allocasynch + * + * Description: + * Allocate an asynchronous data structure from the free list + * + * Assumptions: + * - Never called from an interrupt handler. + * - Protection from re-entrance must be assured by the caller + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static struct lpc17_asynch_s *lpc17_allocasynch(void) +{ + struct lpc17_asynch_s *ret; + irqstate_t flags; + + /* lpc17_freeasynch() may be called from the interrupt level */ + + flags = irqsave(); + ret = (struct lpc17_asynch_s *)g_asynchfree; + if (ret) + { + g_asynchfree = ((struct lpc17_list_s*)ret)->flink; + } + + irqrestore(flags); + return ret; +} +#endif + +/******************************************************************************* + * Name: lpc17_freeio + * + * Description: + * Return an TD buffer to the free list + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void lpc17_freeasynch(struct lpc17_asynch_s *asynch) +{ + struct lpc17_list_s *node; + irqstate_t flags; + + /* Could be called from the interrupt level */ + + flags = irqsave(); + node = (struct lpc17_list_s *)asynch; + node->flink = g_asynchfree; + g_asynchfree = node; + irqrestore(flags); } #endif @@ -1502,10 +1621,22 @@ static int lpc17_usbinterrupt(int irq, void *context) next = (struct lpc17_gtd_s *)td->hw.nexttd; lpc17_tdfree(td); - /* And wake up the thread waiting for the WDH event */ +#ifdef CONFIG_USBHOST_ASYNCH + /* Perform any pending callbacks for the case of asynchronous + * transfers. + */ + if (ed->asynch) + { + DEBUGASSERT(ed->wdhwait == false); + lpc17_asynch_completion(priv, ed); + } + else +#endif if (ed->wdhwait) { + /* Wake up the thread waiting for the WDH event */ + lpc17_givesem(&ed->wdhsem); ed->wdhwait = false; } @@ -2274,69 +2405,40 @@ static int lpc17_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, } /******************************************************************************* - * Name: lpc17_transfer and lcp17_async + * Name: lpc17_transfer_common * * 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. - * - * 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. + * Initiate a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately * * 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 + * priv - Internal driver state structure. + * ed - 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 ('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 - * returned indicating the nature of the failure: + * returned indicating the nature of the failure. * - * EAGAIN - If devices NAKs the transfer (or NYET or other error where - * it may be appropriate to restart the entire transaction). - * EPERM - If the endpoint stalls - * EIO - On a TX or data toggle error - * 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. * *******************************************************************************/ -static int lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, - uint8_t *buffer, size_t buflen) +static int lpc17_transfer_common(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed, uint8_t *buffer, + size_t buflen) { - struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; - struct lpc17_ed_s *ed = (struct lpc17_ed_s *)ep; uint32_t dirpid; uint32_t regval; -#if LPC17_IOBUFFERS > 0 - uint8_t *origbuf = NULL; -#endif bool in; int ret; - DEBUGASSERT(priv && ed && buffer && buflen > 0); - in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN; uvdbg("EP%d %s toggle:%d maxpacket:%d buflen:%d\n", (ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT, @@ -2345,72 +2447,15 @@ static int lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, (ed->hw.ctrl & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT, buflen); - /* 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. - */ - - lpc17_takesem(&priv->exclsem); - - /* Allocate an IO buffer if the user buffer does not lie in AHB SRAM */ - -#if LPC17_IOBUFFERS > 0 - if ((uintptr_t)buffer < LPC17_SRAM_BANK0 || - (uintptr_t)buffer >= (LPC17_SRAM_BANK0 + LPC17_BANK0_SIZE + LPC17_BANK1_SIZE)) - { - /* Will the transfer fit in an IO buffer? */ - - if (buflen > CONFIG_USBHOST_IOBUFSIZE) - { - uvdbg("buflen (%d) > IO buffer size (%d)\n", - buflen, CONFIG_USBHOST_IOBUFSIZE); - ret = -ENOMEM; - goto errout; - } - - /* Allocate an IO buffer in AHB SRAM */ - - origbuf = buffer; - buffer = lpc17_allocio(); - if (!buffer) - { - uvdbg("IO buffer allocation failed\n"); - ret = -ENOMEM; - goto errout; - } - - /* If this is an OUT transaction, copy the user data into the AHB - * SRAM IO buffer. Sad... so inefficient. But without exposing - * the AHB SRAM to the final, end-user client I don't know of any - * way around this copy. - */ - - if (!in) - { - memcpy(buffer, origbuf, buflen); - } - } -#endif - - /* Set the request for the Writeback Done Head event well BEFORE enabling the - * transfer. - */ - - ret = lpc17_wdhwait(priv, ed); - if (ret != OK) - { - udbg("ERROR: Device disconnected\n"); - goto errout; - } - /* Get the direction of the endpoint */ if (in) { - dirpid = GTD_STATUS_DP_IN; + dirpid = GTD_STATUS_DP_IN; } else { - dirpid = GTD_STATUS_DP_OUT; + dirpid = GTD_STATUS_DP_OUT; } /* Then enqueue the transfer */ @@ -2426,33 +2471,118 @@ static int lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, regval = lpc17_getreg(LPC17_USBHOST_CMDST); regval |= OHCI_CMDST_BLF; lpc17_putreg(regval, LPC17_USBHOST_CMDST); - - /* Wait for the Writeback Done Head interrupt */ - - lpc17_takesem(&ed->wdhsem); - - /* Check the TD completion status bits */ - - if (ed->tdstatus == TD_CC_NOERROR) - { - ret = OK; - } - else - { - uvdbg("Bad TD completion status: %d\n", ed->tdstatus); - ret = -EIO; - } } -errout: - /* Make sure that there is no outstanding request on this endpoint */ + return ret; +} - ed->wdhwait = false; - - /* Free any temporary IO buffers */ +/******************************************************************************* + * Name: lpc17_dma_alloc + * + * Description: + * Allocate DMA memory to perform a transfer, copying user data as necessary + * + * Input Parameters: + * priv - Internal driver state structure. + * ed - The IN or OUT endpoint descriptor for the device endpoint on which to + * perform the transfer. + * userbuffer - The user buffer containing the data to be sent (OUT endpoint) + * or received (IN endpoint). + * buflen - The length of the data to be sent or received. + * alloc - The location to return the allocated DMA buffer. + * + * 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. + * + *******************************************************************************/ #if LPC17_IOBUFFERS > 0 - if (buffer && origbuf) +static int lpc17_dma_alloc(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed, uint8_t *userbuffer, + size_t buflen, uint8_t **alloc) +{ + uint8_t *newbuffer; + + if ((uintptr_t)userbuffer < LPC17_SRAM_BANK0 || + (uintptr_t)userbuffer >= (LPC17_SRAM_BANK0 + LPC17_BANK0_SIZE + LPC17_BANK1_SIZE)) + { + /* Will the transfer fit in an IO buffer? */ + + if (buflen > CONFIG_USBHOST_IOBUFSIZE) + { + uvdbg("buflen (%d) > IO buffer size (%d)\n", + buflen, CONFIG_USBHOST_IOBUFSIZE); + return -ENOMEM; + } + + /* Allocate an IO buffer in AHB SRAM */ + + newbuffer = lpc17_allocio(); + if (!newbuffer) + { + uvdbg("IO buffer allocation failed\n"); + return -ENOMEM; + } + + /* If this is an OUT transaction, copy the user data into the AHB + * SRAM IO buffer. Sad... so inefficient. But without exposing + * the AHB SRAM to the final, end-user client I don't know of any + * way around this copy. + */ + + if ((ed->hw.ctrl & ED_CONTROL_D_MASK) != ED_CONTROL_D_IN) + { + memcpy(newbuffer, userbuffer, buflen); + } + + /* Return the allocated buffer */ + + *alloc = newbuffer; + } + + return OK; +} + +/******************************************************************************* + * Name: lpc17_dma_free + * + * Description: + * Free allocated DMA memory. + * + * Input Parameters: + * priv - Internal driver state structure. + * ed - The IN or OUT endpoint descriptor for the device endpoint on which to + * perform the transfer. + * userbuffer - The user buffer containing the data to be sent (OUT endpoint) + * or received (IN endpoint). + * buflen - The length of the data to be sent or received. + * alloc - The allocated DMA buffer to be freed. + * + * 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 void lpc17_dma_free(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed, uint8_t *userbuffer, + size_t buflen, uint8_t *newbuffer) +{ + irqstate_t flags; + + /* Could be called from the interrupt level */ + + flags = irqsave(); + if (userbuffer && newbuffer) { /* If this is an IN transaction, get the user data from the AHB * SRAM IO buffer. Sad... so inefficient. But without exposing @@ -2460,28 +2590,358 @@ errout: * way around this copy. */ - if (in && ret == OK) + if ((ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN) { - memcpy(origbuf, buffer, buflen); + memcpy(userbuffer, newbuffer, buflen); } /* Then free the temporary I/O buffer */ - lpc17_freeio(buffer); + lpc17_freeio(newbuffer); + } + + irqrestore(flags); +} +#endif + +/******************************************************************************* + * Name: lpc17_transfer + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * 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. + * + * 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. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep, + uint8_t *buffer, size_t buflen) +{ + struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; + struct lpc17_ed_s *ed = (struct lpc17_ed_s *)ep; +#if LPC17_IOBUFFERS > 0 + uint8_t *alloc = NULL; + uint8_t *userbuffer = NULL; +#endif + int ret; + + DEBUGASSERT(priv && ed && buffer && buflen > 0); +#ifdef CONFIG_USBHOST_ASYNCH + DEBUGASSERT(ed->wdhwait == false && ed->asynch == NULL); +#else + DEBUGASSERT(ed->wdhwait == false); +#endif + + /* 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. + */ + + lpc17_takesem(&priv->exclsem); + +#if LPC17_IOBUFFERS > 0 + /* Allocate an IO buffer if the user buffer does not lie in AHB SRAM */ + + ret = lpc17_dma_alloc(priv, ed, buffer, buflen, &alloc); + if (ret < 0) + { + udbg("ERROR: lpc17_dma_alloc failed: %d\n", ret); + goto errout_with_sem; + } + + /* If a buffer was allocated, then use it instead of the callers buffer */ + + if (alloc) + { + userbuffer = buffer; + buffer = alloc; } #endif + /* Set the request for the Writeback Done Head event well BEFORE enabling the + * transfer. + */ + + ret = lpc17_wdhwait(priv, ed); + if (ret != OK) + { + udbg("ERROR: Device disconnected\n"); + goto errout_with_buffers; + } + +#ifdef CONFIG_USBHOST_ASYNCH + ed->asynch = NULL; +#endif + + /* Set up the transfer */ + + ret = lpc17_transfer_common(priv, ed, buffer, buflen); + if (ret < 0) + { + udbg("ERROR: lpc17_transfer_common failed: %d\n", ret); + goto errout_with_wdhwait; + } + + /* Wait for the Writeback Done Head interrupt */ + + lpc17_takesem(&ed->wdhsem); + + /* Check the TD completion status bits */ + + if (ed->tdstatus == TD_CC_NOERROR) + { + ret = OK; + } + else + { + udbg("ERROR: Bad TD completion status: %d\n", ed->tdstatus); + ret = -EIO; + } + +errout_with_wdhwait: + /* Make sure that there is no outstanding request on this endpoint */ + + ed->wdhwait = false; + +errout_with_buffers: +#if LPC17_IOBUFFERS > 0 + /* Free any temporary IO buffers */ + + lpc17_dma_free(priv, ed, userbuffer, buflen, alloc); +#endif + +errout_with_sem: lpc17_givesem(&priv->exclsem); return ret; } +/******************************************************************************* + * Name: lpc17_asynch_completion + * + * Description: + * This function is called at the interrupt level when an asynchronous + * transfer completes. It performs the pending callback. + * + * Input Parameters: + * priv - Internal driver state structure. + * ep - 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 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 void lpc17_asynch_completion(struct lpc17_usbhost_s *priv, + struct lpc17_ed_s *ed) { -# error Not implemented - return -ENOSYS; + struct lpc17_asynch_s *asynch; + usbhost_asynch_t callback; + void *arg; + int result; + + DEBUGASSERT(ed != NULL && ed->wdhwait == false && ed->asynch != NULL); + asynch = ed->asynch; + + DEBUGASSERT(asynch->callback != NULL && asynch->user != NULL && asynch->buflen > 0); + + /* Check the TD completion status bits */ + + if (ed->tdstatus == TD_CC_NOERROR) + { + result = OK; + } + else + { + udbg("ERROR: Bad TD completion status: %d\n", ed->tdstatus); + result = -EIO; + } + +#if LPC17_IOBUFFERS > 0 + /* Free any temporary IO buffers */ + + lpc17_dma_free(priv, ed, asynch->user, asynch->buflen, asynch->alloc); +#endif + + /* Extract the callback information before freeing the buffer */ + + callback = asynch->callback; + arg = asynch->arg; + + /* Clear any pending transfer indicators */ + + ed->wdhwait = false; + ed->asynch = NULL; + + /* Make sure that there is no outstanding request on this endpoint */ + +#ifdef CONFIG_DEBUG + memset(asynch, 0, sizeof(struct lpc17_asynch_s)); +#endif + lpc17_freeasynch(asynch); + + /* Then perform the callback */ + + callback(arg, result); +} +#endif + +/******************************************************************************* + * Name: lcp17_async + * + * 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 lpc17_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep, + uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, void *arg) +{ + struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; + struct lpc17_ed_s *ed = (struct lpc17_ed_s *)ep; + struct lpc17_asynch_s *asynch; + int ret; + + DEBUGASSERT(priv && ed && buffer && buflen > 0 && callback); + DEBUGASSERT(ed->wdhwait == false && ed->asynch == NULL); + + /* 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. + */ + + lpc17_takesem(&priv->exclsem); + + /* Allocate a structure to retain the information needed when the asynchronous + * transfer completes. + */ + + asynch = lpc17_allocasynch(); + if (asynch == NULL) + { + udbg("ERROR: lpc17_allocasynch failed\n"); + ret = -ENOMEM; + goto errout_with_sem; + } + + /* Initialize the asynch structure */ + + asynch->buflen = buflen; + asynch->user = buffer; + asynch->callback = callback; + asynch->arg = arg; + +#if LPC17_IOBUFFERS > 0 + /* Allocate an IO buffer if the user buffer does not lie in AHB SRAM */ + + ret = lpc17_dma_alloc(priv, ed, buffer, buflen, &asynch->alloc); + if (ret < 0) + { + udbg("ERROR: lpc17_dma_alloc failed: %d\n", ret); + goto errout_with_sem; + } + + /* If a buffer was allocated, then use it instead of the callers buffer */ + + if (asynch->alloc) + { + buffer = asynch->alloc; + } +#endif + + /* Set the callback information BEFORE enabling the transfer. */ + + ed->wdhwait = false; + ed->asynch = asynch; + + /* Set up the transfer */ + + ret = lpc17_transfer_common(priv, ed, buffer, buflen); + if (ret < 0) + { + udbg("ERROR: lpc17_transfer_common failed: %d\n", ret); + goto errout_with_asynch; + } + + /* And return now. The callback will be invoked when the transfer + * completes. + */ + + lpc17_givesem(&priv->exclsem); + return OK; + +errout_with_asynch: +#if LPC17_IOBUFFERS > 0 + /* Free any temporary IO buffers */ + + lpc17_dma_free(priv, ed, buffer, buflen, asynch->alloc); +#endif + + /* Free the asynchronous structure */ + + lpc17_freeasynch(asynch); + +errout_with_sem: + lpc17_givesem(&priv->exclsem); + return ret; } #endif @@ -2654,6 +3114,9 @@ struct usbhost_connection_s *lpc17_usbhost_initialize(int controller) struct lpc17_usbhost_s *priv = &g_usbhost; struct usbhost_driver_s *drvr; struct usbhost_hubport_s *hport; +#ifdef CONFIG_USBHOST_ASYNCH + struct lpc17_asynch_s *asynch; +#endif uint32_t regval; uint8_t *buffer; irqstate_t flags; @@ -2836,6 +3299,19 @@ struct usbhost_connection_s *lpc17_usbhost_initialize(int controller) } #endif +#ifdef CONFIG_USBHOST_ASYNCH + /* Initialize asynchronous transfer structures */ + + for (i = 0, asynch = (struct lpc17_asynch_s *)g_asynchbuffers; + i < CONFIG_LPC17_USBHOST_NASYNCH; + i++, asynch++) + { + /* Put the asynch structure in a free list */ + + lpc17_freeasynch(asynch); + } +#endif + /* Wait 50MS then perform hardware reset */ up_mdelay(50);