summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-09-05 14:33:27 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-09-05 14:33:27 -0600
commit15160f4f3de4bfa2028c822fb410e0e6eafecdb0 (patch)
tree6316947e936bd030664b37bf393edcfcc6e563d4
parent7ae98ecef66e7a7fab6015d0bc384cca37bee2e0 (diff)
downloadpx4-nuttx-15160f4f3de4bfa2028c822fb410e0e6eafecdb0.tar.gz
px4-nuttx-15160f4f3de4bfa2028c822fb410e0e6eafecdb0.tar.bz2
px4-nuttx-15160f4f3de4bfa2028c822fb410e0e6eafecdb0.zip
SAMA5 UDPHS: Major changes to DMA interrupt and request handling to better handle DMA
-rw-r--r--nuttx/arch/arm/src/sama5/sam_udphs.c628
1 files changed, 357 insertions, 271 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_udphs.c b/nuttx/arch/arm/src/sama5/sam_udphs.c
index c63f2a50c..658586d86 100644
--- a/nuttx/arch/arm/src/sama5/sam_udphs.c
+++ b/nuttx/arch/arm/src/sama5/sam_udphs.c
@@ -155,20 +155,21 @@
#define SAM_TRACEERR_BADSETFEATURE 0x000c
#define SAM_TRACEERR_BINDFAILED 0x000d
#define SAM_TRACEERR_DISPATCHSTALL 0x000e
-#define SAM_TRACEERR_DRIVER 0x000f
-#define SAM_TRACEERR_DRIVERREGISTERED 0x0010
-#define SAM_TRACEERR_EP0SETUPOUTSIZE 0x0011
-#define SAM_TRACEERR_EP0SETUPSTALLED 0x0012
-#define SAM_TRACEERR_EPINBUSY 0x0013
-#define SAM_TRACEERR_EPOUTNULLPACKET 0x0014
-#define SAM_TRACEERR_EPRESERVE 0x0015
-#define SAM_TRACEERR_EPTCFGMAPD 0x0016
-#define SAM_TRACEERR_INVALIDCTRLREQ 0x0017
-#define SAM_TRACEERR_INVALIDPARMS 0x0018
-#define SAM_TRACEERR_IRQREGISTRATION 0x0019
-#define SAM_TRACEERR_NOTCONFIGURED 0x001a
-#define SAM_TRACEERR_REQABORTED 0x001b
-#define SAM_TRACEERR_TXRDYERR 0x001c
+#define SAM_TRACEERR_DMAERR 0x000f
+#define SAM_TRACEERR_DRIVER 0x0010
+#define SAM_TRACEERR_DRIVERREGISTERED 0x0011
+#define SAM_TRACEERR_ENDBUFST 0x0012
+#define SAM_TRACEERR_EP0SETUPOUTSIZE 0x0013
+#define SAM_TRACEERR_EP0SETUPSTALLED 0x0014
+#define SAM_TRACEERR_EPOUTNULLPACKET 0x0015
+#define SAM_TRACEERR_EPRESERVE 0x0016
+#define SAM_TRACEERR_EPTCFGMAPD 0x0017
+#define SAM_TRACEERR_INVALIDCTRLREQ 0x0018
+#define SAM_TRACEERR_INVALIDPARMS 0x0019
+#define SAM_TRACEERR_IRQREGISTRATION 0x001a
+#define SAM_TRACEERR_NOTCONFIGURED 0x001b
+#define SAM_TRACEERR_REQABORTED 0x001c
+#define SAM_TRACEERR_TXRDYERR 0x001d
/* Trace interrupt codes */
@@ -180,33 +181,32 @@
#define SAM_TRACEINTID_DMA 0x0006
#define SAM_TRACEINTID_DMAEOB 0x0007
#define SAM_TRACEINTID_DMAEOC 0x0008
-#define SAM_TRACEINTID_DMAERR 0x0009
-#define SAM_TRACEINTID_ENDRESET 0x000a
-#define SAM_TRACEINTID_EP 0x000b
-#define SAM_TRACEINTID_EP0SETUPIN 0x000c
-#define SAM_TRACEINTID_EP0SETUPOUT 0x000d
-#define SAM_TRACEINTID_EP0SETUPSETADDRESS 0x000e
-#define SAM_TRACEINTID_EPGETSTATUS 0x000f
-#define SAM_TRACEINTID_EPINQEMPTY 0x0010
-#define SAM_TRACEINTID_EPOUTQEMPTY 0x0011
-#define SAM_TRACEINTID_GETCONFIG 0x0012
-#define SAM_TRACEINTID_GETSETDESC 0x0013
-#define SAM_TRACEINTID_GETSETIF 0x0014
-#define SAM_TRACEINTID_GETSTATUS 0x0015
-#define SAM_TRACEINTID_IFGETSTATUS 0x0016
-#define SAM_TRACEINTID_INTERRUPT 0x0017
-#define SAM_TRACEINTID_INTSOF 0x0018
-#define SAM_TRACEINTID_NOSTDREQ 0x0019
-#define SAM_TRACEINTID_PENDING 0x001a
-#define SAM_TRACEINTID_RXRDY 0x001b
-#define SAM_TRACEINTID_RXSETUP 0x001c
-#define SAM_TRACEINTID_SETCONFIG 0x001d
-#define SAM_TRACEINTID_SETFEATURE 0x001e
-#define SAM_TRACEINTID_STALLSNT 0x001f
-#define SAM_TRACEINTID_SYNCHFRAME 0x0020
-#define SAM_TRACEINTID_TXRDY 0x0021
-#define SAM_TRACEINTID_UPSTRRES 0x0022
-#define SAM_TRACEINTID_WAKEUP 0x0023
+#define SAM_TRACEINTID_ENDRESET 0x0009
+#define SAM_TRACEINTID_EP 0x0001
+#define SAM_TRACEINTID_EP0SETUPIN 0x000b
+#define SAM_TRACEINTID_EP0SETUPOUT 0x000c
+#define SAM_TRACEINTID_EP0SETUPSETADDRESS 0x000d
+#define SAM_TRACEINTID_EPGETSTATUS 0x000e
+#define SAM_TRACEINTID_EPINQEMPTY 0x000f
+#define SAM_TRACEINTID_EPOUTQEMPTY 0x0010
+#define SAM_TRACEINTID_GETCONFIG 0x0011
+#define SAM_TRACEINTID_GETSETDESC 0x0012
+#define SAM_TRACEINTID_GETSETIF 0x0013
+#define SAM_TRACEINTID_GETSTATUS 0x0014
+#define SAM_TRACEINTID_IFGETSTATUS 0x0015
+#define SAM_TRACEINTID_INTERRUPT 0x0016
+#define SAM_TRACEINTID_INTSOF 0x0017
+#define SAM_TRACEINTID_NOSTDREQ 0x0018
+#define SAM_TRACEINTID_PENDING 0x0019
+#define SAM_TRACEINTID_RXRDY 0x001a
+#define SAM_TRACEINTID_RXSETUP 0x001b
+#define SAM_TRACEINTID_SETCONFIG 0x001c
+#define SAM_TRACEINTID_SETFEATURE 0x001d
+#define SAM_TRACEINTID_STALLSNT 0x001e
+#define SAM_TRACEINTID_SYNCHFRAME 0x001f
+#define SAM_TRACEINTID_TXRDY 0x0020
+#define SAM_TRACEINTID_UPSTRRES 0x0021
+#define SAM_TRACEINTID_WAKEUP 0x0022
/* Ever-present MIN and MAX macros */
@@ -355,7 +355,6 @@ struct sam_usbdev_s
uint8_t devstate; /* State of the device (see enum sam_devstate_e) */
uint8_t prevstate; /* Previous state of the device before SUSPEND */
uint8_t devaddr; /* Assigned device address */
- uint8_t rxpending:1; /* 1: OUT data in the FIFO, but no read requests */
uint8_t selfpowered:1; /* 1: Device is self powered */
uint16_t epavail; /* Bitset of available endpoints */
@@ -414,9 +413,9 @@ static void sam_dtd_free(struct sam_usbdev_s *priv, struct sam_dtd_s *dtd);
#endif
static void sam_dma_single(uint8_t epno, struct sam_req_s *privreq,
uint32_t dmacontrol);
-static int sam_req_wrdma(struct sam_usbdev_s *priv,
+static void sam_dma_wrsetup(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, struct sam_req_s *privreq);
-static int sam_req_rddma(struct sam_usbdev_s *priv,
+static void sam_dma_rdsetup(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, struct sam_req_s *privreq);
/* Request Helpers **********************************************************/
@@ -430,15 +429,17 @@ static inline void
struct sam_req_s *privreq, int16_t result);
static void sam_req_complete(struct sam_ep_s *privep, int16_t result);
static void sam_ep_txrdy(unsigned int epno);
-static int sam_req_wrnodma(struct sam_usbdev_s *priv,
+static void sam_req_wrsetup(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, struct sam_req_s *privreq);
static int sam_req_write(struct sam_usbdev_s *priv,
struct sam_ep_s *privep);
-static int sam_req_rdnodma(struct sam_usbdev_s *priv,
+static void sam_req_rddone(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, struct sam_req_s *privreq,
- uint16_t pktsize);
+ uint16_t recvsize);
+static void sam_req_rdenable(uint8_t epno);
+static void sam_req_rddisable(uint8_t epno);
static int sam_req_read(struct sam_usbdev_s *priv,
- struct sam_ep_s *privep, uint16_t pktsize);
+ struct sam_ep_s *privep, uint16_t recvsize);
static void sam_req_cancel(struct sam_ep_s *privep, int16_t status);
/* Interrupt level processing ***********************************************/
@@ -582,11 +583,12 @@ const struct trace_msg_t g_usb_trace_strings_deverror[] =
TRACE_STR(SAM_TRACEERR_BADSETFEATURE),
TRACE_STR(SAM_TRACEERR_BINDFAILED),
TRACE_STR(SAM_TRACEERR_DISPATCHSTALL),
+ TRACE_STR(SAM_TRACEERR_DMAERR),
TRACE_STR(SAM_TRACEERR_DRIVER),
TRACE_STR(SAM_TRACEERR_DRIVERREGISTERED),
+ TRACE_STR(SAM_TRACEERR_ENDBUFST),
TRACE_STR(SAM_TRACEERR_EP0SETUPOUTSIZE),
TRACE_STR(SAM_TRACEERR_EP0SETUPSTALLED),
- TRACE_STR(SAM_TRACEERR_EPINBUSY),
TRACE_STR(SAM_TRACEERR_EPOUTNULLPACKET),
TRACE_STR(SAM_TRACEERR_EPRESERVE),
TRACE_STR(SAM_TRACEERR_EPTCFGMAPD),
@@ -615,7 +617,6 @@ const struct trace_msg_t g_usb_trace_strings_intdecode[] =
TRACE_STR(SAM_TRACEINTID_DMA),
TRACE_STR(SAM_TRACEINTID_DMAEOB),
TRACE_STR(SAM_TRACEINTID_DMAEOC),
- TRACE_STR(SAM_TRACEINTID_DMAERR),
TRACE_STR(SAM_TRACEINTID_ENDRESET),
TRACE_STR(SAM_TRACEINTID_EP),
TRACE_STR(SAM_TRACEINTID_EP0SETUPIN),
@@ -928,28 +929,20 @@ static void sam_dma_single(uint8_t epno, struct sam_req_s *privreq,
}
/****************************************************************************
- * Name: sam_req_wrdma
+ * Name: sam_dma_wrsetup
*
* Description:
* Process the next queued write request for an endpoint that supports DMA.
*
****************************************************************************/
-static int sam_req_wrdma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
- struct sam_req_s *privreq)
+static void sam_dma_wrsetup(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
+ struct sam_req_s *privreq)
{
uint32_t regval;
int remaining;
int epno;
- /* The endpoint must be IDLE and ready to begin the next transfer */
-
- if (privep->epstate != UDPHS_EPSTATE_IDLE)
- {
- usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPINBUSY), privep->epstate);
- return -EBUSY;
- }
-
/* Switch to the sending state */
privep->epstate = UDPHS_EPSTATE_SENDING;
@@ -961,7 +954,7 @@ static int sam_req_wrdma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
/* How many bytes remain to be transferred in the request? */
- remaining = privreq->req.len - privreq->req.xfrd - privreq->inflight;
+ remaining = privreq->req.len - privreq->req.xfrd;
/* If there are no bytes to send, then send a zero length packet */
@@ -994,7 +987,6 @@ static int sam_req_wrdma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
sam_dma_single(epno, privreq,
UDPHS_DMACONTROL_ENDBEN | UDPHS_DMACONTROL_ENDBUFFIT |
UDPHS_DMACONTROL_CHANNENB);
- return OK;
}
/* Enable the endpoint interrupt */
@@ -1002,94 +994,67 @@ static int sam_req_wrdma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
regval = sam_getreg(SAM_UDPHS_IEN);
regval |= UDPHS_INT_EPT(epno);
sam_putreg(regval, SAM_UDPHS_IEN);
- return OK;
}
/****************************************************************************
- * Name: sam_req_rddma
+ * Name: sam_dma_rdsetup
*
* Description:
* Process the next queued read request for an endpoint that supports DMA.
*
****************************************************************************/
-static int sam_req_rddma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
- struct sam_req_s *privreq)
+static void sam_dma_rdsetup(struct sam_usbdev_s *priv,
+ struct sam_ep_s *privep,
+ struct sam_req_s *privreq)
{
uint32_t regval;
int remaining;
int epno;
- /* The endpoint must be IDLE and ready to begin the next transfer */
-
- if (privep->epstate != UDPHS_EPSTATE_IDLE)
- {
- usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPINBUSY), privep->epstate);
- return -EBUSY;
- }
-
/* Get the endpoint number */
epno = USB_EPNO(privep->ep.eplog);
- /* Switch to the receiving state */
-
- privep->epstate = UDPHS_EPSTATE_RECEIVING;
- privep->zlpneeded = false;
- privep->zlpsent = false;
- privreq->inflight = 0;
- privreq->req.xfrd = 0;
-
/* How many more bytes can we append to the request buffer? */
remaining = privreq->req.len - privreq->req.xfrd;
- if (remaining > 0)
- {
- /* Clip the DMA transfer size to the size available in the user buffer */
+ DEBUGASSERT(remaining > 0 && privep->epstate == UDPHS_EPSTATE_RECEIVING);
+
+ /* Clip the DMA transfer size to the size available in the user buffer */
#if USBDEV_MAXREQUEUST > DMA_MAX_FIFO_SIZE
- if (remaining > DMA_MAX_FIFO_SIZE)
- {
- privreq->inflight = DMA_MAX_FIFO_SIZE;
- }
- else
+ if (remaining > DMA_MAX_FIFO_SIZE)
+ {
+ privreq->inflight = DMA_MAX_FIFO_SIZE;
+ }
+ else
#endif
- {
- privreq->inflight = remaining;
- }
-
- /* And perform the single DMA transfer.
- *
- * 32.6.10.12 Bulk OUT or Interrupt OUT: Sending a Buffer Using DMA
- * - END_B_EN: Can be used for OUT packet truncation (discarding of
- * unbuffered packet data) at the end of DMA buffer.
- * - END_BUFFIT: Generate an interrupt when BUFF_COUNT in the
- * UDPHS_DMASTATUSx register reaches 0.
- * - END_TR_EN: End of transfer enable, the UDPHS device can put an
- * end to the current DMA transfer, in case of a short packet.
- * - END_TR_IT: End of transfer interrupt enable, an interrupt is sent
- * after the last USB packet has been transferred by the DMA, if the
- * USB transfer ended with a short packet. (Beneficial when the
- * receive size is unknown.)
- * - CHANN_ENB: Run and stop at end of buffer.
- */
-
- regval = UDPHS_DMACONTROL_ENDBEN | UDPHS_DMACONTROL_ENDBUFFIT |
- UDPHS_DMACONTROL_ENDTREN | UDPHS_DMACONTROL_ENDTRIT |
- UDPHS_DMACONTROL_CHANNENB;
-
- sam_dma_single(epno, privreq, regval);
- return OK;
+ {
+ privreq->inflight = remaining;
}
- /* Enable the endpoint interrupt */
+ /* And perform the single DMA transfer.
+ *
+ * 32.6.10.12 Bulk OUT or Interrupt OUT: Sending a Buffer Using DMA
+ * - END_B_EN: Can be used for OUT packet truncation (discarding of
+ * unbuffered packet data) at the end of DMA buffer.
+ * - END_BUFFIT: Generate an interrupt when BUFF_COUNT in the
+ * UDPHS_DMASTATUSx register reaches 0.
+ * - END_TR_EN: End of transfer enable, the UDPHS device can put an
+ * end to the current DMA transfer, in case of a short packet.
+ * - END_TR_IT: End of transfer interrupt enable, an interrupt is sent
+ * after the last USB packet has been transferred by the DMA, if the
+ * USB transfer ended with a short packet. (Beneficial when the
+ * receive size is unknown.)
+ * - CHANN_ENB: Run and stop at end of buffer.
+ */
- regval = sam_getreg(SAM_UDPHS_IEN);
- regval |= UDPHS_INT_EPT(epno);
- sam_putreg(regval, SAM_UDPHS_IEN);
+ regval = UDPHS_DMACONTROL_ENDBEN | UDPHS_DMACONTROL_ENDBUFFIT |
+ UDPHS_DMACONTROL_ENDTREN | UDPHS_DMACONTROL_ENDTRIT |
+ UDPHS_DMACONTROL_CHANNENB;
- sam_putreg(UDPHS_EPTCTL_RXRDYTXKL, SAM_UDPHS_EPTCTLENB(epno));
- return OK;
+ sam_dma_single(epno, privreq, regval);
}
/****************************************************************************
@@ -1220,7 +1185,7 @@ static void sam_ep_txrdy(unsigned int epno)
}
/****************************************************************************
- * Name: sam_req_wrnodma
+ * Name: sam_req_wrsetup
*
* Description:
* Process the next queued write request for an endpoint that does not
@@ -1228,13 +1193,13 @@ static void sam_ep_txrdy(unsigned int epno)
*
****************************************************************************/
-static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
- struct sam_req_s *privreq)
+static void sam_req_wrsetup(struct sam_usbdev_s *priv,
+ struct sam_ep_s *privep,
+ struct sam_req_s *privreq)
{
const uint8_t *buf;
uint8_t *fifo;
uint8_t epno;
- int committed;
int nbytes;
int bytesleft;
@@ -1246,13 +1211,9 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
DEBUGASSERT((sam_getreg(SAM_UDPHS_EPTSTA(epno)) & UDPHS_EPTSTA_TXRDY) == 0);
- /* Get the number of bytes to send. The totals bytes remaining to be sent
- * is the the total size of the buffer, minus the number of bytes
- * successfully transferred, minus the number of bytes in-flight.
- */
+ /* Get the number of bytes remaining to be sent. */
- committed = privreq->req.xfrd + privreq->inflight;
- bytesleft = privreq->req.len - committed;
+ bytesleft = privreq->req.len - privreq->req.xfrd;
/* Clip the requested transfer size to the number of bytes actually
* available
@@ -1268,7 +1229,6 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
* maxpacket and check if we need to send a following zero length packet.
*/
- nbytes = bytesleft;
if (nbytes > 0)
{
/* Either send the maxpacketsize or all of the remaining data in
@@ -1282,7 +1242,7 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
/* This is the new number of bytes "in-flight" */
- privreq->inflight += nbytes;
+ privreq->inflight = nbytes;
usbtrace(TRACE_WRITE(USB_EPNO(privep->ep.eplog)), nbytes);
/* The new buffer pointer is the started of the buffer plus the number
@@ -1290,7 +1250,7 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
* "in-flight".
*/
- buf = privreq->req.buf + committed;
+ buf = privreq->req.buf + privreq->req.xfrd;
/* Write packet in the FIFO buffer */
@@ -1314,7 +1274,6 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
*/
sam_ep_txrdy(epno);
- return OK;
}
/****************************************************************************
@@ -1326,6 +1285,20 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
* interrupts disabled, (2) from interrupt handling when a previous
* transfer completes, or (3) resuming a stalled IN endpoint.
*
+ * Calling rules:
+ *
+ * The transfer state must IDLE
+ *
+ * When a request is queued, the request 'len' is the number of bytes
+ * to transfer and 'xfrd' and 'inflight' must be zero.
+ *
+ * When this function starts a tranfer it will update the request
+ * 'inflight' field to indicate the size of the transfer.
+ *
+ * When the transfer completes, the the 'inflight' field must hold the
+ * number of bytes that have completed the transfer. This function will
+ * update 'xfrd' with the new size of the transfer.
+ *
****************************************************************************/
static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
@@ -1335,7 +1308,6 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
uint32_t eptype;
uint8_t epno;
int bytesleft;
- int ret;
/* Get the unadorned endpoint number */
@@ -1418,18 +1390,11 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
if ((SAM_EPSET_DMA & SAM_EP_BIT(epno)) != 0)
{
- ret = sam_req_wrdma(priv, privep, privreq);
+ sam_dma_wrsetup(priv, privep, privreq);
}
else
{
- ret = sam_req_wrnodma(priv, privep, privreq);
- }
-
- /* Check if the transfer was successfully initiated */
-
- if (ret < 0)
- {
- return ret;
+ sam_req_wrsetup(priv, privep, privreq);
}
}
@@ -1488,16 +1453,17 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
}
/****************************************************************************
- * Name: sam_req_rdnodma
+ * Name: sam_req_rddone
*
* Description:
- * Process the next queued write request for an endpoint that does not
- * support DMA.
+ * The last non-DMA OUT transfer has completed. Read 'recvsize' byts from
+ * the FIFO into the read request buffer.
*
****************************************************************************/
-static int sam_req_rdnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
- struct sam_req_s *privreq, uint16_t pktsize)
+static void sam_req_rddone(struct sam_usbdev_s *priv,
+ struct sam_ep_s *privep,
+ struct sam_req_s *privreq, uint16_t recvsize)
{
const uint8_t *fifo;
uint8_t *dest;
@@ -1505,9 +1471,6 @@ static int sam_req_rdnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
int readlen;
int epno;
- privep->epstate = UDPHS_EPSTATE_IDLE;
- privreq->inflight = 0;
-
/* Get the number of bytes that can be received. This is the size of the
* user-provided request buffer, minus the number of bytes already
* transferred to the user-buffer.
@@ -1519,7 +1482,7 @@ static int sam_req_rdnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
* size remaining in the request buffer provided by the caller.
*/
- readlen = MIN(remaining, pktsize);
+ readlen = MIN(remaining, recvsize);
privreq->req.xfrd += readlen;
/* Get the source and destination transfer addresses */
@@ -1535,8 +1498,45 @@ static int sam_req_rdnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
{
*dest++ = *fifo++;
}
+}
- return OK;
+/****************************************************************************
+ * Name: sam_req_rdenable
+ *
+ * Description:
+ * Make sure that the endpoint RXRDY_TXTK interrupt is enabled in order
+ * to receive the next incoming packet
+ *
+ ****************************************************************************/
+
+static void sam_req_rdenable(uint8_t epno)
+{
+ uint32_t regval;
+
+ regval = sam_getreg(SAM_UDPHS_IEN);
+ regval |= UDPHS_INT_EPT(epno);
+ sam_putreg(regval, SAM_UDPHS_IEN);
+
+ sam_putreg(UDPHS_EPTCTL_RXRDYTXKL, SAM_UDPHS_EPTCTLENB(epno));
+}
+
+/****************************************************************************
+ * Name: sam_req_rddisable
+ *
+ * Description:
+ * Disable endpoint interrupts
+ *
+ ****************************************************************************/
+
+static void sam_req_rddisable(uint8_t epno)
+{
+ uint32_t regval;
+
+ regval = sam_getreg(SAM_UDPHS_IEN);
+ regval &= ~UDPHS_INT_EPT(epno);
+ sam_putreg(regval, SAM_UDPHS_IEN);
+
+ sam_putreg(UDPHS_EPTCTL_RXRDYTXKL, SAM_UDPHS_EPTCTLDIS(epno));
}
/****************************************************************************
@@ -1546,67 +1546,130 @@ static int sam_req_rdnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
* Called only from interrupt handling logic when on OUT packet is received
* on an endpoint in the RECEIVING state.
*
+ * There is a fundamental difference between receiving packets via DMA and
+ * via the FIFO:
+ *
+ * - When receiving data via DMA, then data has already been transferred
+ * and this function is called on the terminating event. The transfer
+ * is complete and we just need to check for end of request events and
+ * if we need to setup the next tranfer.
+ * - When receiving via the FIFO, then transfer is not complete. The
+ * data is in the FIFO and must be transferred from the FIFO to the
+ * request buffer. No setup is needed for the next transfer.
+ *
+ * Calling rules:
+ *
+ * The transfer state must IDLE
+ *
+ * When a request is queued, the request 'len' is size of the request
+ * buffer. Any OUT request can be received that will fit in this
+ * buffer. 'xfrd' and 'inflight' in the request must be zero
+ * If sam_req_read() is called to start a new transfer, the recvsize
+ * parameter must be zero.
+ *
+ * When this function starts a DMA transfer it will update the request
+ * 'inflight' field to hold the maximum size of the transfer; but
+ * 'inflight' is not used with FIFO transfers.
+ *
+ * When the transfer completes, the 'recvsize' paramter must be the
+ * size of the transfer that just completed. For the case of DMA,
+ * that is the size of the DMA transfer that has just been written to
+ * memory; for the FIFO transfer, recvsize is the number of bytes
+ * waiting in the FIFO to be read.
+ *
****************************************************************************/
static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
- uint16_t pktsize)
+ uint16_t recvsize)
{
struct sam_req_s *privreq;
uint32_t regval;
uint32_t eptype;
uint8_t epno;
- int ret;
- /* Check the request from the head of the endpoint request queue */
+ DEBUGASSERT(priv && privep && privep->epstate == UDPHS_EPSTATE_IDLE);
- epno = USB_EPNO(privep->ep.eplog);
- privreq = sam_rqpeek(&privep->reqq);
- if (!privreq)
+ /* Loop in case we need to handle multiple read requests */
+
+ while (privep->epstate == UDPHS_EPSTATE_IDLE)
{
- /* Incoming data available in the FIFO, but no packet to receive the data.
- * Mark that the RX data is pending and hope that a packet is returned
- * soon.
- */
+ /* Check the request from the head of the endpoint request queue */
- usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPOUTQEMPTY), epno);
- priv->rxpending = true;
- return -ENOENT;
- }
+ epno = USB_EPNO(privep->ep.eplog);
+ privreq = sam_rqpeek(&privep->reqq);
+ if (!privreq)
+ {
+ /* No packet to receive data */
- ullvdbg("EP%d: len=%d xfrd=%d\n", epno, privreq->req.len, privreq->req.xfrd);
+ usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPOUTQEMPTY), epno);
+ return -ENOENT;
+ }
- /* Ignore any attempt to receive a zero length packet */
+ ullvdbg("EP%d: len=%d xfrd=%d\n",
+ epno, privreq->req.len, privreq->req.xfrd);
- if (privreq->req.len == 0)
- {
- usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPOUTNULLPACKET), 0);
- sam_req_complete(privep, OK);
- return OK;
- }
+ /* Ignore any attempt to receive a zero length packet */
- usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
+ if (privreq->req.len == 0)
+ {
+ usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPOUTNULLPACKET), 0);
+ sam_req_complete(privep, OK);
+ recvsize = 0;
+ continue;
+ }
- /* The way that we handle the transfer is going to depend on whether
- * or not this endpoint supports DMA.
- */
+ usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), recvsize);
- if ((SAM_EPSET_DMA & SAM_EP_BIT(epno)) != 0)
- {
- ret = sam_req_rddma(priv, privep, privreq);
- }
- else
- {
- ret = sam_req_rdnodma(priv, privep, privreq, pktsize);
- }
+ /* Update the number of bytes transferred with the received size */
- if (ret == OK)
- {
- /* If the receive buffer is full or this is a partial packet,
- * then we are finished with the request buffer).
+ privreq->req.xfrd += recvsize;
+ privreq->inflight = 0;
+
+ /* If this was not a DMA transfer, read the incoming data from the FIFO */
+
+ if ((SAM_EPSET_DMA & SAM_EP_BIT(epno)) == 0)
+ {
+ sam_req_rddone(priv, privep, privreq, recvsize);
+ }
+
+ /* In case we go through the loop again */
+
+ recvsize = 0;
+
+ /* If nothing has yet be transferred into the read request, then
+ * indicate that we are in the RECEIVING state and, if the endpoint
+ * supports DMA, setup the receive DMA.
*/
- if (privreq->inflight < privep->ep.maxpacket ||
- privreq->req.xfrd >= privreq->req.len)
+ if (privreq->req.xfrd == 0)
+ {
+ /* Set the RECEIVING state */
+
+ privep->epstate = UDPHS_EPSTATE_RECEIVING;
+
+ /* If the endpoint supports DMA, set up the DMA now */
+
+ if ((SAM_EPSET_DMA & SAM_EP_BIT(epno)) != 0)
+ {
+ /* Set up the next DMA */
+
+ sam_dma_rdsetup(priv, privep, privreq);
+ }
+ else
+ {
+ /* Enable endpoint RXRDY_TXTK interrupts */
+
+ sam_req_rdenable(epno);
+ }
+ }
+
+ /* We will not try to accumulate packet data here. If anything
+ * has been received, we will complete the transfer immediately and
+ * give the data to the class driver. The idea is that we will let the
+ * receiving be in-charge if incoming buffer.
+ */
+
+ else
{
/* Return the read request to the class driver. */
@@ -1618,13 +1681,11 @@ static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
regval = sam_getreg(SAM_UDPHS_EPTCFG(epno));
eptype = regval & UDPHS_EPTCFG_TYPE_MASK;
- /* Disable interrupt if not control EP */
+ /* Disable endpoint interrupts if not the control endpoint */
- if (UDPHS_EPTCFG_TYPE_CTRL8 != eptype)
+ if (eptype != UDPHS_EPTCFG_TYPE_CTRL8)
{
- regval = sam_getreg(SAM_UDPHS_IEN);
- regval &= ~UDPHS_INT_EPT(epno);
- sam_putreg(regval, SAM_UDPHS_IEN);
+ sam_req_rddisable(epno);
}
/* And complete the request */
@@ -2274,8 +2335,7 @@ static void sam_dma_interrupt(struct sam_usbdev_s *priv, int epno)
uint32_t dmastatus;
uint8_t *buf;
int bufcnt;
- int xfrd;
- int16_t result = OK;
+ int xfrsize;
/* Not all endpoints support DMA */
@@ -2291,13 +2351,6 @@ static void sam_dma_interrupt(struct sam_usbdev_s *priv, int epno)
privreq = sam_rqpeek(&privep->reqq);
DEBUGASSERT(privreq);
- /* Invalidate the data cache for region that just completed DMA.
- * This will force the buffer data to be reloaded from RAM.
- */
-
- buf = &privreq->req.buf[privreq->req.xfrd];
- cp15_invalidate_dcache((uintptr_t)buf, (uintptr_t)buf + privreq->inflight);
-
/* Get the result of the DMA operation */
dmastatus = sam_getreg(SAM_UDPHS_DMASTATUS(epno));
@@ -2310,8 +2363,8 @@ static void sam_dma_interrupt(struct sam_usbdev_s *priv, int epno)
regval &= ~(UDPHS_DMACONTROL_ENDTREN | UDPHS_DMACONTROL_ENDBEN);
sam_putreg(regval, regaddr);
- /* Check for end of the buffer. Set by hardware when the
- * BUFF_COUNT downcount reach zero.
+ /* Check for end of the buffer. Set by hardware when the BUFF_COUNT
+ * downcount reaches zero. This could be either an IN or OUT transfer.
*/
if ((dmastatus & UDPHS_DMASTATUS_ENDBFST) != 0)
@@ -2319,77 +2372,118 @@ static void sam_dma_interrupt(struct sam_usbdev_s *priv, int epno)
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DMAEOB), (uint16_t)dmastatus);
/* BUFF_COUNT holds the number of untransmitted bytes. BUFF_COUNT is
- * equal to zero in case of good transfer
+ * equal to zero in case of good transfer.
+ *
+ * BUFF_COUNT was set to the 'inflight' count when the DMA started and
+ * the BUFF_COUNT has now decremented to zero
+ */
+
+ xfrsize = privreq->inflight;
+ privreq->inflight = 0;
+
+ /* This is just debug logic that only does any if USB debug or tracing
+ * are enabled. This just verifies taht BUFF_COUNT is zero.
*/
bufcnt = (dmastatus & UDPHS_DMASTATUS_BUFCNT_MASK)
>> UDPHS_DMASTATUS_BUFCNT_SHIFT;
- xfrd = privreq->inflight - bufcnt;
- privreq->req.xfrd += xfrd;
- privreq->inflight = bufcnt;
+ if (bufcnt != 0)
+ {
+ usbtrace(TRACE_DEVERROR(SAM_TRACEERR_ENDBUFST), bufcnt);
+ }
- /* Is there more data to send? */
+ /* Were we sending? Or receiving? */
- bufcnt = privreq->req.len - privreq->req.xfrd - privreq->inflight;
- if (bufcnt > 0)
+ if (privep->epstate == UDPHS_EPSTATE_SENDING)
{
- /* Yes, clip to the size of the DMA FIFO */
+ /* This is an IN endpoint. Continuing processing the write
+ * request
+ */
-#if USBDEV_MAXREQUEUST > DMA_MAX_FIFO_SIZE
- if (bufcnt > DMA_MAX_FIFO_SIZE)
- {
- privreq->inflight = DMA_MAX_FIFO_SIZE;
- }
- else
-#endif
- {
- privreq->inflight = bufcnt;
- }
+ DEBUGASSERT(USB_ISEPIN(privep->ep.eplog));
+ privep->epstate = UDPHS_EPSTATE_IDLE;
+ (void)sam_req_write(priv, privep);
+ }
+ else
+ {
+ /* This is an OUT endpoint. Invalidate the data cache for
+ * region that just completed DMA. This will force the
+ * buffer data to be reloaded from RAM. when it is accessed
+ */
- /* And perform the DMA transfer */
+ DEBUGASSERT(USB_ISEPOUT(privep->ep.eplog));
+ buf = &privreq->req.buf[privreq->req.xfrd];
+ cp15_invalidate_dcache((uintptr_t)buf, (uintptr_t)buf + xfrsize);
- regval = UDPHS_DMACONTROL_ENDTREN | UDPHS_DMACONTROL_ENDTRIT |
- UDPHS_DMACONTROL_ENDBEN | UDPHS_DMACONTROL_ENDBUFFIT |
- UDPHS_DMACONTROL_CHANNENB;
+ /* Continuing processing the read request */
- sam_dma_single(epno, privreq, regval);
+ privep->epstate = UDPHS_EPSTATE_IDLE;
+ (void)sam_req_read(priv, privep, xfrsize);
}
}
- /* Check for end of channel transfer. Set by hardware when the last
- * packet transfer is complete
+ /* Check for end of channel transfer. END_TR_ST is set by hardware when
+ * the last packet transfer is complete iff END_TR_EN is set in the
+ * DMACONTROL rgister. The request is complete.
+ *
+ * "Used for OUT transfers only.
+ *
+ * "0 = USB end of transfer is ignored.
+ * "1 = UDPHS device can put an end to the current buffer transfer.
+ *
+ * "When set, a BULK or INTERRUPT short packet or the last packet of
+ * an ISOCHRONOUS (micro) frame (DATAX) will close the current buffer
+ * and the UDPHS_DMASTATUSx register END_TR_ST flag will be raised."
*/
else if ((dmastatus & UDPHS_DMASTATUS_ENDTRST) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DMAEOC), (uint16_t)dmastatus);
+ DEBUGASSERT(privep->epstate == UDPHS_EPSTATE_RECEIVING &&
+ USB_ISEPOUT(privep->ep.eplog));
+
+ /* Get the number of bytes transferred from the DMA status.
+ *
+ * BUFF_COUNT holds the number of untransmitted bytes. In this case,
+ * BUFF_COUNT should not be zero. BUFF_COUNT was set to the
+ * 'inflight' count when the DMA started so the difference will
+ * give us the actual size of the transfer.
+ */
- /* Get the number of btyes transferred from the DMA status */
+ bufcnt = ((dmastatus & UDPHS_DMASTATUS_BUFCNT_MASK)
+ >> UDPHS_DMASTATUS_BUFCNT_SHIFT);
+ xfrsize = privreq->inflight - bufcnt;
- bufcnt = ((dmastatus & UDPHS_DMASTATUS_BUFCNT_MASK)
- >> UDPHS_DMASTATUS_BUFCNT_SHIFT);
+ /* Invalidate the data cache for region that just completed DMA.
+ * This will force the buffer data to be reloaded from RAM.
+ */
- xfrd = privreq->inflight - bufcnt;
- privreq->req.xfrd += xfrd;
- privreq->inflight -= bufcnt;
- }
- else
- {
- usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DMAERR), (uint16_t)dmastatus);
- result = -EIO;
- }
+ buf = &privreq->req.buf[privreq->req.xfrd];
+ cp15_invalidate_dcache((uintptr_t)buf, (uintptr_t)buf + xfrsize);
+
+ /* Complete this transfer now and return the request to the class
+ * implementation.
+ */
- /* Check if we are finished with this requrest */
+ privep->epstate = UDPHS_EPSTATE_IDLE;
+ privreq->req.xfrd += xfrsize;
+ privreq->inflight = 0;
+ sam_req_complete(privep, OK);
- if (privreq->req.len == privreq->req.xfrd)
+ /* Now, try to start the next, queue read request */
+
+ (void)sam_req_read(priv, privep, 0);
+ }
+ else
{
- /* Return the request buffer to the class implementation */
+ usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DMAERR), (uint16_t)dmastatus);
- sam_req_complete(privep, result);
+ /* Return the request buffer to the class implementation with the I/O
+ * error indication.
+ */
- /* Try to start the transfer for the next request */
-#warning Missing logic
+ sam_req_complete(privep, -EIO);
}
}
@@ -2575,17 +2669,15 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
if (eptype == UDPHS_EPTCFG_TYPE_ISO)
{
+ privep->epstate = UDPHS_EPSTATE_IDLE;
sam_req_complete(privep, -EIO);
}
/* If EP is not halted, clear STALL */
- else
+ else if (privep->epstate != UDPHS_EPSTATE_STALLED)
{
- if (privep->epstate != UDPHS_EPSTATE_STALLED)
- {
- sam_putreg(UDPHS_EPTSTA_FRCESTALL, SAM_UDPHS_EPTCLRSTA(epno));
- }
+ sam_putreg(UDPHS_EPTSTA_FRCESTALL, SAM_UDPHS_EPTCLRSTA(epno));
}
}
@@ -3500,7 +3592,7 @@ static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
sam_req_enqueue(&privep->reqq, privreq);
usbtrace(TRACE_INREQQUEUED(epno), req->len);
- /* If the IN endpoint FIFO is available, then transfer the data now */
+ /* If the IN endpoint is IDLE, then transfer the data now */
if (privep->epstate == UDPHS_EPSTATE_IDLE)
{
@@ -3517,16 +3609,11 @@ static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
sam_req_enqueue(&privep->reqq, privreq);
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
- /* This there a incoming data pending the availability of a request? */
+ /* If the OUT endpoint IDLE, then setup the read */
- if (priv->rxpending)
+ if (privep->epstate == UDPHS_EPSTATE_IDLE)
{
- /* NAK any OUT request addressed to the endpoint */
-#warning Missing logic
-
- /* Data is no longer pending */
-
- priv->rxpending = false;
+ ret = sam_req_read(priv, privep, 0);
}
}
@@ -3975,7 +4062,6 @@ static void sam_reset(struct sam_usbdev_s *priv)
sam_setdevaddr(priv, 0);
priv->devstate = UDPHS_DEVSTATE_DEFAULT;
- priv->rxpending = false;
/* Reset and disable all endpoints other. Then re-configure EP0 */