mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-28 06:23:33 +08:00
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
This commit is contained in:
@@ -187,7 +187,7 @@ enum stm32_chreason_e
|
|||||||
CHREASON_NYET, /* NotYet received */
|
CHREASON_NYET, /* NotYet received */
|
||||||
CHREASON_STALL, /* Endpoint stalled */
|
CHREASON_STALL, /* Endpoint stalled */
|
||||||
CHREASON_TXERR, /* Transfer error received */
|
CHREASON_TXERR, /* Transfer error received */
|
||||||
CHREASON_DTERR, /* Data error received */
|
CHREASON_DTERR, /* Data toggle error received */
|
||||||
CHREASON_FRMOR /* Frame overrun */
|
CHREASON_FRMOR /* Frame overrun */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -202,14 +202,13 @@ struct stm32_chan_s
|
|||||||
uint8_t eptype; /* See OTGFS_EPTYPE_* definitions */
|
uint8_t eptype; /* See OTGFS_EPTYPE_* definitions */
|
||||||
uint8_t pid; /* Data PID */
|
uint8_t pid; /* Data PID */
|
||||||
bool inuse; /* True: This channel is "in use" */
|
bool inuse; /* True: This channel is "in use" */
|
||||||
bool indata1; /* IN data toggle. True: DATA01 */
|
volatile bool indata1; /* IN data toggle. True: DATA01 (Bulk and INTR only) */
|
||||||
bool outdata1; /* OUT data toggle. True: DATA01 */
|
volatile bool outdata1; /* OUT data toggle. True: DATA01 */
|
||||||
bool in; /* True: IN endpoint */
|
bool in; /* True: IN endpoint */
|
||||||
volatile bool waiter; /* True: Thread is waiting for a channel event */
|
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 maxpacket; /* Max packet size */
|
||||||
uint16_t buflen; /* Buffer length (remaining) */
|
volatile uint16_t buflen; /* Buffer length (remaining) */
|
||||||
uint16_t xfrlen; /* Number of bytes transferred */
|
volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */
|
||||||
FAR uint8_t *buffer; /* Transfer buffer pointer */
|
FAR uint8_t *buffer; /* Transfer buffer pointer */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -230,7 +229,7 @@ struct stm32_usbhost_s
|
|||||||
|
|
||||||
/* Overall driver status */
|
/* 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 devaddr; /* Device address */
|
||||||
uint8_t ep0in; /* EP0 IN control channel index */
|
uint8_t ep0in; /* EP0 IN control channel index */
|
||||||
uint8_t ep0out; /* EP0 OUT control channel index */
|
uint8_t ep0out; /* EP0 OUT control channel index */
|
||||||
@@ -999,7 +998,7 @@ static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx)
|
|||||||
/* Set up the initial state of the transfer */
|
/* Set up the initial state of the transfer */
|
||||||
|
|
||||||
priv->chan[chidx].result = EBUSY;
|
priv->chan[chidx].result = EBUSY;
|
||||||
priv->chan[chidx].xfrlen = 0;
|
priv->chan[chidx].inflight = 0;
|
||||||
|
|
||||||
/* Compute the expected number of packets associated to the transfer.
|
/* Compute the expected number of packets associated to the transfer.
|
||||||
* If the transfer length is zero (or less than the size of one maximum
|
* 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));
|
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)
|
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++;
|
uint32_t data = *src++;
|
||||||
stm32_putreg(fifo, data);
|
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];
|
FAR struct stm32_chan_s *chan = &priv->chan[chidx];
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
uint32_t pending;
|
uint32_t pending;
|
||||||
unsigned int eptype;
|
|
||||||
|
|
||||||
/* Read the HCINT register to get the pending HC interrupts. Read the
|
/* Read the HCINT register to get the pending HC interrupts. Read the
|
||||||
* HCINTMSK register to get the set of enabled HC interrupts.
|
* 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);
|
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 */
|
/* Then handle the transfer completion event based on the endpoint type */
|
||||||
|
|
||||||
if ((eptype == OTGFS_HCCHAR_EPTYP_CTRL) ||
|
if (chan->eptype == OTGFS_EPTYPE_CTRL || chan->eptype == OTGFS_EPTYPE_BULK)
|
||||||
(eptype == OTGFS_HCCHAR_EPTYP_BULK))
|
|
||||||
{
|
{
|
||||||
/* Halt the channel -- the CHH interrrupt is expected next */
|
/* Halt the channel -- the CHH interrrupt is expected next */
|
||||||
|
|
||||||
stm32_chan_halt(priv, chidx, CHREASON_XFRC);
|
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);
|
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 */
|
/* 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));
|
regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx));
|
||||||
if ((regval & OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_INTR)
|
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;
|
chan->indata1 ^= true;
|
||||||
}
|
}
|
||||||
@@ -1561,28 +1557,9 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
|
|||||||
|
|
||||||
else if ((pending & OTGFS_HCINT_NAK) != 0)
|
else if ((pending & OTGFS_HCINT_NAK) != 0)
|
||||||
{
|
{
|
||||||
/* Handle the NAK based on the endpoint type
|
/* Re-activate CTRL and BULK channels */
|
||||||
*
|
|
||||||
* Fetch the HCCHAR register and extract the endpoint type.
|
|
||||||
*/
|
|
||||||
|
|
||||||
regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx));
|
if (chan->eptype == OTGFS_EPTYPE_CTRL || chan->eptype == OTGFS_EPTYPE_BULK)
|
||||||
eptype = regval & OTGFS_HCCHAR_EPTYP_MASK;
|
|
||||||
|
|
||||||
/* Then process the NAK based on the endpoint type */
|
|
||||||
|
|
||||||
if (eptype == OTGFS_HCCHAR_EPTYP_INTR)
|
|
||||||
{
|
|
||||||
/* Halt the INTR channel -- the CHH interrrupt is expected next */
|
|
||||||
|
|
||||||
stm32_chan_halt(priv, chidx, CHREASON_NAK);
|
|
||||||
}
|
|
||||||
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
|
/* Re-activate the channel by clearing CHDIS and assuring that
|
||||||
* CHENA is set
|
* CHENA is set
|
||||||
@@ -1593,12 +1570,9 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
|
|||||||
stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval);
|
stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the result to EAGAIN to wake up any thread wait for the
|
/* Halt the channel -- the CHH interrrupt is expected next */
|
||||||
* transfer to complete.
|
|
||||||
*/
|
|
||||||
|
|
||||||
chan->result = EAGAIN;
|
stm32_chan_halt(priv, chidx, CHREASON_NAK);
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear the NAK condition */
|
/* 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)
|
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 */
|
/* Halt the channel -- the CHH interrrupt is expected next */
|
||||||
|
|
||||||
stm32_chan_halt(priv, chidx, CHREASON_XFRC);
|
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);
|
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 */
|
/* Manage multiple packet transfers */
|
||||||
|
|
||||||
priv->chan[chidx].buffer += bcnt;
|
priv->chan[chidx].buffer += bcnt;
|
||||||
priv->chan[chidx].xfrlen += bcnt;
|
priv->chan[chidx].buflen -= bcnt;
|
||||||
priv->chan[chidx].xfrd = priv->chan[chidx].xfrlen;
|
|
||||||
|
|
||||||
/* Check if more packets are expected */
|
/* 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);
|
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);
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3325,6 +3302,15 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
|
|
||||||
stm32_takesem(&priv->exclsem);
|
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;
|
||||||
|
|
||||||
|
while (chan->buflen > 0)
|
||||||
|
{
|
||||||
/* Set up for the wait BEFORE starting the transfer */
|
/* Set up for the wait BEFORE starting the transfer */
|
||||||
|
|
||||||
ret = stm32_chan_waitsetup(priv, chan);
|
ret = stm32_chan_waitsetup(priv, chan);
|
||||||
@@ -3388,9 +3374,9 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
|
|
||||||
chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
|
chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0;
|
||||||
|
|
||||||
/* Toggle the IN data PID for the next transfer */
|
/* The indata1 data toggle will be updated in the Rx FIFO
|
||||||
|
* interrupt handling logic as each packet is received.
|
||||||
chan->indata1 ^= true;
|
*/
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -3407,19 +3393,32 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
|
|
||||||
/* 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);
|
||||||
|
|
||||||
|
/* 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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
udbg("Transfer failed: %d\n", ret);
|
udbg("Transfer failed: %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Break out and return this result */
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errout:
|
errout:
|
||||||
stm32_givesem(&priv->exclsem);
|
stm32_givesem(&priv->exclsem);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
Reference in New Issue
Block a user