summaryrefslogtreecommitdiffstats
path: root/nuttx/arch
diff options
context:
space:
mode:
authorpatacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679>2012-08-22 21:04:17 +0000
committerpatacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679>2012-08-22 21:04:17 +0000
commit19f4245012ab8252670362107070a1ad462af698 (patch)
tree67ac8c94adeafb0e750455d9080004566440dbe2 /nuttx/arch
parent2086a304c2ccdb7b6df43e83bbafda11a3bf1177 (diff)
STM32 USB OTG FS host driver -- updates for NAK and data toggle fixes
git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@5047 7fd9a85b-ad96-42d3-883c-3090e2eb8679
Diffstat (limited to 'nuttx/arch')
-rw-r--r--nuttx/arch/arm/src/stm32/stm32_otgfshost.c269
1 files changed, 134 insertions, 135 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_otgfshost.c b/nuttx/arch/arm/src/stm32/stm32_otgfshost.c
index 9644d17e8c..256751d125 100644
--- a/nuttx/arch/arm/src/stm32/stm32_otgfshost.c
+++ b/nuttx/arch/arm/src/stm32/stm32_otgfshost.c
@@ -187,7 +187,7 @@ enum stm32_chreason_e
CHREASON_NYET, /* NotYet received */
CHREASON_STALL, /* Endpoint stalled */
CHREASON_TXERR, /* Transfer error received */
- CHREASON_DTERR, /* Data error received */
+ CHREASON_DTERR, /* Data toggle error received */
CHREASON_FRMOR /* Frame overrun */
};
@@ -202,14 +202,13 @@ struct stm32_chan_s
uint8_t eptype; /* See OTGFS_EPTYPE_* definitions */
uint8_t pid; /* Data PID */
bool inuse; /* True: This channel is "in use" */
- bool indata1; /* IN data toggle. True: DATA01 */
- bool outdata1; /* OUT data toggle. True: DATA01 */
+ volatile bool indata1; /* IN data toggle. True: DATA01 (Bulk and INTR only) */
+ volatile bool outdata1; /* OUT data toggle. True: DATA01 */
bool in; /* True: IN endpoint */
volatile bool waiter; /* True: Thread is waiting for a channel event */
- volatile uint16_t xfrd; /* Number of bytes transferred */
uint16_t maxpacket; /* Max packet size */
- uint16_t buflen; /* Buffer length (remaining) */
- uint16_t xfrlen; /* Number of bytes transferred */
+ volatile uint16_t buflen; /* Buffer length (remaining) */
+ volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */
FAR uint8_t *buffer; /* Transfer buffer pointer */
};
@@ -230,7 +229,7 @@ struct stm32_usbhost_s
/* Overall driver status */
- uint8_t smstate; /* The state of the USB host state machine */
+ volatile uint8_t smstate; /* The state of the USB host state machine */
uint8_t devaddr; /* Device address */
uint8_t ep0in; /* EP0 IN control channel index */
uint8_t ep0out; /* EP0 OUT control channel index */
@@ -998,8 +997,8 @@ static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx)
/* Set up the initial state of the transfer */
- priv->chan[chidx].result = EBUSY;
- priv->chan[chidx].xfrlen = 0;
+ priv->chan[chidx].result = EBUSY;
+ priv->chan[chidx].inflight = 0;
/* Compute the expected number of packets associated to the transfer.
* If the transfer length is zero (or less than the size of one maximum
@@ -1059,7 +1058,10 @@ static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx)
regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx));
- /* Check for an even frame */
+ /* Set/clear the Odd Frame bit. Check for an even frame; if so set Odd
+ * Frame. This field is applicable for only periodic (isochronous and
+ * interrupt) channels.
+ */
if ((stm32_getreg(STM32_OTGFS_HFNUM) & 1) == 0)
{
@@ -1344,6 +1346,10 @@ static void stm32_gint_wrpacket(FAR struct stm32_usbhost_s *priv,
uint32_t data = *src++;
stm32_putreg(fifo, data);
}
+
+ /* Increment the count of bytes "in-flight" in the Tx FIFO */
+
+ priv->chan[chidx].inflight += buflen;
}
/*******************************************************************************
@@ -1371,7 +1377,6 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
FAR struct stm32_chan_s *chan = &priv->chan[chidx];
uint32_t regval;
uint32_t pending;
- unsigned int eptype;
/* Read the HCINT register to get the pending HC interrupts. Read the
* HCINTMSK register to get the set of enabled HC interrupts.
@@ -1451,31 +1456,22 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_XFRC);
- /* Fetch the HCCHAR register and extract the endpoint type. Those
- * values are used in several cases.
- */
-
- regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx));
- eptype = regval & OTGFS_HCCHAR_EPTYP_MASK;
-
/* Then handle the transfer completion event based on the endpoint type */
- if ((eptype == OTGFS_HCCHAR_EPTYP_CTRL) ||
- (eptype == OTGFS_HCCHAR_EPTYP_BULK))
+ if (chan->eptype == OTGFS_EPTYPE_CTRL || chan->eptype == OTGFS_EPTYPE_BULK)
{
/* Halt the channel -- the CHH interrrupt is expected next */
stm32_chan_halt(priv, chidx, CHREASON_XFRC);
- /* Clear any pending NAK condition */
+ /* Clear any pending NAK condition. The 'indata1' data toggle
+ * should have been appropriately updated by the the RxFIFO
+ * logic as each packet was received.
+ */
stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_NAK);
-
- /* Toggle the IN data state */
-
- chan->indata1 ^= true;
}
- else if (eptype == OTGFS_HCCHAR_EPTYP_INTR)
+ else if (chan->eptype == OTGFS_EPTYPE_INTR)
{
/* Force the next transfer on an ODD frame */
@@ -1527,7 +1523,7 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx));
if ((regval & OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_INTR)
{
- /* Toggle the IN data toggle */
+ /* Toggle the IN data toggle (Used by Bulk and INTR only) */
chan->indata1 ^= true;
}
@@ -1561,44 +1557,22 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
else if ((pending & OTGFS_HCINT_NAK) != 0)
{
- /* Handle the NAK based on the endpoint type
- *
- * Fetch the HCCHAR register and extract the endpoint type.
- */
+ /* Re-activate CTRL and BULK channels */
- regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx));
- eptype = regval & OTGFS_HCCHAR_EPTYP_MASK;
-
- /* Then process the NAK based on the endpoint type */
-
- if (eptype == OTGFS_HCCHAR_EPTYP_INTR)
+ if (chan->eptype == OTGFS_EPTYPE_CTRL || chan->eptype == OTGFS_EPTYPE_BULK)
{
- /* Halt the INTR channel -- the CHH interrrupt is expected next */
+ /* Re-activate the channel by clearing CHDIS and assuring that
+ * CHENA is set
+ */
- stm32_chan_halt(priv, chidx, CHREASON_NAK);
+ regval |= OTGFS_HCCHAR_CHENA;
+ regval &= ~OTGFS_HCCHAR_CHDIS;
+ stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval);
}
- else
- {
- /* Re-activate CGRL and BULK channels */
-
- if ((eptype == OTGFS_HCCHAR_EPTYP_CTRL) ||
- (eptype == OTGFS_HCCHAR_EPTYP_BULK))
- {
- /* Re-activate the channel by clearing CHDIS and assuring that
- * CHENA is set
- */
- regval |= OTGFS_HCCHAR_CHENA;
- regval &= ~OTGFS_HCCHAR_CHDIS;
- stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval);
- }
-
- /* Set the result to EAGAIN to wake up any thread wait for the
- * transfer to complete.
- */
+ /* Halt the channel -- the CHH interrrupt is expected next */
- chan->result = EAGAIN;
- }
+ stm32_chan_halt(priv, chidx, CHREASON_NAK);
/* Clear the NAK condition */
@@ -1674,6 +1648,14 @@ static inline void stm32_gint_hcoutisr(FAR struct stm32_usbhost_s *priv,
else if ((pending & OTGFS_HCINT_XFRC) != 0)
{
+ /* Decrement the number of bytes remaining by the number of
+ * bytes that were "in-flight".
+ */
+
+ priv->chan[chidx].buffer += priv->chan[chidx].inflight;
+ priv->chan[chidx].buflen -= priv->chan[chidx].inflight;
+ priv->chan[chidx].inflight = 0;
+
/* Halt the channel -- the CHH interrrupt is expected next */
stm32_chan_halt(priv, chidx, CHREASON_XFRC);
@@ -1986,11 +1968,14 @@ static inline void stm32_gint_rxflvlisr(FAR struct stm32_usbhost_s *priv)
stm32_pktdump("Received", priv->chan[chidx].buffer, bcnt);
+ /* Toggle the IN data pid (Used by Bulk and INTR only) */
+
+ priv->chan[chidx].indata1 ^= true;
+
/* Manage multiple packet transfers */
priv->chan[chidx].buffer += bcnt;
- priv->chan[chidx].xfrlen += bcnt;
- priv->chan[chidx].xfrd = priv->chan[chidx].xfrlen;
+ priv->chan[chidx].buflen -= bcnt;
/* Check if more packets are expected */
@@ -2090,10 +2075,6 @@ static inline void stm32_gint_nptxfeisr(FAR struct stm32_usbhost_s *priv)
}
stm32_gint_wrpacket(priv, priv->chan[chidx].buffer, chidx, wrsize);
-
- priv->chan[chidx].buffer += wrsize;
- priv->chan[chidx].buflen -= wrsize;
- priv->chan[chidx].xfrlen += wrsize;
}
}
@@ -2167,10 +2148,6 @@ static inline void stm32_gint_ptxfeisr(FAR struct stm32_usbhost_s *priv)
}
stm32_gint_wrpacket(priv, priv->chan[chidx].buffer, chidx, wrsize);
-
- priv->chan[chidx].buffer += wrsize;
- priv->chan[chidx].buflen -= wrsize;
- priv->chan[chidx].xfrlen += wrsize;
}
}
@@ -3316,7 +3293,7 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
unsigned int chidx = (unsigned int)ep;
int ret;
- uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen);
+ uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen);
DEBUGASSERT(priv && buffer && chidx < STM32_MAX_TX_FIFOS && buflen > 0);
chan = &priv->chan[chidx];
@@ -3325,99 +3302,121 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
stm32_takesem(&priv->exclsem);
- /* 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;
- }
+ /* Loop until the transfer completes (i.e., buflen is decremented to zero)
+ * or a fatal error occurs (any error other than a simple NAK)
+ */
- /* Set up for the transfer based on the direction and the endpoint type */
+ chan->buffer = buffer;
+ chan->buflen = buflen;
- switch (chan->eptype)
+ while (chan->buflen > 0)
{
- 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 */
+ /* Set up for the wait BEFORE starting the transfer */
- chan->pid = OTGFS_PID_DATA0;
- }
- break;
+ ret = stm32_chan_waitsetup(priv, chan);
+ if (ret != OK)
+ {
+ udbg("ERROR: Device disconnected\n");
+ goto errout;
+ }
- case OTGFS_EPTYPE_BULK: /* Bulk */
- {
- /* Handle the bulk transfer based on the direction of the transfer. */
+ /* Set up for the transfer based on the direction and the endpoint type */
- if (chan->in)
+ switch (chan->eptype)
+ {
+ default:
+ case OTGFS_EPTYPE_CTRL: /* Control */
{
- /* Setup the IN data PID */
+ /* This kind of transfer on control endpoints other than EP0 are not
+ * currently supported
+ */
- chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
+ ret = -ENOSYS;
+ goto errout;
}
- else
+
+ case OTGFS_EPTYPE_ISOC: /* Isochronous */
{
- /* Setup the OUT data PID */
+ /* Set up the IN/OUT data PID */
- chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
+ chan->pid = OTGFS_PID_DATA0;
}
- }
- break;
-
- case OTGFS_EPTYPE_INTR: /* Interrupt */
- {
- /* Handle the interrupt transfer based on the direction of the
- * transfer.
- */
+ break;
- if (chan->in)
+ case OTGFS_EPTYPE_BULK: /* Bulk */
{
- /* Setup the IN data PID */
+ /* Handle the bulk transfer based on the direction of the transfer. */
- chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
+ if (chan->in)
+ {
+ /* Setup the IN data PID */
- /* Toggle the IN data PID for the next transfer */
+ chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
+ }
+ else
+ {
+ /* Setup the OUT data PID */
- chan->indata1 ^= true;
+ chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
+ }
}
- else
+ break;
+
+ case OTGFS_EPTYPE_INTR: /* Interrupt */
{
- /* Setup the OUT data PID */
+ /* Handle the interrupt transfer based on the direction of the
+ * transfer.
+ */
- chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
+ 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 */
- /* Toggle the OUT data PID for the next transfer */
+ chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
- chan->outdata1 ^= true;
+ /* Toggle the OUT data PID for the next transfer */
+
+ chan->outdata1 ^= true;
+ }
}
- }
- }
+ }
- /* Start the transfer */
+ /* Start the transfer */
- chan->buffer = buffer;
- chan->buflen = buflen;
+ stm32_transfer_start(priv, chidx);
- stm32_transfer_start(priv, chidx);
+ /* Wait for the transfer to complete and get the result */
- /* Wait for the transfer to complete and get the result */
+ ret = stm32_chan_wait(priv, chan);
- ret = stm32_chan_wait(priv, chan);
- if (ret < 0)
- {
- udbg("Transfer failed: %d\n", ret);
+ /* EGAIN 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 != -EAGAIN)
+ {
+ /* Output some debug info on an error */
+
+ if (ret < 0)
+ {
+ udbg("Transfer failed: %d\n", ret);
+ }
+
+ /* Break out and return this result */
+
+ break;
+ }
}
errout: