diff options
Diffstat (limited to 'nuttx/arch')
-rw-r--r-- | nuttx/arch/arm/src/stm32/stm32_otgfshost.c | 387 |
1 files changed, 246 insertions, 141 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_otgfshost.c b/nuttx/arch/arm/src/stm32/stm32_otgfshost.c index ce0e9036e..d888c52f1 100644 --- a/nuttx/arch/arm/src/stm32/stm32_otgfshost.c +++ b/nuttx/arch/arm/src/stm32/stm32_otgfshost.c @@ -43,15 +43,12 @@ #include <stdint.h> #include <stdbool.h> #include <stdlib.h> +#include <unistd.h> #include <semaphore.h> #include <string.h> #include <errno.h> #include <debug.h> -#if !defined(CONFIG_DEBUG_VERBOSE) && !defined(CONFIG_DEBUG_USB) -# include <debug.h> -#endif - #include <nuttx/arch.h> #include <nuttx/kmalloc.h> #include <nuttx/usb/usb.h> @@ -315,6 +312,10 @@ static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, FAR uint8_t *buffer, unsigned int buflen); static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, FAR uint8_t *buffer, unsigned int buflen); +static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); +static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); /* Interrupt handling **********************************************************/ /* Lower level interrupt handlers */ @@ -751,7 +752,7 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) stm32_putreg(STM32_OTGFS_HCINTMSK(chidx), regval); /* Enable the top level host channel interrupt. */ - + stm32_modifyreg(STM32_OTGFS_HAINTMSK, 0, OTGFS_HAINT(chidx)); /* Make sure host channel interrupts are enabled. */ @@ -1157,7 +1158,7 @@ static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx) unsigned int wrpackets = avail / chan->maxpacket; wrsize = wrpackets * chan->maxpacket; } - + /* Write packet into the Tx FIFO. */ stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); @@ -1355,6 +1356,222 @@ static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, } /******************************************************************************* + * Name: stm32_in_transfer + * + * Description: + * Transfer 'buflen' bytes into 'buffer' from an IN channel. + * + *******************************************************************************/ + +static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct stm32_chan_s *chan; + uint16_t start; + uint16_t elapsed; + int ret = OK; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + start = stm32_getframe(); + while (chan->buflen > 0) + { + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + udbg("ERROR: Device disconnected\n"); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + switch (chan->eptype) + { + default: + case OTGFS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGFS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the IN data PID */ + + chan->pid = OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_BULK: /* Bulk */ + case OTGFS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the IN data PID */ + + chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + + /* Wait for the transfer to complete and get the result */ + + ret = stm32_chan_wait(priv, chan); + + /* EAGAIN indicates that the device NAKed the transfer and we need + * do try again. Anything else (success or other errors) will + * cause use to return + */ + + if (ret != OK) + { + udbg("Transfer failed: %d\n", ret); + + /* Check for a special case: If (1) the transfer was NAKed and (2) + * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we + * should be able to just flush the Rx and Tx FIFOs and try again. + * We can detect this latter case becasue the then the transfer + * buffer pointer and buffer size will be unaltered. + */ + + elapsed = stm32_getframe() - start; + if (ret != -EAGAIN || /* Not a NAK condition OR */ + elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */ + chan->buflen != buflen) /* Data has been partially transferred */ + { + /* Break out and return the error */ + + break; + } + } + } + + return ret; +} + +/******************************************************************************* + * Name: stm32_out_transfer + * + * Description: + * Transfer the 'buflen' bytes in 'buffer' through an OUT channel. + * + *******************************************************************************/ + +static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + while (chan->buflen > 0) + { + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + udbg("ERROR: Device disconnected\n"); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + switch (chan->eptype) + { + default: + case OTGFS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGFS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the OUT data PID */ + + chan->pid = OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the OUT data PID */ + + chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the OUT data PID */ + + chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + + /* Toggle the OUT data PID for the next transfer */ + + chan->outdata1 ^= true; + } + } + + /* There is a bug in the code at present. With debug OFF, this driver + * overruns the typical FLASH device and there are many problems with + * NAKS sticking a big delay here allows the driver to work but with + * very poor performance when debug is off. + */ + +#if !defined(CONFIG_DEBUG_VERBOSE) && !defined(CONFIG_DEBUG_USB) +#warning "REVISIT this delay" + usleep(100*1000); +#endif + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + + /* Wait for the transfer to complete and get the result */ + + ret = stm32_chan_wait(priv, chan); + + /* EAGAIN indicates that the device NAKed the transfer and we need + * do try again. Anything else (success or other errors) will + * cause use to return + */ + + if (ret != OK) + { + udbg("Transfer failed: %d\n", ret); + return ret; + } + } + + return OK; +} + +/******************************************************************************* * Name: stm32_gint_wrpacket * * Description: @@ -1923,7 +2140,7 @@ static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv) if (!priv->connected) { /* Yes.. then we no longer connected */ - + ullvdbg("Disconnected\n"); /* Are we bound to a class driver? */ @@ -2148,7 +2365,7 @@ static inline void stm32_gint_nptxfeisr(FAR struct stm32_usbhost_s *priv) unsigned int wrpackets = avail / chan->maxpacket; wrsize = wrpackets * chan->maxpacket; } - + /* Otherwise, this will be the last packet to be sent in this transaction. * We now need to disable further NPTXFE interrupts. */ @@ -2238,7 +2455,7 @@ static inline void stm32_gint_ptxfeisr(FAR struct stm32_usbhost_s *priv) unsigned int wrpackets = avail / chan->maxpacket; wrsize = wrpackets * chan->maxpacket; } - + /* Otherwise, this will be the last packet to be sent in this transaction. * We now need to disable further PTXFE interrupts. */ @@ -2382,7 +2599,7 @@ static inline void stm32_gint_hprtisr(FAR struct stm32_usbhost_s *priv) /* Set the Host Frame Interval Register for the 6KHz speed */ stm32_putreg(STM32_OTGFS_HFIR, 6000); - + /* Are we switching from FS to LS? */ if ((hcfg & OTGFS_HCFG_FSLSPCS_MASK) != OTGFS_HCFG_FSLSPCS_LS6MHz) @@ -2459,7 +2676,7 @@ static inline void stm32_gint_iisooxfrisr(FAR struct stm32_usbhost_s *priv) /* CHENA : Set to enable the channel * CHDIS : Set to stop transmitting/receiving data on a channel */ - + regval = stm32_getreg(STM32_OTGFS_HCCHAR(0)); regval |= (OTGFS_HCCHAR_CHDIS | OTGFS_HCCHAR_CHENA); stm32_putreg(STM32_OTGFS_HCCHAR(0), regval); @@ -2718,7 +2935,7 @@ static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx) /* Disable all interrupts so that we have exclusive access to the GINTMSK * (it would be sufficent just to disable the GINT interrupt). */ - + flags = irqsave(); /* Should we enable the periodic or non-peridic Tx FIFO empty interrupts */ @@ -2842,7 +3059,7 @@ static int stm32_enumerate(FAR struct usbhost_driver_s *drvr) } DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); - + /* Allocate and initialize the control OUT channel */ chidx = stm32_chan_alloc(priv); @@ -2956,12 +3173,12 @@ static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcadd priv->chan[priv->ep0out].maxpacket = maxpacketsize; stm32_chan_configure(priv, priv->ep0out); - + /* Configure the EP0 IN channel */ priv->chan[priv->ep0in].maxpacket = maxpacketsize; stm32_chan_configure(priv, priv->ep0in); - + stm32_givesem(&priv->exclsem); return OK; } @@ -3427,7 +3644,7 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, } /* Handle the status IN phase */ - + if (ret == OK) { ret = stm32_ctrl_recvdata(priv, NULL, 0); @@ -3492,145 +3709,33 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, * - Never called from an interrupt handler. * *******************************************************************************/ - + static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen) { - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - FAR struct stm32_chan_s *chan; + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; unsigned int chidx = (unsigned int)ep; - int ret = OK; + int ret; uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); DEBUGASSERT(priv && buffer && chidx < STM32_MAX_TX_FIFOS && buflen > 0); - chan = &priv->chan[chidx]; /* We must have exclusive access to the USB host hardware and state structures */ stm32_takesem(&priv->exclsem); - /* Loop until the transfer completes (i.e., buflen is decremented to zero) - * or a fatal error occurs (any error other than a simple NAK) - */ - - chan->buffer = buffer; - chan->buflen = buflen; + /* Handle IN and OUT transfer slightly differently */ - while (chan->buflen > 0) + if (priv->chan[chidx].in) { - /* Set up for the wait BEFORE starting the transfer */ - - ret = stm32_chan_waitsetup(priv, chan); - if (ret != OK) - { - udbg("ERROR: Device disconnected\n"); - goto errout; - } - - /* Set up for the transfer based on the direction and the endpoint type */ - - switch (chan->eptype) - { - default: - case OTGFS_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - ret = -ENOSYS; - goto errout; - } - - case OTGFS_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the IN/OUT data PID */ - - chan->pid = OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_BULK: /* Bulk */ - { - /* Handle the bulk transfer based on the direction of the transfer. */ - - if (chan->in) - { - /* Setup the IN data PID */ - - chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - } - else - { - /* Setup the OUT data PID */ - - chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - } - } - break; - - case OTGFS_EPTYPE_INTR: /* Interrupt */ - { - /* Handle the interrupt transfer based on the direction of the - * transfer. - */ - - if (chan->in) - { - /* Setup the IN data PID */ - - chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - - /* The indata1 data toggle will be updated in the Rx FIFO - * interrupt handling logic as each packet is received. - */ - } - else - { - /* Setup the OUT data PID */ - - chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - - /* Toggle the OUT data PID for the next transfer */ - - chan->outdata1 ^= true; - } - } - } - - /* There is a bug in the code at present. With debug OFF, this driver - * overruns the typical FLASH device and there are many problems with - * NAKS sticking a big delay here allows the driver to work but with - * very poor performance when debug is off. - */ - -#if !defined(CONFIG_DEBUG_VERBOSE) && !defined(CONFIG_DEBUG_USB) -#warning "REVISIT this delay" - usleep(100*1000); -#endif - - /* Start the transfer */ - - stm32_transfer_start(priv, chidx); - - /* Wait for the transfer to complete and get the result */ - - ret = stm32_chan_wait(priv, chan); - - /* EAGAIN indicates that the device NAKed the transfer and we need - * do try again. Anything else (success or other errors) will - * cause use to return - */ - - if (ret != OK) - { - udbg("Transfer failed: %d\n", ret); - break; - } + ret = stm32_in_transfer(priv, chidx, buffer, buflen); + } + else + { + ret = stm32_out_transfer(priv, chidx, buffer, buflen); } -errout: stm32_givesem(&priv->exclsem); return ret; } @@ -3663,7 +3768,7 @@ static void stm32_disconnect(FAR struct usbhost_driver_s *drvr) struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; priv->class = NULL; } - + /******************************************************************************* * Initialization *******************************************************************************/ @@ -3979,7 +4084,7 @@ static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv) /* Set the PHYSEL bit in the GUSBCFG register to select the OTG FS serial * transceiver: "This bit is always 1 with write-only access" */ - + regval = stm32_getreg(STM32_OTGFS_GUSBCFG);; regval |= OTGFS_GUSBCFG_PHYSEL; stm32_putreg(STM32_OTGFS_GUSBCFG, regval); @@ -4128,7 +4233,7 @@ FAR struct usbhost_driver_s *usbhost_initialize(int controller) stm32_configgpio(GPIO_OTGFS_SOF); #endif - /* Initialize the USB OTG FS core */ + /* Initialize the USB OTG FS core */ stm32_hw_initialize(priv); |