This appears to fix the NAK-issues for IN data transfers. Still an issue with OUT
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5063 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
4fde74ccc7
commit
6bda7a303d
@ -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);
|
||||
@ -1354,6 +1355,222 @@ static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv,
|
||||
return stm32_chan_wait(priv, chan);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
*
|
||||
@ -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)
|
||||
*/
|
||||
/* Handle IN and OUT transfer slightly differently */
|
||||
|
||||
chan->buffer = buffer;
|
||||
chan->buflen = buflen;
|
||||
|
||||
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user